diff options
Diffstat (limited to 'vcl/source/gdi')
73 files changed, 87557 insertions, 0 deletions
diff --git a/vcl/source/gdi/alpha.cxx b/vcl/source/gdi/alpha.cxx new file mode 100644 index 000000000000..6107affaf4e9 --- /dev/null +++ b/vcl/source/gdi/alpha.cxx @@ -0,0 +1,437 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/debug.hxx> +#include <vcl/bmpacc.hxx> +#include <tools/color.hxx> +#include <vcl/alpha.hxx> + +// ------------- +// - AlphaMask - +// ------------- + +AlphaMask::AlphaMask() +{ +} + +// ----------------------------------------------------------------------------- + +AlphaMask::AlphaMask( const Bitmap& rBitmap ) : + Bitmap( rBitmap ) +{ + if( !!rBitmap ) + Bitmap::Convert( BMP_CONVERSION_8BIT_GREYS ); +} + +// ----------------------------------------------------------------------------- + +AlphaMask::AlphaMask( const AlphaMask& rAlphaMask ) : + Bitmap( rAlphaMask ) +{ +} + +// ----------------------------------------------------------------------------- + +AlphaMask::AlphaMask( const Size& rSizePixel, sal_uInt8* pEraseTransparency ) : + Bitmap( rSizePixel, 8, &Bitmap::GetGreyPalette( 256 ) ) +{ + if( pEraseTransparency ) + Bitmap::Erase( Color( *pEraseTransparency, *pEraseTransparency, *pEraseTransparency ) ); +} + +// ----------------------------------------------------------------------------- + +AlphaMask::~AlphaMask() +{ +} + +// ----------------------------------------------------------------------------- + +AlphaMask& AlphaMask::operator=( const Bitmap& rBitmap ) +{ + *(Bitmap*) this = rBitmap; + + if( !!rBitmap ) + Bitmap::Convert( BMP_CONVERSION_8BIT_GREYS ); + + return *this; +} + +// ----------------------------------------------------------------------------- + +const Bitmap& AlphaMask::ImplGetBitmap() const +{ + return( (const Bitmap&) *this ); +} + +// ----------------------------------------------------------------------------- + +void AlphaMask::ImplSetBitmap( const Bitmap& rBitmap ) +{ + DBG_ASSERT( ( 8 == rBitmap.GetBitCount() ) && rBitmap.HasGreyPalette(), "AlphaMask::ImplSetBitmap: invalid bitmap" ); + *(Bitmap*) this = rBitmap; +} + +// ----------------------------------------------------------------------------- + +Bitmap AlphaMask::GetBitmap() const +{ + return ImplGetBitmap(); +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Crop( const Rectangle& rRectPixel ) +{ + return Bitmap::Crop( rRectPixel ); +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Expand( sal_uLong nDX, sal_uLong nDY, sal_uInt8* pInitTransparency ) +{ + Color aColor; + + if( pInitTransparency ) + aColor = Color( *pInitTransparency, *pInitTransparency, *pInitTransparency ); + + return Bitmap::Expand( nDX, nDY, pInitTransparency ? &aColor : NULL ); +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::CopyPixel( const Rectangle& rRectDst, const Rectangle& rRectSrc, + const AlphaMask* pAlphaSrc ) +{ + // Note: this code is copied from Bitmap::CopyPixel but avoids any palette lookups + // this optimization is possible because the palettes of AlphaMasks are always identical (8bit GreyPalette, see ctor) + + const Size aSizePix( GetSizePixel() ); + Rectangle aRectDst( rRectDst ); + sal_Bool bRet = sal_False; + + aRectDst.Intersection( Rectangle( Point(), aSizePix ) ); + + if( !aRectDst.IsEmpty() ) + { + if( pAlphaSrc && ( *pAlphaSrc != *this ) ) + { + Bitmap* pSrc = (Bitmap*) pAlphaSrc; + const Size aCopySizePix( pSrc->GetSizePixel() ); + Rectangle aRectSrc( rRectSrc ); + + aRectSrc.Intersection( Rectangle( Point(), aCopySizePix ) ); + + if( !aRectSrc.IsEmpty() ) + { + BitmapReadAccess* pReadAcc = pSrc->AcquireReadAccess(); + + if( pReadAcc ) + { + BitmapWriteAccess* pWriteAcc = AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = Min( aRectSrc.GetWidth(), aRectDst.GetWidth() ); + const long nHeight = Min( aRectSrc.GetHeight(), aRectDst.GetHeight() ); + const long nSrcEndX = aRectSrc.Left() + nWidth; + const long nSrcEndY = aRectSrc.Top() + nHeight; + long nDstY = aRectDst.Top(); + + for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ ) + for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ ) + pWriteAcc->SetPixel( nDstY, nDstX, pReadAcc->GetPixel( nSrcY, nSrcX ) ); + + ReleaseAccess( pWriteAcc ); + bRet = ( nWidth > 0L ) && ( nHeight > 0L ); + } + + pSrc->ReleaseAccess( pReadAcc ); + } + } + } + else + { + Rectangle aRectSrc( rRectSrc ); + + aRectSrc.Intersection( Rectangle( Point(), aSizePix ) ); + + if( !aRectSrc.IsEmpty() && ( aRectSrc != aRectDst ) ) + { + BitmapWriteAccess* pWriteAcc = AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = Min( aRectSrc.GetWidth(), aRectDst.GetWidth() ); + const long nHeight = Min( aRectSrc.GetHeight(), aRectDst.GetHeight() ); + const long nSrcX = aRectSrc.Left(); + const long nSrcY = aRectSrc.Top(); + const long nSrcEndX1 = nSrcX + nWidth - 1L; + const long nSrcEndY1 = nSrcY + nHeight - 1L; + const long nDstX = aRectDst.Left(); + const long nDstY = aRectDst.Top(); + const long nDstEndX1 = nDstX + nWidth - 1L; + const long nDstEndY1 = nDstY + nHeight - 1L; + + if( ( nDstX <= nSrcX ) && ( nDstY <= nSrcY ) ) + { + for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ ) + for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ ) + pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) ); + } + else if( ( nDstX <= nSrcX ) && ( nDstY >= nSrcY ) ) + { + for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- ) + for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ ) + pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) ); + } + else if( ( nDstX >= nSrcX ) && ( nDstY <= nSrcY ) ) + { + for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ ) + for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- ) + pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) ); + } + else + { + for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- ) + for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- ) + pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) ); + } + + ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + } + } + } + + return bRet; + +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Erase( sal_uInt8 cTransparency ) +{ + return Bitmap::Erase( Color( cTransparency, cTransparency, cTransparency ) ); +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Invert() +{ + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pAcc && pAcc->GetBitCount() == 8 ) + { + BitmapColor aCol( 0 ); + const long nWidth = pAcc->Width(), nHeight = pAcc->Height(); + sal_uInt8* pMap = new sal_uInt8[ 256 ]; + + for( long i = 0; i < 256; i++ ) + pMap[ i ] = ~(sal_uInt8) i; + + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + aCol.SetIndex( pMap[ pAcc->GetPixel( nY, nX ).GetIndex() ] ); + pAcc->SetPixel( nY, nX, aCol ); + } + } + + delete[] pMap; + bRet = sal_True; + } + + if( pAcc ) + ReleaseAccess( pAcc ); + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Mirror( sal_uLong nMirrorFlags ) +{ + return Bitmap::Mirror( nMirrorFlags ); +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Scale( const Size& rNewSize, sal_uLong nScaleFlag ) +{ + sal_Bool bRet = Bitmap::Scale( rNewSize, nScaleFlag ); + + if( bRet && ( nScaleFlag == BMP_SCALE_INTERPOLATE ) ) + Bitmap::Convert( BMP_CONVERSION_8BIT_GREYS ); + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Scale( const double& rScaleX, const double& rScaleY, sal_uLong nScaleFlag ) +{ + sal_Bool bRet = Bitmap::Scale( rScaleX, rScaleY, nScaleFlag ); + + if( bRet && ( nScaleFlag == BMP_SCALE_INTERPOLATE ) ) + Bitmap::Convert( BMP_CONVERSION_8BIT_GREYS ); + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Rotate( long nAngle10, sal_uInt8 cFillTransparency ) +{ + return Bitmap::Rotate( nAngle10, Color( cFillTransparency, cFillTransparency, cFillTransparency ) ); +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Replace( const Bitmap& rMask, sal_uInt8 cReplaceTransparency ) +{ + BitmapReadAccess* pMaskAcc = ( (Bitmap&) rMask ).AcquireReadAccess(); + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pMaskAcc && pAcc ) + { + const BitmapColor aReplace( cReplaceTransparency ); + const long nWidth = Min( pMaskAcc->Width(), pAcc->Width() ); + const long nHeight = Min( pMaskAcc->Height(), pAcc->Height() ); + const BitmapColor aMaskWhite( pMaskAcc->GetBestMatchingColor( Color( COL_WHITE ) ) ); + + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX = 0L; nX < nWidth; nX++ ) + if( pMaskAcc->GetPixel( nY, nX ) == aMaskWhite ) + pAcc->SetPixel( nY, nX, aReplace ); + } + + ( (Bitmap&) rMask ).ReleaseAccess( pMaskAcc ); + ReleaseAccess( pAcc ); + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Replace( sal_uInt8 cSearchTransparency, sal_uInt8 cReplaceTransparency, sal_uLong +#ifdef DBG_UTIL +nTol +#endif +) +{ + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + DBG_ASSERT( !nTol, "AlphaMask::Replace: nTol not used yet" ); + + if( pAcc && pAcc->GetBitCount() == 8 ) + { + const long nWidth = pAcc->Width(), nHeight = pAcc->Height(); + + if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ) + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + Scanline pScan = pAcc->GetScanline( nY ); + + for( long nX = 0L; nX < nWidth; nX++, pScan++ ) + { + if( *pScan == cSearchTransparency ) + *pScan = cReplaceTransparency; + } + } + } + else + { + BitmapColor aReplace( cReplaceTransparency ); + + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + if( pAcc->GetPixel( nY, nX ).GetIndex() == cSearchTransparency ) + pAcc->SetPixel( nY, nX, aReplace ); + } + } + } + + bRet = sal_True; + } + + if( pAcc ) + ReleaseAccess( pAcc ); + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool AlphaMask::Replace( sal_uInt8* pSearchTransparencies, sal_uInt8* pReplaceTransparencies, + sal_uLong nColorCount, sal_uLong* pTols ) +{ + Color* pSearchColors = new Color[ nColorCount ]; + Color* pReplaceColors = new Color[ nColorCount ]; + sal_Bool bRet; + + for( sal_uLong i = 0; i < nColorCount; i++ ) + { + const sal_uInt8 cSearchTransparency = pSearchTransparencies[ i ]; + const sal_uInt8 cReplaceTransparency = pReplaceTransparencies[ i ]; + + pSearchColors[ i ] = Color( cSearchTransparency, cSearchTransparency, cSearchTransparency ); + pReplaceColors[ i ] = Color( cReplaceTransparency, cReplaceTransparency, cReplaceTransparency ); + } + + bRet = Bitmap::Replace( pSearchColors, pReplaceColors, nColorCount, pTols ) && + Bitmap::Convert( BMP_CONVERSION_8BIT_GREYS ); + + delete[] pSearchColors; + delete[] pReplaceColors; + + return bRet; +} + +// ----------------------------------------------------------------------------- + +void AlphaMask::ReleaseAccess( BitmapReadAccess* pAccess ) +{ + if( pAccess ) + { + Bitmap::ReleaseAccess( pAccess ); + Bitmap::Convert( BMP_CONVERSION_8BIT_GREYS ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/animate.cxx b/vcl/source/gdi/animate.cxx new file mode 100644 index 000000000000..63d0010bd5d2 --- /dev/null +++ b/vcl/source/gdi/animate.cxx @@ -0,0 +1,942 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#define ENABLE_BYTESTRING_STREAM_OPERATORS +#include <vcl/animate.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <rtl/crc.h> +#include <vcl/virdev.hxx> +#include <vcl/window.hxx> +#include <impanmvw.hxx> +DBG_NAME( Animation ) + +// ----------- +// - Defines - +// ----------- + +#define MIN_TIMEOUT 2L +#define INC_TIMEOUT 0L + +// ----------- +// - statics - +// ----------- + +sal_uLong Animation::mnAnimCount = 0UL; + +// ------------------- +// - AnimationBitmap - +// ------------------- + +sal_uLong AnimationBitmap::GetChecksum() const +{ + sal_uInt32 nCrc = aBmpEx.GetChecksum(); + SVBT32 aBT32; + + UInt32ToSVBT32( aPosPix.X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( aPosPix.Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( aSizePix.Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( aSizePix.Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( (long) nWait, aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( (long) eDisposal, aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( (long) bUserInput, aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + return nCrc; +} + +// ------------- +// - Animation - +// ------------- + +Animation::Animation() : + mnLoopCount ( 0 ), + mnLoops ( 0 ), + mnPos ( 0 ), + meCycleMode ( CYCLE_NORMAL ), + mbIsInAnimation ( sal_False ), + mbLoopTerminated ( sal_False ), + mbIsWaiting ( sal_False ) +{ + DBG_CTOR( Animation, NULL ); + maTimer.SetTimeoutHdl( LINK( this, Animation, ImplTimeoutHdl ) ); + mpViewList = new List; +} + +// ----------------------------------------------------------------------- + +Animation::Animation( const Animation& rAnimation ) : + maBitmapEx ( rAnimation.maBitmapEx ), + maGlobalSize ( rAnimation.maGlobalSize ), + mnLoopCount ( rAnimation.mnLoopCount ), + mnPos ( rAnimation.mnPos ), + meCycleMode ( rAnimation.meCycleMode ), + mbIsInAnimation ( sal_False ), + mbLoopTerminated ( rAnimation.mbLoopTerminated ), + mbIsWaiting ( rAnimation.mbIsWaiting ) +{ + DBG_CTOR( Animation, NULL ); + + for( long i = 0, nCount = rAnimation.maList.Count(); i < nCount; i++ ) + maList.Insert( new AnimationBitmap( *(AnimationBitmap*) rAnimation.maList.GetObject( i ) ), LIST_APPEND ); + + maTimer.SetTimeoutHdl( LINK( this, Animation, ImplTimeoutHdl ) ); + mpViewList = new List; + mnLoops = mbLoopTerminated ? 0 : mnLoopCount; +} + +// ----------------------------------------------------------------------- + +Animation::~Animation() +{ + DBG_DTOR( Animation, NULL ); + + if( mbIsInAnimation ) + Stop(); + + for( void* pStepBmp = maList.First(); pStepBmp; pStepBmp = maList.Next() ) + delete (AnimationBitmap*) pStepBmp; + + for( void* pView = mpViewList->First(); pView; pView = mpViewList->Next() ) + delete (ImplAnimView*) pView; + + delete mpViewList; +} + +// ----------------------------------------------------------------------- + +Animation& Animation::operator=( const Animation& rAnimation ) +{ + Clear(); + + for( long i = 0, nCount = rAnimation.maList.Count(); i < nCount; i++ ) + maList.Insert( new AnimationBitmap( *(AnimationBitmap*) rAnimation.maList.GetObject( i ) ), LIST_APPEND ); + + maGlobalSize = rAnimation.maGlobalSize; + maBitmapEx = rAnimation.maBitmapEx; + meCycleMode = rAnimation.meCycleMode; + mnLoopCount = rAnimation.mnLoopCount; + mnPos = rAnimation.mnPos; + mbLoopTerminated = rAnimation.mbLoopTerminated; + mbIsWaiting = rAnimation.mbIsWaiting; + mnLoops = mbLoopTerminated ? 0 : mnLoopCount; + + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::operator==( const Animation& rAnimation ) const +{ + const sal_uLong nCount = maList.Count(); + sal_Bool bRet = sal_False; + + if( rAnimation.maList.Count() == nCount && + rAnimation.maBitmapEx == maBitmapEx && + rAnimation.maGlobalSize == maGlobalSize && + rAnimation.meCycleMode == meCycleMode ) + { + bRet = sal_True; + + for( sal_uLong n = 0; n < nCount; n++ ) + { + if( ( *(AnimationBitmap*) maList.GetObject( n ) ) != + ( *(AnimationBitmap*) rAnimation.maList.GetObject( n ) ) ) + { + bRet = sal_False; + break; + } + } + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Animation::IsEqual( const Animation& rAnimation ) const +{ + const sal_uLong nCount = maList.Count(); + sal_Bool bRet = sal_False; + + if( rAnimation.maList.Count() == nCount && + rAnimation.maBitmapEx.IsEqual( maBitmapEx ) && + rAnimation.maGlobalSize == maGlobalSize && + rAnimation.meCycleMode == meCycleMode ) + { + for( sal_uLong n = 0; ( n < nCount ) && !bRet; n++ ) + if( ( (AnimationBitmap*) maList.GetObject( n ) )->IsEqual( *(AnimationBitmap*) rAnimation.maList.GetObject( n ) ) ) + bRet = sal_True; + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Animation::IsEmpty() const +{ + return( maBitmapEx.IsEmpty() && !maList.Count() ); +} + +// ------------------------------------------------------------------ + +void Animation::SetEmpty() +{ + maTimer.Stop(); + mbIsInAnimation = sal_False; + maGlobalSize = Size(); + maBitmapEx.SetEmpty(); + + for( void* pStepBmp = maList.First(); pStepBmp; pStepBmp = maList.Next() ) + delete (AnimationBitmap*) pStepBmp; + maList.Clear(); + + for( void* pView = mpViewList->First(); pView; pView = mpViewList->Next() ) + delete (ImplAnimView*) pView; + mpViewList->Clear(); +} + +// ----------------------------------------------------------------------- + +void Animation::Clear() +{ + SetEmpty(); +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::IsTransparent() const +{ + Point aPoint; + Rectangle aRect( aPoint, maGlobalSize ); + sal_Bool bRet = sal_False; + + // Falls irgendein 'kleines' Bildchen durch den Hintergrund + // ersetzt werden soll, muessen wir 'transparent' sein, um + // richtig dargestellt zu werden, da die Appl. aus Optimierungsgruenden + // kein Invalidate auf nicht-transp. Grafiken ausfuehren + for( long i = 0, nCount = maList.Count(); i < nCount; i++ ) + { + const AnimationBitmap* pAnimBmp = (AnimationBitmap*) maList.GetObject( i ); + + if( DISPOSE_BACK == pAnimBmp->eDisposal && Rectangle( pAnimBmp->aPosPix, pAnimBmp->aSizePix ) != aRect ) + { + bRet = sal_True; + break; + } + } + + if( !bRet ) + bRet = maBitmapEx.IsTransparent(); + + return bRet; +} + +// ----------------------------------------------------------------------- + +sal_uLong Animation::GetSizeBytes() const +{ + sal_uLong nSizeBytes = GetBitmapEx().GetSizeBytes(); + + for( long i = 0, nCount = maList.Count(); i < nCount; i++ ) + { + const AnimationBitmap* pAnimBmp = (AnimationBitmap*) maList.GetObject( i ); + nSizeBytes += pAnimBmp->aBmpEx.GetSizeBytes(); + } + + return nSizeBytes; +} + +// ----------------------------------------------------------------------- + +sal_uLong Animation::GetChecksum() const +{ + SVBT32 aBT32; + sal_uInt32 nCrc = GetBitmapEx().GetChecksum(); + + UInt32ToSVBT32( maList.Count(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( maGlobalSize.Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( maGlobalSize.Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( (long) meCycleMode, aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + for( long i = 0, nCount = maList.Count(); i < nCount; i++ ) + { + UInt32ToSVBT32( ( (AnimationBitmap*) maList.GetObject( i ) )->GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + + return nCrc; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::Start( OutputDevice* pOut, const Point& rDestPt, long nExtraData, + OutputDevice* pFirstFrameOutDev ) +{ + return Start( pOut, rDestPt, pOut->PixelToLogic( maGlobalSize ), nExtraData, pFirstFrameOutDev ); +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::Start( OutputDevice* pOut, const Point& rDestPt, const Size& rDestSz, long nExtraData, + OutputDevice* pFirstFrameOutDev ) +{ + sal_Bool bRet = sal_False; + + if( maList.Count() ) + { + if( ( pOut->GetOutDevType() == OUTDEV_WINDOW ) && !mbLoopTerminated && + ( ANIMATION_TIMEOUT_ON_CLICK != ( (AnimationBitmap*) maList.GetObject( mnPos ) )->nWait ) ) + { + ImplAnimView* pView; + ImplAnimView* pMatch = NULL; + + for( pView = (ImplAnimView*) mpViewList->First(); pView; pView = (ImplAnimView*) mpViewList->Next() ) + { + if( pView->ImplMatches( pOut, nExtraData ) ) + { + if( pView->ImplGetOutPos() == rDestPt && + pView->ImplGetOutSizePix() == pOut->LogicToPixel( rDestSz ) ) + { + pView->ImplRepaint(); + pMatch = pView; + } + else + { + delete (ImplAnimView*) mpViewList->Remove( pView ); + pView = NULL; + } + + break; + } + } + + if( !mpViewList->Count() ) + { + maTimer.Stop(); + mbIsInAnimation = sal_False; + mnPos = 0UL; + } + + if( !pMatch ) + mpViewList->Insert( new ImplAnimView( this, pOut, rDestPt, rDestSz, nExtraData, pFirstFrameOutDev ), LIST_APPEND ); + + if( !mbIsInAnimation ) + { + ImplRestartTimer( ( (AnimationBitmap*) maList.GetObject( mnPos ) )->nWait ); + mbIsInAnimation = sal_True; + } + } + else + Draw( pOut, rDestPt, rDestSz ); + + bRet = sal_True; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +void Animation::Stop( OutputDevice* pOut, long nExtraData ) +{ + ImplAnimView* pView = (ImplAnimView*) mpViewList->First(); + + while( pView ) + { + if( pView->ImplMatches( pOut, nExtraData ) ) + { + delete (ImplAnimView*) mpViewList->Remove( pView ); + pView = (ImplAnimView*) mpViewList->GetCurObject(); + } + else + pView = (ImplAnimView*) mpViewList->Next(); + } + + if( !mpViewList->Count() ) + { + maTimer.Stop(); + mbIsInAnimation = sal_False; + } +} + +// ----------------------------------------------------------------------- + +void Animation::Draw( OutputDevice* pOut, const Point& rDestPt ) const +{ + Draw( pOut, rDestPt, pOut->PixelToLogic( maGlobalSize ) ); +} + +// ----------------------------------------------------------------------- + +void Animation::Draw( OutputDevice* pOut, const Point& rDestPt, const Size& rDestSz ) const +{ + const sal_uLong nCount = maList.Count(); + + if( nCount ) + { + AnimationBitmap* pObj = (AnimationBitmap*) maList.GetObject( Min( mnPos, (long) nCount - 1L ) ); + + if( pOut->GetConnectMetaFile() || ( pOut->GetOutDevType() == OUTDEV_PRINTER ) ) + ( (AnimationBitmap*) maList.GetObject( 0 ) )->aBmpEx.Draw( pOut, rDestPt, rDestSz ); + else if( ANIMATION_TIMEOUT_ON_CLICK == pObj->nWait ) + pObj->aBmpEx.Draw( pOut, rDestPt, rDestSz ); + else + { + const sal_uLong nOldPos = mnPos; + ( (Animation*) this )->mnPos = mbLoopTerminated ? ( nCount - 1UL ) : mnPos; + delete new ImplAnimView( (Animation*) this, pOut, rDestPt, rDestSz, 0 ); + ( (Animation*) this )->mnPos = nOldPos; + } + } +} + +// ----------------------------------------------------------------------- + +void Animation::ImplRestartTimer( sal_uLong nTimeout ) +{ + maTimer.SetTimeout( Max( nTimeout, (sal_uLong)(MIN_TIMEOUT + ( mnAnimCount - 1 ) * INC_TIMEOUT) ) * 10L ); + maTimer.Start(); +} + +// ----------------------------------------------------------------------- + +IMPL_LINK( Animation, ImplTimeoutHdl, Timer*, EMPTYARG ) +{ + const sal_uLong nAnimCount = maList.Count(); + + if( nAnimCount ) + { + ImplAnimView* pView; + sal_Bool bGlobalPause = sal_True; + + if( maNotifyLink.IsSet() ) + { + AInfo* pAInfo; + + // create AInfo-List + for( pView = (ImplAnimView*) mpViewList->First(); pView; pView = (ImplAnimView*) mpViewList->Next() ) + maAInfoList.Insert( pView->ImplCreateAInfo() ); + + maNotifyLink.Call( this ); + + // set view state from AInfo structure + for( pAInfo = (AInfo*) maAInfoList.First(); pAInfo; pAInfo = (AInfo*) maAInfoList.Next() ) + { + if( !pAInfo->pViewData ) + { + pView = new ImplAnimView( this, pAInfo->pOutDev, + pAInfo->aStartOrg, pAInfo->aStartSize, pAInfo->nExtraData ); + + mpViewList->Insert( pView, LIST_APPEND ); + } + else + pView = (ImplAnimView*) pAInfo->pViewData; + + pView->ImplPause( pAInfo->bPause ); + pView->ImplSetMarked( sal_True ); + } + + // delete AInfo structures + for( pAInfo = (AInfo*) maAInfoList.First(); pAInfo; pAInfo = (AInfo*) maAInfoList.Next() ) + delete (AInfo*) pAInfo; + maAInfoList.Clear(); + + // delete all unmarked views and reset marked state + pView = (ImplAnimView*) mpViewList->First(); + while( pView ) + { + if( !pView->ImplIsMarked() ) + { + delete (ImplAnimView*) mpViewList->Remove( pView ); + pView = (ImplAnimView*) mpViewList->GetCurObject(); + } + else + { + if( !pView->ImplIsPause() ) + bGlobalPause = sal_False; + + pView->ImplSetMarked( sal_False ); + pView = (ImplAnimView*) mpViewList->Next(); + } + } + } + else + bGlobalPause = sal_False; + + if( !mpViewList->Count() ) + Stop(); + else if( bGlobalPause ) + ImplRestartTimer( 10 ); + else + { + AnimationBitmap* pStepBmp = (AnimationBitmap*) maList.GetObject( ++mnPos ); + + if( !pStepBmp ) + { + if( mnLoops == 1 ) + { + Stop(); + mbLoopTerminated = sal_True; + mnPos = nAnimCount - 1UL; + maBitmapEx = ( (AnimationBitmap*) maList.GetObject( mnPos ) )->aBmpEx; + return 0L; + } + else + { + if( mnLoops ) + mnLoops--; + + mnPos = 0; + pStepBmp = (AnimationBitmap*) maList.GetObject( mnPos ); + } + } + + // Paint all views; after painting check, if view is + // marked; in this case remove view, because area of output + // lies out of display area of window; mark state is + // set from view itself + pView = (ImplAnimView*) mpViewList->First(); + while( pView ) + { + pView->ImplDraw( mnPos ); + + if( pView->ImplIsMarked() ) + { + delete (ImplAnimView*) mpViewList->Remove( pView ); + pView = (ImplAnimView*) mpViewList->GetCurObject(); + } + else + pView = (ImplAnimView*) mpViewList->Next(); + } + + // stop or restart timer + if( !mpViewList->Count() ) + Stop(); + else + ImplRestartTimer( pStepBmp->nWait ); + } + } + else + Stop(); + + return 0L; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::Insert( const AnimationBitmap& rStepBmp ) +{ + sal_Bool bRet = sal_False; + + if( !IsInAnimation() ) + { + Point aPoint; + Rectangle aGlobalRect( aPoint, maGlobalSize ); + + maGlobalSize = aGlobalRect.Union( Rectangle( rStepBmp.aPosPix, rStepBmp.aSizePix ) ).GetSize(); + maList.Insert( new AnimationBitmap( rStepBmp ), LIST_APPEND ); + + // zunaechst nehmen wir die erste BitmapEx als Ersatz-BitmapEx + if( maList.Count() == 1 ) + maBitmapEx = rStepBmp.aBmpEx; + + bRet = sal_True; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +const AnimationBitmap& Animation::Get( sal_uInt16 nAnimation ) const +{ + DBG_ASSERT( ( nAnimation < maList.Count() ), "No object at this position" ); + return *(AnimationBitmap*) maList.GetObject( nAnimation ); +} + +// ----------------------------------------------------------------------- + +void Animation::Replace( const AnimationBitmap& rNewAnimationBitmap, sal_uInt16 nAnimation ) +{ + DBG_ASSERT( ( nAnimation < maList.Count() ), "No object at this position" ); + + delete (AnimationBitmap*) maList.Replace( new AnimationBitmap( rNewAnimationBitmap ), nAnimation ); + + // Falls wir an erster Stelle einfuegen, + // muessen wir natuerlich auch, + // auch die Ersatzdarstellungs-BitmapEx + // aktualisieren; + if ( ( !nAnimation && ( !mbLoopTerminated || ( maList.Count() == 1 ) ) ) || + ( ( nAnimation == maList.Count() - 1 ) && mbLoopTerminated ) ) + { + maBitmapEx = rNewAnimationBitmap.aBmpEx; + } +} + +// ----------------------------------------------------------------------- + +void Animation::SetLoopCount( const sal_uLong nLoopCount ) +{ + mnLoopCount = nLoopCount; + ResetLoopCount(); +} + +// ----------------------------------------------------------------------- + +void Animation::ResetLoopCount() +{ + mnLoops = mnLoopCount; + mbLoopTerminated = sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::Convert( BmpConversion eConversion ) +{ + DBG_ASSERT( !IsInAnimation(), "Animation modified while it is animated" ); + + sal_Bool bRet; + + if( !IsInAnimation() && maList.Count() ) + { + bRet = sal_True; + + for( void* pStepBmp = maList.First(); pStepBmp && bRet; pStepBmp = maList.Next() ) + bRet = ( ( AnimationBitmap*) pStepBmp )->aBmpEx.Convert( eConversion ); + + maBitmapEx.Convert( eConversion ); + } + else + bRet = sal_False; + + return bRet; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::ReduceColors( sal_uInt16 nNewColorCount, BmpReduce eReduce ) +{ + DBG_ASSERT( !IsInAnimation(), "Animation modified while it is animated" ); + + sal_Bool bRet; + + if( !IsInAnimation() && maList.Count() ) + { + bRet = sal_True; + + for( void* pStepBmp = maList.First(); pStepBmp && bRet; pStepBmp = maList.Next() ) + bRet = ( ( AnimationBitmap*) pStepBmp )->aBmpEx.ReduceColors( nNewColorCount, eReduce ); + + maBitmapEx.ReduceColors( nNewColorCount, eReduce ); + } + else + bRet = sal_False; + + return bRet; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::Invert() +{ + DBG_ASSERT( !IsInAnimation(), "Animation modified while it is animated" ); + + sal_Bool bRet; + + if( !IsInAnimation() && maList.Count() ) + { + bRet = sal_True; + + for( void* pStepBmp = maList.First(); pStepBmp && bRet; pStepBmp = maList.Next() ) + bRet = ( ( AnimationBitmap*) pStepBmp )->aBmpEx.Invert(); + + maBitmapEx.Invert(); + } + else + bRet = sal_False; + + return bRet; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::Mirror( sal_uLong nMirrorFlags ) +{ + DBG_ASSERT( !IsInAnimation(), "Animation modified while it is animated" ); + + sal_Bool bRet; + + if( !IsInAnimation() && maList.Count() ) + { + bRet = sal_True; + + if( nMirrorFlags ) + { + for( AnimationBitmap* pStepBmp = (AnimationBitmap*) maList.First(); + pStepBmp && bRet; + pStepBmp = (AnimationBitmap*) maList.Next() ) + { + if( ( bRet = pStepBmp->aBmpEx.Mirror( nMirrorFlags ) ) == sal_True ) + { + if( nMirrorFlags & BMP_MIRROR_HORZ ) + pStepBmp->aPosPix.X() = maGlobalSize.Width() - pStepBmp->aPosPix.X() - pStepBmp->aSizePix.Width(); + + if( nMirrorFlags & BMP_MIRROR_VERT ) + pStepBmp->aPosPix.Y() = maGlobalSize.Height() - pStepBmp->aPosPix.Y() - pStepBmp->aSizePix.Height(); + } + } + + maBitmapEx.Mirror( nMirrorFlags ); + } + } + else + bRet = sal_False; + + return bRet; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::Dither( sal_uLong nDitherFlags ) +{ + DBG_ASSERT( !IsInAnimation(), "Animation modified while it is animated" ); + + sal_Bool bRet; + + if( !IsInAnimation() && maList.Count() ) + { + bRet = sal_True; + + for( void* pStepBmp = maList.First(); pStepBmp && bRet; pStepBmp = maList.Next() ) + bRet = ( ( AnimationBitmap*) pStepBmp )->aBmpEx.Dither( nDitherFlags ); + + maBitmapEx.Dither( nDitherFlags ); + } + else + bRet = sal_False; + + return bRet; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::Adjust( short nLuminancePercent, short nContrastPercent, + short nChannelRPercent, short nChannelGPercent, short nChannelBPercent, + double fGamma, sal_Bool bInvert ) +{ + DBG_ASSERT( !IsInAnimation(), "Animation modified while it is animated" ); + + sal_Bool bRet; + + if( !IsInAnimation() && maList.Count() ) + { + bRet = sal_True; + + for( void* pStepBmp = maList.First(); pStepBmp && bRet; pStepBmp = maList.Next() ) + { + bRet = ( ( AnimationBitmap*) pStepBmp )->aBmpEx.Adjust( nLuminancePercent, nContrastPercent, + nChannelRPercent, nChannelGPercent, nChannelBPercent, + fGamma, bInvert ); + } + + maBitmapEx.Adjust( nLuminancePercent, nContrastPercent, + nChannelRPercent, nChannelGPercent, nChannelBPercent, + fGamma, bInvert ); + } + else + bRet = sal_False; + + return bRet; +} + +// ----------------------------------------------------------------------- + +sal_Bool Animation::Filter( BmpFilter eFilter, const BmpFilterParam* pFilterParam, const Link* pProgress ) +{ + DBG_ASSERT( !IsInAnimation(), "Animation modified while it is animated" ); + + sal_Bool bRet; + + if( !IsInAnimation() && maList.Count() ) + { + bRet = sal_True; + + for( void* pStepBmp = maList.First(); pStepBmp && bRet; pStepBmp = maList.Next() ) + bRet = ( ( AnimationBitmap*) pStepBmp )->aBmpEx.Filter( eFilter, pFilterParam, pProgress ); + + maBitmapEx.Filter( eFilter, pFilterParam, pProgress ); + } + else + bRet = sal_False; + + return bRet; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const Animation& rAnimation ) +{ + const sal_uInt16 nCount = rAnimation.Count(); + + if( nCount ) + { + const ByteString aDummyStr; + const sal_uInt32 nDummy32 = 0UL; + + // Falls keine BitmapEx gesetzt wurde, schreiben wir + // einfach die erste Bitmap der Animation + if( !rAnimation.GetBitmapEx().GetBitmap() ) + rOStm << rAnimation.Get( 0 ).aBmpEx; + else + rOStm << rAnimation.GetBitmapEx(); + + // Kennung schreiben ( SDANIMA1 ) + rOStm << (sal_uInt32) 0x5344414e << (sal_uInt32) 0x494d4931; + + for( sal_uInt16 i = 0; i < nCount; i++ ) + { + const AnimationBitmap& rAnimBmp = rAnimation.Get( i ); + const sal_uInt16 nRest = nCount - i - 1; + + // AnimationBitmap schreiben + rOStm << rAnimBmp.aBmpEx; + rOStm << rAnimBmp.aPosPix; + rOStm << rAnimBmp.aSizePix; + rOStm << rAnimation.maGlobalSize; + rOStm << (sal_uInt16) ( ( ANIMATION_TIMEOUT_ON_CLICK == rAnimBmp.nWait ) ? 65535 : rAnimBmp.nWait ); + rOStm << (sal_uInt16) rAnimBmp.eDisposal; + rOStm << (sal_uInt8) rAnimBmp.bUserInput; + rOStm << (sal_uInt32) rAnimation.mnLoopCount; + rOStm << nDummy32; // unbenutzt + rOStm << nDummy32; // unbenutzt + rOStm << nDummy32; // unbenutzt + rOStm << aDummyStr; // unbenutzt + rOStm << nRest; // Anzahl der Strukturen, die noch _folgen_ + } + } + + return rOStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, Animation& rAnimation ) +{ + Bitmap aBmp; + sal_uLong nStmPos = rIStm.Tell(); + sal_uInt32 nAnimMagic1, nAnimMagic2; + sal_uInt16 nOldFormat = rIStm.GetNumberFormatInt(); + sal_Bool bReadAnimations = sal_False; + + rIStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + nStmPos = rIStm.Tell(); + rIStm >> nAnimMagic1 >> nAnimMagic2; + + rAnimation.Clear(); + + // Wenn die BitmapEx am Anfang schon gelesen + // wurde ( von Graphic ), koennen wir direkt die Animationsbitmaps einlesen + if( ( nAnimMagic1 == 0x5344414e ) && ( nAnimMagic2 == 0x494d4931 ) && !rIStm.GetError() ) + bReadAnimations = sal_True; + // ansonsten versuchen wir erstmal die Bitmap(-Ex) zu lesen + else + { + rIStm.Seek( nStmPos ); + rIStm >> rAnimation.maBitmapEx; + nStmPos = rIStm.Tell(); + rIStm >> nAnimMagic1 >> nAnimMagic2; + + if( ( nAnimMagic1 == 0x5344414e ) && ( nAnimMagic2 == 0x494d4931 ) && !rIStm.GetError() ) + bReadAnimations = sal_True; + else + rIStm.Seek( nStmPos ); + } + + // ggf. Animationsbitmaps lesen + if( bReadAnimations ) + { + AnimationBitmap aAnimBmp; + BitmapEx aBmpEx; + ByteString aDummyStr; + sal_uInt32 nTmp32; + sal_uInt16 nTmp16; + sal_uInt8 cTmp; + + do + { + rIStm >> aAnimBmp.aBmpEx; + rIStm >> aAnimBmp.aPosPix; + rIStm >> aAnimBmp.aSizePix; + rIStm >> rAnimation.maGlobalSize; + rIStm >> nTmp16; aAnimBmp.nWait = ( ( 65535 == nTmp16 ) ? ANIMATION_TIMEOUT_ON_CLICK : nTmp16 ); + rIStm >> nTmp16; aAnimBmp.eDisposal = ( Disposal) nTmp16; + rIStm >> cTmp; aAnimBmp.bUserInput = (sal_Bool) cTmp; + rIStm >> nTmp32; rAnimation.mnLoopCount = (sal_uInt16) nTmp32; + rIStm >> nTmp32; // unbenutzt + rIStm >> nTmp32; // unbenutzt + rIStm >> nTmp32; // unbenutzt + rIStm >> aDummyStr; // unbenutzt + rIStm >> nTmp16; // Rest zu lesen + + rAnimation.Insert( aAnimBmp ); + } + while( nTmp16 && !rIStm.GetError() ); + + rAnimation.ResetLoopCount(); + } + + rIStm.SetNumberFormatInt( nOldFormat ); + + return rIStm; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/base14.cxx b/vcl/source/gdi/base14.cxx new file mode 100644 index 000000000000..9912b3e91509 --- /dev/null +++ b/vcl/source/gdi/base14.cxx @@ -0,0 +1,691 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "pdfwriter_impl.hxx" +#include <rtl/strbuf.hxx> + +using namespace vcl; + +using ::rtl::OString; +using ::rtl::OStringBuffer; + +OString PDFWriterImpl::BuiltinFont::getNameObject() const +{ + OStringBuffer aBuf( 16 ); + aBuf.append( '/' ); + const char* pRun = m_pPSName; + + unsigned int nCopied = 0; + while( *pRun ) + { + if( *pRun >= 'A' && *pRun <= 'Z' ) + nCopied = 0; + if( nCopied++ < 2 ) + aBuf.append( *pRun ); + pRun++; + } + return aBuf.makeStringAndClear(); +} + +const PDFWriterImpl::BuiltinFont PDFWriterImpl::m_aBuiltinFonts[ 14 ] = { +{ "Courier", // family name + "Normal", // style + "Courier", // PSName + 629, -157, // ascend, descend + FAMILY_MODERN, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_FIXED, // pitch + WIDTH_NORMAL, // width type + WEIGHT_NORMAL, // weight type + ITALIC_NONE, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 600, 600, 600, 600, 600, 600, 600, 600, // 32 - 39 + 600, 600, 600, 600, 600, 600, 600, 600, // 40 - 47 + 600, 600, 600, 600, 600, 600, 600, 600, // 48 - 55 + 600, 600, 600, 600, 600, 600, 600, 600, // 56 - 63 + 600, 600, 600, 600, 600, 600, 600, 600, // 64 - 71 + 600, 600, 600, 600, 600, 600, 600, 600, // 72 - 79 + 600, 600, 600, 600, 600, 600, 600, 600, // 80 - 87 + 600, 600, 600, 600, 600, 600, 600, 600, // 88 - 95 + 600, 600, 600, 600, 600, 600, 600, 600, // 96 - 103 + 600, 600, 600, 600, 600, 600, 600, 600, // 104 - 111 + 600, 600, 600, 600, 600, 600, 600, 600, // 112 - 119 + 600, 600, 600, 600, 600, 600, 600, 0, // 120 - 127 + 600, 0, 600, 600, 600, 600, 600, 600, // 128 - 135 + 600, 600, 600, 600, 600, 0, 600, 0, // 136 - 143 + 0, 600, 600, 600, 600, 600, 600, 600, // 144 - 151 + 600, 600, 600, 600, 600, 0, 600, 600, // 152 - 159 + 600, 600, 600, 600, 600, 600, 600, 600, // 160 - 167 + 600, 600, 600, 600, 600, 600, 600, 600, // 168 - 175 + 600, 600, 600, 600, 600, 600, 600, 600, // 176 - 183 + 600, 600, 600, 600, 600, 600, 600, 600, // 184 - 191 + 600, 600, 600, 600, 600, 600, 600, 600, // 192 - 199 + 600, 600, 600, 600, 600, 600, 600, 600, // 200 - 207 + 600, 600, 600, 600, 600, 600, 600, 600, // 208 - 215 + 600, 600, 600, 600, 600, 600, 600, 600, // 216 - 223 + 600, 600, 600, 600, 600, 600, 600, 600, // 224 - 231 + 600, 600, 600, 600, 600, 600, 600, 600, // 232 - 239 + 600, 600, 600, 600, 600, 600, 600, 600, // 240 - 247 + 600, 600, 600, 600, 600, 600, 600, 600 // 248 - 255 + } +}, + +{ "Courier", // family name + "Italic", // style + "Courier-Oblique", // PSName + 629, -157, // ascend, descend + FAMILY_MODERN, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_FIXED, // pitch + WIDTH_NORMAL, // width type + WEIGHT_NORMAL, // weight type + ITALIC_NORMAL, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 600, 600, 600, 600, 600, 600, 600, 600, // 32 - 39 + 600, 600, 600, 600, 600, 600, 600, 600, // 40 - 47 + 600, 600, 600, 600, 600, 600, 600, 600, // 48 - 55 + 600, 600, 600, 600, 600, 600, 600, 600, // 56 - 63 + 600, 600, 600, 600, 600, 600, 600, 600, // 64 - 71 + 600, 600, 600, 600, 600, 600, 600, 600, // 72 - 79 + 600, 600, 600, 600, 600, 600, 600, 600, // 80 - 87 + 600, 600, 600, 600, 600, 600, 600, 600, // 88 - 95 + 600, 600, 600, 600, 600, 600, 600, 600, // 96 - 103 + 600, 600, 600, 600, 600, 600, 600, 600, // 104 - 111 + 600, 600, 600, 600, 600, 600, 600, 600, // 112 - 119 + 600, 600, 600, 600, 600, 600, 600, 0, // 120 - 127 + 600, 0, 600, 600, 600, 600, 600, 600, // 128 - 135 + 600, 600, 600, 600, 600, 0, 600, 0, // 136 - 143 + 0, 600, 600, 600, 600, 600, 600, 600, // 144 - 151 + 600, 600, 600, 600, 600, 0, 600, 600, // 152 - 159 + 600, 600, 600, 600, 600, 600, 600, 600, // 160 - 167 + 600, 600, 600, 600, 600, 600, 600, 600, // 168 - 175 + 600, 600, 600, 600, 600, 600, 600, 600, // 176 - 183 + 600, 600, 600, 600, 600, 600, 600, 600, // 184 - 191 + 600, 600, 600, 600, 600, 600, 600, 600, // 192 - 199 + 600, 600, 600, 600, 600, 600, 600, 600, // 200 - 207 + 600, 600, 600, 600, 600, 600, 600, 600, // 208 - 215 + 600, 600, 600, 600, 600, 600, 600, 600, // 216 - 223 + 600, 600, 600, 600, 600, 600, 600, 600, // 224 - 231 + 600, 600, 600, 600, 600, 600, 600, 600, // 232 - 239 + 600, 600, 600, 600, 600, 600, 600, 600, // 240 - 247 + 600, 600, 600, 600, 600, 600, 600, 600 // 248 - 255 + } +}, + +{ "Courier", // family name + "Bold", // style + "Courier-Bold", // PSName + 629, -157, // ascend, descend + FAMILY_MODERN, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_FIXED, // pitch + WIDTH_NORMAL, // width type + WEIGHT_BOLD, // weight type + ITALIC_NONE, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 600, 600, 600, 600, 600, 600, 600, 600, // 32 - 39 + 600, 600, 600, 600, 600, 600, 600, 600, // 40 - 47 + 600, 600, 600, 600, 600, 600, 600, 600, // 48 - 55 + 600, 600, 600, 600, 600, 600, 600, 600, // 56 - 63 + 600, 600, 600, 600, 600, 600, 600, 600, // 64 - 71 + 600, 600, 600, 600, 600, 600, 600, 600, // 72 - 79 + 600, 600, 600, 600, 600, 600, 600, 600, // 80 - 87 + 600, 600, 600, 600, 600, 600, 600, 600, // 88 - 95 + 600, 600, 600, 600, 600, 600, 600, 600, // 96 - 103 + 600, 600, 600, 600, 600, 600, 600, 600, // 104 - 111 + 600, 600, 600, 600, 600, 600, 600, 600, // 112 - 119 + 600, 600, 600, 600, 600, 600, 600, 0, // 120 - 127 + 600, 0, 600, 600, 600, 600, 600, 600, // 128 - 135 + 600, 600, 600, 600, 600, 0, 600, 0, // 136 - 143 + 0, 600, 600, 600, 600, 600, 600, 600, // 144 - 151 + 600, 600, 600, 600, 600, 0, 600, 600, // 152 - 159 + 600, 600, 600, 600, 600, 600, 600, 600, // 160 - 167 + 600, 600, 600, 600, 600, 600, 600, 600, // 168 - 175 + 600, 600, 600, 600, 600, 600, 600, 600, // 176 - 183 + 600, 600, 600, 600, 600, 600, 600, 600, // 184 - 191 + 600, 600, 600, 600, 600, 600, 600, 600, // 192 - 199 + 600, 600, 600, 600, 600, 600, 600, 600, // 200 - 207 + 600, 600, 600, 600, 600, 600, 600, 600, // 208 - 215 + 600, 600, 600, 600, 600, 600, 600, 600, // 216 - 223 + 600, 600, 600, 600, 600, 600, 600, 600, // 224 - 231 + 600, 600, 600, 600, 600, 600, 600, 600, // 232 - 239 + 600, 600, 600, 600, 600, 600, 600, 600, // 240 - 247 + 600, 600, 600, 600, 600, 600, 600, 600 // 248 - 255 + } +}, + +{ "Courier", // family name + "Bold Italic", // style + "Courier-BoldOblique", // PSName + 629, -157, // ascend, descend + FAMILY_MODERN, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_FIXED, // pitch + WIDTH_NORMAL, // width type + WEIGHT_BOLD, // weight type + ITALIC_NORMAL, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 600, 600, 600, 600, 600, 600, 600, 600, // 32 - 39 + 600, 600, 600, 600, 600, 600, 600, 600, // 40 - 47 + 600, 600, 600, 600, 600, 600, 600, 600, // 48 - 55 + 600, 600, 600, 600, 600, 600, 600, 600, // 56 - 63 + 600, 600, 600, 600, 600, 600, 600, 600, // 64 - 71 + 600, 600, 600, 600, 600, 600, 600, 600, // 72 - 79 + 600, 600, 600, 600, 600, 600, 600, 600, // 80 - 87 + 600, 600, 600, 600, 600, 600, 600, 600, // 88 - 95 + 600, 600, 600, 600, 600, 600, 600, 600, // 96 - 103 + 600, 600, 600, 600, 600, 600, 600, 600, // 104 - 111 + 600, 600, 600, 600, 600, 600, 600, 600, // 112 - 119 + 600, 600, 600, 600, 600, 600, 600, 0, // 120 - 127 + 600, 0, 600, 600, 600, 600, 600, 600, // 128 - 135 + 600, 600, 600, 600, 600, 0, 600, 0, // 136 - 143 + 0, 600, 600, 600, 600, 600, 600, 600, // 144 - 151 + 600, 600, 600, 600, 600, 0, 600, 600, // 152 - 159 + 600, 600, 600, 600, 600, 600, 600, 600, // 160 - 167 + 600, 600, 600, 600, 600, 600, 600, 600, // 168 - 175 + 600, 600, 600, 600, 600, 600, 600, 600, // 176 - 183 + 600, 600, 600, 600, 600, 600, 600, 600, // 184 - 191 + 600, 600, 600, 600, 600, 600, 600, 600, // 192 - 199 + 600, 600, 600, 600, 600, 600, 600, 600, // 200 - 207 + 600, 600, 600, 600, 600, 600, 600, 600, // 208 - 215 + 600, 600, 600, 600, 600, 600, 600, 600, // 216 - 223 + 600, 600, 600, 600, 600, 600, 600, 600, // 224 - 231 + 600, 600, 600, 600, 600, 600, 600, 600, // 232 - 239 + 600, 600, 600, 600, 600, 600, 600, 600, // 240 - 247 + 600, 600, 600, 600, 600, 600, 600, 600 // 248 - 255 + } +}, + +{ "Helvetica", // family name + "Normal", // style + "Helvetica", // PSName + 718, -207, // ascend, descend + FAMILY_SWISS, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_NORMAL, // weight type + ITALIC_NONE, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 278, 278, 355, 556, 556, 889, 667, 191, // 32 - 39 + 333, 333, 389, 584, 278, 333, 278, 278, // 40 - 47 + 556, 556, 556, 556, 556, 556, 556, 556, // 48 - 55 + 556, 556, 278, 278, 584, 584, 584, 556, // 56 - 63 + 1015, 667, 667, 722, 722, 667, 611, 778, // 64 - 71 + 722, 278, 500, 667, 556, 833, 722, 778, // 72 - 79 + 667, 778, 722, 667, 611, 722, 667, 944, // 80 - 87 + 667, 667, 611, 278, 278, 278, 469, 556, // 88 - 95 + 333, 556, 556, 500, 556, 556, 278, 556, // 96 - 103 + 556, 222, 222, 500, 222, 833, 556, 556, // 104 - 111 + 556, 556, 333, 500, 278, 556, 500, 722, // 112 - 119 + 500, 500, 500, 334, 260, 334, 584, 0, // 120 - 127 + 556, 0, 222, 556, 333, 1000, 556, 556, // 128 - 135 + 333, 1000, 667, 333, 1000, 0, 500, 0, // 136 - 143 + 0, 222, 222, 333, 333, 350, 556, 1000, // 144 - 151 + 333, 1000, 500, 333, 944, 0, 500, 667, // 152 - 159 + 278, 333, 556, 556, 556, 556, 260, 556, // 160 - 167 + 333, 737, 370, 556, 584, 333, 737, 333, // 168 - 175 + 400, 584, 333, 333, 333, 556, 537, 278, // 176 - 183 + 333, 333, 365, 556, 834, 834, 834, 611, // 184 - 191 + 667, 667, 667, 667, 667, 667, 1000, 722, // 192 - 199 + 667, 667, 667, 667, 278, 278, 278, 278, // 200 - 207 + 722, 722, 778, 778, 778, 778, 778, 584, // 208 - 215 + 778, 722, 722, 722, 722, 667, 667, 611, // 216 - 223 + 556, 556, 556, 556, 556, 556, 889, 500, // 224 - 231 + 556, 556, 556, 556, 278, 278, 278, 278, // 232 - 239 + 556, 556, 556, 556, 556, 556, 556, 584, // 240 - 247 + 611, 556, 556, 556, 556, 500, 556, 500 // 248 - 255 + } +}, + +{ "Helvetica", // family name + "Italic", // style + "Helvetica-Oblique", // PSName + 718, -207, // ascend, descend + FAMILY_SWISS, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_NORMAL, // weight type + ITALIC_NORMAL, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 278, 278, 355, 556, 556, 889, 667, 191, // 32 - 39 + 333, 333, 389, 584, 278, 333, 278, 278, // 40 - 47 + 556, 556, 556, 556, 556, 556, 556, 556, // 48 - 55 + 556, 556, 278, 278, 584, 584, 584, 556, // 56 - 63 + 1015, 667, 667, 722, 722, 667, 611, 778, // 64 - 71 + 722, 278, 500, 667, 556, 833, 722, 778, // 72 - 79 + 667, 778, 722, 667, 611, 722, 667, 944, // 80 - 87 + 667, 667, 611, 278, 278, 278, 469, 556, // 88 - 95 + 333, 556, 556, 500, 556, 556, 278, 556, // 96 - 103 + 556, 222, 222, 500, 222, 833, 556, 556, // 104 - 111 + 556, 556, 333, 500, 278, 556, 500, 722, // 112 - 119 + 500, 500, 500, 334, 260, 334, 584, 0, // 120 - 127 + 556, 0, 222, 556, 333, 1000, 556, 556, // 128 - 135 + 333, 1000, 667, 333, 1000, 0, 500, 0, // 136 - 143 + 0, 222, 222, 333, 333, 350, 556, 1000, // 144 - 151 + 333, 1000, 500, 333, 944, 0, 500, 667, // 152 - 159 + 278, 333, 556, 556, 556, 556, 260, 556, // 160 - 167 + 333, 737, 370, 556, 584, 333, 737, 333, // 168 - 175 + 400, 584, 333, 333, 333, 556, 537, 278, // 176 - 183 + 333, 333, 365, 556, 834, 834, 834, 611, // 184 - 191 + 667, 667, 667, 667, 667, 667, 1000, 722, // 192 - 199 + 667, 667, 667, 667, 278, 278, 278, 278, // 200 - 207 + 722, 722, 778, 778, 778, 778, 778, 584, // 208 - 215 + 778, 722, 722, 722, 722, 667, 667, 611, // 216 - 223 + 556, 556, 556, 556, 556, 556, 889, 500, // 224 - 231 + 556, 556, 556, 556, 278, 278, 278, 278, // 232 - 239 + 556, 556, 556, 556, 556, 556, 556, 584, // 240 - 247 + 611, 556, 556, 556, 556, 500, 556, 500 // 248 - 255 + } +}, + +{ "Helvetica", // family name + "Bold", // style + "Helvetica-Bold", // PSName + 718, -207, // ascend, descend + FAMILY_SWISS, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_BOLD, // weight type + ITALIC_NONE, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 278, 333, 474, 556, 556, 889, 722, 238, // 32 - 39 + 333, 333, 389, 584, 278, 333, 278, 278, // 40 - 47 + 556, 556, 556, 556, 556, 556, 556, 556, // 48 - 55 + 556, 556, 333, 333, 584, 584, 584, 611, // 56 - 63 + 975, 722, 722, 722, 722, 667, 611, 778, // 64 - 71 + 722, 278, 556, 722, 611, 833, 722, 778, // 72 - 79 + 667, 778, 722, 667, 611, 722, 667, 944, // 80 - 87 + 667, 667, 611, 333, 278, 333, 584, 556, // 88 - 95 + 333, 556, 611, 556, 611, 556, 333, 611, // 96 - 103 + 611, 278, 278, 556, 278, 889, 611, 611, // 104 - 111 + 611, 611, 389, 556, 333, 611, 556, 778, // 112 - 119 + 556, 556, 500, 389, 280, 389, 584, 0, // 120 - 127 + 556, 0, 278, 556, 500, 1000, 556, 556, // 128 - 135 + 333, 1000, 667, 333, 1000, 0, 500, 0, // 136 - 143 + 0, 278, 278, 500, 500, 350, 556, 1000, // 144 - 151 + 333, 1000, 556, 333, 944, 0, 500, 667, // 152 - 159 + 278, 333, 556, 556, 556, 556, 280, 556, // 160 - 167 + 333, 737, 370, 556, 584, 333, 737, 333, // 168 - 175 + 400, 584, 333, 333, 333, 611, 556, 278, // 176 - 183 + 333, 333, 365, 556, 834, 834, 834, 611, // 184 - 191 + 722, 722, 722, 722, 722, 722, 1000, 722, // 192 - 199 + 667, 667, 667, 667, 278, 278, 278, 278, // 200 - 207 + 722, 722, 778, 778, 778, 778, 778, 584, // 208 - 215 + 778, 722, 722, 722, 722, 667, 667, 611, // 216 - 223 + 556, 556, 556, 556, 556, 556, 889, 556, // 224 - 231 + 556, 556, 556, 556, 278, 278, 278, 278, // 232 - 239 + 611, 611, 611, 611, 611, 611, 611, 584, // 240 - 247 + 611, 611, 611, 611, 611, 556, 611, 556 // 248 - 255 + } +}, + +{ "Helvetica", // family name + "Bold Italic", // style + "Helvetica-BoldOblique", // PSName + 718, -207, // ascend, descend + FAMILY_SWISS, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_BOLD, // weight type + ITALIC_NORMAL, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 278, 333, 474, 556, 556, 889, 722, 238, // 32 - 39 + 333, 333, 389, 584, 278, 333, 278, 278, // 40 - 47 + 556, 556, 556, 556, 556, 556, 556, 556, // 48 - 55 + 556, 556, 333, 333, 584, 584, 584, 611, // 56 - 63 + 975, 722, 722, 722, 722, 667, 611, 778, // 64 - 71 + 722, 278, 556, 722, 611, 833, 722, 778, // 72 - 79 + 667, 778, 722, 667, 611, 722, 667, 944, // 80 - 87 + 667, 667, 611, 333, 278, 333, 584, 556, // 88 - 95 + 333, 556, 611, 556, 611, 556, 333, 611, // 96 - 103 + 611, 278, 278, 556, 278, 889, 611, 611, // 104 - 111 + 611, 611, 389, 556, 333, 611, 556, 778, // 112 - 119 + 556, 556, 500, 389, 280, 389, 584, 0, // 120 - 127 + 556, 0, 278, 556, 500, 1000, 556, 556, // 128 - 135 + 333, 1000, 667, 333, 1000, 0, 500, 0, // 136 - 143 + 0, 278, 278, 500, 500, 350, 556, 1000, // 144 - 151 + 333, 1000, 556, 333, 944, 0, 500, 667, // 152 - 159 + 278, 333, 556, 556, 556, 556, 280, 556, // 160 - 167 + 333, 737, 370, 556, 584, 333, 737, 333, // 168 - 175 + 400, 584, 333, 333, 333, 611, 556, 278, // 176 - 183 + 333, 333, 365, 556, 834, 834, 834, 611, // 184 - 191 + 722, 722, 722, 722, 722, 722, 1000, 722, // 192 - 199 + 667, 667, 667, 667, 278, 278, 278, 278, // 200 - 207 + 722, 722, 778, 778, 778, 778, 778, 584, // 208 - 215 + 778, 722, 722, 722, 722, 667, 667, 611, // 216 - 223 + 556, 556, 556, 556, 556, 556, 889, 556, // 224 - 231 + 556, 556, 556, 556, 278, 278, 278, 278, // 232 - 239 + 611, 611, 611, 611, 611, 611, 611, 584, // 240 - 247 + 611, 611, 611, 611, 611, 556, 611, 556 // 248 - 255 + } +}, + +{ "Times", // family name + "Normal", // style + "Times-Roman", // PSName + 683, -217, // ascend, descend + FAMILY_ROMAN, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_NORMAL, // weight type + ITALIC_NONE, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 250, 333, 408, 500, 500, 833, 778, 180, // 32 - 39 + 333, 333, 500, 564, 250, 333, 250, 278, // 40 - 47 + 500, 500, 500, 500, 500, 500, 500, 500, // 48 - 55 + 500, 500, 278, 278, 564, 564, 564, 444, // 56 - 63 + 921, 722, 667, 667, 722, 611, 556, 722, // 64 - 71 + 722, 333, 389, 722, 611, 889, 722, 722, // 72 - 79 + 556, 722, 667, 556, 611, 722, 722, 944, // 80 - 87 + 722, 722, 611, 333, 278, 333, 469, 500, // 88 - 95 + 333, 444, 500, 444, 500, 444, 333, 500, // 96 - 103 + 500, 278, 278, 500, 278, 778, 500, 500, // 104 - 111 + 500, 500, 333, 389, 278, 500, 500, 722, // 112 - 119 + 500, 500, 444, 480, 200, 480, 541, 0, // 120 - 127 + 500, 0, 333, 500, 444, 1000, 500, 500, // 128 - 135 + 333, 1000, 556, 333, 889, 0, 444, 0, // 136 - 143 + 0, 333, 333, 444, 444, 350, 500, 1000, // 144 - 151 + 333, 980, 389, 333, 722, 0, 444, 722, // 152 - 159 + 250, 333, 500, 500, 500, 500, 200, 500, // 160 - 167 + 333, 760, 276, 500, 564, 333, 760, 333, // 168 - 175 + 400, 564, 300, 300, 333, 500, 453, 250, // 176 - 183 + 333, 300, 310, 500, 750, 750, 750, 444, // 184 - 191 + 722, 722, 722, 722, 722, 722, 889, 667, // 192 - 199 + 611, 611, 611, 611, 333, 333, 333, 333, // 200 - 207 + 722, 722, 722, 722, 722, 722, 722, 564, // 208 - 215 + 722, 722, 722, 722, 722, 722, 556, 500, // 216 - 223 + 444, 444, 444, 444, 444, 444, 667, 444, // 224 - 231 + 444, 444, 444, 444, 278, 278, 278, 278, // 232 - 239 + 500, 500, 500, 500, 500, 500, 500, 564, // 240 - 247 + 500, 500, 500, 500, 500, 500, 500, 500 // 248 - 255 + } +}, + +{ "Times", // family name + "Italic", // style + "Times-Italic", // PSName + 683, -217, // ascend, descend + FAMILY_ROMAN, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_NORMAL, // weight type + ITALIC_NORMAL, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 250, 333, 420, 500, 500, 833, 778, 214, // 32 - 39 + 333, 333, 500, 675, 250, 333, 250, 278, // 40 - 47 + 500, 500, 500, 500, 500, 500, 500, 500, // 48 - 55 + 500, 500, 333, 333, 675, 675, 675, 500, // 56 - 63 + 920, 611, 611, 667, 722, 611, 611, 722, // 64 - 71 + 722, 333, 444, 667, 556, 833, 667, 722, // 72 - 79 + 611, 722, 611, 500, 556, 722, 611, 833, // 80 - 87 + 611, 556, 556, 389, 278, 389, 422, 500, // 88 - 95 + 333, 500, 500, 444, 500, 444, 278, 500, // 96 - 103 + 500, 278, 278, 444, 278, 722, 500, 500, // 104 - 111 + 500, 500, 389, 389, 278, 500, 444, 667, // 112 - 119 + 444, 444, 389, 400, 275, 400, 541, 0, // 120 - 127 + 500, 0, 333, 500, 556, 889, 500, 500, // 128 - 135 + 333, 1000, 500, 333, 944, 0, 389, 0, // 136 - 143 + 0, 333, 333, 556, 556, 350, 500, 889, // 144 - 151 + 333, 980, 389, 333, 667, 0, 389, 556, // 152 - 159 + 250, 389, 500, 500, 500, 500, 275, 500, // 160 - 167 + 333, 760, 276, 500, 675, 333, 760, 333, // 168 - 175 + 400, 675, 300, 300, 333, 500, 523, 250, // 176 - 183 + 333, 300, 310, 500, 750, 750, 750, 500, // 184 - 191 + 611, 611, 611, 611, 611, 611, 889, 667, // 192 - 199 + 611, 611, 611, 611, 333, 333, 333, 333, // 200 - 207 + 722, 667, 722, 722, 722, 722, 722, 675, // 208 - 215 + 722, 722, 722, 722, 722, 556, 611, 500, // 216 - 223 + 500, 500, 500, 500, 500, 500, 667, 444, // 224 - 231 + 444, 444, 444, 444, 278, 278, 278, 278, // 232 - 239 + 500, 500, 500, 500, 500, 500, 500, 675, // 240 - 247 + 500, 500, 500, 500, 500, 444, 500, 444 // 248 - 255 + } +}, + +{ "Times", // family name + "Bold", // style + "Times-Bold", // PSName + 683, -217, // ascend, descend + FAMILY_ROMAN, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_BOLD, // weight type + ITALIC_NONE, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 250, 333, 555, 500, 500, 1000, 833, 278, // 32 - 39 + 333, 333, 500, 570, 250, 333, 250, 278, // 40 - 47 + 500, 500, 500, 500, 500, 500, 500, 500, // 48 - 55 + 500, 500, 333, 333, 570, 570, 570, 500, // 56 - 63 + 930, 722, 667, 722, 722, 667, 611, 778, // 64 - 71 + 778, 389, 500, 778, 667, 944, 722, 778, // 72 - 79 + 611, 778, 722, 556, 667, 722, 722, 1000, // 80 - 87 + 722, 722, 667, 333, 278, 333, 581, 500, // 88 - 95 + 333, 500, 556, 444, 556, 444, 333, 500, // 96 - 103 + 556, 278, 333, 556, 278, 833, 556, 500, // 104 - 111 + 556, 556, 444, 389, 333, 556, 500, 722, // 112 - 119 + 500, 500, 444, 394, 220, 394, 520, 0, // 120 - 127 + 500, 0, 333, 500, 500, 1000, 500, 500, // 128 - 135 + 333, 1000, 556, 333, 1000, 0, 444, 0, // 136 - 143 + 0, 333, 333, 500, 500, 350, 500, 1000, // 144 - 151 + 333, 1000, 389, 333, 722, 0, 444, 722, // 152 - 159 + 250, 333, 500, 500, 500, 500, 220, 500, // 160 - 167 + 333, 747, 300, 500, 570, 333, 747, 333, // 168 - 175 + 400, 570, 300, 300, 333, 556, 540, 250, // 176 - 183 + 333, 300, 330, 500, 750, 750, 750, 500, // 184 - 191 + 722, 722, 722, 722, 722, 722, 1000, 722, // 192 - 199 + 667, 667, 667, 667, 389, 389, 389, 389, // 200 - 207 + 722, 722, 778, 778, 778, 778, 778, 570, // 208 - 215 + 778, 722, 722, 722, 722, 722, 611, 556, // 216 - 223 + 500, 500, 500, 500, 500, 500, 722, 444, // 224 - 231 + 444, 444, 444, 444, 278, 278, 278, 278, // 232 - 239 + 500, 556, 500, 500, 500, 500, 500, 570, // 240 - 247 + 500, 556, 556, 556, 556, 500, 556, 500 // 248 - 255 + } +}, + +{ "Times", // family name + "Bold Italic", // style + "Times-BoldItalic", // PSName + 683, -217, // ascend, descend + FAMILY_ROMAN, // family style + RTL_TEXTENCODING_MS_1252, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_BOLD, // weight type + ITALIC_NORMAL, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 250, 389, 555, 500, 500, 833, 778, 278, // 32 - 39 + 333, 333, 500, 570, 250, 333, 250, 278, // 40 - 47 + 500, 500, 500, 500, 500, 500, 500, 500, // 48 - 55 + 500, 500, 333, 333, 570, 570, 570, 500, // 56 - 63 + 832, 667, 667, 667, 722, 667, 667, 722, // 64 - 71 + 778, 389, 500, 667, 611, 889, 722, 722, // 72 - 79 + 611, 722, 667, 556, 611, 722, 667, 889, // 80 - 87 + 667, 611, 611, 333, 278, 333, 570, 500, // 88 - 95 + 333, 500, 500, 444, 500, 444, 333, 500, // 96 - 103 + 556, 278, 278, 500, 278, 778, 556, 500, // 104 - 111 + 500, 500, 389, 389, 278, 556, 444, 667, // 112 - 119 + 500, 444, 389, 348, 220, 348, 570, 0, // 120 - 127 + 500, 0, 333, 500, 500, 1000, 500, 500, // 128 - 135 + 333, 1000, 556, 333, 944, 0, 389, 0, // 136 - 143 + 0, 333, 333, 500, 500, 350, 500, 1000, // 144 - 151 + 333, 1000, 389, 333, 722, 0, 389, 611, // 152 - 159 + 250, 389, 500, 500, 500, 500, 220, 500, // 160 - 167 + 333, 747, 266, 500, 606, 333, 747, 333, // 168 - 175 + 400, 570, 300, 300, 333, 576, 500, 250, // 176 - 183 + 333, 300, 300, 500, 750, 750, 750, 500, // 184 - 191 + 667, 667, 667, 667, 667, 667, 944, 667, // 192 - 199 + 667, 667, 667, 667, 389, 389, 389, 389, // 200 - 207 + 722, 722, 722, 722, 722, 722, 722, 570, // 208 - 215 + 722, 722, 722, 722, 722, 611, 611, 500, // 216 - 223 + 500, 500, 500, 500, 500, 500, 722, 444, // 224 - 231 + 444, 444, 444, 444, 278, 278, 278, 278, // 232 - 239 + 500, 556, 500, 500, 500, 500, 500, 570, // 240 - 247 + 500, 556, 556, 556, 556, 444, 500, 444 // 248 - 255 + } +}, + +{ "Symbol", // family name + "Normal", // style + "Symbol", // PSName + 1010, -293, // ascend, descend + FAMILY_DONTKNOW, // family style + RTL_TEXTENCODING_ADOBE_SYMBOL, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_NORMAL, // weight type + ITALIC_NONE, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 250, 333, 713, 500, 549, 833, 778, 439, // 32 - 39 + 333, 333, 500, 549, 250, 549, 250, 278, // 40 - 47 + 500, 500, 500, 500, 500, 500, 500, 500, // 48 - 55 + 500, 500, 278, 278, 549, 549, 549, 444, // 56 - 63 + 549, 722, 667, 722, 612, 611, 763, 603, // 64 - 71 + 722, 333, 631, 722, 686, 889, 722, 722, // 72 - 79 + 768, 741, 556, 592, 611, 690, 439, 768, // 80 - 87 + 645, 795, 611, 333, 863, 333, 658, 500, // 88 - 95 + 500, 631, 549, 549, 494, 439, 521, 411, // 96 - 103 + 603, 329, 603, 549, 549, 576, 521, 549, // 104 - 111 + 549, 521, 549, 603, 439, 576, 713, 686, // 112 - 119 + 493, 686, 494, 480, 200, 480, 549, 0, // 120 - 127 + 0, 0, 0, 0, 0, 0, 0, 0, // 128 - 135 + 0, 0, 0, 0, 0, 0, 0, 0, // 136 - 143 + 0, 0, 0, 0, 0, 0, 0, 0, // 144 - 151 + 0, 0, 0, 0, 0, 0, 0, 0, // 152 - 159 + 750, 620, 247, 549, 167, 713, 500, 753, // 160 - 167 + 753, 753, 753, 1042, 987, 603, 987, 603, // 168 - 175 + 400, 549, 411, 549, 549, 713, 494, 460, // 176 - 183 + 549, 549, 549, 549, 1000, 603, 1000, 658, // 184 - 191 + 823, 686, 795, 987, 768, 768, 823, 768, // 192 - 199 + 768, 713, 713, 713, 713, 713, 713, 713, // 200 - 207 + 768, 713, 790, 790, 890, 823, 549, 250, // 208 - 215 + 713, 603, 603, 1042, 987, 603, 987, 603, // 216 - 223 + 494, 329, 790, 790, 786, 713, 384, 384, // 224 - 231 + 384, 384, 384, 384, 494, 494, 494, 494, // 232 - 239 + 0, 329, 274, 686, 686, 686, 384, 384, // 240 - 247 + 384, 384, 384, 384, 494, 494, 494, 0 // 248 - 255 + } +}, + +{ "ZapfDingbats", // family name + "Normal", // style + "ZapfDingbats", // PSName + 820, -143, // ascend, descend + FAMILY_DONTKNOW, // family style + RTL_TEXTENCODING_ADOBE_DINGBATS, // charset + PITCH_VARIABLE, // pitch + WIDTH_NORMAL, // width type + WEIGHT_NORMAL, // weight type + ITALIC_NONE, // italic type + { 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 + 278, 974, 961, 974, 980, 719, 789, 790, // 32 - 39 + 791, 690, 960, 939, 549, 855, 911, 933, // 40 - 47 + 911, 945, 974, 755, 846, 762, 761, 571, // 48 - 55 + 677, 763, 760, 759, 754, 494, 552, 537, // 56 - 63 + 577, 692, 786, 788, 788, 790, 793, 794, // 64 - 71 + 816, 823, 789, 841, 823, 833, 816, 831, // 72 - 79 + 923, 744, 723, 749, 790, 792, 695, 776, // 80 - 87 + 768, 792, 759, 707, 708, 682, 701, 826, // 88 - 95 + 815, 789, 789, 707, 687, 696, 689, 786, // 96 - 103 + 787, 713, 791, 785, 791, 873, 761, 762, // 104 - 111 + 762, 759, 759, 892, 892, 788, 784, 438, // 112 - 119 + 138, 277, 415, 392, 392, 668, 668, 0, // 120 - 127 + 390, 390, 317, 317, 276, 276, 509, 509, // 128 - 135 + 410, 410, 234, 234, 334, 334, 0, 0, // 136 - 143 + 0, 0, 0, 0, 0, 0, 0, 0, // 144 - 151 + 0, 0, 0, 0, 0, 0, 0, 0, // 152 - 159 + 0, 732, 544, 544, 910, 667, 760, 760, // 160 - 167 + 776, 595, 694, 626, 788, 788, 788, 788, // 168 - 175 + 788, 788, 788, 788, 788, 788, 788, 788, // 176 - 183 + 788, 788, 788, 788, 788, 788, 788, 788, // 184 - 191 + 788, 788, 788, 788, 788, 788, 788, 788, // 192 - 199 + 788, 788, 788, 788, 788, 788, 788, 788, // 200 - 207 + 788, 788, 788, 788, 894, 838, 1016, 458, // 208 - 215 + 748, 924, 748, 918, 927, 928, 928, 834, // 216 - 223 + 873, 828, 924, 924, 917, 930, 931, 463, // 224 - 231 + 883, 836, 836, 867, 867, 696, 696, 874, // 232 - 239 + 0, 874, 760, 946, 771, 865, 771, 888, // 240 - 247 + 967, 888, 831, 873, 927, 970, 918, 0 // 248 - 255 + } +} + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bitmap.cxx b/vcl/source/gdi/bitmap.cxx new file mode 100644 index 000000000000..a0b48f4ee124 --- /dev/null +++ b/vcl/source/gdi/bitmap.cxx @@ -0,0 +1,1941 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <rtl/crc.h> +#include <vcl/salbtype.hxx> +#include <tools/stream.hxx> +#include <vcl/bmpacc.hxx> +#include <tools/poly.hxx> +#include <vcl/outdev.hxx> +#include <vcl/impbmp.hxx> +#include <vcl/salbmp.hxx> +#include <tools/rc.h> +#include <vcl/bitmap.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/svapp.hxx> +#include <vcl/image.hxx> + +// ---------- +// - Bitmap - +// ---------- + +Bitmap::Bitmap() : + mpImpBmp( NULL ) +{ +} + +// ------------------------------------------------------------------ + +Bitmap::Bitmap( const ResId& rResId ) : + mpImpBmp( NULL ) +{ + const BitmapEx aBmpEx( rResId ); + + if( !aBmpEx.IsEmpty() ) + *this = aBmpEx.GetBitmap(); +} + +// ------------------------------------------------------------------ + +Bitmap::Bitmap( const Bitmap& rBitmap ) : + maPrefMapMode ( rBitmap.maPrefMapMode ), + maPrefSize ( rBitmap.maPrefSize ) +{ + mpImpBmp = rBitmap.mpImpBmp; + + if ( mpImpBmp ) + mpImpBmp->ImplIncRefCount(); +} + +// ------------------------------------------------------------------ + +Bitmap::Bitmap( SalBitmap* pSalBitmap ) +{ + mpImpBmp = new ImpBitmap(); + mpImpBmp->ImplSetSalBitmap( pSalBitmap ); + maPrefMapMode = MapMode( MAP_PIXEL ); + maPrefSize = mpImpBmp->ImplGetSize(); +} + +// ------------------------------------------------------------------ + +Bitmap::Bitmap( const Size& rSizePixel, sal_uInt16 nBitCount, const BitmapPalette* pPal ) +{ + if( rSizePixel.Width() && rSizePixel.Height() ) + { + BitmapPalette aPal; + BitmapPalette* pRealPal = NULL; + + if( nBitCount <= 8 ) + { + if( !pPal ) + { + if( 1 == nBitCount ) + { + aPal.SetEntryCount( 2 ); + aPal[ 0 ] = Color( COL_BLACK ); + aPal[ 1 ] = Color( COL_WHITE ); + } + else if( ( 4 == nBitCount ) || ( 8 == nBitCount ) ) + { + aPal.SetEntryCount( 1 << nBitCount ); + aPal[ 0 ] = Color( COL_BLACK ); + aPal[ 1 ] = Color( COL_BLUE ); + aPal[ 2 ] = Color( COL_GREEN ); + aPal[ 3 ] = Color( COL_CYAN ); + aPal[ 4 ] = Color( COL_RED ); + aPal[ 5 ] = Color( COL_MAGENTA ); + aPal[ 6 ] = Color( COL_BROWN ); + aPal[ 7 ] = Color( COL_GRAY ); + aPal[ 8 ] = Color( COL_LIGHTGRAY ); + aPal[ 9 ] = Color( COL_LIGHTBLUE ); + aPal[ 10 ] = Color( COL_LIGHTGREEN ); + aPal[ 11 ] = Color( COL_LIGHTCYAN ); + aPal[ 12 ] = Color( COL_LIGHTRED ); + aPal[ 13 ] = Color( COL_LIGHTMAGENTA ); + aPal[ 14 ] = Color( COL_YELLOW ); + aPal[ 15 ] = Color( COL_WHITE ); + + // Dither-Palette erzeugen + if( 8 == nBitCount ) + { + sal_uInt16 nActCol = 16; + + for( sal_uInt16 nB = 0; nB < 256; nB += 51 ) + for( sal_uInt16 nG = 0; nG < 256; nG += 51 ) + for( sal_uInt16 nR = 0; nR < 256; nR += 51 ) + aPal[ nActCol++ ] = BitmapColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB ); + + // Standard-Office-Farbe setzen + aPal[ nActCol++ ] = BitmapColor( 0, 184, 255 ); + } + } + } + else + pRealPal = (BitmapPalette*) pPal; + } + + mpImpBmp = new ImpBitmap; + mpImpBmp->ImplCreate( rSizePixel, nBitCount, pRealPal ? *pRealPal : aPal ); + } + else + mpImpBmp = NULL; +} + +// ------------------------------------------------------------------ + +Bitmap::~Bitmap() +{ + ImplReleaseRef(); +} + +// ------------------------------------------------------------------ + +const BitmapPalette& Bitmap::GetGreyPalette( int nEntries ) +{ + static BitmapPalette aGreyPalette2; + static BitmapPalette aGreyPalette4; + static BitmapPalette aGreyPalette16; + static BitmapPalette aGreyPalette256; + + // create greyscale palette with 2, 4, 16 or 256 entries + if( 2 == nEntries || 4 == nEntries || 16 == nEntries || 256 == nEntries ) + { + if( 2 == nEntries ) + { + if( !aGreyPalette2.GetEntryCount() ) + { + aGreyPalette2.SetEntryCount( 2 ); + aGreyPalette2[ 0 ] = BitmapColor( 0, 0, 0 ); + aGreyPalette2[ 1 ] = BitmapColor( 255, 255, 255 ); + } + + return aGreyPalette2; + } + else if( 4 == nEntries ) + { + if( !aGreyPalette4.GetEntryCount() ) + { + aGreyPalette4.SetEntryCount( 4 ); + aGreyPalette4[ 0 ] = BitmapColor( 0, 0, 0 ); + aGreyPalette4[ 1 ] = BitmapColor( 85, 85, 85 ); + aGreyPalette4[ 2 ] = BitmapColor( 170, 170, 170 ); + aGreyPalette4[ 3 ] = BitmapColor( 255, 255, 255 ); + } + + return aGreyPalette4; + } + else if( 16 == nEntries ) + { + if( !aGreyPalette16.GetEntryCount() ) + { + sal_uInt8 cGrey = 0, cGreyInc = 17; + + aGreyPalette16.SetEntryCount( 16 ); + + for( sal_uInt16 i = 0; i < 16; i++, cGrey = sal::static_int_cast<sal_uInt8>(cGrey + cGreyInc) ) + aGreyPalette16[ i ] = BitmapColor( cGrey, cGrey, cGrey ); + } + + return aGreyPalette16; + } + else + { + if( !aGreyPalette256.GetEntryCount() ) + { + aGreyPalette256.SetEntryCount( 256 ); + + for( sal_uInt16 i = 0; i < 256; i++ ) + aGreyPalette256[ i ] = BitmapColor( (sal_uInt8) i, (sal_uInt8) i, (sal_uInt8) i ); + } + + return aGreyPalette256; + } + } + else + { + OSL_FAIL( "Bitmap::GetGreyPalette: invalid entry count (2/4/16/256 allowed)" ); + return aGreyPalette2; + } +} + +// ------------------------------------------------------------------ + +bool BitmapPalette::IsGreyPalette() const +{ + // TODO: add an IsGreyPalette flag to BitmapPalette + // TODO: unless this causes problems binary compatibility + const int nEntryCount = GetEntryCount(); + if( !nEntryCount ) // NOTE: an empty palette means 1:1 mapping + return true; + // see above: only certain entry values will result in a valid call to GetGreyPalette + if( nEntryCount == 2 || nEntryCount == 4 || nEntryCount == 16 || nEntryCount == 256 ) + { + const BitmapPalette& rGreyPalette = Bitmap::GetGreyPalette( nEntryCount ); + if( rGreyPalette == *this ) + return true; + } + // TODO: is it worth to compare the entries? + return false; +} + +// ------------------------------------------------------------------ + +Bitmap& Bitmap::operator=( const Bitmap& rBitmap ) +{ + maPrefSize = rBitmap.maPrefSize; + maPrefMapMode = rBitmap.maPrefMapMode; + + if ( rBitmap.mpImpBmp ) + rBitmap.mpImpBmp->ImplIncRefCount(); + + ImplReleaseRef(); + mpImpBmp = rBitmap.mpImpBmp; + + return *this; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::IsEqual( const Bitmap& rBmp ) const +{ + return( IsSameInstance( rBmp ) || + ( rBmp.GetSizePixel() == GetSizePixel() && + rBmp.GetBitCount() == GetBitCount() && + rBmp.GetChecksum() == GetChecksum() ) ); +} + +// ------------------------------------------------------------------ + +void Bitmap::SetEmpty() +{ + maPrefMapMode = MapMode(); + maPrefSize = Size(); + + ImplReleaseRef(); + mpImpBmp = NULL; +} + +// ------------------------------------------------------------------ + +Size Bitmap::GetSizePixel() const +{ + return( mpImpBmp ? mpImpBmp->ImplGetSize() : Size() ); +} +// ------------------------------------------------------------------ + +void Bitmap::SetSizePixel( const Size& rNewSize ) +{ + Scale( rNewSize ); +} + +// ------------------------------------------------------------------ + +Size Bitmap::GetSourceSizePixel() const +{ + return( mpImpBmp ? mpImpBmp->ImplGetSourceSize() : Size() ); +} + +// ------------------------------------------------------------------ + +void Bitmap::SetSourceSizePixel( const Size& rSize) +{ + if( mpImpBmp ) + mpImpBmp->ImplSetSourceSize( rSize); +} + +// ------------------------------------------------------------------ + +sal_uInt16 Bitmap::GetBitCount() const +{ + return( mpImpBmp ? mpImpBmp->ImplGetBitCount() : 0 ); +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::HasGreyPalette() const +{ + const sal_uInt16 nBitCount = GetBitCount(); + sal_Bool bRet = sal_False; + + if( 1 == nBitCount ) + { + BitmapReadAccess* pRAcc = ( (Bitmap*) this )->AcquireReadAccess(); + + if( pRAcc ) + { + const BitmapColor& rCol0( pRAcc->GetPaletteColor( 0 ) ); + const BitmapColor& rCol1( pRAcc->GetPaletteColor( 1 ) ); + if( rCol0.GetRed() == rCol0.GetGreen() && rCol0.GetRed() == rCol0.GetBlue() && + rCol1.GetRed() == rCol1.GetGreen() && rCol1.GetRed() == rCol1.GetBlue() ) + { + bRet = sal_True; + } + ( (Bitmap*) this )->ReleaseAccess( pRAcc ); + } + else + bRet = sal_True; + } + else if( 4 == nBitCount || 8 == nBitCount ) + { + BitmapReadAccess* pRAcc = ( (Bitmap*) this )->AcquireReadAccess(); + + if( pRAcc ) + { + if( pRAcc->HasPalette() && ( (BitmapPalette&) pRAcc->GetPalette() == GetGreyPalette( 1 << nBitCount ) ) ) + bRet = sal_True; + + ( (Bitmap*) this )->ReleaseAccess( pRAcc ); + } + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_uLong Bitmap::GetChecksum() const +{ + sal_uLong nRet = 0UL; + + if( mpImpBmp ) + { + nRet = mpImpBmp->ImplGetChecksum(); + + if( !nRet ) + { + BitmapReadAccess* pRAcc = ( (Bitmap*) this )->AcquireReadAccess(); + + if( pRAcc && pRAcc->Width() && pRAcc->Height() ) + { + sal_uInt32 nCrc = 0; + SVBT32 aBT32; + + pRAcc->ImplZeroInitUnusedBits(); + + UInt32ToSVBT32( pRAcc->Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pRAcc->Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pRAcc->GetBitCount(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pRAcc->GetColorMask().GetRedMask(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pRAcc->GetColorMask().GetGreenMask(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pRAcc->GetColorMask().GetBlueMask(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + if( pRAcc->HasPalette() ) + { + nCrc = rtl_crc32( nCrc, pRAcc->GetPalette().ImplGetColorBuffer(), + pRAcc->GetPaletteEntryCount() * sizeof( BitmapColor ) ); + } + + nCrc = rtl_crc32( nCrc, pRAcc->GetBuffer(), pRAcc->GetScanlineSize() * pRAcc->Height() ); + + mpImpBmp->ImplSetChecksum( nRet = nCrc ); + } + + if (pRAcc) ( (Bitmap*) this )->ReleaseAccess( pRAcc ); + } + } + + return nRet; +} + +// ------------------------------------------------------------------ + +void Bitmap::ImplReleaseRef() +{ + if( mpImpBmp ) + { + if( mpImpBmp->ImplGetRefCount() > 1UL ) + mpImpBmp->ImplDecRefCount(); + else + { + delete mpImpBmp; + mpImpBmp = NULL; + } + } +} + +// ------------------------------------------------------------------ + +void Bitmap::ImplMakeUnique() +{ + if( mpImpBmp && mpImpBmp->ImplGetRefCount() > 1UL ) + { + ImpBitmap* pOldImpBmp = mpImpBmp; + + pOldImpBmp->ImplDecRefCount(); + + mpImpBmp = new ImpBitmap; + mpImpBmp->ImplCreate( *pOldImpBmp ); + } +} + +// ------------------------------------------------------------------ + +void Bitmap::ImplAssignWithSize( const Bitmap& rBitmap ) +{ + const Size aOldSizePix( GetSizePixel() ); + const Size aNewSizePix( rBitmap.GetSizePixel() ); + const MapMode aOldMapMode( maPrefMapMode ); + Size aNewPrefSize; + + if( ( aOldSizePix != aNewSizePix ) && aOldSizePix.Width() && aOldSizePix.Height() ) + { + aNewPrefSize.Width() = FRound( maPrefSize.Width() * aNewSizePix.Width() / aOldSizePix.Width() ); + aNewPrefSize.Height() = FRound( maPrefSize.Height() * aNewSizePix.Height() / aOldSizePix.Height() ); + } + else + aNewPrefSize = maPrefSize; + + *this = rBitmap; + + maPrefSize = aNewPrefSize; + maPrefMapMode = aOldMapMode; +} + +// ------------------------------------------------------------------ + +ImpBitmap* Bitmap::ImplGetImpBitmap() const +{ + return mpImpBmp; +} + +// ------------------------------------------------------------------ + +void Bitmap::ImplSetImpBitmap( ImpBitmap* pImpBmp ) +{ + if( pImpBmp != mpImpBmp ) + { + ImplReleaseRef(); + mpImpBmp = pImpBmp; + } +} + +// ------------------------------------------------------------------ + +BitmapReadAccess* Bitmap::AcquireReadAccess() +{ + BitmapReadAccess* pReadAccess = new BitmapReadAccess( *this ); + + if( !*pReadAccess ) + { + delete pReadAccess; + pReadAccess = NULL; + } + + return pReadAccess; +} + +// ------------------------------------------------------------------ + +BitmapWriteAccess* Bitmap::AcquireWriteAccess() +{ + BitmapWriteAccess* pWriteAccess = new BitmapWriteAccess( *this ); + + if( !*pWriteAccess ) + { + delete pWriteAccess; + pWriteAccess = NULL; + } + + return pWriteAccess; +} + +// ------------------------------------------------------------------ + +void Bitmap::ReleaseAccess( BitmapReadAccess* pBitmapAccess ) +{ + delete pBitmapAccess; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Erase( const Color& rFillColor ) +{ + if( !(*this) ) + return sal_True; + + BitmapWriteAccess* pWriteAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pWriteAcc ) + { + const sal_uLong nFormat = pWriteAcc->GetScanlineFormat(); + sal_uInt8 cIndex = 0; + sal_Bool bFast = sal_False; + + switch( nFormat ) + { + case( BMP_FORMAT_1BIT_MSB_PAL ): + case( BMP_FORMAT_1BIT_LSB_PAL ): + { + cIndex = (sal_uInt8) pWriteAcc->GetBestPaletteIndex( rFillColor ); + cIndex = ( cIndex ? 255 : 0 ); + bFast = sal_True; + } + break; + + case( BMP_FORMAT_4BIT_MSN_PAL ): + case( BMP_FORMAT_4BIT_LSN_PAL ): + { + cIndex = (sal_uInt8) pWriteAcc->GetBestPaletteIndex( rFillColor ); + cIndex = cIndex | ( cIndex << 4 ); + bFast = sal_True; + } + break; + + case( BMP_FORMAT_8BIT_PAL ): + { + cIndex = (sal_uInt8) pWriteAcc->GetBestPaletteIndex( rFillColor ); + bFast = sal_True; + } + break; + + case( BMP_FORMAT_24BIT_TC_BGR ): + case( BMP_FORMAT_24BIT_TC_RGB ): + { + if( ( rFillColor.GetRed() == rFillColor.GetGreen() ) && + ( rFillColor.GetRed() == rFillColor.GetBlue() ) ) + { + cIndex = rFillColor.GetRed(); + bFast = sal_True; + } + else + bFast = sal_False; + } + break; + + default: + bFast = sal_False; + break; + } + + if( bFast ) + { + const sal_uLong nBufSize = pWriteAcc->GetScanlineSize() * pWriteAcc->Height(); + memset( pWriteAcc->GetBuffer(), cIndex, nBufSize ); + } + else + { + Point aTmpPoint; + const Rectangle aRect( aTmpPoint, Size( pWriteAcc->Width(), pWriteAcc->Height() ) ); + pWriteAcc->SetFillColor( rFillColor ); + pWriteAcc->FillRect( aRect ); + } + + ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Invert() +{ + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pAcc ) + { + if( pAcc->HasPalette() ) + { + BitmapPalette aBmpPal( pAcc->GetPalette() ); + const sal_uInt16 nCount = aBmpPal.GetEntryCount(); + + for( sal_uInt16 i = 0; i < nCount; i++ ) + aBmpPal[ i ].Invert(); + + pAcc->SetPalette( aBmpPal ); + } + else + { + const long nWidth = pAcc->Width(); + const long nHeight = pAcc->Height(); + + for( long nX = 0L; nX < nWidth; nX++ ) + for( long nY = 0L; nY < nHeight; nY++ ) + pAcc->SetPixel( nY, nX, pAcc->GetPixel( nY, nX ).Invert() ); + } + + ReleaseAccess( pAcc ); + bRet = sal_True; + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Mirror( sal_uLong nMirrorFlags ) +{ + sal_Bool bHorz = ( ( nMirrorFlags & BMP_MIRROR_HORZ ) == BMP_MIRROR_HORZ ); + sal_Bool bVert = ( ( nMirrorFlags & BMP_MIRROR_VERT ) == BMP_MIRROR_VERT ); + sal_Bool bRet = sal_False; + + if( bHorz && !bVert ) + { + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + + if( pAcc ) + { + const long nWidth = pAcc->Width(); + const long nHeight = pAcc->Height(); + const long nWidth1 = nWidth - 1L; + const long nWidth_2 = nWidth >> 1L; + + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L, nOther = nWidth1; nX < nWidth_2; nX++, nOther-- ) + { + const BitmapColor aTemp( pAcc->GetPixel( nY, nX ) ); + + pAcc->SetPixel( nY, nX, pAcc->GetPixel( nY, nOther ) ); + pAcc->SetPixel( nY, nOther, aTemp ); + } + } + + ReleaseAccess( pAcc ); + bRet = sal_True; + } + } + else if( bVert && !bHorz ) + { + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + + if( pAcc ) + { + const long nScanSize = pAcc->GetScanlineSize(); + sal_uInt8* pBuffer = new sal_uInt8[ nScanSize ]; + const long nHeight = pAcc->Height(); + const long nHeight1 = nHeight - 1L; + const long nHeight_2 = nHeight >> 1L; + + for( long nY = 0L, nOther = nHeight1; nY < nHeight_2; nY++, nOther-- ) + { + memcpy( pBuffer, pAcc->GetScanline( nY ), nScanSize ); + memcpy( pAcc->GetScanline( nY ), pAcc->GetScanline( nOther ), nScanSize ); + memcpy( pAcc->GetScanline( nOther ), pBuffer, nScanSize ); + } + + delete[] pBuffer; + ReleaseAccess( pAcc ); + bRet = sal_True; + } + } + else if( bHorz && bVert ) + { + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + + if( pAcc ) + { + const long nWidth = pAcc->Width(); + const long nWidth1 = nWidth - 1L; + const long nHeight = pAcc->Height(); + long nHeight_2 = nHeight >> 1; + + for( long nY = 0L, nOtherY = nHeight - 1L; nY < nHeight_2; nY++, nOtherY-- ) + { + for( long nX = 0L, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX-- ) + { + const BitmapColor aTemp( pAcc->GetPixel( nY, nX ) ); + + pAcc->SetPixel( nY, nX, pAcc->GetPixel( nOtherY, nOtherX ) ); + pAcc->SetPixel( nOtherY, nOtherX, aTemp ); + } + } + + // ggf. noch mittlere Zeile horizontal spiegeln + if( nHeight & 1 ) + { + for( long nX = 0L, nOtherX = nWidth1, nWidth_2 = nWidth >> 1; nX < nWidth_2; nX++, nOtherX-- ) + { + const BitmapColor aTemp( pAcc->GetPixel( nHeight_2, nX ) ); + pAcc->SetPixel( nHeight_2, nX, pAcc->GetPixel( nHeight_2, nOtherX ) ); + pAcc->SetPixel( nHeight_2, nOtherX, aTemp ); + } + } + + ReleaseAccess( pAcc ); + bRet = sal_True; + } + } + else + bRet = sal_True; + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Rotate( long nAngle10, const Color& rFillColor ) +{ + sal_Bool bRet = sal_False; + + nAngle10 %= 3600L; + nAngle10 = ( nAngle10 < 0L ) ? ( 3599L + nAngle10 ) : nAngle10; + + if( !nAngle10 ) + bRet = sal_True; + else if( 1800L == nAngle10 ) + bRet = Mirror( BMP_MIRROR_HORZ | BMP_MIRROR_VERT ); + else + { + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + Bitmap aRotatedBmp; + + if( pReadAcc ) + { + const Size aSizePix( GetSizePixel() ); + + if( ( 900L == nAngle10 ) || ( 2700L == nAngle10 ) ) + { + const Size aNewSizePix( aSizePix.Height(), aSizePix.Width() ); + Bitmap aNewBmp( aNewSizePix, GetBitCount(), &pReadAcc->GetPalette() ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = aSizePix.Width(); + const long nWidth1 = nWidth - 1L; + const long nHeight = aSizePix.Height(); + const long nHeight1 = nHeight - 1L; + const long nNewWidth = aNewSizePix.Width(); + const long nNewHeight = aNewSizePix.Height(); + + if( 900L == nAngle10 ) + { + for( long nY = 0L, nOtherX = nWidth1; nY < nNewHeight; nY++, nOtherX-- ) + for( long nX = 0L, nOtherY = 0L; nX < nNewWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nOtherY++, nOtherX ) ); + } + else if( 2700L == nAngle10 ) + { + for( long nY = 0L, nOtherX = 0L; nY < nNewHeight; nY++, nOtherX++ ) + for( long nX = 0L, nOtherY = nHeight1; nX < nNewWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nOtherY--, nOtherX ) ); + } + + aNewBmp.ReleaseAccess( pWriteAcc ); + } + + aRotatedBmp = aNewBmp; + } + else + { + Point aTmpPoint; + Rectangle aTmpRectangle( aTmpPoint, aSizePix ); + Polygon aPoly( aTmpRectangle ); + aPoly.Rotate( aTmpPoint, (sal_uInt16) nAngle10 ); + + Rectangle aNewBound( aPoly.GetBoundRect() ); + const Size aNewSizePix( aNewBound.GetSize() ); + Bitmap aNewBmp( aNewSizePix, GetBitCount(), &pReadAcc->GetPalette() ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const BitmapColor aFillColor( pWriteAcc->GetBestMatchingColor( rFillColor ) ); + const double fCosAngle = cos( nAngle10 * F_PI1800 ); + const double fSinAngle = sin( nAngle10 * F_PI1800 ); + const double fXMin = aNewBound.Left(); + const double fYMin = aNewBound.Top(); + const long nWidth = aSizePix.Width(); + const long nHeight = aSizePix.Height(); + const long nNewWidth = aNewSizePix.Width(); + const long nNewHeight = aNewSizePix.Height(); + long nX; + long nY; + long nRotX; + long nRotY; + long nSinY; + long nCosY; + long* pCosX = new long[ nNewWidth ]; + long* pSinX = new long[ nNewWidth ]; + long* pCosY = new long[ nNewHeight ]; + long* pSinY = new long[ nNewHeight ]; + + for ( nX = 0; nX < nNewWidth; nX++ ) + { + const double fTmp = ( fXMin + nX ) * 64.; + + pCosX[ nX ] = FRound( fCosAngle * fTmp ); + pSinX[ nX ] = FRound( fSinAngle * fTmp ); + } + + for ( nY = 0; nY < nNewHeight; nY++ ) + { + const double fTmp = ( fYMin + nY ) * 64.; + + pCosY[ nY ] = FRound( fCosAngle * fTmp ); + pSinY[ nY ] = FRound( fSinAngle * fTmp ); + } + + for( nY = 0L; nY < nNewHeight; nY++ ) + { + nSinY = pSinY[ nY ]; + nCosY = pCosY[ nY ]; + + for( nX = 0L; nX < nNewWidth; nX++ ) + { + nRotX = ( pCosX[ nX ] - nSinY ) >> 6; + nRotY = ( pSinX[ nX ] + nCosY ) >> 6; + + if ( ( nRotX > -1L ) && ( nRotX < nWidth ) && ( nRotY > -1L ) && ( nRotY < nHeight ) ) + pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nRotY, nRotX ) ); + else + pWriteAcc->SetPixel( nY, nX, aFillColor ); + } + } + + delete[] pSinX; + delete[] pCosX; + delete[] pSinY; + delete[] pCosY; + + aNewBmp.ReleaseAccess( pWriteAcc ); + } + + aRotatedBmp = aNewBmp; + } + + ReleaseAccess( pReadAcc ); + } + + if( ( bRet = !!aRotatedBmp ) == sal_True ) + ImplAssignWithSize( aRotatedBmp ); + } + + return bRet; +}; + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Crop( const Rectangle& rRectPixel ) +{ + const Size aSizePix( GetSizePixel() ); + Rectangle aRect( rRectPixel ); + sal_Bool bRet = sal_False; + + aRect.Intersection( Rectangle( Point(), aSizePix ) ); + + if( !aRect.IsEmpty() ) + { + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + + if( pReadAcc ) + { + Point aTmpPoint; + const Rectangle aNewRect( aTmpPoint, aRect.GetSize() ); + Bitmap aNewBmp( aNewRect.GetSize(), GetBitCount(), &pReadAcc->GetPalette() ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nOldX = aRect.Left(); + const long nOldY = aRect.Top(); + const long nNewWidth = aNewRect.GetWidth(); + const long nNewHeight = aNewRect.GetHeight(); + + for( long nY = 0, nY2 = nOldY; nY < nNewHeight; nY++, nY2++ ) + for( long nX = 0, nX2 = nOldX; nX < nNewWidth; nX++, nX2++ ) + pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nY2, nX2 ) ); + + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + ImplAssignWithSize( aNewBmp ); + } + } + + return bRet; +}; + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::CopyPixel( const Rectangle& rRectDst, + const Rectangle& rRectSrc, const Bitmap* pBmpSrc ) +{ + const Size aSizePix( GetSizePixel() ); + Rectangle aRectDst( rRectDst ); + sal_Bool bRet = sal_False; + + aRectDst.Intersection( Rectangle( Point(), aSizePix ) ); + + if( !aRectDst.IsEmpty() ) + { + if( pBmpSrc && ( *pBmpSrc != *this ) ) + { + Bitmap* pSrc = (Bitmap*) pBmpSrc; + const Size aCopySizePix( pSrc->GetSizePixel() ); + Rectangle aRectSrc( rRectSrc ); + const sal_uInt16 nSrcBitCount = pBmpSrc->GetBitCount(); + const sal_uInt16 nDstBitCount = GetBitCount(); + + if( nSrcBitCount > nDstBitCount ) + { + long nNextIndex = 0L; + + if( ( nSrcBitCount == 24 ) && ( nDstBitCount < 24 ) ) + Convert( BMP_CONVERSION_24BIT ); + else if( ( nSrcBitCount == 8 ) && ( nDstBitCount < 8 ) ) + { + Convert( BMP_CONVERSION_8BIT_COLORS ); + nNextIndex = 16; + } + else if( ( nSrcBitCount == 4 ) && ( nDstBitCount < 4 ) ) + { + Convert( BMP_CONVERSION_4BIT_COLORS ); + nNextIndex = 2; + } + + if( nNextIndex ) + { + BitmapReadAccess* pSrcAcc = pSrc->AcquireReadAccess(); + BitmapWriteAccess* pDstAcc = AcquireWriteAccess(); + + if( pSrcAcc && pDstAcc ) + { + const long nSrcCount = pDstAcc->GetPaletteEntryCount(); + const long nDstCount = 1 << nDstBitCount; + sal_Bool bFound; + + for( long i = 0L; ( i < nSrcCount ) && ( nNextIndex < nSrcCount ); i++ ) + { + const BitmapColor& rSrcCol = pSrcAcc->GetPaletteColor( (sal_uInt16) i ); + + bFound = sal_False; + + for( long j = 0L; j < nDstCount; j++ ) + { + if( rSrcCol == pDstAcc->GetPaletteColor( (sal_uInt16) j ) ) + { + bFound = sal_True; + break; + } + } + + if( !bFound ) + pDstAcc->SetPaletteColor( (sal_uInt16) nNextIndex++, rSrcCol ); + } + } + + if( pSrcAcc ) + pSrc->ReleaseAccess( pSrcAcc ); + + if( pDstAcc ) + ReleaseAccess( pDstAcc ); + } + } + + aRectSrc.Intersection( Rectangle( Point(), aCopySizePix ) ); + + if( !aRectSrc.IsEmpty() ) + { + BitmapReadAccess* pReadAcc = pSrc->AcquireReadAccess(); + + if( pReadAcc ) + { + BitmapWriteAccess* pWriteAcc = AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = Min( aRectSrc.GetWidth(), aRectDst.GetWidth() ); + const long nHeight = Min( aRectSrc.GetHeight(), aRectDst.GetHeight() ); + const long nSrcEndX = aRectSrc.Left() + nWidth; + const long nSrcEndY = aRectSrc.Top() + nHeight; + long nDstY = aRectDst.Top(); + + if( pReadAcc->HasPalette() && pWriteAcc->HasPalette() ) + { + const sal_uInt16 nCount = pReadAcc->GetPaletteEntryCount(); + sal_uInt8* pMap = new sal_uInt8[ nCount ]; + + // Index-Map fuer Farbtabelle + // aufbauen, da das Bild ja (relativ) farbgenau + // kopiert werden soll + for( sal_uInt16 i = 0; i < nCount; i++ ) + pMap[ i ] = (sal_uInt8) pWriteAcc->GetBestPaletteIndex( pReadAcc->GetPaletteColor( i ) ); + + for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ ) + for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ ) + pWriteAcc->SetPixel( nDstY, nDstX, pMap[ pReadAcc->GetPixel( nSrcY, nSrcX ).GetIndex() ] ); + + delete[] pMap; + } + else if( pReadAcc->HasPalette() ) + { + for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ ) + for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ ) + pWriteAcc->SetPixel( nDstY, nDstX, pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nSrcY, nSrcX ) ) ); + } + else + for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ ) + for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ ) + pWriteAcc->SetPixel( nDstY, nDstX, pReadAcc->GetPixel( nSrcY, nSrcX ) ); + + ReleaseAccess( pWriteAcc ); + bRet = ( nWidth > 0L ) && ( nHeight > 0L ); + } + + pSrc->ReleaseAccess( pReadAcc ); + } + } + } + else + { + Rectangle aRectSrc( rRectSrc ); + + aRectSrc.Intersection( Rectangle( Point(), aSizePix ) ); + + if( !aRectSrc.IsEmpty() && ( aRectSrc != aRectDst ) ) + { + BitmapWriteAccess* pWriteAcc = AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = Min( aRectSrc.GetWidth(), aRectDst.GetWidth() ); + const long nHeight = Min( aRectSrc.GetHeight(), aRectDst.GetHeight() ); + const long nSrcX = aRectSrc.Left(); + const long nSrcY = aRectSrc.Top(); + const long nSrcEndX1 = nSrcX + nWidth - 1L; + const long nSrcEndY1 = nSrcY + nHeight - 1L; + const long nDstX = aRectDst.Left(); + const long nDstY = aRectDst.Top(); + const long nDstEndX1 = nDstX + nWidth - 1L; + const long nDstEndY1 = nDstY + nHeight - 1L; + + if( ( nDstX <= nSrcX ) && ( nDstY <= nSrcY ) ) + { + for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ ) + for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ ) + pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) ); + } + else if( ( nDstX <= nSrcX ) && ( nDstY >= nSrcY ) ) + { + for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- ) + for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ ) + pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) ); + } + else if( ( nDstX >= nSrcX ) && ( nDstY <= nSrcY ) ) + { + for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ ) + for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- ) + pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) ); + } + else + { + for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- ) + for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- ) + pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) ); + } + + ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + } + } + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Expand( sal_uLong nDX, sal_uLong nDY, const Color* pInitColor ) +{ + sal_Bool bRet = sal_False; + + if( nDX || nDY ) + { + const Size aSizePixel( GetSizePixel() ); + const long nWidth = aSizePixel.Width(); + const long nHeight = aSizePixel.Height(); + const Size aNewSize( nWidth + nDX, nHeight + nDY ); + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + + if( pReadAcc ) + { + BitmapPalette aBmpPal( pReadAcc->GetPalette() ); + Bitmap aNewBmp( aNewSize, GetBitCount(), &aBmpPal ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + BitmapColor aColor; + const long nNewX = nWidth; + const long nNewY = nHeight; + const long nNewWidth = pWriteAcc->Width(); + const long nNewHeight = pWriteAcc->Height(); + long nX; + long nY; + + if( pInitColor ) + aColor = pWriteAcc->GetBestMatchingColor( *pInitColor ); + + for( nY = 0L; nY < nHeight; nY++ ) + { + pWriteAcc->CopyScanline( nY, *pReadAcc ); + + if( pInitColor && nDX ) + for( nX = nNewX; nX < nNewWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, aColor ); + } + + if( pInitColor && nDY ) + for( nY = nNewY; nY < nNewHeight; nY++ ) + for( nX = 0; nX < nNewWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, aColor ); + + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + ImplAssignWithSize( aNewBmp ); + } + } + + return bRet; +} + +// ------------------------------------------------------------------ + +Bitmap Bitmap::CreateMask( const Color& rTransColor, sal_uLong nTol ) const +{ + Bitmap aNewBmp( GetSizePixel(), 1 ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pWriteAcc ) + { + BitmapReadAccess* pReadAcc = ( (Bitmap*) this )->AcquireReadAccess(); + + if( pReadAcc ) + { + const long nWidth = pReadAcc->Width(); + const long nHeight = pReadAcc->Height(); + const BitmapColor aBlack( pWriteAcc->GetBestMatchingColor( Color( COL_BLACK ) ) ); + const BitmapColor aWhite( pWriteAcc->GetBestMatchingColor( Color( COL_WHITE ) ) ); + + if( !nTol ) + { + const BitmapColor aTest( pReadAcc->GetBestMatchingColor( rTransColor ) ); + long nX, nY, nShift; + + if( pReadAcc->GetScanlineFormat() == BMP_FORMAT_4BIT_MSN_PAL || + pReadAcc->GetScanlineFormat() == BMP_FORMAT_4BIT_LSN_PAL ) + { + // optimized for 4Bit-MSN/LSN source palette + const sal_uInt8 cTest = aTest.GetIndex(); + const long nShiftInit = ( ( pReadAcc->GetScanlineFormat() == BMP_FORMAT_4BIT_MSN_PAL ) ? 4 : 0 ); + + if( pWriteAcc->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL && + aWhite.GetIndex() == 1 ) + { + // optimized for 1Bit-MSB destination palette + for( nY = 0L; nY < nHeight; nY++ ) + { + Scanline pSrc = pReadAcc->GetScanline( nY ); + Scanline pDst = pWriteAcc->GetScanline( nY ); + for( nX = 0L, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4 ) + { + if( cTest == ( ( pSrc[ nX >> 1 ] >> nShift ) & 0x0f ) ) + pDst[ nX >> 3 ] |= 1 << ( 7 - ( nX & 7 ) ); + else + pDst[ nX >> 3 ] &= ~( 1 << ( 7 - ( nX & 7 ) ) ); + } + } + } + else + { + for( nY = 0L; nY < nHeight; nY++ ) + { + Scanline pSrc = pReadAcc->GetScanline( nY ); + for( nX = 0L, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4 ) + { + if( cTest == ( ( pSrc[ nX >> 1 ] >> nShift ) & 0x0f ) ) + pWriteAcc->SetPixel( nY, nX, aWhite ); + else + pWriteAcc->SetPixel( nY, nX, aBlack ); + } + } + } + } + else if( pReadAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ) + { + // optimized for 8Bit source palette + const sal_uInt8 cTest = aTest.GetIndex(); + + if( pWriteAcc->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL && + aWhite.GetIndex() == 1 ) + { + // optimized for 1Bit-MSB destination palette + for( nY = 0L; nY < nHeight; nY++ ) + { + Scanline pSrc = pReadAcc->GetScanline( nY ); + Scanline pDst = pWriteAcc->GetScanline( nY ); + for( nX = 0L; nX < nWidth; nX++ ) + { + if( cTest == pSrc[ nX ] ) + pDst[ nX >> 3 ] |= 1 << ( 7 - ( nX & 7 ) ); + else + pDst[ nX >> 3 ] &= ~( 1 << ( 7 - ( nX & 7 ) ) ); + } + } + } + else + { + for( nY = 0L; nY < nHeight; nY++ ) + { + Scanline pSrc = pReadAcc->GetScanline( nY ); + for( nX = 0L; nX < nWidth; nX++ ) + { + if( cTest == pSrc[ nX ] ) + pWriteAcc->SetPixel( nY, nX, aWhite ); + else + pWriteAcc->SetPixel( nY, nX, aBlack ); + } + } + } + } + else + { + // not optimized + for( nY = 0L; nY < nHeight; nY++ ) + { + for( nX = 0L; nX < nWidth; nX++ ) + { + if( aTest == pReadAcc->GetPixel( nY, nX ) ) + pWriteAcc->SetPixel( nY, nX, aWhite ); + else + pWriteAcc->SetPixel( nY, nX, aBlack ); + } + } + } + } + else + { + BitmapColor aCol; + long nR, nG, nB; + const long nMinR = MinMax( (long) rTransColor.GetRed() - nTol, 0, 255 ); + const long nMaxR = MinMax( (long) rTransColor.GetRed() + nTol, 0, 255 ); + const long nMinG = MinMax( (long) rTransColor.GetGreen() - nTol, 0, 255 ); + const long nMaxG = MinMax( (long) rTransColor.GetGreen() + nTol, 0, 255 ); + const long nMinB = MinMax( (long) rTransColor.GetBlue() - nTol, 0, 255 ); + const long nMaxB = MinMax( (long) rTransColor.GetBlue() + nTol, 0, 255 ); + + if( pReadAcc->HasPalette() ) + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + aCol = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nY, nX ) ); + nR = aCol.GetRed(); + nG = aCol.GetGreen(); + nB = aCol.GetBlue(); + + if( nMinR <= nR && nMaxR >= nR && + nMinG <= nG && nMaxG >= nG && + nMinB <= nB && nMaxB >= nB ) + { + pWriteAcc->SetPixel( nY, nX, aWhite ); + } + else + pWriteAcc->SetPixel( nY, nX, aBlack ); + } + } + } + else + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + aCol = pReadAcc->GetPixel( nY, nX ); + nR = aCol.GetRed(); + nG = aCol.GetGreen(); + nB = aCol.GetBlue(); + + if( nMinR <= nR && nMaxR >= nR && + nMinG <= nG && nMaxG >= nG && + nMinB <= nB && nMaxB >= nB ) + { + pWriteAcc->SetPixel( nY, nX, aWhite ); + } + else + pWriteAcc->SetPixel( nY, nX, aBlack ); + } + } + } + } + + ( (Bitmap*) this )->ReleaseAccess( pReadAcc ); + bRet = sal_True; + } + + aNewBmp.ReleaseAccess( pWriteAcc ); + } + + if( bRet ) + { + aNewBmp.maPrefSize = maPrefSize; + aNewBmp.maPrefMapMode = maPrefMapMode; + } + else + aNewBmp = Bitmap(); + + return aNewBmp; +} + +// ------------------------------------------------------------------ + +Region Bitmap::CreateRegion( const Color& rColor, const Rectangle& rRect ) const +{ + Region aRegion; + Rectangle aRect( rRect ); + BitmapReadAccess* pReadAcc = ( (Bitmap*) this )->AcquireReadAccess(); + + aRect.Intersection( Rectangle( Point(), GetSizePixel() ) ); + aRect.Justify(); + + if( pReadAcc ) + { + Rectangle aSubRect; + const long nLeft = aRect.Left(); + const long nTop = aRect.Top(); + const long nRight = aRect.Right(); + const long nBottom = aRect.Bottom(); + const BitmapColor aMatch( pReadAcc->GetBestMatchingColor( rColor ) ); + + aRegion.ImplBeginAddRect(); + + for( long nY = nTop; nY <= nBottom; nY++ ) + { + aSubRect.Top() = aSubRect.Bottom() = nY; + + for( long nX = nLeft; nX <= nRight; ) + { + while( ( nX <= nRight ) && ( aMatch != pReadAcc->GetPixel( nY, nX ) ) ) + nX++; + + if( nX <= nRight ) + { + aSubRect.Left() = nX; + + while( ( nX <= nRight ) && ( aMatch == pReadAcc->GetPixel( nY, nX ) ) ) + nX++; + + aSubRect.Right() = nX - 1L; + aRegion.ImplAddRect( aSubRect ); + } + } + } + + aRegion.ImplEndAddRect(); + ( (Bitmap*) this )->ReleaseAccess( pReadAcc ); + } + else + aRegion = aRect; + + return aRegion; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Replace( const Bitmap& rMask, const Color& rReplaceColor ) +{ + BitmapReadAccess* pMaskAcc = ( (Bitmap&) rMask ).AcquireReadAccess(); + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pMaskAcc && pAcc ) + { + const long nWidth = Min( pMaskAcc->Width(), pAcc->Width() ); + const long nHeight = Min( pMaskAcc->Height(), pAcc->Height() ); + const BitmapColor aMaskWhite( pMaskAcc->GetBestMatchingColor( Color( COL_WHITE ) ) ); + BitmapColor aReplace; + + if( pAcc->HasPalette() ) + { + const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount(); + const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount(); + + // erst einmal naechste Farbe nehmen + aReplace = pAcc->GetBestMatchingColor( rReplaceColor ); + + // falls Palettenbild, und die zu setzende Farbe ist nicht + // in der Palette, suchen wir nach freien Eintraegen (teuer) + if( pAcc->GetPaletteColor( (sal_uInt8) aReplace ) != BitmapColor( rReplaceColor ) ) + { + // erst einmal nachsehen, ob wir unsere ReplaceColor + // nicht auf einen freien Platz am Ende der Palette + // setzen koennen + if( nActColors < nMaxColors ) + { + pAcc->SetPaletteEntryCount( nActColors + 1 ); + pAcc->SetPaletteColor( nActColors, rReplaceColor ); + aReplace = BitmapColor( (sal_uInt8) nActColors ); + } + else + { + sal_Bool* pFlags = new sal_Bool[ nMaxColors ]; + + // alle Eintraege auf 0 setzen + memset( pFlags, 0, nMaxColors ); + + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX = 0L; nX < nWidth; nX++ ) + pFlags[ (sal_uInt8) pAcc->GetPixel( nY, nX ) ] = sal_True; + + for( sal_uInt16 i = 0UL; i < nMaxColors; i++ ) + { + // Hurra, wir haben einen unbenutzten Eintrag + if( !pFlags[ i ] ) + { + pAcc->SetPaletteColor( (sal_uInt16) i, rReplaceColor ); + aReplace = BitmapColor( (sal_uInt8) i ); + } + } + + delete[] pFlags; + } + } + } + else + aReplace = rReplaceColor; + + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX = 0L; nX < nWidth; nX++ ) + if( pMaskAcc->GetPixel( nY, nX ) == aMaskWhite ) + pAcc->SetPixel( nY, nX, aReplace ); + + bRet = sal_True; + } + + ( (Bitmap&) rMask ).ReleaseAccess( pMaskAcc ); + ReleaseAccess( pAcc ); + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Replace( const AlphaMask& rAlpha, const Color& rMergeColor ) +{ + Bitmap aNewBmp( GetSizePixel(), 24 ); + BitmapReadAccess* pAcc = AcquireReadAccess(); + BitmapReadAccess* pAlphaAcc = ( (AlphaMask&) rAlpha ).AcquireReadAccess(); + BitmapWriteAccess* pNewAcc = aNewBmp.AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pAcc && pAlphaAcc && pNewAcc ) + { + BitmapColor aCol; + const long nWidth = Min( pAlphaAcc->Width(), pAcc->Width() ); + const long nHeight = Min( pAlphaAcc->Height(), pAcc->Height() ); + + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + aCol = pAcc->GetColor( nY, nX ); + pNewAcc->SetPixel( nY, nX, aCol.Merge( rMergeColor, 255 - (sal_uInt8) pAlphaAcc->GetPixel( nY, nX ) ) ); + } + } + + bRet = sal_True; + } + + ReleaseAccess( pAcc ); + ( (AlphaMask&) rAlpha ).ReleaseAccess( pAlphaAcc ); + aNewBmp.ReleaseAccess( pNewAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uLong nTol ) +{ + // Bitmaps with 1 bit color depth can cause problems + // if they have other entries than black/white in their palette + if( 1 == GetBitCount() ) + Convert( BMP_CONVERSION_4BIT_COLORS ); + + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pAcc ) + { + const long nMinR = MinMax( (long) rSearchColor.GetRed() - nTol, 0, 255 ); + const long nMaxR = MinMax( (long) rSearchColor.GetRed() + nTol, 0, 255 ); + const long nMinG = MinMax( (long) rSearchColor.GetGreen() - nTol, 0, 255 ); + const long nMaxG = MinMax( (long) rSearchColor.GetGreen() + nTol, 0, 255 ); + const long nMinB = MinMax( (long) rSearchColor.GetBlue() - nTol, 0, 255 ); + const long nMaxB = MinMax( (long) rSearchColor.GetBlue() + nTol, 0, 255 ); + + if( pAcc->HasPalette() ) + { + for( sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++ ) + { + const BitmapColor& rCol = pAcc->GetPaletteColor( i ); + + if( nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && + nMinG <= rCol.GetGreen() && nMaxG >= rCol.GetGreen() && + nMinB <= rCol.GetBlue() && nMaxB >= rCol.GetBlue() ) + { + pAcc->SetPaletteColor( i, rReplaceColor ); + } + } + } + else + { + BitmapColor aCol; + const BitmapColor aReplace( pAcc->GetBestMatchingColor( rReplaceColor ) ); + + for( long nY = 0L, nHeight = pAcc->Height(); nY < nHeight; nY++ ) + { + for( long nX = 0L, nWidth = pAcc->Width(); nX < nWidth; nX++ ) + { + aCol = pAcc->GetPixel( nY, nX ); + + if( nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && + nMinG <= aCol.GetGreen() && nMaxG >= aCol.GetGreen() && + nMinB <= aCol.GetBlue() && nMaxB >= aCol.GetBlue() ) + { + pAcc->SetPixel( nY, nX, aReplace ); + } + } + } + } + + ReleaseAccess( pAcc ); + bRet = sal_True; + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Replace( const Color* pSearchColors, const Color* pReplaceColors, + sal_uLong nColorCount, sal_uLong* _pTols ) +{ + // Bitmaps with 1 bit color depth can cause problems + // if they have other entries than black/white in their palette + if( 1 == GetBitCount() ) + Convert( BMP_CONVERSION_4BIT_COLORS ); + + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pAcc ) + { + long* pMinR = new long[ nColorCount ]; + long* pMaxR = new long[ nColorCount ]; + long* pMinG = new long[ nColorCount ]; + long* pMaxG = new long[ nColorCount ]; + long* pMinB = new long[ nColorCount ]; + long* pMaxB = new long[ nColorCount ]; + long* pTols; + sal_uLong i; + + if( !_pTols ) + { + pTols = new long[ nColorCount ]; + memset( pTols, 0, nColorCount * sizeof( long ) ); + } + else + pTols = (long*) _pTols; + + for( i = 0UL; i < nColorCount; i++ ) + { + const Color& rCol = pSearchColors[ i ]; + const long nTol = pTols[ i ]; + + pMinR[ i ] = MinMax( (long) rCol.GetRed() - nTol, 0, 255 ); + pMaxR[ i ] = MinMax( (long) rCol.GetRed() + nTol, 0, 255 ); + pMinG[ i ] = MinMax( (long) rCol.GetGreen() - nTol, 0, 255 ); + pMaxG[ i ] = MinMax( (long) rCol.GetGreen() + nTol, 0, 255 ); + pMinB[ i ] = MinMax( (long) rCol.GetBlue() - nTol, 0, 255 ); + pMaxB[ i ] = MinMax( (long) rCol.GetBlue() + nTol, 0, 255 ); + } + + if( pAcc->HasPalette() ) + { + for( sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount(); nEntry < nPalCount; nEntry++ ) + { + const BitmapColor& rCol = pAcc->GetPaletteColor( nEntry ); + + for( i = 0UL; i < nColorCount; i++ ) + { + if( pMinR[ i ] <= rCol.GetRed() && pMaxR[ i ] >= rCol.GetRed() && + pMinG[ i ] <= rCol.GetGreen() && pMaxG[ i ] >= rCol.GetGreen() && + pMinB[ i ] <= rCol.GetBlue() && pMaxB[ i ] >= rCol.GetBlue() ) + { + pAcc->SetPaletteColor( (sal_uInt16)nEntry, pReplaceColors[ i ] ); + break; + } + } + } + } + else + { + BitmapColor aCol; + BitmapColor* pReplaces = new BitmapColor[ nColorCount ]; + + for( i = 0UL; i < nColorCount; i++ ) + pReplaces[ i ] = pAcc->GetBestMatchingColor( pReplaceColors[ i ] ); + + for( long nY = 0L, nHeight = pAcc->Height(); nY < nHeight; nY++ ) + { + for( long nX = 0L, nWidth = pAcc->Width(); nX < nWidth; nX++ ) + { + aCol = pAcc->GetPixel( nY, nX ); + + for( i = 0UL; i < nColorCount; i++ ) + { + if( pMinR[ i ] <= aCol.GetRed() && pMaxR[ i ] >= aCol.GetRed() && + pMinG[ i ] <= aCol.GetGreen() && pMaxG[ i ] >= aCol.GetGreen() && + pMinB[ i ] <= aCol.GetBlue() && pMaxB[ i ] >= aCol.GetBlue() ) + { + pAcc->SetPixel( nY, nX, pReplaces[ i ] ); + break; + } + } + } + } + + delete[] pReplaces; + } + + if( !_pTols ) + delete[] pTols; + + delete[] pMinR; + delete[] pMaxR; + delete[] pMinG; + delete[] pMaxG; + delete[] pMinB; + delete[] pMaxB; + ReleaseAccess( pAcc ); + bRet = sal_True; + } + + return bRet; +} + +// ------------------------------------------------------------------ + +Bitmap Bitmap::CreateDisplayBitmap( OutputDevice* pDisplay ) +{ + Bitmap aDispBmp( *this ); + + if( mpImpBmp && ( pDisplay->mpGraphics || pDisplay->ImplGetGraphics() ) ) + { + ImpBitmap* pImpDispBmp = new ImpBitmap; + + if( pImpDispBmp->ImplCreate( *mpImpBmp, pDisplay->mpGraphics ) ) + aDispBmp.ImplSetImpBitmap( pImpDispBmp ); + else + delete pImpDispBmp; + } + + return aDispBmp; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::CombineSimple( const Bitmap& rMask, BmpCombine eCombine ) +{ + BitmapReadAccess* pMaskAcc = ( (Bitmap&) rMask ).AcquireReadAccess(); + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pMaskAcc && pAcc ) + { + const long nWidth = Min( pMaskAcc->Width(), pAcc->Width() ); + const long nHeight = Min( pMaskAcc->Height(), pAcc->Height() ); + const Color aColBlack( COL_BLACK ); + BitmapColor aPixel; + BitmapColor aMaskPixel; + const BitmapColor aWhite( pAcc->GetBestMatchingColor( Color( COL_WHITE ) ) ); + const BitmapColor aBlack( pAcc->GetBestMatchingColor( aColBlack ) ); + const BitmapColor aMaskBlack( pMaskAcc->GetBestMatchingColor( aColBlack ) ); + + switch( eCombine ) + { + case( BMP_COMBINE_COPY ): + { + for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ ) + { + if( pMaskAcc->GetPixel( nY, nX ) == aMaskBlack ) + pAcc->SetPixel( nY, nX, aBlack ); + else + pAcc->SetPixel( nY, nX, aWhite ); + } + } + break; + + case( BMP_COMBINE_INVERT ): + { + for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ ) + { + if( pAcc->GetPixel( nY, nX ) == aBlack ) + pAcc->SetPixel( nY, nX, aWhite ); + else + pAcc->SetPixel( nY, nX, aBlack ); + } + } + break; + + case( BMP_COMBINE_AND ): + { + for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ ) + { + if( pMaskAcc->GetPixel( nY, nX ) != aMaskBlack && pAcc->GetPixel( nY, nX ) != aBlack ) + pAcc->SetPixel( nY, nX, aWhite ); + else + pAcc->SetPixel( nY, nX, aBlack ); + } + } + break; + + case( BMP_COMBINE_NAND ): + { + for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ ) + { + if( pMaskAcc->GetPixel( nY, nX ) != aMaskBlack && pAcc->GetPixel( nY, nX ) != aBlack ) + pAcc->SetPixel( nY, nX, aBlack ); + else + pAcc->SetPixel( nY, nX, aWhite ); + } + } + break; + + case( BMP_COMBINE_OR ): + { + for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ ) + { + if( pMaskAcc->GetPixel( nY, nX ) != aMaskBlack || pAcc->GetPixel( nY, nX ) != aBlack ) + pAcc->SetPixel( nY, nX, aWhite ); + else + pAcc->SetPixel( nY, nX, aBlack ); + } + } + break; + + case( BMP_COMBINE_NOR ): + { + for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ ) + { + if( pMaskAcc->GetPixel( nY, nX ) != aMaskBlack || pAcc->GetPixel( nY, nX ) != aBlack ) + pAcc->SetPixel( nY, nX, aBlack ); + else + pAcc->SetPixel( nY, nX, aWhite ); + } + } + break; + + case( BMP_COMBINE_XOR ): + { + for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ ) + { + aPixel = pAcc->GetPixel( nY, nX ); + aMaskPixel = pMaskAcc->GetPixel( nY, nX ); + + if( ( aMaskPixel != aMaskBlack && aPixel == aBlack ) || + ( aMaskPixel == aMaskBlack && aPixel != aBlack ) ) + { + pAcc->SetPixel( nY, nX, aWhite ); + } + else + pAcc->SetPixel( nY, nX, aBlack ); + } + } + break; + + case( BMP_COMBINE_NXOR ): + { + for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ ) + { + aPixel = pAcc->GetPixel( nY, nX ); + aMaskPixel = pMaskAcc->GetPixel( nY, nX ); + + if( ( aMaskPixel != aMaskBlack && aPixel == aBlack ) || + ( aMaskPixel == aMaskBlack && aPixel != aBlack ) ) + { + pAcc->SetPixel( nY, nX, aBlack ); + } + else + pAcc->SetPixel( nY, nX, aWhite ); + } + } + break; + } + + bRet = sal_True; + } + + ( (Bitmap&) rMask ).ReleaseAccess( pMaskAcc ); + ReleaseAccess( pAcc ); + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Blend( const AlphaMask& rAlpha, const Color& rBackgroundColor ) +{ + // TODO: Have a look at OutputDevice::ImplDrawAlpha() for some + // optimizations. Might even consolidate the code here and there. + + // convert to a truecolor bitmap, if we're a paletted one. There's + // room for tradeoff decision here, maybe later for an overload (or a flag) + if( GetBitCount() <= 8 ) + Convert( BMP_CONVERSION_24BIT ); + + BitmapReadAccess* pAlphaAcc = const_cast<AlphaMask&>(rAlpha).AcquireReadAccess(); + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pAlphaAcc && pAcc ) + { + const long nWidth = Min( pAlphaAcc->Width(), pAcc->Width() ); + const long nHeight = Min( pAlphaAcc->Height(), pAcc->Height() ); + + for( long nY = 0L; nY < nHeight; ++nY ) + for( long nX = 0L; nX < nWidth; ++nX ) + pAcc->SetPixel( nY, nX, + pAcc->GetPixel( nY, nX ).Merge( rBackgroundColor, + 255 - pAlphaAcc->GetPixel( nY, nX ) ) ); + + bRet = sal_True; + } + + const_cast<AlphaMask&>(rAlpha).ReleaseAccess( pAlphaAcc ); + ReleaseAccess( pAcc ); + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::MakeMono( sal_uInt8 cThreshold ) +{ + return ImplMakeMono( cThreshold ); +} + +// ------------------------------------------------------------------ + +bool Bitmap::GetSystemData( BitmapSystemData& rData ) const +{ + bool bRet = false; + if( mpImpBmp ) + { + SalBitmap* pSalBitmap = mpImpBmp->ImplGetSalBitmap(); + if( pSalBitmap ) + bRet = pSalBitmap->GetSystemData( rData ); + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bitmap2.cxx b/vcl/source/gdi/bitmap2.cxx new file mode 100644 index 000000000000..9db334395e23 --- /dev/null +++ b/vcl/source/gdi/bitmap2.cxx @@ -0,0 +1,1303 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <tools/zcodec.hxx> +#include <tools/stream.hxx> +#include <vcl/salbtype.hxx> +#include <vcl/bmpacc.hxx> +#include <vcl/outdev.hxx> +#include <vcl/bitmap.hxx> + +#include <utility> + + +// ----------- +// - Defines - +// ----------- + +#define DIBCOREHEADERSIZE ( 12UL ) +#define DIBINFOHEADERSIZE ( sizeof( DIBInfoHeader ) ) +#define BITMAPINFOHEADER 0x28 + +#define SETPIXEL4( pBuf, nX, cChar )( (pBuf)[ (nX) >> 1 ] |= ( (nX) & 1 ) ? ( cChar ): (cChar) << 4 ); + +// ---------------------- +// - Compression defines +// ---------------------- + +#define COMPRESS_OWN ('S'|('D'<<8UL)) +#define COMPRESS_NONE ( 0UL ) +#define RLE_8 ( 1UL ) +#define RLE_4 ( 2UL ) +#define BITFIELDS ( 3UL ) +#define ZCOMPRESS ( COMPRESS_OWN | 0x01000000UL ) /* == 'SD01' (binary) */ + +// ----------------- +// - DIBInfoHeader - +// ----------------- + +struct DIBInfoHeader +{ + sal_uInt32 nSize; + sal_Int32 nWidth; + sal_Int32 nHeight; + sal_uInt16 nPlanes; + sal_uInt16 nBitCount; + sal_uInt32 nCompression; + sal_uInt32 nSizeImage; + sal_Int32 nXPelsPerMeter; + sal_Int32 nYPelsPerMeter; + sal_uInt32 nColsUsed; + sal_uInt32 nColsImportant; + + DIBInfoHeader() : + nSize( 0UL ), + nWidth( 0UL ), + nHeight( 0UL ), + nPlanes( 0 ), + nBitCount( 0 ), + nCompression( 0 ), + nSizeImage( 0 ), + nXPelsPerMeter( 0UL ), + nYPelsPerMeter( 0UL ), + nColsUsed( 0UL ), + nColsImportant( 0UL ) {} + + ~DIBInfoHeader() {} +}; + +namespace +{ + inline sal_uInt16 discretizeBitcount( sal_uInt16 nInputCount ) + { + return ( nInputCount <= 1 ) ? 1 : + ( nInputCount <= 4 ) ? 4 : + ( nInputCount <= 8 ) ? 8 : 24; + } + + inline bool isBitfieldCompression( sal_uLong nScanlineFormat ) + { + return nScanlineFormat == BMP_FORMAT_16BIT_TC_LSB_MASK || + nScanlineFormat == BMP_FORMAT_32BIT_TC_MASK; + } +} + +// ---------- +// - Bitmap - +// ---------- + +SvStream& operator>>( SvStream& rIStm, Bitmap& rBitmap ) +{ + rBitmap.Read( rIStm, sal_True ); + return rIStm; +} + +// ------------------------------------------------------------------ + +SvStream& operator<<( SvStream& rOStm, const Bitmap& rBitmap ) +{ + rBitmap.Write( rOStm, sal_False, sal_True ); + return rOStm; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Read( SvStream& rIStm, sal_Bool bFileHeader, sal_Bool bIsMSOFormat ) +{ + const sal_uInt16 nOldFormat = rIStm.GetNumberFormatInt(); + const sal_uLong nOldPos = rIStm.Tell(); + sal_uLong nOffset = 0UL; + sal_Bool bRet = sal_False; + + rIStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + if( bFileHeader ) + { + if( ImplReadDIBFileHeader( rIStm, nOffset ) ) + bRet = ImplReadDIB( rIStm, *this, nOffset ); + } + else + bRet = ImplReadDIB( rIStm, *this, nOffset, bIsMSOFormat ); + + if( !bRet ) + { + if( !rIStm.GetError() ) + rIStm.SetError( SVSTREAM_GENERALERROR ); + + rIStm.Seek( nOldPos ); + } + + rIStm.SetNumberFormatInt( nOldFormat ); + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplReadDIB( SvStream& rIStm, Bitmap& rBmp, sal_uLong nOffset, sal_Bool bIsMSOFormat ) +{ + DIBInfoHeader aHeader; + const sal_uLong nStmPos = rIStm.Tell(); + sal_Bool bRet = sal_False; + sal_Bool bTopDown = sal_False; + + if( ImplReadDIBInfoHeader( rIStm, aHeader, bTopDown, bIsMSOFormat ) && aHeader.nWidth && aHeader.nHeight && aHeader.nBitCount ) + { + const sal_uInt16 nBitCount( discretizeBitcount(aHeader.nBitCount) ); + + const Size aSizePixel( aHeader.nWidth, abs(aHeader.nHeight) ); + BitmapPalette aDummyPal; + Bitmap aNewBmp( aSizePixel, nBitCount, &aDummyPal ); + BitmapWriteAccess* pAcc = aNewBmp.AcquireWriteAccess(); + + if( pAcc ) + { + sal_uInt16 nColors; + SvStream* pIStm; + SvMemoryStream* pMemStm = NULL; + sal_uInt8* pData = NULL; + + if( nBitCount <= 8 ) + { + if( aHeader.nColsUsed ) + nColors = (sal_uInt16) aHeader.nColsUsed; + else + nColors = ( 1 << aHeader.nBitCount ); + } + else + nColors = 0; + + if( ZCOMPRESS == aHeader.nCompression ) + { + ZCodec aCodec; + sal_uInt32 nCodedSize, nUncodedSize; + sal_uLong nCodedPos; + + // read coding information + rIStm >> nCodedSize >> nUncodedSize >> aHeader.nCompression; + pData = (sal_uInt8*) rtl_allocateMemory( nUncodedSize ); + + // decode buffer + nCodedPos = rIStm.Tell(); + aCodec.BeginCompression(); + aCodec.Read( rIStm, pData, nUncodedSize ); + aCodec.EndCompression(); + + // skip unread bytes from coded buffer + rIStm.SeekRel( nCodedSize - ( rIStm.Tell() - nCodedPos ) ); + + // set decoded bytes to memory stream, + // from which we will read the bitmap data + pMemStm = new SvMemoryStream; + pIStm = pMemStm; + pMemStm->SetBuffer( (char*) pData, nUncodedSize, sal_False, nUncodedSize ); + nOffset = 0; + } + else + pIStm = &rIStm; + + // read palette + if( nColors ) + { + pAcc->SetPaletteEntryCount( nColors ); + ImplReadDIBPalette( *pIStm, *pAcc, aHeader.nSize != DIBCOREHEADERSIZE ); + } + + // read bits + if( !pIStm->GetError() ) + { + if( nOffset ) + pIStm->SeekRel( nOffset - ( pIStm->Tell() - nStmPos ) ); + + bRet = ImplReadDIBBits( *pIStm, aHeader, *pAcc, bTopDown ); + + if( bRet && aHeader.nXPelsPerMeter && aHeader.nYPelsPerMeter ) + { + MapMode aMapMode( MAP_MM, Point(), + Fraction( 1000, aHeader.nXPelsPerMeter ), + Fraction( 1000, aHeader.nYPelsPerMeter ) ); + + aNewBmp.SetPrefMapMode( aMapMode ); + aNewBmp.SetPrefSize( Size( aHeader.nWidth, abs(aHeader.nHeight) ) ); + } + } + + if( pData ) + rtl_freeMemory( pData ); + + delete pMemStm; + aNewBmp.ReleaseAccess( pAcc ); + + if( bRet ) + rBmp = aNewBmp; + } + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplReadDIBFileHeader( SvStream& rIStm, sal_uLong& rOffset ) +{ + sal_uInt32 nTmp32; + sal_uInt16 nTmp16 = 0; + sal_Bool bRet = sal_False; + + rIStm >> nTmp16; + + if ( ( 0x4D42 == nTmp16 ) || ( 0x4142 == nTmp16 ) ) + { + if ( 0x4142 == nTmp16 ) + { + rIStm.SeekRel( 12L ); + rIStm >> nTmp16; + rIStm.SeekRel( 8L ); + rIStm >> nTmp32; + rOffset = nTmp32 - 28UL;; + bRet = ( 0x4D42 == nTmp16 ); + } + else + { + rIStm.SeekRel( 8L ); + rIStm >> nTmp32; + rOffset = nTmp32 - 14UL; + bRet = ( rIStm.GetError() == 0UL ); + } + } + else + rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR ); + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplReadDIBInfoHeader( SvStream& rIStm, DIBInfoHeader& rHeader, sal_Bool& bTopDown, sal_Bool bIsMSOFormat ) +{ + // BITMAPINFOHEADER or BITMAPCOREHEADER + rIStm >> rHeader.nSize; + + // BITMAPCOREHEADER + sal_Int16 nTmp16 = 0; + if ( rHeader.nSize == DIBCOREHEADERSIZE ) + { + + rIStm >> nTmp16; rHeader.nWidth = nTmp16; + rIStm >> nTmp16; rHeader.nHeight = nTmp16; + rIStm >> rHeader.nPlanes; + rIStm >> rHeader.nBitCount; + } + else if ( bIsMSOFormat && ( rHeader.nSize == BITMAPINFOHEADER ) ) + { + sal_uInt8 nTmp8 = 0; + rIStm >> nTmp16; rHeader.nWidth = nTmp16; + rIStm >> nTmp16; rHeader.nHeight = nTmp16; + rIStm >> nTmp8; rHeader.nPlanes = nTmp8; + rIStm >> nTmp8; rHeader.nBitCount = nTmp8; + rIStm >> nTmp16; rHeader.nSizeImage = nTmp16; + rIStm >> nTmp16; rHeader.nCompression = nTmp16; + if ( !rHeader.nSizeImage ) // uncompressed? + rHeader.nSizeImage = ((rHeader.nWidth * rHeader.nBitCount + 31) & ~31) / 8 * rHeader.nHeight; + rIStm >> rHeader.nXPelsPerMeter; + rIStm >> rHeader.nYPelsPerMeter; + rIStm >> rHeader.nColsUsed; + rIStm >> rHeader.nColsImportant; + } + else + { + // unknown Header + if( rHeader.nSize < DIBINFOHEADERSIZE ) + { + sal_uLong nUnknownSize = sizeof( rHeader.nSize ); + + rIStm >> rHeader.nWidth; nUnknownSize += sizeof( rHeader.nWidth ); + rIStm >> rHeader.nHeight; nUnknownSize += sizeof( rHeader.nHeight ); + rIStm >> rHeader.nPlanes; nUnknownSize += sizeof( rHeader.nPlanes ); + rIStm >> rHeader.nBitCount; nUnknownSize += sizeof( rHeader.nBitCount ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nCompression; + nUnknownSize += sizeof( rHeader.nCompression ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nSizeImage; + nUnknownSize += sizeof( rHeader.nSizeImage ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nXPelsPerMeter; + nUnknownSize += sizeof( rHeader.nXPelsPerMeter ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nYPelsPerMeter; + nUnknownSize += sizeof( rHeader.nYPelsPerMeter ); + } + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nColsUsed; + nUnknownSize += sizeof( rHeader.nColsUsed ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nColsImportant; + nUnknownSize += sizeof( rHeader.nColsImportant ); + } + } + } + } + } + } + else + { + rIStm >> rHeader.nWidth; + rIStm >> rHeader.nHeight; //rHeader.nHeight=abs(rHeader.nHeight); + rIStm >> rHeader.nPlanes; + rIStm >> rHeader.nBitCount; + rIStm >> rHeader.nCompression; + rIStm >> rHeader.nSizeImage; + rIStm >> rHeader.nXPelsPerMeter; + rIStm >> rHeader.nYPelsPerMeter; + rIStm >> rHeader.nColsUsed; + rIStm >> rHeader.nColsImportant; + } + + // Eventuell bis zur Palette ueberlesen + if ( rHeader.nSize > DIBINFOHEADERSIZE ) + rIStm.SeekRel( rHeader.nSize - DIBINFOHEADERSIZE ); + } + if ( rHeader.nHeight < 0 ) + { + bTopDown = sal_True; + rHeader.nHeight *= -1; + } + else + bTopDown = sal_False; + + if ( rHeader.nWidth < 0 ) + rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR ); + + // #144105# protect a little against damaged files + if( rHeader.nSizeImage > ( 16 * static_cast< sal_uInt32 >( rHeader.nWidth * rHeader.nHeight ) ) ) + rHeader.nSizeImage = 0; + + return( ( rHeader.nPlanes == 1 ) && ( rIStm.GetError() == 0UL ) ); +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplReadDIBPalette( SvStream& rIStm, BitmapWriteAccess& rAcc, sal_Bool bQuad ) +{ + const sal_uInt16 nColors = rAcc.GetPaletteEntryCount(); + const sal_uLong nPalSize = nColors * ( bQuad ? 4UL : 3UL ); + BitmapColor aPalColor; + + sal_uInt8* pEntries = new sal_uInt8[ nPalSize ]; + rIStm.Read( pEntries, nPalSize ); + + sal_uInt8* pTmpEntry = pEntries; + for( sal_uInt16 i = 0; i < nColors; i++ ) + { + aPalColor.SetBlue( *pTmpEntry++ ); + aPalColor.SetGreen( *pTmpEntry++ ); + aPalColor.SetRed( *pTmpEntry++ ); + + if( bQuad ) + pTmpEntry++; + + rAcc.SetPaletteColor( i, aPalColor ); + } + + delete[] pEntries; + + return( rIStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplReadDIBBits( SvStream& rIStm, DIBInfoHeader& rHeader, BitmapWriteAccess& rAcc, sal_Bool bTopDown ) +{ + const sal_uLong nAlignedWidth = AlignedWidth4Bytes( rHeader.nWidth * rHeader.nBitCount ); + sal_uInt32 nRMask = 0; + sal_uInt32 nGMask = 0; + sal_uInt32 nBMask = 0; + sal_Bool bNative; + sal_Bool bTCMask = ( rHeader.nBitCount == 16 ) || ( rHeader.nBitCount == 32 ); + sal_Bool bRLE = ( RLE_8 == rHeader.nCompression && rHeader.nBitCount == 8 ) || + ( RLE_4 == rHeader.nCompression && rHeader.nBitCount == 4 ); + + // Is native format? + switch( rAcc.GetScanlineFormat() ) + { + case( BMP_FORMAT_1BIT_MSB_PAL ): + case( BMP_FORMAT_4BIT_MSN_PAL ): + case( BMP_FORMAT_8BIT_PAL ): + case( BMP_FORMAT_24BIT_TC_BGR ): + bNative = ( ( rAcc.IsBottomUp() != bTopDown ) && !bRLE && !bTCMask && ( rAcc.GetScanlineSize() == nAlignedWidth ) ); + break; + + default: + bNative = sal_False; + break; + } + // Read data + if( bNative ) + { + // true color DIB's can have a (optimization) palette + if( rHeader.nColsUsed && rHeader.nBitCount > 8 ) + rIStm.SeekRel( rHeader.nColsUsed * ( ( rHeader.nSize != DIBCOREHEADERSIZE ) ? 4 : 3 ) ); + + if ( rHeader.nHeight > 0 ) + rIStm.Read( rAcc.GetBuffer(), rHeader.nHeight * nAlignedWidth ); + else + { + for( int i = abs(rHeader.nHeight)-1; i >= 0; i-- ) + rIStm.Read( ((char*)rAcc.GetBuffer()) + (nAlignedWidth*i), nAlignedWidth ); + } + } + else + { + // Read color mask + if( bTCMask ) + { + if( rHeader.nCompression == BITFIELDS ) + { + rIStm.SeekRel( -12L ); + rIStm >> nRMask; + rIStm >> nGMask; + rIStm >> nBMask; + } + else + { + nRMask = ( rHeader.nBitCount == 16 ) ? 0x00007c00UL : 0x00ff0000UL; + nGMask = ( rHeader.nBitCount == 16 ) ? 0x000003e0UL : 0x0000ff00UL; + nBMask = ( rHeader.nBitCount == 16 ) ? 0x0000001fUL : 0x000000ffUL; + } + } + + if( bRLE ) + { + if ( !rHeader.nSizeImage ) + { + const sal_uLong nOldPos = rIStm.Tell(); + + rIStm.Seek( STREAM_SEEK_TO_END ); + rHeader.nSizeImage = rIStm.Tell() - nOldPos; + rIStm.Seek( nOldPos ); + } + + sal_uInt8* pBuffer = (sal_uInt8*) rtl_allocateMemory( rHeader.nSizeImage ); + + rIStm.Read( (char*) pBuffer, rHeader.nSizeImage ); + ImplDecodeRLE( pBuffer, rHeader, rAcc, RLE_4 == rHeader.nCompression ); + + rtl_freeMemory( pBuffer ); + } + else + { + const long nWidth = rHeader.nWidth; + const long nHeight = abs(rHeader.nHeight); + sal_uInt8* pBuf = new sal_uInt8[ nAlignedWidth ]; + + // true color DIB's can have a (optimization) palette + if( rHeader.nColsUsed && rHeader.nBitCount > 8 ) + rIStm.SeekRel( rHeader.nColsUsed * ( ( rHeader.nSize != DIBCOREHEADERSIZE ) ? 4 : 3 ) ); + + const long nI = bTopDown ? 1 : -1; + long nY = bTopDown ? 0 : nHeight - 1; + long nCount = nHeight; + + switch( rHeader.nBitCount ) + { + case( 1 ): + { + sal_uInt8* pTmp; + sal_uInt8 cTmp; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( pTmp = pBuf, nAlignedWidth ); + cTmp = *pTmp++; + + for( long nX = 0L, nShift = 8L; nX < nWidth; nX++ ) + { + if( !nShift ) + { + nShift = 8L, + cTmp = *pTmp++; + } + + rAcc.SetPixel( nY, nX, sal::static_int_cast<sal_uInt8>(( cTmp >> --nShift ) & 1) ); + } + } + } + break; + + case( 4 ): + { + sal_uInt8* pTmp; + sal_uInt8 cTmp; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( pTmp = pBuf, nAlignedWidth ); + cTmp = *pTmp++; + + for( long nX = 0L, nShift = 2L; nX < nWidth; nX++ ) + { + if( !nShift ) + { + nShift = 2UL, + cTmp = *pTmp++; + } + + rAcc.SetPixel( nY, nX, sal::static_int_cast<sal_uInt8>(( cTmp >> ( --nShift << 2UL ) ) & 0x0f) ); + } + } + } + break; + + case( 8 ): + { + sal_uInt8* pTmp; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( pTmp = pBuf, nAlignedWidth ); + + for( long nX = 0L; nX < nWidth; nX++ ) + rAcc.SetPixel( nY, nX, *pTmp++ ); + } + } + break; + + case( 16 ): + { + ColorMask aMask( nRMask, nGMask, nBMask ); + BitmapColor aColor; + sal_uInt16* pTmp16; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( (char*)( pTmp16 = (sal_uInt16*) pBuf ), nAlignedWidth ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aMask.GetColorFor16BitLSB( aColor, (sal_uInt8*) pTmp16++ ); + rAcc.SetPixel( nY, nX, aColor ); + } + } + } + break; + + case( 24 ): + { + BitmapColor aPixelColor; + sal_uInt8* pTmp; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( pTmp = pBuf, nAlignedWidth ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aPixelColor.SetBlue( *pTmp++ ); + aPixelColor.SetGreen( *pTmp++ ); + aPixelColor.SetRed( *pTmp++ ); + rAcc.SetPixel( nY, nX, aPixelColor ); + } + } + } + break; + + case( 32 ): + { + ColorMask aMask( nRMask, nGMask, nBMask ); + BitmapColor aColor; + sal_uInt32* pTmp32; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( (char*)( pTmp32 = (sal_uInt32*) pBuf ), nAlignedWidth ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aMask.GetColorFor32Bit( aColor, (sal_uInt8*) pTmp32++ ); + rAcc.SetPixel( nY, nX, aColor ); + } + } + } + } + + delete[] pBuf; + } + } + + return( rIStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::Write( SvStream& rOStm, sal_Bool bCompressed, sal_Bool bFileHeader ) const +{ + DBG_ASSERT( mpImpBmp, "Empty Bitmaps can't be saved" ); + + const Size aSizePix( GetSizePixel() ); + sal_Bool bRet = sal_False; + + if( mpImpBmp && aSizePix.Width() && aSizePix.Height() ) + { + BitmapReadAccess* pAcc = ( (Bitmap*) this)->AcquireReadAccess(); + const sal_uInt16 nOldFormat = rOStm.GetNumberFormatInt(); + const sal_uLong nOldPos = rOStm.Tell(); + + rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + if( pAcc ) + { + if( bFileHeader ) + { + if( ImplWriteDIBFileHeader( rOStm, *pAcc ) ) + bRet = ImplWriteDIB( rOStm, *pAcc, bCompressed ); + } + else + bRet = ImplWriteDIB( rOStm, *pAcc, bCompressed ); + + ( (Bitmap*) this)->ReleaseAccess( pAcc ); + } + + if( !bRet ) + { + rOStm.SetError( SVSTREAM_GENERALERROR ); + rOStm.Seek( nOldPos ); + } + + rOStm.SetNumberFormatInt( nOldFormat ); + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplWriteDIB( SvStream& rOStm, BitmapReadAccess& rAcc, sal_Bool bCompressed ) const +{ + const MapMode aMapPixel( MAP_PIXEL ); + DIBInfoHeader aHeader; + sal_uLong nImageSizePos; + sal_uLong nEndPos; + sal_uInt32 nCompression = 0; + sal_Bool bRet = sal_False; + + aHeader.nSize = DIBINFOHEADERSIZE; + aHeader.nWidth = rAcc.Width(); + aHeader.nHeight = rAcc.Height(); + aHeader.nPlanes = 1; + + if( isBitfieldCompression( rAcc.GetScanlineFormat() ) ) + { + aHeader.nBitCount = ( rAcc.GetScanlineFormat() == BMP_FORMAT_16BIT_TC_LSB_MASK ) ? 16 : 32; + aHeader.nSizeImage = rAcc.Height() * rAcc.GetScanlineSize(); + + nCompression = BITFIELDS; + } + else + { + // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are + // not handled properly below (would have to set color + // masks, and nCompression=BITFIELDS - but color mask is + // not set for formats != *_TC_*). Note that this very + // problem might cause trouble at other places - the + // introduction of 32 bit RGBA bitmaps is relatively + // recent. + // #i59239# discretize bitcount to 1,4,8,24 (other cases + // are not written below) + const sal_uInt16 nBitCount( sal::static_int_cast<sal_uInt16>(rAcc.GetBitCount()) ); + + aHeader.nBitCount = discretizeBitcount( nBitCount ); + aHeader.nSizeImage = rAcc.Height() * + AlignedWidth4Bytes( rAcc.Width()*aHeader.nBitCount ); + + if( bCompressed ) + { + if( 4 == nBitCount ) + nCompression = RLE_4; + else if( 8 == nBitCount ) + nCompression = RLE_8; + } + else + nCompression = COMPRESS_NONE; + } + + if( ( rOStm.GetCompressMode() & COMPRESSMODE_ZBITMAP ) && + ( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_40 ) ) + { + aHeader.nCompression = ZCOMPRESS; + } + else + aHeader.nCompression = nCompression; + + if( maPrefSize.Width() && maPrefSize.Height() && ( maPrefMapMode != aMapPixel ) ) + { + // #i48108# Try to recover xpels/ypels as previously stored on + // disk. The problem with just converting maPrefSize to 100th + // mm and then relating that to the bitmap pixel size is that + // MapMode is integer-based, and suffers from roundoffs, + // especially if maPrefSize is small. Trying to circumvent + // that by performing part of the math in floating point. + const Size aScale100000( + OutputDevice::LogicToLogic( Size(100000L, + 100000L), + MAP_100TH_MM, + maPrefMapMode ) ); + const double fBmpWidthM((double)maPrefSize.Width() / aScale100000.Width() ); + const double fBmpHeightM((double)maPrefSize.Height() / aScale100000.Height() ); + if( fabs(fBmpWidthM) > 0.000000001 && + fabs(fBmpHeightM) > 0.000000001 ) + { + aHeader.nXPelsPerMeter = (sal_uInt32)(rAcc.Width() / fBmpWidthM + .5); + aHeader.nYPelsPerMeter = (sal_uInt32)(rAcc.Height() / fBmpHeightM + .5); + } + } + + aHeader.nColsUsed = ( ( aHeader.nBitCount <= 8 ) ? rAcc.GetPaletteEntryCount() : 0 ); + aHeader.nColsImportant = 0; + + rOStm << aHeader.nSize; + rOStm << aHeader.nWidth; + rOStm << aHeader.nHeight; + rOStm << aHeader.nPlanes; + rOStm << aHeader.nBitCount; + rOStm << aHeader.nCompression; + + nImageSizePos = rOStm.Tell(); + rOStm.SeekRel( sizeof( aHeader.nSizeImage ) ); + + rOStm << aHeader.nXPelsPerMeter; + rOStm << aHeader.nYPelsPerMeter; + rOStm << aHeader.nColsUsed; + rOStm << aHeader.nColsImportant; + + if( aHeader.nCompression == ZCOMPRESS ) + { + ZCodec aCodec; + SvMemoryStream aMemStm( aHeader.nSizeImage + 4096, 65535 ); + sal_uLong nCodedPos = rOStm.Tell(), nLastPos; + sal_uInt32 nCodedSize, nUncodedSize; + + // write uncoded data palette + if( aHeader.nColsUsed ) + ImplWriteDIBPalette( aMemStm, rAcc ); + + // write uncoded bits + bRet = ImplWriteDIBBits( aMemStm, rAcc, nCompression, aHeader.nSizeImage ); + + // get uncoded size + nUncodedSize = aMemStm.Tell(); + + // seek over compress info + rOStm.SeekRel( 12 ); + + // write compressed data + aCodec.BeginCompression( 3 ); + aCodec.Write( rOStm, (sal_uInt8*) aMemStm.GetData(), nUncodedSize ); + aCodec.EndCompression(); + + // update compress info ( coded size, uncoded size, uncoded compression ) + nCodedSize = ( nLastPos = rOStm.Tell() ) - nCodedPos - 12; + rOStm.Seek( nCodedPos ); + rOStm << nCodedSize << nUncodedSize << nCompression; + rOStm.Seek( nLastPos ); + + if( bRet ) + bRet = ( rOStm.GetError() == ERRCODE_NONE ); + } + else + { + if( aHeader.nColsUsed ) + ImplWriteDIBPalette( rOStm, rAcc ); + + bRet = ImplWriteDIBBits( rOStm, rAcc, aHeader.nCompression, aHeader.nSizeImage ); + } + + nEndPos = rOStm.Tell(); + rOStm.Seek( nImageSizePos ); + rOStm << aHeader.nSizeImage; + rOStm.Seek( nEndPos ); + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplWriteDIBFileHeader( SvStream& rOStm, BitmapReadAccess& rAcc ) +{ + sal_uInt32 nPalCount = ( rAcc.HasPalette() ? rAcc.GetPaletteEntryCount() : + isBitfieldCompression( rAcc.GetScanlineFormat() ) ? 3UL : 0UL ); + sal_uInt32 nOffset = 14 + DIBINFOHEADERSIZE + nPalCount * 4UL; + + rOStm << (sal_uInt16) 0x4D42; + rOStm << (sal_uInt32) ( nOffset + ( rAcc.Height() * rAcc.GetScanlineSize() ) ); + rOStm << (sal_uInt16) 0; + rOStm << (sal_uInt16) 0; + rOStm << nOffset; + + return( rOStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplWriteDIBPalette( SvStream& rOStm, BitmapReadAccess& rAcc ) +{ + const sal_uInt16 nColors = rAcc.GetPaletteEntryCount(); + const sal_uLong nPalSize = nColors * 4UL; + sal_uInt8* pEntries = new sal_uInt8[ nPalSize ]; + sal_uInt8* pTmpEntry = pEntries; + BitmapColor aPalColor; + + for( sal_uInt16 i = 0; i < nColors; i++ ) + { + const BitmapColor& rPalColor = rAcc.GetPaletteColor( i ); + + *pTmpEntry++ = rPalColor.GetBlue(); + *pTmpEntry++ = rPalColor.GetGreen(); + *pTmpEntry++ = rPalColor.GetRed(); + *pTmpEntry++ = 0; + } + + rOStm.Write( pEntries, nPalSize ); + delete[] pEntries; + + return( rOStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplWriteDIBBits( SvStream& rOStm, BitmapReadAccess& rAcc, + sal_uLong nCompression, sal_uInt32& rImageSize ) +{ + if( BITFIELDS == nCompression ) + { + const ColorMask& rMask = rAcc.GetColorMask(); + SVBT32 aVal32; + + UInt32ToSVBT32( rMask.GetRedMask(), aVal32 ); + rOStm.Write( (sal_uInt8*) aVal32, 4UL ); + + UInt32ToSVBT32( rMask.GetGreenMask(), aVal32 ); + rOStm.Write( (sal_uInt8*) aVal32, 4UL ); + + UInt32ToSVBT32( rMask.GetBlueMask(), aVal32 ); + rOStm.Write( (sal_uInt8*) aVal32, 4UL ); + + rImageSize = rOStm.Tell(); + + if( rAcc.IsBottomUp() ) + rOStm.Write( rAcc.GetBuffer(), rAcc.Height() * rAcc.GetScanlineSize() ); + else + { + for( long nY = rAcc.Height() - 1, nScanlineSize = rAcc.GetScanlineSize(); nY >= 0L; nY-- ) + rOStm.Write( rAcc.GetScanline( nY ), nScanlineSize ); + } + } + else if( ( RLE_4 == nCompression ) || ( RLE_8 == nCompression ) ) + { + rImageSize = rOStm.Tell(); + ImplWriteRLE( rOStm, rAcc, RLE_4 == nCompression ); + } + else if( !nCompression ) + { + // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are not + // handled properly below (would have to set color masks, and + // nCompression=BITFIELDS - but color mask is not set for + // formats != *_TC_*). Note that this very problem might cause + // trouble at other places - the introduction of 32 bit RGBA + // bitmaps is relatively recent. + // #i59239# discretize bitcount for aligned width to 1,4,8,24 + // (other cases are not written below) + const sal_uInt16 nBitCount( sal::static_int_cast<sal_uInt16>(rAcc.GetBitCount()) ); + const sal_uLong nAlignedWidth = AlignedWidth4Bytes( rAcc.Width() * + discretizeBitcount(nBitCount)); + sal_Bool bNative = sal_False; + + switch( rAcc.GetScanlineFormat() ) + { + case( BMP_FORMAT_1BIT_MSB_PAL ): + case( BMP_FORMAT_4BIT_MSN_PAL ): + case( BMP_FORMAT_8BIT_PAL ): + case( BMP_FORMAT_24BIT_TC_BGR ): + { + if( rAcc.IsBottomUp() && ( rAcc.GetScanlineSize() == nAlignedWidth ) ) + bNative = sal_True; + } + break; + + default: + break; + } + + rImageSize = rOStm.Tell(); + + if( bNative ) + rOStm.Write( rAcc.GetBuffer(), nAlignedWidth * rAcc.Height() ); + else + { + const long nWidth = rAcc.Width(); + const long nHeight = rAcc.Height(); + sal_uInt8* pBuf = new sal_uInt8[ nAlignedWidth ]; + sal_uInt8* pTmp; + sal_uInt8 cTmp; + + switch( nBitCount ) + { + case( 1 ): + { + for( long nY = nHeight - 1; nY >= 0L; nY-- ) + { + pTmp = pBuf; + cTmp = 0; + + for( long nX = 0L, nShift = 8L; nX < nWidth; nX++ ) + { + if( !nShift ) + { + nShift = 8L; + *pTmp++ = cTmp; + cTmp = 0; + } + + cTmp |= ( (sal_uInt8) rAcc.GetPixel( nY, nX ) << --nShift ); + } + + *pTmp = cTmp; + rOStm.Write( pBuf, nAlignedWidth ); + } + } + break; + + case( 4 ): + { + for( long nY = nHeight - 1; nY >= 0L; nY-- ) + { + pTmp = pBuf; + cTmp = 0; + + for( long nX = 0L, nShift = 2L; nX < nWidth; nX++ ) + { + if( !nShift ) + { + nShift = 2L; + *pTmp++ = cTmp; + cTmp = 0; + } + + cTmp |= ( (sal_uInt8) rAcc.GetPixel( nY, nX ) << ( --nShift << 2L ) ); + } + *pTmp = cTmp; + rOStm.Write( pBuf, nAlignedWidth ); + } + } + break; + + case( 8 ): + { + for( long nY = nHeight - 1; nY >= 0L; nY-- ) + { + pTmp = pBuf; + + for( long nX = 0L; nX < nWidth; nX++ ) + *pTmp++ = rAcc.GetPixel( nY, nX ); + + rOStm.Write( pBuf, nAlignedWidth ); + } + } + break; + + // #i59239# fallback to 24 bit format, if bitcount is non-default + default: + // FALLTHROUGH intended + case( 24 ): + { + BitmapColor aPixelColor; + + for( long nY = nHeight - 1; nY >= 0L; nY-- ) + { + pTmp = pBuf; + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aPixelColor = rAcc.GetPixel( nY, nX ); + *pTmp++ = aPixelColor.GetBlue(); + *pTmp++ = aPixelColor.GetGreen(); + *pTmp++ = aPixelColor.GetRed(); + } + + rOStm.Write( pBuf, nAlignedWidth ); + } + } + break; + } + + delete[] pBuf; + } + } + + rImageSize = rOStm.Tell() - rImageSize; + + return( rOStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +void Bitmap::ImplDecodeRLE( sal_uInt8* pBuffer, DIBInfoHeader& rHeader, + BitmapWriteAccess& rAcc, sal_Bool bRLE4 ) +{ + Scanline pRLE = pBuffer; + long nY = abs(rHeader.nHeight) - 1L; + const sal_uLong nWidth = rAcc.Width(); + sal_uLong nCountByte; + sal_uLong nRunByte; + sal_uLong nX = 0UL; + sal_uInt8 cTmp; + sal_Bool bEndDecoding = sal_False; + + do + { + if( ( nCountByte = *pRLE++ ) == 0 ) + { + nRunByte = *pRLE++; + + if( nRunByte > 2 ) + { + if( bRLE4 ) + { + nCountByte = nRunByte >> 1; + + for( sal_uLong i = 0UL; i < nCountByte; i++ ) + { + cTmp = *pRLE++; + + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, cTmp >> 4 ); + + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, cTmp & 0x0f ); + } + + if( nRunByte & 1 ) + { + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, *pRLE >> 4 ); + + pRLE++; + } + + if( ( ( nRunByte + 1 ) >> 1 ) & 1 ) + pRLE++; + } + else + { + for( sal_uLong i = 0UL; i < nRunByte; i++ ) + { + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, *pRLE ); + + pRLE++; + } + + if( nRunByte & 1 ) + pRLE++; + } + } + else if( !nRunByte ) + { + nY--; + nX = 0UL; + } + else if( nRunByte == 1 ) + bEndDecoding = sal_True; + else + { + nX += *pRLE++; + nY -= *pRLE++; + } + } + else + { + cTmp = *pRLE++; + + if( bRLE4 ) + { + nRunByte = nCountByte >> 1; + + for( sal_uLong i = 0UL; i < nRunByte; i++ ) + { + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, cTmp >> 4 ); + + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, cTmp & 0x0f ); + } + + if( ( nCountByte & 1 ) && ( nX < nWidth ) ) + rAcc.SetPixel( nY, nX++, cTmp >> 4 ); + } + else + { + for( sal_uLong i = 0UL; ( i < nCountByte ) && ( nX < nWidth ); i++ ) + rAcc.SetPixel( nY, nX++, cTmp ); + } + } + } + while ( !bEndDecoding && ( nY >= 0L ) ); +} + +// ------------------------------------------------------------------ + +sal_Bool Bitmap::ImplWriteRLE( SvStream& rOStm, BitmapReadAccess& rAcc, sal_Bool bRLE4 ) +{ + const sal_uLong nWidth = rAcc.Width(); + const sal_uLong nHeight = rAcc.Height(); + sal_uLong nX; + sal_uLong nSaveIndex; + sal_uLong nCount; + sal_uLong nBufCount; + sal_uInt8* pBuf = new sal_uInt8[ ( nWidth << 1 ) + 2 ]; + sal_uInt8* pTmp; + sal_uInt8 cPix; + sal_uInt8 cLast; + sal_Bool bFound; + + for ( long nY = nHeight - 1L; nY >= 0L; nY-- ) + { + pTmp = pBuf; + nX = nBufCount = 0UL; + + while( nX < nWidth ) + { + nCount = 1L; + cPix = rAcc.GetPixel( nY, nX++ ); + + while( ( nX < nWidth ) && ( nCount < 255L ) && ( cPix == rAcc.GetPixel( nY, nX ) ) ) + { + nX++; + nCount++; + } + + if ( nCount > 1 ) + { + *pTmp++ = (sal_uInt8) nCount; + *pTmp++ = ( bRLE4 ? ( ( cPix << 4 ) | cPix ) : cPix ); + nBufCount += 2; + } + else + { + cLast = cPix; + nSaveIndex = nX - 1UL; + bFound = sal_False; + + while( ( nX < nWidth ) && ( nCount < 256L ) && ( cPix = rAcc.GetPixel( nY, nX ) ) != cLast ) + { + nX++; nCount++; + cLast = cPix; + bFound = sal_True; + } + + if ( bFound ) + nX--; + + if ( nCount > 3 ) + { + *pTmp++ = 0; + *pTmp++ = (sal_uInt8) --nCount; + + if( bRLE4 ) + { + for ( sal_uLong i = 0; i < nCount; i++, pTmp++ ) + { + *pTmp = (sal_uInt8) rAcc.GetPixel( nY, nSaveIndex++ ) << 4; + + if ( ++i < nCount ) + *pTmp |= rAcc.GetPixel( nY, nSaveIndex++ ); + } + + nCount = ( nCount + 1 ) >> 1; + } + else + { + for( sal_uLong i = 0UL; i < nCount; i++ ) + *pTmp++ = rAcc.GetPixel( nY, nSaveIndex++ ); + } + + if ( nCount & 1 ) + { + *pTmp++ = 0; + nBufCount += ( nCount + 3 ); + } + else + nBufCount += ( nCount + 2 ); + } + else + { + *pTmp++ = 1; + *pTmp++ = (sal_uInt8) rAcc.GetPixel( nY, nSaveIndex ) << ( bRLE4 ? 4 : 0 ); + + if ( nCount == 3 ) + { + *pTmp++ = 1; + *pTmp++ = (sal_uInt8) rAcc.GetPixel( nY, ++nSaveIndex ) << ( bRLE4 ? 4 : 0 ); + nBufCount += 4; + } + else + nBufCount += 2; + } + } + } + + pBuf[ nBufCount++ ] = 0; + pBuf[ nBufCount++ ] = 0; + + rOStm.Write( pBuf, nBufCount ); + } + + rOStm << (sal_uInt8) 0; + rOStm << (sal_uInt8) 1; + + delete[] pBuf; + + return( rOStm.GetError() == 0UL ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bitmap3.cxx b/vcl/source/gdi/bitmap3.cxx new file mode 100644 index 000000000000..c2d929ea4fa0 --- /dev/null +++ b/vcl/source/gdi/bitmap3.cxx @@ -0,0 +1,2207 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdlib.h> +#include <vcl/bmpacc.hxx> +#include <vcl/impoct.hxx> +#include <vcl/octree.hxx> +#include <impvect.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/bitmap.hxx> + +// ----------- +// - Defines - +// ----------- + +#define RGB15( _def_cR, _def_cG, _def_cB ) (((sal_uLong)(_def_cR)<<10UL)|((sal_uLong)(_def_cG)<<5UL)|(sal_uLong)(_def_cB)) +#define GAMMA( _def_cVal, _def_InvGamma ) ((sal_uInt8)MinMax(FRound(pow( _def_cVal/255.0,_def_InvGamma)*255.0),0L,255L)) + +#define CALC_ERRORS \ + nTemp = p1T[nX++] >> 12; \ + nBErr = MinMax( nTemp, 0, 255 ); \ + nBErr = nBErr - FloydIndexMap[ nBC = FloydMap[nBErr] ]; \ + nTemp = p1T[nX++] >> 12; \ + nGErr = MinMax( nTemp, 0, 255 ); \ + nGErr = nGErr - FloydIndexMap[ nGC = FloydMap[nGErr] ]; \ + nTemp = p1T[nX] >> 12; \ + nRErr = MinMax( nTemp, 0, 255 ); \ + nRErr = nRErr - FloydIndexMap[ nRC = FloydMap[nRErr] ]; + +#define CALC_TABLES3 \ + p2T[nX++] += FloydError3[nBErr]; \ + p2T[nX++] += FloydError3[nGErr]; \ + p2T[nX++] += FloydError3[nRErr]; + +#define CALC_TABLES5 \ + p2T[nX++] += FloydError5[nBErr]; \ + p2T[nX++] += FloydError5[nGErr]; \ + p2T[nX++] += FloydError5[nRErr]; + +#define CALC_TABLES7 \ + p1T[++nX] += FloydError7[nBErr]; \ + p2T[nX++] += FloydError1[nBErr]; \ + p1T[nX] += FloydError7[nGErr]; \ + p2T[nX++] += FloydError1[nGErr]; \ + p1T[nX] += FloydError7[nRErr]; \ + p2T[nX] += FloydError1[nRErr]; + +// ----------- +// - Statics - +// ----------- + +sal_uLong nVCLRLut[ 6 ] = { 16, 17, 18, 19, 20, 21 }; +sal_uLong nVCLGLut[ 6 ] = { 0, 6, 12, 18, 24, 30 }; +sal_uLong nVCLBLut[ 6 ] = { 0, 36, 72, 108, 144, 180 }; + +// ------------------------------------------------------------------------ + +sal_uLong nVCLDitherLut[ 256 ] = +{ + 0, 49152, 12288, 61440, 3072, 52224, 15360, 64512, 768, 49920, 13056, + 62208, 3840, 52992, 16128, 65280, 32768, 16384, 45056, 28672, 35840, 19456, + 48128, 31744, 33536, 17152, 45824, 29440, 36608, 20224, 48896, 32512, 8192, + 57344, 4096, 53248, 11264, 60416, 7168, 56320, 8960, 58112, 4864, 54016, + 12032, 61184, 7936, 57088, 40960, 24576, 36864, 20480, 44032, 27648, 39936, + 23552, 41728, 25344, 37632, 21248, 44800, 28416, 40704, 24320, 2048, 51200, + 14336, 63488, 1024, 50176, 13312, 62464, 2816, 51968, 15104, 64256, 1792, + 50944, 14080, 63232, 34816, 18432, 47104, 30720, 33792, 17408, 46080, 29696, + 35584, 19200, 47872, 31488, 34560, 18176, 46848, 30464, 10240, 59392, 6144, + 55296, 9216, 58368, 5120, 54272, 11008, 60160, 6912, 56064, 9984, 59136, + 5888, 55040, 43008, 26624, 38912, 22528, 41984, 25600, 37888, 21504, 43776, + 27392, 39680, 23296, 42752, 26368, 38656, 22272, 512, 49664, 12800, 61952, + 3584, 52736, 15872, 65024, 256, 49408, 12544, 61696, 3328, 52480, 15616, + 64768, 33280, 16896, 45568, 29184, 36352, 19968, 48640, 32256, 33024, 16640, + 45312, 28928, 36096, 19712, 48384, 32000, 8704, 57856, 4608, 53760, 11776, + 60928, 7680, 56832, 8448, 57600, 4352, 53504, 11520, 60672, 7424, 56576, + 41472, 25088, 37376, 20992, 44544, 28160, 40448, 24064, 41216, 24832, 37120, + 20736, 44288, 27904, 40192, 23808, 2560, 51712, 14848, 64000, 1536, 50688, + 13824, 62976, 2304, 51456, 14592, 63744, 1280, 50432, 13568, 62720, 35328, + 18944, 47616, 31232, 34304, 17920, 46592, 30208, 35072, 18688, 47360, 30976, + 34048, 17664, 46336, 29952, 10752, 59904, 6656, 55808, 9728, 58880, 5632, + 54784, 10496, 59648, 6400, 55552, 9472, 58624, 5376, 54528, 43520, 27136, + 39424, 23040, 42496, 26112, 38400, 22016, 43264, 26880, 39168, 22784, 42240, + 25856, 38144, 21760 +}; + +// ------------------------------------------------------------------------ + +sal_uLong nVCLLut[ 256 ] = +{ + 0, 1286, 2572, 3858, 5144, 6430, 7716, 9002, + 10288, 11574, 12860, 14146, 15432, 16718, 18004, 19290, + 20576, 21862, 23148, 24434, 25720, 27006, 28292, 29578, + 30864, 32150, 33436, 34722, 36008, 37294, 38580, 39866, + 41152, 42438, 43724, 45010, 46296, 47582, 48868, 50154, + 51440, 52726, 54012, 55298, 56584, 57870, 59156, 60442, + 61728, 63014, 64300, 65586, 66872, 68158, 69444, 70730, + 72016, 73302, 74588, 75874, 77160, 78446, 79732, 81018, + 82304, 83590, 84876, 86162, 87448, 88734, 90020, 91306, + 92592, 93878, 95164, 96450, 97736, 99022,100308,101594, + 102880,104166,105452,106738,108024,109310,110596,111882, + 113168,114454,115740,117026,118312,119598,120884,122170, + 123456,124742,126028,127314,128600,129886,131172,132458, + 133744,135030,136316,137602,138888,140174,141460,142746, + 144032,145318,146604,147890,149176,150462,151748,153034, + 154320,155606,156892,158178,159464,160750,162036,163322, + 164608,165894,167180,168466,169752,171038,172324,173610, + 174896,176182,177468,178754,180040,181326,182612,183898, + 185184,186470,187756,189042,190328,191614,192900,194186, + 195472,196758,198044,199330,200616,201902,203188,204474, + 205760,207046,208332,209618,210904,212190,213476,214762, + 216048,217334,218620,219906,221192,222478,223764,225050, + 226336,227622,228908,230194,231480,232766,234052,235338, + 236624,237910,239196,240482,241768,243054,244340,245626, + 246912,248198,249484,250770,252056,253342,254628,255914, + 257200,258486,259772,261058,262344,263630,264916,266202, + 267488,268774,270060,271346,272632,273918,275204,276490, + 277776,279062,280348,281634,282920,284206,285492,286778, + 288064,289350,290636,291922,293208,294494,295780,297066, + 298352,299638,300924,302210,303496,304782,306068,307354, + 308640,309926,311212,312498,313784,315070,316356,317642, + 318928,320214,321500,322786,324072,325358,326644,327930 +}; + +// ------------------------------------------------------------------------ + +long FloydMap[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 +}; + +// ------------------------------------------------------------------------ + +long FloydError1[61] = +{ + -7680, -7424, -7168, -6912, -6656, -6400, -6144, + -5888, -5632, -5376, -5120, -4864, -4608, -4352, + -4096, -3840, -3584, -3328, -3072, -2816, -2560, + -2304, -2048, -1792, -1536, -1280, -1024, -768, + -512, -256, 0, 256, 512, 768, 1024, 1280, 1536, + 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, + 3840, 4096, 4352, 4608, 4864, 5120, 5376, 5632, + 5888, 6144, 6400, 6656, 6912, 7168, 7424, 7680 +}; + +// ------------------------------------------------------------------------ + +long FloydError3[61] = +{ + -23040, -22272, -21504, -20736, -19968, -19200, + -18432, -17664, -16896, -16128, -15360, -14592, + -13824, -13056, -12288, -11520, -10752, -9984, + -9216, -8448, -7680, -6912, -6144, -5376, -4608, + -3840, -3072, -2304, -1536, -768, 0, 768, 1536, + 2304, 3072, 3840, 4608, 5376, 6144, 6912, 7680, + 8448, 9216, 9984, 10752, 11520, 12288, 13056, + 13824, 14592, 15360, 16128, 16896, 17664, 18432, + 19200, 19968, 20736, 21504, 22272, 23040 +}; + +// ------------------------------------------------------------------------ + +long FloydError5[61] = +{ + -38400, -37120, -35840, -34560, -33280, -32000, + -30720, -29440, -28160, -26880, -25600, -24320, + -23040, -21760, -20480, -19200, -17920, -16640, + -15360, -14080, -12800, -11520, -10240, -8960, + -7680, -6400, -5120, -3840, -2560, -1280, 0, + 1280, 2560, 3840, 5120, 6400, 7680, 8960, 10240, + 11520, 12800, 14080, 15360, 16640, 17920, 19200, + 20480, 21760, 23040, 24320, 25600, 26880, 28160, + 29440, 30720, 32000, 33280, 34560, 35840, 37120, + 38400 +}; + +// ------------------------------------------------------------------------ + +long FloydError7[61] = +{ + -53760, -51968, -50176, -48384, -46592, -44800, + -43008, -41216, -39424, -37632, -35840, -34048, + -32256, -30464, -28672, -26880, -25088, -23296, + -21504, -19712, -17920, -16128, -14336, -12544, + -10752, -8960, -7168, -5376, -3584, -1792, 0, + 1792, 3584, 5376, 7168, 8960, 10752, 12544, 14336, + 16128, 17920, 19712, 21504, 23296, 25088, 26880, + 28672, 30464, 32256, 34048, 35840, 37632, 39424, + 41216, 43008, 44800, 46592, 48384, 50176, 51968, + 53760 +}; + +// ------------------------------------------------------------------------ + +long FloydIndexMap[6] = +{ + -30, 21, 72, 123, 174, 225 +}; + +// -------------------------- +// - ImplCreateDitherMatrix - +// -------------------------- + +void ImplCreateDitherMatrix( sal_uInt8 (*pDitherMatrix)[16][16] ) +{ + double fVal = 3.125; + const double fVal16 = fVal / 16.; + long i, j, k, l; + sal_uInt16 pMtx[ 16 ][ 16 ]; + sal_uInt16 nMax = 0; + static sal_uInt8 pMagic[4][4] = { { 0, 14, 3, 13, }, + {11, 5, 8, 6, }, + {12, 2, 15, 1, }, + {7, 9, 4, 10 } }; + + // MagicSquare aufbauen + for ( i = 0; i < 4; i++ ) + for ( j = 0; j < 4; j++ ) + for ( k = 0; k < 4; k++ ) + for ( l = 0; l < 4; l++ ) + nMax = Max ( pMtx[ (k<<2) + i][(l<<2 ) + j] = + (sal_uInt16) ( 0.5 + pMagic[i][j]*fVal + pMagic[k][l]*fVal16 ), nMax ); + + // auf Intervall [0;254] skalieren + for ( i = 0, fVal = 254. / nMax; i < 16; i++ ) + for( j = 0; j < 16; j++ ) + (*pDitherMatrix)[i][j] = (sal_uInt8) ( fVal * pMtx[i][j] ); +} + +// ---------- +// - Bitmap - +// ---------- + +sal_Bool Bitmap::Convert( BmpConversion eConversion ) +{ + const sal_uInt16 nBitCount = GetBitCount(); + sal_Bool bRet = sal_False; + + switch( eConversion ) + { + case( BMP_CONVERSION_1BIT_THRESHOLD ): + bRet = ImplMakeMono( 128 ); + break; + + case( BMP_CONVERSION_1BIT_MATRIX ): + bRet = ImplMakeMonoDither(); + break; + + case( BMP_CONVERSION_4BIT_GREYS ): + bRet = ImplMakeGreyscales( 16 ); + break; + + case( BMP_CONVERSION_4BIT_COLORS ): + { + if( nBitCount < 4 ) + bRet = ImplConvertUp( 4, NULL ); + else if( nBitCount > 4 ) + bRet = ImplConvertDown( 4, NULL ); + else + bRet = sal_True; + } + break; + + case( BMP_CONVERSION_4BIT_TRANS ): + { + Color aTrans( BMP_COL_TRANS ); + + if( nBitCount < 4 ) + bRet = ImplConvertUp( 4, &aTrans ); + else + bRet = ImplConvertDown( 4, &aTrans ); + } + break; + + case( BMP_CONVERSION_8BIT_GREYS ): + bRet = ImplMakeGreyscales( 256 ); + break; + + case( BMP_CONVERSION_8BIT_COLORS ): + { + if( nBitCount < 8 ) + bRet = ImplConvertUp( 8 ); + else if( nBitCount > 8 ) + bRet = ImplConvertDown( 8 ); + else + bRet = sal_True; + } + break; + + case( BMP_CONVERSION_8BIT_TRANS ): + { + Color aTrans( BMP_COL_TRANS ); + + if( nBitCount < 8 ) + bRet = ImplConvertUp( 8, &aTrans ); + else + bRet = ImplConvertDown( 8, &aTrans ); + } + break; + + case( BMP_CONVERSION_24BIT ): + { + if( nBitCount < 24 ) + bRet = ImplConvertUp( 24, sal_False ); + else + bRet = sal_True; + } + break; + + case( BMP_CONVERSION_GHOSTED ): + bRet = ImplConvertGhosted(); + break; + + default: + OSL_FAIL( "Bitmap::Convert(): Unsupported conversion" ); + break; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplMakeMono( sal_uInt8 cThreshold ) +{ + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc ) + { + Bitmap aNewBmp( GetSizePixel(), 1 ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const BitmapColor aBlack( pWriteAcc->GetBestMatchingColor( Color( COL_BLACK ) ) ); + const BitmapColor aWhite( pWriteAcc->GetBestMatchingColor( Color( COL_WHITE ) ) ); + const long nWidth = pWriteAcc->Width(); + const long nHeight = pWriteAcc->Height(); + + if( pReadAcc->HasPalette() ) + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + if( pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nY, nX ) ).GetLuminance() >= + cThreshold ) + { + pWriteAcc->SetPixel( nY, nX, aWhite ); + } + else + pWriteAcc->SetPixel( nY, nX, aBlack ); + } + } + } + else + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + if( pReadAcc->GetPixel( nY, nX ).GetLuminance() >= + cThreshold ) + { + pWriteAcc->SetPixel( nY, nX, aWhite ); + } + else + pWriteAcc->SetPixel( nY, nX, aBlack ); + } + } + } + + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplMakeMonoDither() +{ + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc ) + { + Bitmap aNewBmp( GetSizePixel(), 1 ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const BitmapColor aBlack( pWriteAcc->GetBestMatchingColor( Color( COL_BLACK ) ) ); + const BitmapColor aWhite( pWriteAcc->GetBestMatchingColor( Color( COL_WHITE ) ) ); + const long nWidth = pWriteAcc->Width(); + const long nHeight = pWriteAcc->Height(); + sal_uInt8 pDitherMatrix[ 16 ][ 16 ]; + + ImplCreateDitherMatrix( &pDitherMatrix ); + + if( pReadAcc->HasPalette() ) + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L, nModY = nY % 16; nX < nWidth; nX++ ) + { + if( pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nY, nX ) ).GetLuminance() > + pDitherMatrix[ nModY ][ nX % 16 ] ) + { + pWriteAcc->SetPixel( nY, nX, aWhite ); + } + else + pWriteAcc->SetPixel( nY, nX, aBlack ); + } + } + } + else + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L, nModY = nY % 16; nX < nWidth; nX++ ) + { + if( pReadAcc->GetPixel( nY, nX ).GetLuminance() > + pDitherMatrix[ nModY ][ nX % 16 ] ) + { + pWriteAcc->SetPixel( nY, nX, aWhite ); + } + else + pWriteAcc->SetPixel( nY, nX, aBlack ); + } + } + } + + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplMakeGreyscales( sal_uInt16 nGreys ) +{ + DBG_ASSERT( nGreys == 16 || nGreys == 256, "Only 16 or 256 greyscales are supported!" ); + + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc ) + { + const BitmapPalette& rPal = GetGreyPalette( nGreys ); + sal_uLong nShift = ( ( nGreys == 16 ) ? 4UL : 0UL ); + sal_Bool bPalDiffers = !pReadAcc->HasPalette() || ( rPal.GetEntryCount() != pReadAcc->GetPaletteEntryCount() ); + + if( !bPalDiffers ) + bPalDiffers = ( (BitmapPalette&) rPal != pReadAcc->GetPalette() ); + + if( bPalDiffers ) + { + Bitmap aNewBmp( GetSizePixel(), ( nGreys == 16 ) ? 4 : 8, &rPal ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = pWriteAcc->Width(); + const long nHeight = pWriteAcc->Height(); + + if( pReadAcc->HasPalette() ) + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + pWriteAcc->SetPixel( nY, nX, + (sal_uInt8) ( pReadAcc->GetPaletteColor( + pReadAcc->GetPixel( nY, nX ) ).GetLuminance() >> nShift ) ); + } + } + } + else if( pReadAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR && + pWriteAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ) + { + nShift += 8; + + for( long nY = 0L; nY < nHeight; nY++ ) + { + Scanline pReadScan = pReadAcc->GetScanline( nY ); + Scanline pWriteScan = pWriteAcc->GetScanline( nY ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + const sal_uLong nB = *pReadScan++; + const sal_uLong nG = *pReadScan++; + const sal_uLong nR = *pReadScan++; + + *pWriteScan++ = (sal_uInt8) ( ( nB * 28UL + nG * 151UL + nR * 77UL ) >> nShift ); + } + } + } + else if( pReadAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB && + pWriteAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ) + { + nShift += 8; + + for( long nY = 0L; nY < nHeight; nY++ ) + { + Scanline pReadScan = pReadAcc->GetScanline( nY ); + Scanline pWriteScan = pWriteAcc->GetScanline( nY ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + const sal_uLong nR = *pReadScan++; + const sal_uLong nG = *pReadScan++; + const sal_uLong nB = *pReadScan++; + + *pWriteScan++ = (sal_uInt8) ( ( nB * 28UL + nG * 151UL + nR * 77UL ) >> nShift ); + } + } + } + else + { + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX = 0L; nX < nWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, sal::static_int_cast<sal_uInt8>(( pReadAcc->GetPixel( nY, nX ) ).GetLuminance() >> nShift) ); + } + + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + else + { + ReleaseAccess( pReadAcc ); + bRet = sal_True; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplConvertUp( sal_uInt16 nBitCount, Color* pExtColor ) +{ + DBG_ASSERT( nBitCount > GetBitCount(), "New BitCount must be greater!" ); + + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc ) + { + BitmapPalette aPal; + Bitmap aNewBmp( GetSizePixel(), nBitCount, pReadAcc->HasPalette() ? &pReadAcc->GetPalette() : &aPal ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = pWriteAcc->Width(); + const long nHeight = pWriteAcc->Height(); + + if( pWriteAcc->HasPalette() ) + { + const sal_uInt16 nOldCount = 1 << GetBitCount(); + const BitmapPalette& rOldPal = pReadAcc->GetPalette(); + + aPal.SetEntryCount( 1 << nBitCount ); + + for( sal_uInt16 i = 0; i < nOldCount; i++ ) + aPal[ i ] = rOldPal[ i ]; + + if( pExtColor ) + aPal[ aPal.GetEntryCount() - 1 ] = *pExtColor; + + pWriteAcc->SetPalette( aPal ); + + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX = 0L; nX < nWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nY, nX ) ); + } + else + { + if( pReadAcc->HasPalette() ) + { + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX = 0L; nX < nWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nY, nX ) ) ); + } + else + { + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX = 0L; nX < nWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nY, nX ) ); + } + } + + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplConvertDown( sal_uInt16 nBitCount, Color* pExtColor ) +{ + DBG_ASSERT( nBitCount <= GetBitCount(), "New BitCount must be lower ( or equal when pExtColor is set )!" ); + + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc ) + { + BitmapPalette aPal; + Bitmap aNewBmp( GetSizePixel(), nBitCount, &aPal ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const sal_uInt16 nCount = 1 << nBitCount; + const long nWidth = pWriteAcc->Width(); + const long nWidth1 = nWidth - 1L; + const long nHeight = pWriteAcc->Height(); + Octree aOctree( *pReadAcc, pExtColor ? ( nCount - 1 ) : nCount ); + InverseColorMap aColorMap( aPal = aOctree.GetPalette() ); + BitmapColor aColor; + ImpErrorQuad aErrQuad; + ImpErrorQuad* pErrQuad1 = new ImpErrorQuad[ nWidth ]; + ImpErrorQuad* pErrQuad2 = new ImpErrorQuad[ nWidth ]; + ImpErrorQuad* pQLine1 = pErrQuad1; + ImpErrorQuad* pQLine2 = 0; + long nX, nY; + long nYTmp = 0L; + sal_uInt8 cIndex; + sal_Bool bQ1 = sal_True; + + if( pExtColor ) + { + aPal.SetEntryCount( aPal.GetEntryCount() + 1 ); + aPal[ aPal.GetEntryCount() - 1 ] = *pExtColor; + } + + // set Black/White always, if we have enough space + if( aPal.GetEntryCount() < ( nCount - 1 ) ) + { + aPal.SetEntryCount( aPal.GetEntryCount() + 2 ); + aPal[ aPal.GetEntryCount() - 2 ] = Color( COL_BLACK ); + aPal[ aPal.GetEntryCount() - 1 ] = Color( COL_WHITE ); + } + + pWriteAcc->SetPalette( aPal ); + + for( nY = 0L; nY < Min( nHeight, 2L ); nY++, nYTmp++ ) + { + for( nX = 0L, pQLine2 = !nY ? pErrQuad1 : pErrQuad2; nX < nWidth; nX++ ) + { + if( pReadAcc->HasPalette() ) + pQLine2[ nX ] = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nYTmp, nX ) ); + else + pQLine2[ nX ] = pReadAcc->GetPixel( nYTmp, nX ); + } + } + + for( nY = 0L; nY < nHeight; nY++, nYTmp++ ) + { + // erstes ZeilenPixel + cIndex = (sal_uInt8) aColorMap.GetBestPaletteIndex( pQLine1[ 0 ].ImplGetColor() ); + pWriteAcc->SetPixel( nY, 0, cIndex ); + + for( nX = 1L; nX < nWidth1; nX++ ) + { + cIndex = (sal_uInt8) aColorMap.GetBestPaletteIndex( aColor = pQLine1[ nX ].ImplGetColor() ); + aErrQuad = ( ImpErrorQuad( aColor ) -= pWriteAcc->GetPaletteColor( cIndex ) ); + pQLine1[ ++nX ].ImplAddColorError7( aErrQuad ); + pQLine2[ nX-- ].ImplAddColorError1( aErrQuad ); + pQLine2[ nX-- ].ImplAddColorError5( aErrQuad ); + pQLine2[ nX++ ].ImplAddColorError3( aErrQuad ); + pWriteAcc->SetPixel( nY, nX, cIndex ); + } + + // letztes ZeilenPixel + if( nX < nWidth ) + { + cIndex = (sal_uInt8) aColorMap.GetBestPaletteIndex( pQLine1[ nWidth1 ].ImplGetColor() ); + pWriteAcc->SetPixel( nY, nX, cIndex ); + } + + // Zeilenpuffer neu fuellen/kopieren + pQLine1 = pQLine2; + pQLine2 = ( bQ1 = !bQ1 ) != sal_False ? pErrQuad2 : pErrQuad1; + + if( nYTmp < nHeight ) + { + for( nX = 0L; nX < nWidth; nX++ ) + { + if( pReadAcc->HasPalette() ) + pQLine2[ nX ] = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nYTmp, nX ) ); + else + pQLine2[ nX ] = pReadAcc->GetPixel( nYTmp, nX ); + } + } + } + + // Zeilenpuffer zerstoeren + delete[] pErrQuad1; + delete[] pErrQuad2; + + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplConvertGhosted() +{ + Bitmap aNewBmp; + BitmapReadAccess* pR = AcquireReadAccess(); + sal_Bool bRet = sal_False; + + if( pR ) + { + if( pR->HasPalette() ) + { + BitmapPalette aNewPal( pR->GetPaletteEntryCount() ); + + for( long i = 0, nCount = aNewPal.GetEntryCount(); i < nCount; i++ ) + { + const BitmapColor& rOld = pR->GetPaletteColor( (sal_uInt16) i ); + aNewPal[ (sal_uInt16) i ] = BitmapColor( ( rOld.GetRed() >> 1 ) | 0x80, + ( rOld.GetGreen() >> 1 ) | 0x80, + ( rOld.GetBlue() >> 1 ) | 0x80 ); + } + + aNewBmp = Bitmap( GetSizePixel(), GetBitCount(), &aNewPal ); + BitmapWriteAccess* pW = aNewBmp.AcquireWriteAccess(); + + if( pW ) + { + pW->CopyBuffer( *pR ); + aNewBmp.ReleaseAccess( pW ); + bRet = sal_True; + } + } + else + { + aNewBmp = Bitmap( GetSizePixel(), 24 ); + + BitmapWriteAccess* pW = aNewBmp.AcquireWriteAccess(); + + if( pW ) + { + const long nWidth = pR->Width(), nHeight = pR->Height(); + + for( long nY = 0; nY < nHeight; nY++ ) + { + for( long nX = 0; nX < nWidth; nX++ ) + { + const BitmapColor aOld( pR->GetPixel( nY, nX ) ); + pW->SetPixel( nY, nX, BitmapColor( ( aOld.GetRed() >> 1 ) | 0x80, + ( aOld.GetGreen() >> 1 ) | 0x80, + ( aOld.GetBlue() >> 1 ) | 0x80 ) ); + + } + } + + aNewBmp.ReleaseAccess( pW ); + bRet = sal_True; + } + } + + ReleaseAccess( pR ); + } + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::Scale( const double& rScaleX, const double& rScaleY, sal_uLong nScaleFlag ) +{ + sal_Bool bRet; + + if( ( rScaleX != 1.0 ) || ( rScaleY != 1.0 ) ) + { + if( BMP_SCALE_FAST == nScaleFlag ) + bRet = ImplScaleFast( rScaleX, rScaleY ); + else if( BMP_SCALE_INTERPOLATE == nScaleFlag ) + bRet = ImplScaleInterpolate( rScaleX, rScaleY ); + else + bRet = sal_False; + } + else + bRet = sal_True; + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::Scale( const Size& rNewSize, sal_uLong nScaleFlag ) +{ + const Size aSize( GetSizePixel() ); + sal_Bool bRet; + + if( aSize.Width() && aSize.Height() ) + { + bRet = Scale( (double) rNewSize.Width() / aSize.Width(), + (double) rNewSize.Height() / aSize.Height(), + nScaleFlag ); + } + else + bRet = sal_True; + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplScaleFast( const double& rScaleX, const double& rScaleY ) +{ + const Size aSizePix( GetSizePixel() ); + const long nNewWidth = FRound( aSizePix.Width() * rScaleX ); + const long nNewHeight = FRound( aSizePix.Height() * rScaleY ); + sal_Bool bRet = sal_False; + + if( nNewWidth && nNewHeight ) + { + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + Bitmap aNewBmp( Size( nNewWidth, nNewHeight ), GetBitCount(), &pReadAcc->GetPalette() ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pReadAcc && pWriteAcc ) + { + const long nScanlineSize = pWriteAcc->GetScanlineSize(); + const long nNewWidth1 = nNewWidth - 1L; + const long nNewHeight1 = nNewHeight - 1L; + const long nWidth = pReadAcc->Width(); + const long nHeight = pReadAcc->Height(); + long* pLutX = new long[ nNewWidth ]; + long* pLutY = new long[ nNewHeight ]; + + if( nNewWidth1 && nNewHeight1 ) + { + long nX, nY, nMapY, nActY = 0L; + + for( nX = 0L; nX < nNewWidth; nX++ ) + pLutX[ nX ] = nX * nWidth / nNewWidth; + + for( nY = 0L; nY < nNewHeight; nY++ ) + pLutY[ nY ] = nY * nHeight / nNewHeight; + + while( nActY < nNewHeight ) + { + nMapY = pLutY[ nActY ]; + + for( nX = 0L; nX < nNewWidth; nX++ ) + pWriteAcc->SetPixel( nActY, nX, pReadAcc->GetPixel( nMapY , pLutX[ nX ] ) ); + + while( ( nActY < nNewHeight1 ) && ( pLutY[ nActY + 1 ] == nMapY ) ) + { + memcpy( pWriteAcc->GetScanline( nActY + 1L ), + pWriteAcc->GetScanline( nActY ), nScanlineSize ); + nActY++; + } + + nActY++; + } + + bRet = sal_True; + } + + delete[] pLutX; + delete[] pLutY; + } + + ReleaseAccess( pReadAcc ); + aNewBmp.ReleaseAccess( pWriteAcc ); + + if( bRet ) + ImplAssignWithSize( aNewBmp ); + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplScaleInterpolate( const double& rScaleX, const double& rScaleY ) +{ + const Size aSizePix( GetSizePixel() ); + const long nNewWidth = FRound( aSizePix.Width() * rScaleX ); + const long nNewHeight = FRound( aSizePix.Height() * rScaleY ); + sal_Bool bRet = sal_False; + + if( ( nNewWidth > 1L ) && ( nNewHeight > 1L ) ) + { + BitmapColor aCol0; + BitmapColor aCol1; + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + long nWidth = pReadAcc->Width(); + long nHeight = pReadAcc->Height(); + Bitmap aNewBmp( Size( nNewWidth, nHeight ), 24 ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + long* pLutInt; + long* pLutFrac; + long nX, nY; + long lXB0, lXB1, lXG0, lXG1, lXR0, lXR1; + double fTemp; + long nTemp; + + if( pReadAcc && pWriteAcc ) + { + const long nNewWidth1 = nNewWidth - 1L; + const long nWidth1 = pReadAcc->Width() - 1L; + const double fRevScaleX = (double) nWidth1 / nNewWidth1; + + pLutInt = new long[ nNewWidth ]; + pLutFrac = new long[ nNewWidth ]; + + for( nX = 0L, nTemp = nWidth - 2L; nX < nNewWidth; nX++ ) + { + fTemp = nX * fRevScaleX; + pLutInt[ nX ] = MinMax( (long) fTemp, 0, nTemp ); + fTemp -= pLutInt[ nX ]; + pLutFrac[ nX ] = (long) ( fTemp * 1024. ); + } + + if( pReadAcc->HasPalette() ) + { + for( nY = 0L; nY < nHeight; nY++ ) + { + if( 1 == nWidth ) + { + aCol0 = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nY, 0 ) ); + + for( nX = 0L; nX < nNewWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, aCol0 ); + } + else + { + for( nX = 0L; nX < nNewWidth; nX++ ) + { + nTemp = pLutInt[ nX ]; + + aCol0 = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nY, nTemp++ ) ); + aCol1 = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nY, nTemp ) ); + + nTemp = pLutFrac[ nX ]; + + lXR1 = aCol1.GetRed() - ( lXR0 = aCol0.GetRed() ); + lXG1 = aCol1.GetGreen() - ( lXG0 = aCol0.GetGreen() ); + lXB1 = aCol1.GetBlue() - ( lXB0 = aCol0.GetBlue() ); + + aCol0.SetRed( (sal_uInt8) ( ( lXR1 * nTemp + ( lXR0 << 10 ) ) >> 10 ) ); + aCol0.SetGreen( (sal_uInt8) ( ( lXG1 * nTemp + ( lXG0 << 10 ) ) >> 10 ) ); + aCol0.SetBlue( (sal_uInt8) ( ( lXB1 * nTemp + ( lXB0 << 10 ) ) >> 10 ) ); + + pWriteAcc->SetPixel( nY, nX, aCol0 ); + } + } + } + } + else + { + for( nY = 0L; nY < nHeight; nY++ ) + { + if( 1 == nWidth ) + { + aCol0 = pReadAcc->GetPixel( nY, 0 ); + + for( nX = 0L; nX < nNewWidth; nX++ ) + pWriteAcc->SetPixel( nY, nX, aCol0 ); + } + else + { + for( nX = 0L; nX < nNewWidth; nX++ ) + { + nTemp = pLutInt[ nX ]; + + aCol0 = pReadAcc->GetPixel( nY, nTemp++ ); + aCol1 = pReadAcc->GetPixel( nY, nTemp ); + + nTemp = pLutFrac[ nX ]; + + lXR1 = aCol1.GetRed() - ( lXR0 = aCol0.GetRed() ); + lXG1 = aCol1.GetGreen() - ( lXG0 = aCol0.GetGreen() ); + lXB1 = aCol1.GetBlue() - ( lXB0 = aCol0.GetBlue() ); + + aCol0.SetRed( (sal_uInt8) ( ( lXR1 * nTemp + ( lXR0 << 10 ) ) >> 10 ) ); + aCol0.SetGreen( (sal_uInt8) ( ( lXG1 * nTemp + ( lXG0 << 10 ) ) >> 10 ) ); + aCol0.SetBlue( (sal_uInt8) ( ( lXB1 * nTemp + ( lXB0 << 10 ) ) >> 10 ) ); + + pWriteAcc->SetPixel( nY, nX, aCol0 ); + } + } + } + } + + delete[] pLutInt; + delete[] pLutFrac; + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + aNewBmp.ReleaseAccess( pWriteAcc ); + + if( bRet ) + { + bRet = sal_False; + ImplAssignWithSize( aNewBmp ); + pReadAcc = AcquireReadAccess(); + aNewBmp = Bitmap( Size( nNewWidth, nNewHeight ), 24 ); + pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pReadAcc && pWriteAcc ) + { + const long nNewHeight1 = nNewHeight - 1L; + const long nHeight1 = pReadAcc->Height() - 1L; + const double fRevScaleY = (double) nHeight1 / nNewHeight1; + + pLutInt = new long[ nNewHeight ]; + pLutFrac = new long[ nNewHeight ]; + + for( nY = 0L, nTemp = nHeight - 2L; nY < nNewHeight; nY++ ) + { + fTemp = nY * fRevScaleY; + pLutInt[ nY ] = MinMax( (long) fTemp, 0, nTemp ); + fTemp -= pLutInt[ nY ]; + pLutFrac[ nY ] = (long) ( fTemp * 1024. ); + } + + if( pReadAcc->HasPalette() ) + { + for( nX = 0L; nX < nNewWidth; nX++ ) + { + if( 1 == nHeight ) + { + aCol0 = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( 0, nX ) ); + + for( nY = 0L; nY < nNewHeight; nY++ ) + pWriteAcc->SetPixel( nY, nX, aCol0 ); + } + else + { + for( nY = 0L; nY < nNewHeight; nY++ ) + { + nTemp = pLutInt[ nY ]; + + aCol0 = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nTemp++, nX ) ); + aCol1 = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nTemp, nX ) ); + + nTemp = pLutFrac[ nY ]; + + lXR1 = aCol1.GetRed() - ( lXR0 = aCol0.GetRed() ); + lXG1 = aCol1.GetGreen() - ( lXG0 = aCol0.GetGreen() ); + lXB1 = aCol1.GetBlue() - ( lXB0 = aCol0.GetBlue() ); + + aCol0.SetRed( (sal_uInt8) ( ( lXR1 * nTemp + ( lXR0 << 10 ) ) >> 10 ) ); + aCol0.SetGreen( (sal_uInt8) ( ( lXG1 * nTemp + ( lXG0 << 10 ) ) >> 10 ) ); + aCol0.SetBlue( (sal_uInt8) ( ( lXB1 * nTemp + ( lXB0 << 10 ) ) >> 10 ) ); + + pWriteAcc->SetPixel( nY, nX, aCol0 ); + } + } + } + } + else + { + for( nX = 0L; nX < nNewWidth; nX++ ) + { + if( 1 == nHeight ) + { + aCol0 = pReadAcc->GetPixel( 0, nX ); + + for( nY = 0L; nY < nNewHeight; nY++ ) + pWriteAcc->SetPixel( nY, nX, aCol0 ); + } + else + { + for( nY = 0L; nY < nNewHeight; nY++ ) + { + nTemp = pLutInt[ nY ]; + + aCol0 = pReadAcc->GetPixel( nTemp++, nX ); + aCol1 = pReadAcc->GetPixel( nTemp, nX ); + + nTemp = pLutFrac[ nY ]; + + lXR1 = aCol1.GetRed() - ( lXR0 = aCol0.GetRed() ); + lXG1 = aCol1.GetGreen() - ( lXG0 = aCol0.GetGreen() ); + lXB1 = aCol1.GetBlue() - ( lXB0 = aCol0.GetBlue() ); + + aCol0.SetRed( (sal_uInt8) ( ( lXR1 * nTemp + ( lXR0 << 10 ) ) >> 10 ) ); + aCol0.SetGreen( (sal_uInt8) ( ( lXG1 * nTemp + ( lXG0 << 10 ) ) >> 10 ) ); + aCol0.SetBlue( (sal_uInt8) ( ( lXB1 * nTemp + ( lXB0 << 10 ) ) >> 10 ) ); + + pWriteAcc->SetPixel( nY, nX, aCol0 ); + } + } + } + } + + delete[] pLutInt; + delete[] pLutFrac; + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + aNewBmp.ReleaseAccess( pWriteAcc ); + + if( bRet ) + ImplAssignWithSize( aNewBmp ); + } + } + + if( !bRet ) + bRet = ImplScaleFast( rScaleX, rScaleY ); + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::Dither( sal_uLong nDitherFlags ) +{ + sal_Bool bRet = sal_False; + + const Size aSizePix( GetSizePixel() ); + + if( aSizePix.Width() == 1 || aSizePix.Height() == 1 ) + bRet = sal_True; + else if( nDitherFlags & BMP_DITHER_MATRIX ) + bRet = ImplDitherMatrix(); + else if( nDitherFlags & BMP_DITHER_FLOYD ) + bRet = ImplDitherFloyd(); + else if( ( nDitherFlags & BMP_DITHER_FLOYD_16 ) && ( GetBitCount() == 24 ) ) + bRet = ImplDitherFloyd16(); + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplDitherMatrix() +{ + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + Bitmap aNewBmp( GetSizePixel(), 8 ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc && pWriteAcc ) + { + const sal_uLong nWidth = pReadAcc->Width(); + const sal_uLong nHeight = pReadAcc->Height(); + BitmapColor aIndex( (sal_uInt8) 0 ); + + if( pReadAcc->HasPalette() ) + { + for( sal_uLong nY = 0UL; nY < nHeight; nY++ ) + { + for( sal_uLong nX = 0UL, nModY = ( nY & 0x0FUL ) << 4UL; nX < nWidth; nX++ ) + { + const BitmapColor aCol( pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nY, nX ) ) ); + const sal_uLong nD = nVCLDitherLut[ nModY + ( nX & 0x0FUL ) ]; + const sal_uLong nR = ( nVCLLut[ aCol.GetRed() ] + nD ) >> 16UL; + const sal_uLong nG = ( nVCLLut[ aCol.GetGreen() ] + nD ) >> 16UL; + const sal_uLong nB = ( nVCLLut[ aCol.GetBlue() ] + nD ) >> 16UL; + + aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ nR ] + nVCLGLut[ nG ] + nVCLBLut[ nB ] ) ); + pWriteAcc->SetPixel( nY, nX, aIndex ); + } + } + } + else + { + for( sal_uLong nY = 0UL; nY < nHeight; nY++ ) + { + for( sal_uLong nX = 0UL, nModY = ( nY & 0x0FUL ) << 4UL; nX < nWidth; nX++ ) + { + const BitmapColor aCol( pReadAcc->GetPixel( nY, nX ) ); + const sal_uLong nD = nVCLDitherLut[ nModY + ( nX & 0x0FUL ) ]; + const sal_uLong nR = ( nVCLLut[ aCol.GetRed() ] + nD ) >> 16UL; + const sal_uLong nG = ( nVCLLut[ aCol.GetGreen() ] + nD ) >> 16UL; + const sal_uLong nB = ( nVCLLut[ aCol.GetBlue() ] + nD ) >> 16UL; + + aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ nR ] + nVCLGLut[ nG ] + nVCLBLut[ nB ] ) ); + pWriteAcc->SetPixel( nY, nX, aIndex ); + } + } + } + + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + aNewBmp.ReleaseAccess( pWriteAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplDitherFloyd() +{ + const Size aSize( GetSizePixel() ); + sal_Bool bRet = sal_False; + + if( ( aSize.Width() > 3 ) && ( aSize.Height() > 2 ) ) + { + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + Bitmap aNewBmp( GetSizePixel(), 8 ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pReadAcc && pWriteAcc ) + { + BitmapColor aColor; + long nWidth = pReadAcc->Width(); + long nWidth1 = nWidth - 1L; + long nHeight = pReadAcc->Height(); + long nX; + long nW = nWidth * 3L; + long nW2 = nW - 3L; + long nRErr, nGErr, nBErr; + long nRC, nGC, nBC; + long nTemp; + long nZ; + long* p1 = new long[ nW ]; + long* p2 = new long[ nW ]; + long* p1T = p1; + long* p2T = p2; + long* pTmp; + sal_Bool bPal = pReadAcc->HasPalette(); + + pTmp = p2T; + + if( bPal ) + { + for( nZ = 0; nZ < nWidth; nZ++ ) + { + aColor = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( 0, nZ ) ); + + *pTmp++ = (long) aColor.GetBlue() << 12; + *pTmp++ = (long) aColor.GetGreen() << 12; + *pTmp++ = (long) aColor.GetRed() << 12; + } + } + else + { + for( nZ = 0; nZ < nWidth; nZ++ ) + { + aColor = pReadAcc->GetPixel( 0, nZ ); + + *pTmp++ = (long) aColor.GetBlue() << 12; + *pTmp++ = (long) aColor.GetGreen() << 12; + *pTmp++ = (long) aColor.GetRed() << 12; + } + } + + for( long nY = 1, nYAcc = 0L; nY <= nHeight; nY++, nYAcc++ ) + { + pTmp = p1T; + p1T = p2T; + p2T = pTmp; + + if( nY < nHeight ) + { + if( bPal ) + { + for( nZ = 0; nZ < nWidth; nZ++ ) + { + aColor = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( nY, nZ ) ); + + *pTmp++ = (long) aColor.GetBlue() << 12; + *pTmp++ = (long) aColor.GetGreen() << 12; + *pTmp++ = (long) aColor.GetRed() << 12; + } + } + else + { + for( nZ = 0; nZ < nWidth; nZ++ ) + { + aColor = pReadAcc->GetPixel( nY, nZ ); + + *pTmp++ = (long) aColor.GetBlue() << 12; + *pTmp++ = (long) aColor.GetGreen() << 12; + *pTmp++ = (long) aColor.GetRed() << 12; + } + } + } + + // erstes Pixel gesondert betrachten + nX = 0; + CALC_ERRORS; + CALC_TABLES7; + nX -= 5; + CALC_TABLES5; + pWriteAcc->SetPixel( nYAcc, 0, BitmapColor( (sal_uInt8) ( nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ] ) ) ); + + // mittlere Pixel ueber Schleife + long nXAcc; + for ( nX = 3L, nXAcc = 1L; nX < nW2; nXAcc++ ) + { + CALC_ERRORS; + CALC_TABLES7; + nX -= 8; + CALC_TABLES3; + CALC_TABLES5; + pWriteAcc->SetPixel( nYAcc, nXAcc, BitmapColor( (sal_uInt8) ( nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ] ) ) ); + } + + // letztes Pixel gesondert betrachten + CALC_ERRORS; + nX -= 5; + CALC_TABLES3; + CALC_TABLES5; + pWriteAcc->SetPixel( nYAcc, nWidth1, BitmapColor( (sal_uInt8) ( nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ] ) ) ); + } + + delete[] p1; + delete[] p2; + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + aNewBmp.ReleaseAccess( pWriteAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aPrefSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aPrefSize; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplDitherFloyd16() +{ + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + Bitmap aNewBmp( GetSizePixel(), 24 ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc && pWriteAcc ) + { + const long nWidth = pWriteAcc->Width(); + const long nWidth1 = nWidth - 1L; + const long nHeight = pWriteAcc->Height(); + BitmapColor aColor; + BitmapColor aBestCol; + ImpErrorQuad aErrQuad; + ImpErrorQuad* pErrQuad1 = new ImpErrorQuad[ nWidth ]; + ImpErrorQuad* pErrQuad2 = new ImpErrorQuad[ nWidth ]; + ImpErrorQuad* pQLine1 = pErrQuad1; + ImpErrorQuad* pQLine2 = 0; + long nX, nY; + long nYTmp = 0L; + sal_Bool bQ1 = sal_True; + + for( nY = 0L; nY < Min( nHeight, 2L ); nY++, nYTmp++ ) + for( nX = 0L, pQLine2 = !nY ? pErrQuad1 : pErrQuad2; nX < nWidth; nX++ ) + pQLine2[ nX ] = pReadAcc->GetPixel( nYTmp, nX ); + + for( nY = 0L; nY < nHeight; nY++, nYTmp++ ) + { + // erstes ZeilenPixel + aBestCol = pQLine1[ 0 ].ImplGetColor(); + aBestCol.SetRed( ( aBestCol.GetRed() & 248 ) | 7 ); + aBestCol.SetGreen( ( aBestCol.GetGreen() & 248 ) | 7 ); + aBestCol.SetBlue( ( aBestCol.GetBlue() & 248 ) | 7 ); + pWriteAcc->SetPixel( nY, 0, aBestCol ); + + for( nX = 1L; nX < nWidth1; nX++ ) + { + aColor = pQLine1[ nX ].ImplGetColor(); + aBestCol.SetRed( ( aColor.GetRed() & 248 ) | 7 ); + aBestCol.SetGreen( ( aColor.GetGreen() & 248 ) | 7 ); + aBestCol.SetBlue( ( aColor.GetBlue() & 248 ) | 7 ); + aErrQuad = ( ImpErrorQuad( aColor ) -= aBestCol ); + pQLine1[ ++nX ].ImplAddColorError7( aErrQuad ); + pQLine2[ nX-- ].ImplAddColorError1( aErrQuad ); + pQLine2[ nX-- ].ImplAddColorError5( aErrQuad ); + pQLine2[ nX++ ].ImplAddColorError3( aErrQuad ); + pWriteAcc->SetPixel( nY, nX, aBestCol ); + } + + // letztes ZeilenPixel + aBestCol = pQLine1[ nWidth1 ].ImplGetColor(); + aBestCol.SetRed( ( aBestCol.GetRed() & 248 ) | 7 ); + aBestCol.SetGreen( ( aBestCol.GetGreen() & 248 ) | 7 ); + aBestCol.SetBlue( ( aBestCol.GetBlue() & 248 ) | 7 ); + pWriteAcc->SetPixel( nY, nX, aBestCol ); + + // Zeilenpuffer neu fuellen/kopieren + pQLine1 = pQLine2; + pQLine2 = ( bQ1 = !bQ1 ) != sal_False ? pErrQuad2 : pErrQuad1; + + if( nYTmp < nHeight ) + for( nX = 0L; nX < nWidth; nX++ ) + pQLine2[ nX ] = pReadAcc->GetPixel( nYTmp, nX ); + } + + // Zeilenpuffer zerstoeren + delete[] pErrQuad1; + delete[] pErrQuad2; + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + aNewBmp.ReleaseAccess( pWriteAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ReduceColors( sal_uInt16 nColorCount, BmpReduce eReduce ) +{ + sal_Bool bRet; + + if( GetColorCount() <= (sal_uLong) nColorCount ) + bRet = sal_True; + else if( nColorCount ) + { + if( BMP_REDUCE_SIMPLE == eReduce ) + bRet = ImplReduceSimple( nColorCount ); + else if( BMP_REDUCE_POPULAR == eReduce ) + bRet = ImplReducePopular( nColorCount ); + else + bRet = ImplReduceMedian( nColorCount ); + } + else + bRet = sal_False; + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplReduceSimple( sal_uInt16 nColorCount ) +{ + Bitmap aNewBmp; + BitmapReadAccess* pRAcc = AcquireReadAccess(); + const sal_uInt16 nColCount = Min( nColorCount, (sal_uInt16) 256 ); + sal_uInt16 nBitCount; + sal_Bool bRet = sal_False; + + if( nColCount <= 2 ) + nBitCount = 1; + else if( nColCount <= 16 ) + nBitCount = 4; + else + nBitCount = 8; + + if( pRAcc ) + { + Octree aOct( *pRAcc, nColCount ); + const BitmapPalette& rPal = aOct.GetPalette(); + BitmapWriteAccess* pWAcc; + + aNewBmp = Bitmap( GetSizePixel(), nBitCount, &rPal ); + pWAcc = aNewBmp.AcquireWriteAccess(); + + if( pWAcc ) + { + const long nWidth = pRAcc->Width(); + const long nHeight = pRAcc->Height(); + + if( pRAcc->HasPalette() ) + { + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX =0L; nX < nWidth; nX++ ) + pWAcc->SetPixel( nY, nX, (sal_uInt8) aOct.GetBestPaletteIndex( pRAcc->GetPaletteColor( pRAcc->GetPixel( nY, nX ) ) ) ); + } + else + { + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX =0L; nX < nWidth; nX++ ) + pWAcc->SetPixel( nY, nX, (sal_uInt8) aOct.GetBestPaletteIndex( pRAcc->GetPixel( nY, nX ) ) ); + } + + aNewBmp.ReleaseAccess( pWAcc ); + bRet = sal_True; + } + + ReleaseAccess( pRAcc ); + } + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + maPrefMapMode = aMap; + maPrefSize = aSize; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +struct PopularColorCount +{ + sal_uInt32 mnIndex; + sal_uInt32 mnCount; +}; + +// ------------------------------------------------------------------------ + +extern "C" int __LOADONCALLAPI ImplPopularCmpFnc( const void* p1, const void* p2 ) +{ + int nRet; + + if( ( (PopularColorCount*) p1 )->mnCount < ( (PopularColorCount*) p2 )->mnCount ) + nRet = 1; + else if( ( (PopularColorCount*) p1 )->mnCount == ( (PopularColorCount*) p2 )->mnCount ) + nRet = 0; + else + nRet = -1; + + return nRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplReducePopular( sal_uInt16 nColCount ) +{ + BitmapReadAccess* pRAcc = AcquireReadAccess(); + sal_uInt16 nBitCount; + sal_Bool bRet = sal_False; + + if( nColCount > 256 ) + nColCount = 256; + + if( nColCount < 17 ) + nBitCount = 4; + else + nBitCount = 8; + + if( pRAcc ) + { + const sal_uInt32 nValidBits = 4; + const sal_uInt32 nRightShiftBits = 8 - nValidBits; + const sal_uInt32 nLeftShiftBits1 = nValidBits; + const sal_uInt32 nLeftShiftBits2 = nValidBits << 1; + const sal_uInt32 nColorsPerComponent = 1 << nValidBits; + const sal_uInt32 nColorOffset = 256 / nColorsPerComponent; + const sal_uInt32 nTotalColors = nColorsPerComponent * nColorsPerComponent * nColorsPerComponent; + const long nWidth = pRAcc->Width(); + const long nHeight = pRAcc->Height(); + PopularColorCount* pCountTable = new PopularColorCount[ nTotalColors ]; + long nX, nY, nR, nG, nB, nIndex; + + rtl_zeroMemory( pCountTable, nTotalColors * sizeof( PopularColorCount ) ); + + for( nR = 0, nIndex = 0; nR < 256; nR += nColorOffset ) + { + for( nG = 0; nG < 256; nG += nColorOffset ) + { + for( nB = 0; nB < 256; nB += nColorOffset ) + { + pCountTable[ nIndex ].mnIndex = nIndex; + nIndex++; + } + } + } + + if( pRAcc->HasPalette() ) + { + for( nY = 0L; nY < nHeight; nY++ ) + { + for( nX = 0L; nX < nWidth; nX++ ) + { + const BitmapColor& rCol = pRAcc->GetPaletteColor( pRAcc->GetPixel( nY, nX ) ); + pCountTable[ ( ( ( (sal_uInt32) rCol.GetRed() ) >> nRightShiftBits ) << nLeftShiftBits2 ) | + ( ( ( (sal_uInt32) rCol.GetGreen() ) >> nRightShiftBits ) << nLeftShiftBits1 ) | + ( ( (sal_uInt32) rCol.GetBlue() ) >> nRightShiftBits ) ].mnCount++; + } + } + } + else + { + for( nY = 0L; nY < nHeight; nY++ ) + { + for( nX = 0L; nX < nWidth; nX++ ) + { + const BitmapColor aCol( pRAcc->GetPixel( nY, nX ) ); + pCountTable[ ( ( ( (sal_uInt32) aCol.GetRed() ) >> nRightShiftBits ) << nLeftShiftBits2 ) | + ( ( ( (sal_uInt32) aCol.GetGreen() ) >> nRightShiftBits ) << nLeftShiftBits1 ) | + ( ( (sal_uInt32) aCol.GetBlue() ) >> nRightShiftBits ) ].mnCount++; + } + } + } + + BitmapPalette aNewPal( nColCount ); + + qsort( pCountTable, nTotalColors, sizeof( PopularColorCount ), ImplPopularCmpFnc ); + + for( sal_uInt16 n = 0; n < nColCount; n++ ) + { + const PopularColorCount& rPop = pCountTable[ n ]; + aNewPal[ n ] = BitmapColor( (sal_uInt8) ( ( rPop.mnIndex >> nLeftShiftBits2 ) << nRightShiftBits ), + (sal_uInt8) ( ( ( rPop.mnIndex >> nLeftShiftBits1 ) & ( nColorsPerComponent - 1 ) ) << nRightShiftBits ), + (sal_uInt8) ( ( rPop.mnIndex & ( nColorsPerComponent - 1 ) ) << nRightShiftBits ) ); + } + + Bitmap aNewBmp( GetSizePixel(), nBitCount, &aNewPal ); + BitmapWriteAccess* pWAcc = aNewBmp.AcquireWriteAccess(); + + if( pWAcc ) + { + BitmapColor aDstCol( (sal_uInt8) 0 ); + sal_uInt8* pIndexMap = new sal_uInt8[ nTotalColors ]; + + for( nR = 0, nIndex = 0; nR < 256; nR += nColorOffset ) + for( nG = 0; nG < 256; nG += nColorOffset ) + for( nB = 0; nB < 256; nB += nColorOffset ) + pIndexMap[ nIndex++ ] = (sal_uInt8) aNewPal.GetBestIndex( BitmapColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB ) ); + + if( pRAcc->HasPalette() ) + { + for( nY = 0L; nY < nHeight; nY++ ) + { + for( nX = 0L; nX < nWidth; nX++ ) + { + const BitmapColor& rCol = pRAcc->GetPaletteColor( pRAcc->GetPixel( nY, nX ) ); + aDstCol.SetIndex( pIndexMap[ ( ( ( (sal_uInt32) rCol.GetRed() ) >> nRightShiftBits ) << nLeftShiftBits2 ) | + ( ( ( (sal_uInt32) rCol.GetGreen() ) >> nRightShiftBits ) << nLeftShiftBits1 ) | + ( ( (sal_uInt32) rCol.GetBlue() ) >> nRightShiftBits ) ] ); + pWAcc->SetPixel( nY, nX, aDstCol ); + } + } + } + else + { + for( nY = 0L; nY < nHeight; nY++ ) + { + for( nX = 0L; nX < nWidth; nX++ ) + { + const BitmapColor aCol( pRAcc->GetPixel( nY, nX ) ); + aDstCol.SetIndex( pIndexMap[ ( ( ( (sal_uInt32) aCol.GetRed() ) >> nRightShiftBits ) << nLeftShiftBits2 ) | + ( ( ( (sal_uInt32) aCol.GetGreen() ) >> nRightShiftBits ) << nLeftShiftBits1 ) | + ( ( (sal_uInt32) aCol.GetBlue() ) >> nRightShiftBits ) ] ); + pWAcc->SetPixel( nY, nX, aDstCol ); + } + } + } + + delete[] pIndexMap; + aNewBmp.ReleaseAccess( pWAcc ); + bRet = sal_True; + } + + delete[] pCountTable; + ReleaseAccess( pRAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplReduceMedian( sal_uInt16 nColCount ) +{ + BitmapReadAccess* pRAcc = AcquireReadAccess(); + sal_uInt16 nBitCount; + sal_Bool bRet = sal_False; + + if( nColCount < 17 ) + nBitCount = 4; + else if( nColCount < 257 ) + nBitCount = 8; + else + { + OSL_FAIL( "Bitmap::ImplReduceMedian(): invalid color count!" ); + nBitCount = 8; + nColCount = 256; + } + + if( pRAcc ) + { + Bitmap aNewBmp( GetSizePixel(), nBitCount ); + BitmapWriteAccess* pWAcc = aNewBmp.AcquireWriteAccess(); + + if( pWAcc ) + { + const sal_uLong nSize = 32768UL * sizeof( sal_uLong ); + sal_uLong* pColBuf = (sal_uLong*) rtl_allocateMemory( nSize ); + const long nWidth = pWAcc->Width(); + const long nHeight = pWAcc->Height(); + long nIndex = 0L; + + memset( (HPBYTE) pColBuf, 0, nSize ); + + // create Buffer + if( pRAcc->HasPalette() ) + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + const BitmapColor& rCol = pRAcc->GetPaletteColor( pRAcc->GetPixel( nY, nX ) ); + pColBuf[ RGB15( rCol.GetRed() >> 3, rCol.GetGreen() >> 3, rCol.GetBlue() >> 3 ) ]++; + } + } + } + else + { + for( long nY = 0L; nY < nHeight; nY++ ) + { + for( long nX = 0L; nX < nWidth; nX++ ) + { + const BitmapColor aCol( pRAcc->GetPixel( nY, nX ) ); + pColBuf[ RGB15( aCol.GetRed() >> 3, aCol.GetGreen() >> 3, aCol.GetBlue() >> 3 ) ]++; + } + } + } + + // create palette via median cut + BitmapPalette aPal( pWAcc->GetPaletteEntryCount() ); + ImplMedianCut( pColBuf, aPal, 0, 31, 0, 31, 0, 31, + nColCount, nWidth * nHeight, nIndex ); + + // do mapping of colors to palette + InverseColorMap aMap( aPal ); + pWAcc->SetPalette( aPal ); + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX = 0L; nX < nWidth; nX++ ) + pWAcc->SetPixel( nY, nX, (sal_uInt8) aMap.GetBestPaletteIndex( pRAcc->GetColor( nY, nX ) ) ); + + rtl_freeMemory( pColBuf ); + aNewBmp.ReleaseAccess( pWAcc ); + bRet = sal_True; + } + + ReleaseAccess( pRAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +void Bitmap::ImplMedianCut( sal_uLong* pColBuf, BitmapPalette& rPal, + long nR1, long nR2, long nG1, long nG2, long nB1, long nB2, + long nColors, long nPixels, long& rIndex ) +{ + if( !nPixels ) + return; + + BitmapColor aCol; + const long nRLen = nR2 - nR1; + const long nGLen = nG2 - nG1; + const long nBLen = nB2 - nB1; + long nR, nG, nB; + sal_uLong* pBuf = pColBuf; + + if( !nRLen && !nGLen && !nBLen ) + { + if( pBuf[ RGB15( nR1, nG1, nB1 ) ] ) + { + aCol.SetRed( (sal_uInt8) ( nR1 << 3 ) ); + aCol.SetGreen( (sal_uInt8) ( nG1 << 3 ) ); + aCol.SetBlue( (sal_uInt8) ( nB1 << 3 ) ); + rPal[ (sal_uInt16) rIndex++ ] = aCol; + } + } + else + { + if( 1 == nColors || 1 == nPixels ) + { + long nPixSum = 0, nRSum = 0, nGSum = 0, nBSum = 0; + + for( nR = nR1; nR <= nR2; nR++ ) + { + for( nG = nG1; nG <= nG2; nG++ ) + { + for( nB = nB1; nB <= nB2; nB++ ) + { + nPixSum = pBuf[ RGB15( nR, nG, nB ) ]; + + if( nPixSum ) + { + nRSum += nR * nPixSum; + nGSum += nG * nPixSum; + nBSum += nB * nPixSum; + } + } + } + } + + aCol.SetRed( (sal_uInt8) ( ( nRSum / nPixels ) << 3 ) ); + aCol.SetGreen( (sal_uInt8) ( ( nGSum / nPixels ) << 3 ) ); + aCol.SetBlue( (sal_uInt8) ( ( nBSum / nPixels ) << 3 ) ); + rPal[ (sal_uInt16) rIndex++ ] = aCol; + } + else + { + const long nTest = ( nPixels >> 1 ); + long nPixOld = 0; + long nPixNew = 0; + + if( nBLen > nGLen && nBLen > nRLen ) + { + nB = nB1 - 1; + + while( nPixNew < nTest ) + { + nB++, nPixOld = nPixNew; + for( nR = nR1; nR <= nR2; nR++ ) + for( nG = nG1; nG <= nG2; nG++ ) + nPixNew += pBuf[ RGB15( nR, nG, nB ) ]; + } + + if( nB < nB2 ) + { + ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB1, nB, nColors >> 1, nPixNew, rIndex ); + ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB + 1, nB2, nColors >> 1, nPixels - nPixNew, rIndex ); + } + else + { + ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB1, nB - 1, nColors >> 1, nPixOld, rIndex ); + ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB, nB2, nColors >> 1, nPixels - nPixOld, rIndex ); + } + } + else if( nGLen > nRLen ) + { + nG = nG1 - 1; + + while( nPixNew < nTest ) + { + nG++, nPixOld = nPixNew; + for( nR = nR1; nR <= nR2; nR++ ) + for( nB = nB1; nB <= nB2; nB++ ) + nPixNew += pBuf[ RGB15( nR, nG, nB ) ]; + } + + if( nG < nG2 ) + { + ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG, nB1, nB2, nColors >> 1, nPixNew, rIndex ); + ImplMedianCut( pBuf, rPal, nR1, nR2, nG + 1, nG2, nB1, nB2, nColors >> 1, nPixels - nPixNew, rIndex ); + } + else + { + ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG - 1, nB1, nB2, nColors >> 1, nPixOld, rIndex ); + ImplMedianCut( pBuf, rPal, nR1, nR2, nG, nG2, nB1, nB2, nColors >> 1, nPixels - nPixOld, rIndex ); + } + } + else + { + nR = nR1 - 1; + + while( nPixNew < nTest ) + { + nR++, nPixOld = nPixNew; + for( nG = nG1; nG <= nG2; nG++ ) + for( nB = nB1; nB <= nB2; nB++ ) + nPixNew += pBuf[ RGB15( nR, nG, nB ) ]; + } + + if( nR < nR2 ) + { + ImplMedianCut( pBuf, rPal, nR1, nR, nG1, nG2, nB1, nB2, nColors >> 1, nPixNew, rIndex ); + ImplMedianCut( pBuf, rPal, nR1 + 1, nR2, nG1, nG2, nB1, nB2, nColors >> 1, nPixels - nPixNew, rIndex ); + } + else + { + ImplMedianCut( pBuf, rPal, nR1, nR - 1, nG1, nG2, nB1, nB2, nColors >> 1, nPixOld, rIndex ); + ImplMedianCut( pBuf, rPal, nR, nR2, nG1, nG2, nB1, nB2, nColors >> 1, nPixels - nPixOld, rIndex ); + } + } + } + } +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::Vectorize( PolyPolygon& rPolyPoly, sal_uLong nFlags, const Link* pProgress ) +{ + return ImplVectorizer().ImplVectorize( *this, rPolyPoly, nFlags, pProgress ); +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::Vectorize( GDIMetaFile& rMtf, sal_uInt8 cReduce, sal_uLong nFlags, const Link* pProgress ) +{ + return ImplVectorizer().ImplVectorize( *this, rMtf, cReduce, nFlags, pProgress ); +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::Adjust( short nLuminancePercent, short nContrastPercent, + short nChannelRPercent, short nChannelGPercent, short nChannelBPercent, + double fGamma, sal_Bool bInvert ) +{ + sal_Bool bRet = sal_False; + + // nothing to do => return quickly + if( !nLuminancePercent && !nContrastPercent && + !nChannelRPercent && !nChannelGPercent && !nChannelBPercent && + ( fGamma == 1.0 ) && !bInvert ) + { + bRet = sal_True; + } + else + { + BitmapWriteAccess* pAcc = AcquireWriteAccess(); + + if( pAcc ) + { + BitmapColor aCol; + const long nW = pAcc->Width(); + const long nH = pAcc->Height(); + sal_uInt8* cMapR = new sal_uInt8[ 256 ]; + sal_uInt8* cMapG = new sal_uInt8[ 256 ]; + sal_uInt8* cMapB = new sal_uInt8[ 256 ]; + long nX, nY; + double fM, fROff, fGOff, fBOff, fOff; + + // calculate slope + if( nContrastPercent >= 0 ) + fM = 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent, 0L, 100L ) ); + else + fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100L, 0L ) ) / 128.0; + + // total offset = luminance offset + contrast offset + fOff = MinMax( nLuminancePercent, -100L, 100L ) * 2.55 + 128.0 - fM * 128.0; + + // channel offset = channel offset + total offset + fROff = nChannelRPercent * 2.55 + fOff; + fGOff = nChannelGPercent * 2.55 + fOff; + fBOff = nChannelBPercent * 2.55 + fOff; + + // calculate gamma value + fGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma ); + const sal_Bool bGamma = ( fGamma != 1.0 ); + + // create mapping table + for( nX = 0L; nX < 256L; nX++ ) + { + cMapR[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fROff ), 0L, 255L ); + cMapG[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fGOff ), 0L, 255L ); + cMapB[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fBOff ), 0L, 255L ); + + if( bGamma ) + { + cMapR[ nX ] = GAMMA( cMapR[ nX ], fGamma ); + cMapG[ nX ] = GAMMA( cMapG[ nX ], fGamma ); + cMapB[ nX ] = GAMMA( cMapB[ nX ], fGamma ); + } + + if( bInvert ) + { + cMapR[ nX ] = ~cMapR[ nX ]; + cMapG[ nX ] = ~cMapG[ nX ]; + cMapB[ nX ] = ~cMapB[ nX ]; + } + } + + // do modifying + if( pAcc->HasPalette() ) + { + BitmapColor aNewCol; + + for( sal_uInt16 i = 0, nCount = pAcc->GetPaletteEntryCount(); i < nCount; i++ ) + { + const BitmapColor& rCol = pAcc->GetPaletteColor( i ); + aNewCol.SetRed( cMapR[ rCol.GetRed() ] ); + aNewCol.SetGreen( cMapG[ rCol.GetGreen() ] ); + aNewCol.SetBlue( cMapB[ rCol.GetBlue() ] ); + pAcc->SetPaletteColor( i, aNewCol ); + } + } + else if( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR ) + { + for( nY = 0L; nY < nH; nY++ ) + { + Scanline pScan = pAcc->GetScanline( nY ); + + for( nX = 0L; nX < nW; nX++ ) + { + *pScan = cMapB[ *pScan ]; pScan++; + *pScan = cMapG[ *pScan ]; pScan++; + *pScan = cMapR[ *pScan ]; pScan++; + } + } + } + else if( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ) + { + for( nY = 0L; nY < nH; nY++ ) + { + Scanline pScan = pAcc->GetScanline( nY ); + + for( nX = 0L; nX < nW; nX++ ) + { + *pScan = cMapR[ *pScan ]; pScan++; + *pScan = cMapG[ *pScan ]; pScan++; + *pScan = cMapB[ *pScan ]; pScan++; + } + } + } + else + { + for( nY = 0L; nY < nH; nY++ ) + { + for( nX = 0L; nX < nW; nX++ ) + { + aCol = pAcc->GetPixel( nY, nX ); + aCol.SetRed( cMapR[ aCol.GetRed() ] ); + aCol.SetGreen( cMapG[ aCol.GetGreen() ] ); + aCol.SetBlue( cMapB[ aCol.GetBlue() ] ); + pAcc->SetPixel( nY, nX, aCol ); + } + } + } + + delete[] cMapR; + delete[] cMapG; + delete[] cMapB; + ReleaseAccess( pAcc ); + bRet = sal_True; + } + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bitmap4.cxx b/vcl/source/gdi/bitmap4.cxx new file mode 100644 index 000000000000..1166f6cc0931 --- /dev/null +++ b/vcl/source/gdi/bitmap4.cxx @@ -0,0 +1,1011 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdlib.h> +#include <vcl/bmpacc.hxx> +#include <vcl/bitmap.hxx> + +// ----------- +// - Defines - +// ----------- + +#define S2(a,b) { register long t; if( ( t = b - a ) < 0 ) { a += t; b -= t; } } +#define MN3(a,b,c) S2(a,b); S2(a,c); +#define MX3(a,b,c) S2(b,c); S2(a,c); +#define MNMX3(a,b,c) MX3(a,b,c); S2(a,b); +#define MNMX4(a,b,c,d) S2(a,b); S2(c,d); S2(a,c); S2(b,d); +#define MNMX5(a,b,c,d,e) S2(a,b); S2(c,d); MN3(a,c,e); MX3(b,d,e); +#define MNMX6(a,b,c,d,e,f) S2(a,d); S2(b,e); S2(c,f); MN3(a,b,c); MX3(d,e,f); + +// ---------- +// - Bitmap - +// ---------- + +sal_Bool Bitmap::Filter( BmpFilter eFilter, const BmpFilterParam* pFilterParam, const Link* pProgress ) +{ + sal_Bool bRet = sal_False; + + switch( eFilter ) + { + case( BMP_FILTER_SMOOTH ): + { + const long pSmoothMatrix[] = { 1, 2, 1, 2, 5, 2, 1, 2, 1 }; + bRet = ImplConvolute3( &pSmoothMatrix[ 0 ], 17, pFilterParam, pProgress ); + } + break; + + case( BMP_FILTER_SHARPEN ): + { + const long pSharpenMatrix[] = { -1, -1, -1, -1, 16, -1, -1, -1, -1 }; + bRet = ImplConvolute3( &pSharpenMatrix[ 0 ], 8, pFilterParam, pProgress ); + } + break; + + case( BMP_FILTER_REMOVENOISE ): + bRet = ImplMedianFilter( pFilterParam, pProgress ); + break; + + case( BMP_FILTER_SOBEL_GREY ): + bRet = ImplSobelGrey( pFilterParam, pProgress ); + break; + + case( BMP_FILTER_SOLARIZE ): + bRet = ImplSolarize( pFilterParam, pProgress ); + break; + + case( BMP_FILTER_SEPIA ): + bRet = ImplSepia( pFilterParam, pProgress ); + break; + + case( BMP_FILTER_MOSAIC ): + bRet = ImplMosaic( pFilterParam, pProgress ); + break; + + case( BMP_FILTER_EMBOSS_GREY ): + bRet = ImplEmbossGrey( pFilterParam, pProgress ); + break; + + case( BMP_FILTER_POPART ): + bRet = ImplPopArt( pFilterParam, pProgress ); + break; + + default: + OSL_FAIL( "Bitmap::Convert(): Unsupported filter" ); + break; + } + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool Bitmap::ImplConvolute3( const long* pMatrix, long nDivisor, + const BmpFilterParam* /*pFilterParam*/, const Link* /*pProgress*/ ) +{ + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc ) + { + Bitmap aNewBmp( GetSizePixel(), 24 ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = pWriteAcc->Width(), nWidth2 = nWidth + 2; + const long nHeight = pWriteAcc->Height(), nHeight2 = nHeight + 2; + long* pColm = new long[ nWidth2 ]; + long* pRows = new long[ nHeight2 ]; + BitmapColor* pColRow1 = (BitmapColor*) new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]; + BitmapColor* pColRow2 = (BitmapColor*) new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]; + BitmapColor* pColRow3 = (BitmapColor*) new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]; + BitmapColor* pRowTmp1 = pColRow1; + BitmapColor* pRowTmp2 = pColRow2; + BitmapColor* pRowTmp3 = pColRow3; + BitmapColor* pColor; + long nY, nX, i, nSumR, nSumG, nSumB, nMatrixVal, nTmp; + long (*pKoeff)[ 256 ] = new long[ 9 ][ 256 ]; + long* pTmp; + + // create LUT of products of matrix value and possible color component values + for( nY = 0; nY < 9; nY++ ) + for( nX = nTmp = 0, nMatrixVal = pMatrix[ nY ]; nX < 256; nX++, nTmp += nMatrixVal ) + pKoeff[ nY ][ nX ] = nTmp; + + // create column LUT + for( i = 0; i < nWidth2; i++ ) + pColm[ i ] = ( i > 0 ) ? ( i - 1 ) : 0; + + pColm[ nWidth + 1 ] = pColm[ nWidth ]; + + // create row LUT + for( i = 0; i < nHeight2; i++ ) + pRows[ i ] = ( i > 0 ) ? ( i - 1 ) : 0; + + pRows[ nHeight + 1 ] = pRows[ nHeight ]; + + // read first three rows of bitmap color + for( i = 0; i < nWidth2; i++ ) + { + pColRow1[ i ] = pReadAcc->GetColor( pRows[ 0 ], pColm[ i ] ); + pColRow2[ i ] = pReadAcc->GetColor( pRows[ 1 ], pColm[ i ] ); + pColRow3[ i ] = pReadAcc->GetColor( pRows[ 2 ], pColm[ i ] ); + } + + // do convolution + for( nY = 0; nY < nHeight; ) + { + for( nX = 0; nX < nWidth; nX++ ) + { + // first row + nSumR = ( pTmp = pKoeff[ 0 ] )[ ( pColor = pRowTmp1 + nX )->GetRed() ]; + nSumG = pTmp[ pColor->GetGreen() ]; + nSumB = pTmp[ pColor->GetBlue() ]; + + nSumR += ( pTmp = pKoeff[ 1 ] )[ ( ++pColor )->GetRed() ]; + nSumG += pTmp[ pColor->GetGreen() ]; + nSumB += pTmp[ pColor->GetBlue() ]; + + nSumR += ( pTmp = pKoeff[ 2 ] )[ ( ++pColor )->GetRed() ]; + nSumG += pTmp[ pColor->GetGreen() ]; + nSumB += pTmp[ pColor->GetBlue() ]; + + // second row + nSumR += ( pTmp = pKoeff[ 3 ] )[ ( pColor = pRowTmp2 + nX )->GetRed() ]; + nSumG += pTmp[ pColor->GetGreen() ]; + nSumB += pTmp[ pColor->GetBlue() ]; + + nSumR += ( pTmp = pKoeff[ 4 ] )[ ( ++pColor )->GetRed() ]; + nSumG += pTmp[ pColor->GetGreen() ]; + nSumB += pTmp[ pColor->GetBlue() ]; + + nSumR += ( pTmp = pKoeff[ 5 ] )[ ( ++pColor )->GetRed() ]; + nSumG += pTmp[ pColor->GetGreen() ]; + nSumB += pTmp[ pColor->GetBlue() ]; + + // third row + nSumR += ( pTmp = pKoeff[ 6 ] )[ ( pColor = pRowTmp3 + nX )->GetRed() ]; + nSumG += pTmp[ pColor->GetGreen() ]; + nSumB += pTmp[ pColor->GetBlue() ]; + + nSumR += ( pTmp = pKoeff[ 7 ] )[ ( ++pColor )->GetRed() ]; + nSumG += pTmp[ pColor->GetGreen() ]; + nSumB += pTmp[ pColor->GetBlue() ]; + + nSumR += ( pTmp = pKoeff[ 8 ] )[ ( ++pColor )->GetRed() ]; + nSumG += pTmp[ pColor->GetGreen() ]; + nSumB += pTmp[ pColor->GetBlue() ]; + + // calculate destination color + pWriteAcc->SetPixel( nY, nX, BitmapColor( (sal_uInt8) MinMax( nSumR / nDivisor, 0, 255 ), + (sal_uInt8) MinMax( nSumG / nDivisor, 0, 255 ), + (sal_uInt8) MinMax( nSumB / nDivisor, 0, 255 ) ) ); + } + + if( ++nY < nHeight ) + { + if( pRowTmp1 == pColRow1 ) + pRowTmp1 = pColRow2, pRowTmp2 = pColRow3, pRowTmp3 = pColRow1; + else if( pRowTmp1 == pColRow2 ) + pRowTmp1 = pColRow3, pRowTmp2 = pColRow1, pRowTmp3 = pColRow2; + else + pRowTmp1 = pColRow1, pRowTmp2 = pColRow2, pRowTmp3 = pColRow3; + + for( i = 0; i < nWidth2; i++ ) + pRowTmp3[ i ] = pReadAcc->GetColor( pRows[ nY + 2 ], pColm[ i ] ); + } + } + + delete[] pKoeff; + delete[] (sal_uInt8*) pColRow1; + delete[] (sal_uInt8*) pColRow2; + delete[] (sal_uInt8*) pColRow3; + delete[] pColm; + delete[] pRows; + + aNewBmp.ReleaseAccess( pWriteAcc ); + + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool Bitmap::ImplMedianFilter( const BmpFilterParam* /*pFilterParam*/, const Link* /*pProgress*/ ) +{ + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc ) + { + Bitmap aNewBmp( GetSizePixel(), 24 ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = pWriteAcc->Width(), nWidth2 = nWidth + 2; + const long nHeight = pWriteAcc->Height(), nHeight2 = nHeight + 2; + long* pColm = new long[ nWidth2 ]; + long* pRows = new long[ nHeight2 ]; + BitmapColor* pColRow1 = (BitmapColor*) new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]; + BitmapColor* pColRow2 = (BitmapColor*) new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]; + BitmapColor* pColRow3 = (BitmapColor*) new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]; + BitmapColor* pRowTmp1 = pColRow1; + BitmapColor* pRowTmp2 = pColRow2; + BitmapColor* pRowTmp3 = pColRow3; + BitmapColor* pColor; + long nY, nX, i; + long nR1, nR2, nR3, nR4, nR5, nR6, nR7, nR8, nR9; + long nG1, nG2, nG3, nG4, nG5, nG6, nG7, nG8, nG9; + long nB1, nB2, nB3, nB4, nB5, nB6, nB7, nB8, nB9; + + // create column LUT + for( i = 0; i < nWidth2; i++ ) + pColm[ i ] = ( i > 0 ) ? ( i - 1 ) : 0; + + pColm[ nWidth + 1 ] = pColm[ nWidth ]; + + // create row LUT + for( i = 0; i < nHeight2; i++ ) + pRows[ i ] = ( i > 0 ) ? ( i - 1 ) : 0; + + pRows[ nHeight + 1 ] = pRows[ nHeight ]; + + // read first three rows of bitmap color + if (nHeight2 > 2) + { + for( i = 0; i < nWidth2; i++ ) + { + pColRow1[ i ] = pReadAcc->GetColor( pRows[ 0 ], pColm[ i ] ); + pColRow2[ i ] = pReadAcc->GetColor( pRows[ 1 ], pColm[ i ] ); + pColRow3[ i ] = pReadAcc->GetColor( pRows[ 2 ], pColm[ i ] ); + } + } + + // do median filtering + for( nY = 0; nY < nHeight; ) + { + for( nX = 0; nX < nWidth; nX++ ) + { + nR1 = ( pColor = pRowTmp1 + nX )->GetRed(), nG1 = pColor->GetGreen(), nB1 = pColor->GetBlue(); + nR2 = ( ++pColor )->GetRed(), nG2 = pColor->GetGreen(), nB2 = pColor->GetBlue(); + nR3 = ( ++pColor )->GetRed(), nG3 = pColor->GetGreen(), nB3 = pColor->GetBlue(); + + nR4 = ( pColor = pRowTmp2 + nX )->GetRed(), nG4 = pColor->GetGreen(), nB4 = pColor->GetBlue(); + nR5 = ( ++pColor )->GetRed(), nG5 = pColor->GetGreen(), nB5 = pColor->GetBlue(); + nR6 = ( ++pColor )->GetRed(), nG6 = pColor->GetGreen(), nB6 = pColor->GetBlue(); + + nR7 = ( pColor = pRowTmp3 + nX )->GetRed(), nG7 = pColor->GetGreen(), nB7 = pColor->GetBlue(); + nR8 = ( ++pColor )->GetRed(), nG8 = pColor->GetGreen(), nB8 = pColor->GetBlue(); + nR9 = ( ++pColor )->GetRed(), nG9 = pColor->GetGreen(), nB9 = pColor->GetBlue(); + + MNMX6( nR1, nR2, nR3, nR4, nR5, nR6 ); + MNMX5( nR7, nR2, nR3, nR4, nR5 ); + MNMX4( nR8, nR2, nR3, nR4 ); + MNMX3( nR9, nR2, nR3 ); + + MNMX6( nG1, nG2, nG3, nG4, nG5, nG6 ); + MNMX5( nG7, nG2, nG3, nG4, nG5 ); + MNMX4( nG8, nG2, nG3, nG4 ); + MNMX3( nG9, nG2, nG3 ); + + MNMX6( nB1, nB2, nB3, nB4, nB5, nB6 ); + MNMX5( nB7, nB2, nB3, nB4, nB5 ); + MNMX4( nB8, nB2, nB3, nB4 ); + MNMX3( nB9, nB2, nB3 ); + + // set destination color + pWriteAcc->SetPixel( nY, nX, BitmapColor( (sal_uInt8) nR2, (sal_uInt8) nG2, (sal_uInt8) nB2 ) ); + } + + if( ++nY < nHeight ) + { + if( pRowTmp1 == pColRow1 ) + pRowTmp1 = pColRow2, pRowTmp2 = pColRow3, pRowTmp3 = pColRow1; + else if( pRowTmp1 == pColRow2 ) + pRowTmp1 = pColRow3, pRowTmp2 = pColRow1, pRowTmp3 = pColRow2; + else + pRowTmp1 = pColRow1, pRowTmp2 = pColRow2, pRowTmp3 = pColRow3; + + for( i = 0; i < nWidth2; i++ ) + pRowTmp3[ i ] = pReadAcc->GetColor( pRows[ nY + 2 ], pColm[ i ] ); + } + } + + delete[] (sal_uInt8*) pColRow1; + delete[] (sal_uInt8*) pColRow2; + delete[] (sal_uInt8*) pColRow3; + delete[] pColm; + delete[] pRows; + + aNewBmp.ReleaseAccess( pWriteAcc ); + + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool Bitmap::ImplSobelGrey( const BmpFilterParam* /*pFilterParam*/, const Link* /*pProgress*/ ) +{ + sal_Bool bRet = ImplMakeGreyscales( 256 ); + + if( bRet ) + { + bRet = sal_False; + + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + + if( pReadAcc ) + { + Bitmap aNewBmp( GetSizePixel(), 8, &pReadAcc->GetPalette() ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + BitmapColor aGrey( (sal_uInt8) 0 ); + const long nWidth = pWriteAcc->Width(); + const long nHeight = pWriteAcc->Height(); + const long nMask111 = -1, nMask121 = 0, nMask131 = 1; + const long nMask211 = -2, nMask221 = 0, nMask231 = 2; + const long nMask311 = -1, nMask321 = 0, nMask331 = 1; + const long nMask112 = 1, nMask122 = 2, nMask132 = 1; + const long nMask212 = 0, nMask222 = 0, nMask232 = 0; + const long nMask312 = -1, nMask322 = -2, nMask332 = -1; + long nGrey11, nGrey12, nGrey13; + long nGrey21, nGrey22, nGrey23; + long nGrey31, nGrey32, nGrey33; + long* pHMap = new long[ nWidth + 2 ]; + long* pVMap = new long[ nHeight + 2 ]; + long nX, nY, nSum1, nSum2; + + // fill mapping tables + pHMap[ 0 ] = 0; + for( nX = 1; nX <= nWidth; nX++ ) + pHMap[ nX ] = nX - 1; + pHMap[ nWidth + 1 ] = nWidth - 1; + + pVMap[ 0 ] = 0; + for( nY = 1; nY <= nHeight; nY++ ) + pVMap[ nY ] = nY - 1; + pVMap[ nHeight + 1 ] = nHeight - 1; + + for( nY = 0; nY < nHeight ; nY++ ) + { + nGrey11 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 0 ] ).GetIndex(); + nGrey12 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 1 ] ).GetIndex(); + nGrey13 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 2 ] ).GetIndex(); + nGrey21 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 0 ] ).GetIndex(); + nGrey22 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 1 ] ).GetIndex(); + nGrey23 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 2 ] ).GetIndex(); + nGrey31 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 0 ] ).GetIndex(); + nGrey32 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 1 ] ).GetIndex(); + nGrey33 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 2 ] ).GetIndex(); + + for( nX = 0; nX < nWidth; nX++ ) + { + nSum1 = nSum2 = 0; + + nSum1 += nMask111 * nGrey11; + nSum2 += nMask112 * nGrey11; + + nSum1 += nMask121 * nGrey12; + nSum2 += nMask122 * nGrey12; + + nSum1 += nMask131 * nGrey13; + nSum2 += nMask132 * nGrey13; + + nSum1 += nMask211 * nGrey21; + nSum2 += nMask212 * nGrey21; + + nSum1 += nMask221 * nGrey22; + nSum2 += nMask222 * nGrey22; + + nSum1 += nMask231 * nGrey23; + nSum2 += nMask232 * nGrey23; + + nSum1 += nMask311 * nGrey31; + nSum2 += nMask312 * nGrey31; + + nSum1 += nMask321 * nGrey32; + nSum2 += nMask322 * nGrey32; + + nSum1 += nMask331 * nGrey33; + nSum2 += nMask332 * nGrey33; + + nSum1 = (long) sqrt( (double)( nSum1 * nSum1 + nSum2 * nSum2 ) ); + aGrey.SetIndex( ~(sal_uInt8) SAL_BOUND( nSum1, 0, 255 ) ); + pWriteAcc->SetPixel( nY, nX, aGrey ); + + if( nX < ( nWidth - 1 ) ) + { + const long nNextX = pHMap[ nX + 3 ]; + + nGrey11 = nGrey12; nGrey12 = nGrey13; nGrey13 = pReadAcc->GetPixel( pVMap[ nY ], nNextX ).GetIndex(); + nGrey21 = nGrey22; nGrey22 = nGrey23; nGrey23 = pReadAcc->GetPixel( pVMap[ nY + 1 ], nNextX ).GetIndex(); + nGrey31 = nGrey32; nGrey32 = nGrey33; nGrey33 = pReadAcc->GetPixel( pVMap[ nY + 2 ], nNextX ).GetIndex(); + } + } + } + + delete[] pHMap; + delete[] pVMap; + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + } + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool Bitmap::ImplEmbossGrey( const BmpFilterParam* pFilterParam, const Link* /*pProgress*/ ) +{ + sal_Bool bRet = ImplMakeGreyscales( 256 ); + + if( bRet ) + { + bRet = sal_False; + + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + + if( pReadAcc ) + { + Bitmap aNewBmp( GetSizePixel(), 8, &pReadAcc->GetPalette() ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + BitmapColor aGrey( (sal_uInt8) 0 ); + const long nWidth = pWriteAcc->Width(); + const long nHeight = pWriteAcc->Height(); + long nGrey11, nGrey12, nGrey13; + long nGrey21, nGrey22, nGrey23; + long nGrey31, nGrey32, nGrey33; + double fAzim = ( ( pFilterParam && pFilterParam->meFilter == BMP_FILTER_EMBOSS_GREY ) ? + ( pFilterParam->maEmbossAngles.mnAzimuthAngle100 * 0.01 ) : 0.0 ) * F_PI180; + double fElev = ( ( pFilterParam && pFilterParam->meFilter == BMP_FILTER_EMBOSS_GREY ) ? + ( pFilterParam->maEmbossAngles.mnElevationAngle100 * 0.01 ) : 90.0 ) * F_PI180; + long* pHMap = new long[ nWidth + 2 ]; + long* pVMap = new long[ nHeight + 2 ]; + long nX, nY, nNx, nNy, nDotL; + const long nLx = FRound( cos( fAzim ) * cos( fElev ) * 255.0 ); + const long nLy = FRound( sin( fAzim ) * cos( fElev ) * 255.0 ); + const long nLz = FRound( sin( fElev ) * 255.0 ); + const long nZ2 = ( ( 6 * 255 ) / 4 ) * ( ( 6 * 255 ) / 4 ); + const long nNzLz = ( ( 6 * 255 ) / 4 ) * nLz; + const sal_uInt8 cLz = (sal_uInt8) SAL_BOUND( nLz, 0, 255 ); + + // fill mapping tables + pHMap[ 0 ] = 0; + for( nX = 1; nX <= nWidth; nX++ ) + pHMap[ nX ] = nX - 1; + pHMap[ nWidth + 1 ] = nWidth - 1; + + pVMap[ 0 ] = 0; + for( nY = 1; nY <= nHeight; nY++ ) + pVMap[ nY ] = nY - 1; + pVMap[ nHeight + 1 ] = nHeight - 1; + + for( nY = 0; nY < nHeight ; nY++ ) + { + nGrey11 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 0 ] ).GetIndex(); + nGrey12 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 1 ] ).GetIndex(); + nGrey13 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 2 ] ).GetIndex(); + nGrey21 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 0 ] ).GetIndex(); + nGrey22 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 1 ] ).GetIndex(); + nGrey23 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 2 ] ).GetIndex(); + nGrey31 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 0 ] ).GetIndex(); + nGrey32 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 1 ] ).GetIndex(); + nGrey33 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 2 ] ).GetIndex(); + + for( nX = 0; nX < nWidth; nX++ ) + { + nNx = nGrey11 + nGrey21 + nGrey31 - nGrey13 - nGrey23 - nGrey33; + nNy = nGrey31 + nGrey32 + nGrey33 - nGrey11 - nGrey12 - nGrey13; + + if( !nNx && !nNy ) + aGrey.SetIndex( cLz ); + else if( ( nDotL = nNx * nLx + nNy * nLy +nNzLz ) < 0 ) + aGrey.SetIndex( 0 ); + else + { + const double fGrey = nDotL / sqrt( (double)(nNx * nNx + nNy * nNy + nZ2) ); + aGrey.SetIndex( (sal_uInt8) SAL_BOUND( fGrey, 0, 255 ) ); + } + + pWriteAcc->SetPixel( nY, nX, aGrey ); + + if( nX < ( nWidth - 1 ) ) + { + const long nNextX = pHMap[ nX + 3 ]; + + nGrey11 = nGrey12; nGrey12 = nGrey13; nGrey13 = pReadAcc->GetPixel( pVMap[ nY ], nNextX ).GetIndex(); + nGrey21 = nGrey22; nGrey22 = nGrey23; nGrey23 = pReadAcc->GetPixel( pVMap[ nY + 1 ], nNextX ).GetIndex(); + nGrey31 = nGrey32; nGrey32 = nGrey33; nGrey33 = pReadAcc->GetPixel( pVMap[ nY + 2 ], nNextX ).GetIndex(); + } + } + } + + delete[] pHMap; + delete[] pVMap; + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + } + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool Bitmap::ImplSolarize( const BmpFilterParam* pFilterParam, const Link* /*pProgress*/ ) +{ + sal_Bool bRet = sal_False; + BitmapWriteAccess* pWriteAcc = AcquireWriteAccess(); + + if( pWriteAcc ) + { + const sal_uInt8 cThreshold = ( pFilterParam && pFilterParam->meFilter == BMP_FILTER_SOLARIZE ) ? + pFilterParam->mcSolarGreyThreshold : 128; + + if( pWriteAcc->HasPalette() ) + { + const BitmapPalette& rPal = pWriteAcc->GetPalette(); + + for( sal_uInt16 i = 0, nCount = rPal.GetEntryCount(); i < nCount; i++ ) + { + if( rPal[ i ].GetLuminance() >= cThreshold ) + { + BitmapColor aCol( rPal[ i ] ); + pWriteAcc->SetPaletteColor( i, aCol.Invert() ); + } + } + } + else + { + BitmapColor aCol; + const long nWidth = pWriteAcc->Width(); + const long nHeight = pWriteAcc->Height(); + + for( long nY = 0; nY < nHeight ; nY++ ) + { + for( long nX = 0; nX < nWidth; nX++ ) + { + aCol = pWriteAcc->GetPixel( nY, nX ); + + if( aCol.GetLuminance() >= cThreshold ) + pWriteAcc->SetPixel( nY, nX, aCol.Invert() ); + } + } + } + + ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool Bitmap::ImplSepia( const BmpFilterParam* pFilterParam, const Link* /*pProgress*/ ) +{ + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + sal_Bool bRet = sal_False; + + if( pReadAcc ) + { + long nSepiaPercent = ( pFilterParam && pFilterParam->meFilter == BMP_FILTER_SEPIA ) ? + pFilterParam->mcSolarGreyThreshold : 10; + const long nSepia = 10000 - 100 * SAL_BOUND( nSepiaPercent, 0, 100 ); + BitmapPalette aSepiaPal( 256 ); + + DBG_ASSERT( nSepiaPercent <= 100, "Bitmap::ImplSepia(): sepia value out of range; defaulting to 100%" ); + + for( sal_uInt16 i = 0; i < 256; i++ ) + { + BitmapColor& rCol = aSepiaPal[ i ]; + const sal_uInt8 cSepiaValue = (sal_uInt8) ( ( nSepia * i ) / 10000 ); + + rCol.SetRed( (sal_uInt8) i ); + rCol.SetGreen( cSepiaValue ); + rCol.SetBlue( cSepiaValue ); + } + + Bitmap aNewBmp( GetSizePixel(), 8, &aSepiaPal ); + BitmapWriteAccess* pWriteAcc = aNewBmp.AcquireWriteAccess(); + + if( pWriteAcc ) + { + BitmapColor aCol( (sal_uInt8) 0 ); + const long nWidth = pWriteAcc->Width(); + const long nHeight = pWriteAcc->Height(); + + if( pReadAcc->HasPalette() ) + { + for( long nY = 0; nY < nHeight ; nY++ ) + { + const sal_uInt16 nPalCount = pReadAcc->GetPaletteEntryCount(); + sal_uInt8* pIndexMap = new sal_uInt8[ nPalCount ]; + + for( sal_uInt16 i = 0; i < nPalCount; i++ ) + pIndexMap[ i ] = pReadAcc->GetPaletteColor( i ).GetLuminance(); + + for( long nX = 0; nX < nWidth; nX++ ) + { + aCol.SetIndex( pIndexMap[ pReadAcc->GetPixel( nY, nX ).GetIndex() ] ); + pWriteAcc->SetPixel( nY, nX, aCol ); + } + + delete[] pIndexMap; + } + } + else + { + for( long nY = 0; nY < nHeight ; nY++ ) + { + for( long nX = 0; nX < nWidth; nX++ ) + { + aCol.SetIndex( pReadAcc->GetPixel( nY, nX ).GetLuminance() ); + pWriteAcc->SetPixel( nY, nX, aCol ); + } + } + } + + aNewBmp.ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + } + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool Bitmap::ImplMosaic( const BmpFilterParam* pFilterParam, const Link* /*pProgress*/ ) +{ + sal_uLong nTileWidth = ( pFilterParam && pFilterParam->meFilter == BMP_FILTER_MOSAIC ) ? + pFilterParam->maMosaicTileSize.mnTileWidth : 4; + sal_uLong nTileHeight = ( pFilterParam && pFilterParam->meFilter == BMP_FILTER_MOSAIC ) ? + pFilterParam->maMosaicTileSize.mnTileHeight : 4; + sal_Bool bRet = sal_False; + + if( !nTileWidth ) + nTileWidth = 1; + + if( !nTileHeight ) + nTileHeight = 1; + + if( nTileWidth > 1 || nTileHeight > 1 ) + { + Bitmap* pNewBmp; + BitmapReadAccess* pReadAcc; + BitmapWriteAccess* pWriteAcc; + + if( GetBitCount() > 8 ) + { + pNewBmp = NULL; + pReadAcc = pWriteAcc = AcquireWriteAccess(); + } + else + { + pNewBmp = new Bitmap( GetSizePixel(), 24 ); + pReadAcc = AcquireReadAccess(); + pWriteAcc = pNewBmp->AcquireWriteAccess(); + } + + if( pReadAcc && pWriteAcc ) + { + BitmapColor aCol; + long nWidth = pReadAcc->Width(); + long nHeight = pReadAcc->Height(); + long nX, nY, nX1, nX2, nY1, nY2, nSumR, nSumG, nSumB; + double fArea_1; + + nY1 = 0; nY2 = nTileHeight - 1; + + if( nY2 >= nHeight ) + nY2 = nHeight - 1; + + do + { + nX1 = 0; nX2 = nTileWidth - 1; + + if( nX2 >= nWidth ) + nX2 = nWidth - 1; + + fArea_1 = 1.0 / ( ( nX2 - nX1 + 1 ) * ( nY2 - nY1 + 1 ) ); + + if( !pNewBmp ) + { + do + { + for( nY = nY1, nSumR = nSumG = nSumB = 0; nY <= nY2; nY++ ) + { + for( nX = nX1; nX <= nX2; nX++ ) + { + aCol = pReadAcc->GetPixel( nY, nX ); + nSumR += aCol.GetRed(); + nSumG += aCol.GetGreen(); + nSumB += aCol.GetBlue(); + } + } + + aCol.SetRed( (sal_uInt8) ( nSumR * fArea_1 ) ); + aCol.SetGreen( (sal_uInt8) ( nSumG * fArea_1 ) ); + aCol.SetBlue( (sal_uInt8) ( nSumB * fArea_1 ) ); + + for( nY = nY1; nY <= nY2; nY++ ) + for( nX = nX1; nX <= nX2; nX++ ) + pWriteAcc->SetPixel( nY, nX, aCol ); + + nX1 += nTileWidth; nX2 += nTileWidth; + + if( nX2 >= nWidth ) + { + nX2 = nWidth - 1; + fArea_1 = 1.0 / ( ( nX2 - nX1 + 1 ) * ( nY2 - nY1 + 1 ) ); + } + } + while( nX1 < nWidth ); + } + else + { + do + { + for( nY = nY1, nSumR = nSumG = nSumB = 0; nY <= nY2; nY++ ) + { + for( nX = nX1; nX <= nX2; nX++ ) + { + const BitmapColor& rCol = pReadAcc->GetPaletteColor( (sal_uInt8) pReadAcc->GetPixel( nY, nX ) ); + nSumR += rCol.GetRed(); + nSumG += rCol.GetGreen(); + nSumB += rCol.GetBlue(); + } + } + + aCol.SetRed( (sal_uInt8) ( nSumR * fArea_1 ) ); + aCol.SetGreen( (sal_uInt8) ( nSumG * fArea_1 ) ); + aCol.SetBlue( (sal_uInt8) ( nSumB * fArea_1 ) ); + + for( nY = nY1; nY <= nY2; nY++ ) + for( nX = nX1; nX <= nX2; nX++ ) + pWriteAcc->SetPixel( nY, nX, aCol ); + + nX1 += nTileWidth; nX2 += nTileWidth; + + if( nX2 >= nWidth ) + { + nX2 = nWidth - 1; + fArea_1 = 1.0 / ( ( nX2 - nX1 + 1 ) * ( nY2 - nY1 + 1 ) ); + } + } + while( nX1 < nWidth ); + } + + nY1 += nTileHeight; nY2 += nTileHeight; + + if( nY2 >= nHeight ) + nY2 = nHeight - 1; + } + while( nY1 < nHeight ); + + bRet = sal_True; + } + + ReleaseAccess( pReadAcc ); + + if( pNewBmp ) + { + pNewBmp->ReleaseAccess( pWriteAcc ); + + if( bRet ) + { + const MapMode aMap( maPrefMapMode ); + const Size aSize( maPrefSize ); + + *this = *pNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + + delete pNewBmp; + } + } + else + bRet = sal_True; + + return bRet; +} + +// ----------------------------------------------------------------------------- + +struct PopArtEntry +{ + sal_uInt32 mnIndex; + sal_uInt32 mnCount; +}; + +// ------------------------------------------------------------------------ + +extern "C" int __LOADONCALLAPI ImplPopArtCmpFnc( const void* p1, const void* p2 ) +{ + int nRet; + + if( ( (PopArtEntry*) p1 )->mnCount < ( (PopArtEntry*) p2 )->mnCount ) + nRet = 1; + else if( ( (PopArtEntry*) p1 )->mnCount == ( (PopArtEntry*) p2 )->mnCount ) + nRet = 0; + else + nRet = -1; + + return nRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool Bitmap::ImplPopArt( const BmpFilterParam* /*pFilterParam*/, const Link* /*pProgress*/ ) +{ + sal_Bool bRet = ( GetBitCount() > 8 ) ? Convert( BMP_CONVERSION_8BIT_COLORS ) : sal_True; + + if( bRet ) + { + bRet = sal_False; + + BitmapWriteAccess* pWriteAcc = AcquireWriteAccess(); + + if( pWriteAcc ) + { + const long nWidth = pWriteAcc->Width(); + const long nHeight = pWriteAcc->Height(); + const sal_uLong nEntryCount = 1 << pWriteAcc->GetBitCount(); + sal_uLong n; + PopArtEntry* pPopArtTable = new PopArtEntry[ nEntryCount ]; + + for( n = 0; n < nEntryCount; n++ ) + { + PopArtEntry& rEntry = pPopArtTable[ n ]; + rEntry.mnIndex = (sal_uInt16) n; + rEntry.mnCount = 0; + } + + // get pixel count for each palette entry + for( long nY = 0; nY < nHeight ; nY++ ) + for( long nX = 0; nX < nWidth; nX++ ) + pPopArtTable[ pWriteAcc->GetPixel( nY, nX ).GetIndex() ].mnCount++; + + // sort table + qsort( pPopArtTable, nEntryCount, sizeof( PopArtEntry ), ImplPopArtCmpFnc ); + + // get last used entry + sal_uLong nFirstEntry; + sal_uLong nLastEntry = 0; + + for( n = 0; n < nEntryCount; n++ ) + if( pPopArtTable[ n ].mnCount ) + nLastEntry = n; + + // rotate palette (one entry) + const BitmapColor aFirstCol( pWriteAcc->GetPaletteColor( sal::static_int_cast<sal_uInt16>(pPopArtTable[ 0 ].mnIndex) ) ); + for( nFirstEntry = 0; nFirstEntry < nLastEntry; nFirstEntry++ ) + { + pWriteAcc->SetPaletteColor( sal::static_int_cast<sal_uInt16>(pPopArtTable[ nFirstEntry ].mnIndex), + pWriteAcc->GetPaletteColor( sal::static_int_cast<sal_uInt16>(pPopArtTable[ nFirstEntry + 1 ].mnIndex) ) ); + } + pWriteAcc->SetPaletteColor( sal::static_int_cast<sal_uInt16>(pPopArtTable[ nLastEntry ].mnIndex), aFirstCol ); + + // cleanup + delete[] pPopArtTable; + ReleaseAccess( pWriteAcc ); + bRet = sal_True; + } + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bitmapex.cxx b/vcl/source/gdi/bitmapex.cxx new file mode 100644 index 000000000000..d1f130d9d1cc --- /dev/null +++ b/vcl/source/gdi/bitmapex.cxx @@ -0,0 +1,938 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <ctype.h> +#include <rtl/crc.h> +#include <vcl/salbtype.hxx> +#include <tools/stream.hxx> +#include <tools/debug.hxx> +#include <vcl/outdev.hxx> +#include <vcl/alpha.hxx> +#include <vcl/image.h> +#include <vcl/bitmapex.hxx> +#include <vcl/pngread.hxx> +#include <vcl/impimagetree.hxx> +#include <tools/rc.h> +#include <vcl/svapp.hxx> +#include <vcl/bmpacc.hxx> +#include <vcl/virdev.hxx> + +// ------------ +// - BitmapEx - +// ------------ + +BitmapEx::BitmapEx() : + eTransparent( TRANSPARENT_NONE ), + bAlpha ( sal_False ) +{ +} + +// ------------------------------------------------------------------ + +BitmapEx::BitmapEx( const BitmapEx& rBitmapEx ) : + aBitmap ( rBitmapEx.aBitmap ), + aMask ( rBitmapEx.aMask ), + aBitmapSize ( rBitmapEx.aBitmapSize ), + aTransparentColor ( rBitmapEx.aTransparentColor ), + eTransparent ( rBitmapEx.eTransparent ), + bAlpha ( rBitmapEx.bAlpha ) +{ +} + +BitmapEx::BitmapEx( const BitmapEx& rBitmapEx, Point aSrc, Size aSize ) : + eTransparent( TRANSPARENT_NONE ), + bAlpha ( sal_False ) +{ + if( rBitmapEx.IsEmpty() ) + return; + + aBitmap = Bitmap( aSize, rBitmapEx.aBitmap.GetBitCount() ); + aBitmapSize = aSize; + if( rBitmapEx.IsAlpha() ) + { + bAlpha = sal_True; + aMask = AlphaMask( aSize ).ImplGetBitmap(); + } + else if( rBitmapEx.IsTransparent() ) + aMask = Bitmap( aSize, rBitmapEx.aMask.GetBitCount() ); + + Rectangle aDestRect( Point( 0, 0 ), aSize ); + Rectangle aSrcRect( aSrc, aSize ); + CopyPixel( aDestRect, aSrcRect, &rBitmapEx ); +} + +// ------------------------------------------------------------------ + +BitmapEx::BitmapEx( const ResId& rResId ) : + eTransparent( TRANSPARENT_NONE ), + bAlpha ( sal_False ) +{ + static ImplImageTreeSingletonRef aImageTree; + ResMgr* pResMgr = NULL; + + ResMgr::GetResourceSkipHeader( rResId.SetRT( RSC_BITMAP ), &pResMgr ); + pResMgr->ReadLong(); + pResMgr->ReadLong(); + + const String aFileName( pResMgr->ReadString() ); + ::rtl::OUString aCurrentSymbolsStyle = Application::GetSettings().GetStyleSettings().GetCurrentSymbolsStyleName(); + + if( !aImageTree->loadImage( aFileName, aCurrentSymbolsStyle, *this, true ) ) + { +#ifdef DBG_UTIL + ByteString aErrorStr( "BitmapEx::BitmapEx( const ResId& rResId ): could not load image <" ); + OSL_FAIL( ( ( aErrorStr += ByteString( aFileName, RTL_TEXTENCODING_ASCII_US ) ) += '>' ).GetBuffer() ); +#endif + } +} + +// ------------------------------------------------------------------ + +BitmapEx::BitmapEx( const Bitmap& rBmp ) : + aBitmap ( rBmp ), + aBitmapSize ( aBitmap.GetSizePixel() ), + eTransparent( TRANSPARENT_NONE ), + bAlpha ( sal_False ) +{ +} + +// ------------------------------------------------------------------ + +BitmapEx::BitmapEx( const Bitmap& rBmp, const Bitmap& rMask ) : + aBitmap ( rBmp ), + aMask ( rMask ), + aBitmapSize ( aBitmap.GetSizePixel() ), + eTransparent ( !rMask ? TRANSPARENT_NONE : TRANSPARENT_BITMAP ), + bAlpha ( sal_False ) +{ + DBG_ASSERT( !rMask || rBmp.GetSizePixel() == rMask.GetSizePixel(), + "BitmapEx::BitmapEx(): size mismatch for bitmap and mask." ); + + // #105489# Ensure a mask is exactly one bit deep + if( !!aMask && aMask.GetBitCount() != 1 ) + { + OSL_TRACE("BitmapEx: forced mask to monochrome"); + aMask.ImplMakeMono( 255 ); + } +} + +// ------------------------------------------------------------------ + +BitmapEx::BitmapEx( const Bitmap& rBmp, const AlphaMask& rAlphaMask ) : + aBitmap ( rBmp ), + aMask ( rAlphaMask.ImplGetBitmap() ), + aBitmapSize ( aBitmap.GetSizePixel() ), + eTransparent ( !rAlphaMask ? TRANSPARENT_NONE : TRANSPARENT_BITMAP ), + bAlpha ( !rAlphaMask ? sal_False : sal_True ) +{ + DBG_ASSERT( !rAlphaMask || rBmp.GetSizePixel() == rAlphaMask.GetSizePixel(), + "BitmapEx::BitmapEx(): size mismatch for bitmap and alpha mask." ); + + // #i75531# the workaround below can go when + // X11SalGraphics::drawAlphaBitmap()'s render acceleration + // can handle the bitmap depth mismatch directly + if( aBitmap.GetBitCount() < aMask.GetBitCount() ) + aBitmap.Convert( BMP_CONVERSION_24BIT ); +} + +// ------------------------------------------------------------------ + +BitmapEx::BitmapEx( const Bitmap& rBmp, const Color& rTransparentColor ) : + aBitmap ( rBmp ), + aBitmapSize ( aBitmap.GetSizePixel() ), + aTransparentColor ( rTransparentColor ), + eTransparent ( TRANSPARENT_BITMAP ), + bAlpha ( sal_False ) +{ + aMask = aBitmap.CreateMask( aTransparentColor ); + + DBG_ASSERT( rBmp.GetSizePixel() == aMask.GetSizePixel(), + "BitmapEx::BitmapEx(): size mismatch for bitmap and alpha mask." ); +} + +// ------------------------------------------------------------------ + +BitmapEx::~BitmapEx() +{ +} + +// ------------------------------------------------------------------ + +// ------------------------------------------------------------------ + +BitmapEx& BitmapEx::operator=( const BitmapEx& rBitmapEx ) +{ + if( &rBitmapEx != this ) + { + aBitmap = rBitmapEx.aBitmap; + aMask = rBitmapEx.aMask; + aBitmapSize = rBitmapEx.aBitmapSize; + aTransparentColor = rBitmapEx.aTransparentColor; + eTransparent = rBitmapEx.eTransparent; + bAlpha = rBitmapEx.bAlpha; + } + + return *this; +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::operator==( const BitmapEx& rBitmapEx ) const +{ + if( eTransparent != rBitmapEx.eTransparent ) + return sal_False; + + if( aBitmap != rBitmapEx.aBitmap ) + return sal_False; + + if( aBitmapSize != rBitmapEx.aBitmapSize ) + return sal_False; + + if( eTransparent == TRANSPARENT_NONE ) + return sal_True; + + if( eTransparent == TRANSPARENT_COLOR ) + return aTransparentColor == rBitmapEx.aTransparentColor; + + return( ( aMask == rBitmapEx.aMask ) && ( bAlpha == rBitmapEx.bAlpha ) ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::IsEqual( const BitmapEx& rBmpEx ) const +{ + return( rBmpEx.eTransparent == eTransparent && + rBmpEx.bAlpha == bAlpha && + rBmpEx.aBitmap.IsEqual( aBitmap ) && + rBmpEx.aMask.IsEqual( aMask ) ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::IsEmpty() const +{ + return( aBitmap.IsEmpty() && aMask.IsEmpty() ); +} + +// ------------------------------------------------------------------ + +void BitmapEx::SetEmpty() +{ + aBitmap.SetEmpty(); + aMask.SetEmpty(); + eTransparent = TRANSPARENT_NONE; + bAlpha = sal_False; +} + +// ------------------------------------------------------------------ + +void BitmapEx::Clear() +{ + SetEmpty(); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::IsTransparent() const +{ + return( eTransparent != TRANSPARENT_NONE ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::IsAlpha() const +{ + return( IsTransparent() && bAlpha ); +} + +// ------------------------------------------------------------------ + +Bitmap BitmapEx::GetBitmap( const Color* pTransReplaceColor ) const +{ + Bitmap aRetBmp( aBitmap ); + + if( pTransReplaceColor && ( eTransparent != TRANSPARENT_NONE ) ) + { + Bitmap aTempMask; + + if( eTransparent == TRANSPARENT_COLOR ) + aTempMask = aBitmap.CreateMask( aTransparentColor ); + else + aTempMask = aMask; + + if( !IsAlpha() ) + aRetBmp.Replace( aTempMask, *pTransReplaceColor ); + else + aRetBmp.Replace( GetAlpha(), *pTransReplaceColor ); + } + + return aRetBmp; +} + +// ------------------------------------------------------------------ + +Bitmap BitmapEx::GetMask() const +{ + Bitmap aRet( aMask ); + + if( IsAlpha() ) + aRet.ImplMakeMono( 255 ); + + return aRet; +} + +// ------------------------------------------------------------------ + +AlphaMask BitmapEx::GetAlpha() const +{ + AlphaMask aAlpha; + + if( IsAlpha() ) + aAlpha.ImplSetBitmap( aMask ); + else + aAlpha = aMask; + + return aAlpha; +} + +// ------------------------------------------------------------------ + +sal_uLong BitmapEx::GetSizeBytes() const +{ + sal_uLong nSizeBytes = aBitmap.GetSizeBytes(); + + if( eTransparent == TRANSPARENT_BITMAP ) + nSizeBytes += aMask.GetSizeBytes(); + + return nSizeBytes; +} + +// ------------------------------------------------------------------ + +sal_uLong BitmapEx::GetChecksum() const +{ + sal_uInt32 nCrc = aBitmap.GetChecksum(); + SVBT32 aBT32; + + UInt32ToSVBT32( (long) eTransparent, aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( (long) bAlpha, aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + if( ( TRANSPARENT_BITMAP == eTransparent ) && !aMask.IsEmpty() ) + { + UInt32ToSVBT32( aMask.GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + + return nCrc; +} + +// ------------------------------------------------------------------ + +void BitmapEx::SetSizePixel( const Size& rNewSize ) +{ + Scale( rNewSize ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Invert() +{ + sal_Bool bRet = sal_False; + + if( !!aBitmap ) + { + bRet = aBitmap.Invert(); + + if( bRet && ( eTransparent == TRANSPARENT_COLOR ) ) + aTransparentColor = BitmapColor( aTransparentColor ).Invert(); + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Mirror( sal_uLong nMirrorFlags ) +{ + sal_Bool bRet = sal_False; + + if( !!aBitmap ) + { + bRet = aBitmap.Mirror( nMirrorFlags ); + + if( bRet && ( eTransparent == TRANSPARENT_BITMAP ) && !!aMask ) + aMask.Mirror( nMirrorFlags ); + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Scale( const double& rScaleX, const double& rScaleY, sal_uLong nScaleFlag ) +{ + sal_Bool bRet = sal_False; + + if( !!aBitmap ) + { + bRet = aBitmap.Scale( rScaleX, rScaleY, nScaleFlag ); + + if( bRet && ( eTransparent == TRANSPARENT_BITMAP ) && !!aMask ) + aMask.Scale( rScaleX, rScaleY, BMP_SCALE_FAST ); + + aBitmapSize = aBitmap.GetSizePixel(); + + DBG_ASSERT( !aMask || aBitmap.GetSizePixel() == aMask.GetSizePixel(), + "BitmapEx::Scale(): size mismatch for bitmap and alpha mask." ); + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool BitmapEx::Scale( const Size& rNewSize, sal_uLong nScaleFlag ) +{ + sal_Bool bRet; + + if( aBitmapSize.Width() && aBitmapSize.Height() ) + { + bRet = Scale( (double) rNewSize.Width() / aBitmapSize.Width(), + (double) rNewSize.Height() / aBitmapSize.Height(), + nScaleFlag ); + } + else + bRet = sal_True; + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Rotate( long nAngle10, const Color& rFillColor ) +{ + sal_Bool bRet = sal_False; + + if( !!aBitmap ) + { + const sal_Bool bTransRotate = ( Color( COL_TRANSPARENT ) == rFillColor ); + + if( bTransRotate ) + { + if( eTransparent == TRANSPARENT_COLOR ) + bRet = aBitmap.Rotate( nAngle10, aTransparentColor ); + else + { + bRet = aBitmap.Rotate( nAngle10, COL_BLACK ); + + if( eTransparent == TRANSPARENT_NONE ) + { + aMask = Bitmap( aBitmapSize, 1 ); + aMask.Erase( COL_BLACK ); + eTransparent = TRANSPARENT_BITMAP; + } + + if( bRet && !!aMask ) + aMask.Rotate( nAngle10, COL_WHITE ); + } + } + else + { + bRet = aBitmap.Rotate( nAngle10, rFillColor ); + + if( bRet && ( eTransparent == TRANSPARENT_BITMAP ) && !!aMask ) + aMask.Rotate( nAngle10, COL_WHITE ); + } + + aBitmapSize = aBitmap.GetSizePixel(); + + DBG_ASSERT( !aMask || aBitmap.GetSizePixel() == aMask.GetSizePixel(), + "BitmapEx::Rotate(): size mismatch for bitmap and alpha mask." ); + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Crop( const Rectangle& rRectPixel ) +{ + sal_Bool bRet = sal_False; + + if( !!aBitmap ) + { + bRet = aBitmap.Crop( rRectPixel ); + + if( bRet && ( eTransparent == TRANSPARENT_BITMAP ) && !!aMask ) + aMask.Crop( rRectPixel ); + + aBitmapSize = aBitmap.GetSizePixel(); + + DBG_ASSERT( !aMask || aBitmap.GetSizePixel() == aMask.GetSizePixel(), + "BitmapEx::Crop(): size mismatch for bitmap and alpha mask." ); + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Convert( BmpConversion eConversion ) +{ + return( !!aBitmap ? aBitmap.Convert( eConversion ) : sal_False ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::ReduceColors( sal_uInt16 nNewColorCount, BmpReduce eReduce ) +{ + return( !!aBitmap ? aBitmap.ReduceColors( nNewColorCount, eReduce ) : sal_False ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Expand( sal_uLong nDX, sal_uLong nDY, const Color* pInitColor, sal_Bool bExpandTransparent ) +{ + sal_Bool bRet = sal_False; + + if( !!aBitmap ) + { + bRet = aBitmap.Expand( nDX, nDY, pInitColor ); + + if( bRet && ( eTransparent == TRANSPARENT_BITMAP ) && !!aMask ) + { + Color aColor( bExpandTransparent ? COL_WHITE : COL_BLACK ); + aMask.Expand( nDX, nDY, &aColor ); + } + + aBitmapSize = aBitmap.GetSizePixel(); + + DBG_ASSERT( !aMask || aBitmap.GetSizePixel() == aMask.GetSizePixel(), + "BitmapEx::Expand(): size mismatch for bitmap and alpha mask." ); + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::CopyPixel( const Rectangle& rRectDst, const Rectangle& rRectSrc, + const BitmapEx* pBmpExSrc ) +{ + sal_Bool bRet = sal_False; + + if( !pBmpExSrc || pBmpExSrc->IsEmpty() ) + { + if( !aBitmap.IsEmpty() ) + { + bRet = aBitmap.CopyPixel( rRectDst, rRectSrc ); + + if( bRet && ( eTransparent == TRANSPARENT_BITMAP ) && !!aMask ) + aMask.CopyPixel( rRectDst, rRectSrc ); + } + } + else + { + if( !aBitmap.IsEmpty() ) + { + bRet = aBitmap.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->aBitmap ); + + if( bRet ) + { + if( pBmpExSrc->IsAlpha() ) + { + if( IsAlpha() ) + // cast to use the optimized AlphaMask::CopyPixel + ((AlphaMask*) &aMask)->CopyPixel( rRectDst, rRectSrc, (AlphaMask*)&pBmpExSrc->aMask ); + else if( IsTransparent() ) + { + AlphaMask* pAlpha = new AlphaMask( aMask ); + + aMask = pAlpha->ImplGetBitmap(); + delete pAlpha; + bAlpha = sal_True; + aMask.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->aMask ); + } + else + { + sal_uInt8 cBlack = 0; + AlphaMask* pAlpha = new AlphaMask( GetSizePixel(), &cBlack ); + + aMask = pAlpha->ImplGetBitmap(); + delete pAlpha; + eTransparent = TRANSPARENT_BITMAP; + bAlpha = sal_True; + aMask.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->aMask ); + } + } + else if( pBmpExSrc->IsTransparent() ) + { + if( IsAlpha() ) + { + AlphaMask aAlpha( pBmpExSrc->aMask ); + aMask.CopyPixel( rRectDst, rRectSrc, &aAlpha.ImplGetBitmap() ); + } + else if( IsTransparent() ) + aMask.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->aMask ); + else + { + aMask = Bitmap( GetSizePixel(), 1 ); + aMask.Erase( Color( COL_BLACK ) ); + eTransparent = TRANSPARENT_BITMAP; + aMask.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->aMask ); + } + } + else if( IsAlpha() ) + { + sal_uInt8 cBlack = 0; + const AlphaMask aAlphaSrc( pBmpExSrc->GetSizePixel(), &cBlack ); + + aMask.CopyPixel( rRectDst, rRectSrc, &aAlphaSrc.ImplGetBitmap() ); + } + else if( IsTransparent() ) + { + Bitmap aMaskSrc( pBmpExSrc->GetSizePixel(), 1 ); + + aMaskSrc.Erase( Color( COL_BLACK ) ); + aMask.CopyPixel( rRectDst, rRectSrc, &aMaskSrc ); + } + } + } + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Erase( const Color& rFillColor ) +{ + sal_Bool bRet = sal_False; + + if( !!aBitmap ) + { + bRet = aBitmap.Erase( rFillColor ); + + if( bRet && ( eTransparent == TRANSPARENT_BITMAP ) && !!aMask ) + { + // #104416# Respect transparency on fill color + if( rFillColor.GetTransparency() ) + { + const Color aFill( rFillColor.GetTransparency(), rFillColor.GetTransparency(), rFillColor.GetTransparency() ); + aMask.Erase( aFill ); + } + else + { + const Color aBlack( COL_BLACK ); + aMask.Erase( aBlack ); + } + } + } + + return bRet; +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Dither( sal_uLong nDitherFlags ) +{ + return( !!aBitmap ? aBitmap.Dither( nDitherFlags ) : sal_False ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uLong nTol ) +{ + return( !!aBitmap ? aBitmap.Replace( rSearchColor, rReplaceColor, nTol ) : sal_False ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Replace( const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount, const sal_uLong* pTols ) +{ + return( !!aBitmap ? aBitmap.Replace( pSearchColors, pReplaceColors, nColorCount, (sal_uLong*) pTols ) : sal_False ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Adjust( short nLuminancePercent, short nContrastPercent, + short nChannelRPercent, short nChannelGPercent, short nChannelBPercent, + double fGamma, sal_Bool bInvert ) +{ + return( !!aBitmap ? aBitmap.Adjust( nLuminancePercent, nContrastPercent, + nChannelRPercent, nChannelGPercent, nChannelBPercent, + fGamma, bInvert ) : sal_False ); +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapEx::Filter( BmpFilter eFilter, const BmpFilterParam* pFilterParam, const Link* pProgress ) +{ + return( !!aBitmap ? aBitmap.Filter( eFilter, pFilterParam, pProgress ) : sal_False ); +} + +// ------------------------------------------------------------------ + +void BitmapEx::Draw( OutputDevice* pOutDev, const Point& rDestPt ) const +{ + pOutDev->DrawBitmapEx( rDestPt, *this ); +} + +// ------------------------------------------------------------------ + +void BitmapEx::Draw( OutputDevice* pOutDev, + const Point& rDestPt, const Size& rDestSize ) const +{ + pOutDev->DrawBitmapEx( rDestPt, rDestSize, *this ); +} + +// ------------------------------------------------------------------ + +void BitmapEx::Draw( OutputDevice* pOutDev, + const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel ) const +{ + pOutDev->DrawBitmapEx( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, *this ); +} + +BitmapEx BitmapEx:: AutoScaleBitmap(BitmapEx & aBitmap, const long aStandardSize) +{ + Point aEmptyPoint(0,0); + sal_Int32 imgNewWidth = 0; + sal_Int32 imgNewHeight = 0; + double imgposX = 0; + double imgposY = 0; + BitmapEx aRet = aBitmap; + double imgOldWidth = aRet.GetSizePixel().Width(); + double imgOldHeight =aRet.GetSizePixel().Height(); + + Size aScaledSize; + if (imgOldWidth >= aStandardSize || imgOldHeight >= aStandardSize) + { + if (imgOldWidth >= imgOldHeight) + { + imgNewWidth = aStandardSize; + imgNewHeight = sal_Int32(imgOldHeight / (imgOldWidth / aStandardSize) + 0.5); + imgposX = 0; + imgposY = (aStandardSize - (imgOldHeight / (imgOldWidth / aStandardSize) + 0.5)) / 2 + 0.5; + } + else + { + imgNewHeight = aStandardSize; + imgNewWidth = sal_Int32(imgOldWidth / (imgOldHeight / aStandardSize) + 0.5); + imgposY = 0; + imgposX = (aStandardSize - (imgOldWidth / (imgOldHeight / aStandardSize) + 0.5)) / 2 + 0.5; + } + + aScaledSize = Size( imgNewWidth, imgNewHeight ); + aRet.Scale( aScaledSize, BMP_SCALE_INTERPOLATE ); + } + else + { + imgposX = (aStandardSize - imgOldWidth) / 2 + 0.5; + imgposY = (aStandardSize - imgOldHeight) / 2 + 0.5; + } + + Size aStdSize( aStandardSize, aStandardSize ); + Rectangle aRect(aEmptyPoint, aStdSize ); + + VirtualDevice aVirDevice( *Application::GetDefaultDevice(), 0, 1 ); + aVirDevice.SetOutputSizePixel( aStdSize ); + aVirDevice.SetFillColor( COL_TRANSPARENT ); + aVirDevice.SetLineColor( COL_TRANSPARENT ); + + //draw a rect into virDevice + aVirDevice.DrawRect( aRect ); + Point aPointPixel( (long)imgposX, (long)imgposY ); + aVirDevice.DrawBitmapEx( aPointPixel, aRet ); + aRet = aVirDevice.GetBitmapEx( aEmptyPoint, aStdSize ); + + return aRet; +} +// ------------------------------------------------------------------ + +sal_uInt8 BitmapEx::GetTransparency(sal_Int32 nX, sal_Int32 nY) const +{ + sal_uInt8 nTransparency(0xff); + + if(!aBitmap.IsEmpty()) + { + if(nX >= 0 && nX < aBitmapSize.Width() && nY >= 0 && nY < aBitmapSize.Height()) + { + switch(eTransparent) + { + case TRANSPARENT_NONE: + { + // not transparent, ergo all covered + nTransparency = 0x00; + break; + } + case TRANSPARENT_COLOR: + { + Bitmap aTestBitmap(aBitmap); + BitmapReadAccess* pRead = aTestBitmap.AcquireReadAccess(); + + if(pRead) + { + const Color aColor = pRead->GetColor(nY, nX); + + // if color is not equal to TransparentColor, we are not transparent + if(aColor != aTransparentColor) + { + nTransparency = 0x00; + } + + aTestBitmap.ReleaseAccess(pRead); + } + break; + } + case TRANSPARENT_BITMAP: + { + if(!aMask.IsEmpty()) + { + Bitmap aTestBitmap(aMask); + BitmapReadAccess* pRead = aTestBitmap.AcquireReadAccess(); + + if(pRead) + { + const BitmapColor aBitmapColor(pRead->GetPixel(nY, nX)); + + if(bAlpha) + { + nTransparency = aBitmapColor.GetIndex(); + } + else + { + if(0x00 == aBitmapColor.GetIndex()) + { + nTransparency = 0x00; + } + } + + aTestBitmap.ReleaseAccess(pRead); + } + } + break; + } + } + } + } + + return nTransparency; +} + +// ------------------------------------------------------------------ + +SvStream& operator<<( SvStream& rOStm, const BitmapEx& rBitmapEx ) +{ + rBitmapEx.aBitmap.Write( rOStm ); + + rOStm << (sal_uInt32) 0x25091962; + rOStm << (sal_uInt32) 0xACB20201; + rOStm << (sal_uInt8) rBitmapEx.eTransparent; + + if( rBitmapEx.eTransparent == TRANSPARENT_BITMAP ) + rBitmapEx.aMask.Write( rOStm ); + else if( rBitmapEx.eTransparent == TRANSPARENT_COLOR ) + rOStm << rBitmapEx.aTransparentColor; + + return rOStm; +} + +// ------------------------------------------------------------------ + +SvStream& operator>>( SvStream& rIStm, BitmapEx& rBitmapEx ) +{ + Bitmap aBmp; + + rIStm >> aBmp; + + if( !rIStm.GetError() ) + { + const sal_uLong nStmPos = rIStm.Tell(); + sal_uInt32 nMagic1 = 0; + sal_uInt32 nMagic2 = 0; + + rIStm >> nMagic1 >> nMagic2; + + if( ( nMagic1 != 0x25091962 ) || ( nMagic2 != 0xACB20201 ) || rIStm.GetError() ) + { + rIStm.ResetError(); + rIStm.Seek( nStmPos ); + rBitmapEx = aBmp; + } + else + { + sal_uInt8 bTransparent = false; + + rIStm >> bTransparent; + + if( bTransparent == (sal_uInt8) TRANSPARENT_BITMAP ) + { + Bitmap aMask; + + rIStm >> aMask; + + if( !!aMask) + { + // do we have an alpha mask? + if( ( 8 == aMask.GetBitCount() ) && aMask.HasGreyPalette() ) + { + AlphaMask aAlpha; + + // create alpha mask quickly (without greyscale conversion) + aAlpha.ImplSetBitmap( aMask ); + rBitmapEx = BitmapEx( aBmp, aAlpha ); + } + else + rBitmapEx = BitmapEx( aBmp, aMask ); + } + else + rBitmapEx = aBmp; + } + else if( bTransparent == (sal_uInt8) TRANSPARENT_COLOR ) + { + Color aTransparentColor; + + rIStm >> aTransparentColor; + rBitmapEx = BitmapEx( aBmp, aTransparentColor ); + } + else + rBitmapEx = aBmp; + } + } + + return rIStm; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bmpacc.cxx b/vcl/source/gdi/bmpacc.cxx new file mode 100644 index 000000000000..0762821c3267 --- /dev/null +++ b/vcl/source/gdi/bmpacc.cxx @@ -0,0 +1,450 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <vcl/salbtype.hxx> +#include <vcl/impbmp.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/bmpacc.hxx> +#include <string.h> + +// -------------------- +// - BitmapReadAccess - +// -------------------- + +BitmapReadAccess::BitmapReadAccess( Bitmap& rBitmap, sal_Bool bModify ) : + mpBuffer ( NULL ), + mpScanBuf ( NULL ), + mFncGetPixel ( NULL ), + mFncSetPixel ( NULL ), + mbModify ( bModify ) +{ + ImplCreate( rBitmap ); +} + +// ------------------------------------------------------------------ + +BitmapReadAccess::BitmapReadAccess( Bitmap& rBitmap ) : + mpBuffer ( NULL ), + mpScanBuf ( NULL ), + mFncGetPixel ( NULL ), + mFncSetPixel ( NULL ), + mbModify ( sal_False ) +{ + ImplCreate( rBitmap ); +} + +// ------------------------------------------------------------------ + +BitmapReadAccess::~BitmapReadAccess() +{ + ImplDestroy(); +} + +// ------------------------------------------------------------------ + +void BitmapReadAccess::ImplCreate( Bitmap& rBitmap ) +{ + ImpBitmap* pImpBmp = rBitmap.ImplGetImpBitmap(); + + DBG_ASSERT( pImpBmp, "Forbidden Access to empty bitmap!" ); + + if( pImpBmp ) + { + if( mbModify && !maBitmap.ImplGetImpBitmap() ) + { + rBitmap.ImplMakeUnique(); + pImpBmp = rBitmap.ImplGetImpBitmap(); + } + else + { + DBG_ASSERT( !mbModify || pImpBmp->ImplGetRefCount() == 2, + "Unpredictable results: bitmap is referenced more than once!" ); + } + + mpBuffer = pImpBmp->ImplAcquireBuffer( !mbModify ); + + if( !mpBuffer ) + { + ImpBitmap* pNewImpBmp = new ImpBitmap; + + if( pNewImpBmp->ImplCreate( *pImpBmp, rBitmap.GetBitCount() ) ) + { + pImpBmp = pNewImpBmp; + rBitmap.ImplSetImpBitmap( pImpBmp ); + mpBuffer = pImpBmp->ImplAcquireBuffer( !mbModify ); + } + else + delete pNewImpBmp; + } + + if( mpBuffer ) + { + const long nHeight = mpBuffer->mnHeight; + Scanline pTmpLine = mpBuffer->mpBits; + + mpScanBuf = new Scanline[ nHeight ]; + maColorMask = mpBuffer->maColorMask; + + if( BMP_SCANLINE_ADJUSTMENT( mpBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN ) + { + for( long nY = 0L; nY < nHeight; nY++, pTmpLine += mpBuffer->mnScanlineSize ) + mpScanBuf[ nY ] = pTmpLine; + } + else + { + for( long nY = nHeight - 1; nY >= 0; nY--, pTmpLine += mpBuffer->mnScanlineSize ) + mpScanBuf[ nY ] = pTmpLine; + } + + if( !ImplSetAccessPointers( BMP_SCANLINE_FORMAT( mpBuffer->mnFormat ) ) ) + { + delete[] mpScanBuf; + mpScanBuf = NULL; + + pImpBmp->ImplReleaseBuffer( mpBuffer, !mbModify ); + mpBuffer = NULL; + } + else + maBitmap = rBitmap; + } + } +} + +// ------------------------------------------------------------------ + +void BitmapReadAccess::ImplDestroy() +{ + ImpBitmap* pImpBmp = maBitmap.ImplGetImpBitmap(); + + delete[] mpScanBuf; + mpScanBuf = NULL; + + if( mpBuffer && pImpBmp ) + { + pImpBmp->ImplReleaseBuffer( mpBuffer, !mbModify ); + mpBuffer = NULL; + } +} + +// ------------------------------------------------------------------ + +sal_Bool BitmapReadAccess::ImplSetAccessPointers( sal_uLong nFormat ) +{ + sal_Bool bRet = sal_True; + + switch( nFormat ) + { + CASE_FORMAT( _1BIT_MSB_PAL ) + CASE_FORMAT( _1BIT_LSB_PAL ) + CASE_FORMAT( _4BIT_MSN_PAL ) + CASE_FORMAT( _4BIT_LSN_PAL ) + CASE_FORMAT( _8BIT_PAL ) + CASE_FORMAT( _8BIT_TC_MASK ) + CASE_FORMAT( _16BIT_TC_MSB_MASK ) + CASE_FORMAT( _16BIT_TC_LSB_MASK ) + CASE_FORMAT( _24BIT_TC_BGR ) + CASE_FORMAT( _24BIT_TC_RGB ) + CASE_FORMAT( _24BIT_TC_MASK ) + CASE_FORMAT( _32BIT_TC_ABGR ) + CASE_FORMAT( _32BIT_TC_ARGB ) + CASE_FORMAT( _32BIT_TC_BGRA ) + CASE_FORMAT( _32BIT_TC_RGBA ) + CASE_FORMAT( _32BIT_TC_MASK ) + + default: + bRet = sal_False; + break; + } + + return bRet; +} + +// ------------------------------------------------------------------ + +void BitmapReadAccess::ImplZeroInitUnusedBits() +{ + const sal_uInt32 nWidth = Width(), nHeight = Height(), nScanSize = GetScanlineSize(); + + if( nWidth && nHeight && nScanSize && GetBuffer() ) + { + sal_uInt32 nBits; + bool bMsb; + + const sal_uLong nScanlineFormat = GetScanlineFormat(); + switch( nScanlineFormat ) + { + case( BMP_FORMAT_1BIT_MSB_PAL ): + nBits = 1; + bMsb = true; + break; + + case( BMP_FORMAT_1BIT_LSB_PAL ): + nBits = 1; + bMsb = false; + break; + + case( BMP_FORMAT_4BIT_MSN_PAL ): + nBits = 4; + bMsb = true; + break; + + case( BMP_FORMAT_4BIT_LSN_PAL ): + nBits = 4; + bMsb = false; + break; + + case( BMP_FORMAT_8BIT_PAL ): + case( BMP_FORMAT_8BIT_TC_MASK ): + bMsb = true; + nBits = 8; + break; + + case( BMP_FORMAT_16BIT_TC_MSB_MASK ): + case( BMP_FORMAT_16BIT_TC_LSB_MASK ): + bMsb = true; + nBits = 16; + break; + + case( BMP_FORMAT_24BIT_TC_BGR ): + case( BMP_FORMAT_24BIT_TC_RGB ): + case( BMP_FORMAT_24BIT_TC_MASK ): + bMsb = true; + nBits = 24; + break; + + case( BMP_FORMAT_32BIT_TC_ABGR ): + case( BMP_FORMAT_32BIT_TC_ARGB ): + case( BMP_FORMAT_32BIT_TC_BGRA ): + case( BMP_FORMAT_32BIT_TC_RGBA ): + case( BMP_FORMAT_32BIT_TC_MASK ): + bMsb = true; + nBits = 32; + break; + + default: + { + OSL_FAIL( "BitmapWriteAccess::ZeroInitUnusedBits: Unsupported pixel format"); + nBits = 0; + bMsb = true; + } + break; + } + + nBits *= nWidth; + if( nScanSize % 4 || !bMsb ) + { + DBG_ASSERT( 8*nScanSize >= nBits, + "BitmapWriteAccess::ZeroInitUnusedBits: span size smaller than width?!"); + const sal_uInt32 nLeftOverBits = 8*sizeof(sal_uInt8)*nScanSize - nBits; + if( nLeftOverBits != 0 ) // else there is really nothing to do + { + const sal_uInt32 nBytes = (nLeftOverBits + 7U) >> 3U; + sal_uInt8 nMask; + + if( bMsb ) + nMask = static_cast<sal_uInt8>(0xffU << (nLeftOverBits & 3UL)); + else + nMask = static_cast<sal_uInt8>(0xffU >> (nLeftOverBits & 3UL)); + + sal_uInt8* pLastBytes = (sal_uInt8*)GetBuffer() + ( nScanSize - nBytes ); + for( sal_uInt32 i = 0; i < nHeight; i++, pLastBytes += nScanSize ) + { + *pLastBytes &= nMask; + for( sal_uInt32 j = 1; j < nBytes; j++ ) + pLastBytes[j] = 0; + } + } + } + else if( nBits & 0x1f ) + { + sal_uInt32 nMask = 0xffffffff << ( ( nScanSize << 3 ) - nBits ); + sal_uInt8* pLast4Bytes = (sal_uInt8*) GetBuffer() + ( nScanSize - 4 ); + +#ifdef OSL_LITENDIAN + nMask = SWAPLONG( nMask ); +#endif + for( sal_uInt32 i = 0; i < nHeight; i++, pLast4Bytes += nScanSize ) + ( *(sal_uInt32*) pLast4Bytes ) &= nMask; + } + } +} + +// ------------------------------------------------------------------ + +void BitmapReadAccess::Flush() +{ + ImplDestroy(); +} + +// ------------------------------------------------------------------ + +void BitmapReadAccess::ReAccess( sal_Bool bModify ) +{ + const ImpBitmap* pImpBmp = maBitmap.ImplGetImpBitmap(); + + DBG_ASSERT( !mpBuffer, "No ReAccess possible while bitmap is being accessed!" ); + DBG_ASSERT( pImpBmp && ( pImpBmp->ImplGetRefCount() > 1UL ), "Accessed bitmap does not exist anymore!" ); + + if( !mpBuffer && pImpBmp && ( pImpBmp->ImplGetRefCount() > 1UL ) ) + { + mbModify = bModify; + ImplCreate( maBitmap ); + } +} + +// ------------------------------------------------------------------ + +sal_uInt16 BitmapReadAccess::GetBestPaletteIndex( const BitmapColor& rBitmapColor ) const +{ + return( HasPalette() ? mpBuffer->maPalette.GetBestIndex( rBitmapColor ) : 0 ); +} + +// --------------------- +// - BitmapWriteAccess - +// --------------------- + +BitmapWriteAccess::BitmapWriteAccess( Bitmap& rBitmap ) : + BitmapReadAccess( rBitmap, sal_True ), + mpLineColor ( NULL ), + mpFillColor ( NULL ) +{ +} + +// ------------------------------------------------------------------ + +BitmapWriteAccess::~BitmapWriteAccess() +{ + delete mpLineColor; + delete mpFillColor; +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::CopyScanline( long nY, const BitmapReadAccess& rReadAcc ) +{ + DBG_ASSERT( ( nY >= 0 ) && ( nY < mpBuffer->mnHeight ), "y-coordinate in destination out of range!" ); + DBG_ASSERT( nY < rReadAcc.Height(), "y-coordinate in source out of range!" ); + DBG_ASSERT( ( HasPalette() && rReadAcc.HasPalette() ) || ( !HasPalette() && !rReadAcc.HasPalette() ), "No copying possible between palette bitmap and TC bitmap!" ); + + if( ( GetScanlineFormat() == rReadAcc.GetScanlineFormat() ) && + ( GetScanlineSize() >= rReadAcc.GetScanlineSize() ) ) + { + memcpy( mpScanBuf[ nY ], rReadAcc.GetScanline( nY ), rReadAcc.GetScanlineSize() ); + } + else + // TODO: use fastbmp infrastructure + for( long nX = 0L, nWidth = Min( mpBuffer->mnWidth, rReadAcc.Width() ); nX < nWidth; nX++ ) + SetPixel( nY, nX, rReadAcc.GetPixel( nY, nX ) ); +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::CopyScanline( long nY, ConstScanline aSrcScanline, + sal_uLong nSrcScanlineFormat, sal_uLong nSrcScanlineSize ) +{ + const sal_uLong nFormat = BMP_SCANLINE_FORMAT( nSrcScanlineFormat ); + + DBG_ASSERT( ( nY >= 0 ) && ( nY < mpBuffer->mnHeight ), "y-coordinate in destination out of range!" ); + DBG_ASSERT( ( HasPalette() && nFormat <= BMP_FORMAT_8BIT_PAL ) || + ( !HasPalette() && nFormat > BMP_FORMAT_8BIT_PAL ), + "No copying possible between palette and non palette scanlines!" ); + + const sal_uLong nCount = Min( GetScanlineSize(), nSrcScanlineSize ); + + if( nCount ) + { + if( GetScanlineFormat() == BMP_SCANLINE_FORMAT( nSrcScanlineFormat ) ) + memcpy( mpScanBuf[ nY ], aSrcScanline, nCount ); + else + { + DBG_ASSERT( nFormat != BMP_FORMAT_8BIT_TC_MASK && + nFormat != BMP_FORMAT_16BIT_TC_MSB_MASK && nFormat != BMP_FORMAT_16BIT_TC_LSB_MASK && + nFormat != BMP_FORMAT_24BIT_TC_MASK && nFormat != BMP_FORMAT_32BIT_TC_MASK, + "No support for pixel formats with color masks yet!" ); + + // TODO: use fastbmp infrastructure + FncGetPixel pFncGetPixel; + + switch( nFormat ) + { + case( BMP_FORMAT_1BIT_MSB_PAL ): pFncGetPixel = GetPixelFor_1BIT_MSB_PAL; break; + case( BMP_FORMAT_1BIT_LSB_PAL ): pFncGetPixel = GetPixelFor_1BIT_LSB_PAL; break; + case( BMP_FORMAT_4BIT_MSN_PAL ): pFncGetPixel = GetPixelFor_4BIT_MSN_PAL; break; + case( BMP_FORMAT_4BIT_LSN_PAL ): pFncGetPixel = GetPixelFor_4BIT_LSN_PAL; break; + case( BMP_FORMAT_8BIT_PAL ): pFncGetPixel = GetPixelFor_8BIT_PAL; break; + case( BMP_FORMAT_8BIT_TC_MASK ): pFncGetPixel = GetPixelFor_8BIT_TC_MASK; break; + case( BMP_FORMAT_16BIT_TC_MSB_MASK ): pFncGetPixel = GetPixelFor_16BIT_TC_MSB_MASK; break; + case( BMP_FORMAT_16BIT_TC_LSB_MASK ): pFncGetPixel = GetPixelFor_16BIT_TC_LSB_MASK; break; + case( BMP_FORMAT_24BIT_TC_BGR ): pFncGetPixel = GetPixelFor_24BIT_TC_BGR; break; + case( BMP_FORMAT_24BIT_TC_RGB ): pFncGetPixel = GetPixelFor_24BIT_TC_RGB; break; + case( BMP_FORMAT_24BIT_TC_MASK ): pFncGetPixel = GetPixelFor_24BIT_TC_MASK; break; + case( BMP_FORMAT_32BIT_TC_ABGR ): pFncGetPixel = GetPixelFor_32BIT_TC_ABGR; break; + case( BMP_FORMAT_32BIT_TC_ARGB ): pFncGetPixel = GetPixelFor_32BIT_TC_ARGB; break; + case( BMP_FORMAT_32BIT_TC_BGRA ): pFncGetPixel = GetPixelFor_32BIT_TC_BGRA; break; + case( BMP_FORMAT_32BIT_TC_RGBA ): pFncGetPixel = GetPixelFor_32BIT_TC_RGBA; break; + case( BMP_FORMAT_32BIT_TC_MASK ): pFncGetPixel = GetPixelFor_32BIT_TC_MASK; break; + + default: + pFncGetPixel = NULL; + break; + } + + if( pFncGetPixel ) + { + const ColorMask aDummyMask; + + for( long nX = 0L, nWidth = mpBuffer->mnWidth; nX < nWidth; nX++ ) + SetPixel( nY, nX, pFncGetPixel( aSrcScanline, nX, aDummyMask ) ); + } + } + } +} + + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::CopyBuffer( const BitmapReadAccess& rReadAcc ) +{ + DBG_ASSERT( ( HasPalette() && rReadAcc.HasPalette() ) || ( !HasPalette() && !rReadAcc.HasPalette() ), "No copying possible between palette bitmap and TC bitmap!" ); + + if( ( GetScanlineFormat() == rReadAcc.GetScanlineFormat() ) && + ( GetScanlineSize() == rReadAcc.GetScanlineSize() ) ) + { + const long nHeight = Min( mpBuffer->mnHeight, rReadAcc.Height() ); + const sal_uLong nCount = nHeight * mpBuffer->mnScanlineSize; + + memcpy( mpBuffer->mpBits, rReadAcc.GetBuffer(), nCount ); + } + else + for( long nY = 0L, nHeight = Min( mpBuffer->mnHeight, rReadAcc.Height() ); nY < nHeight; nY++ ) + CopyScanline( nY, rReadAcc ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bmpacc2.cxx b/vcl/source/gdi/bmpacc2.cxx new file mode 100644 index 000000000000..718bd56cdb0c --- /dev/null +++ b/vcl/source/gdi/bmpacc2.cxx @@ -0,0 +1,334 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <vcl/salbtype.hxx> +#include <vcl/bmpacc.hxx> + +// ---------------- +// - BitmapAccess - +// ---------------- + +IMPL_FORMAT_GETPIXEL_NOMASK( _1BIT_MSB_PAL ) +{ + return( pScanline[ nX >> 3 ] & ( 1 << ( 7 - ( nX & 7 ) ) ) ? 1 : 0 ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _1BIT_MSB_PAL ) +{ + sal_uInt8& rByte = pScanline[ nX >> 3 ]; + + ( rBitmapColor.GetIndex() & 1 ) ? ( rByte |= 1 << ( 7 - ( nX & 7 ) ) ) : + ( rByte &= ~( 1 << ( 7 - ( nX & 7 ) ) ) ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _1BIT_LSB_PAL ) +{ + return( pScanline[ nX >> 3 ] & ( 1 << ( nX & 7 ) ) ? 1 : 0 ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _1BIT_LSB_PAL ) +{ + sal_uInt8& rByte = pScanline[ nX >> 3 ]; + + ( rBitmapColor.GetIndex() & 1 ) ? ( rByte |= 1 << ( nX & 7 ) ) : + ( rByte &= ~( 1 << ( nX & 7 ) ) ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _4BIT_MSN_PAL ) +{ + return( ( pScanline[ nX >> 1 ] >> ( nX & 1 ? 0 : 4 ) ) & 0x0f ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _4BIT_MSN_PAL ) +{ + sal_uInt8& rByte = pScanline[ nX >> 1 ]; + + ( nX & 1 ) ? ( rByte &= 0xf0, rByte |= ( rBitmapColor.GetIndex() & 0x0f ) ) : + ( rByte &= 0x0f, rByte |= ( rBitmapColor.GetIndex() << 4 ) ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _4BIT_LSN_PAL ) +{ + return( ( pScanline[ nX >> 1 ] >> ( nX & 1 ? 4 : 0 ) ) & 0x0f ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _4BIT_LSN_PAL ) +{ + sal_uInt8& rByte = pScanline[ nX >> 1 ]; + + ( nX & 1 ) ? ( rByte &= 0x0f, rByte |= ( rBitmapColor.GetIndex() << 4 ) ) : + ( rByte &= 0xf0, rByte |= ( rBitmapColor.GetIndex() & 0x0f ) ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _8BIT_PAL ) +{ + return pScanline[ nX ]; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _8BIT_PAL ) +{ + pScanline[ nX ] = rBitmapColor.GetIndex(); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL( _8BIT_TC_MASK ) +{ + BitmapColor aColor; + rMask.GetColorFor8Bit( aColor, pScanline + nX ); + return aColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL( _8BIT_TC_MASK ) +{ + rMask.SetColorFor8Bit( rBitmapColor, pScanline + nX ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL( _16BIT_TC_MSB_MASK ) +{ + BitmapColor aColor; + rMask.GetColorFor16BitMSB( aColor, pScanline + ( nX << 1UL ) ); + return aColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL( _16BIT_TC_MSB_MASK ) +{ + rMask.SetColorFor16BitMSB( rBitmapColor, pScanline + ( nX << 1UL ) ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL( _16BIT_TC_LSB_MASK ) +{ + BitmapColor aColor; + rMask.GetColorFor16BitLSB( aColor, pScanline + ( nX << 1UL ) ); + return aColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL( _16BIT_TC_LSB_MASK ) +{ + rMask.SetColorFor16BitLSB( rBitmapColor, pScanline + ( nX << 1UL ) ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _24BIT_TC_BGR ) +{ + BitmapColor aBitmapColor; + + aBitmapColor.SetBlue( *( pScanline = pScanline + nX * 3 )++ ); + aBitmapColor.SetGreen( *pScanline++ ); + aBitmapColor.SetRed( *pScanline ); + + return aBitmapColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _24BIT_TC_BGR ) +{ + *( pScanline = pScanline + nX * 3 )++ = rBitmapColor.GetBlue(); + *pScanline++ = rBitmapColor.GetGreen(); + *pScanline = rBitmapColor.GetRed(); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _24BIT_TC_RGB ) +{ + BitmapColor aBitmapColor; + + aBitmapColor.SetRed( *( pScanline = pScanline + nX * 3 )++ ); + aBitmapColor.SetGreen( *pScanline++ ); + aBitmapColor.SetBlue( *pScanline ); + + return aBitmapColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _24BIT_TC_RGB ) +{ + *( pScanline = pScanline + nX * 3 )++ = rBitmapColor.GetRed(); + *pScanline++ = rBitmapColor.GetGreen(); + *pScanline = rBitmapColor.GetBlue(); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL( _24BIT_TC_MASK ) +{ + BitmapColor aColor; + rMask.GetColorFor24Bit( aColor, pScanline + nX * 3L ); + return aColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL( _24BIT_TC_MASK ) +{ + rMask.SetColorFor24Bit( rBitmapColor, pScanline + nX * 3L ); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _32BIT_TC_ABGR ) +{ + BitmapColor aBitmapColor; + + aBitmapColor.SetBlue( *( pScanline = pScanline + ( nX << 2 ) + 1 )++ ); + aBitmapColor.SetGreen( *pScanline++ ); + aBitmapColor.SetRed( *pScanline ); + + return aBitmapColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _32BIT_TC_ABGR ) +{ + *( pScanline = pScanline + ( nX << 2 ) )++ = 0; + *pScanline++ = rBitmapColor.GetBlue(); + *pScanline++ = rBitmapColor.GetGreen(); + *pScanline = rBitmapColor.GetRed(); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _32BIT_TC_ARGB ) +{ + BitmapColor aBitmapColor; + + aBitmapColor.SetRed( *( pScanline = pScanline + ( nX << 2 ) + 1 )++ ); + aBitmapColor.SetGreen( *pScanline++ ); + aBitmapColor.SetBlue( *pScanline ); + + return aBitmapColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _32BIT_TC_ARGB ) +{ + *( pScanline = pScanline + ( nX << 2 ) )++ = 0; + *pScanline++ = rBitmapColor.GetRed(); + *pScanline++ = rBitmapColor.GetGreen(); + *pScanline = rBitmapColor.GetBlue(); +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _32BIT_TC_BGRA ) +{ + BitmapColor aBitmapColor; + + aBitmapColor.SetBlue( *( pScanline = pScanline + ( nX << 2 ) )++ ); + aBitmapColor.SetGreen( *pScanline++ ); + aBitmapColor.SetRed( *pScanline ); + + return aBitmapColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _32BIT_TC_BGRA ) +{ + *( pScanline = pScanline + ( nX << 2 ) )++ = rBitmapColor.GetBlue(); + *pScanline++ = rBitmapColor.GetGreen(); + *pScanline++ = rBitmapColor.GetRed(); + *pScanline = 0; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL_NOMASK( _32BIT_TC_RGBA ) +{ + BitmapColor aBitmapColor; + + aBitmapColor.SetRed( *( pScanline = pScanline + ( nX << 2 ) )++ ); + aBitmapColor.SetGreen( *pScanline++ ); + aBitmapColor.SetBlue( *pScanline ); + + return aBitmapColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL_NOMASK( _32BIT_TC_RGBA ) +{ + *( pScanline = pScanline + ( nX << 2 ) )++ = rBitmapColor.GetRed(); + *pScanline++ = rBitmapColor.GetGreen(); + *pScanline++ = rBitmapColor.GetBlue(); + *pScanline = 0; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_GETPIXEL( _32BIT_TC_MASK ) +{ + BitmapColor aColor; + rMask.GetColorFor32Bit( aColor, pScanline + ( nX << 2UL ) ); + return aColor; +} + +// ------------------------------------------------------------------ + +IMPL_FORMAT_SETPIXEL( _32BIT_TC_MASK ) +{ + rMask.SetColorFor32Bit( rBitmapColor, pScanline + ( nX << 2UL ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bmpacc3.cxx b/vcl/source/gdi/bmpacc3.cxx new file mode 100644 index 000000000000..5e87d680e9c0 --- /dev/null +++ b/vcl/source/gdi/bmpacc3.cxx @@ -0,0 +1,413 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <vcl/salbtype.hxx> +#include <vcl/bitmap.hxx> +#include <tools/poly.hxx> +#include <vcl/region.hxx> +#include <vcl/bmpacc.hxx> +#include <vcl/bmpfast.hxx> + +// --------------------- +// - BitmapWriteAccess - +// --------------------- + +void BitmapWriteAccess::SetLineColor() +{ + delete mpLineColor; + mpLineColor = NULL; +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::SetLineColor( const Color& rColor ) +{ + delete mpLineColor; + + if( rColor.GetTransparency() == 255 ) + mpLineColor = NULL; + else + mpLineColor = ( HasPalette() ? new BitmapColor( (sal_uInt8) GetBestPaletteIndex( rColor ) ) : new BitmapColor( rColor ) ); +} + +// ------------------------------------------------------------------ + +Color BitmapWriteAccess::GetLineColor() const +{ + Color aRet; + + if( mpLineColor ) + aRet = (const Color&) *mpLineColor; + else + aRet.SetTransparency( 255 ); + + return aRet; +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::SetFillColor() +{ + delete mpFillColor; + mpFillColor = NULL; +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::SetFillColor( const Color& rColor ) +{ + delete mpFillColor; + + if( rColor.GetTransparency() == 255 ) + mpFillColor = NULL; + else + mpFillColor = ( HasPalette() ? new BitmapColor( (sal_uInt8) GetBestPaletteIndex( rColor ) ) : new BitmapColor( rColor ) ); +} + +// ------------------------------------------------------------------ + +Color BitmapWriteAccess::GetFillColor() const +{ + Color aRet; + + if( mpFillColor ) + aRet = (const Color&) *mpFillColor; + else + aRet.SetTransparency( 255 ); + + return aRet; +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::Erase( const Color& rColor ) +{ + // convert the color format from RGB to palette index if needed + // TODO: provide and use Erase( BitmapColor& method) + BitmapColor aColor = rColor; + if( HasPalette() ) + aColor = BitmapColor( (sal_uInt8)GetBestPaletteIndex( rColor) ); + // try fast bitmap method first + if( ImplFastEraseBitmap( *mpBuffer, aColor ) ) + return; + + // use the canonical method to clear the bitmap + BitmapColor* pOldFillColor = mpFillColor ? new BitmapColor( *mpFillColor ) : NULL; + const Point aPoint; + const Rectangle aRect( aPoint, maBitmap.GetSizePixel() ); + + SetFillColor( rColor ); + FillRect( aRect ); + delete mpFillColor; + mpFillColor = pOldFillColor; +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::DrawLine( const Point& rStart, const Point& rEnd ) +{ + if( mpLineColor ) + { + const BitmapColor& rLineColor = *mpLineColor; + long nX, nY; + + if ( rStart.X() == rEnd.X() ) + { + // vertikale Line + const long nEndY = rEnd.Y(); + + nX = rStart.X(); + nY = rStart.Y(); + + if ( nEndY > nY ) + { + for (; nY <= nEndY; nY++ ) + SetPixel( nY, nX, rLineColor ); + } + else + { + for (; nY >= nEndY; nY-- ) + SetPixel( nY, nX, rLineColor ); + } + } + else if ( rStart.Y() == rEnd.Y() ) + { + // horizontale Line + const long nEndX = rEnd.X(); + + nX = rStart.X(); + nY = rStart.Y(); + + if ( nEndX > nX ) + { + for (; nX <= nEndX; nX++ ) + SetPixel( nY, nX, rLineColor ); + } + else + { + for (; nX >= nEndX; nX-- ) + SetPixel( nY, nX, rLineColor ); + } + } + else + { + const long nDX = labs( rEnd.X() - rStart.X() ); + const long nDY = labs( rEnd.Y() - rStart.Y() ); + long nX1; + long nY1; + long nX2; + long nY2; + + if ( nDX >= nDY ) + { + if ( rStart.X() < rEnd.X() ) + { + nX1 = rStart.X(); + nY1 = rStart.Y(); + nX2 = rEnd.X(); + nY2 = rEnd.Y(); + } + else + { + nX1 = rEnd.X(); + nY1 = rEnd.Y(); + nX2 = rStart.X(); + nY2 = rStart.Y(); + } + + const long nDYX = ( nDY - nDX ) << 1; + const long nDY2 = nDY << 1; + long nD = nDY2 - nDX; + sal_Bool bPos = nY1 < nY2; + + for ( nX = nX1, nY = nY1; nX <= nX2; nX++ ) + { + SetPixel( nY, nX, rLineColor ); + + if ( nD < 0 ) + nD += nDY2; + else + { + nD += nDYX; + + if ( bPos ) + nY++; + else + nY--; + } + } + } + else + { + if ( rStart.Y() < rEnd.Y() ) + { + nX1 = rStart.X(); + nY1 = rStart.Y(); + nX2 = rEnd.X(); + nY2 = rEnd.Y(); + } + else + { + nX1 = rEnd.X(); + nY1 = rEnd.Y(); + nX2 = rStart.X(); + nY2 = rStart.Y(); + } + + const long nDYX = ( nDX - nDY ) << 1; + const long nDY2 = nDX << 1; + long nD = nDY2 - nDY; + sal_Bool bPos = nX1 < nX2; + + for ( nX = nX1, nY = nY1; nY <= nY2; nY++ ) + { + SetPixel( nY, nX, rLineColor ); + + if ( nD < 0 ) + nD += nDY2; + else + { + nD += nDYX; + + if ( bPos ) + nX++; + else + nX--; + } + } + } + } + } +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::FillRect( const Rectangle& rRect ) +{ + if( mpFillColor ) + { + const BitmapColor& rFillColor = *mpFillColor; + Point aPoint; + Rectangle aRect( aPoint, maBitmap.GetSizePixel() ); + + aRect.Intersection( rRect ); + + if( !aRect.IsEmpty() ) + { + const long nStartX = rRect.Left(); + const long nStartY = rRect.Top(); + const long nEndX = rRect.Right(); + const long nEndY = rRect.Bottom(); + + for( long nY = nStartY; nY <= nEndY; nY++ ) + for( long nX = nStartX; nX <= nEndX; nX++ ) + SetPixel( nY, nX, rFillColor ); + } + } +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::DrawRect( const Rectangle& rRect ) +{ + if( mpFillColor ) + FillRect( rRect ); + + if( mpLineColor && ( !mpFillColor || ( *mpFillColor != *mpLineColor ) ) ) + { + DrawLine( rRect.TopLeft(), rRect.TopRight() ); + DrawLine( rRect.TopRight(), rRect.BottomRight() ); + DrawLine( rRect.BottomRight(), rRect.BottomLeft() ); + DrawLine( rRect.BottomLeft(), rRect.TopLeft() ); + } +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::FillPolygon( const Polygon& rPoly ) +{ + const sal_uInt16 nSize = rPoly.GetSize(); + + if( nSize && mpFillColor ) + { + const BitmapColor& rFillColor = *mpFillColor; + Region aRegion( rPoly ); + Rectangle aRect; + + aRegion.Intersect( Rectangle( Point(), Size( Width(), Height() ) ) ); + + if( !aRegion.IsEmpty() ) + { + RegionHandle aRegHandle( aRegion.BeginEnumRects() ); + + while( aRegion.GetNextEnumRect( aRegHandle, aRect ) ) + for( long nY = aRect.Top(), nEndY = aRect.Bottom(); nY <= nEndY; nY++ ) + for( long nX = aRect.Left(), nEndX = aRect.Right(); nX <= nEndX; nX++ ) + SetPixel( nY, nX, rFillColor ); + + aRegion.EndEnumRects( aRegHandle ); + } + } +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::DrawPolygon( const Polygon& rPoly ) +{ + if( mpFillColor ) + FillPolygon( rPoly ); + + if( mpLineColor && ( !mpFillColor || ( *mpFillColor != *mpLineColor ) ) ) + { + const sal_uInt16 nSize = rPoly.GetSize(); + + for( sal_uInt16 i = 0, nSize1 = nSize - 1; i < nSize1; i++ ) + DrawLine( rPoly[ i ], rPoly[ i + 1 ] ); + + if( rPoly[ nSize - 1 ] != rPoly[ 0 ] ) + DrawLine( rPoly[ nSize - 1 ], rPoly[ 0 ] ); + } +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::FillPolyPolygon( const PolyPolygon& rPolyPoly ) +{ + const sal_uInt16 nCount = rPolyPoly.Count(); + + if( nCount && mpFillColor ) + { + const BitmapColor& rFillColor = *mpFillColor; + Region aRegion( rPolyPoly ); + Rectangle aRect; + + aRegion.Intersect( Rectangle( Point(), Size( Width(), Height() ) ) ); + + if( !aRegion.IsEmpty() ) + { + RegionHandle aRegHandle( aRegion.BeginEnumRects() ); + + while( aRegion.GetNextEnumRect( aRegHandle, aRect ) ) + for( long nY = aRect.Top(), nEndY = aRect.Bottom(); nY <= nEndY; nY++ ) + for( long nX = aRect.Left(), nEndX = aRect.Right(); nX <= nEndX; nX++ ) + SetPixel( nY, nX, rFillColor ); + + aRegion.EndEnumRects( aRegHandle ); + } + } +} + +// ------------------------------------------------------------------ + +void BitmapWriteAccess::DrawPolyPolygon( const PolyPolygon& rPolyPoly ) +{ + if( mpFillColor ) + FillPolyPolygon( rPolyPoly ); + + if( mpLineColor && ( !mpFillColor || ( *mpFillColor != *mpLineColor ) ) ) + { + for( sal_uInt16 n = 0, nCount = rPolyPoly.Count(); n < nCount; ) + { + const Polygon& rPoly = rPolyPoly[ n++ ]; + const sal_uInt16 nSize = rPoly.GetSize(); + + if( nSize ) + { + for( sal_uInt16 i = 0, nSize1 = nSize - 1; i < nSize1; i++ ) + DrawLine( rPoly[ i ], rPoly[ i + 1 ] ); + + if( rPoly[ nSize - 1 ] != rPoly[ 0 ] ) + DrawLine( rPoly[ nSize - 1 ], rPoly[ 0 ] ); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bmpconv.cxx b/vcl/source/gdi/bmpconv.cxx new file mode 100644 index 000000000000..886055817312 --- /dev/null +++ b/vcl/source/gdi/bmpconv.cxx @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "vcl/bitmap.hxx" +#include "vcl/svapp.hxx" +#include "vcl/salctype.hxx" +#include <osl/mutex.hxx> +#include "tools/stream.hxx" +#include "com/sun/star/script/XInvocation.hpp" +#include "com/sun/star/awt/XBitmap.hpp" +#include "cppuhelper/compbase1.hxx" + + +using namespace com::sun::star::uno; +using namespace com::sun::star::script; +using namespace com::sun::star::beans; +using namespace com::sun::star::reflection; +using namespace com::sun::star::awt; + +using ::rtl::OUString; + +namespace vcl { + +class BmpTransporter : + public cppu::WeakImplHelper1< com::sun::star::awt::XBitmap > +{ + Sequence<sal_Int8> m_aBM; + com::sun::star::awt::Size m_aSize; +public: + BmpTransporter( const Bitmap& rBM ); + virtual ~BmpTransporter(); + + virtual com::sun::star::awt::Size SAL_CALL getSize() throw(); + virtual Sequence< sal_Int8 > SAL_CALL getDIB() throw(); + virtual Sequence< sal_Int8 > SAL_CALL getMaskDIB() throw(); +}; + +class BmpConverter : + public cppu::WeakImplHelper1< com::sun::star::script::XInvocation > +{ +public: + BmpConverter(); + virtual ~BmpConverter(); + + virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection() throw(); + virtual void SAL_CALL setValue( const OUString& rProperty, const Any& rValue ) + throw( UnknownPropertyException ); + virtual Any SAL_CALL getValue( const OUString& rProperty ) + throw( UnknownPropertyException ); + virtual sal_Bool SAL_CALL hasMethod( const OUString& rName ) throw(); + virtual sal_Bool SAL_CALL hasProperty( const OUString& rProp ) throw(); + + virtual Any SAL_CALL invoke( const OUString& rFunction, + const Sequence< Any >& rParams, + Sequence< sal_Int16 >& rOutParamIndex, + Sequence< Any >& rOutParam + ) + throw( CannotConvertException, InvocationTargetException ); +}; + +} + +using namespace vcl; + +Reference< XInvocation > vcl::createBmpConverter() +{ + return static_cast<XInvocation*>(new BmpConverter()); +} + +BmpConverter::BmpConverter() +{ +} + +BmpConverter::~BmpConverter() +{ +} + +Reference< XIntrospectionAccess > SAL_CALL BmpConverter::getIntrospection() throw() +{ + return Reference< XIntrospectionAccess >(); +} + +void SAL_CALL BmpConverter::setValue( const OUString&, const Any& ) throw( UnknownPropertyException ) +{ + throw UnknownPropertyException(); +} + +Any SAL_CALL BmpConverter::getValue( const OUString& ) throw( UnknownPropertyException ) +{ + throw UnknownPropertyException(); +} + +sal_Bool SAL_CALL BmpConverter::hasMethod( const OUString& rName ) throw() +{ + return rName.equalsIgnoreAsciiCase( OUString(RTL_CONSTASCII_USTRINGPARAM("convert-bitmap-depth")) ); +} + +sal_Bool SAL_CALL BmpConverter::hasProperty( const OUString& ) throw() +{ + return sal_False; +} + +Any SAL_CALL BmpConverter::invoke( + const OUString& rFunction, + const Sequence< Any >& rParams, + Sequence< sal_Int16 >&, + Sequence< Any >& ) + throw( CannotConvertException, InvocationTargetException ) +{ + Any aRet; + + if( rFunction.equalsIgnoreAsciiCase( OUString(RTL_CONSTASCII_USTRINGPARAM("convert-bitmap-depth")) ) ) + { + Reference< XBitmap > xBM; + sal_uInt16 nTargetDepth = 0; + if( rParams.getLength() != 2 ) + throw CannotConvertException(); + + if( ! (rParams.getConstArray()[0] >>= xBM ) || + ! ( rParams.getConstArray()[1] >>= nTargetDepth ) ) + throw CannotConvertException(); + + Sequence< sal_Int8 > aDIB = xBM->getDIB(); + + // call into vcl not thread safe + SolarMutexGuard aGuard; + + SvMemoryStream aStream( aDIB.getArray(), aDIB.getLength(), STREAM_READ | STREAM_WRITE ); + Bitmap aBM; + aBM.Read( aStream, sal_True ); + if( nTargetDepth < 4 ) + nTargetDepth = 1; + else if( nTargetDepth < 8 ) + nTargetDepth = 4; + else if( nTargetDepth >8 && nTargetDepth < 24 ) + nTargetDepth = 24; + + if( aBM.GetBitCount() == 24 && nTargetDepth <= 8 ) + aBM.Dither( BMP_DITHER_FLOYD ); + + if( aBM.GetBitCount() != nTargetDepth ) + { + switch( nTargetDepth ) + { + case 1: aBM.Convert( BMP_CONVERSION_1BIT_THRESHOLD );break; + case 4: aBM.ReduceColors( BMP_CONVERSION_4BIT_COLORS );break; + case 8: aBM.ReduceColors( BMP_CONVERSION_8BIT_COLORS );break; + case 24: aBM.Convert( BMP_CONVERSION_24BIT );break; + } + } + xBM = new BmpTransporter( aBM ); + aRet <<= xBM; + } + else + throw InvocationTargetException(); + + return aRet; +} + +BmpTransporter::BmpTransporter( const Bitmap& rBM ) +{ + m_aSize.Width = rBM.GetSizePixel().Width(); + m_aSize.Height = rBM.GetSizePixel().Height(); + SvMemoryStream aStream; + rBM.Write( aStream, sal_False, sal_True ); + m_aBM = Sequence<sal_Int8>(static_cast<const sal_Int8*>(aStream.GetData()), + aStream.GetEndOfData()); +} + +BmpTransporter::~BmpTransporter() +{ +} + +com::sun::star::awt::Size SAL_CALL BmpTransporter::getSize() throw() +{ + return m_aSize; +} + +Sequence< sal_Int8 > SAL_CALL BmpTransporter::getDIB() throw() +{ + return m_aBM; +} + +Sequence< sal_Int8 > SAL_CALL BmpTransporter::getMaskDIB() throw() +{ + return Sequence< sal_Int8 >(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bmpfast.cxx b/vcl/source/gdi/bmpfast.cxx new file mode 100644 index 000000000000..8fc5188c9204 --- /dev/null +++ b/vcl/source/gdi/bmpfast.cxx @@ -0,0 +1,986 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <vcl/bmpfast.hxx> + +#ifndef NO_OPTIMIZED_BITMAP_ACCESS + +#include <tools/debug.hxx> +#include <vcl/bmpacc.hxx> + +#define FAST_ARGB_BGRA + +#include <stdlib.h> +static bool bDisableFastBitops = (getenv( "SAL_DISABLE_BITMAPS_OPTS" ) != NULL); + +typedef unsigned char PIXBYTE; + +class BasePixelPtr +{ +public: + BasePixelPtr( PIXBYTE* p = NULL ) : mpPixel( p ) {} + void SetRawPtr( PIXBYTE* pRawPtr ) { mpPixel = pRawPtr; } + PIXBYTE* GetRawPtr( void ) const { return mpPixel; } + void AddByteOffset( int nByteOffset ) { mpPixel += nByteOffset; } + bool operator<( const BasePixelPtr& rCmp ) const { return (mpPixel < rCmp.mpPixel); } + +protected: + PIXBYTE* mpPixel; +}; + +template <sal_uLong PIXFMT> +class TrueColorPixelPtr : public BasePixelPtr +{ +public: + PIXBYTE GetRed() const; + PIXBYTE GetGreen() const; + PIXBYTE GetBlue() const; + PIXBYTE GetAlpha() const; + + void SetColor( PIXBYTE r, PIXBYTE g, PIXBYTE b ) const; + void SetAlpha( PIXBYTE a ) const; + void operator++(int); +}; + +// ======================================================================= +// template specializations for truecolor pixel formats + +template <> +class TrueColorPixelPtr<BMP_FORMAT_24BIT_TC_RGB> : public BasePixelPtr +{ +public: + void operator++() { mpPixel += 3; } + + PIXBYTE GetRed() const { return mpPixel[0]; } + PIXBYTE GetGreen() const { return mpPixel[1]; } + PIXBYTE GetBlue() const { return mpPixel[2]; } + PIXBYTE GetAlpha() const { return 0; } + void SetAlpha( PIXBYTE ) const {} + + void SetColor( PIXBYTE r, PIXBYTE g, PIXBYTE b ) const + { + mpPixel[0] = r; + mpPixel[1] = g; + mpPixel[2] = b; + } +}; + +template <> +class TrueColorPixelPtr<BMP_FORMAT_24BIT_TC_BGR> : public BasePixelPtr +{ +public: + void operator++() { mpPixel += 3; } + + PIXBYTE GetRed() const { return mpPixel[2]; } + PIXBYTE GetGreen() const { return mpPixel[1]; } + PIXBYTE GetBlue() const { return mpPixel[0]; } + PIXBYTE GetAlpha() const { return 0; } + void SetAlpha( PIXBYTE ) const {} + + void SetColor( PIXBYTE r, PIXBYTE g, PIXBYTE b ) const + { + mpPixel[0] = b; + mpPixel[1] = g; + mpPixel[2] = r; + } +}; + +template <> +class TrueColorPixelPtr<BMP_FORMAT_32BIT_TC_ARGB> : public BasePixelPtr +{ +public: + void operator++() { mpPixel += 4; } + + PIXBYTE GetRed() const { return mpPixel[1]; } + PIXBYTE GetGreen() const { return mpPixel[2]; } + PIXBYTE GetBlue() const { return mpPixel[3]; } + PIXBYTE GetAlpha() const { return mpPixel[0]; } + void SetAlpha( PIXBYTE a ) const { mpPixel[0] = a; } + + void SetColor( PIXBYTE r, PIXBYTE g, PIXBYTE b ) const + { + mpPixel[1] = r; + mpPixel[2] = g; + mpPixel[3] = b; + } +}; + +template <> +class TrueColorPixelPtr<BMP_FORMAT_32BIT_TC_ABGR> : public BasePixelPtr +{ +public: + void operator++() { mpPixel += 4; } + + PIXBYTE GetRed() const { return mpPixel[3]; } + PIXBYTE GetGreen() const { return mpPixel[2]; } + PIXBYTE GetBlue() const { return mpPixel[1]; } + PIXBYTE GetAlpha() const { return mpPixel[0]; } + void SetAlpha( PIXBYTE a ) const { mpPixel[0] = a; } + + void SetColor( PIXBYTE r, PIXBYTE g, PIXBYTE b ) const + { + mpPixel[1] = b; + mpPixel[2] = g; + mpPixel[3] = r; + } +}; + +template <> +class TrueColorPixelPtr<BMP_FORMAT_32BIT_TC_RGBA> : public BasePixelPtr +{ +public: + void operator++() { mpPixel += 4; } + + PIXBYTE GetRed() const { return mpPixel[0]; } + PIXBYTE GetGreen() const { return mpPixel[1]; } + PIXBYTE GetBlue() const { return mpPixel[2]; } + PIXBYTE GetAlpha() const { return mpPixel[3]; } + void SetAlpha( PIXBYTE a ) const{ mpPixel[3] = a; } + + void SetColor( PIXBYTE r, PIXBYTE g, PIXBYTE b ) const + { + mpPixel[0] = r; + mpPixel[1] = g; + mpPixel[2] = b; + } +}; + +template <> +class TrueColorPixelPtr<BMP_FORMAT_32BIT_TC_BGRA> : public BasePixelPtr +{ +public: + void operator++() { mpPixel += 4; } + + PIXBYTE GetRed() const { return mpPixel[2]; } + PIXBYTE GetGreen() const { return mpPixel[1]; } + PIXBYTE GetBlue() const { return mpPixel[0]; } + PIXBYTE GetAlpha() const { return mpPixel[3]; } + void SetAlpha( PIXBYTE a ) const{ mpPixel[3] = a; } + + void SetColor( PIXBYTE r, PIXBYTE g, PIXBYTE b ) const + { + mpPixel[0] = b; + mpPixel[1] = g; + mpPixel[2] = r; + } +}; + +template <> +class TrueColorPixelPtr<BMP_FORMAT_16BIT_TC_MSB_MASK> : public BasePixelPtr +{ +public: + void operator++() { mpPixel += 2; } + + // TODO: non565-RGB + PIXBYTE GetRed() const { return (mpPixel[0] & 0xF8U); } + PIXBYTE GetGreen() const { return (mpPixel[0]<<5U) | ((mpPixel[1]>>3U)&28U); } + PIXBYTE GetBlue() const { return (mpPixel[1]<<3U); } + PIXBYTE GetAlpha() const { return 0; } + void SetAlpha( PIXBYTE ) const {} + + void SetColor( PIXBYTE r, PIXBYTE g, PIXBYTE b ) const + { + mpPixel[0] = ((g >> 5U) & 7U) | (r & 0xF8U); + mpPixel[1] = ((g & 28U)<< 3U) | (b >> 3U); + } +}; + +template <> +class TrueColorPixelPtr<BMP_FORMAT_16BIT_TC_LSB_MASK> : public BasePixelPtr +{ +public: + void operator++() { mpPixel += 2; } + + // TODO: non565-RGB + PIXBYTE GetRed() const { return (mpPixel[1] & 0xF8U); } + PIXBYTE GetGreen() const { return (mpPixel[1]<<5U) | ((mpPixel[0]>>3U)&28U); } + PIXBYTE GetBlue() const { return (mpPixel[0]<<3U); } + PIXBYTE GetAlpha() const { return 0; } + void SetAlpha( PIXBYTE ) const {} + + void SetColor( PIXBYTE r, PIXBYTE g, PIXBYTE b ) const + { + mpPixel[0] = ((g & 28U)<< 3U) | (b >> 3U); + mpPixel[1] = ((g >> 5U) & 7U) | (r & 0xF8U); + } +}; + +// ----------------------------------------------------------------------- + +template <> +class TrueColorPixelPtr<BMP_FORMAT_8BIT_TC_MASK> : public BasePixelPtr +{ +public: + void operator++() { mpPixel += 1; } + PIXBYTE GetAlpha() const { return mpPixel[0]; } + void SetAlpha( PIXBYTE a ) const { mpPixel[0] = a; } + void SetColor( PIXBYTE, PIXBYTE, PIXBYTE ) const {} +}; + +// TODO: for some reason many Alpha maps are BMP_FORMAT_8BIT_PAL +// they should be BMP_FORMAT_8BIT_TC_MASK +template <> +class TrueColorPixelPtr<BMP_FORMAT_8BIT_PAL> +: public TrueColorPixelPtr<BMP_FORMAT_8BIT_TC_MASK> +{}; + +// ======================================================================= +// converting truecolor formats + +template <sal_uLong SRCFMT, sal_uLong DSTFMT> +inline void ImplConvertPixel( const TrueColorPixelPtr<DSTFMT>& rDst, + const TrueColorPixelPtr<SRCFMT>& rSrc ) +{ + rDst.SetColor( rSrc.GetRed(), rSrc.GetGreen(), rSrc.GetBlue() ); + rDst.SetAlpha( rSrc.GetAlpha() ); +} + +// ----------------------------------------------------------------------- + +template <> +inline void ImplConvertPixel<BMP_FORMAT_16BIT_TC_LSB_MASK, BMP_FORMAT_16BIT_TC_MSB_MASK> ( + const TrueColorPixelPtr<BMP_FORMAT_16BIT_TC_MSB_MASK>& rDst, + const TrueColorPixelPtr<BMP_FORMAT_16BIT_TC_LSB_MASK>& rSrc ) +{ + // byte swapping + const PIXBYTE* pSrc = rSrc.GetRawPtr(); + PIXBYTE* pDst = rDst.GetRawPtr(); + pDst[1] = pSrc[0]; + pDst[0] = pSrc[1]; +} + +// ----------------------------------------------------------------------- + +template <sal_uLong SRCFMT, sal_uLong DSTFMT> +inline void ImplConvertLine( const TrueColorPixelPtr<DSTFMT>& rDst, + const TrueColorPixelPtr<SRCFMT>& rSrc, int nPixelCount ) +{ + TrueColorPixelPtr<DSTFMT> aDst( rDst ); + TrueColorPixelPtr<SRCFMT> aSrc( rSrc ); + while( --nPixelCount >= 0 ) + { + ImplConvertPixel( aDst, aSrc ); + ++aSrc; + ++aDst; + } +} + +// ======================================================================= +// alpha blending truecolor pixels + +template <unsigned ALPHABITS, sal_uLong SRCFMT, sal_uLong DSTFMT> +inline void ImplBlendPixels( const TrueColorPixelPtr<DSTFMT>& rDst, + const TrueColorPixelPtr<SRCFMT>& rSrc, unsigned nAlphaVal ) +{ + if( !nAlphaVal ) + ImplConvertPixel( rDst, rSrc ); + else if( nAlphaVal != ~(~0 << ALPHABITS) ) + { + static const unsigned nAlphaShift = (ALPHABITS > 8) ? 8 : ALPHABITS; + if( ALPHABITS > nAlphaShift ) + nAlphaVal >>= ALPHABITS - nAlphaShift; + + int nR = rDst.GetRed(); + int nS = rSrc.GetRed(); + nR = nS + (((nR - nS) * nAlphaVal) >> nAlphaShift); + + int nG = rDst.GetGreen(); + nS = rSrc.GetGreen(); + nG = nS + (((nG - nS) * nAlphaVal) >> nAlphaShift); + + int nB = rDst.GetBlue(); + nS = rSrc.GetBlue(); + nB = nS + (((nB - nS) * nAlphaVal) >> nAlphaShift); + + rDst.SetColor( sal::static_int_cast<PIXBYTE>(nR), + sal::static_int_cast<PIXBYTE>(nG), + sal::static_int_cast<PIXBYTE>(nB) ); + } +} + +// ----------------------------------------------------------------------- + +template <unsigned ALPHABITS, sal_uLong MASKFMT, sal_uLong SRCFMT, sal_uLong DSTFMT> +inline void ImplBlendLines( const TrueColorPixelPtr<DSTFMT>& rDst, + const TrueColorPixelPtr<SRCFMT>& rSrc, const TrueColorPixelPtr<MASKFMT>& rMsk, + int nPixelCount ) +{ + TrueColorPixelPtr<MASKFMT> aMsk( rMsk ); + TrueColorPixelPtr<DSTFMT> aDst( rDst ); + TrueColorPixelPtr<SRCFMT> aSrc( rSrc ); + while( --nPixelCount >= 0 ) + { + ImplBlendPixels<ALPHABITS>( aDst, aSrc, aMsk.GetAlpha() ); + ++aDst; + ++aSrc; + ++aMsk; + } +} + +// ----------------------------------------------------------------------- + +template <unsigned ALPHABITS, sal_uLong SRCFMT, sal_uLong DSTFMT> +inline void ImplBlendLines( const TrueColorPixelPtr<DSTFMT>& rDst, + const TrueColorPixelPtr<SRCFMT>& rSrc, unsigned nAlphaVal, + int nPixelCount ) +{ + if( nAlphaVal == ~(~0 << ALPHABITS) ) + ImplConvertLine( rDst, rSrc, nPixelCount ); + else if( nAlphaVal ) + { + TrueColorPixelPtr<SRCFMT> aSrc( rSrc ); + TrueColorPixelPtr<DSTFMT> aDst( rDst ); + while( --nPixelCount >= 0 ) + { + ImplBlendPixels<ALPHABITS>( aDst, aSrc, nAlphaVal ); + ++aDst; + ++aSrc; + } + } +} + +// ======================================================================= + +static bool ImplCopyImage( BitmapBuffer& rDstBuffer, const BitmapBuffer& rSrcBuffer ) +{ + const int nSrcLinestep = rSrcBuffer.mnScanlineSize; + int nDstLinestep = rDstBuffer.mnScanlineSize; + + const PIXBYTE* pRawSrc = rSrcBuffer.mpBits; + PIXBYTE* pRawDst = rDstBuffer.mpBits; + + // source and destination don't match upside down + if( BMP_FORMAT_TOP_DOWN & (rSrcBuffer.mnFormat ^ rDstBuffer.mnFormat) ) + { + pRawDst += (rSrcBuffer.mnHeight - 1) * nDstLinestep; + nDstLinestep = -rDstBuffer.mnScanlineSize; + } + else if( nSrcLinestep == nDstLinestep ) + { + memcpy( pRawDst, pRawSrc, rSrcBuffer.mnHeight * nDstLinestep ); + return true; + } + + int nByteWidth = nSrcLinestep; + if( nByteWidth > rDstBuffer.mnScanlineSize ) + nByteWidth = rDstBuffer.mnScanlineSize; + + for( int y = rSrcBuffer.mnHeight; --y >= 0; ) + { + memcpy( pRawDst, pRawSrc, nByteWidth ); + pRawSrc += nSrcLinestep; + pRawDst += nDstLinestep; + } + + return true; +} + +// ----------------------------------------------------------------------- + +template <sal_uLong DSTFMT,sal_uLong SRCFMT> +bool ImplConvertToBitmap( TrueColorPixelPtr<SRCFMT>& rSrcLine, + BitmapBuffer& rDstBuffer, const BitmapBuffer& rSrcBuffer ) +{ + // help the compiler to avoid instantiations of unneeded conversions + DBG_ASSERT( SRCFMT != DSTFMT, "ImplConvertToBitmap into same format"); + if( SRCFMT == DSTFMT ) + return false; + + const int nSrcLinestep = rSrcBuffer.mnScanlineSize; + int nDstLinestep = rDstBuffer.mnScanlineSize; + + TrueColorPixelPtr<DSTFMT> aDstLine; aDstLine.SetRawPtr( rDstBuffer.mpBits ); + + // source and destination don't match upside down + if( BMP_FORMAT_TOP_DOWN & (rSrcBuffer.mnFormat ^ rDstBuffer.mnFormat) ) + { + aDstLine.AddByteOffset( (rSrcBuffer.mnHeight - 1) * nDstLinestep ); + nDstLinestep = -nDstLinestep; + } + + for( int y = rSrcBuffer.mnHeight; --y >= 0; ) + { + ImplConvertLine( aDstLine, rSrcLine, rSrcBuffer.mnWidth ); + rSrcLine.AddByteOffset( nSrcLinestep ); + aDstLine.AddByteOffset( nDstLinestep ); + } + + return true; +} + +// ----------------------------------------------------------------------- + +template <sal_uLong SRCFMT> +inline bool ImplConvertFromBitmap( BitmapBuffer& rDst, const BitmapBuffer& rSrc ) +{ + TrueColorPixelPtr<SRCFMT> aSrcType; aSrcType.SetRawPtr( rSrc.mpBits ); + + // select the matching instantiation for the destination's bitmap format + switch( rDst.mnFormat & ~BMP_FORMAT_TOP_DOWN ) + { + case BMP_FORMAT_1BIT_MSB_PAL: + case BMP_FORMAT_1BIT_LSB_PAL: + case BMP_FORMAT_4BIT_MSN_PAL: + case BMP_FORMAT_4BIT_LSN_PAL: + case BMP_FORMAT_8BIT_PAL: + break; + + case BMP_FORMAT_8BIT_TC_MASK: +// return ImplConvertToBitmap<BMP_FORMAT_8BIT_TC_MASK>( aSrcType, rDst, rSrc ); + case BMP_FORMAT_24BIT_TC_MASK: +// return ImplConvertToBitmap<BMP_FORMAT_24BIT_TC_MASK>( aSrcType, rDst, rSrc ); + case BMP_FORMAT_32BIT_TC_MASK: +// return ImplConvertToBitmap<BMP_FORMAT_32BIT_TC_MASK>( aSrcType, rDst, rSrc ); + break; + + case BMP_FORMAT_16BIT_TC_MSB_MASK: + return ImplConvertToBitmap<BMP_FORMAT_16BIT_TC_MSB_MASK>( aSrcType, rDst, rSrc ); + case BMP_FORMAT_16BIT_TC_LSB_MASK: + return ImplConvertToBitmap<BMP_FORMAT_16BIT_TC_LSB_MASK>( aSrcType, rDst, rSrc ); + + case BMP_FORMAT_24BIT_TC_BGR: + return ImplConvertToBitmap<BMP_FORMAT_24BIT_TC_BGR>( aSrcType, rDst, rSrc ); + case BMP_FORMAT_24BIT_TC_RGB: + return ImplConvertToBitmap<BMP_FORMAT_24BIT_TC_RGB>( aSrcType, rDst, rSrc ); + + case BMP_FORMAT_32BIT_TC_ABGR: + return ImplConvertToBitmap<BMP_FORMAT_32BIT_TC_ABGR>( aSrcType, rDst, rSrc ); +#ifdef FAST_ARGB_BGRA + case BMP_FORMAT_32BIT_TC_ARGB: + return ImplConvertToBitmap<BMP_FORMAT_32BIT_TC_ARGB>( aSrcType, rDst, rSrc ); + case BMP_FORMAT_32BIT_TC_BGRA: + return ImplConvertToBitmap<BMP_FORMAT_32BIT_TC_BGRA>( aSrcType, rDst, rSrc ); +#endif + case BMP_FORMAT_32BIT_TC_RGBA: + return ImplConvertToBitmap<BMP_FORMAT_32BIT_TC_RGBA>( aSrcType, rDst, rSrc ); + } + +#ifdef DEBUG + static int nNotAccelerated = 0; + if( rSrc.mnWidth * rSrc.mnHeight >= 4000 ) + if( ++nNotAccelerated == 100 ) + { + int foo = 0; (void)foo; // so no warning is created when building on pro with debug + DBG_WARNING2( "ImplConvertFromBitmap for not accelerated case (0x%04X->0x%04X)", + rSrc.mnFormat, rDst.mnFormat ); + } +#endif + + return false; +} + +// ======================================================================= + +// an universal stretching conversion is overkill in most common situations +// => performance benefits for speeding up the non-stretching cases +bool ImplFastBitmapConversion( BitmapBuffer& rDst, const BitmapBuffer& rSrc, + const SalTwoRect& rTR ) +{ + if( bDisableFastBitops ) + return false; + + // horizontal mirroring not implemented yet + if( rTR.mnDestWidth < 0 ) + return false; + // vertical mirroring + if( rTR.mnDestHeight < 0 ) + // TODO: rDst.mnFormat ^= BMP_FORMAT_TOP_DOWN; + return false; + + // offseted conversion is not implemented yet + if( rTR.mnSrcX || rTR.mnSrcY ) + return false; + if( rTR.mnDestX || rTR.mnDestY ) + return false; + + // stretched conversion is not implemented yet + if( rTR.mnDestWidth != rTR.mnSrcWidth ) + return false; + if( rTR.mnDestHeight!= rTR.mnSrcHeight ) + return false; + + // check source image size + if( rSrc.mnWidth < rTR.mnSrcX + rTR.mnSrcWidth ) + return false; + if( rSrc.mnHeight < rTR.mnSrcY + rTR.mnSrcHeight ) + return false; + + // check dest image size + if( rDst.mnWidth < rTR.mnDestX + rTR.mnDestWidth ) + return false; + if( rDst.mnHeight < rTR.mnDestY + rTR.mnDestHeight ) + return false; + + const sal_uLong nSrcFormat = rSrc.mnFormat & ~BMP_FORMAT_TOP_DOWN; + const sal_uLong nDstFormat = rDst.mnFormat & ~BMP_FORMAT_TOP_DOWN; + + // TODO: also implement conversions for 16bit colormasks with non-565 format + if( nSrcFormat & (BMP_FORMAT_16BIT_TC_LSB_MASK | BMP_FORMAT_16BIT_TC_MSB_MASK) ) + if( rSrc.maColorMask.GetRedMask() != 0xF800 + || rSrc.maColorMask.GetGreenMask()!= 0x07E0 + || rSrc.maColorMask.GetBlueMask() != 0x001F ) + return false; + if( nDstFormat & (BMP_FORMAT_16BIT_TC_LSB_MASK | BMP_FORMAT_16BIT_TC_MSB_MASK) ) + if( rDst.maColorMask.GetRedMask() != 0xF800 + || rDst.maColorMask.GetGreenMask()!= 0x07E0 + || rDst.maColorMask.GetBlueMask() != 0x001F ) + return false; + + // special handling of trivial cases + if( nSrcFormat == nDstFormat ) + { + // accelerated palette conversions not yet implemented + if( rSrc.maPalette != rDst.maPalette ) + return false; + return ImplCopyImage( rDst, rSrc ); + } + + // select the matching instantiation for the source's bitmap format + switch( nSrcFormat ) + { + case BMP_FORMAT_1BIT_MSB_PAL: + case BMP_FORMAT_1BIT_LSB_PAL: + case BMP_FORMAT_4BIT_MSN_PAL: + case BMP_FORMAT_4BIT_LSN_PAL: + case BMP_FORMAT_8BIT_PAL: + break; + + case BMP_FORMAT_8BIT_TC_MASK: +// return ImplConvertFromBitmap<BMP_FORMAT_8BIT_TC_MASK>( rDst, rSrc ); + case BMP_FORMAT_24BIT_TC_MASK: +// return ImplConvertFromBitmap<BMP_FORMAT_24BIT_TC_MASK>( rDst, rSrc ); + case BMP_FORMAT_32BIT_TC_MASK: +// return ImplConvertFromBitmap<BMP_FORMAT_32BIT_TC_MASK>( rDst, rSrc ); + break; + + case BMP_FORMAT_16BIT_TC_MSB_MASK: + return ImplConvertFromBitmap<BMP_FORMAT_16BIT_TC_MSB_MASK>( rDst, rSrc ); + case BMP_FORMAT_16BIT_TC_LSB_MASK: + return ImplConvertFromBitmap<BMP_FORMAT_16BIT_TC_LSB_MASK>( rDst, rSrc ); + + case BMP_FORMAT_24BIT_TC_BGR: + return ImplConvertFromBitmap<BMP_FORMAT_24BIT_TC_BGR>( rDst, rSrc ); + case BMP_FORMAT_24BIT_TC_RGB: + return ImplConvertFromBitmap<BMP_FORMAT_24BIT_TC_RGB>( rDst, rSrc ); + + case BMP_FORMAT_32BIT_TC_ABGR: + return ImplConvertFromBitmap<BMP_FORMAT_32BIT_TC_ABGR>( rDst, rSrc ); +#ifdef FAST_ARGB_BGRA + case BMP_FORMAT_32BIT_TC_ARGB: + return ImplConvertFromBitmap<BMP_FORMAT_32BIT_TC_ARGB>( rDst, rSrc ); + case BMP_FORMAT_32BIT_TC_BGRA: + return ImplConvertFromBitmap<BMP_FORMAT_32BIT_TC_BGRA>( rDst, rSrc ); +#endif + case BMP_FORMAT_32BIT_TC_RGBA: + return ImplConvertFromBitmap<BMP_FORMAT_32BIT_TC_RGBA>( rDst, rSrc ); + } + +#ifdef DEBUG + static int nNotAccelerated = 0; + if( rSrc.mnWidth * rSrc.mnHeight >= 4000 ) + { + if( ++nNotAccelerated == 100 ) + { + int foo = 0; (void)foo; // so no warning is created when building on pro with debug + DBG_WARNING2( "ImplFastBitmapConversion for not accelerated case (0x%04X->0x%04X)", rSrc.mnFormat, rDst.mnFormat ); + } + } +#endif + + return false; +} + +// ======================================================================= + +template <sal_uLong DSTFMT,sal_uLong SRCFMT> //,sal_uLong MSKFMT> +bool ImplBlendToBitmap( TrueColorPixelPtr<SRCFMT>& rSrcLine, + BitmapBuffer& rDstBuffer, const BitmapBuffer& rSrcBuffer, + const BitmapBuffer& rMskBuffer ) +{ + //DBG_ASSERT( rMskBuffer.mnFormat == MSKFMT, "FastBmp BlendImage: wrong MSKFMT" ); + DBG_ASSERT( rMskBuffer.mnFormat == BMP_FORMAT_8BIT_PAL, "FastBmp BlendImage: unusual MSKFMT" ); + + const int nSrcLinestep = rSrcBuffer.mnScanlineSize; + int nMskLinestep = rMskBuffer.mnScanlineSize; + int nDstLinestep = rDstBuffer.mnScanlineSize; + + TrueColorPixelPtr<BMP_FORMAT_8BIT_PAL> aMskLine; aMskLine.SetRawPtr( rMskBuffer.mpBits ); + TrueColorPixelPtr<DSTFMT> aDstLine; aDstLine.SetRawPtr( rDstBuffer.mpBits ); + + // special case for single line masks + if( rMskBuffer.mnHeight == 1 ) + nMskLinestep = 0; + + // source and mask don't match: upside down + if( (rSrcBuffer.mnFormat ^ rMskBuffer.mnFormat) & BMP_FORMAT_TOP_DOWN ) + { + aMskLine.AddByteOffset( (rSrcBuffer.mnHeight - 1) * nMskLinestep ); + nMskLinestep = -nMskLinestep; + } + + // source and destination don't match: upside down + if( (rSrcBuffer.mnFormat ^ rDstBuffer.mnFormat) & BMP_FORMAT_TOP_DOWN ) + { + aDstLine.AddByteOffset( (rSrcBuffer.mnHeight - 1) * nDstLinestep ); + nDstLinestep = -nDstLinestep; + } + + for( int y = rSrcBuffer.mnHeight; --y >= 0; ) + { + ImplBlendLines<8>( aDstLine, rSrcLine, aMskLine, rDstBuffer.mnWidth ); + aDstLine.AddByteOffset( nDstLinestep ); + rSrcLine.AddByteOffset( nSrcLinestep ); + aMskLine.AddByteOffset( nMskLinestep ); + } + + return true; +} + +// some specializations to reduce the code size +template <> +inline bool ImplBlendToBitmap<BMP_FORMAT_24BIT_TC_BGR,BMP_FORMAT_24BIT_TC_BGR>( + TrueColorPixelPtr<BMP_FORMAT_24BIT_TC_BGR>&, + BitmapBuffer& rDstBuffer, const BitmapBuffer& rSrcBuffer, + const BitmapBuffer& rMskBuffer ) + { + TrueColorPixelPtr<BMP_FORMAT_24BIT_TC_RGB> aSrcType; aSrcType.SetRawPtr( rSrcBuffer.mpBits ); + return ImplBlendToBitmap<BMP_FORMAT_24BIT_TC_RGB>( aSrcType, rDstBuffer, rSrcBuffer, rMskBuffer ); + } + +template <> +inline bool ImplBlendToBitmap<BMP_FORMAT_32BIT_TC_ABGR,BMP_FORMAT_32BIT_TC_ABGR>( + TrueColorPixelPtr<BMP_FORMAT_32BIT_TC_ABGR>&, + BitmapBuffer& rDstBuffer, const BitmapBuffer& rSrcBuffer, + const BitmapBuffer& rMskBuffer ) + { + TrueColorPixelPtr<BMP_FORMAT_32BIT_TC_ARGB> aSrcType; aSrcType.SetRawPtr( rSrcBuffer.mpBits ); + return ImplBlendToBitmap<BMP_FORMAT_32BIT_TC_ARGB>( aSrcType, rDstBuffer, rSrcBuffer, rMskBuffer ); + } + +template <> +inline bool ImplBlendToBitmap<BMP_FORMAT_32BIT_TC_BGRA,BMP_FORMAT_32BIT_TC_BGRA>( + TrueColorPixelPtr<BMP_FORMAT_32BIT_TC_BGRA>&, + BitmapBuffer& rDstBuffer, const BitmapBuffer& rSrcBuffer, + const BitmapBuffer& rMskBuffer ) + { + TrueColorPixelPtr<BMP_FORMAT_32BIT_TC_RGBA> aSrcType; aSrcType.SetRawPtr( rSrcBuffer.mpBits ); + return ImplBlendToBitmap<BMP_FORMAT_32BIT_TC_RGBA>( aSrcType, rDstBuffer, rSrcBuffer, rMskBuffer ); + } + +// ----------------------------------------------------------------------- + +template <sal_uLong SRCFMT> +bool ImplBlendFromBitmap( BitmapBuffer& rDst, const BitmapBuffer& rSrc, const BitmapBuffer& rMsk ) +{ + TrueColorPixelPtr<SRCFMT> aSrcType; aSrcType.SetRawPtr( rSrc.mpBits ); + + // select the matching instantiation for the destination's bitmap format + switch( rDst.mnFormat & ~BMP_FORMAT_TOP_DOWN ) + { + case BMP_FORMAT_1BIT_MSB_PAL: + case BMP_FORMAT_1BIT_LSB_PAL: + case BMP_FORMAT_4BIT_MSN_PAL: + case BMP_FORMAT_4BIT_LSN_PAL: + case BMP_FORMAT_8BIT_PAL: + break; + + case BMP_FORMAT_8BIT_TC_MASK: +// return ImplBlendToBitmap<BMP_FORMAT_8BIT_TC_MASK>( aSrcType, rDst, rSrc, rMsk ); + case BMP_FORMAT_24BIT_TC_MASK: +// return ImplBlendToBitmap<BMP_FORMAT_24BIT_TC_MASK>( aSrcType, rDst, rSrc, rMsk ); + case BMP_FORMAT_32BIT_TC_MASK: +// return ImplBlendToBitmap<BMP_FORMAT_32BIT_TC_MASK>( aSrcType, rDst, rSrc, rMsk ); + break; + + case BMP_FORMAT_16BIT_TC_MSB_MASK: + return ImplBlendToBitmap<BMP_FORMAT_16BIT_TC_MSB_MASK>( aSrcType, rDst, rSrc, rMsk ); + case BMP_FORMAT_16BIT_TC_LSB_MASK: + return ImplBlendToBitmap<BMP_FORMAT_16BIT_TC_LSB_MASK>( aSrcType, rDst, rSrc, rMsk ); + + case BMP_FORMAT_24BIT_TC_BGR: + return ImplBlendToBitmap<BMP_FORMAT_24BIT_TC_BGR>( aSrcType, rDst, rSrc, rMsk ); + case BMP_FORMAT_24BIT_TC_RGB: + return ImplBlendToBitmap<BMP_FORMAT_24BIT_TC_RGB>( aSrcType, rDst, rSrc, rMsk ); + + case BMP_FORMAT_32BIT_TC_ABGR: + return ImplBlendToBitmap<BMP_FORMAT_32BIT_TC_ABGR>( aSrcType, rDst, rSrc, rMsk ); +#ifdef FAST_ARGB_BGRA + case BMP_FORMAT_32BIT_TC_ARGB: + return ImplBlendToBitmap<BMP_FORMAT_32BIT_TC_ARGB>( aSrcType, rDst, rSrc, rMsk ); + case BMP_FORMAT_32BIT_TC_BGRA: + return ImplBlendToBitmap<BMP_FORMAT_32BIT_TC_BGRA>( aSrcType, rDst, rSrc, rMsk ); +#endif + case BMP_FORMAT_32BIT_TC_RGBA: + return ImplBlendToBitmap<BMP_FORMAT_32BIT_TC_RGBA>( aSrcType, rDst, rSrc, rMsk ); + } + +#ifdef DEBUG + static int nNotAccelerated = 0; + if( rSrc.mnWidth * rSrc.mnHeight >= 4000 ) + if( ++nNotAccelerated == 100 ) + { + int foo = 0; (void)foo; // so no warning is created when building on pro with debug + DBG_WARNING3( "ImplBlendFromBitmap for not accelerated case (0x%04X*0x%04X->0x%04X)", + rSrc.mnFormat, rMsk.mnFormat, rDst.mnFormat ); + } +#endif + + return false; +} + +// ----------------------------------------------------------------------- + +bool ImplFastBitmapBlending( BitmapWriteAccess& rDstWA, + const BitmapReadAccess& rSrcRA, const BitmapReadAccess& rMskRA, + const SalTwoRect& rTR ) +{ + if( bDisableFastBitops ) + return false; + + // accelerated blending of paletted bitmaps not implemented yet + if( rSrcRA.HasPalette() ) + return false; + if( rDstWA.HasPalette() ) + return false; + // TODO: either get rid of mask's use of 8BIT_PAL or check the palette + + // horizontal mirroring not implemented yet + if( rTR.mnDestWidth < 0 ) + return false; + // vertical mirroring + if( rTR.mnDestHeight < 0 ) + // TODO: rDst.mnFormat ^= BMP_FORMAT_TOP_DOWN; + return false; + + // offseted blending is not implemented yet + if( rTR.mnSrcX || rTR.mnSrcY ) + return false; + if( rTR.mnDestX || rTR.mnDestY ) + return false; + + // stretched blending is not implemented yet + if( rTR.mnDestWidth != rTR.mnSrcWidth ) + return false; + if( rTR.mnDestHeight!= rTR.mnSrcHeight ) + return false; + + // check source image size + if( rSrcRA.Width() < rTR.mnSrcX + rTR.mnSrcWidth ) + return false; + if( rSrcRA.Height() < rTR.mnSrcY + rTR.mnSrcHeight ) + return false; + + // check mask image size + if( rMskRA.Width() < rTR.mnSrcX + rTR.mnSrcWidth ) + return false; + if( rMskRA.Height() < rTR.mnSrcY + rTR.mnSrcHeight ) + if( rMskRA.Height() != 1 ) + return false; + + // check dest image size + if( rDstWA.Width() < rTR.mnDestX + rTR.mnDestWidth ) + return false; + if( rDstWA.Height() < rTR.mnDestY + rTR.mnDestHeight ) + return false; + + BitmapBuffer& rDst = *rDstWA.ImplGetBitmapBuffer(); + const BitmapBuffer& rSrc = *rSrcRA.ImplGetBitmapBuffer(); + const BitmapBuffer& rMsk = *rMskRA.ImplGetBitmapBuffer(); + + const sal_uLong nSrcFormat = rSrc.mnFormat & ~BMP_FORMAT_TOP_DOWN; + const sal_uLong nDstFormat = rDst.mnFormat & ~BMP_FORMAT_TOP_DOWN; + + // accelerated conversions for 16bit colormasks with non-565 format are not yet implemented + if( nSrcFormat & (BMP_FORMAT_16BIT_TC_LSB_MASK | BMP_FORMAT_16BIT_TC_MSB_MASK) ) + if( rSrc.maColorMask.GetRedMask() != 0xF800 + || rSrc.maColorMask.GetGreenMask()!= 0x07E0 + || rSrc.maColorMask.GetBlueMask() != 0x001F) + return false; + if( nDstFormat & (BMP_FORMAT_16BIT_TC_LSB_MASK | BMP_FORMAT_16BIT_TC_MSB_MASK) ) + if( rDst.maColorMask.GetRedMask() != 0xF800 + || rDst.maColorMask.GetGreenMask()!= 0x07E0 + || rDst.maColorMask.GetBlueMask() != 0x001F) + return false; + + // select the matching instantiation for the source's bitmap format + switch( nSrcFormat ) + { + case BMP_FORMAT_1BIT_MSB_PAL: + case BMP_FORMAT_1BIT_LSB_PAL: + case BMP_FORMAT_4BIT_MSN_PAL: + case BMP_FORMAT_4BIT_LSN_PAL: + case BMP_FORMAT_8BIT_PAL: + break; + + case BMP_FORMAT_8BIT_TC_MASK: +// return ImplBlendFromBitmap<BMP_FORMAT_8BIT_TC_MASK>( rDst, rSrc ); + case BMP_FORMAT_24BIT_TC_MASK: +// return ImplBlendFromBitmap<BMP_FORMAT_24BIT_TC_MASK>( rDst, rSrc ); + case BMP_FORMAT_32BIT_TC_MASK: +// return ImplBlendFromBitmap<BMP_FORMAT_32BIT_TC_MASK>( rDst, rSrc ); + break; + + case BMP_FORMAT_16BIT_TC_MSB_MASK: + return ImplBlendFromBitmap<BMP_FORMAT_16BIT_TC_MSB_MASK>( rDst, rSrc, rMsk ); + case BMP_FORMAT_16BIT_TC_LSB_MASK: + return ImplBlendFromBitmap<BMP_FORMAT_16BIT_TC_LSB_MASK>( rDst, rSrc, rMsk ); + + case BMP_FORMAT_24BIT_TC_BGR: + return ImplBlendFromBitmap<BMP_FORMAT_24BIT_TC_BGR>( rDst, rSrc, rMsk ); + case BMP_FORMAT_24BIT_TC_RGB: + return ImplBlendFromBitmap<BMP_FORMAT_24BIT_TC_RGB>( rDst, rSrc, rMsk ); + + case BMP_FORMAT_32BIT_TC_ABGR: + return ImplBlendFromBitmap<BMP_FORMAT_32BIT_TC_ABGR>( rDst, rSrc, rMsk ); +#ifdef FAST_ARGB_BGRA + case BMP_FORMAT_32BIT_TC_ARGB: + return ImplBlendFromBitmap<BMP_FORMAT_32BIT_TC_ARGB>( rDst, rSrc, rMsk ); + case BMP_FORMAT_32BIT_TC_BGRA: + return ImplBlendFromBitmap<BMP_FORMAT_32BIT_TC_BGRA>( rDst, rSrc, rMsk ); +#endif + case BMP_FORMAT_32BIT_TC_RGBA: + return ImplBlendFromBitmap<BMP_FORMAT_32BIT_TC_RGBA>( rDst, rSrc, rMsk ); + } + +#ifdef DEBUG + static int nNotAccelerated = 0; + if( rSrc.mnWidth * rSrc.mnHeight >= 4000 ) + if( ++nNotAccelerated == 100 ) + { + int foo = 0; (void)foo; // so no warning is created when building on pro with debug + DBG_WARNING3( "ImplFastBlend for not accelerated case (0x%04X*0x%04X->0x%04X)", + rSrc.mnFormat, rMsk.mnFormat, rDst.mnFormat ); + } +#endif + + return false; +} + +bool ImplFastEraseBitmap( BitmapBuffer& rDst, const BitmapColor& rColor ) +{ + if( bDisableFastBitops ) + return false; + + const sal_uLong nDstFormat = rDst.mnFormat & ~BMP_FORMAT_TOP_DOWN; + + // erasing a bitmap is often just a byte-wise memory fill + bool bByteFill = true; + sal_uInt8 nFillByte; + + switch( nDstFormat ) + { + case BMP_FORMAT_1BIT_MSB_PAL: + case BMP_FORMAT_1BIT_LSB_PAL: + nFillByte = rColor.GetIndex(); + nFillByte = static_cast<sal_uInt8>( -(nFillByte & 1) ); // 0x00 or 0xFF + break; + case BMP_FORMAT_4BIT_MSN_PAL: + case BMP_FORMAT_4BIT_LSN_PAL: + nFillByte = rColor.GetIndex(); + nFillByte &= 0x0F; + nFillByte |= (nFillByte << 4); + break; + case BMP_FORMAT_8BIT_PAL: + case BMP_FORMAT_8BIT_TC_MASK: + nFillByte = rColor.GetIndex(); + break; + + case BMP_FORMAT_24BIT_TC_MASK: + case BMP_FORMAT_24BIT_TC_BGR: + case BMP_FORMAT_24BIT_TC_RGB: + nFillByte = rColor.GetRed(); + if( (nFillByte != rColor.GetGreen()) + || (nFillByte != rColor.GetBlue()) ) + bByteFill = false; + break; + + default: + bByteFill = false; + nFillByte = 0x00; + break; + } + + if( bByteFill ) + { + long nByteCount = rDst.mnHeight * rDst.mnScanlineSize; + rtl_fillMemory( rDst.mpBits, nByteCount, nFillByte ); + return true; + } + + // TODO: handle other bitmap formats + switch( nDstFormat ) + { + case BMP_FORMAT_32BIT_TC_MASK: + case BMP_FORMAT_16BIT_TC_MSB_MASK: + case BMP_FORMAT_16BIT_TC_LSB_MASK: + + case BMP_FORMAT_24BIT_TC_BGR: + case BMP_FORMAT_24BIT_TC_RGB: + + case BMP_FORMAT_32BIT_TC_ABGR: +#ifdef FAST_ARGB_BGRA + case BMP_FORMAT_32BIT_TC_ARGB: + case BMP_FORMAT_32BIT_TC_BGRA: +#endif + case BMP_FORMAT_32BIT_TC_RGBA: + break; + + default: + break; + } + + return false; +} + +// ======================================================================= + +#else // NO_OPTIMIZED_BITMAP_ACCESS + +bool ImplFastBitmapConversion( BitmapBuffer&, const BitmapBuffer& ) +{ + return false; +} + +bool ImplFastBitmapBlending( BitmapWriteAccess&, + const BitmapReadAccess&, const BitmapReadAccess&, + const Size&, const Point& ) +{ + return false; +} + +bool ImplFastEraseBitmap( BitmapBuffer&, const BitmapColor& ) +{ + return false; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/configsettings.cxx b/vcl/source/gdi/configsettings.cxx new file mode 100644 index 000000000000..ff701ab640fa --- /dev/null +++ b/vcl/source/gdi/configsettings.cxx @@ -0,0 +1,207 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <vcl/configsettings.hxx> +#include <vcl/svdata.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> + +using namespace utl; +using namespace vcl; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; + +using ::rtl::OUString; + +#define SETTINGS_CONFIGNODE "VCL/Settings" + +/* + * SettingsConfigItem::get + */ + +SettingsConfigItem* SettingsConfigItem::get() +{ + ImplSVData* pSVData = ImplGetSVData(); + if( ! pSVData->mpSettingsConfigItem ) + pSVData->mpSettingsConfigItem = new SettingsConfigItem(); + return pSVData->mpSettingsConfigItem; +} + +/* + * SettignsConfigItem constructor + */ + +SettingsConfigItem::SettingsConfigItem() + : + ConfigItem( OUString( RTL_CONSTASCII_USTRINGPARAM( SETTINGS_CONFIGNODE ) ), + CONFIG_MODE_DELAYED_UPDATE ), + m_aSettings( 0 ) +{ + getValues(); +} + +/* + * SettingsConfigItem destructor + */ + +SettingsConfigItem::~SettingsConfigItem() +{ + if( IsModified() ) + Commit(); +} + +/* + * SettingsConfigItem::Commit + */ + +void SettingsConfigItem::Commit() +{ + if( ! IsValidConfigMgr() ) + return; + + boost::unordered_map< OUString, SmallOUStrMap, rtl::OUStringHash >::const_iterator group; + + for( group = m_aSettings.begin(); group != m_aSettings.end(); ++group ) + { + String aKeyName( group->first ); + /*sal_Bool bAdded =*/ AddNode( OUString(), aKeyName ); + Sequence< PropertyValue > aValues( group->second.size() ); + PropertyValue* pValues = aValues.getArray(); + int nIndex = 0; + SmallOUStrMap::const_iterator it; + for( it = group->second.begin(); it != group->second.end(); ++it ) + { + String aName( aKeyName ); + aName.Append( '/' ); + aName.Append( String( it->first ) ); + pValues[nIndex].Name = aName; + pValues[nIndex].Handle = 0; + pValues[nIndex].Value <<= it->second; + pValues[nIndex].State = PropertyState_DIRECT_VALUE; + nIndex++; + } + ReplaceSetProperties( aKeyName, aValues ); + } +} + +/* + * SettingsConfigItem::Notify + */ + +void SettingsConfigItem::Notify( const Sequence< OUString >& ) +{ + getValues(); +} + +/* + * SettingsConfigItem::getValues + */ +void SettingsConfigItem::getValues() +{ + if( ! IsValidConfigMgr() ) + return; + + m_aSettings.clear(); + + Sequence< OUString > aNames( GetNodeNames( OUString() ) ); + + for( int j = 0; j < aNames.getLength(); j++ ) + { +#if OSL_DEBUG_LEVEL > 2 + OSL_TRACE( "found settings data for \"%s\"\n", + OUStringToOString( aNames.getConstArray()[j], RTL_TEXTENCODING_ASCII_US ).getStr() + ); +#endif + String aKeyName( aNames.getConstArray()[j] ); + Sequence< OUString > aKeys( GetNodeNames( aKeyName ) ); + Sequence< OUString > aSettingsKeys( aKeys.getLength() ); + const OUString* pFrom = aKeys.getConstArray(); + OUString* pTo = aSettingsKeys.getArray(); + for( int m = 0; m < aKeys.getLength(); m++ ) + { + String aName( aKeyName ); + aName.Append( '/' ); + aName.Append( String( pFrom[m] ) ); + pTo[m] = aName; + } + Sequence< Any > aValues( GetProperties( aSettingsKeys ) ); + const Any* pValue = aValues.getConstArray(); + for( int i = 0; i < aValues.getLength(); i++, pValue++ ) + { + if( pValue->getValueTypeClass() == TypeClass_STRING ) + { + const OUString* pLine = (const OUString*)pValue->getValue(); + if( pLine->getLength() ) + m_aSettings[ aKeyName ][ pFrom[i] ] = *pLine; +#if OSL_DEBUG_LEVEL > 2 + OSL_TRACE( " \"%s\"=\"%.30s\"\n", + OUStringToOString( aKeys.getConstArray()[i], RTL_TEXTENCODING_ASCII_US ).getStr(), + OUStringToOString( *pLine, RTL_TEXTENCODING_ASCII_US ).getStr() + ); +#endif + } + } + } +} + +/* + * SettingsConfigItem::getDefaultFont + */ + +const OUString& SettingsConfigItem::getValue( const OUString& rGroup, const OUString& rKey ) const +{ + ::boost::unordered_map< OUString, SmallOUStrMap, rtl::OUStringHash >::const_iterator group = m_aSettings.find( rGroup ); + if( group == m_aSettings.end() || group->second.find( rKey ) == group->second.end() ) + { + static OUString aEmpty; + return aEmpty; + } + return group->second.find(rKey)->second; +} + +/* + * SettingsConfigItem::setDefaultFont + */ + +void SettingsConfigItem::setValue( const OUString& rGroup, const OUString& rKey, const OUString& rValue ) +{ + bool bModified = m_aSettings[ rGroup ][ rKey ] != rValue; + if( bModified ) + { + m_aSettings[ rGroup ][ rKey ] = rValue; + SetModified(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/cvtgrf.cxx b/vcl/source/gdi/cvtgrf.cxx new file mode 100644 index 000000000000..261781ebcf50 --- /dev/null +++ b/vcl/source/gdi/cvtgrf.cxx @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <svsys.h> +#include <vcl/metaact.hxx> +#include <vcl/salinst.hxx> +#include <vcl/svdata.hxx> +#include <vcl/cvtgrf.hxx> + +// -------------- +// - Callback - +// -------------- + +// -------------------- +// - GraphicConverter - +// -------------------- + +GraphicConverter::GraphicConverter() : + mpConvertData( NULL ) +{ +} + +// ------------------------------------------------------------------------ + +GraphicConverter::~GraphicConverter() +{ +} + +// ------------------------------------------------------------------------ + +sal_uLong GraphicConverter::ImplConvert( sal_uLong nInFormat, void* pInBuffer, sal_uLong nInBufSize, + void** ppOutBuffer, sal_uLong nOutFormat ) +{ + sal_uLong nRetBufSize = 0UL; + + if( ( nInFormat != nOutFormat ) && pInBuffer ) + { + if( ( nInFormat == CVT_SVM ) || ( nInFormat == CVT_BMP ) ) + { + SvMemoryStream aIStm; + Graphic aGraphic; + + aIStm.SetBuffer( (char*) pInBuffer, nInBufSize, sal_False, nInBufSize ); + aIStm >> aGraphic; + + if( !aIStm.GetError() ) + { + SvMemoryStream aOStm( 64535, 64535 ); + + mpConvertData = new ConvertData( aGraphic, aOStm, nOutFormat ); + + if( maFilterHdl.IsSet() && maFilterHdl.Call( mpConvertData ) ) + { + nRetBufSize = aOStm.Seek( STREAM_SEEK_TO_END ); + *ppOutBuffer = (void*) aOStm.GetData(); + aOStm.ObjectOwnsMemory( sal_False ); + } + + delete mpConvertData; + mpConvertData = NULL; + } + } + else if( ( nOutFormat == CVT_SVM ) || ( nOutFormat == CVT_BMP ) ) + { + SvMemoryStream aIStm; + + aIStm.SetBuffer( (char*) pInBuffer, nInBufSize, sal_False, nInBufSize ); + mpConvertData = new ConvertData( Graphic(), aIStm, nInFormat ); + + if( maFilterHdl.IsSet() && maFilterHdl.Call( mpConvertData ) ) + { + SvMemoryStream aOStm( 645535, 64535 ); + Graphic& rGraphic = mpConvertData->maGraphic; + + if( ( rGraphic.GetType() == GRAPHIC_BITMAP ) && ( CVT_SVM == nOutFormat ) ) + { + GDIMetaFile aMtf; + + aMtf.SetPrefSize( rGraphic.GetPrefSize() ); + aMtf.SetPrefMapMode( rGraphic.GetPrefMapMode() ); + aMtf.AddAction( new MetaBmpExScaleAction( Point(), aMtf.GetPrefSize(), rGraphic.GetBitmapEx() ) ); + rGraphic = aMtf; + } + else if( ( rGraphic.GetType() == GRAPHIC_GDIMETAFILE ) && ( CVT_BMP == nOutFormat ) ) + rGraphic = rGraphic.GetBitmapEx(); + + aOStm << rGraphic; + + if( !aOStm.GetError() ) + { + nRetBufSize = aOStm.Seek( STREAM_SEEK_TO_END ); + *ppOutBuffer = (void*) aOStm.GetData(); + aOStm.ObjectOwnsMemory( sal_False ); + } + } + + delete mpConvertData; + mpConvertData = NULL; + } + } + + return nRetBufSize; +} + +// ------------------------------------------------------------------------ + +sal_uLong GraphicConverter::Import( SvStream& rIStm, Graphic& rGraphic, sal_uLong nFormat ) +{ + GraphicConverter* pCvt = ImplGetSVData()->maGDIData.mpGrfConverter; + sal_uLong nRet = ERRCODE_IO_GENERAL; + + if( pCvt && pCvt->GetFilterHdl().IsSet() ) + { + ConvertData aData( rGraphic, rIStm, nFormat ); + + if( pCvt->GetFilterHdl().Call( &aData ) ) + { + rGraphic = aData.maGraphic; + nRet = ERRCODE_NONE; + } + else if( rIStm.GetError() ) + nRet = rIStm.GetError(); + } + + return nRet; +} + +// ------------------------------------------------------------------------ + +sal_uLong GraphicConverter::Export( SvStream& rOStm, const Graphic& rGraphic, sal_uLong nFormat ) +{ + GraphicConverter* pCvt = ImplGetSVData()->maGDIData.mpGrfConverter; + sal_uLong nRet = ERRCODE_IO_GENERAL; + + if( pCvt && pCvt->GetFilterHdl().IsSet() ) + { + ConvertData aData( rGraphic, rOStm, nFormat ); + + if( pCvt->GetFilterHdl().Call( &aData ) ) + nRet = ERRCODE_NONE; + else if( rOStm.GetError() ) + nRet = rOStm.GetError(); + } + + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/cvtsvm.cxx b/vcl/source/gdi/cvtsvm.cxx new file mode 100644 index 000000000000..2d9fbef36e44 --- /dev/null +++ b/vcl/source/gdi/cvtsvm.cxx @@ -0,0 +1,2512 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#define ENABLE_BYTESTRING_STREAM_OPERATORS + +#include <algorithm> +#include <string.h> +#include <tools/stack.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <vcl/virdev.hxx> +#include <vcl/graph.hxx> +#include <vcl/lineinfo.hxx> +#include <vcl/salbtype.hxx> +#include <vcl/cvtsvm.hxx> + +// ----------- +// - Defines - +// ----------- + +#define CVTSVM_WRITE_SUBACTIONCOUNT 1 + +// ----------- +// - Inlines - +// ----------- + +void ImplReadRect( SvStream& rIStm, Rectangle& rRect ) +{ + Point aTL; + Point aBR; + + rIStm >> aTL; + rIStm >> aBR; + + rRect = Rectangle( aTL, aBR ); +} + +// ------------------------------------------------------------------------ + +void ImplWriteRect( SvStream& rOStm, const Rectangle& rRect ) +{ + rOStm << rRect.TopLeft(); + rOStm << rRect.BottomRight(); +} + +// ------------------------------------------------------------------------ + +void ImplReadPoly( SvStream& rIStm, Polygon& rPoly ) +{ + sal_Int32 nSize; + + rIStm >> nSize; + rPoly = Polygon( (sal_uInt16) nSize ); + + for( sal_uInt16 i = 0; i < (sal_uInt16) nSize; i++ ) + rIStm >> rPoly[ i ]; +} + +// ------------------------------------------------------------------------ + +void ImplReadPolyPoly( SvStream& rIStm, PolyPolygon& rPolyPoly ) +{ + Polygon aPoly; + sal_Int32 nPolyCount; + + rIStm >> nPolyCount; + + for( sal_uInt16 i = 0; i < (sal_uInt16) nPolyCount; i++ ) + { + ImplReadPoly( rIStm, aPoly ); + rPolyPoly.Insert( aPoly ); + } +} + +// ------------------------------------------------------------------------ + +void ImplWritePolyPolyAction( SvStream& rOStm, const PolyPolygon& rPolyPoly ) +{ + const sal_uInt16 nPoly = rPolyPoly.Count(); + sal_uInt16 nPoints = 0; + sal_uInt16 n; + + for( n = 0; n < nPoly; n++ ) + nPoints = sal::static_int_cast<sal_uInt16>(nPoints + rPolyPoly[ n ].GetSize()); + + rOStm << (sal_Int16) GDI_POLYPOLYGON_ACTION; + rOStm << (sal_Int32) ( 8 + ( nPoly << 2 ) + ( nPoints << 3 ) ); + rOStm << (sal_Int32) nPoly; + + for( n = 0; n < nPoly; n++ ) + { + // #i102224# Here the evtl. curved nature of Polygon was + // ignored (for all those Years). Adapted to at least write + // a polygon representing the curve as good as possible + Polygon aSimplePoly; + rPolyPoly[n].AdaptiveSubdivide(aSimplePoly); + const sal_uInt16 nSize(aSimplePoly.GetSize()); + + rOStm << (sal_Int32) nSize; + + for( sal_uInt16 j = 0; j < nSize; j++ ) + rOStm << aSimplePoly[ j ]; + } +} + +// ------------------------------------------------------------------------ + +void ImplReadColor( SvStream& rIStm, Color& rColor ) +{ + sal_Int16 nVal; + + rIStm >> nVal; rColor.SetRed( sal::static_int_cast<sal_uInt8>((sal_uInt16)nVal >> 8) ); + rIStm >> nVal; rColor.SetGreen( sal::static_int_cast<sal_uInt8>((sal_uInt16)nVal >> 8) ); + rIStm >> nVal; rColor.SetBlue( sal::static_int_cast<sal_uInt8>((sal_uInt16)nVal >> 8) ); +} + +// ------------------------------------------------------------------------ + +void ImplWriteColor( SvStream& rOStm, const Color& rColor ) +{ + sal_Int16 nVal; + + nVal = ( (sal_Int16) rColor.GetRed() << 8 ) | rColor.GetRed(); + rOStm << nVal; + + nVal = ( (sal_Int16) rColor.GetGreen() << 8 ) | rColor.GetGreen(); + rOStm << nVal; + + nVal = ( (sal_Int16) rColor.GetBlue() << 8 ) | rColor.GetBlue(); + rOStm << nVal; +} + +// ------------------------------------------------------------------------ + +void ImplReadMapMode( SvStream& rIStm, MapMode& rMapMode ) +{ + Point aOrg; + sal_Int32 nXNum; + sal_Int32 nXDenom; + sal_Int32 nYNum; + sal_Int32 nYDenom; + sal_Int16 nUnit; + + rIStm >> nUnit >> aOrg >> nXNum >> nXDenom >> nYNum >> nYDenom; + rMapMode = MapMode( (MapUnit) nUnit, aOrg, Fraction( nXNum, nXDenom ), Fraction( nYNum, nYDenom ) ); +} + +// ------------------------------------------------------------------------ + +void ImplWriteMapMode( SvStream& rOStm, const MapMode& rMapMode ) +{ + rOStm << (sal_Int16) rMapMode.GetMapUnit(); + rOStm << rMapMode.GetOrigin(); + rOStm << (sal_Int32) rMapMode.GetScaleX().GetNumerator(); + rOStm << (sal_Int32) rMapMode.GetScaleX().GetDenominator(); + rOStm << (sal_Int32) rMapMode.GetScaleY().GetNumerator(); + rOStm << (sal_Int32) rMapMode.GetScaleY().GetDenominator(); +} + +// ------------------------------------------------------------------------ + +void ImplWritePushAction( SvStream& rOStm ) +{ + rOStm << (sal_Int16) GDI_PUSH_ACTION; + rOStm << (sal_Int32) 4; +} + +// ------------------------------------------------------------------------ + +void ImplWritePopAction( SvStream& rOStm ) +{ + rOStm << (sal_Int16) GDI_POP_ACTION; + rOStm << (sal_Int32) 4; +} + +// ------------------------------------------------------------------------ + +void ImplWriteLineColor( SvStream& rOStm, const Color& rColor, sal_Int16 nStyle, sal_Int32 nWidth = 0L ) +{ + if( rColor.GetTransparency() > 127 ) + nStyle = 0; + + rOStm << (sal_Int16) GDI_PEN_ACTION; + rOStm << (sal_Int32) 16; + ImplWriteColor( rOStm, rColor ); + rOStm << nWidth; + rOStm << nStyle; +} + +// ------------------------------------------------------------------------ + +void ImplWriteFillColor( SvStream& rOStm, const Color& rColor, sal_Int16 nStyle ) +{ + rOStm << (sal_Int16) GDI_FILLBRUSH_ACTION; + rOStm << (sal_Int32) 20; + ImplWriteColor( rOStm, rColor ); + + if( rColor.GetTransparency() > 127 ) + nStyle = 0; + + if( nStyle > 1 ) + { + ImplWriteColor( rOStm, COL_WHITE ); + rOStm << nStyle; + rOStm << (sal_Int16) 1; + } + else + { + ImplWriteColor( rOStm, COL_BLACK ); + rOStm << nStyle; + rOStm << (sal_Int16) 0; + } +} + +// ------------------------------------------------------------------------ + +void ImplWriteFont( SvStream& rOStm, const Font& rFont, + rtl_TextEncoding& rActualCharSet ) +{ + char aName[32]; + short nWeight; + + ByteString aByteName( rFont.GetName(), rOStm.GetStreamCharSet() ); + strncpy( aName, aByteName.GetBuffer(), 32 ); + + switch ( rFont.GetWeight() ) + { + case WEIGHT_THIN: + case WEIGHT_ULTRALIGHT: + case WEIGHT_LIGHT: + nWeight = 1; + break; + + case WEIGHT_NORMAL: + case WEIGHT_MEDIUM: + nWeight = 2; + break; + + case WEIGHT_BOLD: + case WEIGHT_ULTRABOLD: + case WEIGHT_BLACK: + nWeight = 3; + break; + + default: + nWeight = 0; + break; + } + + rOStm << (sal_Int16) GDI_FONT_ACTION; + rOStm << (sal_Int32) 78; + + rActualCharSet = GetStoreCharSet( rFont.GetCharSet() ); + ImplWriteColor( rOStm, rFont.GetColor() ); + ImplWriteColor( rOStm, rFont.GetFillColor() ); + rOStm.Write( aName, 32 ); + rOStm << rFont.GetSize(); + rOStm << (sal_Int16) 0; // no character orientation anymore + rOStm << (sal_Int16) rFont.GetOrientation(); + rOStm << (sal_Int16) rActualCharSet; + rOStm << (sal_Int16) rFont.GetFamily(); + rOStm << (sal_Int16) rFont.GetPitch(); + rOStm << (sal_Int16) rFont.GetAlign(); + rOStm << (sal_Int16) nWeight; + rOStm << (sal_Int16) rFont.GetUnderline(); + rOStm << (sal_Int16) rFont.GetStrikeout(); + rOStm << (sal_Bool) ( rFont.GetItalic() != ITALIC_NONE ); + rOStm << rFont.IsOutline(); + rOStm << rFont.IsShadow(); + rOStm << rFont.IsTransparent(); + if ( rActualCharSet == RTL_TEXTENCODING_DONTKNOW ) + rActualCharSet = gsl_getSystemTextEncoding(); +} + +// ------------------------------------------------------------------------ + +void ImplWriteRasterOpAction( SvStream& rOStm, sal_Int16 nRasterOp ) +{ + rOStm << (sal_Int16) GDI_RASTEROP_ACTION << (sal_Int32) 6 << nRasterOp; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImplWriteUnicodeComment( SvStream& rOStm, const String& rString ) +{ + xub_StrLen i, nStringLen = rString.Len(); + if ( nStringLen ) + { + sal_uInt32 nSize = ( nStringLen << 1 ) + 4; + sal_uInt16 nType = GDI_UNICODE_COMMENT; + + rOStm << nType << nSize; + for ( i = 0; i < nStringLen; i++ ) + { + sal_Unicode nUni = rString.GetChar( i ); + rOStm << nUni; + } + } + return nStringLen != 0; +} + +// ------------------------------------------------------------------------ + +void ImplReadUnicodeComment( sal_uInt32 nStrmPos, SvStream& rIStm, String& rString ) +{ + sal_uInt32 nOld = rIStm.Tell(); + if ( nStrmPos ) + { + sal_uInt16 nType; + sal_uInt32 nActionSize; + xub_StrLen nStringLen; + + rIStm.Seek( nStrmPos ); + rIStm >> nType + >> nActionSize; + + nStringLen = sal::static_int_cast<xub_StrLen>(( nActionSize - 4 ) >> 1); + + if ( nStringLen && ( nType == GDI_UNICODE_COMMENT ) ) + { + sal_Unicode* pBuffer = rString.AllocBuffer( nStringLen ); + while ( nStringLen-- ) + rIStm >> *pBuffer++; + } + } + rIStm.Seek( nOld ); +} + +// ------------------------------------------------------------------------ + +void ImplSkipActions( SvStream& rIStm, sal_uLong nSkipCount ) +{ + sal_Int32 nActionSize; + sal_Int16 nType; + + for( sal_uLong i = 0UL; i < nSkipCount; i++ ) + { + rIStm >> nType >> nActionSize; + rIStm.SeekRel( nActionSize - 4L ); + } +} + +// ------------------------------------------------------------------------ + +bool ImplWriteExtendedPolyPolygonAction(SvStream& rOStm, const PolyPolygon& rPolyPolygon, bool bOnlyWhenCurve) +{ + const sal_uInt16 nPolygonCount(rPolyPolygon.Count()); + + if(nPolygonCount) + { + sal_uInt32 nAllPolygonCount(0); + sal_uInt32 nAllPointCount(0); + sal_uInt32 nAllFlagCount(0); + sal_uInt16 a(0); + + for(a = 0; a < nPolygonCount; a++) + { + const Polygon& rCandidate = rPolyPolygon.GetObject(a); + const sal_uInt16 nPointCount(rCandidate.GetSize()); + + if(nPointCount) + { + nAllPolygonCount++; + nAllPointCount += nPointCount; + + if(rCandidate.HasFlags()) + { + nAllFlagCount += nPointCount; + } + } + } + + if((bOnlyWhenCurve && nAllFlagCount) || (!bOnlyWhenCurve && nAllPointCount)) + { + rOStm << (sal_Int16) GDI_EXTENDEDPOLYGON_ACTION; + + const sal_Int32 nActionSize( + 4 + // Action size + 2 + // PolygonCount + (nAllPolygonCount * 2) + // Points per polygon + (nAllPointCount << 3) + // Points themselves + nAllPolygonCount + // Bool if (when poly has points) it has flags, too + nAllFlagCount); // Flags themselves + + rOStm << nActionSize; + rOStm << (sal_uInt16)nAllPolygonCount; + + for(a = 0; a < nPolygonCount; a++) + { + const Polygon& rCandidate = rPolyPolygon.GetObject(a); + const sal_uInt16 nPointCount(rCandidate.GetSize()); + + if(nPointCount) + { + rOStm << nPointCount; + + for(sal_uInt16 b(0); b < nPointCount; b++) + { + rOStm << rCandidate[b]; + } + + if(rCandidate.HasFlags()) + { + rOStm << (sal_uInt8)true; + + for(sal_uInt16 c(0); c < nPointCount; c++) + { + rOStm << (sal_uInt8)rCandidate.GetFlags(c); + } + } + else + { + rOStm << (sal_uInt8)false; + } + } + } + + return true; + } + } + + return false; +} + +// ------------------------------------------------------------------------ + +void ImplReadExtendedPolyPolygonAction(SvStream& rIStm, PolyPolygon& rPolyPoly) +{ + rPolyPoly.Clear(); + sal_uInt16 nPolygonCount(0); + rIStm >> nPolygonCount; + + for(sal_uInt16 a(0); a < nPolygonCount; a++) + { + sal_uInt16 nPointCount(0); + rIStm >> nPointCount; + Polygon aCandidate(nPointCount); + + if(nPointCount) + { + for(sal_uInt16 b(0); b < nPointCount; b++) + { + rIStm >> aCandidate[b]; + } + + sal_uInt8 bHasFlags(false); + rIStm >> bHasFlags; + + if(bHasFlags) + { + sal_uInt8 aPolyFlags(0); + + for(sal_uInt16 c(0); c < nPointCount; c++) + { + rIStm >> aPolyFlags; + aCandidate.SetFlags(c, (PolyFlags)aPolyFlags); + } + } + } + + rPolyPoly.Insert(aCandidate); + } +} + +// ---------------- +// - SVMConverter - +// ---------------- + +SVMConverter::SVMConverter( SvStream& rStm, GDIMetaFile& rMtf, sal_uLong nConvertMode ) +{ + if( !rStm.GetError() ) + { + if( CONVERT_FROM_SVM1 == nConvertMode ) + ImplConvertFromSVM1( rStm, rMtf ); + else if( CONVERT_TO_SVM1 == nConvertMode ) + ImplConvertToSVM1( rStm, rMtf ); + } +} + +// ------------------------------------------------------------------------ + +void SVMConverter::ImplConvertFromSVM1( SvStream& rIStm, GDIMetaFile& rMtf ) +{ + const sal_uLong nPos = rIStm.Tell(); + const sal_uInt16 nOldFormat = rIStm.GetNumberFormatInt(); + + rIStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + char aCode[ 5 ]; + Size aPrefSz; + sal_Int16 nSize; + sal_Int16 nVersion; + + // read header + rIStm.Read( (char*) &aCode, sizeof( aCode ) ); // Kennung + rIStm >> nSize; // Size + rIStm >> nVersion; // Version + rIStm >> aPrefSz.Width(); // PrefSize.Width() + rIStm >> aPrefSz.Height(); // PrefSize.Height() + + // check header-magic and version + if( rIStm.GetError() + || ( memcmp( aCode, "SVGDI", sizeof( aCode ) ) != 0 ) + || ( nVersion != 200 ) ) + { + rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR ); + rIStm.SetNumberFormatInt( nOldFormat ); + rIStm.Seek( nPos ); + return; + } + + LineInfo aLineInfo( LINE_NONE, 0 ); + Stack aLIStack; + VirtualDevice aFontVDev; + rtl_TextEncoding eActualCharSet = gsl_getSystemTextEncoding(); + sal_Bool bFatLine = sal_False; + + // TODO: fix reindentation below if you can accept being blamed by the SCM + MapMode aMapMode; + Polygon aActionPoly; + Rectangle aRect; + Point aPt, aPt1; + Size aSz; + Color aActionColor; + sal_Int32 nTmp, nTmp1, nActionSize; + sal_Int32 nActions; + sal_Int16 nType; + + sal_uInt32 nUnicodeCommentStreamPos = 0; + sal_Int32 nUnicodeCommentActionNumber = 0; + + ImplReadMapMode( rIStm, aMapMode ); // MapMode + rIStm >> nActions; // Action count + + rMtf.SetPrefSize( aPrefSz ); + rMtf.SetPrefMapMode( aMapMode ); + sal_uInt32 nLastPolygonAction(0); + + for( sal_Int32 i = 0L; i < nActions; i++ ) + { + rIStm >> nType; + sal_Int32 nActBegin = rIStm.Tell(); + rIStm >> nActionSize; + + DBG_ASSERT( ( nType <= 33 ) || ( nType >= 1024 ), "Unknown GDIMetaAction while converting!" ); + + switch( nType ) + { + case( GDI_PIXEL_ACTION ): + { + rIStm >> aPt; + ImplReadColor( rIStm, aActionColor ); + rMtf.AddAction( new MetaPixelAction( aPt, aActionColor ) ); + } + break; + + case( GDI_POINT_ACTION ): + { + rIStm >> aPt; + rMtf.AddAction( new MetaPointAction( aPt ) ); + } + break; + + case( GDI_LINE_ACTION ): + { + rIStm >> aPt >> aPt1; + rMtf.AddAction( new MetaLineAction( aPt, aPt1, aLineInfo ) ); + } + break; + + case (GDI_LINEJOIN_ACTION) : + { + sal_Int16 nLineJoin(0); + rIStm >> nLineJoin; + aLineInfo.SetLineJoin((basegfx::B2DLineJoin)nLineJoin); + } + break; + + case (GDI_LINEDASHDOT_ACTION) : + { + sal_Int16 a(0); + sal_Int32 b(0); + + rIStm >> a; aLineInfo.SetDashCount(a); + rIStm >> b; aLineInfo.SetDashLen(b); + rIStm >> a; aLineInfo.SetDotCount(a); + rIStm >> b; aLineInfo.SetDotLen(b); + rIStm >> b; aLineInfo.SetDistance(b); + + if(((aLineInfo.GetDashCount() && aLineInfo.GetDashLen()) + || (aLineInfo.GetDotCount() && aLineInfo.GetDotLen())) + && aLineInfo.GetDistance()) + { + aLineInfo.SetStyle(LINE_DASH); + } + } + break; + + case (GDI_EXTENDEDPOLYGON_ACTION) : + { + // read the PolyPolygon in every case + PolyPolygon aInputPolyPolygon; + ImplReadExtendedPolyPolygonAction(rIStm, aInputPolyPolygon); + + // now check if it can be set somewhere + if(nLastPolygonAction < rMtf.GetActionCount()) + { + MetaPolyLineAction* pPolyLineAction = dynamic_cast< MetaPolyLineAction* >(rMtf.GetAction(nLastPolygonAction)); + + if(pPolyLineAction) + { + // replace MetaPolyLineAction when we have a single polygon. Do not rely on the + // same point count; the originally written GDI_POLYLINE_ACTION may have been + // Subdivided for better quality for older usages + if(1 == aInputPolyPolygon.Count()) + { + rMtf.ReplaceAction( + new MetaPolyLineAction( + aInputPolyPolygon.GetObject(0), + pPolyLineAction->GetLineInfo()), + nLastPolygonAction); + pPolyLineAction->Delete(); + } + } + else + { + MetaPolyPolygonAction* pPolyPolygonAction = dynamic_cast< MetaPolyPolygonAction* >(rMtf.GetAction(nLastPolygonAction)); + + if(pPolyPolygonAction) + { + // replace MetaPolyPolygonAction when we have a curved polygon. Do rely on the + // same sub-polygon count + if(pPolyPolygonAction->GetPolyPolygon().Count() == aInputPolyPolygon.Count()) + { + rMtf.ReplaceAction( + new MetaPolyPolygonAction( + aInputPolyPolygon), + nLastPolygonAction); + pPolyPolygonAction->Delete(); + } + } + else + { + MetaPolygonAction* pPolygonAction = dynamic_cast< MetaPolygonAction* >(rMtf.GetAction(nLastPolygonAction)); + + if(pPolygonAction) + { + // replace MetaPolygonAction + if(1 == aInputPolyPolygon.Count()) + { + rMtf.ReplaceAction( + new MetaPolygonAction( + aInputPolyPolygon.GetObject(0)), + nLastPolygonAction); + pPolygonAction->Delete(); + } + } + } + } + } + } + break; + + case( GDI_RECT_ACTION ): + { + ImplReadRect( rIStm, aRect ); + rIStm >> nTmp >> nTmp1; + + if( nTmp || nTmp1 ) + rMtf.AddAction( new MetaRoundRectAction( aRect, nTmp, nTmp1 ) ); + else + { + rMtf.AddAction( new MetaRectAction( aRect ) ); + + if( bFatLine ) + rMtf.AddAction( new MetaPolyLineAction( aRect, aLineInfo ) ); + } + } + break; + + case( GDI_ELLIPSE_ACTION ): + { + ImplReadRect( rIStm, aRect ); + + if( bFatLine ) + { + const Polygon aPoly( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 ); + + rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR ) ); + rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, sal_False ) ); + rMtf.AddAction( new MetaPolygonAction( aPoly ) ); + rMtf.AddAction( new MetaPopAction() ); + rMtf.AddAction( new MetaPolyLineAction( aPoly, aLineInfo ) ); + } + else + rMtf.AddAction( new MetaEllipseAction( aRect ) ); + } + break; + + case( GDI_ARC_ACTION ): + { + ImplReadRect( rIStm, aRect ); + rIStm >> aPt >> aPt1; + + if( bFatLine ) + { + const Polygon aPoly( aRect, aPt, aPt1, POLY_ARC ); + + rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR ) ); + rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, sal_False ) ); + rMtf.AddAction( new MetaPolygonAction( aPoly ) ); + rMtf.AddAction( new MetaPopAction() ); + rMtf.AddAction( new MetaPolyLineAction( aPoly, aLineInfo ) ); + } + else + rMtf.AddAction( new MetaArcAction( aRect, aPt, aPt1 ) ); + } + break; + + case( GDI_PIE_ACTION ): + { + ImplReadRect( rIStm, aRect ); + rIStm >> aPt >> aPt1; + + if( bFatLine ) + { + const Polygon aPoly( aRect, aPt, aPt1, POLY_PIE ); + + rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR ) ); + rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, sal_False ) ); + rMtf.AddAction( new MetaPolygonAction( aPoly ) ); + rMtf.AddAction( new MetaPopAction() ); + rMtf.AddAction( new MetaPolyLineAction( aPoly, aLineInfo ) ); + } + else + rMtf.AddAction( new MetaPieAction( aRect, aPt, aPt1 ) ); + } + break; + + case( GDI_INVERTRECT_ACTION ): + case( GDI_HIGHLIGHTRECT_ACTION ): + { + ImplReadRect( rIStm, aRect ); + rMtf.AddAction( new MetaPushAction( PUSH_RASTEROP ) ); + rMtf.AddAction( new MetaRasterOpAction( ROP_INVERT ) ); + rMtf.AddAction( new MetaRectAction( aRect ) ); + rMtf.AddAction( new MetaPopAction() ); + } + break; + + case( GDI_POLYLINE_ACTION ): + { + ImplReadPoly( rIStm, aActionPoly ); + nLastPolygonAction = rMtf.GetActionCount(); + + if( bFatLine ) + rMtf.AddAction( new MetaPolyLineAction( aActionPoly, aLineInfo ) ); + else + rMtf.AddAction( new MetaPolyLineAction( aActionPoly ) ); + } + break; + + case( GDI_POLYGON_ACTION ): + { + ImplReadPoly( rIStm, aActionPoly ); + + if( bFatLine ) + { + rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR ) ); + rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, sal_False ) ); + rMtf.AddAction( new MetaPolygonAction( aActionPoly ) ); + rMtf.AddAction( new MetaPopAction() ); + rMtf.AddAction( new MetaPolyLineAction( aActionPoly, aLineInfo ) ); + } + else + { + nLastPolygonAction = rMtf.GetActionCount(); + rMtf.AddAction( new MetaPolygonAction( aActionPoly ) ); + } + } + break; + + case( GDI_POLYPOLYGON_ACTION ): + { + PolyPolygon aPolyPoly; + + ImplReadPolyPoly( rIStm, aPolyPoly ); + + if( bFatLine ) + { + rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR ) ); + rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, sal_False ) ); + rMtf.AddAction( new MetaPolyPolygonAction( aPolyPoly ) ); + rMtf.AddAction( new MetaPopAction() ); + + for( sal_uInt16 nPoly = 0, nCount = aPolyPoly.Count(); nPoly < nCount; nPoly++ ) + rMtf.AddAction( new MetaPolyLineAction( aPolyPoly[ nPoly ], aLineInfo ) ); + } + else + { + nLastPolygonAction = rMtf.GetActionCount(); + rMtf.AddAction( new MetaPolyPolygonAction( aPolyPoly ) ); + } + } + break; + + case( GDI_FONT_ACTION ): + { + Font aFont; + char aName[ 32 ]; + sal_Int32 nWidth, nHeight; + sal_Int16 nCharSet, nFamily, nPitch, nAlign, nWeight, nUnderline, nStrikeout; + sal_Int16 nCharOrient, nLineOrient; + sal_Bool bItalic, bOutline, bShadow, bTransparent; + + ImplReadColor( rIStm, aActionColor ); aFont.SetColor( aActionColor ); + ImplReadColor( rIStm, aActionColor ); aFont.SetFillColor( aActionColor ); + rIStm.Read( aName, 32 ); + aFont.SetName( UniString( aName, rIStm.GetStreamCharSet() ) ); + rIStm >> nWidth >> nHeight; + rIStm >> nCharOrient >> nLineOrient; + rIStm >> nCharSet >> nFamily >> nPitch >> nAlign >> nWeight >> nUnderline >> nStrikeout; + rIStm >> bItalic >> bOutline >> bShadow >> bTransparent; + + aFont.SetSize( Size( nWidth, nHeight ) ); + aFont.SetCharSet( (CharSet) nCharSet ); + aFont.SetFamily( (FontFamily) nFamily ); + aFont.SetPitch( (FontPitch) nPitch ); + aFont.SetAlign( (FontAlign) nAlign ); + aFont.SetWeight( ( nWeight == 1 ) ? WEIGHT_LIGHT : ( nWeight == 2 ) ? WEIGHT_NORMAL : + ( nWeight == 3 ) ? WEIGHT_BOLD : WEIGHT_DONTKNOW ); + aFont.SetUnderline( (FontUnderline) nUnderline ); + aFont.SetStrikeout( (FontStrikeout) nStrikeout ); + aFont.SetItalic( bItalic ? ITALIC_NORMAL : ITALIC_NONE ); + aFont.SetOutline( bOutline ); + aFont.SetShadow( bShadow ); + aFont.SetOrientation( nLineOrient ); + aFont.SetTransparent( bTransparent ); + + eActualCharSet = aFont.GetCharSet(); + if ( eActualCharSet == RTL_TEXTENCODING_DONTKNOW ) + eActualCharSet = gsl_getSystemTextEncoding(); + + rMtf.AddAction( new MetaFontAction( aFont ) ); + rMtf.AddAction( new MetaTextAlignAction( aFont.GetAlign() ) ); + rMtf.AddAction( new MetaTextColorAction( aFont.GetColor() ) ); + rMtf.AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) ); + + // #106172# Track font relevant data in shadow VDev + aFontVDev.SetFont( aFont ); + } + break; + + case( GDI_TEXT_ACTION ): + { + ByteString aByteStr; + sal_Int32 nIndex, nLen; + + rIStm >> aPt >> nIndex >> nLen >> nTmp; + if ( nTmp && ( static_cast< sal_uInt32 >( nTmp ) < ( SAL_MAX_UINT16 - 1 ) ) ) + { + rIStm.Read( aByteStr.AllocBuffer( (sal_uInt16)nTmp ), nTmp + 1 ); + UniString aStr( aByteStr, eActualCharSet ); + if ( nUnicodeCommentActionNumber == i ) + ImplReadUnicodeComment( nUnicodeCommentStreamPos, rIStm, aStr ); + rMtf.AddAction( new MetaTextAction( aPt, aStr, (sal_uInt16) nIndex, (sal_uInt16) nLen ) ); + } + rIStm.Seek( nActBegin + nActionSize ); + } + break; + + case( GDI_TEXTARRAY_ACTION ): + { + ByteString aByteStr; + sal_Int32* pDXAry = NULL; + sal_Int32 nIndex, nLen, nAryLen; + + rIStm >> aPt >> nIndex >> nLen >> nTmp >> nAryLen; + if ( nTmp && ( static_cast< sal_uInt32 >( nTmp ) < ( SAL_MAX_UINT16 - 1 ) ) ) + { + rIStm.Read( aByteStr.AllocBuffer( (sal_uInt16)nTmp ), nTmp + 1 ); + UniString aStr( aByteStr, eActualCharSet ); + + if( nAryLen > 0L ) + { + sal_Int32 nStrLen( aStr.Len() ); + + pDXAry = new sal_Int32[ Max( nAryLen, nStrLen ) ]; + + for( long j = 0L; j < nAryLen; j++ ) + rIStm >> nTmp, pDXAry[ j ] = nTmp; + + // #106172# Add last DX array elem, if missing + if( nAryLen != nStrLen ) + { + if( nAryLen+1 == nStrLen ) + { + sal_Int32* pTmpAry = new sal_Int32[nStrLen]; + + aFontVDev.GetTextArray( aStr, pTmpAry, (sal_uInt16) nIndex, (sal_uInt16) nLen ); + + // now, the difference between the + // last and the second last DX array + // is the advancement for the last + // glyph. Thus, to complete our meta + // action's DX array, just add that + // difference to last elem and store + // in very last. + if( nStrLen > 1 ) + pDXAry[ nStrLen-1 ] = pDXAry[ nStrLen-2 ] + pTmpAry[ nStrLen-1 ] - pTmpAry[ nStrLen-2 ]; + else + pDXAry[ nStrLen-1 ] = pTmpAry[ nStrLen-1 ]; // len=1: 0th position taken to be 0 + + delete[] pTmpAry; + } + #ifdef DBG_UTIL + else + OSL_FAIL("More than one DX array element missing on SVM import"); + #endif + } + } + if ( nUnicodeCommentActionNumber == i ) + ImplReadUnicodeComment( nUnicodeCommentStreamPos, rIStm, aStr ); + rMtf.AddAction( new MetaTextArrayAction( aPt, aStr, pDXAry, (sal_uInt16) nIndex, (sal_uInt16) nLen ) ); + + if( pDXAry ) + delete[] pDXAry; + } + rIStm.Seek( nActBegin + nActionSize ); + } + break; + + case( GDI_STRETCHTEXT_ACTION ): + { + ByteString aByteStr; + sal_Int32 nIndex, nLen, nWidth; + + rIStm >> aPt >> nIndex >> nLen >> nTmp >> nWidth; + if ( nTmp && ( static_cast< sal_uInt32 >( nTmp ) < ( SAL_MAX_INT16 - 1 ) ) ) + { + rIStm.Read( aByteStr.AllocBuffer( (sal_uInt16)nTmp ), nTmp + 1 ); + UniString aStr( aByteStr, eActualCharSet ); + if ( nUnicodeCommentActionNumber == i ) + ImplReadUnicodeComment( nUnicodeCommentStreamPos, rIStm, aStr ); + rMtf.AddAction( new MetaStretchTextAction( aPt, nWidth, aStr, (sal_uInt16) nIndex, (sal_uInt16) nLen ) ); + } + rIStm.Seek( nActBegin + nActionSize ); + } + break; + + case( GDI_BITMAP_ACTION ): + { + Bitmap aBmp; + + rIStm >> aPt >> aBmp; + rMtf.AddAction( new MetaBmpAction( aPt, aBmp ) ); + } + break; + + case( GDI_BITMAPSCALE_ACTION ): + { + Bitmap aBmp; + + rIStm >> aPt >> aSz >> aBmp; + rMtf.AddAction( new MetaBmpScaleAction( aPt, aSz, aBmp ) ); + } + break; + + case( GDI_BITMAPSCALEPART_ACTION ): + { + Bitmap aBmp; + Size aSz2; + + rIStm >> aPt >> aSz >> aPt1 >> aSz2 >> aBmp; + rMtf.AddAction( new MetaBmpScalePartAction( aPt, aSz, aPt1, aSz2, aBmp ) ); + } + break; + + case( GDI_PEN_ACTION ): + { + sal_Int32 nPenWidth; + sal_Int16 nPenStyle; + + ImplReadColor( rIStm, aActionColor ); + rIStm >> nPenWidth >> nPenStyle; + + aLineInfo.SetStyle( nPenStyle ? LINE_SOLID : LINE_NONE ); + aLineInfo.SetWidth( nPenWidth ); + bFatLine = nPenStyle && !aLineInfo.IsDefault(); + + rMtf.AddAction( new MetaLineColorAction( aActionColor, nPenStyle != 0 ) ); + } + break; + + case( GDI_FILLBRUSH_ACTION ): + { + sal_Int16 nBrushStyle; + + ImplReadColor( rIStm, aActionColor ); + rIStm.SeekRel( 6L ); + rIStm >> nBrushStyle; + rMtf.AddAction( new MetaFillColorAction( aActionColor, nBrushStyle != 0 ) ); + rIStm.SeekRel( 2L ); + } + break; + + case( GDI_MAPMODE_ACTION ): + { + ImplReadMapMode( rIStm, aMapMode ); + rMtf.AddAction( new MetaMapModeAction( aMapMode ) ); + + // #106172# Track font relevant data in shadow VDev + aFontVDev.SetMapMode( aMapMode ); + } + break; + + case( GDI_CLIPREGION_ACTION ): + { + Region aRegion; + sal_Int16 nRegType; + sal_Int16 bIntersect; + sal_Bool bClip = sal_False; + + rIStm >> nRegType >> bIntersect; + ImplReadRect( rIStm, aRect ); + + switch( nRegType ) + { + case( 0 ): + break; + + case( 1 ): + { + Rectangle aRegRect; + + ImplReadRect( rIStm, aRegRect ); + aRegion = Region( aRegRect ); + bClip = sal_True; + } + break; + + case( 2 ): + { + ImplReadPoly( rIStm, aActionPoly ); + aRegion = Region( aActionPoly ); + bClip = sal_True; + } + break; + + case( 3 ): + { + PolyPolygon aPolyPoly; + sal_Int32 nPolyCount; + + rIStm >> nPolyCount; + + for( sal_uInt16 j = 0; j < (sal_uInt16) nPolyCount; j++ ) + { + ImplReadPoly( rIStm, aActionPoly ); + aPolyPoly.Insert( aActionPoly ); + } + + aRegion = Region( aPolyPoly ); + bClip = sal_True; + } + break; + } + + if( bIntersect ) + aRegion.Intersect( aRect ); + + rMtf.AddAction( new MetaClipRegionAction( aRegion, bClip ) ); + } + break; + + case( GDI_MOVECLIPREGION_ACTION ): + { + rIStm >> nTmp >> nTmp1; + rMtf.AddAction( new MetaMoveClipRegionAction( nTmp, nTmp1 ) ); + } + break; + + case( GDI_ISECTCLIPREGION_ACTION ): + { + ImplReadRect( rIStm, aRect ); + rMtf.AddAction( new MetaISectRectClipRegionAction( aRect ) ); + } + break; + + case( GDI_RASTEROP_ACTION ): + { + RasterOp eRasterOp; + sal_Int16 nRasterOp; + + rIStm >> nRasterOp; + + switch( nRasterOp ) + { + case( 1 ): + eRasterOp = ROP_INVERT; + break; + + case( 4 ): + case( 5 ): + eRasterOp = ROP_XOR; + break; + + default: + eRasterOp = ROP_OVERPAINT; + break; + } + + rMtf.AddAction( new MetaRasterOpAction( eRasterOp ) ); + } + break; + + case( GDI_PUSH_ACTION ): + { + aLIStack.Push( new LineInfo( aLineInfo ) ); + rMtf.AddAction( new MetaPushAction( PUSH_ALL ) ); + + // #106172# Track font relevant data in shadow VDev + aFontVDev.Push(); + } + break; + + case( GDI_POP_ACTION ): + { + + LineInfo* pLineInfo = (LineInfo*) aLIStack.Pop(); + + // restore line info + if( pLineInfo ) + { + aLineInfo = *pLineInfo; + delete pLineInfo; + bFatLine = ( LINE_NONE != aLineInfo.GetStyle() ) && !aLineInfo.IsDefault(); + } + + rMtf.AddAction( new MetaPopAction() ); + + // #106172# Track font relevant data in shadow VDev + aFontVDev.Pop(); + } + break; + + case( GDI_GRADIENT_ACTION ): + { + Color aStartCol; + Color aEndCol; + sal_Int16 nStyle; + sal_Int16 nAngle; + sal_Int16 nBorder; + sal_Int16 nOfsX; + sal_Int16 nOfsY; + sal_Int16 nIntensityStart; + sal_Int16 nIntensityEnd; + + ImplReadRect( rIStm, aRect ); + rIStm >> nStyle; + ImplReadColor( rIStm, aStartCol ); + ImplReadColor( rIStm, aEndCol ); + rIStm >> nAngle >> nBorder >> nOfsX >> nOfsY >> nIntensityStart >> nIntensityEnd; + + Gradient aGrad( (GradientStyle) nStyle, aStartCol, aEndCol ); + + aGrad.SetAngle( nAngle ); + aGrad.SetBorder( nBorder ); + aGrad.SetOfsX( nOfsX ); + aGrad.SetOfsY( nOfsY ); + aGrad.SetStartIntensity( nIntensityStart ); + aGrad.SetEndIntensity( nIntensityEnd ); + rMtf.AddAction( new MetaGradientAction( aRect, aGrad ) ); + } + break; + + case( GDI_TRANSPARENT_COMMENT ): + { + PolyPolygon aPolyPoly; + sal_Int32 nFollowingActionCount; + sal_Int16 nTrans; + + rIStm >> aPolyPoly >> nTrans >> nFollowingActionCount; + ImplSkipActions( rIStm, nFollowingActionCount ); + rMtf.AddAction( new MetaTransparentAction( aPolyPoly, nTrans ) ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + i += nFollowingActionCount; +#endif + } + break; + + case( GDI_FLOATTRANSPARENT_COMMENT ): + { + GDIMetaFile aMtf; + Point aPos; + Size aSize; + Gradient aGradient; + sal_Int32 nFollowingActionCount; + + rIStm >> aMtf >> aPos >> aSize >> aGradient >> nFollowingActionCount; + ImplSkipActions( rIStm, nFollowingActionCount ); + rMtf.AddAction( new MetaFloatTransparentAction( aMtf, aPos, aSize, aGradient ) ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + i += nFollowingActionCount; +#endif + } + break; + + case( GDI_HATCH_COMMENT ): + { + PolyPolygon aPolyPoly; + Hatch aHatch; + sal_Int32 nFollowingActionCount; + + rIStm >> aPolyPoly >> aHatch >> nFollowingActionCount; + ImplSkipActions( rIStm, nFollowingActionCount ); + rMtf.AddAction( new MetaHatchAction( aPolyPoly, aHatch ) ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + i += nFollowingActionCount; +#endif + } + break; + + case( GDI_REFPOINT_COMMENT ): + { + Point aRefPoint; + sal_Bool bSet; + sal_Int32 nFollowingActionCount; + + rIStm >> aRefPoint >> bSet >> nFollowingActionCount; + ImplSkipActions( rIStm, nFollowingActionCount ); + rMtf.AddAction( new MetaRefPointAction( aRefPoint, bSet ) ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + i += nFollowingActionCount; +#endif + + // #106172# Track font relevant data in shadow VDev + if( bSet ) + aFontVDev.SetRefPoint( aRefPoint ); + else + aFontVDev.SetRefPoint(); + } + break; + + case( GDI_TEXTLINECOLOR_COMMENT ): + { + Color aColor; + sal_Bool bSet; + sal_Int32 nFollowingActionCount; + + rIStm >> aColor >> bSet >> nFollowingActionCount; + ImplSkipActions( rIStm, nFollowingActionCount ); + rMtf.AddAction( new MetaTextLineColorAction( aColor, bSet ) ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + i += nFollowingActionCount; +#endif + } + break; + + case( GDI_TEXTLINE_COMMENT ): + { + Point aStartPt; + long nWidth; + sal_uInt32 nStrikeout; + sal_uInt32 nUnderline; + sal_Int32 nFollowingActionCount; + + rIStm >> aStartPt >> nWidth >> nStrikeout >> nUnderline >> nFollowingActionCount; + ImplSkipActions( rIStm, nFollowingActionCount ); + rMtf.AddAction( new MetaTextLineAction( aStartPt, nWidth, + (FontStrikeout) nStrikeout, + (FontUnderline) nUnderline, + UNDERLINE_NONE ) ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + i += nFollowingActionCount; +#endif + } + break; + + case( GDI_GRADIENTEX_COMMENT ): + { + PolyPolygon aPolyPoly; + Gradient aGradient; + sal_Int32 nFollowingActionCount; + + rIStm >> aPolyPoly >> aGradient >> nFollowingActionCount; + ImplSkipActions( rIStm, nFollowingActionCount ); + rMtf.AddAction( new MetaGradientExAction( aPolyPoly, aGradient ) ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + i += nFollowingActionCount; +#endif + } + break; + + case( GDI_COMMENT_COMMENT ): + { + ByteString aComment; + sal_Int32 nValue; + sal_uInt32 nDataSize; + sal_uInt8* pData; + sal_Int32 nFollowingActionCount; + + rIStm >> aComment >> nValue >> nDataSize; + + if( nDataSize ) + { + pData = new sal_uInt8[ nDataSize ]; + rIStm.Read( pData, nDataSize ); + } + else + pData = NULL; + + rIStm >> nFollowingActionCount; + ImplSkipActions( rIStm, nFollowingActionCount ); + rMtf.AddAction( new MetaCommentAction( aComment, nValue, pData, nDataSize ) ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + i += nFollowingActionCount; +#endif + } + break; + + case ( GDI_UNICODE_COMMENT ): + { + nUnicodeCommentActionNumber = i + 1; + nUnicodeCommentStreamPos = rIStm.Tell() - 6; + rIStm.SeekRel( nActionSize - 4 ); + } + break; + + default: + rIStm.SeekRel( nActionSize - 4L ); + break; + } + } + + // cleanup push-pop stack if neccessary + for( void* pLineInfo = aLIStack.Pop(); pLineInfo; pLineInfo = aLIStack.Pop() ) + delete (LineInfo*) pLineInfo; + + rIStm.SetNumberFormatInt( nOldFormat ); +} + +// ------------------------------------------------------------------------ + +void SVMConverter::ImplConvertToSVM1( SvStream& rOStm, GDIMetaFile& rMtf ) +{ + sal_uLong nCountPos; + Font aSaveFont; + const sal_uInt16 nOldFormat = rOStm.GetNumberFormatInt(); + rtl_TextEncoding eActualCharSet = gsl_getSystemTextEncoding(); + const Size aPrefSize( rMtf.GetPrefSize() ); + sal_Bool bRop_0_1 = sal_False; + VirtualDevice aSaveVDev; + Color aLineCol( COL_BLACK ); + Stack aLineColStack; + + rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + //MagicCode schreiben + rOStm << "SVGDI"; // Kennung + rOStm << (sal_Int16) 42; // HeaderSize + rOStm << (sal_Int16) 200; // VERSION + rOStm << (sal_Int32) aPrefSize.Width(); + rOStm << (sal_Int32) aPrefSize.Height(); + ImplWriteMapMode( rOStm, rMtf.GetPrefMapMode() ); + + // ActionCount wird spaeter geschrieben + nCountPos = rOStm.Tell(); + rOStm.SeekRel( 4L ); + + const sal_Int32 nActCount = ImplWriteActions( rOStm, rMtf, aSaveVDev, bRop_0_1, aLineCol, aLineColStack, eActualCharSet ); + const sal_uLong nActPos = rOStm.Tell(); + + rOStm.Seek( nCountPos ); + rOStm << nActCount; + rOStm.Seek( nActPos ); + rOStm.SetNumberFormatInt( nOldFormat ); + + // cleanup push-pop stack if neccessary + for( void* pCol = aLineColStack.Pop(); pCol; pCol = aLineColStack.Pop() ) + delete (Color*) pCol; +} + +// ------------------------------------------------------------------------ + +sal_uLong SVMConverter::ImplWriteActions( SvStream& rOStm, GDIMetaFile& rMtf, + VirtualDevice& rSaveVDev, sal_Bool& rRop_0_1, + Color& rLineCol, Stack& rLineColStack, + rtl_TextEncoding& rActualCharSet ) +{ + sal_uLong nCount = 0; + for( sal_uLong i = 0, nActionCount = rMtf.GetActionCount(); i < nActionCount; i++ ) + { + const MetaAction* pAction = rMtf.GetAction( i ); + + switch( pAction->GetType() ) + { + case( META_PIXEL_ACTION ): + { + MetaPixelAction* pAct = (MetaPixelAction*) pAction; + + rOStm << (sal_Int16) GDI_PIXEL_ACTION; + rOStm << (sal_Int32) 18; + rOStm << pAct->GetPoint(); + ImplWriteColor( rOStm, pAct->GetColor() ); + nCount++; + } + break; + + case( META_POINT_ACTION ): + { + MetaPointAction* pAct = (MetaPointAction*) pAction; + + rOStm << (sal_Int16) GDI_POINT_ACTION; + rOStm << (sal_Int32) 12; + rOStm << pAct->GetPoint(); + nCount++; + } + break; + + case( META_LINE_ACTION ): + { + MetaLineAction* pAct = (MetaLineAction*) pAction; + const LineInfo& rInfo = pAct->GetLineInfo(); + const bool bFatLine(!rInfo.IsDefault() && (LINE_NONE != rInfo.GetStyle())); + const bool bLineJoin(bFatLine && basegfx::B2DLINEJOIN_ROUND != rInfo.GetLineJoin()); + const bool bLineDashDot(LINE_DASH == rInfo.GetStyle()); + + if( bFatLine ) + { + ImplWritePushAction( rOStm ); + ImplWriteLineColor( rOStm, rLineCol, 1, rInfo.GetWidth() ); + + if(bLineJoin) + { + rOStm << (sal_Int16) GDI_LINEJOIN_ACTION; + rOStm << (sal_Int32) 6; + rOStm << (sal_Int16) rInfo.GetLineJoin(); + } + + if(bLineDashDot) + { + rOStm << (sal_Int16) GDI_LINEDASHDOT_ACTION; + rOStm << (sal_Int32) 4 + 16; + rOStm << (sal_Int16)rInfo.GetDashCount(); + rOStm << (sal_Int32)rInfo.GetDashLen(); + rOStm << (sal_Int16)rInfo.GetDotCount(); + rOStm << (sal_Int32)rInfo.GetDotLen(); + rOStm << (sal_Int32)rInfo.GetDistance(); + } + } + + rOStm << (sal_Int16) GDI_LINE_ACTION; + rOStm << (sal_Int32) 20; + rOStm << pAct->GetStartPoint(); + rOStm << pAct->GetEndPoint(); + nCount++; + + if( bFatLine ) + { + ImplWritePopAction( rOStm ); + nCount += 3; + + if(bLineJoin) + { + nCount += 1; + } + + if(bLineDashDot) + { + nCount += 1; + } + } + } + break; + + case( META_RECT_ACTION ): + { + MetaRectAction* pAct = (MetaRectAction*) pAction; + + rOStm << (sal_Int16) GDI_RECT_ACTION; + rOStm << (sal_Int32) 28; + ImplWriteRect( rOStm, pAct->GetRect() ); + rOStm << (sal_Int32) 0; + rOStm << (sal_Int32) 0; + nCount++; + } + break; + + case( META_ROUNDRECT_ACTION ): + { + MetaRoundRectAction* pAct = (MetaRoundRectAction*) pAction; + + rOStm << (sal_Int16) GDI_RECT_ACTION; + rOStm << (sal_Int32) 28; + ImplWriteRect( rOStm, pAct->GetRect() ); + rOStm << (sal_Int32) pAct->GetHorzRound(); + rOStm << (sal_Int32) pAct->GetVertRound(); + nCount++; + } + break; + + case( META_ELLIPSE_ACTION ): + { + MetaEllipseAction* pAct = (MetaEllipseAction*) pAction; + + rOStm << (sal_Int16) GDI_ELLIPSE_ACTION; + rOStm << (sal_Int32) 20; + ImplWriteRect( rOStm, pAct->GetRect() ); + nCount++; + } + break; + + case( META_ARC_ACTION ): + { + MetaArcAction* pAct = (MetaArcAction*) pAction; + + rOStm << (sal_Int16) GDI_ARC_ACTION; + rOStm << (sal_Int32) 36; + ImplWriteRect( rOStm, pAct->GetRect() ); + rOStm << pAct->GetStartPoint(); + rOStm << pAct->GetEndPoint(); + nCount++; + } + break; + + case( META_PIE_ACTION ): + { + MetaPieAction* pAct = (MetaPieAction*) pAction; + + rOStm << (sal_Int16) GDI_PIE_ACTION; + rOStm << (sal_Int32) 36; + ImplWriteRect( rOStm, pAct->GetRect() ); + rOStm << pAct->GetStartPoint(); + rOStm << pAct->GetEndPoint(); + nCount++; + } + break; + + case( META_CHORD_ACTION ): + { + MetaChordAction* pAct = (MetaChordAction*) pAction; + Polygon aChordPoly( pAct->GetRect(), pAct->GetStartPoint(), + pAct->GetEndPoint(), POLY_CHORD ); + const sal_uInt16 nPoints = aChordPoly.GetSize(); + + rOStm << (sal_Int16) GDI_POLYGON_ACTION; + rOStm << (sal_Int32) ( 8 + ( nPoints << 3 ) ); + rOStm << (sal_Int32) nPoints; + + for( sal_uInt16 n = 0; n < nPoints; n++ ) + rOStm << aChordPoly[ n ]; + nCount++; + } + break; + + case( META_POLYLINE_ACTION ): + { + // #i102224# + MetaPolyLineAction* pAct = (MetaPolyLineAction*) pAction; + // #i102224# Here the evtl. curved nature of Polygon was + // ignored (for all those Years). Adapted to at least write + // a polygon representing the curve as good as possible + Polygon aSimplePoly; + pAct->GetPolygon().AdaptiveSubdivide(aSimplePoly); + const LineInfo& rInfo = pAct->GetLineInfo(); + const sal_uInt16 nPoints(aSimplePoly.GetSize()); + const bool bFatLine(!rInfo.IsDefault() && (LINE_NONE != rInfo.GetStyle())); + const bool bLineJoin(bFatLine && basegfx::B2DLINEJOIN_ROUND != rInfo.GetLineJoin()); + const bool bLineDashDot(LINE_DASH == rInfo.GetStyle()); + + if( bFatLine ) + { + ImplWritePushAction( rOStm ); + ImplWriteLineColor( rOStm, rLineCol, 1, rInfo.GetWidth() ); + + if(bLineJoin) + { + rOStm << (sal_Int16) GDI_LINEJOIN_ACTION; + rOStm << (sal_Int32) 6; + rOStm << (sal_Int16) rInfo.GetLineJoin(); + } + } + + if(bLineDashDot) + { + rOStm << (sal_Int16) GDI_LINEDASHDOT_ACTION; + rOStm << (sal_Int32) 4 + 16; + rOStm << (sal_Int16)rInfo.GetDashCount(); + rOStm << (sal_Int32)rInfo.GetDashLen(); + rOStm << (sal_Int16)rInfo.GetDotCount(); + rOStm << (sal_Int32)rInfo.GetDotLen(); + rOStm << (sal_Int32)rInfo.GetDistance(); + } + + rOStm << (sal_Int16) GDI_POLYLINE_ACTION; + rOStm << (sal_Int32) ( 8 + ( nPoints << 3 ) ); + rOStm << (sal_Int32) nPoints; + + for( sal_uInt16 n = 0; n < nPoints; n++ ) + { + rOStm << aSimplePoly[ n ]; + } + + nCount++; + + const PolyPolygon aPolyPolygon(pAct->GetPolygon()); + if(ImplWriteExtendedPolyPolygonAction(rOStm, aPolyPolygon, true)) + { + nCount++; + } + + if( bFatLine ) + { + ImplWritePopAction( rOStm ); + nCount += 3; + + if(bLineJoin) + { + nCount += 1; + } + } + + if(bLineDashDot) + { + nCount += 1; + } + } + break; + + case( META_POLYGON_ACTION ): + { + MetaPolygonAction* pAct = (MetaPolygonAction*)pAction; + // #i102224# Here the evtl. curved nature of Polygon was + // ignored (for all those Years). Adapted to at least write + // a polygon representing the curve as good as possible + Polygon aSimplePoly; + pAct->GetPolygon().AdaptiveSubdivide(aSimplePoly); + const sal_uInt16 nPoints(aSimplePoly.GetSize()); + + rOStm << (sal_Int16) GDI_POLYGON_ACTION; + rOStm << (sal_Int32) ( 8 + ( nPoints << 3 ) ); + rOStm << (sal_Int32) nPoints; + + for( sal_uInt16 n = 0; n < nPoints; n++ ) + rOStm << aSimplePoly[ n ]; + + nCount++; + + const PolyPolygon aPolyPolygon(pAct->GetPolygon()); + if(ImplWriteExtendedPolyPolygonAction(rOStm, aPolyPolygon, true)) + { + nCount++; + } + } + break; + + case( META_POLYPOLYGON_ACTION ): + { + MetaPolyPolygonAction* pAct = (MetaPolyPolygonAction*) pAction; + ImplWritePolyPolyAction( rOStm, pAct->GetPolyPolygon() ); + nCount++; + + if(ImplWriteExtendedPolyPolygonAction(rOStm, pAct->GetPolyPolygon(), true)) + { + nCount++; + } + } + break; + + case( META_TEXT_ACTION ): + { + MetaTextAction* pAct = (MetaTextAction*) pAction; + String aUniText( pAct->GetText() ); + ByteString aText( aUniText, rActualCharSet ); + const sal_uLong nStrLen = aText.Len(); + + if ( ImplWriteUnicodeComment( rOStm, aUniText ) ) + nCount++; + + rOStm << (sal_Int16) GDI_TEXT_ACTION; + rOStm << (sal_Int32) ( 24 + ( nStrLen + 1 ) ); + rOStm << pAct->GetPoint(); + rOStm << (sal_Int32) pAct->GetIndex(); + rOStm << (sal_Int32) pAct->GetLen(); + rOStm << (sal_Int32) nStrLen; + rOStm.Write( aText.GetBuffer(), nStrLen + 1 ); + nCount++; + } + break; + + case( META_TEXTARRAY_ACTION ): + { + MetaTextArrayAction* pAct = (MetaTextArrayAction*)pAction; + ByteString aText( pAct->GetText(), rActualCharSet ); + String aUniText( pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ); + sal_uLong nAryLen; + sal_uLong nLen = pAct->GetLen(); + const sal_uLong nTextLen = aText.Len(); + sal_Int32* pDXArray = pAct->GetDXArray(); + + if ( ImplWriteUnicodeComment( rOStm, aUniText ) ) + nCount++; + + if( ( nLen + pAct->GetIndex() ) > nTextLen ) + { + if( pAct->GetIndex() <= nTextLen ) + nLen = nTextLen - pAct->GetIndex(); + else + nLen = 0UL; + } + + if( !pDXArray || !nLen ) + nAryLen = 0; + else + nAryLen = nLen; // #105987# Write out all of DX array + + rOStm << (sal_Int16) GDI_TEXTARRAY_ACTION; + rOStm << (sal_Int32) ( 28 + ( nLen + 1 ) + ( nAryLen * 4 ) ); + rOStm << pAct->GetPoint(); + rOStm << (sal_Int32) 0; + rOStm << (sal_Int32) nLen; + rOStm << (sal_Int32) nLen; + rOStm << (sal_Int32) nAryLen; + rOStm.Write( aText.GetBuffer()+pAct->GetIndex(), nLen + 1 ); + + for( sal_uLong n = 0UL ; n < nAryLen; n++ ) + rOStm << (sal_Int32) pDXArray[ n ]; + + nCount++; + } + break; + + case( META_STRETCHTEXT_ACTION ): + { + MetaStretchTextAction* pAct = (MetaStretchTextAction*) pAction; + String aUniText( pAct->GetText() ); + ByteString aText( aUniText, rActualCharSet ); + const sal_uLong nStrLen = aText.Len(); + + if ( ImplWriteUnicodeComment( rOStm, aUniText ) ) + nCount++; + + rOStm << (sal_Int16) GDI_STRETCHTEXT_ACTION; + rOStm << (sal_Int32) ( 28 + ( nStrLen + 1 ) ); + rOStm << pAct->GetPoint(); + rOStm << (sal_Int32) pAct->GetIndex(); + rOStm << (sal_Int32) pAct->GetLen(); + rOStm << (sal_Int32) nStrLen; + rOStm << (sal_Int32) pAct->GetWidth(); + rOStm.Write( aText.GetBuffer(), nStrLen + 1 ); + nCount++; + } + break; + + case( META_BMP_ACTION ): + { + MetaBmpAction* pAct = (MetaBmpAction*) pAction; + + rOStm << (sal_Int16) GDI_BITMAP_ACTION; + rOStm << (sal_Int32) 12; + rOStm << pAct->GetPoint(); + rOStm << pAct->GetBitmap(); + nCount++; + } + break; + + case( META_BMPSCALE_ACTION ): + { + MetaBmpScaleAction* pAct = (MetaBmpScaleAction*) pAction; + + rOStm << (sal_Int16) GDI_BITMAPSCALE_ACTION; + rOStm << (sal_Int32) 20; + rOStm << pAct->GetPoint(); + rOStm << pAct->GetSize(); + rOStm << pAct->GetBitmap(); + nCount++; + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + MetaBmpScalePartAction* pAct = (MetaBmpScalePartAction*) pAction; + + rOStm << (sal_Int16) GDI_BITMAPSCALEPART_ACTION; + rOStm << (sal_Int32) 36; + rOStm << pAct->GetDestPoint(); + rOStm << pAct->GetDestSize(); + rOStm << pAct->GetSrcPoint(); + rOStm << pAct->GetSrcSize(); + rOStm << pAct->GetBitmap(); + nCount++; + } + break; + + case( META_BMPEX_ACTION ): + { + MetaBmpExAction* pAct = (MetaBmpExAction*) pAction; + const Bitmap aBmp( Graphic( pAct->GetBitmapEx() ).GetBitmap() ); + + rOStm << (sal_Int16) GDI_BITMAP_ACTION; + rOStm << (sal_Int32) 12; + rOStm << pAct->GetPoint(); + rOStm << aBmp; + nCount++; + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + MetaBmpExScaleAction* pAct = (MetaBmpExScaleAction*) pAction; + const Bitmap aBmp( Graphic( pAct->GetBitmapEx() ).GetBitmap() ); + + rOStm << (sal_Int16) GDI_BITMAPSCALE_ACTION; + rOStm << (sal_Int32) 20; + rOStm << pAct->GetPoint(); + rOStm << pAct->GetSize(); + rOStm << aBmp; + nCount++; + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + MetaBmpExScalePartAction* pAct = (MetaBmpExScalePartAction*) pAction; + const Bitmap aBmp( Graphic( pAct->GetBitmapEx() ).GetBitmap() ); + + rOStm << (sal_Int16) GDI_BITMAPSCALEPART_ACTION; + rOStm << (sal_Int32) 36; + rOStm << pAct->GetDestPoint(); + rOStm << pAct->GetDestSize(); + rOStm << pAct->GetSrcPoint(); + rOStm << pAct->GetSrcSize(); + rOStm << aBmp; + nCount++; + } + break; + + case( META_GRADIENT_ACTION ): + { + MetaGradientAction* pAct = (MetaGradientAction*) pAction; + const Gradient& rGrad = pAct->GetGradient(); + + rOStm << (sal_Int16) GDI_GRADIENT_ACTION; + rOStm << (sal_Int32) 46; + ImplWriteRect( rOStm, pAct->GetRect() ); + rOStm << (sal_Int16) rGrad.GetStyle(); + ImplWriteColor( rOStm, rGrad.GetStartColor() ); + ImplWriteColor( rOStm, rGrad.GetEndColor() ); + rOStm << (sal_Int16) rGrad.GetAngle(); + rOStm << (sal_Int16) rGrad.GetBorder(); + rOStm << (sal_Int16) rGrad.GetOfsX(); + rOStm << (sal_Int16) rGrad.GetOfsY(); + rOStm << (sal_Int16) rGrad.GetStartIntensity(); + rOStm << (sal_Int16) rGrad.GetEndIntensity(); + nCount++; + } + break; + + case( META_GRADIENTEX_ACTION ): + { + const MetaGradientExAction* pA = (MetaGradientExAction*) pAction; + sal_uLong nOldPos, nNewPos; + + // write RefPoint comment + rOStm << (sal_Int16) GDI_GRADIENTEX_COMMENT; + + // we'll write the ActionSize later + nOldPos = rOStm.Tell(); + rOStm.SeekRel( 4 ); + + // write data + rOStm << pA->GetPolyPolygon() << pA->GetGradient(); + rOStm << (sal_Int32) 0; // number of actions that follow this comment + + // calculate and write ActionSize of comment + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) ( nNewPos - nOldPos ); + rOStm.Seek( nNewPos ); + + nCount++; + } + break; + + case( META_WALLPAPER_ACTION ): + { + MetaWallpaperAction* pAct = (MetaWallpaperAction*) pAction; + const Color& rColor = pAct->GetWallpaper().GetColor(); + + ImplWritePushAction( rOStm ); + ImplWriteLineColor( rOStm, rColor, 1 ); + ImplWriteFillColor( rOStm, rColor, 1 ); + + rOStm << (sal_Int16) GDI_RECT_ACTION; + rOStm << (sal_Int32) 28; + ImplWriteRect( rOStm, pAct->GetRect() ); + rOStm << (sal_Int32) 0; + rOStm << (sal_Int32) 0; + + ImplWritePopAction( rOStm ); + nCount += 5; + } + break; + + case( META_CLIPREGION_ACTION ): + { + MetaClipRegionAction* pAct = (MetaClipRegionAction*) pAction; + const Region& rRegion = pAct->GetRegion(); + Rectangle aClipRect; + + rOStm << (sal_Int16) GDI_CLIPREGION_ACTION; + rOStm << (sal_Int32) 24; + + if( pAct->IsClipping() ) + { + aClipRect = rRegion.GetBoundRect(); + rOStm << (sal_Int16) 1; + } + else + rOStm << (sal_Int16) 0; + + rOStm << (sal_Int16) 0; + ImplWriteRect( rOStm, aClipRect ); + + if( pAct->IsClipping() ) + ImplWriteRect( rOStm, aClipRect ); + + nCount++; + } + break; + + case( META_ISECTRECTCLIPREGION_ACTION ): + { + MetaISectRectClipRegionAction* pAct = (MetaISectRectClipRegionAction*) pAction; + + rOStm << (sal_Int16) GDI_ISECTCLIPREGION_ACTION; + rOStm << (sal_Int32) 20; + rOStm << pAct->GetRect(); + nCount++; + } + break; + + case( META_MOVECLIPREGION_ACTION ): + { + MetaMoveClipRegionAction* pAct = (MetaMoveClipRegionAction*) pAction; + + rOStm << (sal_Int16) GDI_MOVECLIPREGION_ACTION; + rOStm << (sal_Int32) 12; + rOStm << (sal_Int32) pAct->GetHorzMove(); + rOStm << (sal_Int32) pAct->GetVertMove(); + nCount++; + } + break; + + case( META_LINECOLOR_ACTION ): + { + MetaLineColorAction* pAct = (MetaLineColorAction*) pAction; + ImplWriteLineColor( rOStm, rLineCol = pAct->GetColor(), pAct->IsSetting() ? 1 : 0 ); + nCount++; + } + break; + + case( META_FILLCOLOR_ACTION ): + { + MetaFillColorAction* pAct = (MetaFillColorAction*) pAction; + ImplWriteFillColor( rOStm, pAct->GetColor(), pAct->IsSetting() ? 1 : 0 ); + nCount++; + } + break; + + case( META_FONT_ACTION ): + { + rSaveVDev.SetFont( ( (MetaFontAction*) pAction )->GetFont() ); + ImplWriteFont( rOStm, rSaveVDev.GetFont(), rActualCharSet ); + nCount++; + } + break; + + case( META_TEXTCOLOR_ACTION ): + { + Font aSaveFont( rSaveVDev.GetFont() ); + + aSaveFont.SetColor( ( (MetaTextColorAction*) pAction )->GetColor() ); + rSaveVDev.SetFont( aSaveFont ); + ImplWriteFont( rOStm, rSaveVDev.GetFont(), rActualCharSet ); + nCount++; + } + break; + + case( META_TEXTFILLCOLOR_ACTION ): + { + MetaTextFillColorAction* pAct = (MetaTextFillColorAction*) pAction; + Font aSaveFont( rSaveVDev.GetFont() ); + + if( pAct->IsSetting() ) + aSaveFont.SetFillColor( pAct->GetColor() ); + else + aSaveFont.SetFillColor( Color( COL_TRANSPARENT ) ); + + rSaveVDev.SetFont( aSaveFont ); + ImplWriteFont( rOStm, rSaveVDev.GetFont(), rActualCharSet ); + nCount++; + } + break; + + case( META_TEXTALIGN_ACTION ): + { + Font aSaveFont( rSaveVDev.GetFont() ); + + aSaveFont.SetAlign( ( (MetaTextAlignAction*) pAction )->GetTextAlign() ); + rSaveVDev.SetFont( aSaveFont ); + ImplWriteFont( rOStm, rSaveVDev.GetFont(), rActualCharSet ); + nCount++; + } + break; + + case( META_MAPMODE_ACTION ): + { + MetaMapModeAction* pAct = (MetaMapModeAction*) pAction; + + rOStm << (sal_Int16) GDI_MAPMODE_ACTION; + rOStm << (sal_Int32) 30; + ImplWriteMapMode( rOStm, pAct->GetMapMode() ); + nCount++; + } + break; + + case( META_PUSH_ACTION ): + { + ImplWritePushAction( rOStm ); + rLineColStack.Push( new Color( rLineCol ) ); + rSaveVDev.Push(); + nCount++; + } + break; + + case( META_POP_ACTION ): + { + Color* pCol = (Color*) rLineColStack.Pop(); + + if( pCol ) + { + rLineCol = *pCol; + delete pCol; + } + + ImplWritePopAction( rOStm ); + rSaveVDev.Pop(); + nCount++; + } + break; + + case( META_RASTEROP_ACTION ): + { + MetaRasterOpAction* pAct = (MetaRasterOpAction*) pAction; + + if( ( pAct->GetRasterOp() != ROP_0 ) && ( pAct->GetRasterOp() != ROP_1 ) ) + { + sal_Int16 nRasterOp; + + // Falls vorher ROP_0/1 gesetzt war, alten + // Zustand durch Pop erst wieder herstellen + if( rRop_0_1 ) + { + ImplWritePopAction( rOStm ); + rSaveVDev.Pop(); + rRop_0_1 = sal_False; + nCount++; + } + + switch( pAct->GetRasterOp() ) + { + case( ROP_OVERPAINT ) : nRasterOp = 0; break; + case( ROP_XOR ) : nRasterOp = 4; break; + case( ROP_INVERT ): nRasterOp = 1; break; + default: nRasterOp = 0; break; + } + + ImplWriteRasterOpAction( rOStm, nRasterOp ); + nCount++; + } + else + { + ImplWritePushAction( rOStm ); + rSaveVDev.Push(); + + if( pAct->GetRasterOp() == ROP_0 ) + { + ImplWriteLineColor( rOStm, COL_BLACK, 1 ); + ImplWriteFillColor( rOStm, COL_BLACK, 1 ); + } + else + { + ImplWriteLineColor( rOStm, COL_WHITE, 1 ); + ImplWriteFillColor( rOStm, COL_WHITE, 1 ); + } + + ImplWriteRasterOpAction( rOStm, 0 ); + rRop_0_1 = sal_True; + nCount += 4; + } + } + break; + + case( META_TRANSPARENT_ACTION ): + { + const PolyPolygon& rPolyPoly = ( (MetaTransparentAction*) pAction )->GetPolyPolygon(); + const sal_Int16 nTrans = ( (MetaTransparentAction*) pAction )->GetTransparence(); + const sal_Int16 nBrushStyle = ( nTrans < 38 ) ? 8 : ( nTrans < 63 ) ? 9 : 10; + sal_uLong nOldPos, nNewPos; + + // write transparence comment + rOStm << (sal_Int16) GDI_TRANSPARENT_COMMENT; + + // we'll write the ActionSize later + nOldPos = rOStm.Tell(); + rOStm.SeekRel( 4 ); + + // write comment data + rOStm << rPolyPoly; + rOStm << nTrans; + rOStm << (sal_Int32) 15; // number of actions that follow this comment + + // calculate and write ActionSize of comment + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) ( nNewPos - nOldPos ); + rOStm.Seek( nNewPos ); + + { + // write actions for transparence + ImplWritePushAction( rOStm ); + { + ImplWriteRasterOpAction( rOStm, 4 ); + ImplWritePolyPolyAction( rOStm, rPolyPoly ); + + ImplWritePushAction( rOStm ); + { + ImplWriteRasterOpAction( rOStm, 2 ); + ImplWriteFillColor( rOStm, COL_BLACK, nBrushStyle ); + ImplWritePolyPolyAction( rOStm, rPolyPoly ); + } + ImplWritePopAction( rOStm ); + + ImplWriteRasterOpAction( rOStm, 4 ); + ImplWritePolyPolyAction( rOStm, rPolyPoly ); + } + ImplWritePopAction( rOStm ); + + ImplWritePushAction( rOStm ); + { + ImplWriteFillColor( rOStm, Color(), 0 ); + ImplWritePolyPolyAction( rOStm, rPolyPoly ); + } + ImplWritePopAction( rOStm ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + nCount += 15; +#endif + } + + nCount++; + } + break; + + case( META_FLOATTRANSPARENT_ACTION ): + { + const MetaFloatTransparentAction* pA = (MetaFloatTransparentAction*) pAction; + const GDIMetaFile& rTransMtf = pA->GetGDIMetaFile(); + const Point& rPos = pA->GetPoint(); + const Size& rSize = pA->GetSize(); + const Gradient& rGradient = pA->GetGradient(); + sal_uLong nOldPos, nNewPos; + + // write RefPoint comment + rOStm << (sal_Int16) GDI_FLOATTRANSPARENT_COMMENT; + + // we'll write the ActionSize later + nOldPos = rOStm.Tell(); + rOStm.SeekRel( 4 ); + + // write comment data + rOStm << rTransMtf << rPos << rSize << rGradient; + + // calculate and write ActionSize of comment + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) ( nNewPos - nOldPos + 4 ); + rOStm.Seek( ( nOldPos = nNewPos ) + 4 ); + + { + // write actions for float transparence + sal_uLong nAddCount; + GDIMetaFile aMtf( rTransMtf ); + const Size aSrcSize( rTransMtf.GetPrefSize() ); + Point aSrcPt( rTransMtf.GetPrefMapMode().GetOrigin() ); + const double fScaleX = aSrcSize.Width() ? (double) rSize.Width() / aSrcSize.Width() : 1.0; + const double fScaleY = aSrcSize.Height() ? (double) rSize.Height() / aSrcSize.Height() : 1.0; + long nMoveX, nMoveY; + + if( fScaleX != 1.0 || fScaleY != 1.0 ) + { + aMtf.Scale( fScaleX, fScaleY ); + aSrcPt.X() = FRound( aSrcPt.X() * fScaleX ), aSrcPt.Y() = FRound( aSrcPt.Y() * fScaleY ); + } + + nMoveX = rPos.X() - aSrcPt.X(), nMoveY = rPos.Y() - aSrcPt.Y(); + + if( nMoveX || nMoveY ) + aMtf.Move( nMoveX, nMoveY ); + + nAddCount = ImplWriteActions( rOStm, aMtf, rSaveVDev, rRop_0_1, rLineCol, rLineColStack, rActualCharSet ); + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) nAddCount; + rOStm.Seek( nNewPos ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + nCount += nAddCount; +#endif + } + + nCount++; + } + break; + + case( META_HATCH_ACTION ): + { + const MetaHatchAction* pA = (MetaHatchAction*) pAction; + const PolyPolygon& rPolyPoly = pA->GetPolyPolygon(); + const Hatch& rHatch = pA->GetHatch(); + sal_uLong nOldPos, nNewPos, nAddCount; + + // write hatch comment + rOStm << (sal_Int16) GDI_HATCH_COMMENT; + + // we'll write the ActionSize later + nOldPos = rOStm.Tell(); + rOStm.SeekRel( 4 ); + + // write comment data + rOStm << rPolyPoly; + rOStm << rHatch; + + // calculate and write ActionSize of comment + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) ( nNewPos - nOldPos + 4 ); + rOStm.Seek( ( nOldPos = nNewPos ) + 4 ); + + { + // write actions for hatch + VirtualDevice aVDev; + GDIMetaFile aTmpMtf; + + aVDev.AddHatchActions( rPolyPoly, rHatch, aTmpMtf ); + nAddCount = ImplWriteActions( rOStm, aTmpMtf, rSaveVDev, rRop_0_1, rLineCol, rLineColStack, rActualCharSet ); + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) nAddCount; + rOStm.Seek( nNewPos ); + +#ifdef CVTSVM_WRITE_SUBACTIONCOUNT + nCount += nAddCount; +#endif + } + + nCount++; + } + break; + + case( META_REFPOINT_ACTION ): + { + const MetaRefPointAction* pA = (MetaRefPointAction*) pAction; + const Point& rRefPoint = pA->GetRefPoint(); + const sal_Bool bSet = pA->IsSetting(); + sal_uLong nOldPos, nNewPos; + + // write RefPoint comment + rOStm << (sal_Int16) GDI_REFPOINT_COMMENT; + + // we'll write the ActionSize later + nOldPos = rOStm.Tell(); + rOStm.SeekRel( 4 ); + + // write data + rOStm << rRefPoint << bSet; + rOStm << (sal_Int32) 0; // number of actions that follow this comment + + // calculate and write ActionSize of comment + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) ( nNewPos - nOldPos ); + rOStm.Seek( nNewPos ); + + nCount++; + } + break; + + case( META_TEXTLINECOLOR_ACTION ): + { + const MetaTextLineColorAction* pA = (MetaTextLineColorAction*) pAction; + const Color& rColor = pA->GetColor(); + const sal_Bool bSet = pA->IsSetting(); + sal_uLong nOldPos, nNewPos; + + // write RefPoint comment + rOStm << (sal_Int16) GDI_TEXTLINECOLOR_COMMENT; + + // we'll write the ActionSize later + nOldPos = rOStm.Tell(); + rOStm.SeekRel( 4 ); + + // write data + rOStm << rColor << bSet; + rOStm << (sal_Int32) 0; // number of actions that follow this comment + + // calculate and write ActionSize of comment + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) ( nNewPos - nOldPos ); + rOStm.Seek( nNewPos ); + + nCount++; + } + break; + + case( META_TEXTLINE_ACTION ): + { + const MetaTextLineAction* pA = (MetaTextLineAction*) pAction; + const Point& rStartPt = pA->GetStartPoint(); + const long nWidth = pA->GetWidth(); + const FontStrikeout eStrikeout = pA->GetStrikeout(); + const FontUnderline eUnderline = pA->GetUnderline(); + sal_uLong nOldPos, nNewPos; + + // write RefPoint comment + rOStm << (sal_Int16) GDI_TEXTLINE_COMMENT; + + // we'll write the ActionSize later + nOldPos = rOStm.Tell(); + rOStm.SeekRel( 4 ); + + // write data + rOStm << rStartPt << nWidth << + static_cast<sal_uInt32>(eStrikeout) << + static_cast<sal_uInt32>(eUnderline); + rOStm << (sal_Int32) 0; // number of actions that follow this comment + + // calculate and write ActionSize of comment + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) ( nNewPos - nOldPos ); + rOStm.Seek( nNewPos ); + + nCount++; + } + break; + + case( META_EPS_ACTION ): + break; + + case( META_COMMENT_ACTION ): + { + const MetaCommentAction* pA = (MetaCommentAction*) pAction; + const sal_uInt32 nDataSize = pA->GetDataSize(); + sal_uLong nOldPos, nNewPos; + + // write RefPoint comment + rOStm << (sal_Int16) GDI_COMMENT_COMMENT; + + // we'll write the ActionSize later + nOldPos = rOStm.Tell(); + rOStm.SeekRel( 4 ); + + // write data + rOStm << pA->GetComment() << pA->GetValue() << nDataSize; + + if( nDataSize ) + rOStm.Write( pA->GetData(), nDataSize ); + + rOStm << (sal_Int32) 0; // number of actions that follow this comment + + // calculate and write ActionSize of comment + nNewPos = rOStm.Tell(); + rOStm.Seek( nOldPos ); + rOStm << (sal_Int32) ( nNewPos - nOldPos ); + rOStm.Seek( nNewPos ); + + nCount++; + } + break; + +#ifdef DBG_UTIL + default: + { + ByteString aStr( "Missing implementation for Action#: " ); + aStr += ByteString::CreateFromInt32( pAction->GetType() ); + aStr += '!'; + OSL_FAIL( aStr.GetBuffer() ); + } + break; +#endif + +/* + case( META_TEXTRECT_ACTION ): + { + MetaTextRectAction* pAct = (MetaTextRectAction*) pAction; + + rOStm << ; + rOStm << ; + + nCount++; + } + break; +*/ + +/* + case( META_MASK_ACTION ): + { + MetaMaskAction* pAct = (MetaMaskAction*) pAction; + + rOStm << ; + rOStm << ; + + nCount++; + } + break; +*/ + +/* + case( META_MASKSCALE_ACTION ): + { + MetaMaskScaleAction* pAct = (MetaMaskScaleAction*) pAction; + + rOStm << ; + rOStm << ; + + nCount++; + } + break; +*/ + +/* + case( META_MASKSCALEPART_ACTION ): + { + MetaMaskScalePartAction* pAct = (MetaMaskScalePartAction*) pAction; + + rOStm << ; + rOStm << ; + + nCount++; + } + break; +*/ + +/* + case( META_ISECTREGIONCLIPREGION_ACTION ): + { + MetaISectRegionClipRegionAction* pAct = (MetaISectRegionClipRegionAction*) pAction; + + rOStm << ; + rOStm << ; + + nCount++; + } + break; +*/ + } + } + + return nCount; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/extoutdevdata.cxx b/vcl/source/gdi/extoutdevdata.cxx new file mode 100644 index 000000000000..2b8ad0fb9ae6 --- /dev/null +++ b/vcl/source/gdi/extoutdevdata.cxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <vcl/extoutdevdata.hxx> +#include <tools/rtti.hxx> + +namespace vcl +{ + +TYPEINIT0(ExtOutDevData); +ExtOutDevData::~ExtOutDevData() +{ +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/font.cxx b/vcl/source/gdi/font.cxx new file mode 100644 index 000000000000..2a10563441fa --- /dev/null +++ b/vcl/source/gdi/font.cxx @@ -0,0 +1,1120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "sft.hxx" + +#include "tools/stream.hxx" +#include "tools/vcompat.hxx" +#include "tools/debug.hxx" +#include "vcl/font.hxx" +#include "vcl/impfont.hxx" +#include "vcl/outfont.hxx" +#include "unotools/fontcfg.hxx" +#include <sal/macros.h> + +#include <algorithm> + +using namespace vcl; + +// ======================================================================= + +DBG_NAME( Font ) + +// ----------------------------------------------------------------------- + +Impl_Font::Impl_Font() : + maColor( COL_TRANSPARENT ), + maFillColor( COL_TRANSPARENT ) +{ + mnRefCount = 1; + meCharSet = RTL_TEXTENCODING_DONTKNOW; + meLanguage = LANGUAGE_DONTKNOW; + meCJKLanguage = LANGUAGE_DONTKNOW; + meFamily = FAMILY_DONTKNOW; + mePitch = PITCH_DONTKNOW; + meAlign = ALIGN_TOP; + meWeight = WEIGHT_DONTKNOW; + meWidthType = WIDTH_DONTKNOW; + meItalic = ITALIC_NONE; + meUnderline = UNDERLINE_NONE; + meOverline = UNDERLINE_NONE; + meStrikeout = STRIKEOUT_NONE; + meRelief = RELIEF_NONE; + meEmphasisMark = EMPHASISMARK_NONE; + mnOrientation = 0; + mnKerning = 0; + mbWordLine = false; + mbOutline = false; + mbShadow = false; + mbVertical = false; + mbTransparent = true; + mbConfigLookup = false; +} + +// ----------------------------------------------------------------------- + +Impl_Font::Impl_Font( const Impl_Font& rImplFont ) +: maFamilyName( rImplFont.maFamilyName ), + maStyleName( rImplFont.maStyleName ), + maSize( rImplFont.maSize ), + maColor( rImplFont.maColor ), + maFillColor( rImplFont.maFillColor ) +{ + mnRefCount = 1; + meCharSet = rImplFont.meCharSet; + meLanguage = rImplFont.meLanguage; + meCJKLanguage = rImplFont.meCJKLanguage; + meFamily = rImplFont.meFamily; + mePitch = rImplFont.mePitch; + meAlign = rImplFont.meAlign; + meWeight = rImplFont.meWeight; + meWidthType = rImplFont.meWidthType; + meItalic = rImplFont.meItalic; + meUnderline = rImplFont.meUnderline; + meOverline = rImplFont.meOverline; + meStrikeout = rImplFont.meStrikeout; + meRelief = rImplFont.meRelief; + meEmphasisMark = rImplFont.meEmphasisMark; + mnOrientation = rImplFont.mnOrientation; + mnKerning = rImplFont.mnKerning; + mbWordLine = rImplFont.mbWordLine; + mbOutline = rImplFont.mbOutline; + mbShadow = rImplFont.mbShadow; + mbVertical = rImplFont.mbVertical; + mbTransparent = rImplFont.mbTransparent; + mbConfigLookup = rImplFont.mbConfigLookup; +} + +// ----------------------------------------------------------------------- + +bool Impl_Font::operator==( const Impl_Font& rOther ) const +{ + // equality tests split up for easier debugging + if( (meWeight != rOther.meWeight) + || (meItalic != rOther.meItalic) + || (meFamily != rOther.meFamily) + || (mePitch != rOther.mePitch) ) + return false; + + if( (meCharSet != rOther.meCharSet) + || (meLanguage != rOther.meLanguage) + || (meCJKLanguage != rOther.meCJKLanguage) + || (meAlign != rOther.meAlign) ) + return false; + + if( (maSize != rOther.maSize) + || (mnOrientation != rOther.mnOrientation) + || (mbVertical != rOther.mbVertical) ) + return false; + + if( (maFamilyName != rOther.maFamilyName) + || (maStyleName != rOther.maStyleName) ) + return false; + + if( (maColor != rOther.maColor) + || (maFillColor != rOther.maFillColor) ) + return false; + + if( (meUnderline != rOther.meUnderline) + || (meOverline != rOther.meOverline) + || (meStrikeout != rOther.meStrikeout) + || (meRelief != rOther.meRelief) + || (meEmphasisMark != rOther.meEmphasisMark) + || (mbWordLine != rOther.mbWordLine) + || (mbOutline != rOther.mbOutline) + || (mbShadow != rOther.mbShadow) + || (mnKerning != rOther.mnKerning) + || (mbTransparent != rOther.mbTransparent) ) + return false; + + return true; +} + +// ----------------------------------------------------------------------- + +void Impl_Font::AskConfig() +{ + if( mbConfigLookup ) + return; + + mbConfigLookup = true; + + // prepare the FontSubst configuration lookup + const utl::FontSubstConfiguration* pFontSubst = utl::FontSubstConfiguration::get(); + + String aShortName; + String aFamilyName; + sal_uLong nType = 0; + FontWeight eWeight = WEIGHT_DONTKNOW; + FontWidth eWidthType = WIDTH_DONTKNOW; + String aMapName = maFamilyName; + GetEnglishSearchFontName( aMapName ); + utl::FontSubstConfiguration::getMapName( aMapName, + aShortName, aFamilyName, eWeight, eWidthType, nType ); + + // lookup the font name in the configuration + const utl::FontNameAttr* pFontAttr = pFontSubst->getSubstInfo( aMapName ); + + // if the direct lookup failed try again with an alias name + if ( !pFontAttr && (aShortName != aMapName) ) + pFontAttr = pFontSubst->getSubstInfo( aShortName ); + + if( pFontAttr ) + { + // the font was found in the configuration + if( meFamily == FAMILY_DONTKNOW ) + { + if ( pFontAttr->Type & IMPL_FONT_ATTR_SERIF ) + meFamily = FAMILY_ROMAN; + else if ( pFontAttr->Type & IMPL_FONT_ATTR_SANSSERIF ) + meFamily = FAMILY_SWISS; + else if ( pFontAttr->Type & IMPL_FONT_ATTR_TYPEWRITER ) + meFamily = FAMILY_MODERN; + else if ( pFontAttr->Type & IMPL_FONT_ATTR_ITALIC ) + meFamily = FAMILY_SCRIPT; + else if ( pFontAttr->Type & IMPL_FONT_ATTR_DECORATIVE ) + meFamily = FAMILY_DECORATIVE; + } + + if( mePitch == PITCH_DONTKNOW ) + { + if ( pFontAttr->Type & IMPL_FONT_ATTR_FIXED ) + mePitch = PITCH_FIXED; + } + } + + // if some attributes are still unknown then use the FontSubst magic + if( meFamily == FAMILY_DONTKNOW ) + { + if( nType & IMPL_FONT_ATTR_SERIF ) + meFamily = FAMILY_ROMAN; + else if( nType & IMPL_FONT_ATTR_SANSSERIF ) + meFamily = FAMILY_SWISS; + else if( nType & IMPL_FONT_ATTR_TYPEWRITER ) + meFamily = FAMILY_MODERN; + else if( nType & IMPL_FONT_ATTR_ITALIC ) + meFamily = FAMILY_SCRIPT; + else if( nType & IMPL_FONT_ATTR_DECORATIVE ) + meFamily = FAMILY_DECORATIVE; + } + + if( meWeight == WEIGHT_DONTKNOW ) + meWeight = eWeight; + if( meWidthType == WIDTH_DONTKNOW ) + meWidthType = eWidthType; +} + +// ======================================================================= + +void Font::MakeUnique() +{ + // create a copy if others still reference it + if ( mpImplFont->mnRefCount != 1 ) + { + if ( mpImplFont->mnRefCount ) + mpImplFont->mnRefCount--; + mpImplFont = new Impl_Font( *mpImplFont ); + } +} + +// ----------------------------------------------------------------------- + +Font::Font() +{ + DBG_CTOR( Font, NULL ); + + static Impl_Font aStaticImplFont; + // RefCount is zero for static objects + aStaticImplFont.mnRefCount = 0; + mpImplFont = &aStaticImplFont; +} + +// ----------------------------------------------------------------------- + +Font::Font( const Font& rFont ) +{ + DBG_CTOR( Font, NULL ); + DBG_CHKOBJ( &rFont, Font, NULL ); + DBG_ASSERT( rFont.mpImplFont->mnRefCount < 0xFFFE, "Font: RefCount overflow" ); + + mpImplFont = rFont.mpImplFont; + // do not count static objects (where RefCount is zero) + if ( mpImplFont->mnRefCount ) + mpImplFont->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +Font::Font( const String& rFamilyName, const Size& rSize ) +{ + DBG_CTOR( Font, NULL ); + + mpImplFont = new Impl_Font; + mpImplFont->maFamilyName= rFamilyName; + mpImplFont->maSize = rSize; +} + +// ----------------------------------------------------------------------- + +Font::Font( const String& rFamilyName, const String& rStyleName, const Size& rSize ) +{ + DBG_CTOR( Font, NULL ); + + mpImplFont = new Impl_Font; + mpImplFont->maFamilyName= rFamilyName; + mpImplFont->maStyleName = rStyleName; + mpImplFont->maSize = rSize; +} + +// ----------------------------------------------------------------------- + +Font::Font( FontFamily eFamily, const Size& rSize ) +{ + DBG_CTOR( Font, NULL ); + + mpImplFont = new Impl_Font; + mpImplFont->meFamily = eFamily; + mpImplFont->maSize = rSize; +} + +// ----------------------------------------------------------------------- + +Font::~Font() +{ + DBG_DTOR( Font, NULL ); + + // decrement reference counter and delete if last reference + // if the object is not static (Refcounter==0) + if ( mpImplFont->mnRefCount ) + { + if ( mpImplFont->mnRefCount == 1 ) + delete mpImplFont; + else + mpImplFont->mnRefCount--; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetColor( const Color& rColor ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->maColor != rColor ) + { + MakeUnique(); + mpImplFont->maColor = rColor; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetFillColor( const Color& rColor ) +{ + DBG_CHKTHIS( Font, NULL ); + + MakeUnique(); + mpImplFont->maFillColor = rColor; + if ( rColor.GetTransparency() ) + mpImplFont->mbTransparent = true; +} + +// ----------------------------------------------------------------------- + +void Font::SetTransparent( sal_Bool bTransparent ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->mbTransparent != bTransparent ) + { + MakeUnique(); + mpImplFont->mbTransparent = bTransparent; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetAlign( FontAlign eAlign ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meAlign != eAlign ) + { + MakeUnique(); + mpImplFont->meAlign = eAlign; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetName( const String& rFamilyName ) +{ + DBG_CHKTHIS( Font, NULL ); + + MakeUnique(); + mpImplFont->maFamilyName = rFamilyName; +} + +// ----------------------------------------------------------------------- + +void Font::SetStyleName( const String& rStyleName ) +{ + DBG_CHKTHIS( Font, NULL ); + + MakeUnique(); + mpImplFont->maStyleName = rStyleName; +} + +// ----------------------------------------------------------------------- + +void Font::SetSize( const Size& rSize ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->maSize != rSize ) + { + MakeUnique(); + mpImplFont->maSize = rSize; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetFamily( FontFamily eFamily ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meFamily != eFamily ) + { + MakeUnique(); + mpImplFont->meFamily = eFamily; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetCharSet( CharSet eCharSet ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meCharSet != eCharSet ) + { + MakeUnique(); + mpImplFont->meCharSet = eCharSet; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetLanguage( LanguageType eLanguage ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meLanguage != eLanguage ) + { + MakeUnique(); + mpImplFont->meLanguage = eLanguage; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetCJKContextLanguage( LanguageType eLanguage ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meCJKLanguage != eLanguage ) + { + MakeUnique(); + mpImplFont->meCJKLanguage = eLanguage; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetPitch( FontPitch ePitch ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->mePitch != ePitch ) + { + MakeUnique(); + mpImplFont->mePitch = ePitch; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetOrientation( short nOrientation ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->mnOrientation != nOrientation ) + { + MakeUnique(); + mpImplFont->mnOrientation = nOrientation; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetVertical( sal_Bool bVertical ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->mbVertical != bVertical ) + { + MakeUnique(); + mpImplFont->mbVertical = bVertical; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetKerning( FontKerning nKerning ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->mnKerning != nKerning ) + { + MakeUnique(); + mpImplFont->mnKerning = nKerning; + } +} + +// ----------------------------------------------------------------------- + +sal_Bool Font::IsKerning() const +{ + return (mpImplFont->mnKerning & KERNING_FONTSPECIFIC) != 0; +} + +// ----------------------------------------------------------------------- + +void Font::SetWeight( FontWeight eWeight ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meWeight != eWeight ) + { + MakeUnique(); + mpImplFont->meWeight = eWeight; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetWidthType( FontWidth eWidth ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meWidthType != eWidth ) + { + MakeUnique(); + mpImplFont->meWidthType = eWidth; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetItalic( FontItalic eItalic ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meItalic != eItalic ) + { + MakeUnique(); + mpImplFont->meItalic = eItalic; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetOutline( sal_Bool bOutline ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->mbOutline != bOutline ) + { + MakeUnique(); + mpImplFont->mbOutline = bOutline; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetShadow( sal_Bool bShadow ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->mbShadow != bShadow ) + { + MakeUnique(); + mpImplFont->mbShadow = bShadow; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetUnderline( FontUnderline eUnderline ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meUnderline != eUnderline ) + { + MakeUnique(); + mpImplFont->meUnderline = eUnderline; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetOverline( FontUnderline eOverline ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meOverline != eOverline ) + { + MakeUnique(); + mpImplFont->meOverline = eOverline; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetStrikeout( FontStrikeout eStrikeout ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meStrikeout != eStrikeout ) + { + MakeUnique(); + mpImplFont->meStrikeout = eStrikeout; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetRelief( FontRelief eRelief ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meRelief != eRelief ) + { + MakeUnique(); + mpImplFont->meRelief = eRelief; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetEmphasisMark( FontEmphasisMark eEmphasisMark ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->meEmphasisMark != eEmphasisMark ) + { + MakeUnique(); + mpImplFont->meEmphasisMark = eEmphasisMark; + } +} + +// ----------------------------------------------------------------------- + +void Font::SetWordLineMode( sal_Bool bWordLine ) +{ + DBG_CHKTHIS( Font, NULL ); + + if( mpImplFont->mbWordLine != bWordLine ) + { + MakeUnique(); + mpImplFont->mbWordLine = bWordLine; + } +} + +// ----------------------------------------------------------------------- + +Font& Font::operator=( const Font& rFont ) +{ + DBG_CHKTHIS( Font, NULL ); + DBG_CHKOBJ( &rFont, Font, NULL ); + DBG_ASSERT( rFont.mpImplFont->mnRefCount < 0xFFFE, "Font: RefCount overflow" ); + + // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann + // RefCount == 0 fuer statische Objekte + if ( rFont.mpImplFont->mnRefCount ) + rFont.mpImplFont->mnRefCount++; + + // Wenn es keine statischen ImplDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplFont->mnRefCount ) + { + if ( mpImplFont->mnRefCount == 1 ) + delete mpImplFont; + else + mpImplFont->mnRefCount--; + } + + mpImplFont = rFont.mpImplFont; + + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool Font::operator==( const Font& rFont ) const +{ + DBG_CHKTHIS( Font, NULL ); + DBG_CHKOBJ( &rFont, Font, NULL ); + + if( mpImplFont == rFont.mpImplFont ) + return sal_True; + if( *mpImplFont == *rFont.mpImplFont ) + return sal_True; + + return sal_False; +} + +// ----------------------------------------------------------------------- + +void Font::Merge( const Font& rFont ) +{ + if ( rFont.GetName().Len() ) + { + SetName( rFont.GetName() ); + SetStyleName( rFont.GetStyleName() ); + SetCharSet( GetCharSet() ); + SetLanguage( rFont.GetLanguage() ); + SetCJKContextLanguage( rFont.GetCJKContextLanguage() ); + // don't use access methods here, might lead to AskConfig(), if DONTKNOW + SetFamily( rFont.mpImplFont->meFamily ); + SetPitch( rFont.mpImplFont->mePitch ); + } + + // don't use access methods here, might lead to AskConfig(), if DONTKNOW + if ( rFont.mpImplFont->meWeight != WEIGHT_DONTKNOW ) + SetWeight( rFont.GetWeight() ); + if ( rFont.mpImplFont->meItalic != ITALIC_DONTKNOW ) + SetItalic( rFont.GetItalic() ); + if ( rFont.mpImplFont->meWidthType != WIDTH_DONTKNOW ) + SetWidthType( rFont.GetWidthType() ); + + + if ( rFont.GetSize().Height() ) + SetSize( rFont.GetSize() ); + if ( rFont.GetUnderline() != UNDERLINE_DONTKNOW ) + { + SetUnderline( rFont.GetUnderline() ); + SetWordLineMode( rFont.IsWordLineMode() ); + } + if ( rFont.GetOverline() != UNDERLINE_DONTKNOW ) + { + SetOverline( rFont.GetOverline() ); + SetWordLineMode( rFont.IsWordLineMode() ); + } + if ( rFont.GetStrikeout() != STRIKEOUT_DONTKNOW ) + { + SetStrikeout( rFont.GetStrikeout() ); + SetWordLineMode( rFont.IsWordLineMode() ); + } + + // Defaults? + SetOrientation( rFont.GetOrientation() ); + SetVertical( rFont.IsVertical() ); + SetEmphasisMark( rFont.GetEmphasisMark() ); + SetKerning( rFont.IsKerning() ); + SetOutline( rFont.IsOutline() ); + SetShadow( rFont.IsShadow() ); + SetRelief( rFont.GetRelief() ); +} + +void Font::GetFontAttributes( ImplFontAttributes& rAttrs ) const +{ + // #i56788# Use members directly, don't risc config access. + rAttrs.maName = mpImplFont->maFamilyName; + rAttrs.maStyleName = mpImplFont->maStyleName; + rAttrs.meFamily = mpImplFont->meFamily; + rAttrs.mePitch = mpImplFont->mePitch; + rAttrs.meItalic = mpImplFont->meItalic; + rAttrs.meWeight = mpImplFont->meWeight; + rAttrs.meWidthType = WIDTH_DONTKNOW; + rAttrs.mbSymbolFlag= (mpImplFont->meCharSet == RTL_TEXTENCODING_SYMBOL); +} + + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, Impl_Font& rImpl_Font ) +{ + VersionCompat aCompat( rIStm, STREAM_READ ); + sal_uInt16 nTmp16; + sal_Bool bTmp; + sal_uInt8 nTmp8; + + rIStm.ReadByteString( rImpl_Font.maFamilyName, rIStm.GetStreamCharSet() ); + rIStm.ReadByteString( rImpl_Font.maStyleName, rIStm.GetStreamCharSet() ); + rIStm >> rImpl_Font.maSize; + + rIStm >> nTmp16; rImpl_Font.meCharSet = (rtl_TextEncoding) nTmp16; + rIStm >> nTmp16; rImpl_Font.meFamily = (FontFamily) nTmp16; + rIStm >> nTmp16; rImpl_Font.mePitch = (FontPitch) nTmp16; + rIStm >> nTmp16; rImpl_Font.meWeight = (FontWeight) nTmp16; + rIStm >> nTmp16; rImpl_Font.meUnderline = (FontUnderline) nTmp16; + rIStm >> nTmp16; rImpl_Font.meStrikeout = (FontStrikeout) nTmp16; + rIStm >> nTmp16; rImpl_Font.meItalic = (FontItalic) nTmp16; + rIStm >> nTmp16; rImpl_Font.meLanguage = (LanguageType) nTmp16; + rIStm >> nTmp16; rImpl_Font.meWidthType = (FontWidth) nTmp16; + + rIStm >> rImpl_Font.mnOrientation; + + rIStm >> bTmp; rImpl_Font.mbWordLine = bTmp; + rIStm >> bTmp; rImpl_Font.mbOutline = bTmp; + rIStm >> bTmp; rImpl_Font.mbShadow = bTmp; + rIStm >> nTmp8; rImpl_Font.mnKerning = nTmp8; + + if( aCompat.GetVersion() >= 2 ) + { + rIStm >> nTmp8; rImpl_Font.meRelief = (FontRelief)nTmp8; + rIStm >> nTmp16; rImpl_Font.meCJKLanguage = (LanguageType)nTmp16; + rIStm >> bTmp; rImpl_Font.mbVertical = bTmp; + rIStm >> nTmp16; rImpl_Font.meEmphasisMark = (FontEmphasisMark)nTmp16; + } + if( aCompat.GetVersion() >= 3 ) + { + rIStm >> nTmp16; rImpl_Font.meOverline = (FontUnderline) nTmp16; + } + // Relief + // CJKContextLanguage + + return rIStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const Impl_Font& rImpl_Font ) +{ + VersionCompat aCompat( rOStm, STREAM_WRITE, 3 ); + rOStm.WriteByteString( rImpl_Font.maFamilyName, rOStm.GetStreamCharSet() ); + rOStm.WriteByteString( rImpl_Font.maStyleName, rOStm.GetStreamCharSet() ); + rOStm << rImpl_Font.maSize; + + rOStm << (sal_uInt16) GetStoreCharSet( rImpl_Font.meCharSet ); + rOStm << (sal_uInt16) rImpl_Font.meFamily; + rOStm << (sal_uInt16) rImpl_Font.mePitch; + rOStm << (sal_uInt16) rImpl_Font.meWeight; + rOStm << (sal_uInt16) rImpl_Font.meUnderline; + rOStm << (sal_uInt16) rImpl_Font.meStrikeout; + rOStm << (sal_uInt16) rImpl_Font.meItalic; + rOStm << (sal_uInt16) rImpl_Font.meLanguage; + rOStm << (sal_uInt16) rImpl_Font.meWidthType; + + rOStm << rImpl_Font.mnOrientation; + + rOStm << (sal_Bool) rImpl_Font.mbWordLine; + rOStm << (sal_Bool) rImpl_Font.mbOutline; + rOStm << (sal_Bool) rImpl_Font.mbShadow; + rOStm << (sal_uInt8) rImpl_Font.mnKerning; + + // new in version 2 + rOStm << (sal_uInt8) rImpl_Font.meRelief; + rOStm << (sal_uInt16) rImpl_Font.meCJKLanguage; + rOStm << (sal_Bool) rImpl_Font.mbVertical; + rOStm << (sal_uInt16) rImpl_Font.meEmphasisMark; + + // new in version 3 + rOStm << (sal_uInt16) rImpl_Font.meOverline; + + return rOStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, Font& rFont ) +{ + rFont.MakeUnique(); + return( rIStm >> *rFont.mpImplFont ); +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const Font& rFont ) +{ + return( rOStm << *rFont.mpImplFont ); +} + +// ----------------------------------------------------------------------- +namespace +{ + bool identifyTrueTypeFont( const void* i_pBuffer, sal_uInt32 i_nSize, Font& o_rResult ) + { + bool bResult = false; +// FIXME: This is HACK. We do not build psprint's part on aqua... +// How to solve this? +#ifndef QUARTZ + TrueTypeFont* pTTF = NULL; + if( OpenTTFontBuffer( const_cast<void*>(i_pBuffer), i_nSize, 0, &pTTF ) == SF_OK ) + { + TTGlobalFontInfo aInfo; + GetTTGlobalFontInfo( pTTF, &aInfo ); + // most important: the family name + if( aInfo.ufamily ) + o_rResult.SetName( aInfo.ufamily ); + else if( aInfo.family ) + o_rResult.SetName( rtl::OStringToOUString( aInfo.family, RTL_TEXTENCODING_ASCII_US ) ); + // set weight + if( aInfo.weight ) + { + if( aInfo.weight < FW_EXTRALIGHT ) + o_rResult.SetWeight( WEIGHT_THIN ); + else if( aInfo.weight < FW_LIGHT ) + o_rResult.SetWeight( WEIGHT_ULTRALIGHT ); + else if( aInfo.weight < FW_NORMAL ) + o_rResult.SetWeight( WEIGHT_LIGHT ); + else if( aInfo.weight < FW_MEDIUM ) + o_rResult.SetWeight( WEIGHT_NORMAL ); + else if( aInfo.weight < FW_SEMIBOLD ) + o_rResult.SetWeight( WEIGHT_MEDIUM ); + else if( aInfo.weight < FW_BOLD ) + o_rResult.SetWeight( WEIGHT_SEMIBOLD ); + else if( aInfo.weight < FW_EXTRABOLD ) + o_rResult.SetWeight( WEIGHT_BOLD ); + else if( aInfo.weight < FW_BLACK ) + o_rResult.SetWeight( WEIGHT_ULTRABOLD ); + else + o_rResult.SetWeight( WEIGHT_BLACK ); + } + else + o_rResult.SetWeight( (aInfo.macStyle & 1) ? WEIGHT_BOLD : WEIGHT_NORMAL ); + // set width + if( aInfo.width ) + { + if( aInfo.width == FWIDTH_ULTRA_CONDENSED ) + o_rResult.SetWidth( WIDTH_ULTRA_CONDENSED ); + else if( aInfo.width == FWIDTH_EXTRA_CONDENSED ) + o_rResult.SetWidth( WIDTH_EXTRA_CONDENSED ); + else if( aInfo.width == FWIDTH_CONDENSED ) + o_rResult.SetWidth( WIDTH_CONDENSED ); + else if( aInfo.width == FWIDTH_SEMI_CONDENSED ) + o_rResult.SetWidth( WIDTH_SEMI_CONDENSED ); + else if( aInfo.width == FWIDTH_NORMAL ) + o_rResult.SetWidth( WIDTH_NORMAL ); + else if( aInfo.width == FWIDTH_SEMI_EXPANDED ) + o_rResult.SetWidth( WIDTH_SEMI_EXPANDED ); + else if( aInfo.width == FWIDTH_EXPANDED ) + o_rResult.SetWidth( WIDTH_EXPANDED ); + else if( aInfo.width == FWIDTH_EXTRA_EXPANDED ) + o_rResult.SetWidth( WIDTH_EXTRA_EXPANDED ); + else if( aInfo.width >= FWIDTH_ULTRA_EXPANDED ) + o_rResult.SetWidth( WIDTH_ULTRA_EXPANDED ); + } + // set italic + o_rResult.SetItalic( (aInfo.italicAngle != 0) ? ITALIC_NORMAL : ITALIC_NONE ); + + // set pitch + o_rResult.SetPitch( (aInfo.pitch == 0) ? PITCH_VARIABLE : PITCH_FIXED ); + + // set style name + if( aInfo.usubfamily ) + o_rResult.SetStyleName( rtl::OUString( aInfo.usubfamily ) ); + else if( aInfo.subfamily ) + o_rResult.SetStyleName( rtl::OUString::createFromAscii( aInfo.subfamily ) ); + + // cleanup + CloseTTFont( pTTF ); + // success + bResult = true; + } +#endif + return bResult; + } + + struct WeightSearchEntry + { + const char* string; + int string_len; + FontWeight weight; + + bool operator<( const WeightSearchEntry& rRight ) const + { + return rtl_str_compareIgnoreAsciiCase_WithLength( string, string_len, rRight.string, rRight.string_len ) < 0; + } + } + weight_table[] = + { + { "black", 5, WEIGHT_BLACK }, + { "bold", 4, WEIGHT_BOLD }, + { "book", 4, WEIGHT_LIGHT }, + { "demi", 4, WEIGHT_SEMIBOLD }, + { "heavy", 5, WEIGHT_BLACK }, + { "light", 5, WEIGHT_LIGHT }, + { "medium", 6, WEIGHT_MEDIUM }, + { "regular", 7, WEIGHT_NORMAL }, + { "super", 5, WEIGHT_ULTRABOLD }, + { "thin", 4, WEIGHT_THIN } + }; + + bool identifyType1Font( const char* i_pBuffer, sal_uInt32 i_nSize, Font& o_rResult ) + { + bool bResult = false; + // might be a type1, find eexec + const char* pStream = i_pBuffer; + const char* pExec = "eexec"; + const char* pExecPos = std::search( pStream, pStream+i_nSize, pExec, pExec+5 ); + if( pExecPos != pStream+i_nSize) + { + // find /FamilyName entry + static const char* pFam = "/FamilyName"; + const char* pFamPos = std::search( pStream, pExecPos, pFam, pFam+11 ); + if( pFamPos != pExecPos ) + { + // extract the string value behind /FamilyName + const char* pOpen = pFamPos+11; + while( pOpen < pExecPos && *pOpen != '(' ) + pOpen++; + const char* pClose = pOpen; + while( pClose < pExecPos && *pClose != ')' ) + pClose++; + if( pClose - pOpen > 1 ) + { + o_rResult.SetName( rtl::OStringToOUString( rtl::OString( pOpen+1, pClose-pOpen-1 ), RTL_TEXTENCODING_ASCII_US ) ); + } + } + + // parse /ItalicAngle + static const char* pItalic = "/ItalicAngle"; + const char* pItalicPos = std::search( pStream, pExecPos, pItalic, pItalic+12 ); + if( pItalicPos != pExecPos ) + { + sal_Int32 nItalic = rtl_str_toInt32( pItalicPos+12, 10 ); + o_rResult.SetItalic( (nItalic != 0) ? ITALIC_NORMAL : ITALIC_NONE ); + } + + // parse /Weight + static const char* pWeight = "/Weight"; + const char* pWeightPos = std::search( pStream, pExecPos, pWeight, pWeight+7 ); + if( pWeightPos != pExecPos ) + { + // extract the string value behind /Weight + const char* pOpen = pWeightPos+7; + while( pOpen < pExecPos && *pOpen != '(' ) + pOpen++; + const char* pClose = pOpen; + while( pClose < pExecPos && *pClose != ')' ) + pClose++; + if( pClose - pOpen > 1 ) + { + WeightSearchEntry aEnt; + aEnt.string = pOpen+1; + aEnt.string_len = (pClose-pOpen)-1; + aEnt.weight = WEIGHT_NORMAL; + const int nEnt = SAL_N_ELEMENTS( weight_table ); + WeightSearchEntry* pFound = std::lower_bound( weight_table, weight_table+nEnt, aEnt ); + if( pFound != (weight_table+nEnt) ) + o_rResult.SetWeight( pFound->weight ); + } + } + + // parse isFixedPitch + static const char* pFixed = "/isFixedPitch"; + const char* pFixedPos = std::search( pStream, pExecPos, pFixed, pFixed+13 ); + if( pFixedPos != pExecPos ) + { + // skip whitespace + while( pFixedPos < pExecPos-4 && + ( *pFixedPos == ' ' || + *pFixedPos == '\t' || + *pFixedPos == '\r' || + *pFixedPos == '\n' ) ) + { + pFixedPos++; + } + // find "true" value + if( rtl_str_compareIgnoreAsciiCase_WithLength( pFixedPos, 4, "true", 4 ) == 0 ) + o_rResult.SetPitch( PITCH_FIXED ); + else + o_rResult.SetPitch( PITCH_VARIABLE ); + } + } + return bResult; + } +} + +Font Font::identifyFont( const void* i_pBuffer, sal_uInt32 i_nSize ) +{ + Font aResult; + if( ! identifyTrueTypeFont( i_pBuffer, i_nSize, aResult ) ) + { + const char* pStream = reinterpret_cast<const char*>(i_pBuffer); + if( pStream && i_nSize > 100 && + *pStream == '%' && pStream[1] == '!' ) + { + identifyType1Font( pStream, i_nSize, aResult ); + } + } + + return aResult; +} + +// the inlines from the font.hxx header are now instantiated for pImpl-ification +// TODO: reformat +const Color& Font::GetColor() const { return mpImplFont->maColor; } +const Color& Font::GetFillColor() const { return mpImplFont->maFillColor; } +sal_Bool Font::IsTransparent() const { return mpImplFont->mbTransparent; } +FontAlign Font::GetAlign() const { return mpImplFont->meAlign; } +const String& Font::GetName() const { return mpImplFont->maFamilyName; } +const String& Font::GetStyleName() const { return mpImplFont->maStyleName; } +const Size& Font::GetSize() const { return mpImplFont->maSize; } +void Font::SetHeight( long nHeight ) { SetSize( Size( mpImplFont->maSize.Width(), nHeight ) ); } +long Font::GetHeight() const { return mpImplFont->maSize.Height(); } +void Font::SetWidth( long nWidth ) { SetSize( Size( nWidth, mpImplFont->maSize.Height() ) ); } +long Font::GetWidth() const { return mpImplFont->maSize.Width(); } +rtl_TextEncoding Font::GetCharSet() const { return mpImplFont->meCharSet; } +LanguageType Font::GetLanguage() const { return mpImplFont->meLanguage; } +LanguageType Font::GetCJKContextLanguage() const { return mpImplFont->meCJKLanguage; } +short Font::GetOrientation() const { return mpImplFont->mnOrientation; } +sal_Bool Font::IsVertical() const { return mpImplFont->mbVertical; } +FontKerning Font::GetKerning() const { return mpImplFont->mnKerning; } +FontPitch Font::GetPitch() const { return mpImplFont->GetPitch(); } +FontWeight Font::GetWeight() const { return mpImplFont->GetWeight(); } +FontWidth Font::GetWidthType() const { return mpImplFont->GetWidthType(); } +FontItalic Font::GetItalic() const { return mpImplFont->GetItalic(); } +FontFamily Font::GetFamily() const { return mpImplFont->GetFamily(); } +sal_Bool Font::IsOutline() const { return mpImplFont->mbOutline; } +sal_Bool Font::IsShadow() const { return mpImplFont->mbShadow; } +FontRelief Font::GetRelief() const { return mpImplFont->meRelief; } +FontUnderline Font::GetUnderline() const { return mpImplFont->meUnderline; } +FontUnderline Font::GetOverline() const { return mpImplFont->meOverline; } +FontStrikeout Font::GetStrikeout() const { return mpImplFont->meStrikeout; } +FontEmphasisMark Font::GetEmphasisMark() const { return mpImplFont->meEmphasisMark; } +sal_Bool Font::IsWordLineMode() const { return mpImplFont->mbWordLine; } +sal_Bool Font::IsSameInstance( const Font& rFont ) const { return (mpImplFont == rFont.mpImplFont); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/gdimtf.cxx b/vcl/source/gdi/gdimtf.cxx new file mode 100644 index 000000000000..79b8f417891b --- /dev/null +++ b/vcl/source/gdi/gdimtf.cxx @@ -0,0 +1,3265 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <rtl/crc.h> +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <vcl/metaact.hxx> +#include <vcl/salbtype.hxx> +#include <vcl/outdev.hxx> +#include <vcl/window.hxx> +#include <vcl/cvtsvm.hxx> +#include <vcl/virdev.hxx> +#include <vcl/salbmp.hxx> +#include <vcl/svapp.hxx> +#include <vcl/svdata.hxx> +#include <vcl/salinst.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/graphictools.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/unohelp.hxx> + +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/rendering/MtfRenderer.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/awt/XGraphics.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/graphic/XGraphicRenderer.hpp> + +using namespace com::sun::star; + +// ----------- +// - Defines - +// ----------- + +#define GAMMA( _def_cVal, _def_InvGamma ) ((sal_uInt8)MinMax(FRound(pow( _def_cVal/255.0,_def_InvGamma)*255.0),0L,255L)) + +// -------------------------- +// - Color exchange structs - +// -------------------------- + +struct ImplColAdjustParam +{ + sal_uInt8* pMapR; + sal_uInt8* pMapG; + sal_uInt8* pMapB; +}; + +struct ImplBmpAdjustParam +{ + short nLuminancePercent; + short nContrastPercent; + short nChannelRPercent; + short nChannelGPercent; + short nChannelBPercent; + double fGamma; + sal_Bool bInvert; +}; + +// ----------------------------------------------------------------------------- + +struct ImplColConvertParam +{ + MtfConversion eConversion; +}; + +struct ImplBmpConvertParam +{ + BmpConversion eConversion; +}; + +// ----------------------------------------------------------------------------- + +struct ImplColMonoParam +{ + Color aColor; +}; + +struct ImplBmpMonoParam +{ + Color aColor; +}; + +// ----------------------------------------------------------------------------- + +struct ImplColReplaceParam +{ + sal_uLong* pMinR; + sal_uLong* pMaxR; + sal_uLong* pMinG; + sal_uLong* pMaxG; + sal_uLong* pMinB; + sal_uLong* pMaxB; + const Color* pDstCols; + sal_uLong nCount; +}; + +struct ImplBmpReplaceParam +{ + const Color* pSrcCols; + const Color* pDstCols; + sal_uLong nCount; + const sal_uLong* pTols; +}; + + +// --------- +// - Label - +// --------- + +struct ImpLabel +{ + String aLabelName; + sal_uLong nActionPos; + + ImpLabel( const String& rLabelName, sal_uLong _nActionPos ) : + aLabelName( rLabelName ), + nActionPos( _nActionPos ) {} +}; + +// ------------- +// - LabelList - +// ------------- + +class ImpLabelList : private List +{ +public: + + ImpLabelList() : List( 8, 4, 4 ) {} + ImpLabelList( const ImpLabelList& rList ); + ~ImpLabelList(); + + void ImplInsert( ImpLabel* p ) { Insert( p, LIST_APPEND ); } + ImpLabel* ImplRemove( sal_uLong nPos ) { return (ImpLabel*) Remove( nPos ); } + void ImplReplace( ImpLabel* p ) { Replace( (void*)p ); } + ImpLabel* ImplFirst() { return (ImpLabel*) First(); } + ImpLabel* ImplNext() { return (ImpLabel*) Next(); } + ImpLabel* ImplGetLabel( sal_uLong nPos ) const { return (ImpLabel*) GetObject( nPos ); } + sal_uLong ImplGetLabelPos( const String& rLabelName ); + sal_uLong ImplCount() const { return Count(); } +}; + +// ------------------------------------------------------------------------ + +ImpLabelList::ImpLabelList( const ImpLabelList& rList ) : + List( rList ) +{ + for( ImpLabel* pLabel = ImplFirst(); pLabel; pLabel = ImplNext() ) + ImplReplace( new ImpLabel( *pLabel ) ); +} + +// ------------------------------------------------------------------------ + +ImpLabelList::~ImpLabelList() +{ + for( ImpLabel* pLabel = ImplFirst(); pLabel; pLabel = ImplNext() ) + delete pLabel; +} + +// ------------------------------------------------------------------------ + +sal_uLong ImpLabelList::ImplGetLabelPos( const String& rLabelName ) +{ + sal_uLong nLabelPos = METAFILE_LABEL_NOTFOUND; + + for( ImpLabel* pLabel = ImplFirst(); pLabel; pLabel = ImplNext() ) + { + if ( rLabelName == pLabel->aLabelName ) + { + nLabelPos = GetCurPos(); + break; + } + } + + return nLabelPos; +} + +// --------------- +// - GDIMetaFile - +// --------------- + +GDIMetaFile::GDIMetaFile() : + List ( 0x3EFF, 64, 64 ), + aPrefSize ( 1, 1 ), + pPrev ( NULL ), + pNext ( NULL ), + pOutDev ( NULL ), + pLabelList ( NULL ), + bPause ( sal_False ), + bRecord ( sal_False ), + bUseCanvas ( sal_False ) +{ +} + +// ------------------------------------------------------------------------ + +GDIMetaFile::GDIMetaFile( const GDIMetaFile& rMtf ) : + List ( rMtf ), + aPrefMapMode ( rMtf.aPrefMapMode ), + aPrefSize ( rMtf.aPrefSize ), + aHookHdlLink ( rMtf.aHookHdlLink ), + pPrev ( rMtf.pPrev ), + pNext ( rMtf.pNext ), + pOutDev ( NULL ), + bPause ( sal_False ), + bRecord ( sal_False ), + bUseCanvas ( rMtf.bUseCanvas ) +{ + // RefCount der MetaActions erhoehen + for( void* pAct = First(); pAct; pAct = Next() ) + ( (MetaAction*) pAct )->Duplicate(); + + if( rMtf.pLabelList ) + pLabelList = new ImpLabelList( *rMtf.pLabelList ); + else + pLabelList = NULL; + + if( rMtf.bRecord ) + { + Record( rMtf.pOutDev ); + + if ( rMtf.bPause ) + Pause( sal_True ); + } +} + +// ------------------------------------------------------------------------ + +GDIMetaFile::~GDIMetaFile() +{ + Clear(); +} + +// ------------------------------------------------------------------------ + +GDIMetaFile& GDIMetaFile::operator=( const GDIMetaFile& rMtf ) +{ + if( this != &rMtf ) + { + Clear(); + + List::operator=( rMtf ); + + // RefCount der MetaActions erhoehen + for( void* pAct = First(); pAct; pAct = Next() ) + ( (MetaAction*) pAct )->Duplicate(); + + if( rMtf.pLabelList ) + pLabelList = new ImpLabelList( *rMtf.pLabelList ); + else + pLabelList = NULL; + + aPrefMapMode = rMtf.aPrefMapMode; + aPrefSize = rMtf.aPrefSize; + aHookHdlLink = rMtf.aHookHdlLink; + pPrev = rMtf.pPrev; + pNext = rMtf.pNext; + pOutDev = NULL; + bPause = sal_False; + bRecord = sal_False; + bUseCanvas = rMtf.bUseCanvas; + + if( rMtf.bRecord ) + { + Record( rMtf.pOutDev ); + + if( rMtf.bPause ) + Pause( sal_True ); + } + } + + return *this; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::operator==( const GDIMetaFile& rMtf ) const +{ + const sal_uLong nObjCount = Count(); + sal_Bool bRet = sal_False; + + if( this == &rMtf ) + bRet = sal_True; + else if( rMtf.GetActionCount() == nObjCount && + rMtf.GetPrefSize() == aPrefSize && + rMtf.GetPrefMapMode() == aPrefMapMode ) + { + bRet = sal_True; + + for( sal_uLong n = 0UL; n < nObjCount; n++ ) + { + if( GetObject( n ) != rMtf.GetObject( n ) ) + { + bRet = sal_False; + break; + } + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::IsEqual( const GDIMetaFile& rMtf ) const +{ + const sal_uLong nObjCount = Count(); + sal_Bool bRet = sal_False; + + if( this == &rMtf ) + bRet = sal_True; + else if( rMtf.GetActionCount() == nObjCount && + rMtf.GetPrefSize() == aPrefSize && + rMtf.GetPrefMapMode() == aPrefMapMode ) + { + bRet = sal_True; + + for( sal_uLong n = 0UL; n < nObjCount; n++ ) + { + if(!((MetaAction*)GetObject( n ))->IsEqual(*((MetaAction*)rMtf.GetObject( n )))) + { + bRet = sal_False; + break; + } + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Clear() +{ + if( bRecord ) + Stop(); + + for( void* pAct = First(); pAct; pAct = Next() ) + ( (MetaAction*) pAct )->Delete(); + + List::Clear(); + + delete pLabelList; + pLabelList = NULL; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Linker( OutputDevice* pOut, sal_Bool bLink ) +{ + if( bLink ) + { + pNext = NULL; + pPrev = pOut->GetConnectMetaFile(); + pOut->SetConnectMetaFile( this ); + + if( pPrev ) + pPrev->pNext = this; + } + else + { + if( pNext ) + { + pNext->pPrev = pPrev; + + if( pPrev ) + pPrev->pNext = pNext; + } + else + { + if( pPrev ) + pPrev->pNext = NULL; + + pOut->SetConnectMetaFile( pPrev ); + } + + pPrev = NULL; + pNext = NULL; + } +} + +// ------------------------------------------------------------------------ + +long GDIMetaFile::Hook() +{ + return aHookHdlLink.Call( this ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Record( OutputDevice* pOut ) +{ + if( bRecord ) + Stop(); + + Last(); + pOutDev = pOut; + bRecord = sal_True; + Linker( pOut, sal_True ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Play( GDIMetaFile& rMtf, sal_uLong nPos ) +{ + if ( !bRecord && !rMtf.bRecord ) + { + MetaAction* pAction = GetCurAction(); + const sal_uLong nObjCount = Count(); + + rMtf.UseCanvas( rMtf.GetUseCanvas() || bUseCanvas ); + + if( nPos > nObjCount ) + nPos = nObjCount; + + for( sal_uLong nCurPos = GetCurPos(); nCurPos < nPos; nCurPos++ ) + { + if( !Hook() ) + { + pAction->Duplicate(); + rMtf.AddAction( pAction ); + } + + pAction = (MetaAction*) Next(); + } + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Play( OutputDevice* pOut, sal_uLong nPos ) +{ + if( !bRecord ) + { + MetaAction* pAction = GetCurAction(); + const sal_uLong nObjCount = Count(); + sal_uLong i = 0, nSyncCount = ( pOut->GetOutDevType() == OUTDEV_WINDOW ) ? 0x000000ff : 0xffffffff; + + if( nPos > nObjCount ) + nPos = nObjCount; + + // #i23407# Set backwards-compatible text language and layout mode + // This is necessary, since old metafiles don't even know of these + // recent add-ons. Newer metafiles must of course explicitely set + // those states. + pOut->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE ); + pOut->SetLayoutMode( 0 ); + pOut->SetDigitLanguage( 0 ); + + for( sal_uLong nCurPos = GetCurPos(); nCurPos < nPos; nCurPos++ ) + { + if( !Hook() ) + { + MetaCommentAction* pCommentAct = static_cast<MetaCommentAction*>(pAction); + if( pAction->GetType() == META_COMMENT_ACTION && + pCommentAct->GetComment().Equals("DELEGATE_PLUGGABLE_RENDERER") ) + { + ImplDelegate2PluggableRenderer(pCommentAct, pOut); + } + else + { + pAction->Execute( pOut ); + } + + // flush output from time to time + if( i++ > nSyncCount ) + ( (Window*) pOut )->Flush(), i = 0; + } + + pAction = (MetaAction*) Next(); + } + + pOut->Pop(); + } +} + +// ------------------------------------------------------------------------ + +bool GDIMetaFile::ImplPlayWithRenderer( OutputDevice* pOut, const Point& rPos, Size rDestSize ) +{ + const Window* win = dynamic_cast <Window*> ( pOut ); + + if (!win) + win = Application::GetActiveTopWindow(); + if (!win) + win = Application::GetFirstTopLevelWindow(); + + if (!win) + return false; + + try { + const uno::Reference<rendering::XCanvas>& xCanvas = win->GetCanvas (); + Size aSize (rDestSize.Width () + 1, rDestSize.Height () + 1); + const uno::Reference<rendering::XBitmap>& xBitmap = xCanvas->getDevice ()->createCompatibleAlphaBitmap (vcl::unotools::integerSize2DFromSize( aSize)); + uno::Reference< lang::XMultiServiceFactory > xFactory = vcl::unohelper::GetMultiServiceFactory(); + if( xFactory.is() && xBitmap.is () ) { + uno::Reference< rendering::XMtfRenderer > xMtfRenderer; + uno::Sequence< uno::Any > args (1); + uno::Reference< rendering::XBitmapCanvas > xBitmapCanvas( xBitmap, uno::UNO_QUERY ); + if( xBitmapCanvas.is() ) { + args[0] = uno::Any( xBitmapCanvas ); + xMtfRenderer.set( xFactory->createInstanceWithArguments( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.rendering.MtfRenderer")), + args ), uno::UNO_QUERY ); + + if( xMtfRenderer.is() ) { + xBitmapCanvas->clear(); + uno::Reference< beans::XFastPropertySet > xMtfFastPropertySet( xMtfRenderer, uno::UNO_QUERY ); + if( xMtfFastPropertySet.is() ) + // set this metafile to the renderer to + // speedup things (instead of copying data to + // sequence of bytes passed to renderer) + xMtfFastPropertySet->setFastPropertyValue( 0, uno::Any( reinterpret_cast<sal_Int64>( this ) ) ); + + xMtfRenderer->draw( rDestSize.Width(), rDestSize.Height() ); + + uno::Reference< beans::XFastPropertySet > xFastPropertySet( xBitmapCanvas, uno::UNO_QUERY ); + if( xFastPropertySet.get() ) { + // 0 means get BitmapEx + uno::Any aAny = xFastPropertySet->getFastPropertyValue( 0 ); + BitmapEx* pBitmapEx = (BitmapEx*) *reinterpret_cast<const sal_Int64*>(aAny.getValue()); + if( pBitmapEx ) { + pOut->DrawBitmapEx( rPos, *pBitmapEx ); + delete pBitmapEx; + return true; + } + } + + SalBitmap* pSalBmp = ImplGetSVData()->mpDefInst->CreateSalBitmap(); + SalBitmap* pSalMask = ImplGetSVData()->mpDefInst->CreateSalBitmap(); + + if( pSalBmp->Create( xBitmapCanvas, aSize ) && pSalMask->Create( xBitmapCanvas, aSize, true ) ) { + Bitmap aBitmap( pSalBmp ); + Bitmap aMask( pSalMask ); + AlphaMask aAlphaMask( aMask ); + BitmapEx aBitmapEx( aBitmap, aAlphaMask ); + pOut->DrawBitmapEx( rPos, aBitmapEx ); + return true; + } + + delete pSalBmp; + delete pSalMask; + } + } + } + } catch( uno::RuntimeException& ) { + throw; // runtime errors are fatal + } catch( uno::Exception& ) { + // ignore errors, no way of reporting them here + } + + return false; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::ImplDelegate2PluggableRenderer( const MetaCommentAction* pAct, OutputDevice* pOut ) +{ + OSL_ASSERT( pAct->GetComment().Equals("DELEGATE_PLUGGABLE_RENDERER") ); + + // read payload - string of service name, followed by raw render input + const sal_uInt8* pData = pAct->GetData(); + const sal_uInt8* const pEndData = pData + pAct->GetDataSize(); + if( !pData ) + return; + + ::rtl::OUStringBuffer aBuffer; + while( pData<pEndData && *pData ) + aBuffer.append(static_cast<sal_Unicode>(*pData++)); + const ::rtl::OUString aRendererServiceName=aBuffer.makeStringAndClear(); + ++pData; + + while( pData<pEndData && *pData ) + aBuffer.append(static_cast<sal_Unicode>(*pData++)); + const ::rtl::OUString aGraphicServiceName=aBuffer.makeStringAndClear(); + ++pData; + + uno::Reference< lang::XMultiServiceFactory > xFactory = vcl::unohelper::GetMultiServiceFactory(); + if( pData<pEndData && xFactory.is() ) + { + try + { + // instantiate render service + uno::Sequence<uno::Any> aRendererArgs(1); + aRendererArgs[0] = makeAny(uno::Reference<awt::XGraphics>(pOut->CreateUnoGraphics())); + uno::Reference<graphic::XGraphicRenderer> xRenderer( + xFactory->createInstanceWithArguments( + aRendererServiceName, + aRendererArgs), + uno::UNO_QUERY ); + + // instantiate graphic service + uno::Reference<graphic::XGraphic> xGraphic( + xFactory->createInstance( + aGraphicServiceName), + uno::UNO_QUERY ); + + uno::Reference<lang::XInitialization> xInit( + xGraphic, uno::UNO_QUERY); + + if(xGraphic.is() && xRenderer.is() && xInit.is()) + { + // delay intialization of XGraphic, to only expose + // XGraphic-generating services to arbitrary binary data + uno::Sequence< sal_Int8 > aSeq( + (sal_Int8*)&pData, pEndData-pData ); + uno::Sequence<uno::Any> aGraphicsArgs(1); + aGraphicsArgs[0] = makeAny(aSeq); + xInit->initialize(aGraphicsArgs); + + xRenderer->render(xGraphic); + } + } + catch( uno::RuntimeException& ) + { + // runtime errors are fatal + throw; + } + catch( uno::Exception& ) + { + // ignore errors, no way of reporting them here + } + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Play( OutputDevice* pOut, const Point& rPos, + const Size& rSize, sal_uLong nPos ) +{ + Region aDrawClipRegion; + MapMode aDrawMap( GetPrefMapMode() ); + Size aDestSize( pOut->LogicToPixel( rSize ) ); + + if( aDestSize.Width() && aDestSize.Height() ) + { + GDIMetaFile* pMtf = pOut->GetConnectMetaFile(); + + if( bUseCanvas && !pMtf && ImplPlayWithRenderer( pOut, rPos, aDestSize ) ) + return; + + Size aTmpPrefSize( pOut->LogicToPixel( GetPrefSize(), aDrawMap ) ); + + if( !aTmpPrefSize.Width() ) + aTmpPrefSize.Width() = aDestSize.Width(); + + if( !aTmpPrefSize.Height() ) + aTmpPrefSize.Height() = aDestSize.Height(); + + Fraction aScaleX( aDestSize.Width(), aTmpPrefSize.Width() ); + Fraction aScaleY( aDestSize.Height(), aTmpPrefSize.Height() ); + + aScaleX *= aDrawMap.GetScaleX(); aDrawMap.SetScaleX( aScaleX ); + aScaleY *= aDrawMap.GetScaleY(); aDrawMap.SetScaleY( aScaleY ); + + // #i47260# Convert logical output position to offset within + // the metafile's mapmode. Therefore, disable pixel offset on + // outdev, it's inverse mnOutOffLogicX/Y is calculated for a + // different mapmode (the one currently set on pOut, that is) + // - thus, aDrawMap's origin would generally be wrong. And + // even _if_ aDrawMap is similar to pOutDev's current mapmode, + // it's _still_ undesirable to have pixel offset unequal zero, + // because one would still get round-off errors (the + // round-trip error for LogicToPixel( PixelToLogic() ) was the + // reason for having pixel offset in the first place). + const Size& rOldOffset( pOut->GetPixelOffset() ); + const Size aEmptySize; + pOut->SetPixelOffset( aEmptySize ); + aDrawMap.SetOrigin( pOut->PixelToLogic( pOut->LogicToPixel( rPos ), aDrawMap ) ); + pOut->SetPixelOffset( rOldOffset ); + + pOut->Push(); + + if ( pMtf && pMtf->IsRecord() && ( pOut->GetOutDevType() != OUTDEV_PRINTER ) ) + pOut->SetRelativeMapMode( aDrawMap ); + else + pOut->SetMapMode( aDrawMap ); + + // #i23407# Set backwards-compatible text language and layout mode + // This is necessary, since old metafiles don't even know of these + // recent add-ons. Newer metafiles must of course explicitely set + // those states. + pOut->SetLayoutMode( 0 ); + pOut->SetDigitLanguage( 0 ); + + Play( pOut, nPos ); + + pOut->Pop(); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Pause( sal_Bool _bPause ) +{ + if( bRecord ) + { + if( _bPause ) + { + if( !bPause ) + Linker( pOutDev, sal_False ); + } + else + { + if( bPause ) + Linker( pOutDev, sal_True ); + } + + bPause = _bPause; + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Stop() +{ + if( bRecord ) + { + bRecord = sal_False; + + if( !bPause ) + Linker( pOutDev, sal_False ); + else + bPause = sal_False; + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::WindStart() +{ + if( !bRecord ) + First(); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::WindEnd() +{ + if( !bRecord ) + Last(); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Wind( sal_uLong nActionPos ) +{ + if( !bRecord ) + Seek( nActionPos ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::WindPrev() +{ + if( !bRecord ) + Prev(); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::WindNext() +{ + if( !bRecord ) + Next(); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::AddAction( MetaAction* pAction ) +{ + Insert( pAction, LIST_APPEND ); + + if( pPrev ) + { + pAction->Duplicate(); + pPrev->AddAction( pAction ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::AddAction( MetaAction* pAction, sal_uLong nPos ) +{ + Insert( pAction, nPos ); + + if( pPrev ) + { + pAction->Duplicate(); + pPrev->AddAction( pAction, nPos ); + } +} + +// ------------------------------------------------------------------------ + +// @since #110496# +void GDIMetaFile::RemoveAction( sal_uLong nPos ) +{ + Remove( nPos ); + + if( pPrev ) + pPrev->RemoveAction( nPos ); +} + +// ------------------------------------------------------------------------ + +MetaAction* GDIMetaFile::CopyAction( sal_uLong nPos ) const +{ + return ( (MetaAction*) GetObject( nPos ) )->Clone(); +} + +// ------------------------------------------------------------------------ + +sal_uLong GDIMetaFile::GetActionPos( const String& rLabel ) +{ + ImpLabel* pLabel = NULL; + + if( pLabelList ) + pLabel = pLabelList->ImplGetLabel( pLabelList->ImplGetLabelPos( rLabel ) ); + else + pLabel = NULL; + + return( pLabel ? pLabel->nActionPos : METAFILE_LABEL_NOTFOUND ); +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::InsertLabel( const String& rLabel, sal_uLong nActionPos ) +{ + sal_Bool bRet = sal_False; + + if( !pLabelList ) + pLabelList = new ImpLabelList; + + if( pLabelList->ImplGetLabelPos( rLabel ) == METAFILE_LABEL_NOTFOUND ) + { + pLabelList->ImplInsert( new ImpLabel( rLabel, nActionPos ) ); + bRet = sal_True; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::RemoveLabel( const String& rLabel ) +{ + if( pLabelList ) + { + const sal_uLong nLabelPos = pLabelList->ImplGetLabelPos( rLabel ); + + if( nLabelPos != METAFILE_LABEL_NOTFOUND ) + delete pLabelList->ImplRemove( nLabelPos ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::RenameLabel( const String& rLabel, const String& rNewLabel ) +{ + if( pLabelList ) + { + const sal_uLong nLabelPos = pLabelList->ImplGetLabelPos( rLabel ); + + if ( nLabelPos != METAFILE_LABEL_NOTFOUND ) + pLabelList->ImplGetLabel( nLabelPos )->aLabelName = rNewLabel; + } +} + +// ------------------------------------------------------------------------ + +sal_uLong GDIMetaFile::GetLabelCount() const +{ + return( pLabelList ? pLabelList->ImplCount() : 0UL ); +} + +// ------------------------------------------------------------------------ + +String GDIMetaFile::GetLabel( sal_uLong nLabel ) +{ + String aString; + + if( pLabelList ) + { + const ImpLabel* pLabel = pLabelList->ImplGetLabel( nLabel ); + + if( pLabel ) + aString = pLabel->aLabelName; + } + + return aString; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::SaveStatus() +{ + if ( bRecord ) + { + if ( bPause ) + Linker( pOutDev, sal_True ); + + AddAction( new MetaLineColorAction( pOutDev->GetLineColor(), + pOutDev->IsLineColor() ) ); + AddAction( new MetaFillColorAction( pOutDev->GetFillColor(), + pOutDev->IsFillColor() ) ); + AddAction( new MetaFontAction( pOutDev->GetFont() ) ); + AddAction( new MetaTextColorAction( pOutDev->GetTextColor() ) ); + AddAction( new MetaTextFillColorAction( pOutDev->GetTextFillColor(), + pOutDev->IsTextFillColor() ) ); + AddAction( new MetaTextLineColorAction( pOutDev->GetTextLineColor(), + pOutDev->IsTextLineColor() ) ); + AddAction( new MetaOverlineColorAction( pOutDev->GetOverlineColor(), + pOutDev->IsOverlineColor() ) ); + AddAction( new MetaTextAlignAction( pOutDev->GetTextAlign() ) ); + AddAction( new MetaRasterOpAction( pOutDev->GetRasterOp() ) ); + AddAction( new MetaMapModeAction( pOutDev->GetMapMode() ) ); + AddAction( new MetaClipRegionAction( pOutDev->GetClipRegion(), + pOutDev->IsClipRegion() ) ); + + if ( bPause ) + Linker( pOutDev, sal_False ); + + return sal_True; + } + else + return sal_False; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::Mirror( sal_uLong nMirrorFlags ) +{ + const Size aOldPrefSize( GetPrefSize() ); + long nMoveX, nMoveY; + double fScaleX, fScaleY; + sal_Bool bRet; + + if( nMirrorFlags & MTF_MIRROR_HORZ ) + nMoveX = SAL_ABS( aOldPrefSize.Width() ) - 1, fScaleX = -1.0; + else + nMoveX = 0, fScaleX = 1.0; + + if( nMirrorFlags & MTF_MIRROR_VERT ) + nMoveY = SAL_ABS( aOldPrefSize.Height() ) - 1, fScaleY = -1.0; + else + nMoveY = 0, fScaleY = 1.0; + + if( ( fScaleX != 1.0 ) || ( fScaleY != 1.0 ) ) + { + Scale( fScaleX, fScaleY ); + Move( nMoveX, nMoveY ); + SetPrefSize( aOldPrefSize ); + bRet = sal_True; + } + else + bRet = sal_False; + + return bRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Move( long nX, long nY ) +{ + const Size aBaseOffset( nX, nY ); + Size aOffset( aBaseOffset ); + VirtualDevice aMapVDev; + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + for( MetaAction* pAct = (MetaAction*) First(); pAct; pAct = (MetaAction*) Next() ) + { + const long nType = pAct->GetType(); + MetaAction* pModAct; + + if( pAct->GetRefCount() > 1 ) + { + Replace( pModAct = pAct->Clone(), GetCurPos() ); + pAct->Delete(); + } + else + pModAct = pAct; + + if( ( META_MAPMODE_ACTION == nType ) || + ( META_PUSH_ACTION == nType ) || + ( META_POP_ACTION == nType ) ) + { + pModAct->Execute( &aMapVDev ); + aOffset = aMapVDev.LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev.GetMapMode() ); + } + + pModAct->Move( aOffset.Width(), aOffset.Height() ); + } +} + +void GDIMetaFile::Move( long nX, long nY, long nDPIX, long nDPIY ) +{ + const Size aBaseOffset( nX, nY ); + Size aOffset( aBaseOffset ); + VirtualDevice aMapVDev; + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetReferenceDevice( nDPIX, nDPIY ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + for( MetaAction* pAct = (MetaAction*) First(); pAct; pAct = (MetaAction*) Next() ) + { + const long nType = pAct->GetType(); + MetaAction* pModAct; + + if( pAct->GetRefCount() > 1 ) + { + Replace( pModAct = pAct->Clone(), GetCurPos() ); + pAct->Delete(); + } + else + pModAct = pAct; + + if( ( META_MAPMODE_ACTION == nType ) || + ( META_PUSH_ACTION == nType ) || + ( META_POP_ACTION == nType ) ) + { + pModAct->Execute( &aMapVDev ); + if( aMapVDev.GetMapMode().GetMapUnit() == MAP_PIXEL ) + { + aOffset = aMapVDev.LogicToPixel( aBaseOffset, GetPrefMapMode() ); + MapMode aMap( aMapVDev.GetMapMode() ); + aOffset.Width() = static_cast<long>(aOffset.Width() * (double)aMap.GetScaleX()); + aOffset.Height() = static_cast<long>(aOffset.Height() * (double)aMap.GetScaleY()); + } + else + aOffset = aMapVDev.LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev.GetMapMode() ); + } + + pModAct->Move( aOffset.Width(), aOffset.Height() ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Scale( double fScaleX, double fScaleY ) +{ + for( MetaAction* pAct = (MetaAction*) First(); pAct; pAct = (MetaAction*) Next() ) + { + MetaAction* pModAct; + + if( pAct->GetRefCount() > 1 ) + { + Replace( pModAct = pAct->Clone(), GetCurPos() ); + pAct->Delete(); + } + else + pModAct = pAct; + + pModAct->Scale( fScaleX, fScaleY ); + } + + aPrefSize.Width() = FRound( aPrefSize.Width() * fScaleX ); + aPrefSize.Height() = FRound( aPrefSize.Height() * fScaleY ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Scale( const Fraction& rScaleX, const Fraction& rScaleY ) +{ + Scale( (double) rScaleX, (double) rScaleY ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Clip( const Rectangle& i_rClipRect ) +{ + Rectangle aCurRect( i_rClipRect ); + VirtualDevice aMapVDev; + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + for( MetaAction* pAct = (MetaAction*) First(); pAct; pAct = (MetaAction*) Next() ) + { + const long nType = pAct->GetType(); + + if( ( META_MAPMODE_ACTION == nType ) || + ( META_PUSH_ACTION == nType ) || + ( META_POP_ACTION == nType ) ) + { + pAct->Execute( &aMapVDev ); + aCurRect = aMapVDev.LogicToLogic( i_rClipRect, GetPrefMapMode(), aMapVDev.GetMapMode() ); + } + else if( nType == META_CLIPREGION_ACTION ) + { + MetaClipRegionAction* pOldAct = (MetaClipRegionAction*)pAct; + Region aNewReg( aCurRect ); + if( pOldAct->IsClipping() ) + aNewReg.Intersect( pOldAct->GetRegion() ); + MetaClipRegionAction* pNewAct = new MetaClipRegionAction( aNewReg, sal_True ); + Replace( pNewAct, GetCurPos() ); + pOldAct->Delete(); + } + } +} + +// ------------------------------------------------------------------------ + +Point GDIMetaFile::ImplGetRotatedPoint( const Point& rPt, const Point& rRotatePt, + const Size& rOffset, double fSin, double fCos ) +{ + const long nX = rPt.X() - rRotatePt.X(); + const long nY = rPt.Y() - rRotatePt.Y(); + + return Point( FRound( fCos * nX + fSin * nY ) + rRotatePt.X() + rOffset.Width(), + -FRound( fSin * nX - fCos * nY ) + rRotatePt.Y() + rOffset.Height() ); +} + +// ------------------------------------------------------------------------ + +Polygon GDIMetaFile::ImplGetRotatedPolygon( const Polygon& rPoly, const Point& rRotatePt, + const Size& rOffset, double fSin, double fCos ) +{ + Polygon aRet( rPoly ); + + aRet.Rotate( rRotatePt, fSin, fCos ); + aRet.Move( rOffset.Width(), rOffset.Height() ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +PolyPolygon GDIMetaFile::ImplGetRotatedPolyPolygon( const PolyPolygon& rPolyPoly, const Point& rRotatePt, + const Size& rOffset, double fSin, double fCos ) +{ + PolyPolygon aRet( rPolyPoly ); + + aRet.Rotate( rRotatePt, fSin, fCos ); + aRet.Move( rOffset.Width(), rOffset.Height() ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::ImplAddGradientEx( GDIMetaFile& rMtf, + const OutputDevice& rMapDev, + const PolyPolygon& rPolyPoly, + const Gradient& rGrad ) +{ + // #105055# Generate comment, GradientEx and Gradient actions + // (within DrawGradient) + VirtualDevice aVDev( rMapDev, 0 ); + aVDev.EnableOutput( sal_False ); + GDIMetaFile aGradMtf; + + aGradMtf.Record( &aVDev ); + aVDev.DrawGradient( rPolyPoly, rGrad ); + aGradMtf.Stop(); + + int i, nAct( aGradMtf.GetActionCount() ); + for( i=0; i<nAct; ++i ) + { + MetaAction* pMetaAct = aGradMtf.GetAction(i); + pMetaAct->Duplicate(); + rMtf.AddAction( pMetaAct ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Rotate( long nAngle10 ) +{ + nAngle10 %= 3600L; + nAngle10 = ( nAngle10 < 0L ) ? ( 3599L + nAngle10 ) : nAngle10; + + if( nAngle10 ) + { + GDIMetaFile aMtf; + VirtualDevice aMapVDev; + const double fAngle = F_PI1800 * nAngle10; + const double fSin = sin( fAngle ); + const double fCos = cos( fAngle ); + Rectangle aRect=Rectangle( Point(), GetPrefSize() ); + Polygon aPoly( aRect ); + + aPoly.Rotate( Point(), fSin, fCos ); + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + const Rectangle aNewBound( aPoly.GetBoundRect() ); + + const Point aOrigin( GetPrefMapMode().GetOrigin().X(), GetPrefMapMode().GetOrigin().Y() ); + const Size aOffset( -aNewBound.Left(), -aNewBound.Top() ); + + Point aRotAnchor( aOrigin ); + Size aRotOffset( aOffset ); + + for( MetaAction* pAction = (MetaAction*) First(); pAction; pAction = (MetaAction*) Next() ) + { + const sal_uInt16 nActionType = pAction->GetType(); + + switch( nActionType ) + { + case( META_PIXEL_ACTION ): + { + MetaPixelAction* pAct = (MetaPixelAction*) pAction; + aMtf.AddAction( new MetaPixelAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetColor() ) ); + } + break; + + case( META_POINT_ACTION ): + { + MetaPointAction* pAct = (MetaPointAction*) pAction; + aMtf.AddAction( new MetaPointAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_LINE_ACTION ): + { + MetaLineAction* pAct = (MetaLineAction*) pAction; + aMtf.AddAction( new MetaLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + ImplGetRotatedPoint( pAct->GetEndPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetLineInfo() ) ); + } + break; + + case( META_RECT_ACTION ): + { + MetaRectAction* pAct = (MetaRectAction*) pAction; + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_ROUNDRECT_ACTION ): + { + MetaRoundRectAction* pAct = (MetaRoundRectAction*) pAction; + const Polygon aRoundRectPoly( pAct->GetRect(), pAct->GetHorzRound(), pAct->GetVertRound() ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aRoundRectPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_ELLIPSE_ACTION ): + { + MetaEllipseAction* pAct = (MetaEllipseAction*) pAction; + const Polygon aEllipsePoly( pAct->GetRect().Center(), pAct->GetRect().GetWidth() >> 1, pAct->GetRect().GetHeight() >> 1 ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aEllipsePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_ARC_ACTION ): + { + MetaArcAction* pAct = (MetaArcAction*) pAction; + const Polygon aArcPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), POLY_ARC ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aArcPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_PIE_ACTION ): + { + MetaPieAction* pAct = (MetaPieAction*) pAction; + const Polygon aPiePoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), POLY_PIE ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aPiePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_CHORD_ACTION ): + { + MetaChordAction* pAct = (MetaChordAction*) pAction; + const Polygon aChordPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), POLY_CHORD ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aChordPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_POLYLINE_ACTION ): + { + MetaPolyLineAction* pAct = (MetaPolyLineAction*) pAction; + aMtf.AddAction( new MetaPolyLineAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->GetLineInfo() ) ); + } + break; + + case( META_POLYGON_ACTION ): + { + MetaPolygonAction* pAct = (MetaPolygonAction*) pAction; + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_POLYPOLYGON_ACTION ): + { + MetaPolyPolygonAction* pAct = (MetaPolyPolygonAction*) pAction; + aMtf.AddAction( new MetaPolyPolygonAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_TEXT_ACTION ): + { + MetaTextAction* pAct = (MetaTextAction*) pAction; + aMtf.AddAction( new MetaTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) ); + } + break; + + case( META_TEXTARRAY_ACTION ): + { + MetaTextArrayAction* pAct = (MetaTextArrayAction*) pAction; + aMtf.AddAction( new MetaTextArrayAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetText(), pAct->GetDXArray(), pAct->GetIndex(), pAct->GetLen() ) ); + } + break; + + case( META_STRETCHTEXT_ACTION ): + { + MetaStretchTextAction* pAct = (MetaStretchTextAction*) pAction; + aMtf.AddAction( new MetaStretchTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetWidth(), pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) ); + } + break; + + case( META_TEXTLINE_ACTION ): + { + MetaTextLineAction* pAct = (MetaTextLineAction*) pAction; + aMtf.AddAction( new MetaTextLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetWidth(), pAct->GetStrikeout(), pAct->GetUnderline(), pAct->GetOverline() ) ); + } + break; + + case( META_BMPSCALE_ACTION ): + { + MetaBmpScaleAction* pAct = (MetaBmpScaleAction*) pAction; + Polygon aBmpPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aBmpRect( aBmpPoly.GetBoundRect() ); + BitmapEx aBmpEx( pAct->GetBitmap() ); + + aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) ); + aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), + aBmpEx ) ); + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + MetaBmpScalePartAction* pAct = (MetaBmpScalePartAction*) pAction; + Polygon aBmpPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aBmpRect( aBmpPoly.GetBoundRect() ); + BitmapEx aBmpEx( pAct->GetBitmap() ); + + aBmpEx.Crop( Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) ); + aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) ); + + aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) ); + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + MetaBmpExScaleAction* pAct = (MetaBmpExScaleAction*) pAction; + Polygon aBmpPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aBmpRect( aBmpPoly.GetBoundRect() ); + BitmapEx aBmpEx( pAct->GetBitmapEx() ); + + aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) ); + + aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) ); + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + MetaBmpExScalePartAction* pAct = (MetaBmpExScalePartAction*) pAction; + Polygon aBmpPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aBmpRect( aBmpPoly.GetBoundRect() ); + BitmapEx aBmpEx( pAct->GetBitmapEx() ); + + aBmpEx.Crop( Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) ); + aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) ); + + aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) ); + } + break; + + case( META_GRADIENT_ACTION ): + { + MetaGradientAction* pAct = (MetaGradientAction*) pAction; + + ImplAddGradientEx( aMtf, aMapVDev, + ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetGradient() ); + } + break; + + case( META_GRADIENTEX_ACTION ): + { + MetaGradientExAction* pAct = (MetaGradientExAction*) pAction; + aMtf.AddAction( new MetaGradientExAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetGradient() ) ); + } + break; + + // #105055# Handle gradientex comment block correctly + case( META_COMMENT_ACTION ): + { + MetaCommentAction* pCommentAct = (MetaCommentAction*) pAction; + if( pCommentAct->GetComment().Equals( "XGRAD_SEQ_BEGIN" ) ) + { + int nBeginComments( 1 ); + pAction = (MetaAction*) Next(); + + // skip everything, except gradientex action + while( pAction ) + { + const sal_uInt16 nType = pAction->GetType(); + + if( META_GRADIENTEX_ACTION == nType ) + { + // Add rotated gradientex + MetaGradientExAction* pAct = (MetaGradientExAction*) pAction; + ImplAddGradientEx( aMtf, aMapVDev, + ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetGradient() ); + } + else if( META_COMMENT_ACTION == nType) + { + MetaCommentAction* pAct = (MetaCommentAction*) pAction; + if( pAct->GetComment().Equals( "XGRAD_SEQ_END" ) ) + { + // handle nested blocks + --nBeginComments; + + // gradientex comment block: end reached, done. + if( !nBeginComments ) + break; + } + else if( pAct->GetComment().Equals( "XGRAD_SEQ_BEGIN" ) ) + { + // handle nested blocks + ++nBeginComments; + } + + } + + pAction = (MetaAction*) Next(); + } + } + else + { + sal_Bool bPathStroke = pCommentAct->GetComment().Equals( "XPATHSTROKE_SEQ_BEGIN" ); + if ( bPathStroke || pCommentAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) ) + { + if ( pCommentAct->GetDataSize() ) + { + SvMemoryStream aMemStm( (void*)pCommentAct->GetData(), pCommentAct->GetDataSize(), STREAM_READ ); + SvMemoryStream aDest; + if ( bPathStroke ) + { + SvtGraphicStroke aStroke; + aMemStm >> aStroke; + Polygon aPath; + aStroke.getPath( aPath ); + aStroke.setPath( ImplGetRotatedPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) ); + aDest << aStroke; + aMtf.AddAction( new MetaCommentAction( "XPATHSTROKE_SEQ_BEGIN", 0, + static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) ); + } + else + { + SvtGraphicFill aFill; + aMemStm >> aFill; + PolyPolygon aPath; + aFill.getPath( aPath ); + aFill.setPath( ImplGetRotatedPolyPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) ); + aDest << aFill; + aMtf.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0, + static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) ); + } + } + } + else if ( pCommentAct->GetComment().Equals( "XPATHSTROKE_SEQ_END" ) + || pCommentAct->GetComment().Equals( "XPATHFILL_SEQ_END" ) ) + { + pAction->Execute( &aMapVDev ); + pAction->Duplicate(); + aMtf.AddAction( pAction ); + } + } + } + break; + + case( META_HATCH_ACTION ): + { + MetaHatchAction* pAct = (MetaHatchAction*) pAction; + Hatch aHatch( pAct->GetHatch() ); + + aHatch.SetAngle( aHatch.GetAngle() + (sal_uInt16) nAngle10 ); + aMtf.AddAction( new MetaHatchAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), + aHatch ) ); + } + break; + + case( META_TRANSPARENT_ACTION ): + { + MetaTransparentAction* pAct = (MetaTransparentAction*) pAction; + aMtf.AddAction( new MetaTransparentAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetTransparence() ) ); + } + break; + + case( META_FLOATTRANSPARENT_ACTION ): + { + MetaFloatTransparentAction* pAct = (MetaFloatTransparentAction*) pAction; + GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() ); + Polygon aMtfPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aMtfRect( aMtfPoly.GetBoundRect() ); + + aTransMtf.Rotate( nAngle10 ); + aMtf.AddAction( new MetaFloatTransparentAction( aTransMtf, aMtfRect.TopLeft(), aMtfRect.GetSize(), + pAct->GetGradient() ) ); + } + break; + + case( META_EPS_ACTION ): + { + MetaEPSAction* pAct = (MetaEPSAction*) pAction; + GDIMetaFile aEPSMtf( pAct->GetSubstitute() ); + Polygon aEPSPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aEPSRect( aEPSPoly.GetBoundRect() ); + + aEPSMtf.Rotate( nAngle10 ); + aMtf.AddAction( new MetaEPSAction( aEPSRect.TopLeft(), aEPSRect.GetSize(), + pAct->GetLink(), aEPSMtf ) ); + } + break; + + case( META_CLIPREGION_ACTION ): + { + MetaClipRegionAction* pAct = (MetaClipRegionAction*) pAction; + + if( pAct->IsClipping() && pAct->GetRegion().HasPolyPolygon() ) + aMtf.AddAction( new MetaClipRegionAction( Region( ImplGetRotatedPolyPolygon( pAct->GetRegion().GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ), sal_True ) ); + else + { + pAction->Duplicate(); + aMtf.AddAction( pAction ); + } + } + break; + + case( META_ISECTRECTCLIPREGION_ACTION ): + { + MetaISectRectClipRegionAction* pAct = (MetaISectRectClipRegionAction*) pAction; + aMtf.AddAction( new MetaISectRegionClipRegionAction( ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_ISECTREGIONCLIPREGION_ACTION ): + { + MetaISectRegionClipRegionAction* pAct = (MetaISectRegionClipRegionAction*) pAction; + const Region& rRegion = pAct->GetRegion(); + + if( rRegion.HasPolyPolygon() ) + aMtf.AddAction( new MetaISectRegionClipRegionAction( Region( ImplGetRotatedPolyPolygon( rRegion.GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ) ); + else + { + pAction->Duplicate(); + aMtf.AddAction( pAction ); + } + } + break; + + case( META_REFPOINT_ACTION ): + { + MetaRefPointAction* pAct = (MetaRefPointAction*) pAction; + aMtf.AddAction( new MetaRefPointAction( ImplGetRotatedPoint( pAct->GetRefPoint(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->IsSetting() ) ); + } + break; + + case( META_FONT_ACTION ): + { + MetaFontAction* pAct = (MetaFontAction*) pAction; + Font aFont( pAct->GetFont() ); + + aFont.SetOrientation( aFont.GetOrientation() + (sal_uInt16) nAngle10 ); + aMtf.AddAction( new MetaFontAction( aFont ) ); + } + break; + + case( META_BMP_ACTION ): + case( META_BMPEX_ACTION ): + case( META_MASK_ACTION ): + case( META_MASKSCALE_ACTION ): + case( META_MASKSCALEPART_ACTION ): + case( META_WALLPAPER_ACTION ): + case( META_TEXTRECT_ACTION ): + case( META_MOVECLIPREGION_ACTION ): + { + OSL_FAIL( "GDIMetaFile::Rotate(): unsupported action" ); + } + break; + + default: + { + pAction->Execute( &aMapVDev ); + pAction->Duplicate(); + aMtf.AddAction( pAction ); + + // update rotation point and offset, if necessary + if( ( META_MAPMODE_ACTION == nActionType ) || + ( META_PUSH_ACTION == nActionType ) || + ( META_POP_ACTION == nActionType ) ) + { + aRotAnchor = aMapVDev.LogicToLogic( aOrigin, aPrefMapMode, aMapVDev.GetMapMode() ); + aRotOffset = aMapVDev.LogicToLogic( aOffset, aPrefMapMode, aMapVDev.GetMapMode() ); + } + } + break; + } + } + + aMtf.aPrefMapMode = aPrefMapMode; + aMtf.aPrefSize = aNewBound.GetSize(); + + *this = aMtf; + } +} + +// ------------------------------------------------------------------------ + +static void ImplActionBounds( Rectangle& o_rOutBounds, + const Rectangle& i_rInBounds, + const std::vector<Rectangle>& i_rClipStack ) +{ + Rectangle aBounds( i_rInBounds ); + if( ! i_rInBounds.IsEmpty() && ! i_rClipStack.empty() && ! i_rClipStack.back().IsEmpty() ) + aBounds.Intersection( i_rClipStack.back() ); + if( ! aBounds.IsEmpty() ) + { + if( ! o_rOutBounds.IsEmpty() ) + o_rOutBounds.Union( aBounds ); + else + o_rOutBounds = aBounds; + } +} + +Rectangle GDIMetaFile::GetBoundRect( OutputDevice& i_rReference ) +{ + GDIMetaFile aMtf; + VirtualDevice aMapVDev( i_rReference ); + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + std::vector<Rectangle> aClipStack( 1, Rectangle() ); + std::vector<sal_uInt16> aPushFlagStack; + + Rectangle aBound; + + for( MetaAction* pAction = (MetaAction*) First(); pAction; pAction = (MetaAction*) Next() ) + { + const sal_uInt16 nActionType = pAction->GetType(); + + switch( nActionType ) + { + case( META_PIXEL_ACTION ): + { + MetaPixelAction* pAct = (MetaPixelAction*) pAction; + ImplActionBounds( aBound, + Rectangle( aMapVDev.LogicToLogic( pAct->GetPoint(), aMapVDev.GetMapMode(), GetPrefMapMode() ), + aMapVDev.PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ), + aClipStack ); + } + break; + + case( META_POINT_ACTION ): + { + MetaPointAction* pAct = (MetaPointAction*) pAction; + ImplActionBounds( aBound, + Rectangle( aMapVDev.LogicToLogic( pAct->GetPoint(), aMapVDev.GetMapMode(), GetPrefMapMode() ), + aMapVDev.PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ), + aClipStack ); + } + break; + + case( META_LINE_ACTION ): + { + MetaLineAction* pAct = (MetaLineAction*) pAction; + Point aP1( pAct->GetStartPoint() ), aP2( pAct->GetEndPoint() ); + Rectangle aRect( aP1, aP2 ); + aRect.Justify(); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_RECT_ACTION ): + { + MetaRectAction* pAct = (MetaRectAction*) pAction; + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_ROUNDRECT_ACTION ): + { + MetaRoundRectAction* pAct = (MetaRoundRectAction*) pAction; + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_ELLIPSE_ACTION ): + { + MetaEllipseAction* pAct = (MetaEllipseAction*) pAction; + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_ARC_ACTION ): + { + MetaArcAction* pAct = (MetaArcAction*) pAction; + // FIXME: this is imprecise + // e.g. for small arcs the whole rectangle is WAY too large + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_PIE_ACTION ): + { + MetaPieAction* pAct = (MetaPieAction*) pAction; + // FIXME: this is imprecise + // e.g. for small arcs the whole rectangle is WAY too large + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_CHORD_ACTION ): + { + MetaChordAction* pAct = (MetaChordAction*) pAction; + // FIXME: this is imprecise + // e.g. for small arcs the whole rectangle is WAY too large + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_POLYLINE_ACTION ): + { + MetaPolyLineAction* pAct = (MetaPolyLineAction*) pAction; + Rectangle aRect( pAct->GetPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_POLYGON_ACTION ): + { + MetaPolygonAction* pAct = (MetaPolygonAction*) pAction; + Rectangle aRect( pAct->GetPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_POLYPOLYGON_ACTION ): + { + MetaPolyPolygonAction* pAct = (MetaPolyPolygonAction*) pAction; + Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TEXT_ACTION ): + { + MetaTextAction* pAct = (MetaTextAction*) pAction; + Rectangle aRect; + // hdu said base = index + aMapVDev.GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen() ); + Point aPt( pAct->GetPoint() ); + aRect.Move( aPt.X(), aPt.Y() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TEXTARRAY_ACTION ): + { + MetaTextArrayAction* pAct = (MetaTextArrayAction*) pAction; + Rectangle aRect; + // hdu said base = index + aMapVDev.GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(), + 0, pAct->GetDXArray() ); + Point aPt( pAct->GetPoint() ); + aRect.Move( aPt.X(), aPt.Y() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_STRETCHTEXT_ACTION ): + { + MetaStretchTextAction* pAct = (MetaStretchTextAction*) pAction; + Rectangle aRect; + // hdu said base = index + aMapVDev.GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(), + pAct->GetWidth(), NULL ); + Point aPt( pAct->GetPoint() ); + aRect.Move( aPt.X(), aPt.Y() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TEXTLINE_ACTION ): + { + MetaTextLineAction* pAct = (MetaTextLineAction*) pAction; + // measure a test string to get ascend and descent right + static const sal_Unicode pStr[] = { 0xc4, 0x67, 0 }; + String aStr( pStr ); + + Rectangle aRect; + aMapVDev.GetTextBoundRect( aRect, aStr, 0, 0, aStr.Len(), 0, NULL ); + Point aPt( pAct->GetStartPoint() ); + aRect.Move( aPt.X(), aPt.Y() ); + aRect.Right() = aRect.Left() + pAct->GetWidth(); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPSCALE_ACTION ): + { + MetaBmpScaleAction* pAct = (MetaBmpScaleAction*) pAction; + Rectangle aRect( pAct->GetPoint(), pAct->GetSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + MetaBmpScalePartAction* pAct = (MetaBmpScalePartAction*) pAction; + Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + MetaBmpExScaleAction* pAct = (MetaBmpExScaleAction*) pAction; + Rectangle aRect( pAct->GetPoint(), pAct->GetSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + MetaBmpExScalePartAction* pAct = (MetaBmpExScalePartAction*) pAction; + Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_GRADIENT_ACTION ): + { + MetaGradientAction* pAct = (MetaGradientAction*) pAction; + Rectangle aRect( pAct->GetRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_GRADIENTEX_ACTION ): + { + MetaGradientExAction* pAct = (MetaGradientExAction*) pAction; + Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_COMMENT_ACTION ): + { + // nothing to do + }; + break; + + case( META_HATCH_ACTION ): + { + MetaHatchAction* pAct = (MetaHatchAction*) pAction; + Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TRANSPARENT_ACTION ): + { + MetaTransparentAction* pAct = (MetaTransparentAction*) pAction; + Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_FLOATTRANSPARENT_ACTION ): + { + MetaFloatTransparentAction* pAct = (MetaFloatTransparentAction*) pAction; + GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() ); + // get the bound rect of the contained metafile + Rectangle aRect( aTransMtf.GetBoundRect( i_rReference ) ); + // scale the rect now on the assumption that the correct top left of the metafile + // (not its bounds !) is (0,0) + Size aPSize( aTransMtf.GetPrefSize() ); + aPSize = aMapVDev.LogicToLogic( aPSize, aTransMtf.GetPrefMapMode(), aMapVDev.GetMapMode() ); + Size aActSize( pAct->GetSize() ); + double fX = double(aActSize.Width())/double(aPSize.Width()); + double fY = double(aActSize.Height())/double(aPSize.Height()); + aRect.Left() = long(double(aRect.Left())*fX); + aRect.Right() = long(double(aRect.Right())*fX); + aRect.Top() = long(double(aRect.Top())*fY); + aRect.Bottom() = long(double(aRect.Bottom())*fY); + + // transform the rect to current VDev state + aRect = aMapVDev.LogicToLogic( aRect, aTransMtf.GetPrefMapMode(), aMapVDev.GetMapMode() ); + + ImplActionBounds( aBound, aRect, aClipStack ); + } + break; + + case( META_EPS_ACTION ): + { + MetaEPSAction* pAct = (MetaEPSAction*) pAction; + Rectangle aRect( pAct->GetPoint(), pAct->GetSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_CLIPREGION_ACTION ): + { + MetaClipRegionAction* pAct = (MetaClipRegionAction*) pAction; + if( pAct->IsClipping() ) + aClipStack.back() = aMapVDev.LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ); + else + aClipStack.back() = Rectangle(); + } + break; + + case( META_ISECTRECTCLIPREGION_ACTION ): + { + MetaISectRectClipRegionAction* pAct = (MetaISectRectClipRegionAction*) pAction; + Rectangle aRect( aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ) ); + if( aClipStack.back().IsEmpty() ) + aClipStack.back() = aRect; + else + aClipStack.back().Intersection( aRect ); + } + break; + + case( META_ISECTREGIONCLIPREGION_ACTION ): + { + MetaISectRegionClipRegionAction* pAct = (MetaISectRegionClipRegionAction*) pAction; + Rectangle aRect( aMapVDev.LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ) ); + if( aClipStack.back().IsEmpty() ) + aClipStack.back() = aRect; + else + aClipStack.back().Intersection( aRect ); + } + break; + + case( META_BMP_ACTION ): + { + MetaBmpAction* pAct = (MetaBmpAction*) pAction; + Rectangle aRect( pAct->GetPoint(), aMapVDev.PixelToLogic( pAct->GetBitmap().GetSizePixel() ) ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPEX_ACTION ): + { + MetaBmpExAction* pAct = (MetaBmpExAction*) pAction; + Rectangle aRect( pAct->GetPoint(), aMapVDev.PixelToLogic( pAct->GetBitmapEx().GetSizePixel() ) ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_MASK_ACTION ): + { + MetaMaskAction* pAct = (MetaMaskAction*) pAction; + Rectangle aRect( pAct->GetPoint(), aMapVDev.PixelToLogic( pAct->GetBitmap().GetSizePixel() ) ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_MASKSCALE_ACTION ): + { + MetaMaskScalePartAction* pAct = (MetaMaskScalePartAction*) pAction; + Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_MASKSCALEPART_ACTION ): + { + MetaMaskScalePartAction* pAct = (MetaMaskScalePartAction*) pAction; + Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_WALLPAPER_ACTION ): + { + MetaWallpaperAction* pAct = (MetaWallpaperAction*) pAction; + Rectangle aRect( pAct->GetRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TEXTRECT_ACTION ): + { + MetaTextRectAction* pAct = (MetaTextRectAction*) pAction; + Rectangle aRect( pAct->GetRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_MOVECLIPREGION_ACTION ): + { + MetaMoveClipRegionAction* pAct = (MetaMoveClipRegionAction*) pAction; + if( ! aClipStack.back().IsEmpty() ) + { + Size aDelta( pAct->GetHorzMove(), pAct->GetVertMove() ); + aDelta = aMapVDev.LogicToLogic( aDelta, aMapVDev.GetMapMode(), GetPrefMapMode() ); + aClipStack.back().Move( aDelta.Width(), aDelta.Width() ); + } + } + break; + + default: + { + pAction->Execute( &aMapVDev ); + + if( nActionType == META_PUSH_ACTION ) + { + MetaPushAction* pAct = (MetaPushAction*) pAction; + aPushFlagStack.push_back( pAct->GetFlags() ); + if( (aPushFlagStack.back() & PUSH_CLIPREGION) != 0 ) + { + Rectangle aRect( aClipStack.back() ); + aClipStack.push_back( aRect ); + } + } + else if( nActionType == META_POP_ACTION ) + { + // sanity check + if( ! aPushFlagStack.empty() ) + { + if( (aPushFlagStack.back() & PUSH_CLIPREGION) != 0 ) + { + if( aClipStack.size() > 1 ) + aClipStack.pop_back(); + } + aPushFlagStack.pop_back(); + } + } + } + break; + } + } + return aBound; +} + +// ------------------------------------------------------------------------ + +Color GDIMetaFile::ImplColAdjustFnc( const Color& rColor, const void* pColParam ) +{ + return Color( rColor.GetTransparency(), + ( (const ImplColAdjustParam*) pColParam )->pMapR[ rColor.GetRed() ], + ( (const ImplColAdjustParam*) pColParam )->pMapG[ rColor.GetGreen() ], + ( (const ImplColAdjustParam*) pColParam )->pMapB[ rColor.GetBlue() ] ); + +} + +// ------------------------------------------------------------------------ + +BitmapEx GDIMetaFile::ImplBmpAdjustFnc( const BitmapEx& rBmpEx, const void* pBmpParam ) +{ + const ImplBmpAdjustParam* p = (const ImplBmpAdjustParam*) pBmpParam; + BitmapEx aRet( rBmpEx ); + + aRet.Adjust( p->nLuminancePercent, p->nContrastPercent, + p->nChannelRPercent, p->nChannelGPercent, p->nChannelBPercent, + p->fGamma, p->bInvert ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +Color GDIMetaFile::ImplColConvertFnc( const Color& rColor, const void* pColParam ) +{ + sal_uInt8 cLum = rColor.GetLuminance(); + + if( MTF_CONVERSION_1BIT_THRESHOLD == ( (const ImplColConvertParam*) pColParam )->eConversion ) + cLum = ( cLum < 128 ) ? 0 : 255; + + return Color( rColor.GetTransparency(), cLum, cLum, cLum ); +} + +// ------------------------------------------------------------------------ + +BitmapEx GDIMetaFile::ImplBmpConvertFnc( const BitmapEx& rBmpEx, const void* pBmpParam ) +{ + BitmapEx aRet( rBmpEx ); + + aRet.Convert( ( (const ImplBmpConvertParam*) pBmpParam )->eConversion ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +Color GDIMetaFile::ImplColMonoFnc( const Color&, const void* pColParam ) +{ + return( ( (const ImplColMonoParam*) pColParam )->aColor ); +} + +// ------------------------------------------------------------------------ + +BitmapEx GDIMetaFile::ImplBmpMonoFnc( const BitmapEx& rBmpEx, const void* pBmpParam ) +{ + BitmapPalette aPal( 3 ); + + aPal[ 0 ] = Color( COL_BLACK ); + aPal[ 1 ] = Color( COL_WHITE ); + aPal[ 2 ] = ( (const ImplBmpMonoParam*) pBmpParam )->aColor; + + Bitmap aBmp( rBmpEx.GetSizePixel(), 4, &aPal ); + aBmp.Erase( ( (const ImplBmpMonoParam*) pBmpParam )->aColor ); + + if( rBmpEx.IsAlpha() ) + return BitmapEx( aBmp, rBmpEx.GetAlpha() ); + else if( rBmpEx.IsTransparent() ) + return BitmapEx( aBmp, rBmpEx.GetMask() ); + else + return aBmp; +} + +// ------------------------------------------------------------------------ + +Color GDIMetaFile::ImplColReplaceFnc( const Color& rColor, const void* pColParam ) +{ + const sal_uLong nR = rColor.GetRed(), nG = rColor.GetGreen(), nB = rColor.GetBlue(); + + for( sal_uLong i = 0; i < ( (const ImplColReplaceParam*) pColParam )->nCount; i++ ) + { + if( ( ( (const ImplColReplaceParam*) pColParam )->pMinR[ i ] <= nR ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMaxR[ i ] >= nR ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMinG[ i ] <= nG ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMaxG[ i ] >= nG ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMinB[ i ] <= nB ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMaxB[ i ] >= nB ) ) + { + return( ( (const ImplColReplaceParam*) pColParam )->pDstCols[ i ] ); + } + } + + return rColor; +} + +// ------------------------------------------------------------------------ + +BitmapEx GDIMetaFile::ImplBmpReplaceFnc( const BitmapEx& rBmpEx, const void* pBmpParam ) +{ + const ImplBmpReplaceParam* p = (const ImplBmpReplaceParam*) pBmpParam; + BitmapEx aRet( rBmpEx ); + + aRet.Replace( p->pSrcCols, p->pDstCols, p->nCount, p->pTols ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::ImplExchangeColors( ColorExchangeFnc pFncCol, const void* pColParam, + BmpExchangeFnc pFncBmp, const void* pBmpParam ) +{ + GDIMetaFile aMtf; + + aMtf.aPrefSize = aPrefSize; + aMtf.aPrefMapMode = aPrefMapMode; + + for( MetaAction* pAction = (MetaAction*) First(); pAction; pAction = (MetaAction*) Next() ) + { + const sal_uInt16 nType = pAction->GetType(); + + switch( nType ) + { + case( META_PIXEL_ACTION ): + { + MetaPixelAction* pAct = (MetaPixelAction*) pAction; + aMtf.Insert( new MetaPixelAction( pAct->GetPoint(), pFncCol( pAct->GetColor(), pColParam ) ), LIST_APPEND ); + } + break; + + case( META_LINECOLOR_ACTION ): + { + MetaLineColorAction* pAct = (MetaLineColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaLineColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_FILLCOLOR_ACTION ): + { + MetaFillColorAction* pAct = (MetaFillColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaFillColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_TEXTCOLOR_ACTION ): + { + MetaTextColorAction* pAct = (MetaTextColorAction*) pAction; + aMtf.Insert( new MetaTextColorAction( pFncCol( pAct->GetColor(), pColParam ) ), LIST_APPEND ); + } + break; + + case( META_TEXTFILLCOLOR_ACTION ): + { + MetaTextFillColorAction* pAct = (MetaTextFillColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaTextFillColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_TEXTLINECOLOR_ACTION ): + { + MetaTextLineColorAction* pAct = (MetaTextLineColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaTextLineColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_OVERLINECOLOR_ACTION ): + { + MetaOverlineColorAction* pAct = (MetaOverlineColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaOverlineColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_FONT_ACTION ): + { + MetaFontAction* pAct = (MetaFontAction*) pAction; + Font aFont( pAct->GetFont() ); + + aFont.SetColor( pFncCol( aFont.GetColor(), pColParam ) ); + aFont.SetFillColor( pFncCol( aFont.GetFillColor(), pColParam ) ); + aMtf.Insert( new MetaFontAction( aFont ), LIST_APPEND ); + } + break; + + case( META_WALLPAPER_ACTION ): + { + MetaWallpaperAction* pAct = (MetaWallpaperAction*) pAction; + Wallpaper aWall( pAct->GetWallpaper() ); + const Rectangle& rRect = pAct->GetRect(); + + aWall.SetColor( pFncCol( aWall.GetColor(), pColParam ) ); + + if( aWall.IsBitmap() ) + aWall.SetBitmap( pFncBmp( aWall.GetBitmap(), pBmpParam ) ); + + if( aWall.IsGradient() ) + { + Gradient aGradient( aWall.GetGradient() ); + + aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) ); + aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) ); + aWall.SetGradient( aGradient ); + } + + aMtf.Insert( new MetaWallpaperAction( rRect, aWall ), LIST_APPEND ); + } + break; + + case( META_BMP_ACTION ): + case( META_BMPEX_ACTION ): + case( META_MASK_ACTION ): + { + OSL_FAIL( "Don't use bitmap actions of this type in metafiles!" ); + } + break; + + case( META_BMPSCALE_ACTION ): + { + MetaBmpScaleAction* pAct = (MetaBmpScaleAction*) pAction; + aMtf.Insert( new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(), + pFncBmp( pAct->GetBitmap(), pBmpParam ).GetBitmap() ), + LIST_APPEND ); + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + MetaBmpScalePartAction* pAct = (MetaBmpScalePartAction*) pAction; + aMtf.Insert( new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(), + pAct->GetSrcPoint(), pAct->GetSrcSize(), + pFncBmp( pAct->GetBitmap(), pBmpParam ).GetBitmap() ), + LIST_APPEND ); + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + MetaBmpExScaleAction* pAct = (MetaBmpExScaleAction*) pAction; + aMtf.Insert( new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(), + pFncBmp( pAct->GetBitmapEx(), pBmpParam ) ), + LIST_APPEND ); + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + MetaBmpExScalePartAction* pAct = (MetaBmpExScalePartAction*) pAction; + aMtf.Insert( new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(), + pAct->GetSrcPoint(), pAct->GetSrcSize(), + pFncBmp( pAct->GetBitmapEx(), pBmpParam ) ), + LIST_APPEND ); + } + break; + + case( META_MASKSCALE_ACTION ): + { + MetaMaskScaleAction* pAct = (MetaMaskScaleAction*) pAction; + aMtf.Insert( new MetaMaskScaleAction( pAct->GetPoint(), pAct->GetSize(), + pAct->GetBitmap(), + pFncCol( pAct->GetColor(), pColParam ) ), + LIST_APPEND ); + } + break; + + case( META_MASKSCALEPART_ACTION ): + { + MetaMaskScalePartAction* pAct = (MetaMaskScalePartAction*) pAction; + aMtf.Insert( new MetaMaskScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(), + pAct->GetSrcPoint(), pAct->GetSrcSize(), + pAct->GetBitmap(), + pFncCol( pAct->GetColor(), pColParam ) ), + LIST_APPEND ); + } + break; + + case( META_GRADIENT_ACTION ): + { + MetaGradientAction* pAct = (MetaGradientAction*) pAction; + Gradient aGradient( pAct->GetGradient() ); + + aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) ); + aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) ); + aMtf.Insert( new MetaGradientAction( pAct->GetRect(), aGradient ), LIST_APPEND ); + } + break; + + case( META_GRADIENTEX_ACTION ): + { + MetaGradientExAction* pAct = (MetaGradientExAction*) pAction; + Gradient aGradient( pAct->GetGradient() ); + + aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) ); + aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) ); + aMtf.Insert( new MetaGradientExAction( pAct->GetPolyPolygon(), aGradient ), LIST_APPEND ); + } + break; + + case( META_HATCH_ACTION ): + { + MetaHatchAction* pAct = (MetaHatchAction*) pAction; + Hatch aHatch( pAct->GetHatch() ); + + aHatch.SetColor( pFncCol( aHatch.GetColor(), pColParam ) ); + aMtf.Insert( new MetaHatchAction( pAct->GetPolyPolygon(), aHatch ), LIST_APPEND ); + } + break; + + case( META_FLOATTRANSPARENT_ACTION ): + { + MetaFloatTransparentAction* pAct = (MetaFloatTransparentAction*) pAction; + GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() ); + + aTransMtf.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam ); + aMtf.Insert( new MetaFloatTransparentAction( aTransMtf, + pAct->GetPoint(), pAct->GetSize(), + pAct->GetGradient() ), + LIST_APPEND ); + } + break; + + case( META_EPS_ACTION ): + { + MetaEPSAction* pAct = (MetaEPSAction*) pAction; + GDIMetaFile aSubst( pAct->GetSubstitute() ); + + aSubst.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam ); + aMtf.Insert( new MetaEPSAction( pAct->GetPoint(), pAct->GetSize(), + pAct->GetLink(), aSubst ), + LIST_APPEND ); + } + break; + + default: + { + pAction->Duplicate(); + aMtf.Insert( pAction, LIST_APPEND ); + } + break; + } + } + + *this = aMtf; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent, + short nChannelRPercent, short nChannelGPercent, + short nChannelBPercent, double fGamma, sal_Bool bInvert ) +{ + // nothing to do? => return quickly + if( nLuminancePercent || nContrastPercent || + nChannelRPercent || nChannelGPercent || nChannelBPercent || + ( fGamma != 1.0 ) || bInvert ) + { + double fM, fROff, fGOff, fBOff, fOff; + ImplColAdjustParam aColParam; + ImplBmpAdjustParam aBmpParam; + + aColParam.pMapR = new sal_uInt8[ 256 ]; + aColParam.pMapG = new sal_uInt8[ 256 ]; + aColParam.pMapB = new sal_uInt8[ 256 ]; + + // calculate slope + if( nContrastPercent >= 0 ) + fM = 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent, 0L, 100L ) ); + else + fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100L, 0L ) ) / 128.0; + + // total offset = luminance offset + contrast offset + fOff = MinMax( nLuminancePercent, -100L, 100L ) * 2.55 + 128.0 - fM * 128.0; + + // channel offset = channel offset + total offset + fROff = nChannelRPercent * 2.55 + fOff; + fGOff = nChannelGPercent * 2.55 + fOff; + fBOff = nChannelBPercent * 2.55 + fOff; + + // calculate gamma value + fGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma ); + const sal_Bool bGamma = ( fGamma != 1.0 ); + + // create mapping table + for( long nX = 0L; nX < 256L; nX++ ) + { + aColParam.pMapR[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fROff ), 0L, 255L ); + aColParam.pMapG[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fGOff ), 0L, 255L ); + aColParam.pMapB[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fBOff ), 0L, 255L ); + + if( bGamma ) + { + aColParam.pMapR[ nX ] = GAMMA( aColParam.pMapR[ nX ], fGamma ); + aColParam.pMapG[ nX ] = GAMMA( aColParam.pMapG[ nX ], fGamma ); + aColParam.pMapB[ nX ] = GAMMA( aColParam.pMapB[ nX ], fGamma ); + } + + if( bInvert ) + { + aColParam.pMapR[ nX ] = ~aColParam.pMapR[ nX ]; + aColParam.pMapG[ nX ] = ~aColParam.pMapG[ nX ]; + aColParam.pMapB[ nX ] = ~aColParam.pMapB[ nX ]; + } + } + + aBmpParam.nLuminancePercent = nLuminancePercent; + aBmpParam.nContrastPercent = nContrastPercent; + aBmpParam.nChannelRPercent = nChannelRPercent; + aBmpParam.nChannelGPercent = nChannelGPercent; + aBmpParam.nChannelBPercent = nChannelBPercent; + aBmpParam.fGamma = fGamma; + aBmpParam.bInvert = bInvert; + + // do color adjustment + ImplExchangeColors( ImplColAdjustFnc, &aColParam, ImplBmpAdjustFnc, &aBmpParam ); + + delete[] aColParam.pMapR; + delete[] aColParam.pMapG; + delete[] aColParam.pMapB; + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Convert( MtfConversion eConversion ) +{ + // nothing to do? => return quickly + if( eConversion != MTF_CONVERSION_NONE ) + { + ImplColConvertParam aColParam; + ImplBmpConvertParam aBmpParam; + + aColParam.eConversion = eConversion; + aBmpParam.eConversion = ( MTF_CONVERSION_1BIT_THRESHOLD == eConversion ) ? BMP_CONVERSION_1BIT_THRESHOLD : BMP_CONVERSION_8BIT_GREYS; + + ImplExchangeColors( ImplColConvertFnc, &aColParam, ImplBmpConvertFnc, &aBmpParam ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::ReplaceColors( const Color& rSearchColor, const Color& rReplaceColor, sal_uLong nTol ) +{ + ReplaceColors( &rSearchColor, &rReplaceColor, 1, &nTol ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::ReplaceColors( const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount, sal_uLong* pTols ) +{ + ImplColReplaceParam aColParam; + ImplBmpReplaceParam aBmpParam; + + aColParam.pMinR = new sal_uLong[ nColorCount ]; + aColParam.pMaxR = new sal_uLong[ nColorCount ]; + aColParam.pMinG = new sal_uLong[ nColorCount ]; + aColParam.pMaxG = new sal_uLong[ nColorCount ]; + aColParam.pMinB = new sal_uLong[ nColorCount ]; + aColParam.pMaxB = new sal_uLong[ nColorCount ]; + + for( sal_uLong i = 0; i < nColorCount; i++ ) + { + const long nTol = pTols ? ( pTols[ i ] * 255 ) / 100 : 0; + long nVal; + + nVal = pSearchColors[ i ].GetRed(); + aColParam.pMinR[ i ] = (sal_uLong) Max( nVal - nTol, 0L ); + aColParam.pMaxR[ i ] = (sal_uLong) Min( nVal + nTol, 255L ); + + nVal = pSearchColors[ i ].GetGreen(); + aColParam.pMinG[ i ] = (sal_uLong) Max( nVal - nTol, 0L ); + aColParam.pMaxG[ i ] = (sal_uLong) Min( nVal + nTol, 255L ); + + nVal = pSearchColors[ i ].GetBlue(); + aColParam.pMinB[ i ] = (sal_uLong) Max( nVal - nTol, 0L ); + aColParam.pMaxB[ i ] = (sal_uLong) Min( nVal + nTol, 255L ); + } + + aColParam.pDstCols = pReplaceColors; + aColParam.nCount = nColorCount; + + aBmpParam.pSrcCols = pSearchColors; + aBmpParam.pDstCols = pReplaceColors; + aBmpParam.nCount = nColorCount; + aBmpParam.pTols = pTols; + + ImplExchangeColors( ImplColReplaceFnc, &aColParam, ImplBmpReplaceFnc, &aBmpParam ); + + delete[] aColParam.pMinR; + delete[] aColParam.pMaxR; + delete[] aColParam.pMinG; + delete[] aColParam.pMaxG; + delete[] aColParam.pMinB; + delete[] aColParam.pMaxB; +}; + +// ------------------------------------------------------------------------ + +GDIMetaFile GDIMetaFile::GetMonochromeMtf( const Color& rColor ) const +{ + GDIMetaFile aRet( *this ); + + ImplColMonoParam aColParam; + ImplBmpMonoParam aBmpParam; + + aColParam.aColor = rColor; + aBmpParam.aColor = rColor; + + aRet.ImplExchangeColors( ImplColMonoFnc, &aColParam, ImplBmpMonoFnc, &aBmpParam ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +sal_uLong GDIMetaFile::GetChecksum() const +{ + GDIMetaFile aMtf; + SvMemoryStream aMemStm( 65535, 65535 ); + ImplMetaWriteData aWriteData; aWriteData.meActualCharSet = aMemStm.GetStreamCharSet(); + SVBT16 aBT16; + SVBT32 aBT32; + sal_uLong nCrc = 0; + + for( sal_uLong i = 0, nObjCount = GetActionCount(); i < nObjCount; i++ ) + { + MetaAction* pAction = GetAction( i ); + + switch( pAction->GetType() ) + { + case( META_BMP_ACTION ): + { + MetaBmpAction* pAct = (MetaBmpAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPSCALE_ACTION ): + { + MetaBmpScaleAction* pAct = (MetaBmpScaleAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + MetaBmpScalePartAction* pAct = (MetaBmpScalePartAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPEX_ACTION ): + { + MetaBmpExAction* pAct = (MetaBmpExAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmapEx().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + MetaBmpExScaleAction* pAct = (MetaBmpExScaleAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmapEx().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + MetaBmpExScalePartAction* pAct = (MetaBmpExScalePartAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmapEx().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_MASK_ACTION ): + { + MetaMaskAction* pAct = (MetaMaskAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetColor().GetColor(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_MASKSCALE_ACTION ): + { + MetaMaskScaleAction* pAct = (MetaMaskScaleAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetColor().GetColor(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_MASKSCALEPART_ACTION ): + { + MetaMaskScalePartAction* pAct = (MetaMaskScalePartAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetColor().GetColor(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case META_EPS_ACTION : + { + MetaEPSAction* pAct = (MetaEPSAction*) pAction; + nCrc = rtl_crc32( nCrc, pAct->GetLink().GetData(), pAct->GetLink().GetDataSize() ); + } + break; + + default: + { + pAction->Write( aMemStm, &aWriteData ); + nCrc = rtl_crc32( nCrc, aMemStm.GetData(), aMemStm.Tell() ); + aMemStm.Seek( 0 ); + } + break; + } + } + + return nCrc; +} + +// ------------------------------------------------------------------------ + +sal_uLong GDIMetaFile::GetSizeBytes() const +{ + sal_uLong nSizeBytes = 0; + + for( sal_uLong i = 0, nObjCount = GetActionCount(); i < nObjCount; ++i ) + { + MetaAction* pAction = GetAction( i ); + + // default action size is set to 32 (=> not the exact value) + nSizeBytes += 32; + + // add sizes for large action content + switch( pAction->GetType() ) + { + case( META_BMP_ACTION ): nSizeBytes += ( (MetaBmpAction*) pAction )->GetBitmap().GetSizeBytes(); break; + case( META_BMPSCALE_ACTION ): nSizeBytes += ( (MetaBmpScaleAction*) pAction )->GetBitmap().GetSizeBytes(); break; + case( META_BMPSCALEPART_ACTION ): nSizeBytes += ( (MetaBmpScalePartAction*) pAction )->GetBitmap().GetSizeBytes(); break; + + case( META_BMPEX_ACTION ): nSizeBytes += ( (MetaBmpExAction*) pAction )->GetBitmapEx().GetSizeBytes(); break; + case( META_BMPEXSCALE_ACTION ): nSizeBytes += ( (MetaBmpExScaleAction*) pAction )->GetBitmapEx().GetSizeBytes(); break; + case( META_BMPEXSCALEPART_ACTION ): nSizeBytes += ( (MetaBmpExScalePartAction*) pAction )->GetBitmapEx().GetSizeBytes(); break; + + case( META_MASK_ACTION ): nSizeBytes += ( (MetaMaskAction*) pAction )->GetBitmap().GetSizeBytes(); break; + case( META_MASKSCALE_ACTION ): nSizeBytes += ( (MetaMaskScaleAction*) pAction )->GetBitmap().GetSizeBytes(); break; + case( META_MASKSCALEPART_ACTION ): nSizeBytes += ( (MetaMaskScalePartAction*) pAction )->GetBitmap().GetSizeBytes(); break; + + case( META_POLYLINE_ACTION ): nSizeBytes += ( ( (MetaPolyLineAction*) pAction )->GetPolygon().GetSize() * sizeof( Point ) ); break; + case( META_POLYGON_ACTION ): nSizeBytes += ( ( (MetaPolygonAction*) pAction )->GetPolygon().GetSize() * sizeof( Point ) ); break; + case( META_POLYPOLYGON_ACTION ): + { + const PolyPolygon& rPolyPoly = ( (MetaPolyPolygonAction*) pAction )->GetPolyPolygon(); + + for( sal_uInt16 n = 0; n < rPolyPoly.Count(); ++n ) + nSizeBytes += ( rPolyPoly[ n ].GetSize() * sizeof( Point ) ); + } + break; + + case( META_TEXT_ACTION ): nSizeBytes += ( ( (MetaTextAction*) pAction )->GetText().Len() * sizeof( sal_Unicode ) ); break; + case( META_STRETCHTEXT_ACTION ): nSizeBytes += ( ( (MetaStretchTextAction*) pAction )->GetText().Len() * sizeof( sal_Unicode ) ); break; + case( META_TEXTRECT_ACTION ): nSizeBytes += ( ( (MetaTextRectAction*) pAction )->GetText().Len() * sizeof( sal_Unicode ) ); break; + case( META_TEXTARRAY_ACTION ): + { + MetaTextArrayAction* pTextArrayAction = (MetaTextArrayAction*) pAction; + + nSizeBytes += ( pTextArrayAction->GetText().Len() * sizeof( sal_Unicode ) ); + + if( pTextArrayAction->GetDXArray() ) + nSizeBytes += ( pTextArrayAction->GetLen() << 2 ); + } + break; + + default: + break; + } + } + + return( nSizeBytes ); +} + +// ------------------------------------------------------------------------ + +SvStream& operator>>( SvStream& rIStm, GDIMetaFile& rGDIMetaFile ) +{ + if( !rIStm.GetError() ) + { + char aId[ 7 ]; + sal_uLong nStmPos = rIStm.Tell(); + sal_uInt16 nOldFormat = rIStm.GetNumberFormatInt(); + + rIStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + aId[ 0 ] = 0; + aId[ 6 ] = 0; + rIStm.Read( aId, 6 ); + + if ( !strcmp( aId, "VCLMTF" ) ) + { + // new format + VersionCompat* pCompat; + MetaAction* pAction; + sal_uInt32 nStmCompressMode = 0; + sal_uInt32 nCount = 0; + + pCompat = new VersionCompat( rIStm, STREAM_READ ); + + rIStm >> nStmCompressMode; + rIStm >> rGDIMetaFile.aPrefMapMode; + rIStm >> rGDIMetaFile.aPrefSize; + rIStm >> nCount; + + delete pCompat; + + ImplMetaReadData aReadData; + aReadData.meActualCharSet = rIStm.GetStreamCharSet(); + + for( sal_uInt32 nAction = 0UL; ( nAction < nCount ) && !rIStm.IsEof(); nAction++ ) + { + pAction = MetaAction::ReadMetaAction( rIStm, &aReadData ); + + if( pAction ) + rGDIMetaFile.AddAction( pAction ); + } + } + else + { + // to avoid possible compiler optimizations => new/delete + rIStm.Seek( nStmPos ); + delete( new SVMConverter( rIStm, rGDIMetaFile, CONVERT_FROM_SVM1 ) ); + } + + // check for errors + if( rIStm.GetError() ) + { + rGDIMetaFile.Clear(); + rIStm.Seek( nStmPos ); + } + + rIStm.SetNumberFormatInt( nOldFormat ); + } + + return rIStm; +} + +// ------------------------------------------------------------------------ + +SvStream& operator<<( SvStream& rOStm, const GDIMetaFile& rGDIMetaFile ) +{ + if( !rOStm.GetError() ) + { + if( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 ) + ((GDIMetaFile&) rGDIMetaFile ).Write( rOStm ); + else + delete( new SVMConverter( rOStm, (GDIMetaFile&) rGDIMetaFile, CONVERT_TO_SVM1 ) ); + } + + return rOStm; +} + +// ------------------------------------------------------------------------ + +SvStream& GDIMetaFile::Read( SvStream& rIStm ) +{ + Clear(); + rIStm >> *this; + + return rIStm; +} + +// ------------------------------------------------------------------------ + +SvStream& GDIMetaFile::Write( SvStream& rOStm ) +{ + VersionCompat* pCompat; + const sal_uInt32 nStmCompressMode = rOStm.GetCompressMode(); + sal_uInt16 nOldFormat = rOStm.GetNumberFormatInt(); + + rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + rOStm.Write( "VCLMTF", 6 ); + + pCompat = new VersionCompat( rOStm, STREAM_WRITE, 1 ); + + rOStm << nStmCompressMode; + rOStm << aPrefMapMode; + rOStm << aPrefSize; + rOStm << (sal_uInt32) GetActionCount(); + + delete pCompat; + + ImplMetaWriteData aWriteData; + aWriteData.meActualCharSet = rOStm.GetStreamCharSet(); + + MetaAction* pAct = (MetaAction*)First(); + while ( pAct ) + { + pAct->Write( rOStm, &aWriteData ); + pAct = (MetaAction*)Next(); + } + + rOStm.SetNumberFormatInt( nOldFormat ); + + return rOStm; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::CreateThumbnail( sal_uInt32 nMaximumExtent, + BitmapEx& rBmpEx, + const BitmapEx* pOverlay, + const Rectangle* pOverlayRect ) const +{ + // the implementation is provided by KA + + // initialization seems to be complicated but is used to avoid rounding errors + VirtualDevice aVDev; + const Point aNullPt; + const Point aTLPix( aVDev.LogicToPixel( aNullPt, GetPrefMapMode() ) ); + const Point aBRPix( aVDev.LogicToPixel( Point( GetPrefSize().Width() - 1, GetPrefSize().Height() - 1 ), GetPrefMapMode() ) ); + Size aDrawSize( aVDev.LogicToPixel( GetPrefSize(), GetPrefMapMode() ) ); + Size aSizePix( labs( aBRPix.X() - aTLPix.X() ) + 1, labs( aBRPix.Y() - aTLPix.Y() ) + 1 ); + Point aPosPix; + + if ( !rBmpEx.IsEmpty() ) + rBmpEx.SetEmpty(); + + // determine size that has the same aspect ratio as image size and + // fits into the rectangle determined by nMaximumExtent + if ( aSizePix.Width() && aSizePix.Height() + && ( sal::static_int_cast< unsigned long >(aSizePix.Width()) > + nMaximumExtent || + sal::static_int_cast< unsigned long >(aSizePix.Height()) > + nMaximumExtent ) ) + { + const Size aOldSizePix( aSizePix ); + double fWH = static_cast< double >( aSizePix.Width() ) / aSizePix.Height(); + + if ( fWH <= 1.0 ) + { + aSizePix.Width() = FRound( nMaximumExtent * fWH ); + aSizePix.Height() = nMaximumExtent; + } + else + { + aSizePix.Width() = nMaximumExtent; + aSizePix.Height() = FRound( nMaximumExtent / fWH ); + } + + aDrawSize.Width() = FRound( ( static_cast< double >( aDrawSize.Width() ) * aSizePix.Width() ) / aOldSizePix.Width() ); + aDrawSize.Height() = FRound( ( static_cast< double >( aDrawSize.Height() ) * aSizePix.Height() ) / aOldSizePix.Height() ); + } + + Size aFullSize; + Point aBackPosPix; + Rectangle aOverlayRect; + + // calculate addigtional positions and sizes if an overlay image is used + if ( pOverlay ) + { + aFullSize = Size( nMaximumExtent, nMaximumExtent ); + aOverlayRect = Rectangle( aNullPt, aFullSize ); + + aOverlayRect.Intersection( pOverlayRect ? *pOverlayRect : Rectangle( aNullPt, pOverlay->GetSizePixel() ) ); + + if ( !aOverlayRect.IsEmpty() ) + aBackPosPix = Point( ( nMaximumExtent - aSizePix.Width() ) >> 1, ( nMaximumExtent - aSizePix.Height() ) >> 1 ); + else + pOverlay = NULL; + } + else + { + aFullSize = aSizePix; + pOverlay = NULL; + } + + // draw image(s) into VDev and get resulting image + if ( aVDev.SetOutputSizePixel( aFullSize ) ) + { + // draw metafile into VDev + const_cast<GDIMetaFile *>(this)->WindStart(); + const_cast<GDIMetaFile *>(this)->Play( &aVDev, aBackPosPix, aDrawSize ); + + // draw overlay if neccessary + if ( pOverlay ) + aVDev.DrawBitmapEx( aOverlayRect.TopLeft(), aOverlayRect.GetSize(), *pOverlay ); + + // get paint bitmap + Bitmap aBmp( aVDev.GetBitmap( aNullPt, aVDev.GetOutputSizePixel() ) ); + + // assure that we have a true color image + if ( aBmp.GetBitCount() != 24 ) + aBmp.Convert( BMP_CONVERSION_24BIT ); + + // create resulting mask bitmap with metafile output set to black + GDIMetaFile aMonchromeMtf( GetMonochromeMtf( COL_BLACK ) ); + aVDev.DrawWallpaper( Rectangle( aNullPt, aSizePix ), Wallpaper( Color( COL_WHITE ) ) ); + aMonchromeMtf.WindStart(); + aMonchromeMtf.Play( &aVDev, aBackPosPix, aDrawSize ); + + // watch for overlay mask + if ( pOverlay ) + { + Bitmap aOverlayMergeBmp( aVDev.GetBitmap( aOverlayRect.TopLeft(), aOverlayRect.GetSize() ) ); + + // create ANDed resulting mask at overlay area + if ( pOverlay->IsTransparent() ) + aVDev.DrawBitmap( aOverlayRect.TopLeft(), aOverlayRect.GetSize(), pOverlay->GetMask() ); + else + { + aVDev.SetLineColor( COL_BLACK ); + aVDev.SetFillColor( COL_BLACK ); + aVDev.DrawRect( aOverlayRect); + } + + aOverlayMergeBmp.CombineSimple( aVDev.GetBitmap( aOverlayRect.TopLeft(), aOverlayRect.GetSize() ), BMP_COMBINE_AND ); + aVDev.DrawBitmap( aOverlayRect.TopLeft(), aOverlayRect.GetSize(), aOverlayMergeBmp ); + } + + rBmpEx = BitmapEx( aBmp, aVDev.GetBitmap( aNullPt, aVDev.GetOutputSizePixel() ) ); + } + + return !rBmpEx.IsEmpty(); +} + +void GDIMetaFile::UseCanvas( sal_Bool _bUseCanvas ) +{ + bUseCanvas = _bUseCanvas; +} + +// ------------------------------------------------------------------------ + +MetaCommentAction* makePluggableRendererAction( const rtl::OUString& rRendererServiceName, + const rtl::OUString& rGraphicServiceName, + const void* _pData, + sal_uInt32 nDataSize ) +{ + const sal_uInt8* pData=(sal_uInt8*)_pData; + + // data gets copied twice, unfortunately + rtl::OString aRendererServiceName( + rRendererServiceName.getStr(), + rRendererServiceName.getLength(), + RTL_TEXTENCODING_ASCII_US); + rtl::OString aGraphicServiceName( + rGraphicServiceName.getStr(), + rGraphicServiceName.getLength(), + RTL_TEXTENCODING_ASCII_US); + + std::vector<sal_uInt8> aMem( + aRendererServiceName.getLength()+ + aGraphicServiceName.getLength()+2+nDataSize); + sal_uInt8* pMem=&aMem[0]; + + std::copy(aRendererServiceName.getStr(), + aRendererServiceName.getStr()+aRendererServiceName.getLength()+1, + pMem); + pMem+=aRendererServiceName.getLength()+1; + std::copy(aGraphicServiceName.getStr(), + aGraphicServiceName.getStr()+aGraphicServiceName.getLength()+1, + pMem); + pMem+=aGraphicServiceName.getLength()+1; + + std::copy(pData,pData+nDataSize, + pMem); + + return new MetaCommentAction( + "DELEGATE_PLUGGABLE_RENDERER", + 0, + &aMem[0], + aMem.size()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/gfxlink.cxx b/vcl/source/gdi/gfxlink.cxx new file mode 100644 index 000000000000..29b6a62253a6 --- /dev/null +++ b/vcl/source/gdi/gfxlink.cxx @@ -0,0 +1,472 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <osl/file.h> +#include <tools/vcompat.hxx> +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/tempfile.hxx> +#include <ucbhelper/content.hxx> +#include <vcl/graph.hxx> +#include <vcl/gfxlink.hxx> +#include <vcl/cvtgrf.hxx> +#include <com/sun/star/ucb/CommandAbortedException.hpp> + +// ----------- +// - GfxLink - +// ----------- + +GfxLink::GfxLink() : + meType ( GFX_LINK_TYPE_NONE ), + mpBuf ( NULL ), + mpSwap ( NULL ), + mnBufSize ( 0 ), + mnUserId ( 0UL ), + mpImpData ( new ImpGfxLink ) +{ +} + +// ------------------------------------------------------------------------ + +GfxLink::GfxLink( const GfxLink& rGfxLink ) : + mpImpData( new ImpGfxLink ) +{ + ImplCopy( rGfxLink ); +} + +// ------------------------------------------------------------------------ + +GfxLink::GfxLink( sal_uInt8* pBuf, sal_uInt32 nSize, GfxLinkType nType, sal_Bool bOwns ) : + mpImpData( new ImpGfxLink ) +{ + DBG_ASSERT( (pBuf != NULL && nSize) || (!bOwns && nSize == 0), + "GfxLink::GfxLink(): empty/NULL buffer given" ); + + meType = nType; + mnBufSize = nSize; + mpSwap = NULL; + mnUserId = 0UL; + + if( bOwns ) + mpBuf = new ImpBuffer( pBuf ); + else if( nSize ) + { + mpBuf = new ImpBuffer( nSize ); + memcpy( mpBuf->mpBuffer, pBuf, nSize ); + } + else + mpBuf = NULL; +} + +// ------------------------------------------------------------------------ + +GfxLink::~GfxLink() +{ + if( mpBuf && !( --mpBuf->mnRefCount ) ) + delete mpBuf; + + if( mpSwap && !( --mpSwap->mnRefCount ) ) + delete mpSwap; + + delete mpImpData; +} + +// ------------------------------------------------------------------------ + +GfxLink& GfxLink::operator=( const GfxLink& rGfxLink ) +{ + if( &rGfxLink != this ) + { + if ( mpBuf && !( --mpBuf->mnRefCount ) ) + delete mpBuf; + + if( mpSwap && !( --mpSwap->mnRefCount ) ) + delete mpSwap; + + ImplCopy( rGfxLink ); + } + + return *this; +} + +// ------------------------------------------------------------------------ + +sal_Bool GfxLink::IsEqual( const GfxLink& rGfxLink ) const +{ + sal_Bool bIsEqual = sal_False; + + if ( ( mnBufSize == rGfxLink.mnBufSize ) && ( meType == rGfxLink.meType ) ) + { + const sal_uInt8* pSource = GetData(); + const sal_uInt8* pDest = rGfxLink.GetData(); + sal_uInt32 nSourceSize = GetDataSize(); + sal_uInt32 nDestSize = rGfxLink.GetDataSize(); + if ( pSource && pDest && ( nSourceSize == nDestSize ) ) + { + bIsEqual = memcmp( pSource, pDest, nSourceSize ) == 0; + } + else if ( ( pSource == 0 ) && ( pDest == 0 ) ) + bIsEqual = sal_True; + } + return bIsEqual; +} + +// ------------------------------------------------------------------------ + +void GfxLink::ImplCopy( const GfxLink& rGfxLink ) +{ + mnBufSize = rGfxLink.mnBufSize; + meType = rGfxLink.meType; + mpBuf = rGfxLink.mpBuf; + mpSwap = rGfxLink.mpSwap; + mnUserId = rGfxLink.mnUserId; + *mpImpData = *rGfxLink.mpImpData; + + if( mpBuf ) + mpBuf->mnRefCount++; + + if( mpSwap ) + mpSwap->mnRefCount++; +} + +// ------------------------------------------------------------------------ + +GfxLinkType GfxLink::GetType() const +{ + return meType; +} + +// ------------------------------------------------------------------------ + +sal_Bool GfxLink::IsNative() const +{ + return( meType >= GFX_LINK_FIRST_NATIVE_ID && meType <= GFX_LINK_LAST_NATIVE_ID ); +} + +// ------------------------------------------------------------------------ + +sal_uInt32 GfxLink::GetDataSize() const +{ + return mnBufSize; +} + +// ------------------------------------------------------------------------ + +const sal_uInt8* GfxLink::GetData() const +{ + if( IsSwappedOut() ) + ( (GfxLink*) this )->SwapIn(); + + return( mpBuf ? mpBuf->mpBuffer : NULL ); +} + +// ------------------------------------------------------------------------ + +const Size& GfxLink::GetPrefSize() const +{ + return mpImpData->maPrefSize; +} + +// ------------------------------------------------------------------------ + +void GfxLink::SetPrefSize( const Size& rPrefSize ) +{ + mpImpData->maPrefSize = rPrefSize; + mpImpData->mbPrefSizeValid = true; +} + +// ------------------------------------------------------------------------ + +bool GfxLink::IsPrefSizeValid() +{ + return mpImpData->mbPrefSizeValid; +} + +// ------------------------------------------------------------------------ + +const MapMode& GfxLink::GetPrefMapMode() const +{ + return mpImpData->maPrefMapMode; +} + +// ------------------------------------------------------------------------ + +void GfxLink::SetPrefMapMode( const MapMode& rPrefMapMode ) +{ + mpImpData->maPrefMapMode = rPrefMapMode; + mpImpData->mbPrefMapModeValid = true; +} + +// ------------------------------------------------------------------------ + +bool GfxLink::IsPrefMapModeValid() +{ + return mpImpData->mbPrefMapModeValid; +} + +// ------------------------------------------------------------------------ + +sal_Bool GfxLink::LoadNative( Graphic& rGraphic ) +{ + sal_Bool bRet = sal_False; + + if( IsNative() && mnBufSize ) + { + const sal_uInt8* pData = GetData(); + + if( pData ) + { + SvMemoryStream aMemStm; + sal_uLong nCvtType; + + aMemStm.SetBuffer( (char*) pData, mnBufSize, sal_False, mnBufSize ); + + switch( meType ) + { + case( GFX_LINK_TYPE_NATIVE_GIF ): nCvtType = CVT_GIF; break; + case( GFX_LINK_TYPE_NATIVE_JPG ): nCvtType = CVT_JPG; break; + case( GFX_LINK_TYPE_NATIVE_PNG ): nCvtType = CVT_PNG; break; + case( GFX_LINK_TYPE_NATIVE_TIF ): nCvtType = CVT_TIF; break; + case( GFX_LINK_TYPE_NATIVE_WMF ): nCvtType = CVT_WMF; break; + case( GFX_LINK_TYPE_NATIVE_MET ): nCvtType = CVT_MET; break; + case( GFX_LINK_TYPE_NATIVE_PCT ): nCvtType = CVT_PCT; break; + + default: nCvtType = CVT_UNKNOWN; break; + } + + if( nCvtType && ( GraphicConverter::Import( aMemStm, rGraphic, nCvtType ) == ERRCODE_NONE ) ) + bRet = sal_True; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +void GfxLink::SwapOut() +{ + if( !IsSwappedOut() && mpBuf ) + { + mpSwap = new ImpSwap( mpBuf->mpBuffer, mnBufSize ); + + if( !mpSwap->IsSwapped() ) + { + delete mpSwap; + mpSwap = NULL; + } + else + { + if( !( --mpBuf->mnRefCount ) ) + delete mpBuf; + + mpBuf = NULL; + } + } +} + +// ------------------------------------------------------------------------ + +void GfxLink::SwapIn() +{ + if( IsSwappedOut() ) + { + mpBuf = new ImpBuffer( mpSwap->GetData() ); + + if( !( --mpSwap->mnRefCount ) ) + delete mpSwap; + + mpSwap = NULL; + } +} + +// ------------------------------------------------------------------------ + +sal_Bool GfxLink::ExportNative( SvStream& rOStream ) const +{ + if( GetDataSize() ) + { + if( IsSwappedOut() ) + mpSwap->WriteTo( rOStream ); + else if( GetData() ) + rOStream.Write( GetData(), GetDataSize() ); + } + + return ( rOStream.GetError() == ERRCODE_NONE ); +} + +// ------------------------------------------------------------------------ + +SvStream& operator<<( SvStream& rOStream, const GfxLink& rGfxLink ) +{ + VersionCompat* pCompat = new VersionCompat( rOStream, STREAM_WRITE, 2 ); + + // Version 1 + rOStream << (sal_uInt16) rGfxLink.GetType() << rGfxLink.GetDataSize() << rGfxLink.GetUserId(); + + // Version 2 + rOStream << rGfxLink.GetPrefSize() << rGfxLink.GetPrefMapMode(); + + delete pCompat; + + if( rGfxLink.GetDataSize() ) + { + if( rGfxLink.IsSwappedOut() ) + rGfxLink.mpSwap->WriteTo( rOStream ); + else if( rGfxLink.GetData() ) + rOStream.Write( rGfxLink.GetData(), rGfxLink.GetDataSize() ); + } + + return rOStream; +} + +// ------------------------------------------------------------------------ + +SvStream& operator>>( SvStream& rIStream, GfxLink& rGfxLink) +{ + Size aSize; + MapMode aMapMode; + sal_uInt32 nSize; + sal_uInt32 nUserId; + sal_uInt16 nType; + sal_uInt8* pBuf; + bool bMapAndSizeValid( false ); + VersionCompat* pCompat = new VersionCompat( rIStream, STREAM_READ ); + + // Version 1 + rIStream >> nType >> nSize >> nUserId; + + if( pCompat->GetVersion() >= 2 ) + { + rIStream >> aSize >> aMapMode; + bMapAndSizeValid = true; + } + + delete pCompat; + + pBuf = new sal_uInt8[ nSize ]; + rIStream.Read( pBuf, nSize ); + + rGfxLink = GfxLink( pBuf, nSize, (GfxLinkType) nType, sal_True ); + rGfxLink.SetUserId( nUserId ); + + if( bMapAndSizeValid ) + { + rGfxLink.SetPrefSize( aSize ); + rGfxLink.SetPrefMapMode( aMapMode ); + } + + return rIStream; +} + +// ----------- +// - ImpSwap - +// ----------- + +ImpSwap::ImpSwap( sal_uInt8* pData, sal_uLong nDataSize ) : + mnDataSize( nDataSize ), + mnRefCount( 1UL ) +{ + if( pData && mnDataSize ) + { + ::utl::TempFile aTempFile; + + maURL = aTempFile.GetURL(); + if( maURL.getLength() ) + { + SvStream* pOStm = ::utl::UcbStreamHelper::CreateStream( maURL, STREAM_READWRITE | STREAM_SHARE_DENYWRITE ); + if( pOStm ) + { + pOStm->Write( pData, mnDataSize ); + sal_Bool bError = ( ERRCODE_NONE != pOStm->GetError() ); + delete pOStm; + + if( bError ) + { + osl_removeFile( maURL.pData ); + maURL = String(); + } + } + } + } +} + +// ------------------------------------------------------------------------ + +ImpSwap::~ImpSwap() +{ + if( IsSwapped() ) + osl_removeFile( maURL.pData ); +} + +// ------------------------------------------------------------------------ + +sal_uInt8* ImpSwap::GetData() const +{ + sal_uInt8* pData; + + if( IsSwapped() ) + { + SvStream* pIStm = ::utl::UcbStreamHelper::CreateStream( maURL, STREAM_READWRITE ); + if( pIStm ) + { + pData = new sal_uInt8[ mnDataSize ]; + pIStm->Read( pData, mnDataSize ); + sal_Bool bError = ( ERRCODE_NONE != pIStm->GetError() ); + delete pIStm; + + if( bError ) + delete[] pData, pData = NULL; + } + else + pData = NULL; + } + else + pData = NULL; + + return pData; +} + +// ------------------------------------------------------------------------ + +void ImpSwap::WriteTo( SvStream& rOStm ) const +{ + sal_uInt8* pData = GetData(); + + if( pData ) + { + rOStm.Write( pData, mnDataSize ); + delete[] pData; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/gradient.cxx b/vcl/source/gdi/gradient.cxx new file mode 100644 index 000000000000..38d8ba4567a2 --- /dev/null +++ b/vcl/source/gdi/gradient.cxx @@ -0,0 +1,440 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <tools/debug.hxx> +#include <vcl/gradient.hxx> + +// ======================================================================= + +DBG_NAME( Gradient ) + +// ----------------------------------------------------------------------- + +Impl_Gradient::Impl_Gradient() : + maStartColor( COL_BLACK ), + maEndColor( COL_WHITE ) +{ + mnRefCount = 1; + meStyle = GRADIENT_LINEAR; + mnAngle = 0; + mnBorder = 0; + mnOfsX = 50; + mnOfsY = 50; + mnIntensityStart = 100; + mnIntensityEnd = 100; + mnStepCount = 0; +} + +// ----------------------------------------------------------------------- + +Impl_Gradient::Impl_Gradient( const Impl_Gradient& rImplGradient ) : + maStartColor( rImplGradient.maStartColor ), + maEndColor( rImplGradient.maEndColor ) +{ + mnRefCount = 1; + meStyle = rImplGradient.meStyle; + mnAngle = rImplGradient.mnAngle; + mnBorder = rImplGradient.mnBorder; + mnOfsX = rImplGradient.mnOfsX; + mnOfsY = rImplGradient.mnOfsY; + mnIntensityStart = rImplGradient.mnIntensityStart; + mnIntensityEnd = rImplGradient.mnIntensityEnd; + mnStepCount = rImplGradient.mnStepCount; +} + +// ----------------------------------------------------------------------- + +void Gradient::MakeUnique() +{ + // Falls noch andere Referenzen bestehen, dann kopieren + if ( mpImplGradient->mnRefCount != 1 ) + { + if( mpImplGradient->mnRefCount ) + mpImplGradient->mnRefCount--; + + mpImplGradient = new Impl_Gradient( *mpImplGradient ); + } +} + +// ----------------------------------------------------------------------- + +Gradient::Gradient() +{ + DBG_CTOR( Gradient, NULL ); + + mpImplGradient = new Impl_Gradient; +} + +// ----------------------------------------------------------------------- + +Gradient::Gradient( const Gradient& rGradient ) +{ + DBG_CTOR( Gradient, NULL ); + DBG_CHKOBJ( &rGradient, Gradient, NULL ); + + // Instance Daten uebernehmen und Referenzcounter erhoehen + mpImplGradient = rGradient.mpImplGradient; + mpImplGradient->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +Gradient::Gradient( GradientStyle eStyle ) +{ + DBG_CTOR( Gradient, NULL ); + + mpImplGradient = new Impl_Gradient; + mpImplGradient->meStyle = eStyle; +} + +// ----------------------------------------------------------------------- + +Gradient::Gradient( GradientStyle eStyle, + const Color& rStartColor, const Color& rEndColor ) +{ + DBG_CTOR( Gradient, NULL ); + + mpImplGradient = new Impl_Gradient; + mpImplGradient->meStyle = eStyle; + mpImplGradient->maStartColor = rStartColor; + mpImplGradient->maEndColor = rEndColor; +} + +// ----------------------------------------------------------------------- + +Gradient::~Gradient() +{ + DBG_DTOR( Gradient, NULL ); + + // Wenn es die letzte Referenz ist, loeschen, + // sonst Referenzcounter decrementieren + if ( mpImplGradient->mnRefCount == 1 ) + delete mpImplGradient; + else + mpImplGradient->mnRefCount--; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetStyle( GradientStyle eStyle ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->meStyle = eStyle; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetStartColor( const Color& rColor ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->maStartColor = rColor; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetEndColor( const Color& rColor ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->maEndColor = rColor; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetAngle( sal_uInt16 nAngle ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->mnAngle = nAngle; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetBorder( sal_uInt16 nBorder ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->mnBorder = nBorder; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetOfsX( sal_uInt16 nOfsX ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->mnOfsX = nOfsX; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetOfsY( sal_uInt16 nOfsY ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->mnOfsY = nOfsY; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetStartIntensity( sal_uInt16 nIntens ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->mnIntensityStart = nIntens; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetEndIntensity( sal_uInt16 nIntens ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->mnIntensityEnd = nIntens; +} + +// ----------------------------------------------------------------------- + +void Gradient::SetSteps( sal_uInt16 nSteps ) +{ + DBG_CHKTHIS( Gradient, NULL ); + + MakeUnique(); + mpImplGradient->mnStepCount = nSteps; +} + +// ----------------------------------------------------------------------- + +void Gradient::GetBoundRect( const Rectangle& rRect, Rectangle& rBoundRect, Point& rCenter ) const +{ + Rectangle aRect( rRect ); + sal_uInt16 nAngle = GetAngle() % 3600; + + if( GetStyle() == GRADIENT_LINEAR || GetStyle() == GRADIENT_AXIAL ) + { + aRect.Left()--; + aRect.Top()--; + aRect.Right()++; + aRect.Bottom()++; + + const double fAngle = nAngle * F_PI1800; + const double fWidth = aRect.GetWidth(); + const double fHeight = aRect.GetHeight(); + double fDX = fWidth * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle ) ); + double fDY = fHeight * fabs( cos( fAngle ) ) + fWidth * fabs( sin( fAngle ) ); + + fDX = ( fDX - fWidth ) * 0.5 + 0.5; + fDY = ( fDY - fHeight ) * 0.5 + 0.5; + + aRect.Left() -= (long) fDX; + aRect.Right() += (long) fDX; + aRect.Top() -= (long) fDY; + aRect.Bottom() += (long) fDY; + + rBoundRect = aRect; + rCenter = rRect.Center(); + } + else + { + + if( GetStyle() == GRADIENT_SQUARE || GetStyle() == GRADIENT_RECT ) + { + const double fAngle = nAngle * F_PI1800; + const double fWidth = aRect.GetWidth(); + const double fHeight = aRect.GetHeight(); + double fDX = fWidth * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle ) ); + double fDY = fHeight * fabs( cos( fAngle ) ) + fWidth * fabs( sin( fAngle ) ); + + fDX = ( fDX - fWidth ) * 0.5 + 0.5; + fDY = ( fDY - fHeight ) * 0.5 + 0.5; + + aRect.Left() -= (long) fDX; + aRect.Right() += (long) fDX; + aRect.Top() -= (long) fDY; + aRect.Bottom() += (long) fDY; + } + + Size aSize( aRect.GetSize() ); + + if( GetStyle() == GRADIENT_RADIAL ) + { + // Radien-Berechnung fuer Kreis + aSize.Width() = (long)(0.5 + sqrt((double)aSize.Width()*(double)aSize.Width() + (double)aSize.Height()*(double)aSize.Height())); + aSize.Height() = aSize.Width(); + } + else if( GetStyle() == GRADIENT_ELLIPTICAL ) + { + // Radien-Berechnung fuer Ellipse + aSize.Width() = (long)( 0.5 + (double) aSize.Width() * 1.4142 ); + aSize.Height() = (long)( 0.5 + (double) aSize.Height() * 1.4142 ); + } + else if( GetStyle() == GRADIENT_SQUARE ) + { + if ( aSize.Width() > aSize.Height() ) + aSize.Height() = aSize.Width(); + else + aSize.Width() = aSize.Height(); + } + + // neue Mittelpunkte berechnen + long nZWidth = aRect.GetWidth() * (long) GetOfsX() / 100; + long nZHeight = aRect.GetHeight() * (long) GetOfsY() / 100; + long nBorderX = (long) GetBorder() * aSize.Width() / 100; + long nBorderY = (long) GetBorder() * aSize.Height() / 100; + rCenter = Point( aRect.Left() + nZWidth, aRect.Top() + nZHeight ); + + // Rand beruecksichtigen + aSize.Width() -= nBorderX; + aSize.Height() -= nBorderY; + + // Ausgaberechteck neu setzen + aRect.Left() = rCenter.X() - ( aSize.Width() >> 1 ); + aRect.Top() = rCenter.Y() - ( aSize.Height() >> 1 ); + + aRect.SetSize( aSize ); + rBoundRect = rRect; + } +} + +// ----------------------------------------------------------------------- + +Gradient& Gradient::operator=( const Gradient& rGradient ) +{ + DBG_CHKTHIS( Gradient, NULL ); + DBG_CHKOBJ( &rGradient, Gradient, NULL ); + + // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann + rGradient.mpImplGradient->mnRefCount++; + + // Wenn es die letzte Referenz ist, loeschen, + // sonst Referenzcounter decrementieren + if ( mpImplGradient->mnRefCount == 1 ) + delete mpImplGradient; + else + mpImplGradient->mnRefCount--; + mpImplGradient = rGradient.mpImplGradient; + + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool Gradient::operator==( const Gradient& rGradient ) const +{ + DBG_CHKTHIS( Gradient, NULL ); + DBG_CHKOBJ( &rGradient, Gradient, NULL ); + + if ( mpImplGradient == rGradient.mpImplGradient ) + return sal_True; + + if ( (mpImplGradient->meStyle == rGradient.mpImplGradient->meStyle) || + (mpImplGradient->mnAngle == rGradient.mpImplGradient->mnAngle) || + (mpImplGradient->mnBorder == rGradient.mpImplGradient->mnBorder) || + (mpImplGradient->mnOfsX == rGradient.mpImplGradient->mnOfsX) || + (mpImplGradient->mnOfsY == rGradient.mpImplGradient->mnOfsY) || + (mpImplGradient->mnStepCount == rGradient.mpImplGradient->mnStepCount) || + (mpImplGradient->mnIntensityStart == rGradient.mpImplGradient->mnIntensityStart) || + (mpImplGradient->mnIntensityEnd == rGradient.mpImplGradient->mnIntensityEnd) || + (mpImplGradient->maStartColor == rGradient.mpImplGradient->maStartColor) || + (mpImplGradient->maEndColor == rGradient.mpImplGradient->maEndColor) ) + return sal_True; + else + return sal_False; +} + +SvStream& operator>>( SvStream& rIStm, Impl_Gradient& rImpl_Gradient ) +{ + VersionCompat aCompat( rIStm, STREAM_READ ); + sal_uInt16 nTmp16; + + rIStm >> nTmp16; rImpl_Gradient.meStyle = (GradientStyle) nTmp16; + + rIStm >> rImpl_Gradient.maStartColor >> + rImpl_Gradient.maEndColor >> + rImpl_Gradient.mnAngle >> + rImpl_Gradient.mnBorder >> + rImpl_Gradient.mnOfsX >> + rImpl_Gradient.mnOfsY >> + rImpl_Gradient.mnIntensityStart >> + rImpl_Gradient.mnIntensityEnd >> + rImpl_Gradient.mnStepCount; + + return rIStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const Impl_Gradient& rImpl_Gradient ) +{ + VersionCompat aCompat( rOStm, STREAM_WRITE, 1 ); + + rOStm << (sal_uInt16) rImpl_Gradient.meStyle << + rImpl_Gradient.maStartColor << + rImpl_Gradient.maEndColor << + rImpl_Gradient.mnAngle << + rImpl_Gradient.mnBorder << + rImpl_Gradient.mnOfsX << + rImpl_Gradient.mnOfsY << + rImpl_Gradient.mnIntensityStart << + rImpl_Gradient.mnIntensityEnd << + rImpl_Gradient.mnStepCount; + + return rOStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, Gradient& rGradient ) +{ + rGradient.MakeUnique(); + return( rIStm >> *rGradient.mpImplGradient ); +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const Gradient& rGradient ) +{ + return( rOStm << *rGradient.mpImplGradient ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/graph.cxx b/vcl/source/gdi/graph.cxx new file mode 100644 index 000000000000..f61bbf8dc041 --- /dev/null +++ b/vcl/source/gdi/graph.cxx @@ -0,0 +1,817 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <vcl/impgraph.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> +#include <vcl/graph.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/lang/XTypeProvider.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> + +// ----------------------- +// - Compression defines - +// ----------------------- + +#define COMPRESS_OWN ('S'|('D'<<8UL)) +#define COMPRESS_NONE ( 0UL ) +#define RLE_8 ( 1UL ) +#define RLE_4 ( 2UL ) +#define BITFIELDS ( 3UL ) +#define ZCOMPRESS ( COMPRESS_OWN | 0x01000000UL ) /* == 'SD01' (binary) */ + +using namespace ::com::sun::star; + +// ----------------------- +// - Default-Drawmethode - +// ----------------------- + +static void ImplDrawDefault( OutputDevice* pOutDev, const UniString* pText, + Font* pFont, const Bitmap* pBitmap, const BitmapEx* pBitmapEx, + const Point& rDestPt, const Size& rDestSize ) +{ + sal_uInt16 nPixel = (sal_uInt16) pOutDev->PixelToLogic( Size( 1, 1 ) ).Width(); + sal_uInt16 nPixelWidth = nPixel; + Point aPoint( rDestPt.X() + nPixelWidth, rDestPt.Y() + nPixelWidth ); + Size aSize( rDestSize.Width() - ( nPixelWidth << 1 ), rDestSize.Height() - ( nPixelWidth << 1 ) ); + sal_Bool bFilled = ( pBitmap != NULL || pBitmapEx != NULL || pFont != NULL ); + Rectangle aBorderRect( aPoint, aSize ); + + pOutDev->Push(); + + pOutDev->SetFillColor(); + + // Auf dem Drucker ein schwarzes Rechteck und auf dem Bildschirm eins mit 3D-Effekt + if ( pOutDev->GetOutDevType() == OUTDEV_PRINTER ) + pOutDev->SetLineColor( COL_BLACK ); + else + { + aBorderRect.Left() += nPixel; + aBorderRect.Top() += nPixel; + + pOutDev->SetLineColor( COL_LIGHTGRAY ); + pOutDev->DrawRect( aBorderRect ); + + aBorderRect.Left() -= nPixel; + aBorderRect.Top() -= nPixel; + aBorderRect.Right() -= nPixel; + aBorderRect.Bottom() -= nPixel; + pOutDev->SetLineColor( COL_GRAY ); + } + + pOutDev->DrawRect( aBorderRect ); + + aPoint.X() += nPixelWidth + 2*nPixel; + aPoint.Y() += nPixelWidth + 2*nPixel; + aSize.Width() -= 2*nPixelWidth + 4*nPixel; + aSize.Height() -= 2*nPixelWidth + 4*nPixel; + + if( aSize.Width() > 0 && aSize.Height() > 0 + && ( ( pBitmap && !!*pBitmap ) || ( pBitmapEx && !!*pBitmapEx ) ) ) + { + Size aBitmapSize( pOutDev->PixelToLogic( pBitmap ? pBitmap->GetSizePixel() : pBitmapEx->GetSizePixel() ) ); + + if( aSize.Height() > aBitmapSize.Height() && aSize.Width() > aBitmapSize.Width() ) + { + if ( pBitmap ) + pOutDev->DrawBitmap( aPoint, *pBitmap ); + else + pOutDev->DrawBitmapEx( aPoint, *pBitmapEx ); + aPoint.X() += aBitmapSize.Width() + 2*nPixel; + aSize.Width() -= aBitmapSize.Width() + 2*nPixel; + } + } + + if ( aSize.Width() > 0 && aSize.Height() > 0 && pFont && pText && pText->Len() + && !(!pOutDev->IsOutputEnabled() /*&& pOutDev->GetConnectMetaFile() */) ) + { + MapMode aMapMode( MAP_POINT ); + Size aSz = pOutDev->LogicToLogic( Size( 0, 12 ), &aMapMode, NULL ); + long nThreshold = aSz.Height() / 2; + long nStep = nThreshold / 3; + + if ( !nStep ) + nStep = aSz.Height() - nThreshold; + + for(;; aSz.Height() -= nStep ) + { + pFont->SetSize( aSz ); + pOutDev->SetFont( *pFont ); + + long nTextHeight = pOutDev->GetTextHeight(); + long nTextWidth = pOutDev->GetTextWidth( *pText ); + if ( nTextHeight ) + { + // Die N"aherung ber"ucksichtigt keine Ungenauigkeiten durch + // Wortumbr"uche + long nLines = aSize.Height() / nTextHeight; + long nWidth = aSize.Width() * nLines; // N"aherung!!! + + if ( nTextWidth <= nWidth || aSz.Height() <= nThreshold ) + { + sal_uInt16 nStart = 0; + sal_uInt16 nLen = 0; + + while( nStart < pText->Len() && pText->GetChar( nStart ) == ' ' ) + nStart++; + while( nStart+nLen < pText->Len() && pText->GetChar( nStart+nLen ) != ' ' ) + nLen++; + while( nStart < pText->Len() && nLines-- ) + { + sal_uInt16 nNext = nLen; + do + { + while ( nStart+nNext < pText->Len() && pText->GetChar( nStart+nNext ) == ' ' ) + nNext++; + while ( nStart+nNext < pText->Len() && pText->GetChar( nStart+nNext ) != ' ' ) + nNext++; + nTextWidth = pOutDev->GetTextWidth( *pText, nStart, nNext ); + if ( nTextWidth > aSize.Width() ) + break; + nLen = nNext; + } + while ( nStart+nNext < pText->Len() ); + + sal_uInt16 n = nLen; + nTextWidth = pOutDev->GetTextWidth( *pText, nStart, n ); + while( nTextWidth > aSize.Width() ) + nTextWidth = pOutDev->GetTextWidth( *pText, nStart, --n ); + pOutDev->DrawText( aPoint, *pText, nStart, n ); + + aPoint.Y() += nTextHeight; + nStart = sal::static_int_cast<sal_uInt16>(nStart + nLen); + nLen = nNext-nLen; + while( nStart < pText->Len() && pText->GetChar( nStart ) == ' ' ) + { + nStart++; + nLen--; + } + } + break; + } + } + else + break; + } + } + + // Falls die Default-Graphik keinen Inhalt hat, + // malen wir ein rotes Kreuz + if( !bFilled ) + { + aBorderRect.Left()++; + aBorderRect.Top()++; + aBorderRect.Right()--; + aBorderRect.Bottom()--; + + pOutDev->SetLineColor( COL_LIGHTRED ); + pOutDev->DrawLine( aBorderRect.TopLeft(), aBorderRect.BottomRight() ); + pOutDev->DrawLine( aBorderRect.TopRight(), aBorderRect.BottomLeft() ); + } + + pOutDev->Pop(); +} + +// ----------- +// - Graphic - +// ----------- + +TYPEINIT1_AUTOFACTORY( Graphic, SvDataCopyStream ); + +// ------------------------------------------------------------------------ + +Graphic::Graphic() +{ + mpImpGraphic = new ImpGraphic; +} + +// ------------------------------------------------------------------------ + +Graphic::Graphic( const Graphic& rGraphic ) : +SvDataCopyStream() +{ + if( rGraphic.IsAnimated() ) + mpImpGraphic = new ImpGraphic( *rGraphic.mpImpGraphic ); + else + { + mpImpGraphic = rGraphic.mpImpGraphic; + mpImpGraphic->mnRefCount++; + } +} + +// ------------------------------------------------------------------------ + +Graphic::Graphic( const Bitmap& rBmp ) +{ + mpImpGraphic = new ImpGraphic( rBmp ); +} + +// ------------------------------------------------------------------------ + +Graphic::Graphic( const BitmapEx& rBmpEx ) +{ + mpImpGraphic = new ImpGraphic( rBmpEx ); +} + +// ------------------------------------------------------------------------ + +Graphic::Graphic( const Animation& rAnimation ) +{ + mpImpGraphic = new ImpGraphic( rAnimation ); +} + +// ------------------------------------------------------------------------ + +Graphic::Graphic( const GDIMetaFile& rMtf ) +{ + mpImpGraphic = new ImpGraphic( rMtf ); +} + +// ------------------------------------------------------------------------ + +Graphic::Graphic( const ::com::sun::star::uno::Reference< ::com::sun::star::graphic::XGraphic >& rxGraphic ) +{ + uno::Reference< lang::XUnoTunnel > xTunnel( rxGraphic, uno::UNO_QUERY ); + uno::Reference< lang::XTypeProvider > xProv( rxGraphic, uno::UNO_QUERY ); + const ::Graphic* pGraphic = ( ( xTunnel.is() && xProv.is() ) ? + reinterpret_cast< ::Graphic* >( xTunnel->getSomething( xProv->getImplementationId() ) ) : + NULL ); + + if( pGraphic ) + { + if( pGraphic->IsAnimated() ) + mpImpGraphic = new ImpGraphic( *pGraphic->mpImpGraphic ); + else + { + mpImpGraphic = pGraphic->mpImpGraphic; + mpImpGraphic->mnRefCount++; + } + } + else + mpImpGraphic = new ImpGraphic; +} + +// ------------------------------------------------------------------------ + +Graphic::~Graphic() +{ + if( mpImpGraphic->mnRefCount == 1UL ) + delete mpImpGraphic; + else + mpImpGraphic->mnRefCount--; +} + +// ------------------------------------------------------------------------ + +void Graphic::ImplTestRefCount() +{ + if( mpImpGraphic->mnRefCount > 1UL ) + { + mpImpGraphic->mnRefCount--; + mpImpGraphic = new ImpGraphic( *mpImpGraphic ); + } +} + +// ------------------------------------------------------------------------ + +Graphic& Graphic::operator=( const Graphic& rGraphic ) +{ + if( &rGraphic != this ) + { + if( rGraphic.IsAnimated() ) + { + if( mpImpGraphic->mnRefCount == 1UL ) + delete mpImpGraphic; + else + mpImpGraphic->mnRefCount--; + + mpImpGraphic = new ImpGraphic( *rGraphic.mpImpGraphic ); + } + else + { + rGraphic.mpImpGraphic->mnRefCount++; + + if( mpImpGraphic->mnRefCount == 1UL ) + delete mpImpGraphic; + else + mpImpGraphic->mnRefCount--; + + mpImpGraphic = rGraphic.mpImpGraphic; + } + } + + return *this; +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::operator==( const Graphic& rGraphic ) const +{ + return( *mpImpGraphic == *rGraphic.mpImpGraphic ); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::operator!=( const Graphic& rGraphic ) const +{ + return( *mpImpGraphic != *rGraphic.mpImpGraphic ); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::operator!() const +{ + return( GRAPHIC_NONE == mpImpGraphic->ImplGetType() ); +} + +// ------------------------------------------------------------------------ + +void Graphic::Load( SvStream& rIStm ) +{ + rIStm >> *this; +} + +// ------------------------------------------------------------------------ + +void Graphic::Save( SvStream& rOStm ) +{ + rOStm << *this; +} + +// ------------------------------------------------------------------------ + +void Graphic::Assign( const SvDataCopyStream& rCopyStream ) +{ + *this = (const Graphic& ) rCopyStream; +} + +// ------------------------------------------------------------------------ + +void Graphic::Clear() +{ + ImplTestRefCount(); + mpImpGraphic->ImplClear(); +} + +// ------------------------------------------------------------------------ + +GraphicType Graphic::GetType() const +{ + return mpImpGraphic->ImplGetType(); +} + +// ------------------------------------------------------------------------ + +void Graphic::SetDefaultType() +{ + ImplTestRefCount(); + mpImpGraphic->ImplSetDefaultType(); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::IsSupportedGraphic() const +{ + return mpImpGraphic->ImplIsSupportedGraphic(); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::IsTransparent() const +{ + return mpImpGraphic->ImplIsTransparent(); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::IsAlpha() const +{ + return mpImpGraphic->ImplIsAlpha(); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::IsAnimated() const +{ + return mpImpGraphic->ImplIsAnimated(); +} + +// ------------------------------------------------------------------------ + +Bitmap Graphic::GetBitmap(const GraphicConversionParameters& rParameters) const +{ + return mpImpGraphic->ImplGetBitmap(rParameters); +} + +// ------------------------------------------------------------------------ + +BitmapEx Graphic::GetBitmapEx(const GraphicConversionParameters& rParameters) const +{ + return mpImpGraphic->ImplGetBitmapEx(rParameters); +} + +// ------------------------------------------------------------------------ + +Animation Graphic::GetAnimation() const +{ + return mpImpGraphic->ImplGetAnimation(); +} + +// ------------------------------------------------------------------------ + +const GDIMetaFile& Graphic::GetGDIMetaFile() const +{ + return mpImpGraphic->ImplGetGDIMetaFile(); +} + +// ------------------------------------------------------------------------ + +uno::Reference< graphic::XGraphic > Graphic::GetXGraphic() const +{ + uno::Reference< graphic::XGraphic > xRet; + + if( GetType() != GRAPHIC_NONE ) + { + uno::Reference < lang::XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() ); + + if( xMSF.is() ) + { + uno::Reference< graphic::XGraphicProvider > xProv( xMSF->createInstance( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.graphic.GraphicProvider" ) ) ), + uno::UNO_QUERY ); + + if( xProv.is() ) + { + uno::Sequence< beans::PropertyValue > aLoadProps( 1 ); + ::rtl::OUString aURL( RTL_CONSTASCII_USTRINGPARAM( "private:memorygraphic/" ) ); + + aLoadProps[ 0 ].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "URL" ) ); + aLoadProps[ 0 ].Value <<= ( aURL += ::rtl::OUString::valueOf( reinterpret_cast< sal_Int64 >( this ) ) ); + + xRet = xProv->queryGraphic( aLoadProps ); + } + } + } + + return xRet; +} + +// ------------------------------------------------------------------------ + +Size Graphic::GetPrefSize() const +{ + return mpImpGraphic->ImplGetPrefSize(); +} + +// ------------------------------------------------------------------------ + +void Graphic::SetPrefSize( const Size& rPrefSize ) +{ + ImplTestRefCount(); + mpImpGraphic->ImplSetPrefSize( rPrefSize ); +} + +// ------------------------------------------------------------------------ + +MapMode Graphic::GetPrefMapMode() const +{ + return mpImpGraphic->ImplGetPrefMapMode(); +} + +// ------------------------------------------------------------------------ + +void Graphic::SetPrefMapMode( const MapMode& rPrefMapMode ) +{ + ImplTestRefCount(); + mpImpGraphic->ImplSetPrefMapMode( rPrefMapMode ); +} + +// ------------------------------------------------------------------ + +Size Graphic::GetSizePixel( const OutputDevice* pRefDevice ) const +{ + Size aRet; + + if( GRAPHIC_BITMAP == mpImpGraphic->ImplGetType() ) + aRet = mpImpGraphic->ImplGetBitmapEx(GraphicConversionParameters()).GetSizePixel(); + else + aRet = ( pRefDevice ? pRefDevice : Application::GetDefaultDevice() )->LogicToPixel( GetPrefSize(), GetPrefMapMode() ); + + return aRet; +} + +// ------------------------------------------------------------------ + +sal_uLong Graphic::GetSizeBytes() const +{ + return mpImpGraphic->ImplGetSizeBytes(); +} + +// ------------------------------------------------------------------------ + +void Graphic::Draw( OutputDevice* pOutDev, const Point& rDestPt ) const +{ + mpImpGraphic->ImplDraw( pOutDev, rDestPt ); +} + +// ------------------------------------------------------------------------ + +void Graphic::Draw( OutputDevice* pOutDev, + const Point& rDestPt, const Size& rDestSz ) const +{ + if( GRAPHIC_DEFAULT == mpImpGraphic->ImplGetType() ) + ImplDrawDefault( pOutDev, NULL, NULL, NULL, NULL, rDestPt, rDestSz ); + else + mpImpGraphic->ImplDraw( pOutDev, rDestPt, rDestSz ); +} + +// ------------------------------------------------------------------------ + +void Graphic::Draw( OutputDevice* pOutDev, const String& rText, + Font& rFont, const Bitmap& rBitmap, + const Point& rDestPt, const Size& rDestSz ) +{ + ImplDrawDefault( pOutDev, &rText, &rFont, &rBitmap, NULL, rDestPt, rDestSz ); +} + +// ------------------------------------------------------------------------ + +void Graphic::DrawEx( OutputDevice* pOutDev, const String& rText, + Font& rFont, const BitmapEx& rBitmap, + const Point& rDestPt, const Size& rDestSz ) +{ + ImplDrawDefault( pOutDev, &rText, &rFont, NULL, &rBitmap, rDestPt, rDestSz ); +} + +// ------------------------------------------------------------------------ + +void Graphic::StartAnimation( OutputDevice* pOutDev, const Point& rDestPt, long nExtraData, + OutputDevice* pFirstFrameOutDev ) +{ + ImplTestRefCount(); + mpImpGraphic->ImplStartAnimation( pOutDev, rDestPt, nExtraData, pFirstFrameOutDev ); +} + +// ------------------------------------------------------------------------ + +void Graphic::StartAnimation( OutputDevice* pOutDev, const Point& rDestPt, + const Size& rDestSz, long nExtraData, + OutputDevice* pFirstFrameOutDev ) +{ + ImplTestRefCount(); + mpImpGraphic->ImplStartAnimation( pOutDev, rDestPt, rDestSz, nExtraData, pFirstFrameOutDev ); +} + +// ------------------------------------------------------------------------ + +void Graphic::StopAnimation( OutputDevice* pOutDev, long nExtraData ) +{ + ImplTestRefCount(); + mpImpGraphic->ImplStopAnimation( pOutDev, nExtraData ); +} + +// ------------------------------------------------------------------------ + +void Graphic::SetAnimationNotifyHdl( const Link& rLink ) +{ + mpImpGraphic->ImplSetAnimationNotifyHdl( rLink ); +} + +// ------------------------------------------------------------------------ + +Link Graphic::GetAnimationNotifyHdl() const +{ + return mpImpGraphic->ImplGetAnimationNotifyHdl(); +} + +// ------------------------------------------------------------------------ + +sal_uLong Graphic::GetAnimationLoopCount() const +{ + return mpImpGraphic->ImplGetAnimationLoopCount(); +} + +// ------------------------------------------------------------------------ + +void Graphic::ResetAnimationLoopCount() +{ + mpImpGraphic->ImplResetAnimationLoopCount(); +} + +// ------------------------------------------------------------------------ + +List* Graphic::GetAnimationInfoList() const +{ + return mpImpGraphic->ImplGetAnimationInfoList(); +} + +// ------------------------------------------------------------------------ + +GraphicReader* Graphic::GetContext() +{ + return mpImpGraphic->ImplGetContext(); +} + +// ------------------------------------------------------------------------ + +void Graphic::SetContext( GraphicReader* pReader ) +{ + mpImpGraphic->ImplSetContext( pReader ); +} + +// ------------------------------------------------------------------------ + +sal_uInt16 Graphic::GetGraphicsCompressMode( SvStream& rIStm ) +{ + const sal_uLong nPos = rIStm.Tell(); + const sal_uInt16 nOldFormat = rIStm.GetNumberFormatInt(); + sal_uInt32 nTmp32; + sal_uInt16 nTmp16; + sal_uInt16 nCompressMode = COMPRESSMODE_NONE; + + rIStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + rIStm >> nTmp32; + + // is it a swapped graphic with a bitmap? + rIStm.SeekRel( (nTmp32 == (sal_uInt32) GRAPHIC_BITMAP ) ? 40 : -4 ); + + // try to read bitmap id + rIStm >> nTmp16; + + // check id of BitmapFileHeader + if( 0x4D42 == nTmp16 ) + { + // seek to compress field of BitmapInfoHeader + rIStm.SeekRel( 28 ); + rIStm >> nTmp32; + + // Compare with our own compressmode + if( ZCOMPRESS == nTmp32 ) + nCompressMode = COMPRESSMODE_ZBITMAP; + } + + rIStm.SetNumberFormatInt( nOldFormat ); + rIStm.Seek( nPos ); + + return nCompressMode; +} + +// ------------------------------------------------------------------------ + +void Graphic::SetDocFileName( const String& rName, sal_uLong nFilePos ) +{ + mpImpGraphic->ImplSetDocFileName( rName, nFilePos ); +} + +// ------------------------------------------------------------------------ + +const String& Graphic::GetDocFileName() const +{ + return mpImpGraphic->ImplGetDocFileName(); +} + +// ------------------------------------------------------------------------ + +sal_uLong Graphic::GetDocFilePos() const +{ + return mpImpGraphic->ImplGetDocFilePos(); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::ReadEmbedded( SvStream& rIStream, sal_Bool bSwap ) +{ + ImplTestRefCount(); + return mpImpGraphic->ImplReadEmbedded( rIStream, bSwap ); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::WriteEmbedded( SvStream& rOStream ) +{ + ImplTestRefCount(); + return mpImpGraphic->ImplWriteEmbedded( rOStream ); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::SwapOut() +{ + ImplTestRefCount(); + return mpImpGraphic->ImplSwapOut(); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::SwapOut( SvStream* pOStream ) +{ + ImplTestRefCount(); + return mpImpGraphic->ImplSwapOut( pOStream ); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::SwapIn() +{ + ImplTestRefCount(); + return mpImpGraphic->ImplSwapIn(); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::SwapIn( SvStream* pStrm ) +{ + ImplTestRefCount(); + return mpImpGraphic->ImplSwapIn( pStrm ); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::IsSwapOut() const +{ + return mpImpGraphic->ImplIsSwapOut(); +} + +// ------------------------------------------------------------------------ + +void Graphic::SetLink( const GfxLink& rGfxLink ) +{ + ImplTestRefCount(); + mpImpGraphic->ImplSetLink( rGfxLink ); +} + +// ------------------------------------------------------------------------ + +GfxLink Graphic::GetLink() const +{ + return mpImpGraphic->ImplGetLink(); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::IsLink() const +{ + return mpImpGraphic->ImplIsLink(); +} + +// ------------------------------------------------------------------------ + +sal_uLong Graphic::GetChecksum() const +{ + return mpImpGraphic->ImplGetChecksum(); +} + +// ------------------------------------------------------------------------ + +sal_Bool Graphic::ExportNative( SvStream& rOStream ) const +{ + return mpImpGraphic->ImplExportNative( rOStream ); +} + +// ------------------------------------------------------------------------ + +SvStream& operator>>( SvStream& rIStream, Graphic& rGraphic ) +{ + rGraphic.ImplTestRefCount(); + return rIStream >> *rGraphic.mpImpGraphic; +} + +// ------------------------------------------------------------------------ + +SvStream& operator<<( SvStream& rOStream, const Graphic& rGraphic ) +{ + return rOStream << *rGraphic.mpImpGraphic; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/graphictools.cxx b/vcl/source/gdi/graphictools.cxx new file mode 100644 index 000000000000..2e20a3639790 --- /dev/null +++ b/vcl/source/gdi/graphictools.cxx @@ -0,0 +1,762 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/vcompat.hxx> + +#include <vcl/graphictools.hxx> + +static ::rtl::OString polyToString( const Polygon& rPoly ) +{ + ::rtl::OString aStr; + sal_uInt16 nVertex; + for(nVertex=0; nVertex<rPoly.GetSize(); ++nVertex) + { + aStr += "("; + switch( rPoly.GetFlags(nVertex) ) + { + case POLY_NORMAL: + case POLY_SMOOTH: + case POLY_SYMMTR: + aStr += "n: "; + break; + + case POLY_CONTROL: + aStr += "c: "; + break; + + default: + OSL_FAIL( "SvtGraphicStroke::polyToString invalid flag"); + break; + } + aStr += ::rtl::OString::valueOf( static_cast< double >( rPoly[nVertex].getX() ) ); + aStr += ","; + aStr += ::rtl::OString::valueOf( static_cast< double >( rPoly[nVertex].getY() ) ); + aStr += ") "; + } + + return aStr; +} + +static ::rtl::OString polyPolyToString( const PolyPolygon& rPolyPoly ) +{ + ::rtl::OString aStr; + sal_uInt16 nPoly; + for(nPoly=0; nPoly<rPolyPoly.Count(); ++nPoly) + { + const Polygon& rPoly = rPolyPoly[nPoly]; + + aStr += "{ "; + aStr += polyToString( rPoly ); + aStr += "} "; + } + + return aStr; +} + +static ::rtl::OString dashToString( const SvtGraphicStroke::DashArray& rDashArray ) +{ + ::rtl::OString aStr; + + aStr += "dash: [ "; + + int i, nDashes( rDashArray.size() ); + for(i=0; i<nDashes; ++i) + { + aStr += ::rtl::OString::valueOf( rDashArray[i] ); + aStr += " "; + } + + aStr += "] "; + + return aStr; +} + +static ::rtl::OString colorToString( Color aColor ) +{ + ::rtl::OString aStr; + + aStr += "color: [ "; + aStr += ::rtl::OString::valueOf( aColor.GetRed() ); + aStr += " "; + aStr += ::rtl::OString::valueOf( aColor.GetGreen() ); + aStr += " "; + aStr += ::rtl::OString::valueOf( aColor.GetBlue() ); + aStr += " ] "; + + return aStr; +} + +//////////////////////////////////////////////////////////////////////////// + +SvtGraphicFill::Transform::Transform() +{ + matrix[0] = 1.0; matrix[1] = 0.0; matrix[2] = 0.0; + matrix[3] = 0.0; matrix[4] = 1.0; matrix[5] = 0.0; +} + +//////////////////////////////////////////////////////////////////////////// + +SvtGraphicStroke::SvtGraphicStroke() : + maPath(), + maStartArrow(), + maEndArrow(), + mfTransparency(), + mfStrokeWidth(), + maCapType(), + maJoinType(), + mfMiterLimit( 3.0 ), + maDashArray() +{ +} + +SvtGraphicStroke::SvtGraphicStroke( const Polygon& rPath, + const PolyPolygon& rStartArrow, + const PolyPolygon& rEndArrow, + double fTransparency, + double fStrokeWidth, + CapType aCap, + JoinType aJoin, + double fMiterLimit, + const DashArray& rDashArray ) : + maPath( rPath ), + maStartArrow( rStartArrow ), + maEndArrow( rEndArrow ), + mfTransparency( fTransparency ), + mfStrokeWidth( fStrokeWidth ), + maCapType( aCap ), + maJoinType( aJoin ), + mfMiterLimit( fMiterLimit ), + maDashArray( rDashArray ) +{ +} + +void SvtGraphicStroke::getPath( Polygon& rPath ) const +{ + rPath = maPath; +} + +void SvtGraphicStroke::getStartArrow( PolyPolygon& rPath ) const +{ + rPath = maStartArrow; +} + +void SvtGraphicStroke::getEndArrow( PolyPolygon& rPath ) const +{ + rPath = maEndArrow; +} + +double SvtGraphicStroke::getTransparency() const +{ + return mfTransparency; +} + +double SvtGraphicStroke::getStrokeWidth() const +{ + return mfStrokeWidth; +} + +SvtGraphicStroke::CapType SvtGraphicStroke::getCapType() const +{ + return maCapType; +} + +SvtGraphicStroke::JoinType SvtGraphicStroke::getJoinType() const +{ + return maJoinType; +} + +double SvtGraphicStroke::getMiterLimit() const +{ + return mfMiterLimit; +} + +void SvtGraphicStroke::getDashArray( DashArray& rDashArray ) const +{ + rDashArray = maDashArray; +} + +::rtl::OString SvtGraphicStroke::toString() const +{ + ::rtl::OString aStr; + + aStr += polyToString( maPath ); + aStr += "trans: "; + aStr += ::rtl::OString::valueOf( static_cast< double >(getTransparency()) ); + aStr += " width: "; + aStr += ::rtl::OString::valueOf( static_cast< double >(getStrokeWidth()) ); + aStr += " cap: "; + switch( getCapType() ) + { + case capButt: + aStr += "butt"; + break; + + case capRound: + aStr += "round"; + break; + + case capSquare: + aStr += "square"; + break; + + default: + OSL_FAIL( "SvtGraphicStroke::toString missing cap type"); + break; + } + aStr += " join: "; + switch( getJoinType() ) + { + case joinMiter: + aStr += "miter"; + break; + + case joinRound: + aStr += "round"; + break; + + case joinBevel: + aStr += "bevel"; + break; + + case joinNone: + aStr += "none"; + break; + + default: + OSL_FAIL( "SvtGraphicStroke::toString missing join type"); + break; + } + aStr += " "; + + if( maStartArrow.Count() ) + { + aStr += "start: "; + aStr += polyPolyToString( maStartArrow ); + aStr += " "; + } + + if( maEndArrow.Count() ) + { + aStr += "end: "; + aStr += polyPolyToString( maEndArrow ); + aStr += " "; + } + + aStr += dashToString( maDashArray ); + + return aStr; +} + +void SvtGraphicStroke::setPath( const Polygon& rPoly ) +{ + maPath = rPoly; +} + +void SvtGraphicStroke::setStartArrow( const PolyPolygon& rPoly ) +{ + maStartArrow = rPoly; +} + +void SvtGraphicStroke::setEndArrow( const PolyPolygon& rPoly ) +{ + maEndArrow = rPoly; +} + +void SvtGraphicStroke::setTransparency( double fTrans ) +{ + mfTransparency = fTrans; +} + +void SvtGraphicStroke::setStrokeWidth( double fWidth ) +{ + mfStrokeWidth = fWidth; +} + +void SvtGraphicStroke::setCapType( CapType eType ) +{ + maCapType = eType; +} + +void SvtGraphicStroke::setJoinType( JoinType eType ) +{ + maJoinType = eType; +} + +void SvtGraphicStroke::setMiterLimit( double fMiterLimit ) +{ + mfMiterLimit = fMiterLimit; +} + +void SvtGraphicStroke::setDashArray( const DashArray& rDashArray ) +{ + maDashArray = rDashArray; +} + +SvStream& operator<<( SvStream& rOStm, const SvtGraphicStroke& rClass ) +{ + VersionCompat aCompat( rOStm, STREAM_WRITE, 1 ); + + rClass.maPath.Write( rOStm ); + rClass.maStartArrow.Write( rOStm ); + rClass.maEndArrow.Write( rOStm ); + rOStm << rClass.mfTransparency; + rOStm << rClass.mfStrokeWidth; + sal_uInt16 nTmp = sal::static_int_cast<sal_uInt16>( rClass.maCapType ); + rOStm << nTmp; + nTmp = sal::static_int_cast<sal_uInt16>( rClass.maJoinType ); + rOStm << nTmp; + rOStm << rClass.mfMiterLimit; + + rOStm << static_cast<sal_uInt32>(rClass.maDashArray.size()); + size_t i; + for(i=0; i<rClass.maDashArray.size(); ++i) + rOStm << rClass.maDashArray[i]; + + return rOStm; +} + +SvStream& operator>>( SvStream& rIStm, SvtGraphicStroke& rClass ) +{ + VersionCompat aCompat( rIStm, STREAM_READ ); + + rClass.maPath.Read( rIStm ); + rClass.maStartArrow.Read( rIStm ); + rClass.maEndArrow.Read( rIStm ); + rIStm >> rClass.mfTransparency; + rIStm >> rClass.mfStrokeWidth; + sal_uInt16 nTmp; + rIStm >> nTmp; + rClass.maCapType = SvtGraphicStroke::CapType(nTmp); + rIStm >> nTmp; + rClass.maJoinType = SvtGraphicStroke::JoinType(nTmp); + rIStm >> rClass.mfMiterLimit; + + sal_uInt32 nSize; + rIStm >> nSize; + rClass.maDashArray.resize(nSize); + size_t i; + for(i=0; i<rClass.maDashArray.size(); ++i) + rIStm >> rClass.maDashArray[i]; + + return rIStm; +} + + +///////////////////////////////////////////////////////////////////////////// + +SvtGraphicFill::SvtGraphicFill() : + maPath(), + maFillColor( COL_BLACK ), + mfTransparency(), + maFillRule(), + maFillType(), + maFillTransform(), + maHatchType(), + maHatchColor( COL_BLACK ), + maGradientType(), + maGradient1stColor( COL_BLACK ), + maGradient2ndColor( COL_BLACK ), + maGradientStepCount( gradientStepsInfinite ), + maFillGraphic() +{ +} + +SvtGraphicFill::SvtGraphicFill( const PolyPolygon& rPath, + Color aFillColor, + double fTransparency, + FillRule aFillRule, + FillType aFillType, + const Transform& aFillTransform, + bool bTiling, + HatchType aHatchType, + Color aHatchColor, + GradientType aGradientType, + Color aGradient1stColor, + Color aGradient2ndColor, + int aGradientStepCount, + const Graphic& aFillGraphic ) : + maPath( rPath ), + maFillColor( aFillColor ), + mfTransparency( fTransparency ), + maFillRule( aFillRule ), + maFillType( aFillType ), + maFillTransform( aFillTransform ), + mbTiling( bTiling ), + maHatchType( aHatchType ), + maHatchColor( aHatchColor ), + maGradientType( aGradientType ), + maGradient1stColor( aGradient1stColor ), + maGradient2ndColor( aGradient2ndColor ), + maGradientStepCount( aGradientStepCount ), + maFillGraphic( aFillGraphic ) +{ +} + +void SvtGraphicFill::getPath( PolyPolygon& rPath ) const +{ + rPath = maPath; +} + +Color SvtGraphicFill::getFillColor() const +{ + return maFillColor; +} + +double SvtGraphicFill::getTransparency() const +{ + return mfTransparency; +} + +SvtGraphicFill::FillRule SvtGraphicFill::getFillRule() const +{ + return maFillRule; +} + +SvtGraphicFill::FillType SvtGraphicFill::getFillType() const +{ + return maFillType; +} + +void SvtGraphicFill::getTransform( Transform& rTrans ) const +{ + rTrans = maFillTransform; +} + +bool SvtGraphicFill::IsTiling() const +{ + return mbTiling; +} + +bool SvtGraphicFill::isTiling() const +{ + return mbTiling; +} + +SvtGraphicFill::HatchType SvtGraphicFill::getHatchType() const +{ + return maHatchType; +} + +Color SvtGraphicFill::getHatchColor() const +{ + return maHatchColor; +} + +SvtGraphicFill::GradientType SvtGraphicFill::getGradientType() const +{ + return maGradientType; +} + +Color SvtGraphicFill::getGradient1stColor() const +{ + return maGradient1stColor; +} + +Color SvtGraphicFill::getGradient2ndColor() const +{ + return maGradient2ndColor; +} + +int SvtGraphicFill::getGradientStepCount() const +{ + return maGradientStepCount; +} + +void SvtGraphicFill::getGraphic( Graphic& rGraphic ) const +{ + rGraphic = maFillGraphic; +} + +::rtl::OString SvtGraphicFill::toString() const +{ + ::rtl::OString aStr; + + aStr += polyPolyToString( maPath ); + aStr += "fill"; + aStr += colorToString( getFillColor() ); + aStr += " trans: "; + aStr += ::rtl::OString::valueOf( static_cast< double >(getTransparency()) ); + aStr += " rule: "; + switch( getFillRule() ) + { + case fillNonZero: + aStr += "nonzero"; + break; + + case fillEvenOdd: + aStr += "evenodd"; + break; + + default: + OSL_FAIL( "SvtGraphicFill::toString missing fill rule"); + break; + } + aStr += " type: "; + switch( getFillType() ) + { + case fillSolid: + aStr += "solid"; + break; + + case fillGradient: + aStr += "gradient"; + break; + + case fillHatch: + aStr += "hatch"; + break; + + case fillTexture: + aStr += "bitmap"; + break; + + default: + OSL_FAIL( "SvtGraphicStroke::toString missing fill type"); + break; + } + + aStr += " transform: [ "; + int i; + for(i=0; i<Transform::MatrixSize; ++i) + aStr += ::rtl::OString::valueOf( maFillTransform.matrix[i] ); + aStr += " ] "; + + aStr += " hatch: "; + switch( getHatchType() ) + { + case hatchSingle: + aStr += "single"; + break; + + case hatchDouble: + aStr += "double"; + break; + + case hatchTriple: + aStr += "triple"; + break; + + default: + OSL_FAIL( "SvtGraphicStroke::toString missing hatch type"); + break; + } + + aStr += " hatch"; + aStr += colorToString( getHatchColor() ); + + aStr += " gradient: "; + switch( getGradientType() ) + { + case gradientLinear: + aStr += "linear"; + break; + + case gradientRadial: + aStr += "radial"; + break; + + case gradientRectangular: + aStr += "rectangular"; + break; + + default: + OSL_FAIL( "SvtGraphicStroke::toString missing gradient type"); + break; + } + + aStr += " grad1st"; + aStr += colorToString( getGradient1stColor() ); + + aStr += " grad2nd"; + aStr += colorToString( getGradient2ndColor() ); + + aStr += " gradstep"; + aStr += ::rtl::OString::valueOf( (sal_Int32)getGradientStepCount() ); + + if( maFillGraphic.GetType() != GRAPHIC_NONE ) + { + aStr += " fillgraphic: "; + switch( maFillGraphic.GetType() ) + { + case GRAPHIC_BITMAP: + aStr += "bitmap"; + break; + + case GRAPHIC_GDIMETAFILE: + aStr += "metafile"; + break; + + case GRAPHIC_DEFAULT: + aStr += "default"; + break; + + default: + OSL_FAIL( "SvtGraphicStroke::toString missing graphic type"); + break; + } + + aStr += " of "; + aStr += ::rtl::OString::valueOf( static_cast< sal_Int32 >(maFillGraphic.GetSizeBytes()) ); + aStr += " bytes"; + } + + return aStr; +} + +void SvtGraphicFill::setPath( const PolyPolygon& rPath ) +{ + maPath = rPath; +} + +void SvtGraphicFill::setFillColor( Color aFillColor ) +{ + maFillColor = aFillColor; +} + +void SvtGraphicFill::setTransparency( double fTransparency ) +{ + mfTransparency = fTransparency; +} + +void SvtGraphicFill::setFillRule( FillRule aFillRule ) +{ + maFillRule = aFillRule; +} + +void SvtGraphicFill::setFillType( FillType aFillType ) +{ + maFillType = aFillType; +} + +void SvtGraphicFill::setTransform( const Transform& rTransform ) +{ + maFillTransform = rTransform; +} + +void SvtGraphicFill::setTiling( bool bTiling ) +{ + mbTiling = bTiling; +} + +void SvtGraphicFill::setHatchType( HatchType aHatchType ) +{ + maHatchType = aHatchType; +} + +void SvtGraphicFill::setHatchColor( Color aHatchColor ) +{ + maHatchColor = aHatchColor; +} + +void SvtGraphicFill::setGradientType( GradientType aGradType ) +{ + maGradientType = aGradType; +} + +void SvtGraphicFill::setGradient1stColor( Color aColor ) +{ + maGradient1stColor = aColor; +} + +void SvtGraphicFill::setGradient2ndColor( Color aColor ) +{ + maGradient2ndColor = aColor; +} + +void SvtGraphicFill::setGradientStepCount( int aCount ) +{ + maGradientStepCount = aCount; +} + +void SvtGraphicFill::setGraphic( const Graphic& rGraphic ) +{ + maFillGraphic = rGraphic; +} + +SvStream& operator<<( SvStream& rOStm, const SvtGraphicFill& rClass ) +{ + VersionCompat aCompat( rOStm, STREAM_WRITE, 1 ); + + rClass.maPath.Write( rOStm ); + rOStm << rClass.maFillColor; + rOStm << rClass.mfTransparency; + sal_uInt16 nTmp = sal::static_int_cast<sal_uInt16>( rClass.maFillRule ); + rOStm << nTmp; + nTmp = sal::static_int_cast<sal_uInt16>( rClass.maFillType ); + rOStm << nTmp; + int i; + for(i=0; i<SvtGraphicFill::Transform::MatrixSize; ++i) + rOStm << rClass.maFillTransform.matrix[i]; + nTmp = rClass.mbTiling; + rOStm << nTmp; + nTmp = sal::static_int_cast<sal_uInt16>( rClass.maHatchType ); + rOStm << nTmp; + rOStm << rClass.maHatchColor; + nTmp = sal::static_int_cast<sal_uInt16>( rClass.maGradientType ); + rOStm << nTmp; + rOStm << rClass.maGradient1stColor; + rOStm << rClass.maGradient2ndColor; + rOStm << rClass.maGradientStepCount; + rOStm << rClass.maFillGraphic; + + return rOStm; +} + +SvStream& operator>>( SvStream& rIStm, SvtGraphicFill& rClass ) +{ + VersionCompat aCompat( rIStm, STREAM_READ ); + + rClass.maPath.Read( rIStm ); + rIStm >> rClass.maFillColor; + rIStm >> rClass.mfTransparency; + sal_uInt16 nTmp; + rIStm >> nTmp; + rClass.maFillRule = SvtGraphicFill::FillRule( nTmp ); + rIStm >> nTmp; + rClass.maFillType = SvtGraphicFill::FillType( nTmp ); + int i; + for(i=0; i<SvtGraphicFill::Transform::MatrixSize; ++i) + rIStm >> rClass.maFillTransform.matrix[i]; + rIStm >> nTmp; + rClass.mbTiling = nTmp; + rIStm >> nTmp; + rClass.maHatchType = SvtGraphicFill::HatchType( nTmp ); + rIStm >> rClass.maHatchColor; + rIStm >> nTmp; + rClass.maGradientType = SvtGraphicFill::GradientType( nTmp ); + rIStm >> rClass.maGradient1stColor; + rIStm >> rClass.maGradient2ndColor; + rIStm >> rClass.maGradientStepCount; + rIStm >> rClass.maFillGraphic; + + return rIStm; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/hatch.cxx b/vcl/source/gdi/hatch.cxx new file mode 100644 index 000000000000..9836b2e6e4b4 --- /dev/null +++ b/vcl/source/gdi/hatch.cxx @@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <tools/debug.hxx> +#include <vcl/hatch.hxx> + +DBG_NAME( Hatch ) + +// -------------- +// - ImplHatch - +// -------------- + +ImplHatch::ImplHatch() : + mnRefCount ( 1 ), + maColor ( COL_BLACK ), + meStyle ( HATCH_SINGLE ), + mnDistance ( 1 ), + mnAngle ( 0 ) +{ +} + +// ----------------------------------------------------------------------- + +ImplHatch::ImplHatch( const ImplHatch& rImplHatch ) : + mnRefCount ( 1 ), + maColor ( rImplHatch.maColor ), + meStyle ( rImplHatch.meStyle ), + mnDistance ( rImplHatch.mnDistance ), + mnAngle ( rImplHatch.mnAngle ) +{ +} + +// --------- +// - Hatch - +// --------- + +Hatch::Hatch() +{ + DBG_CTOR( Hatch, NULL ); + mpImplHatch = new ImplHatch; +} + +// ----------------------------------------------------------------------- + +Hatch::Hatch( const Hatch& rHatch ) +{ + DBG_CTOR( Hatch, NULL ); + DBG_CHKOBJ( &rHatch, Hatch, NULL ); + mpImplHatch = rHatch.mpImplHatch; + mpImplHatch->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +Hatch::Hatch( HatchStyle eStyle, const Color& rColor, + long nDistance, sal_uInt16 nAngle10 ) +{ + DBG_CTOR( Hatch, NULL ); + mpImplHatch = new ImplHatch; + mpImplHatch->maColor = rColor; + mpImplHatch->meStyle = eStyle; + mpImplHatch->mnDistance = nDistance; + mpImplHatch->mnAngle = nAngle10; +} + +// ----------------------------------------------------------------------- + +Hatch::~Hatch() +{ + DBG_DTOR( Hatch, NULL ); + if( !( --mpImplHatch->mnRefCount ) ) + delete mpImplHatch; +} + +// ----------------------------------------------------------------------- + +Hatch& Hatch::operator=( const Hatch& rHatch ) +{ + DBG_CHKTHIS( Hatch, NULL ); + DBG_CHKOBJ( &rHatch, Hatch, NULL ); + + rHatch.mpImplHatch->mnRefCount++; + + if( !( --mpImplHatch->mnRefCount ) ) + delete mpImplHatch; + + mpImplHatch = rHatch.mpImplHatch; + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool Hatch::operator==( const Hatch& rHatch ) const +{ + DBG_CHKTHIS( Hatch, NULL ); + DBG_CHKOBJ( &rHatch, Hatch, NULL ); + + return( mpImplHatch == rHatch.mpImplHatch || + ( mpImplHatch->maColor == rHatch.mpImplHatch->maColor && + mpImplHatch->meStyle == rHatch.mpImplHatch->meStyle && + mpImplHatch->mnDistance == rHatch.mpImplHatch->mnDistance && + mpImplHatch->mnAngle == rHatch.mpImplHatch->mnAngle ) ); +} + +// ----------------------------------------------------------------------- + +void Hatch::ImplMakeUnique() +{ + if( mpImplHatch->mnRefCount != 1 ) + { + if( mpImplHatch->mnRefCount ) + mpImplHatch->mnRefCount--; + + mpImplHatch = new ImplHatch( *mpImplHatch ); + } +} + +// ----------------------------------------------------------------------- + +void Hatch::SetStyle( HatchStyle eStyle ) +{ + DBG_CHKTHIS( Hatch, NULL ); + ImplMakeUnique(); + mpImplHatch->meStyle = eStyle; +} + +// ----------------------------------------------------------------------- + +void Hatch::SetColor( const Color& rColor ) +{ + DBG_CHKTHIS( Hatch, NULL ); + ImplMakeUnique(); + mpImplHatch->maColor = rColor; +} + +// ----------------------------------------------------------------------- + +void Hatch::SetDistance( long nDistance ) +{ + DBG_CHKTHIS( Hatch, NULL ); + ImplMakeUnique(); + mpImplHatch->mnDistance = nDistance; +} + +// ----------------------------------------------------------------------- + +void Hatch::SetAngle( sal_uInt16 nAngle10 ) +{ + DBG_CHKTHIS( Hatch, NULL ); + ImplMakeUnique(); + mpImplHatch->mnAngle = nAngle10; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, ImplHatch& rImplHatch ) +{ + VersionCompat aCompat( rIStm, STREAM_READ ); + sal_uInt16 nTmp16; + + rIStm >> nTmp16; rImplHatch.meStyle = (HatchStyle) nTmp16; + rIStm >> rImplHatch.maColor >> rImplHatch.mnDistance >> rImplHatch.mnAngle; + + return rIStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const ImplHatch& rImplHatch ) +{ + VersionCompat aCompat( rOStm, STREAM_WRITE, 1 ); + + rOStm << (sal_uInt16) rImplHatch.meStyle << rImplHatch.maColor; + rOStm << rImplHatch.mnDistance << rImplHatch.mnAngle; + + return rOStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, Hatch& rHatch ) +{ + rHatch.ImplMakeUnique(); + return( rIStm >> *rHatch.mpImplHatch ); +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const Hatch& rHatch ) +{ + return( rOStm << *rHatch.mpImplHatch ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/image.cxx b/vcl/source/gdi/image.cxx new file mode 100644 index 000000000000..24c86c326910 --- /dev/null +++ b/vcl/source/gdi/image.cxx @@ -0,0 +1,974 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <boost/scoped_ptr.hpp> +#include <boost/scoped_array.hpp> + +#include <rtl/logfile.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <tools/rc.h> +#include <tools/rc.hxx> +#include <tools/resmgr.hxx> +#include <vcl/settings.hxx> +#include <vcl/outdev.hxx> +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <vcl/impimagetree.hxx> +#include <vcl/image.h> +#include <vcl/image.hxx> +#include <vcl/imagerepository.hxx> + +#if OSL_DEBUG_LEVEL > 0 +#include <rtl/strbuf.hxx> +#endif + +DBG_NAME( Image ) +DBG_NAME( ImageList ) + +#define IMAGE_FILE_VERSION 100 + +using namespace ::com::sun::star; + +// --------- +// - Image - +// --------- + +Image::Image() : + mpImplData( NULL ) +{ + DBG_CTOR( Image, NULL ); +} + +// ----------------------------------------------------------------------- + +Image::Image( const ResId& rResId ) : + mpImplData( NULL ) +{ + DBG_CTOR( Image, NULL ); + + rResId.SetRT( RSC_IMAGE ); + + ResMgr* pResMgr = rResId.GetResMgr(); + if( pResMgr && pResMgr->GetResource( rResId ) ) + { + pResMgr->Increment( sizeof( RSHEADER_TYPE ) ); + + BitmapEx aBmpEx; + sal_uLong nObjMask = pResMgr->ReadLong(); + + if( nObjMask & RSC_IMAGE_IMAGEBITMAP ) + { + aBmpEx = BitmapEx( ResId( (RSHEADER_TYPE*)pResMgr->GetClass(), *pResMgr ) ); + pResMgr->Increment( pResMgr->GetObjSize( (RSHEADER_TYPE*)pResMgr->GetClass() ) ); + } + + if( nObjMask & RSC_IMAGE_MASKBITMAP ) + { + if( !aBmpEx.IsEmpty() && aBmpEx.GetTransparentType() == TRANSPARENT_NONE ) + { + const Bitmap aMaskBitmap( ResId( (RSHEADER_TYPE*)pResMgr->GetClass(), *pResMgr ) ); + aBmpEx = BitmapEx( aBmpEx.GetBitmap(), aMaskBitmap ); + } + + pResMgr->Increment( pResMgr->GetObjSize( (RSHEADER_TYPE*)pResMgr->GetClass() ) ); + } + + if( nObjMask & RSC_IMAGE_MASKCOLOR ) + { + if( !aBmpEx.IsEmpty() && aBmpEx.GetTransparentType() == TRANSPARENT_NONE ) + { + const Color aMaskColor( ResId( (RSHEADER_TYPE*)pResMgr->GetClass(), *pResMgr ) ); + aBmpEx = BitmapEx( aBmpEx.GetBitmap(), aMaskColor ); + } + + pResMgr->Increment( pResMgr->GetObjSize( (RSHEADER_TYPE*)pResMgr->GetClass() ) ); + } + if( ! aBmpEx.IsEmpty() ) + ImplInit( aBmpEx ); + } +} + +// ----------------------------------------------------------------------- + +Image::Image( const Image& rImage ) : + mpImplData( rImage.mpImplData ) +{ + DBG_CTOR( Image, NULL ); + + if( mpImplData ) + ++mpImplData->mnRefCount; +} + +// ----------------------------------------------------------------------- + +Image::Image( const BitmapEx& rBitmapEx ) : + mpImplData( NULL ) +{ + DBG_CTOR( Image, NULL ); + + ImplInit( rBitmapEx ); +} + +// ----------------------------------------------------------------------- + +Image::Image( const Bitmap& rBitmap ) : + mpImplData( NULL ) +{ + DBG_CTOR( Image, NULL ); + + ImplInit( rBitmap ); +} + +// ----------------------------------------------------------------------- + +Image::Image( const Bitmap& rBitmap, const Bitmap& rMaskBitmap ) : + mpImplData( NULL ) +{ + DBG_CTOR( Image, NULL ); + + const BitmapEx aBmpEx( rBitmap, rMaskBitmap ); + + ImplInit( aBmpEx ); +} + +// ----------------------------------------------------------------------- + +Image::Image( const Bitmap& rBitmap, const Color& rColor ) : + mpImplData( NULL ) +{ + DBG_CTOR( Image, NULL ); + + const BitmapEx aBmpEx( rBitmap, rColor ); + + ImplInit( aBmpEx ); +} + +// ----------------------------------------------------------------------- + +Image::Image( const uno::Reference< graphic::XGraphic >& rxGraphic ) : + mpImplData( NULL ) +{ + DBG_CTOR( Image, NULL ); + + const Graphic aGraphic( rxGraphic ); + ImplInit( aGraphic.GetBitmapEx() ); +} + +// ----------------------------------------------------------------------- + +Image::~Image() +{ + DBG_DTOR( Image, NULL ); + + if( mpImplData && ( 0 == --mpImplData->mnRefCount ) ) + delete mpImplData; +} + +// ----------------------------------------------------------------------- + +void Image::ImplInit( const BitmapEx& rBmpEx ) +{ + if( !rBmpEx.IsEmpty() ) + { + mpImplData = new ImplImage; + mpImplData->mnRefCount = 1; + + if( rBmpEx.GetTransparentType() == TRANSPARENT_NONE ) + { + mpImplData->meType = IMAGETYPE_BITMAP; + mpImplData->mpData = new Bitmap( rBmpEx.GetBitmap() ); + } + else + { + mpImplData->meType = IMAGETYPE_IMAGE; + mpImplData->mpData = new ImplImageData( rBmpEx ); + } + } +} + +// ----------------------------------------------------------------------- + +Size Image::GetSizePixel() const +{ + DBG_CHKTHIS( Image, NULL ); + + Size aRet; + + if( mpImplData ) + { + switch( mpImplData->meType ) + { + case IMAGETYPE_BITMAP: + aRet = static_cast< Bitmap* >( mpImplData->mpData )->GetSizePixel(); + break; + + case IMAGETYPE_IMAGE: + aRet = static_cast< ImplImageData* >( mpImplData->mpData )->maBmpEx.GetSizePixel(); + break; + } + } + + return aRet; +} + +// ----------------------------------------------------------------------- + +BitmapEx Image::GetBitmapEx() const +{ + DBG_CHKTHIS( Image, NULL ); + + BitmapEx aRet; + + if( mpImplData ) + { + switch( mpImplData->meType ) + { + case IMAGETYPE_BITMAP: + aRet = *static_cast< Bitmap* >( mpImplData->mpData ); + break; + + case IMAGETYPE_IMAGE: + aRet = static_cast< ImplImageData* >( mpImplData->mpData )->maBmpEx; + break; + } + } + + return aRet; +} + +// ----------------------------------------------------------------------- + +uno::Reference< graphic::XGraphic > Image::GetXGraphic() const +{ + const Graphic aGraphic( GetBitmapEx() ); + + return aGraphic.GetXGraphic(); +} + +// ----------------------------------------------------------------------- + +void Image::Invert() +{ + BitmapEx aInvertedBmp( GetBitmapEx() ); + aInvertedBmp.Invert(); + *this = aInvertedBmp; +} + +// ----------------------------------------------------------------------- + +void Image::GetColorTransformArrays( ImageColorTransform eColorTransform, + Color*& rpSrcColor, Color*& rpDstColor, sal_uLong& rColorCount ) +{ + if( IMAGECOLORTRANSFORM_HIGHCONTRAST == eColorTransform ) + { + rpSrcColor = new Color[ 4 ]; + rpDstColor = new Color[ 4 ]; + rColorCount = 4; + + rpSrcColor[ 0 ] = Color( COL_BLACK ); + rpDstColor[ 0 ] = Color( COL_WHITE ); + + rpSrcColor[ 1 ] = Color( COL_WHITE ); + rpDstColor[ 1 ] = Color( COL_BLACK ); + + rpSrcColor[ 2 ] = Color( COL_BLUE ); + rpDstColor[ 2 ] = Color( COL_WHITE ); + + rpSrcColor[ 3 ] = Color( COL_LIGHTBLUE ); + rpDstColor[ 3 ] = Color( COL_WHITE ); + } + else + { + rpSrcColor = rpDstColor = NULL; + rColorCount = 0; + } +} + +// ----------------------------------------------------------------------- + +Image& Image::operator=( const Image& rImage ) +{ + DBG_CHKTHIS( Image, NULL ); + DBG_CHKOBJ( &rImage, Image, NULL ); + + if( rImage.mpImplData ) + ++rImage.mpImplData->mnRefCount; + + if( mpImplData && ( 0 == --mpImplData->mnRefCount ) ) + delete mpImplData; + + mpImplData = rImage.mpImplData; + + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool Image::operator==( const Image& rImage ) const +{ + DBG_CHKTHIS( Image, NULL ); + DBG_CHKOBJ( &rImage, Image, NULL ); + + bool bRet = false; + + if( rImage.mpImplData == mpImplData ) + bRet = true; + else if( !rImage.mpImplData || !mpImplData ) + bRet = false; + else if( rImage.mpImplData->mpData == mpImplData->mpData ) + bRet = true; + else if( rImage.mpImplData->meType == mpImplData->meType ) + { + switch( mpImplData->meType ) + { + case IMAGETYPE_BITMAP: + bRet = ( *static_cast< Bitmap* >( rImage.mpImplData->mpData ) == *static_cast< Bitmap* >( mpImplData->mpData ) ); + break; + + case IMAGETYPE_IMAGE: + bRet = static_cast< ImplImageData* >( rImage.mpImplData->mpData )->IsEqual( *static_cast< ImplImageData* >( mpImplData->mpData ) ); + break; + + default: + bRet = false; + break; + } + } + + return bRet; +} + +// ------------- +// - ImageList - +// ------------- + +ImageList::ImageList( sal_uInt16 nInit, sal_uInt16 nGrow ) : + mpImplData( NULL ), + mnInitSize( nInit ), + mnGrowSize( nGrow ) +{ + DBG_CTOR( ImageList, NULL ); +} + +// ----------------------------------------------------------------------- + +ImageList::ImageList( const ResId& rResId ) : + mpImplData( NULL ), + mnInitSize( 1 ), + mnGrowSize( 4 ) +{ + RTL_LOGFILE_CONTEXT( aLog, "vcl: ImageList::ImageList( const ResId& rResId )" ); + + DBG_CTOR( ImageList, NULL ); + + rResId.SetRT( RSC_IMAGELIST ); + + ResMgr* pResMgr = rResId.GetResMgr(); + + if( pResMgr && pResMgr->GetResource( rResId ) ) + { + pResMgr->Increment( sizeof( RSHEADER_TYPE ) ); + + sal_uLong nObjMask = pResMgr->ReadLong(); + const String aPrefix( pResMgr->ReadString() ); + ::boost::scoped_ptr< Color > spMaskColor; + + if( nObjMask & RSC_IMAGE_MASKCOLOR ) + spMaskColor.reset( new Color( ResId( (RSHEADER_TYPE*)pResMgr->GetClass(), *pResMgr ) ) ); + + pResMgr->Increment( pResMgr->GetObjSize( (RSHEADER_TYPE*)pResMgr->GetClass() ) ); + + if( nObjMask & RSC_IMAGELIST_IDLIST ) + { + for( sal_Int32 i = 0, nCount = pResMgr->ReadLong(); i < nCount; ++i ) + pResMgr->ReadLong(); + } + + sal_Int32 nCount = pResMgr->ReadLong(); + ImplInit( static_cast< sal_uInt16 >( nCount ), Size() ); + + BitmapEx aEmpty; + for( sal_Int32 i = 0; i < nCount; ++i ) + { + rtl::OUString aName = pResMgr->ReadString(); + sal_uInt16 nId = static_cast< sal_uInt16 >( pResMgr->ReadLong() ); + mpImplData->AddImage( aName, nId, aEmpty ); + } + + if( nObjMask & RSC_IMAGELIST_IDCOUNT ) + pResMgr->ReadShort(); + } +} + +// ----------------------------------------------------------------------- + +ImageList::ImageList( const ::std::vector< ::rtl::OUString >& rNameVector, + const ::rtl::OUString& rPrefix, + const Color* ) : + mpImplData( NULL ), + mnInitSize( 1 ), + mnGrowSize( 4 ) +{ + RTL_LOGFILE_CONTEXT( aLog, "vcl: ImageList::ImageList(const vector< OUString >& ..." ); + + DBG_CTOR( ImageList, NULL ); + + ImplInit( sal::static_int_cast< sal_uInt16 >( rNameVector.size() ), Size() ); + + mpImplData->maPrefix = rPrefix; + for( sal_uInt32 i = 0; i < rNameVector.size(); ++i ) + { + mpImplData->AddImage( rNameVector[ i ], static_cast< sal_uInt16 >( i ) + 1, BitmapEx() ); + } +} + +// ----------------------------------------------------------------------- + +ImageList::ImageList( const ImageList& rImageList ) : + mpImplData( rImageList.mpImplData ), + mnInitSize( rImageList.mnInitSize ), + mnGrowSize( rImageList.mnGrowSize ) +{ + DBG_CTOR( ImageList, NULL ); + + if( mpImplData ) + ++mpImplData->mnRefCount; +} + +// ----------------------------------------------------------------------- + +ImageList::~ImageList() +{ + DBG_DTOR( ImageList, NULL ); + + if( mpImplData && ( 0 == --mpImplData->mnRefCount ) ) + delete mpImplData; +} + +void ImageList::ImplInit( sal_uInt16 nItems, const Size &rSize ) +{ + mpImplData = new ImplImageList; + mpImplData->mnRefCount = 1; + mpImplData->maImages.reserve( nItems ); + mpImplData->maImageSize = rSize; +} + +// ----------------------------------------------------------------------- + +void ImageAryData::Load(const rtl::OUString &rPrefix) +{ + static ImplImageTreeSingletonRef aImageTree; + + ::rtl::OUString aSymbolsStyle = Application::GetSettings().GetStyleSettings().GetCurrentSymbolsStyleName(); + + BitmapEx aBmpEx; + + rtl::OUString aFileName = rPrefix; + aFileName += maName; +#if OSL_DEBUG_LEVEL > 0 + bool bSuccess = +#endif + aImageTree->loadImage( aFileName, aSymbolsStyle, maBitmapEx, true ); +#if OSL_DEBUG_LEVEL > 0 + if ( !bSuccess ) + { + ::rtl::OStringBuffer aMessage; + aMessage.append( "ImageAryData::Load: failed to load image '" ); + aMessage.append( ::rtl::OUStringToOString( aFileName, RTL_TEXTENCODING_UTF8 ).getStr() ); + aMessage.append( "'" ); + OSL_FAIL( aMessage.makeStringAndClear().getStr() ); + } +#endif +} + +// ----------------------------------------------------------------------- + +void ImageList::ImplMakeUnique() +{ + if( mpImplData && mpImplData->mnRefCount > 1 ) + { + --mpImplData->mnRefCount; + mpImplData = new ImplImageList( *mpImplData ) ; + } +} + +// ----------------------------------------------------------------------- +// Rather a performance hazard: +BitmapEx ImageList::GetAsHorizontalStrip() const +{ + Size aSize( mpImplData->maImageSize ); + sal_uInt16 nCount = GetImageCount(); + if( !nCount ) + return BitmapEx(); + aSize.Width() *= nCount; + + // Load any stragglers + for (sal_uInt16 nIdx = 0; nIdx < nCount; nIdx++) + { + ImageAryData *pData = mpImplData->maImages[ nIdx ]; + if( pData->IsLoadable() ) + pData->Load( mpImplData->maPrefix ); + } + + BitmapEx aTempl = mpImplData->maImages[ 0 ]->maBitmapEx; + BitmapEx aResult; + Bitmap aPixels( aSize, aTempl.GetBitmap().GetBitCount() ); + if( aTempl.IsAlpha() ) + aResult = BitmapEx( aPixels, AlphaMask( aSize ) ); + else if( aTempl.IsTransparent() ) + aResult = BitmapEx( aPixels, Bitmap( aSize, aTempl.GetMask().GetBitCount() ) ); + else + aResult = BitmapEx( aPixels ); + + Rectangle aSrcRect( Point( 0, 0 ), mpImplData->maImageSize ); + for (sal_uInt16 nIdx = 0; nIdx < nCount; nIdx++) + { + Rectangle aDestRect( Point( nIdx * mpImplData->maImageSize.Width(), 0 ), + mpImplData->maImageSize ); + ImageAryData *pData = mpImplData->maImages[ nIdx ]; + aResult.CopyPixel( aDestRect, aSrcRect, &pData->maBitmapEx); + } + + return aResult; +} + +// ----------------------------------------------------------------------- + +void ImageList::InsertFromHorizontalStrip( const BitmapEx &rBitmapEx, + const std::vector< rtl::OUString > &rNameVector ) +{ + sal_uInt16 nItems = sal::static_int_cast< sal_uInt16 >( rNameVector.size() ); + + if (!nItems) + return; + + Size aSize( rBitmapEx.GetSizePixel() ); + DBG_ASSERT (rBitmapEx.GetSizePixel().Width() % nItems == 0, + "ImageList::InsertFromHorizontalStrip - very odd size"); + aSize.Width() /= nItems; + ImplInit( nItems, aSize ); + + for (sal_uInt16 nIdx = 0; nIdx < nItems; nIdx++) + { + BitmapEx aBitmap( rBitmapEx, Point( nIdx * aSize.Width(), 0 ), aSize ); + mpImplData->AddImage( rNameVector[ nIdx ], nIdx + 1, aBitmap ); + } +} + +// ----------------------------------------------------------------------- + +void ImageList::InsertFromHorizontalBitmap( const ResId& rResId, + sal_uInt16 nCount, + const Color *pMaskColor, + const Color *pSearchColors, + const Color *pReplaceColors, + sal_uLong nColorCount) +{ + BitmapEx aBmpEx( rResId ); + if (!aBmpEx.IsTransparent()) + { + if( pMaskColor ) + aBmpEx = BitmapEx( aBmpEx.GetBitmap(), *pMaskColor ); + else + aBmpEx = BitmapEx( aBmpEx.GetBitmap() ); + } + if ( nColorCount && pSearchColors && pReplaceColors ) + aBmpEx.Replace( pSearchColors, pReplaceColors, nColorCount ); + + std::vector< rtl::OUString > aNames( nCount ); + InsertFromHorizontalStrip( aBmpEx, aNames ); +} + +// ----------------------------------------------------------------------- + +sal_uInt16 ImageList::ImplGetImageId( const ::rtl::OUString& rImageName ) const +{ + DBG_CHKTHIS( ImageList, NULL ); + + ImageAryData *pImg = mpImplData->maNameHash[ rImageName ]; + if( pImg ) + return pImg->mnId; + else + return 0; +} + +// ----------------------------------------------------------------------- + +void ImageList::AddImage( sal_uInt16 nId, const Image& rImage ) +{ + DBG_CHKTHIS( ImageList, NULL ); + DBG_CHKOBJ( &rImage, Image, NULL ); + DBG_ASSERT( nId, "ImageList::AddImage(): ImageId == 0" ); + DBG_ASSERT( GetImagePos( nId ) == IMAGELIST_IMAGE_NOTFOUND, "ImageList::AddImage() - ImageId already exists" ); + DBG_ASSERT( rImage.mpImplData, "ImageList::AddImage(): Wrong Size" ); + DBG_ASSERT( !mpImplData || (rImage.GetSizePixel() == mpImplData->maImageSize), "ImageList::AddImage(): Wrong Size" ); + + if( !mpImplData ) + ImplInit( 0, rImage.GetSizePixel() ); + + mpImplData->AddImage( rtl::OUString(), nId, rImage.GetBitmapEx()); +} + +// ----------------------------------------------------------------------- + +void ImageList::AddImage( const ::rtl::OUString& rImageName, const Image& rImage ) +{ + DBG_ASSERT( GetImagePos( rImageName ) == IMAGELIST_IMAGE_NOTFOUND, "ImageList::AddImage() - ImageName already exists" ); + + if( !mpImplData ) + ImplInit( 0, rImage.GetSizePixel() ); + + mpImplData->AddImage( rImageName, GetImageCount() + 1, + rImage.GetBitmapEx() ); +} + +// ----------------------------------------------------------------------- + +void ImageList::ReplaceImage( sal_uInt16 nId, const Image& rImage ) +{ + DBG_CHKTHIS( ImageList, NULL ); + DBG_CHKOBJ( &rImage, Image, NULL ); + DBG_ASSERT( GetImagePos( nId ) != IMAGELIST_IMAGE_NOTFOUND, "ImageList::ReplaceImage(): Unknown nId" ); + + RemoveImage( nId ); + AddImage( nId, rImage ); +} + +// ----------------------------------------------------------------------- + +void ImageList::ReplaceImage( const ::rtl::OUString& rImageName, const Image& rImage ) +{ + const sal_uInt16 nId = ImplGetImageId( rImageName ); + + if( nId ) + { + RemoveImage( nId ); + + if( !mpImplData ) + ImplInit( 0, rImage.GetSizePixel() ); + mpImplData->AddImage( rImageName, nId, rImage.GetBitmapEx()); + } +} + +// ----------------------------------------------------------------------- + +void ImageList::ReplaceImage( sal_uInt16 nId, sal_uInt16 nReplaceId ) +{ + DBG_CHKTHIS( ImageList, NULL ); + DBG_ASSERT( GetImagePos( nId ) != IMAGELIST_IMAGE_NOTFOUND, "ImageList::ReplaceImage(): Unknown nId" ); + DBG_ASSERT( GetImagePos( nReplaceId ) != IMAGELIST_IMAGE_NOTFOUND, "ImageList::ReplaceImage(): Unknown nReplaceId" ); + + sal_uLong nPosDest = GetImagePos( nId ); + sal_uLong nPosSrc = GetImagePos( nReplaceId ); + if( nPosDest != IMAGELIST_IMAGE_NOTFOUND && + nPosSrc != IMAGELIST_IMAGE_NOTFOUND ) + { + ImplMakeUnique(); + mpImplData->maImages[nPosDest] = mpImplData->maImages[nPosSrc]; + } +} + +// ----------------------------------------------------------------------- + +void ImageList::ReplaceImage( const ::rtl::OUString& rImageName, const ::rtl::OUString& rReplaceName ) +{ + const sal_uInt16 nId1 = ImplGetImageId( rImageName ), nId2 = ImplGetImageId( rReplaceName ); + + if( nId1 && nId2 ) + ReplaceImage( nId1, nId2 ); +} + +// ----------------------------------------------------------------------- + +void ImageList::RemoveImage( sal_uInt16 nId ) +{ + DBG_CHKTHIS( ImageList, NULL ); + + for( sal_uInt32 i = 0; i < mpImplData->maImages.size(); ++i ) + { + if( mpImplData->maImages[ i ]->mnId == nId ) + { + mpImplData->RemoveImage( static_cast< sal_uInt16 >( i ) ); + break; + } + } +} + +// ----------------------------------------------------------------------- + +void ImageList::RemoveImage( const ::rtl::OUString& rImageName ) +{ + const sal_uInt16 nId = ImplGetImageId( rImageName ); + + if( nId ) + RemoveImage( nId ); +} + +// ----------------------------------------------------------------------- + +Image ImageList::GetImage( sal_uInt16 nId ) const +{ + DBG_CHKTHIS( ImageList, NULL ); + + Image aRet; + + if( mpImplData ) + { + std::vector<ImageAryData *>::iterator aIter; + for( aIter = mpImplData->maImages.begin(); + aIter != mpImplData->maImages.end(); ++aIter) + { + if ((*aIter)->mnId == nId) + { + if( (*aIter)->IsLoadable() ) + (*aIter)->Load( mpImplData->maPrefix ); + + aRet = Image( (*aIter)->maBitmapEx ); + } + } + } + + if (!aRet) + { + BitmapEx rBitmap; + bool res = ::vcl::ImageRepository::loadDefaultImage(rBitmap); + if (res) + aRet = Image(rBitmap); + } + + return aRet; +} + +// ----------------------------------------------------------------------- + +Image ImageList::GetImage( const ::rtl::OUString& rImageName ) const +{ + if( mpImplData ) + { + ImageAryData *pImg = mpImplData->maNameHash[ rImageName ]; + + if( pImg ) + { + if( pImg->IsLoadable() ) + pImg->Load( mpImplData->maPrefix ); + return Image( pImg->maBitmapEx ); + } + } + + return Image(); +} + +// ----------------------------------------------------------------------- + +void ImageList::Clear() +{ + DBG_CHKTHIS( ImageList, NULL ); + + if( mpImplData && ( 0 == --mpImplData->mnRefCount ) ) + delete mpImplData; + + mpImplData = NULL; +} + +// ----------------------------------------------------------------------- + +sal_uInt16 ImageList::GetImageCount() const +{ + DBG_CHKTHIS( ImageList, NULL ); + + return mpImplData ? static_cast< sal_uInt16 >( mpImplData->maImages.size() ) : 0; +} + +// ----------------------------------------------------------------------- + +sal_uInt16 ImageList::GetImagePos( sal_uInt16 nId ) const +{ + DBG_CHKTHIS( ImageList, NULL ); + + if( mpImplData && nId ) + { + for( sal_uInt32 i = 0; i < mpImplData->maImages.size(); ++i ) + { + if (mpImplData->maImages[ i ]->mnId == nId) + return static_cast< sal_uInt16 >( i ); + } + } + + return IMAGELIST_IMAGE_NOTFOUND; +} + +bool ImageList::HasImageAtPos( sal_uInt16 nId ) const +{ + return GetImagePos( nId ) != IMAGELIST_IMAGE_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +sal_uInt16 ImageList::GetImagePos( const ::rtl::OUString& rImageName ) const +{ + DBG_CHKTHIS( ImageList, NULL ); + + if( mpImplData && rImageName.getLength() ) + { + for( sal_uInt32 i = 0; i < mpImplData->maImages.size(); i++ ) + { + if (mpImplData->maImages[i]->maName == rImageName) + return static_cast< sal_uInt16 >( i ); + } + } + + return IMAGELIST_IMAGE_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +sal_uInt16 ImageList::GetImageId( sal_uInt16 nPos ) const +{ + DBG_CHKTHIS( ImageList, NULL ); + + if( mpImplData && (nPos < GetImageCount()) ) + return mpImplData->maImages[ nPos ]->mnId; + + return 0; +} + +// ----------------------------------------------------------------------- + +void ImageList::GetImageIds( ::std::vector< sal_uInt16 >& rIds ) const +{ + RTL_LOGFILE_CONTEXT( aLog, "vcl: ImageList::GetImageIds" ); + + DBG_CHKTHIS( ImageList, NULL ); + + rIds = ::std::vector< sal_uInt16 >(); + + if( mpImplData ) + { + for( sal_uInt32 i = 0; i < mpImplData->maImages.size(); i++ ) + rIds.push_back( mpImplData->maImages[i]->mnId ); + } +} + +// ----------------------------------------------------------------------- + +::rtl::OUString ImageList::GetImageName( sal_uInt16 nPos ) const +{ + DBG_CHKTHIS( ImageList, NULL ); + + if( mpImplData && (nPos < GetImageCount()) ) + return mpImplData->maImages[ nPos ]->maName; + + return ::rtl::OUString(); +} + +// ----------------------------------------------------------------------- + +void ImageList::GetImageNames( ::std::vector< ::rtl::OUString >& rNames ) const +{ + RTL_LOGFILE_CONTEXT( aLog, "vcl: ImageList::GetImageNames" ); + + DBG_CHKTHIS( ImageList, NULL ); + + rNames = ::std::vector< ::rtl::OUString >(); + + if( mpImplData ) + { + for( sal_uInt32 i = 0; i < mpImplData->maImages.size(); i++ ) + { + const rtl::OUString& rName( mpImplData->maImages[ i ]->maName ); + if( rName.getLength() != 0 ) + rNames.push_back( rName ); + } + } +} + +// ----------------------------------------------------------------------- + +Size ImageList::GetImageSize() const +{ + DBG_CHKTHIS( ImageList, NULL ); + + Size aRet; + + if( mpImplData ) + { + aRet = mpImplData->maImageSize; + + // force load of 1st image to see - uncommon case. + if( aRet.Width() == 0 && aRet.Height() == 0 && + !mpImplData->maImages.empty() ) + { + Image aTmp = GetImage( mpImplData->maImages[ 0 ]->mnId ); + aRet = mpImplData->maImageSize = aTmp.GetSizePixel(); + } + } + return aRet; +} + +// ----------------------------------------------------------------------- + +ImageList& ImageList::operator=( const ImageList& rImageList ) +{ + DBG_CHKTHIS( ImageList, NULL ); + DBG_CHKOBJ( &rImageList, ImageList, NULL ); + + if( rImageList.mpImplData ) + ++rImageList.mpImplData->mnRefCount; + + if( mpImplData && ( 0 == --mpImplData->mnRefCount ) ) + delete mpImplData; + + mpImplData = rImageList.mpImplData; + + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool ImageList::operator==( const ImageList& rImageList ) const +{ + DBG_CHKTHIS( ImageList, NULL ); + DBG_CHKOBJ( &rImageList, ImageList, NULL ); + + bool bRet = false; + + if( rImageList.mpImplData == mpImplData ) + bRet = true; + else if( !rImageList.mpImplData || !mpImplData ) + bRet = false; + else if( rImageList.GetImageCount() == GetImageCount() && + rImageList.mpImplData->maImageSize == mpImplData->maImageSize ) + bRet = true; // strange semantic + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/imagerepository.cxx b/vcl/source/gdi/imagerepository.cxx new file mode 100644 index 000000000000..ea5b5f5d87a4 --- /dev/null +++ b/vcl/source/gdi/imagerepository.cxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <vcl/bitmapex.hxx> +#include <vcl/imagerepository.hxx> +#include <vcl/svapp.hxx> +#include "vcl/impimagetree.hxx" + +//........................................................................ +namespace vcl +{ +//........................................................................ + + //==================================================================== + //= ImageRepository + //==================================================================== + //-------------------------------------------------------------------- + bool ImageRepository::loadImage( const ::rtl::OUString& _rName, BitmapEx& _out_rImage, bool _bSearchLanguageDependent, bool loadMissing ) + { + ::rtl::OUString sCurrentSymbolsStyle = Application::GetSettings().GetStyleSettings().GetCurrentSymbolsStyleName(); + + ImplImageTreeSingletonRef aImplImageTree; + return aImplImageTree->loadImage( _rName, sCurrentSymbolsStyle, _out_rImage, _bSearchLanguageDependent, loadMissing ); + } + + bool ImageRepository::loadDefaultImage( BitmapEx& _out_rImage) + { + ::rtl::OUString sCurrentSymbolsStyle = Application::GetSettings().GetStyleSettings().GetCurrentSymbolsStyleName(); + ImplImageTreeSingletonRef aImplImageTree; + return aImplImageTree->loadDefaultImage( sCurrentSymbolsStyle,_out_rImage); + } + +//........................................................................ +} // namespace vcl +//........................................................................ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/impanmvw.cxx b/vcl/source/gdi/impanmvw.cxx new file mode 100644 index 000000000000..59ee15fa558a --- /dev/null +++ b/vcl/source/gdi/impanmvw.cxx @@ -0,0 +1,358 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "impanmvw.hxx" +#include <vcl/virdev.hxx> +#include <vcl/window.hxx> +#include <vcl/salbtype.hxx> + +// ---------------- +// - ImplAnimView - +// ---------------- + +ImplAnimView::ImplAnimView( Animation* pParent, OutputDevice* pOut, + const Point& rPt, const Size& rSz, + sal_uLong nExtraData, + OutputDevice* pFirstFrameOutDev ) : + mpParent ( pParent ), + mpOut ( pFirstFrameOutDev ? pFirstFrameOutDev : pOut ), + mnExtraData ( nExtraData ), + maPt ( rPt ), + maSz ( rSz ), + maSzPix ( mpOut->LogicToPixel( maSz ) ), + maClip ( mpOut->GetClipRegion() ), + mpBackground ( new VirtualDevice ), + mpRestore ( new VirtualDevice ), + meLastDisposal ( DISPOSE_BACK ), + mbPause ( sal_False ), + mbMarked ( sal_False ), + mbHMirr ( maSz.Width() < 0L ), + mbVMirr ( maSz.Height() < 0L ) +{ + mpParent->ImplIncAnimCount(); + + // mirrored horizontically? + if( mbHMirr ) + { + maDispPt.X() = maPt.X() + maSz.Width() + 1L; + maDispSz.Width() = -maSz.Width(); + maSzPix.Width() = -maSzPix.Width(); + } + else + { + maDispPt.X() = maPt.X(); + maDispSz.Width() = maSz.Width(); + } + + // mirrored vertically? + if( mbVMirr ) + { + maDispPt.Y() = maPt.Y() + maSz.Height() + 1L; + maDispSz.Height() = -maSz.Height(); + maSzPix.Height() = -maSzPix.Height(); + } + else + { + maDispPt.Y() = maPt.Y(); + maDispSz.Height() = maSz.Height(); + } + + // save background + mpBackground->SetOutputSizePixel( maSzPix ); + + if( mpOut->GetOutDevType() == OUTDEV_WINDOW ) + { + MapMode aTempMap( mpOut->GetMapMode() ); + aTempMap.SetOrigin( Point() ); + mpBackground->SetMapMode( aTempMap ); + ( (Window*) mpOut )->SaveBackground( maDispPt, maDispSz, Point(), *mpBackground ); + mpBackground->SetMapMode( MapMode() ); + } + else + mpBackground->DrawOutDev( Point(), maSzPix, maDispPt, maDispSz, *mpOut ); + + // initial drawing to actual position + ImplDrawToPos( mpParent->ImplGetCurPos() ); + + // if first frame OutputDevice is set, update variables now for real OutputDevice + if( pFirstFrameOutDev ) + maClip = ( mpOut = pOut )->GetClipRegion(); +} + +// ------------------------------------------------------------------------ + +ImplAnimView::~ImplAnimView() +{ + delete mpBackground; + delete mpRestore; + + mpParent->ImplDecAnimCount(); +} + +// ------------------------------------------------------------------------ + +sal_Bool ImplAnimView::ImplMatches( OutputDevice* pOut, long nExtraData ) const +{ + sal_Bool bRet = sal_False; + + if( nExtraData ) + { + if( ( mnExtraData == nExtraData ) && ( !pOut || ( pOut == mpOut ) ) ) + bRet = sal_True; + } + else if( !pOut || ( pOut == mpOut ) ) + bRet = sal_True; + + return bRet; +} + +// ------------------------------------------------------------------------ + +void ImplAnimView::ImplGetPosSize( const AnimationBitmap& rAnm, Point& rPosPix, Size& rSizePix ) +{ + const Size& rAnmSize = mpParent->GetDisplaySizePixel(); + Point aPt2( rAnm.aPosPix.X() + rAnm.aSizePix.Width() - 1L, + rAnm.aPosPix.Y() + rAnm.aSizePix.Height() - 1L ); + double fFactX, fFactY; + + // calculate x scaling + if( rAnmSize.Width() > 1L ) + fFactX = (double) ( maSzPix.Width() - 1L ) / ( rAnmSize.Width() - 1L ); + else + fFactX = 1.0; + + // calculate y scaling + if( rAnmSize.Height() > 1L ) + fFactY = (double) ( maSzPix.Height() - 1L ) / ( rAnmSize.Height() - 1L ); + else + fFactY = 1.0; + + rPosPix.X() = FRound( rAnm.aPosPix.X() * fFactX ); + rPosPix.Y() = FRound( rAnm.aPosPix.Y() * fFactY ); + + aPt2.X() = FRound( aPt2.X() * fFactX ); + aPt2.Y() = FRound( aPt2.Y() * fFactY ); + + rSizePix.Width() = aPt2.X() - rPosPix.X() + 1L; + rSizePix.Height() = aPt2.Y() - rPosPix.Y() + 1L; + + // mirrored horizontically? + if( mbHMirr ) + rPosPix.X() = maSzPix.Width() - 1L - aPt2.X(); + + // mirrored vertically? + if( mbVMirr ) + rPosPix.Y() = maSzPix.Height() - 1L - aPt2.Y(); +} + +// ------------------------------------------------------------------------ + +void ImplAnimView::ImplDrawToPos( sal_uLong nPos ) +{ + VirtualDevice aVDev; + Region* pOldClip = !maClip.IsNull() ? new Region( mpOut->GetClipRegion() ) : NULL; + + aVDev.SetOutputSizePixel( maSzPix, sal_False ); + nPos = Min( nPos, (sal_uLong) mpParent->Count() - 1UL ); + + for( sal_uLong i = 0UL; i <= nPos; i++ ) + ImplDraw( i, &aVDev ); + + if( pOldClip ) + mpOut->SetClipRegion( maClip ); + + mpOut->DrawOutDev( maDispPt, maDispSz, Point(), maSzPix, aVDev ); + + if( pOldClip ) + { + mpOut->SetClipRegion( *pOldClip ); + delete pOldClip; + } +} + +// ------------------------------------------------------------------------ + +void ImplAnimView::ImplDraw( sal_uLong nPos ) +{ + ImplDraw( nPos, NULL ); +} + +// ------------------------------------------------------------------------ + +void ImplAnimView::ImplDraw( sal_uLong nPos, VirtualDevice* pVDev ) +{ + Rectangle aOutRect( mpOut->PixelToLogic( Point() ), mpOut->GetOutputSize() ); + + // check, if output lies out of display + if( aOutRect.Intersection( Rectangle( maDispPt, maDispSz ) ).IsEmpty() ) + ImplSetMarked( sal_True ); + else if( !mbPause ) + { + VirtualDevice* pDev; + Point aPosPix; + Point aBmpPosPix; + Size aSizePix; + Size aBmpSizePix; + const sal_uLong nLastPos = mpParent->Count() - 1; + const AnimationBitmap& rAnm = mpParent->Get( (sal_uInt16) ( mnActPos = Min( nPos, nLastPos ) ) ); + + ImplGetPosSize( rAnm, aPosPix, aSizePix ); + + // mirrored horizontically? + if( mbHMirr ) + { + aBmpPosPix.X() = aPosPix.X() + aSizePix.Width() - 1L; + aBmpSizePix.Width() = -aSizePix.Width(); + } + else + { + aBmpPosPix.X() = aPosPix.X(); + aBmpSizePix.Width() = aSizePix.Width(); + } + + // mirrored vertically? + if( mbVMirr ) + { + aBmpPosPix.Y() = aPosPix.Y() + aSizePix.Height() - 1L; + aBmpSizePix.Height() = -aSizePix.Height(); + } + else + { + aBmpPosPix.Y() = aPosPix.Y(); + aBmpSizePix.Height() = aSizePix.Height(); + } + + // get output device + if( !pVDev ) + { + pDev = new VirtualDevice; + pDev->SetOutputSizePixel( maSzPix, sal_False ); + pDev->DrawOutDev( Point(), maSzPix, maDispPt, maDispSz, *mpOut ); + } + else + pDev = pVDev; + + // restore background after each run + if( !nPos ) + { + meLastDisposal = DISPOSE_BACK; + maRestPt = Point(); + maRestSz = maSzPix; + } + + // restore + if( ( DISPOSE_NOT != meLastDisposal ) && maRestSz.Width() && maRestSz.Height() ) + { + if( DISPOSE_BACK == meLastDisposal ) + pDev->DrawOutDev( maRestPt, maRestSz, maRestPt, maRestSz, *mpBackground ); + else + pDev->DrawOutDev( maRestPt, maRestSz, Point(), maRestSz, *mpRestore ); + } + + meLastDisposal = rAnm.eDisposal; + maRestPt = aPosPix; + maRestSz = aSizePix; + + // Was muessen wir beim naechsten Mal restaurieren ? + // ==> ggf. in eine Bitmap stecken, ansonsten SaveBitmap + // aus Speichergruenden loeschen + if( ( meLastDisposal == DISPOSE_BACK ) || ( meLastDisposal == DISPOSE_NOT ) ) + mpRestore->SetOutputSizePixel( Size( 1, 1 ), sal_False ); + else + { + mpRestore->SetOutputSizePixel( maRestSz, sal_False ); + mpRestore->DrawOutDev( Point(), maRestSz, aPosPix, aSizePix, *pDev ); + } + + pDev->DrawBitmapEx( aBmpPosPix, aBmpSizePix, rAnm.aBmpEx ); + + if( !pVDev ) + { + Region* pOldClip = !maClip.IsNull() ? new Region( mpOut->GetClipRegion() ) : NULL; + + if( pOldClip ) + mpOut->SetClipRegion( maClip ); + + mpOut->DrawOutDev( maDispPt, maDispSz, Point(), maSzPix, *pDev ); + + if( pOldClip ) + { + mpOut->SetClipRegion( *pOldClip ); + delete pOldClip; + } + + delete pDev; + + if( mpOut->GetOutDevType() == OUTDEV_WINDOW ) + ( (Window*) mpOut )->Sync(); + } + } +} + +// ------------------------------------------------------------------------ + +void ImplAnimView::ImplRepaint() +{ + const sal_Bool bOldPause = mbPause; + + if( mpOut->GetOutDevType() == OUTDEV_WINDOW ) + { + MapMode aTempMap( mpOut->GetMapMode() ); + aTempMap.SetOrigin( Point() ); + mpBackground->SetMapMode( aTempMap ); + ( (Window*) mpOut )->SaveBackground( maDispPt, maDispSz, Point(), *mpBackground ); + mpBackground->SetMapMode( MapMode() ); + } + else + mpBackground->DrawOutDev( Point(), maSzPix, maDispPt, maDispSz, *mpOut ); + + mbPause = sal_False; + ImplDrawToPos( mnActPos ); + mbPause = bOldPause; +} + +// ------------------------------------------------------------------------ + +AInfo* ImplAnimView::ImplCreateAInfo() const +{ + AInfo* pAInfo = new AInfo; + + pAInfo->aStartOrg = maPt; + pAInfo->aStartSize = maSz; + pAInfo->pOutDev = mpOut; + pAInfo->pViewData = (void*) this; + pAInfo->nExtraData = mnExtraData; + pAInfo->bPause = mbPause; + + return pAInfo; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/impanmvw.hxx b/vcl/source/gdi/impanmvw.hxx new file mode 100644 index 000000000000..4e96ecbd9b64 --- /dev/null +++ b/vcl/source/gdi/impanmvw.hxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_IMPANMVW_HXX +#define _SV_IMPANMVW_HXX + +#include <vcl/animate.hxx> + +// ---------------- +// - ImplAnimView - +// ---------------- + +class Animation; +class OutputDevice; +class VirtualDevice; +struct AnimationBitmap; + +class ImplAnimView +{ +private: + + Animation* mpParent; + OutputDevice* mpOut; + long mnExtraData; + Point maPt; + Point maDispPt; + Point maRestPt; + Size maSz; + Size maSzPix; + Size maDispSz; + Size maRestSz; + MapMode maMap; + Region maClip; + VirtualDevice* mpBackground; + VirtualDevice* mpRestore; + sal_uLong mnActPos; + Disposal meLastDisposal; + sal_Bool mbPause; + sal_Bool mbFirst; + sal_Bool mbMarked; + sal_Bool mbHMirr; + sal_Bool mbVMirr; + + void ImplGetPosSize( const AnimationBitmap& rAnm, Point& rPosPix, Size& rSizePix ); + void ImplDraw( sal_uLong nPos, VirtualDevice* pVDev ); + +public: + + ImplAnimView( Animation* pParent, OutputDevice* pOut, + const Point& rPt, const Size& rSz, sal_uLong nExtraData, + OutputDevice* pFirstFrameOutDev = NULL ); + ~ImplAnimView(); + + sal_Bool ImplMatches( OutputDevice* pOut, long nExtraData ) const; + void ImplDrawToPos( sal_uLong nPos ); + void ImplDraw( sal_uLong nPos ); + void ImplRepaint(); + AInfo* ImplCreateAInfo() const; + + const Point& ImplGetOutPos() const { return maPt; } + + const Size& ImplGetOutSize() const { return maSz; } + const Size& ImplGetOutSizePix() const { return maSzPix; } + + void ImplPause( sal_Bool bPause ) { mbPause = bPause; } + sal_Bool ImplIsPause() const { return mbPause; } + + void ImplSetMarked( sal_Bool bMarked ) { mbMarked = bMarked; } + sal_Bool ImplIsMarked() const { return mbMarked; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/impbmp.cxx b/vcl/source/gdi/impbmp.cxx new file mode 100644 index 000000000000..6957e32b3e25 --- /dev/null +++ b/vcl/source/gdi/impbmp.cxx @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <svsys.h> +#include <vcl/salbmp.hxx> +#include <tools/debug.hxx> +#include <vcl/impbmp.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/svdata.hxx> +#include <vcl/salinst.hxx> + +// -------------- +// - ImpBitmap - +// -------------- + +ImpBitmap::ImpBitmap() : + mnRefCount ( 1UL ), + mnChecksum ( 0UL ), + mpSalBitmap ( ImplGetSVData()->mpDefInst->CreateSalBitmap() ), + maSourceSize( 0, 0 ) +{ +} + +// ----------------------------------------------------------------------- + +ImpBitmap::~ImpBitmap() +{ + delete mpSalBitmap; +} + +// ----------------------------------------------------------------------- +void ImpBitmap::ImplSetSalBitmap( SalBitmap* pBitmap ) +{ + delete mpSalBitmap, mpSalBitmap = pBitmap; +} + +// ----------------------------------------------------------------------- + +sal_Bool ImpBitmap::ImplCreate( const Size& rSize, sal_uInt16 nBitCount, const BitmapPalette& rPal ) +{ + maSourceSize = rSize; + return mpSalBitmap->Create( rSize, nBitCount, rPal ); +} + +// ----------------------------------------------------------------------- + +sal_Bool ImpBitmap::ImplCreate( const ImpBitmap& rImpBitmap ) +{ + maSourceSize = rImpBitmap.maSourceSize; + mnChecksum = rImpBitmap.mnChecksum; + return mpSalBitmap->Create( *rImpBitmap.mpSalBitmap ); +} + +// ----------------------------------------------------------------------- + +sal_Bool ImpBitmap::ImplCreate( const ImpBitmap& rImpBitmap, SalGraphics* pGraphics ) +{ + return mpSalBitmap->Create( *rImpBitmap.mpSalBitmap, pGraphics ); +} + +// ----------------------------------------------------------------------- + +sal_Bool ImpBitmap::ImplCreate( const ImpBitmap& rImpBitmap, sal_uInt16 nNewBitCount ) +{ + return mpSalBitmap->Create( *rImpBitmap.mpSalBitmap, nNewBitCount ); +} + +// ----------------------------------------------------------------------- + +void ImpBitmap::ImplDestroy() +{ + mpSalBitmap->Destroy(); +} + +// ----------------------------------------------------------------------- + +Size ImpBitmap::ImplGetSize() const +{ + return mpSalBitmap->GetSize(); +} + +// ----------------------------------------------------------------------- + +sal_uInt16 ImpBitmap::ImplGetBitCount() const +{ + sal_uInt16 nBitCount = mpSalBitmap->GetBitCount(); + return( ( nBitCount <= 1 ) ? 1 : ( nBitCount <= 4 ) ? 4 : ( nBitCount <= 8 ) ? 8 : 24 ); +} + +// ----------------------------------------------------------------------- + +BitmapBuffer* ImpBitmap::ImplAcquireBuffer( sal_Bool bReadOnly ) +{ + return mpSalBitmap->AcquireBuffer( bReadOnly ); +} + +// ----------------------------------------------------------------------- + +void ImpBitmap::ImplReleaseBuffer( BitmapBuffer* pBuffer, sal_Bool bReadOnly ) +{ + mpSalBitmap->ReleaseBuffer( pBuffer, bReadOnly ); + + if( !bReadOnly ) + mnChecksum = 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx new file mode 100644 index 000000000000..bf99364a0f64 --- /dev/null +++ b/vcl/source/gdi/impgraph.cxx @@ -0,0 +1,1678 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/vcompat.hxx> +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> +#include <ucbhelper/content.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/tempfile.hxx> +#include <vcl/outdev.hxx> +#include <vcl/virdev.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <vcl/impgraph.hxx> +#include <vcl/gfxlink.hxx> +#include <vcl/cvtgrf.hxx> +#include <vcl/salbtype.hxx> +#include <vcl/graph.hxx> +#include <vcl/metaact.hxx> +#include <com/sun/star/ucb/CommandAbortedException.hpp> + +// ----------- +// - Defines - +// ----------- + +#define GRAPHIC_MAXPARTLEN 256000L +#define GRAPHIC_MTFTOBMP_MAXEXT 2048 +#define GRAPHIC_STREAMBUFSIZE 8192UL + +#define SYS_WINMETAFILE 0x00000003L +#define SYS_WNTMETAFILE 0x00000004L +#define SYS_OS2METAFILE 0x00000005L +#define SYS_MACMETAFILE 0x00000006L + +#define GRAPHIC_FORMAT_50 static_cast<sal_uInt32>(COMPAT_FORMAT( 'G', 'R', 'F', '5' )) +#define NATIVE_FORMAT_50 static_cast<sal_uInt32>(COMPAT_FORMAT( 'N', 'A', 'T', '5' )) + +// --------------- +// - ImpSwapFile - +// --------------- + +struct ImpSwapFile +{ + INetURLObject aSwapURL; + sal_uLong nRefCount; +}; + +// ----------------- +// - Graphicreader - +// ----------------- + +class ReaderData +{ +public: + Size maPreviewSize; +}; + +GraphicReader::~GraphicReader() +{ + delete mpReaderData; +} + +// ------------------------------------------------------------------------ + +sal_Bool GraphicReader::IsPreviewModeEnabled() const +{ + if( !mpReaderData ) + return sal_False; + if( mpReaderData->maPreviewSize.Width() ) + return sal_True; + if( mpReaderData->maPreviewSize.Height() ) + return sal_True; + return sal_False; +} + +// ------------------------------------------------------------------------ + +void GraphicReader::DisablePreviewMode() +{ + if( mpReaderData ) + mpReaderData->maPreviewSize = Size( 0, 0 ); +} + +// ------------------------------------------------------------------------ + +void GraphicReader::SetPreviewSize( const Size& rSize ) +{ + if( !mpReaderData ) + mpReaderData = new ReaderData; + mpReaderData->maPreviewSize = rSize; +} + +// ------------------------------------------------------------------------ + +Size GraphicReader::GetPreviewSize() const +{ + Size aSize( 0, 0 ); + if( mpReaderData ) + aSize = mpReaderData->maPreviewSize; + return aSize; +} + +// -------------- +// - ImpGraphic - +// -------------- + +ImpGraphic::ImpGraphic() : + mpAnimation ( NULL ), + mpContext ( NULL ), + mpSwapFile ( NULL ), + mpGfxLink ( NULL ), + meType ( GRAPHIC_NONE ), + mnDocFilePos ( 0UL ), + mnSizeBytes ( 0UL ), + mnRefCount ( 1UL ), + mbSwapOut ( sal_False ), + mbSwapUnderway ( sal_False ) +{ +} + +// ------------------------------------------------------------------------ + +ImpGraphic::ImpGraphic( const ImpGraphic& rImpGraphic ) : + maMetaFile ( rImpGraphic.maMetaFile ), + maEx ( rImpGraphic.maEx ), + mpContext ( NULL ), + mpSwapFile ( rImpGraphic.mpSwapFile ), + meType ( rImpGraphic.meType ), + maDocFileURLStr ( rImpGraphic.maDocFileURLStr ), + mnDocFilePos ( rImpGraphic.mnDocFilePos ), + mnSizeBytes ( rImpGraphic.mnSizeBytes ), + mnRefCount ( 1UL ), + mbSwapOut ( rImpGraphic.mbSwapOut ), + mbSwapUnderway ( sal_False ) +{ + if( mpSwapFile ) + mpSwapFile->nRefCount++; + + if( rImpGraphic.mpGfxLink ) + mpGfxLink = new GfxLink( *rImpGraphic.mpGfxLink ); + else + mpGfxLink = NULL; + + if( rImpGraphic.mpAnimation ) + { + mpAnimation = new Animation( *rImpGraphic.mpAnimation ); + maEx = mpAnimation->GetBitmapEx(); + } + else + mpAnimation = NULL; +} + +// ------------------------------------------------------------------------ + +ImpGraphic::ImpGraphic( const Bitmap& rBitmap ) : + maEx ( rBitmap ), + mpAnimation ( NULL ), + mpContext ( NULL ), + mpSwapFile ( NULL ), + mpGfxLink ( NULL ), + meType ( !rBitmap ? GRAPHIC_NONE : GRAPHIC_BITMAP ), + mnDocFilePos ( 0UL ), + mnSizeBytes ( 0UL ), + mnRefCount ( 1UL ), + mbSwapOut ( sal_False ), + mbSwapUnderway ( sal_False ) +{ +} + +// ------------------------------------------------------------------------ + +ImpGraphic::ImpGraphic( const BitmapEx& rBitmapEx ) : + maEx ( rBitmapEx ), + mpAnimation ( NULL ), + mpContext ( NULL ), + mpSwapFile ( NULL ), + mpGfxLink ( NULL ), + meType ( !rBitmapEx ? GRAPHIC_NONE : GRAPHIC_BITMAP ), + mnDocFilePos ( 0UL ), + mnSizeBytes ( 0UL ), + mnRefCount ( 1UL ), + mbSwapOut ( sal_False ), + mbSwapUnderway ( sal_False ) +{ +} + +// ------------------------------------------------------------------------ + +ImpGraphic::ImpGraphic( const Animation& rAnimation ) : + maEx ( rAnimation.GetBitmapEx() ), + mpAnimation ( new Animation( rAnimation ) ), + mpContext ( NULL ), + mpSwapFile ( NULL ), + mpGfxLink ( NULL ), + meType ( GRAPHIC_BITMAP ), + mnDocFilePos ( 0UL ), + mnSizeBytes ( 0UL ), + mnRefCount ( 1UL ), + mbSwapOut ( sal_False ), + mbSwapUnderway ( sal_False ) +{ +} + +// ------------------------------------------------------------------------ + +ImpGraphic::ImpGraphic( const GDIMetaFile& rMtf ) : + maMetaFile ( rMtf ), + mpAnimation ( NULL ), + mpContext ( NULL ), + mpSwapFile ( NULL ), + mpGfxLink ( NULL ), + meType ( GRAPHIC_GDIMETAFILE ), + mnDocFilePos ( 0UL ), + mnSizeBytes ( 0UL ), + mnRefCount ( 1UL ), + mbSwapOut ( sal_False ), + mbSwapUnderway ( sal_False ) +{ +} + +// ------------------------------------------------------------------------ + +ImpGraphic::~ImpGraphic() +{ + ImplClear(); + + if( (sal_uLong) mpContext > 1UL ) + delete mpContext; +} + +// ------------------------------------------------------------------------ + +ImpGraphic& ImpGraphic::operator=( const ImpGraphic& rImpGraphic ) +{ + if( &rImpGraphic != this ) + { + if( !mbSwapUnderway ) + ImplClear(); + + maMetaFile = rImpGraphic.maMetaFile; + meType = rImpGraphic.meType; + mnSizeBytes = rImpGraphic.mnSizeBytes; + + delete mpAnimation; + + if ( rImpGraphic.mpAnimation ) + { + mpAnimation = new Animation( *rImpGraphic.mpAnimation ); + maEx = mpAnimation->GetBitmapEx(); + } + else + { + mpAnimation = NULL; + maEx = rImpGraphic.maEx; + } + + if( !mbSwapUnderway ) + { + maDocFileURLStr = rImpGraphic.maDocFileURLStr; + mnDocFilePos = rImpGraphic.mnDocFilePos; + mbSwapOut = rImpGraphic.mbSwapOut; + mpSwapFile = rImpGraphic.mpSwapFile; + + if( mpSwapFile ) + mpSwapFile->nRefCount++; + } + + delete mpGfxLink; + + if( rImpGraphic.mpGfxLink ) + mpGfxLink = new GfxLink( *rImpGraphic.mpGfxLink ); + else + mpGfxLink = NULL; + } + + return *this; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::operator==( const ImpGraphic& rImpGraphic ) const +{ + sal_Bool bRet = sal_False; + + if( this == &rImpGraphic ) + bRet = sal_True; + else if( !ImplIsSwapOut() && ( rImpGraphic.meType == meType ) ) + { + switch( meType ) + { + case( GRAPHIC_NONE ): + bRet = sal_True; + break; + + case( GRAPHIC_GDIMETAFILE ): + { + if( rImpGraphic.maMetaFile == maMetaFile ) + bRet = sal_True; + } + break; + + case( GRAPHIC_BITMAP ): + { + if( mpAnimation ) + { + if( rImpGraphic.mpAnimation && ( *rImpGraphic.mpAnimation == *mpAnimation ) ) + bRet = sal_True; + } + else if( !rImpGraphic.mpAnimation && ( rImpGraphic.maEx == maEx ) ) + bRet = sal_True; + } + break; + + default: + break; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplClearGraphics( sal_Bool bCreateSwapInfo ) +{ + if( bCreateSwapInfo && !ImplIsSwapOut() ) + { + maSwapInfo.maPrefMapMode = ImplGetPrefMapMode(); + maSwapInfo.maPrefSize = ImplGetPrefSize(); + } + + maEx.Clear(); + maMetaFile.Clear(); + + if( mpAnimation ) + { + mpAnimation->Clear(); + delete mpAnimation; + mpAnimation = NULL; + } + + if( mpGfxLink ) + { + delete mpGfxLink; + mpGfxLink = NULL; + } +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplClear() +{ + if( mpSwapFile ) + { + if( mpSwapFile->nRefCount > 1 ) + mpSwapFile->nRefCount--; + else + { + try + { + ::ucbhelper::Content aCnt( mpSwapFile->aSwapURL.GetMainURL( INetURLObject::NO_DECODE ), + ::com::sun::star::uno::Reference< ::com::sun::star::ucb::XCommandEnvironment >() ); + + aCnt.executeCommand( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("delete")), + ::com::sun::star::uno::makeAny( sal_Bool( sal_True ) ) ); + } + catch( const ::com::sun::star::ucb::ContentCreationException& ) + { + } + catch( const ::com::sun::star::uno::RuntimeException& ) + { + } + catch( const ::com::sun::star::ucb::CommandAbortedException& ) + { + } + catch( const ::com::sun::star::uno::Exception& ) + { + } + + delete mpSwapFile; + } + + mpSwapFile = NULL; + } + + mbSwapOut = sal_False; + mnDocFilePos = 0UL; + maDocFileURLStr.Erase(); + + // cleanup + ImplClearGraphics( sal_False ); + meType = GRAPHIC_NONE; + mnSizeBytes = 0; +} + +// ------------------------------------------------------------------------ + +GraphicType ImpGraphic::ImplGetType() const +{ + return meType; +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplSetDefaultType() +{ + ImplClear(); + meType = GRAPHIC_DEFAULT; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplIsSupportedGraphic() const +{ + return( meType != GRAPHIC_NONE ); +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplIsTransparent() const +{ + sal_Bool bRet; + + if( meType == GRAPHIC_BITMAP ) + bRet = ( mpAnimation ? mpAnimation->IsTransparent() : maEx.IsTransparent() ); + else + bRet = sal_True; + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplIsAlpha() const +{ + sal_Bool bRet; + + if( meType == GRAPHIC_BITMAP ) + bRet = ( NULL == mpAnimation ) && maEx.IsAlpha(); + else + bRet = sal_False; + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplIsAnimated() const +{ + return( mpAnimation != NULL ); +} + +// ------------------------------------------------------------------------ + +Bitmap ImpGraphic::ImplGetBitmap(const GraphicConversionParameters& rParameters) const +{ + Bitmap aRetBmp; + + if( meType == GRAPHIC_BITMAP ) + { + const BitmapEx& rRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maEx ); + const Color aReplaceColor( COL_WHITE ); + + aRetBmp = rRetBmpEx.GetBitmap( &aReplaceColor ); + + if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height()) + aRetBmp.Scale(rParameters.getSizePixel()); + } + else if( ( meType != GRAPHIC_DEFAULT ) && ImplIsSupportedGraphic() ) + { + // use corner points of graphic to determine the pixel + // extent of the graphic (rounding errors are possible else) + VirtualDevice aVDev; + const Point aNullPt; + const Point aTLPix( aVDev.LogicToPixel( aNullPt, maMetaFile.GetPrefMapMode() ) ); + const Point aBRPix( aVDev.LogicToPixel( Point( maMetaFile.GetPrefSize().Width() - 1, maMetaFile.GetPrefSize().Height() - 1 ), maMetaFile.GetPrefMapMode() ) ); + Size aDrawSize( aVDev.LogicToPixel( maMetaFile.GetPrefSize(), maMetaFile.GetPrefMapMode() ) ); + Size aSizePix( labs( aBRPix.X() - aTLPix.X() ) + 1, labs( aBRPix.Y() - aTLPix.Y() ) + 1 ); + + if(rParameters.getSizePixel().Width() && rParameters.getSizePixel().Height()) + { + aDrawSize.Width() = FRound((double)rParameters.getSizePixel().Width() * + (double)aDrawSize.Width() / (double)aSizePix.Width()); + aDrawSize.Height() = FRound((double)rParameters.getSizePixel().Height() * + (double)aDrawSize.Height() / (double)aSizePix.Height()); + + aSizePix = rParameters.getSizePixel(); + } + + if( aSizePix.Width() && aSizePix.Height() && !rParameters.getUnlimitedSize() + && (aSizePix.Width() > GRAPHIC_MTFTOBMP_MAXEXT || aSizePix.Height() > GRAPHIC_MTFTOBMP_MAXEXT)) + { + const Size aOldSizePix( aSizePix ); + double fWH = (double) aSizePix.Width() / aSizePix.Height(); + + if( fWH <= 1.0 ) + aSizePix.Width() = FRound( GRAPHIC_MTFTOBMP_MAXEXT * fWH ), aSizePix.Height() = GRAPHIC_MTFTOBMP_MAXEXT; + else + aSizePix.Width() = GRAPHIC_MTFTOBMP_MAXEXT, aSizePix.Height() = FRound( GRAPHIC_MTFTOBMP_MAXEXT / fWH ); + + aDrawSize.Width() = FRound( ( (double) aDrawSize.Width() * aSizePix.Width() ) / aOldSizePix.Width() ); + aDrawSize.Height() = FRound( ( (double) aDrawSize.Height() * aSizePix.Height() ) / aOldSizePix.Height() ); + } + + if( aVDev.SetOutputSizePixel( aSizePix ) ) + { + if(rParameters.getAntiAliase()) + { + aVDev.SetAntialiasing(aVDev.GetAntialiasing() | ANTIALIASING_ENABLE_B2DDRAW); + } + + if(rParameters.getSnapHorVerLines()) + { + aVDev.SetAntialiasing(aVDev.GetAntialiasing() | ANTIALIASING_PIXELSNAPHAIRLINE); + } + + ImplDraw( &aVDev, aNullPt, aDrawSize ); + aRetBmp = aVDev.GetBitmap( aNullPt, aVDev.GetOutputSizePixel() ); + } + } + + if( !!aRetBmp ) + { + aRetBmp.SetPrefMapMode( ImplGetPrefMapMode() ); + aRetBmp.SetPrefSize( ImplGetPrefSize() ); + } + + return aRetBmp; +} + +// ------------------------------------------------------------------------ + +BitmapEx ImpGraphic::ImplGetBitmapEx(const GraphicConversionParameters& rParameters) const +{ + BitmapEx aRetBmpEx; + + if( meType == GRAPHIC_BITMAP ) + { + aRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maEx ); + + if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height()) + aRetBmpEx.Scale(rParameters.getSizePixel()); + } + else if( ( meType != GRAPHIC_DEFAULT ) && ImplIsSupportedGraphic() ) + { + const ImpGraphic aMonoMask( maMetaFile.GetMonochromeMtf( COL_BLACK ) ); + aRetBmpEx = BitmapEx(ImplGetBitmap(rParameters), aMonoMask.ImplGetBitmap(rParameters)); + } + + return aRetBmpEx; +} + +// ------------------------------------------------------------------------ + +Animation ImpGraphic::ImplGetAnimation() const +{ + Animation aAnimation; + + if( mpAnimation ) + aAnimation = *mpAnimation; + + return aAnimation; +} + +// ------------------------------------------------------------------------ + +const GDIMetaFile& ImpGraphic::ImplGetGDIMetaFile() const +{ + return maMetaFile; +} + +// ------------------------------------------------------------------------ + +Size ImpGraphic::ImplGetPrefSize() const +{ + Size aSize; + + if( ImplIsSwapOut() ) + aSize = maSwapInfo.maPrefSize; + else + { + switch( meType ) + { + case( GRAPHIC_NONE ): + case( GRAPHIC_DEFAULT ): + break; + + case( GRAPHIC_BITMAP ): + { + aSize = maEx.GetPrefSize(); + + if( !aSize.Width() || !aSize.Height() ) + aSize = maEx.GetSizePixel(); + } + break; + + default: + { + if( ImplIsSupportedGraphic() ) + aSize = maMetaFile.GetPrefSize(); + } + break; + } + } + + return aSize; +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplSetPrefSize( const Size& rPrefSize ) +{ + switch( meType ) + { + case( GRAPHIC_NONE ): + case( GRAPHIC_DEFAULT ): + break; + + case( GRAPHIC_BITMAP ): + // #108077# Push through pref size to animation object, + // will be lost on copy otherwise + if( ImplIsAnimated() ) + const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefSize( rPrefSize ); + + maEx.SetPrefSize( rPrefSize ); + break; + + default: + { + if( ImplIsSupportedGraphic() ) + maMetaFile.SetPrefSize( rPrefSize ); + } + break; + } +} + +// ------------------------------------------------------------------------ + +MapMode ImpGraphic::ImplGetPrefMapMode() const +{ + MapMode aMapMode; + + if( ImplIsSwapOut() ) + aMapMode = maSwapInfo.maPrefMapMode; + else + { + switch( meType ) + { + case( GRAPHIC_NONE ): + case( GRAPHIC_DEFAULT ): + break; + + case( GRAPHIC_BITMAP ): + { + const Size aSize( maEx.GetPrefSize() ); + + if ( aSize.Width() && aSize.Height() ) + aMapMode = maEx.GetPrefMapMode(); + } + break; + + default: + { + if( ImplIsSupportedGraphic() ) + return maMetaFile.GetPrefMapMode(); + } + break; + } + } + + return aMapMode; +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplSetPrefMapMode( const MapMode& rPrefMapMode ) +{ + switch( meType ) + { + case( GRAPHIC_NONE ): + case( GRAPHIC_DEFAULT ): + break; + + case( GRAPHIC_BITMAP ): + // #108077# Push through pref mapmode to animation object, + // will be lost on copy otherwise + if( ImplIsAnimated() ) + const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefMapMode( rPrefMapMode ); + + maEx.SetPrefMapMode( rPrefMapMode ); + break; + + default: + { + if( ImplIsSupportedGraphic() ) + maMetaFile.SetPrefMapMode( rPrefMapMode ); + } + break; + } +} + +// ------------------------------------------------------------------------ + +sal_uLong ImpGraphic::ImplGetSizeBytes() const +{ + if( 0 == mnSizeBytes ) + { + if( meType == GRAPHIC_BITMAP ) + { + mnSizeBytes = mpAnimation ? mpAnimation->GetSizeBytes() : maEx.GetSizeBytes(); + } + else if( meType == GRAPHIC_GDIMETAFILE ) + { + mnSizeBytes = maMetaFile.GetSizeBytes(); + } + } + + return( mnSizeBytes ); +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplDraw( OutputDevice* pOutDev, const Point& rDestPt ) const +{ + if( ImplIsSupportedGraphic() && !ImplIsSwapOut() ) + { + switch( meType ) + { + case( GRAPHIC_DEFAULT ): + break; + + case( GRAPHIC_BITMAP ): + { + if ( mpAnimation ) + mpAnimation->Draw( pOutDev, rDestPt ); + else + maEx.Draw( pOutDev, rDestPt ); + } + break; + + default: + ImplDraw( pOutDev, rDestPt, maMetaFile.GetPrefSize() ); + break; + } + } +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplDraw( OutputDevice* pOutDev, + const Point& rDestPt, const Size& rDestSize ) const +{ + if( ImplIsSupportedGraphic() && !ImplIsSwapOut() ) + { + switch( meType ) + { + case( GRAPHIC_DEFAULT ): + break; + + case( GRAPHIC_BITMAP ): + { + if( mpAnimation ) + mpAnimation->Draw( pOutDev, rDestPt, rDestSize ); + else + maEx.Draw( pOutDev, rDestPt, rDestSize ); + } + break; + + default: + { + ( (ImpGraphic*) this )->maMetaFile.WindStart(); + ( (ImpGraphic*) this )->maMetaFile.Play( pOutDev, rDestPt, rDestSize ); + ( (ImpGraphic*) this )->maMetaFile.WindStart(); + } + break; + } + } +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplStartAnimation( OutputDevice* pOutDev, + const Point& rDestPt, + long nExtraData, + OutputDevice* pFirstFrameOutDev ) +{ + if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && mpAnimation ) + mpAnimation->Start( pOutDev, rDestPt, nExtraData, pFirstFrameOutDev ); +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplStartAnimation( OutputDevice* pOutDev, const Point& rDestPt, + const Size& rDestSize, long nExtraData, + OutputDevice* pFirstFrameOutDev ) +{ + if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && mpAnimation ) + mpAnimation->Start( pOutDev, rDestPt, rDestSize, nExtraData, pFirstFrameOutDev ); +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplStopAnimation( OutputDevice* pOutDev, long nExtraData ) +{ + if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && mpAnimation ) + mpAnimation->Stop( pOutDev, nExtraData ); +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplSetAnimationNotifyHdl( const Link& rLink ) +{ + if( mpAnimation ) + mpAnimation->SetNotifyHdl( rLink ); +} + +// ------------------------------------------------------------------------ + +Link ImpGraphic::ImplGetAnimationNotifyHdl() const +{ + Link aLink; + + if( mpAnimation ) + aLink = mpAnimation->GetNotifyHdl(); + + return aLink; +} + +// ------------------------------------------------------------------------ + +sal_uLong ImpGraphic::ImplGetAnimationLoopCount() const +{ + return( mpAnimation ? mpAnimation->GetLoopCount() : 0UL ); +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplResetAnimationLoopCount() +{ + if( mpAnimation ) + mpAnimation->ResetLoopCount(); +} + +// ------------------------------------------------------------------------ + +List* ImpGraphic::ImplGetAnimationInfoList() const +{ + return( mpAnimation ? mpAnimation->GetAInfoList() : NULL ); +} + +// ------------------------------------------------------------------------ + +GraphicReader* ImpGraphic::ImplGetContext() +{ + return mpContext; +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplSetContext( GraphicReader* pReader ) +{ + mpContext = pReader; +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplSetDocFileName( const String& rName, sal_uLong nFilePos ) +{ + const INetURLObject aURL( rName ); + + DBG_ASSERT( !rName.Len() || ( aURL.GetProtocol() != INET_PROT_NOT_VALID ), "Graphic::SetDocFileName(...): invalid URL" ); + + maDocFileURLStr = aURL.GetMainURL( INetURLObject::NO_DECODE ); + mnDocFilePos = nFilePos; +} + +// ------------------------------------------------------------------------ + +const String& ImpGraphic::ImplGetDocFileName() const +{ + return maDocFileURLStr; +} + +// ------------------------------------------------------------------------ + +sal_uLong ImpGraphic::ImplGetDocFilePos() const +{ + return mnDocFilePos; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplReadEmbedded( SvStream& rIStm, sal_Bool bSwap ) +{ + MapMode aMapMode; + Size aSize; + const sal_uLong nStartPos = rIStm.Tell(); + sal_uInt32 nId; + sal_uLong nHeaderLen; + long nType; + long nLen; + const sal_uInt16 nOldFormat = rIStm.GetNumberFormatInt(); + sal_Bool bRet = sal_False; + + if( !mbSwapUnderway ) + { + const String aTempURLStr( maDocFileURLStr ); + const sal_uLong nTempPos = mnDocFilePos; + + ImplClear(); + + maDocFileURLStr = aTempURLStr; + mnDocFilePos = nTempPos; + } + + rIStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + rIStm >> nId; + + // check version + if( GRAPHIC_FORMAT_50 == nId ) + { + // read new style header + VersionCompat* pCompat = new VersionCompat( rIStm, STREAM_READ ); + + rIStm >> nType; + rIStm >> nLen; + rIStm >> aSize; + rIStm >> aMapMode; + + delete pCompat; + } + else + { + // read old style header + long nWidth, nHeight; + long nMapMode, nScaleNumX, nScaleDenomX; + long nScaleNumY, nScaleDenomY, nOffsX, nOffsY; + + rIStm.SeekRel( -4L ); + + rIStm >> nType >> nLen >> nWidth >> nHeight; + rIStm >> nMapMode >> nScaleNumX >> nScaleDenomX >> nScaleNumY; + rIStm >> nScaleDenomY >> nOffsX >> nOffsY; + + // swapped + if( nType > 100L ) + { + nType = SWAPLONG( nType ); + nLen = SWAPLONG( nLen ); + nWidth = SWAPLONG( nWidth ); + nHeight = SWAPLONG( nHeight ); + nMapMode = SWAPLONG( nMapMode ); + nScaleNumX = SWAPLONG( nScaleNumX ); + nScaleDenomX = SWAPLONG( nScaleDenomX ); + nScaleNumY = SWAPLONG( nScaleNumY ); + nScaleDenomY = SWAPLONG( nScaleDenomY ); + nOffsX = SWAPLONG( nOffsX ); + nOffsY = SWAPLONG( nOffsY ); + } + + aSize = Size( nWidth, nHeight ); + aMapMode = MapMode( (MapUnit) nMapMode, Point( nOffsX, nOffsY ), + Fraction( nScaleNumX, nScaleDenomX ), + Fraction( nScaleNumY, nScaleDenomY ) ); + } + + nHeaderLen = rIStm.Tell() - nStartPos; + meType = (GraphicType) nType; + + if( meType ) + { + if( meType == GRAPHIC_BITMAP ) + { + maEx.aBitmapSize = aSize; + + if( aMapMode != MapMode() ) + { + maEx.SetPrefMapMode( aMapMode ); + maEx.SetPrefSize( aSize ); + } + } + else + { + maMetaFile.SetPrefMapMode( aMapMode ); + maMetaFile.SetPrefSize( aSize ); + } + + if( bSwap ) + { + if( maDocFileURLStr.Len() ) + { + rIStm.Seek( nStartPos + nHeaderLen + nLen ); + bRet = mbSwapOut = sal_True; + } + else + { + ::utl::TempFile aTempFile; + const INetURLObject aTmpURL( aTempFile.GetURL() ); + + if( aTmpURL.GetMainURL( INetURLObject::NO_DECODE ).getLength() ) + { + SvStream* pOStm = ::utl::UcbStreamHelper::CreateStream( aTmpURL.GetMainURL( INetURLObject::NO_DECODE ), STREAM_READWRITE | STREAM_SHARE_DENYWRITE ); + + if( pOStm ) + { + sal_uLong nFullLen = nHeaderLen + nLen; + sal_uLong nPartLen = Min( nFullLen, (sal_uLong) GRAPHIC_MAXPARTLEN ); + sal_uInt8* pBuffer = (sal_uInt8*) rtl_allocateMemory( nPartLen ); + + pOStm->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + if( pBuffer ) + { + rIStm.Seek( nStartPos ); + + while( nFullLen ) + { + rIStm.Read( (char*) pBuffer, nPartLen ); + pOStm->Write( (char*) pBuffer, nPartLen ); + + nFullLen -= nPartLen; + + if( nFullLen < GRAPHIC_MAXPARTLEN ) + nPartLen = nFullLen; + } + + rtl_freeMemory( pBuffer ); + sal_uLong nReadErr = rIStm.GetError(), nWriteErr = pOStm->GetError(); + delete pOStm, pOStm = NULL; + + if( !nReadErr && !nWriteErr ) + { + bRet = mbSwapOut = sal_True; + mpSwapFile = new ImpSwapFile; + mpSwapFile->nRefCount = 1; + mpSwapFile->aSwapURL = aTmpURL; + } + else + { + try + { + ::ucbhelper::Content aCnt( aTmpURL.GetMainURL( INetURLObject::NO_DECODE ), + ::com::sun::star::uno::Reference< ::com::sun::star::ucb::XCommandEnvironment >() ); + + aCnt.executeCommand( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("delete")), + ::com::sun::star::uno::makeAny( sal_Bool( sal_True ) ) ); + } + catch( const ::com::sun::star::ucb::ContentCreationException& ) + { + } + catch( const ::com::sun::star::uno::RuntimeException& ) + { + } + catch( const ::com::sun::star::ucb::CommandAbortedException& ) + { + } + catch( const ::com::sun::star::uno::Exception& ) + { + } + } + } + + delete pOStm; + } + } + } + } + else if( meType == GRAPHIC_BITMAP || meType == GRAPHIC_GDIMETAFILE ) + { + rIStm >> *this; + bRet = ( rIStm.GetError() == 0UL ); + } + else if( meType >= SYS_WINMETAFILE && meType <= SYS_MACMETAFILE ) + { + Graphic aSysGraphic; + sal_uLong nCvtType; + + switch( sal::static_int_cast<sal_uLong>(meType) ) + { + case( SYS_WINMETAFILE ): + case( SYS_WNTMETAFILE ): nCvtType = CVT_WMF; break; + case( SYS_OS2METAFILE ): nCvtType = CVT_MET; break; + case( SYS_MACMETAFILE ): nCvtType = CVT_PCT; break; + + default: + nCvtType = CVT_UNKNOWN; + break; + } + + if( nType && GraphicConverter::Import( rIStm, aSysGraphic, nCvtType ) == ERRCODE_NONE ) + { + *this = ImpGraphic( aSysGraphic.GetGDIMetaFile() ); + bRet = ( rIStm.GetError() == 0UL ); + } + else + meType = GRAPHIC_DEFAULT; + } + + if( bRet ) + { + ImplSetPrefMapMode( aMapMode ); + ImplSetPrefSize( aSize ); + } + } + else + bRet = sal_True; + + rIStm.SetNumberFormatInt( nOldFormat ); + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplWriteEmbedded( SvStream& rOStm ) +{ + sal_Bool bRet = sal_False; + + if( ( meType != GRAPHIC_NONE ) && ( meType != GRAPHIC_DEFAULT ) && !ImplIsSwapOut() ) + { + const MapMode aMapMode( ImplGetPrefMapMode() ); + const Size aSize( ImplGetPrefSize() ); + const sal_uInt16 nOldFormat = rOStm.GetNumberFormatInt(); + sal_uLong nDataFieldPos; + + rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + // write correct version ( old style/new style header ) + if( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 ) + { + // write ID for new format (5.0) + rOStm << GRAPHIC_FORMAT_50; + + // write new style header + VersionCompat* pCompat = new VersionCompat( rOStm, STREAM_WRITE, 1 ); + + rOStm << (long) meType; + + // data size is updated later + nDataFieldPos = rOStm.Tell(); + rOStm << (long) 0; + + rOStm << aSize; + rOStm << aMapMode; + + delete pCompat; + } + else + { + // write old style (<=4.0) header + rOStm << (long) meType; + + // data size is updated later + nDataFieldPos = rOStm.Tell(); + rOStm << (long) 0; + + rOStm << (long) aSize.Width(); + rOStm << (long) aSize.Height(); + rOStm << (long) aMapMode.GetMapUnit(); + rOStm << (long) aMapMode.GetScaleX().GetNumerator(); + rOStm << (long) aMapMode.GetScaleX().GetDenominator(); + rOStm << (long) aMapMode.GetScaleY().GetNumerator(); + rOStm << (long) aMapMode.GetScaleY().GetDenominator(); + rOStm << (long) aMapMode.GetOrigin().X(); + rOStm << (long) aMapMode.GetOrigin().Y(); + } + + // write data block + if( !rOStm.GetError() ) + { + const sal_uLong nDataStart = rOStm.Tell(); + + if( ImplIsSupportedGraphic() ) + rOStm << *this; + + if( !rOStm.GetError() ) + { + const sal_uLong nStmPos2 = rOStm.Tell(); + rOStm.Seek( nDataFieldPos ); + rOStm << (long) ( nStmPos2 - nDataStart ); + rOStm.Seek( nStmPos2 ); + bRet = sal_True; + } + } + + rOStm.SetNumberFormatInt( nOldFormat ); + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplSwapOut() +{ + sal_Bool bRet = sal_False; + + if( !ImplIsSwapOut() ) + { + if( !maDocFileURLStr.Len() ) + { + ::utl::TempFile aTempFile; + const INetURLObject aTmpURL( aTempFile.GetURL() ); + + if( aTmpURL.GetMainURL( INetURLObject::NO_DECODE ).getLength() ) + { + SvStream* pOStm = ::utl::UcbStreamHelper::CreateStream( aTmpURL.GetMainURL( INetURLObject::NO_DECODE ), STREAM_READWRITE | STREAM_SHARE_DENYWRITE ); + + if( pOStm ) + { + pOStm->SetVersion( SOFFICE_FILEFORMAT_50 ); + pOStm->SetCompressMode( COMPRESSMODE_NATIVE ); + + if( ( bRet = ImplSwapOut( pOStm ) ) == sal_True ) + { + mpSwapFile = new ImpSwapFile; + mpSwapFile->nRefCount = 1; + mpSwapFile->aSwapURL = aTmpURL; + } + else + { + delete pOStm, pOStm = NULL; + + try + { + ::ucbhelper::Content aCnt( aTmpURL.GetMainURL( INetURLObject::NO_DECODE ), + ::com::sun::star::uno::Reference< ::com::sun::star::ucb::XCommandEnvironment >() ); + + aCnt.executeCommand( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("delete")), + ::com::sun::star::uno::makeAny( sal_Bool( sal_True ) ) ); + } + catch( const ::com::sun::star::ucb::ContentCreationException& ) + { + } + catch( const ::com::sun::star::uno::RuntimeException& ) + { + } + catch( const ::com::sun::star::ucb::CommandAbortedException& ) + { + } + catch( const ::com::sun::star::uno::Exception& ) + { + } + } + + delete pOStm; + } + } + } + else + { + ImplClearGraphics( sal_True ); + bRet = mbSwapOut = sal_True; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplSwapOut( SvStream* pOStm ) +{ + sal_Bool bRet = sal_False; + + if( pOStm ) + { + pOStm->SetBufferSize( GRAPHIC_STREAMBUFSIZE ); + + if( !pOStm->GetError() && ImplWriteEmbedded( *pOStm ) ) + { + pOStm->Flush(); + + if( !pOStm->GetError() ) + { + ImplClearGraphics( sal_True ); + bRet = mbSwapOut = sal_True; + } + } + } + else + { + ImplClearGraphics( sal_True ); + bRet = mbSwapOut = sal_True; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplSwapIn() +{ + sal_Bool bRet = sal_False; + + if( ImplIsSwapOut() ) + { + String aSwapURL; + + if( mpSwapFile ) + aSwapURL = mpSwapFile->aSwapURL.GetMainURL( INetURLObject::NO_DECODE ); + else + aSwapURL = maDocFileURLStr; + + if( aSwapURL.Len() ) + { + SvStream* pIStm = ::utl::UcbStreamHelper::CreateStream( aSwapURL, STREAM_READWRITE | STREAM_SHARE_DENYWRITE ); + + if( pIStm ) + { + pIStm->SetVersion( SOFFICE_FILEFORMAT_50 ); + pIStm->SetCompressMode( COMPRESSMODE_NATIVE ); + + if( !mpSwapFile ) + pIStm->Seek( mnDocFilePos ); + + bRet = ImplSwapIn( pIStm ); + delete pIStm; + + if( mpSwapFile ) + { + if( mpSwapFile->nRefCount > 1 ) + mpSwapFile->nRefCount--; + else + { + try + { + ::ucbhelper::Content aCnt( aSwapURL, + ::com::sun::star::uno::Reference< ::com::sun::star::ucb::XCommandEnvironment >() ); + + aCnt.executeCommand( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("delete")), + ::com::sun::star::uno::makeAny( sal_Bool( sal_True ) ) ); + } + catch( const ::com::sun::star::ucb::ContentCreationException& ) + { + } + catch( const ::com::sun::star::uno::RuntimeException& ) + { + } + catch( const ::com::sun::star::ucb::CommandAbortedException& ) + { + } + catch( const ::com::sun::star::uno::Exception& ) + { + } + + delete mpSwapFile; + } + + mpSwapFile = NULL; + } + } + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplSwapIn( SvStream* pIStm ) +{ + sal_Bool bRet = sal_False; + + if( pIStm ) + { + pIStm->SetBufferSize( GRAPHIC_STREAMBUFSIZE ); + + if( !pIStm->GetError() ) + { + mbSwapUnderway = sal_True; + bRet = ImplReadEmbedded( *pIStm ); + mbSwapUnderway = sal_False; + + if( !bRet ) + ImplClear(); + else + mbSwapOut = sal_False; + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplIsSwapOut() const +{ + return mbSwapOut; +} + +// ------------------------------------------------------------------------ + +void ImpGraphic::ImplSetLink( const GfxLink& rGfxLink ) +{ + delete mpGfxLink; + mpGfxLink = new GfxLink( rGfxLink ); + + if( mpGfxLink->IsNative() ) + mpGfxLink->SwapOut(); +} + +// ------------------------------------------------------------------------ + +GfxLink ImpGraphic::ImplGetLink() +{ + return( mpGfxLink ? *mpGfxLink : GfxLink() ); +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplIsLink() const +{ + return ( mpGfxLink != NULL ) ? sal_True : sal_False; +} + +// ------------------------------------------------------------------------ + +sal_uLong ImpGraphic::ImplGetChecksum() const +{ + sal_uLong nRet = 0; + + if( ImplIsSupportedGraphic() && !ImplIsSwapOut() ) + { + switch( meType ) + { + case( GRAPHIC_DEFAULT ): + break; + + case( GRAPHIC_BITMAP ): + { + if( mpAnimation ) + nRet = mpAnimation->GetChecksum(); + else + nRet = maEx.GetChecksum(); + } + break; + + default: + nRet = maMetaFile.GetChecksum(); + break; + } + } + + return nRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool ImpGraphic::ImplExportNative( SvStream& rOStm ) const +{ + sal_Bool bResult = sal_False; + + if( !rOStm.GetError() ) + { + if( !ImplIsSwapOut() ) + { + if( mpGfxLink && mpGfxLink->IsNative() ) + bResult = mpGfxLink->ExportNative( rOStm ); + else + { + rOStm << *this; + bResult = ( rOStm.GetError() == ERRCODE_NONE ); + } + } + else + rOStm.SetError( SVSTREAM_GENERALERROR ); + } + + return bResult; +} + +// ------------------------------------------------------------------------ + +SvStream& operator>>( SvStream& rIStm, ImpGraphic& rImpGraphic ) +{ + if( !rIStm.GetError() ) + { + const sal_uLong nStmPos1 = rIStm.Tell(); + sal_uInt32 nTmp; + + if ( !rImpGraphic.mbSwapUnderway ) + rImpGraphic.ImplClear(); + + // read Id + rIStm >> nTmp; + + // if there is no more data, avoid further expensive + // reading which will create VDevs and other stuff, just to + // read nothing. CAUTION: Eof is only true AFTER reading another + // byte, a speciality of SvMemoryStream (!) + if(!rIStm.GetError() && !rIStm.IsEof()) + { + if( NATIVE_FORMAT_50 == nTmp ) + { + Graphic aGraphic; + GfxLink aLink; + VersionCompat* pCompat; + + // read compat info + pCompat = new VersionCompat( rIStm, STREAM_READ ); + delete pCompat; + + rIStm >> aLink; + + // set dummy link to avoid creation of additional link after filtering; + // we set a default link to avoid unnecessary swapping of native data + aGraphic.SetLink( GfxLink() ); + + if( !rIStm.GetError() && aLink.LoadNative( aGraphic ) ) + { + // set link only, if no other link was set + const sal_Bool bSetLink = ( rImpGraphic.mpGfxLink == NULL ); + + // assign graphic + rImpGraphic = *aGraphic.ImplGetImpGraphic(); + + if( aLink.IsPrefMapModeValid() ) + rImpGraphic.ImplSetPrefMapMode( aLink.GetPrefMapMode() ); + + if( aLink.IsPrefSizeValid() ) + rImpGraphic.ImplSetPrefSize( aLink.GetPrefSize() ); + + if( bSetLink ) + rImpGraphic.ImplSetLink( aLink ); + } + else + { + rIStm.Seek( nStmPos1 ); + rIStm.SetError( ERRCODE_IO_WRONGFORMAT ); + } + } + else + { + BitmapEx aBmpEx; + const sal_uInt16 nOldFormat = rIStm.GetNumberFormatInt(); + + rIStm.SeekRel( -4 ); + rIStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + rIStm >> aBmpEx; + + if( !rIStm.GetError() ) + { + sal_uInt32 nMagic1(0), nMagic2(0); + sal_uLong nActPos = rIStm.Tell(); + + rIStm >> nMagic1 >> nMagic2; + rIStm.Seek( nActPos ); + + rImpGraphic = ImpGraphic( aBmpEx ); + + if( !rIStm.GetError() && ( 0x5344414e == nMagic1 ) && ( 0x494d4931 == nMagic2 ) ) + { + delete rImpGraphic.mpAnimation; + rImpGraphic.mpAnimation = new Animation; + rIStm >> *rImpGraphic.mpAnimation; + + // #108077# manually set loaded BmpEx to Animation + // (which skips loading its BmpEx if already done) + rImpGraphic.mpAnimation->SetBitmapEx(aBmpEx); + } + else + rIStm.ResetError(); + } + else + { + GDIMetaFile aMtf; + + rIStm.Seek( nStmPos1 ); + rIStm.ResetError(); + rIStm >> aMtf; + + if( !rIStm.GetError() ) + rImpGraphic = aMtf; + else + rIStm.Seek( nStmPos1 ); + } + + rIStm.SetNumberFormatInt( nOldFormat ); + } + } + } + + return rIStm; +} + +// ------------------------------------------------------------------------ + +SvStream& operator<<( SvStream& rOStm, const ImpGraphic& rImpGraphic ) +{ + if( !rOStm.GetError() ) + { + if( !rImpGraphic.ImplIsSwapOut() ) + { + if( ( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 ) && + ( rOStm.GetCompressMode() & COMPRESSMODE_NATIVE ) && + rImpGraphic.mpGfxLink && rImpGraphic.mpGfxLink->IsNative() ) + { + VersionCompat* pCompat; + + // native format + rOStm << NATIVE_FORMAT_50; + + // write compat info + pCompat = new VersionCompat( rOStm, STREAM_WRITE, 1 ); + delete pCompat; + + rImpGraphic.mpGfxLink->SetPrefMapMode( rImpGraphic.ImplGetPrefMapMode() ); + rImpGraphic.mpGfxLink->SetPrefSize( rImpGraphic.ImplGetPrefSize() ); + rOStm << *rImpGraphic.mpGfxLink; + } + else + { + // own format + const sal_uInt16 nOldFormat = rOStm.GetNumberFormatInt(); + rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + switch( rImpGraphic.ImplGetType() ) + { + case( GRAPHIC_NONE ): + case( GRAPHIC_DEFAULT ): + break; + + case GRAPHIC_BITMAP: + { + if ( rImpGraphic.ImplIsAnimated() ) + rOStm << *rImpGraphic.mpAnimation; + else + rOStm << rImpGraphic.maEx; + } + break; + + default: + { + if( rImpGraphic.ImplIsSupportedGraphic() ) + rOStm << rImpGraphic.maMetaFile; + } + break; + } + + rOStm.SetNumberFormatInt( nOldFormat ); + } + } + else + rOStm.SetError( SVSTREAM_GENERALERROR ); + } + + return rOStm; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/impimage.cxx b/vcl/source/gdi/impimage.cxx new file mode 100644 index 000000000000..d2e479e1edb5 --- /dev/null +++ b/vcl/source/gdi/impimage.cxx @@ -0,0 +1,619 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <vcl/outdev.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/alpha.hxx> +#include <vcl/window.hxx> +#include <vcl/bmpacc.hxx> +#include <vcl/virdev.hxx> +#include <vcl/image.h> +#include <vcl/image.hxx> + +// ----------- +// - Defines - +// ----------- + +#define IMPSYSIMAGEITEM_MASK ( 0x01 ) +#define IMPSYSIMAGEITEM_ALPHA ( 0x02 ) +#define DISA_ALL ( 0xffff ) + +// ---------------- +// - ImageAryData - +// ---------------- + +ImageAryData::ImageAryData() : + maName(), + mnId( 0 ), + maBitmapEx() +{ +} + +// ----------------------------------------------------------------------- + +ImageAryData::ImageAryData( const ImageAryData& rData ) : + maName( rData.maName ), + mnId( rData.mnId ), + maBitmapEx( rData.maBitmapEx ) +{ +} + +ImageAryData::ImageAryData( const rtl::OUString &aName, + sal_uInt16 nId, const BitmapEx &aBitmap ) + : maName( aName ), mnId( nId ), maBitmapEx( aBitmap ) +{ +} + +// ----------------------------------------------------------------------- + +ImageAryData::~ImageAryData() +{ +} + +// ----------------------------------------------------------------------- + +ImageAryData& ImageAryData::operator=( const ImageAryData& rData ) +{ + maName = rData.maName; + mnId = rData.mnId; + maBitmapEx = rData.maBitmapEx; + + return *this; +} + +// ----------------- +// - ImplImageList - +// ----------------- + +ImplImageList::ImplImageList() +{ +} + +ImplImageList::ImplImageList( const ImplImageList &aSrc ) : + maPrefix( aSrc.maPrefix ), + maImageSize( aSrc.maImageSize ), + mnRefCount( 1 ) +{ + maImages.reserve( aSrc.maImages.size() ); + for ( ImageAryDataVec::const_iterator aIt = aSrc.maImages.begin(), aEnd = aSrc.maImages.end(); aIt != aEnd; ++aIt ) + { + ImageAryData* pAryData = new ImageAryData( **aIt ); + maImages.push_back( pAryData ); + if( pAryData->maName.getLength() ) + maNameHash [ pAryData->maName ] = pAryData; + } +} + +ImplImageList::~ImplImageList() +{ + for ( ImageAryDataVec::iterator aIt = maImages.begin(), aEnd = maImages.end(); aIt != aEnd; ++aIt ) + delete *aIt; +} + +void ImplImageList::AddImage( const ::rtl::OUString &aName, + sal_uInt16 nId, const BitmapEx &aBitmapEx ) +{ + ImageAryData *pImg = new ImageAryData( aName, nId, aBitmapEx ); + maImages.push_back( pImg ); + if( aName.getLength() ) + maNameHash [ aName ] = pImg; +} + +void ImplImageList::RemoveImage( sal_uInt16 nPos ) +{ + ImageAryData *pImg = maImages[ nPos ]; + if( pImg->maName.getLength() ) + maNameHash.erase( pImg->maName ); + maImages.erase( maImages.begin() + nPos ); +} + +sal_uInt16 ImplImageList::GetImageCount() const +{ + return sal::static_int_cast< sal_uInt16 >( maImages.size() ); +} + +// ----------------- +// - ImplImageData - +// ----------------- + +ImplImageData::ImplImageData( const BitmapEx& rBmpEx ) : + mpImageBitmap( NULL ), + maBmpEx( rBmpEx ) +{ +} + +// ----------------------------------------------------------------------- + +ImplImageData::~ImplImageData() +{ + delete mpImageBitmap; +} + +// ----------------- +// - ImplImageData - +// ----------------- + +sal_Bool ImplImageData::IsEqual( const ImplImageData& rData ) +{ + return( maBmpEx == rData.maBmpEx ); +} + +// ------------- +// - ImplImage - +// ------------- + +ImplImage::ImplImage() +{ +} + +// ------------------------------------------------------------------------------ + +ImplImage::~ImplImage() +{ + switch( meType ) + { + case IMAGETYPE_BITMAP: + delete static_cast< Bitmap* >( mpData ); + break; + + case IMAGETYPE_IMAGE: + delete static_cast< ImplImageData* >( mpData ); + break; + } +} + +// ---------------- +// - ImplImageBmp - +// ---------------- + +ImplImageBmp::ImplImageBmp() : + mpDisplayBmp( NULL ), + mpInfoAry( NULL ), + mnSize( 0 ) +{ +} + +// ------------- +// - ImplImage - +// ------------- + +ImplImageBmp::~ImplImageBmp() +{ + delete[] mpInfoAry; + delete mpDisplayBmp; +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::Create( long nItemWidth, long nItemHeight, sal_uInt16 nInitSize ) +{ + const Size aTotalSize( nInitSize * nItemWidth, nItemHeight ); + + maBmpEx = Bitmap( aTotalSize, 24 ); + maDisabledBmpEx.SetEmpty(); + + delete mpDisplayBmp; + mpDisplayBmp = NULL; + + maSize = Size( nItemWidth, nItemHeight ); + mnSize = nInitSize; + + delete[] mpInfoAry; + mpInfoAry = new sal_uInt8[ mnSize ]; + memset( mpInfoAry, 0, mnSize ); +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::Create( const BitmapEx& rBmpEx, long nItemWidth, long nItemHeight, sal_uInt16 nInitSize ) +{ + maBmpEx = rBmpEx; + maDisabledBmpEx.SetEmpty(); + + delete mpDisplayBmp; + mpDisplayBmp = NULL; + + maSize = Size( nItemWidth, nItemHeight ); + mnSize = nInitSize; + + delete[] mpInfoAry; + mpInfoAry = new sal_uInt8[ mnSize ]; + memset( mpInfoAry, + rBmpEx.IsAlpha() ? IMPSYSIMAGEITEM_ALPHA : ( rBmpEx.IsTransparent() ? IMPSYSIMAGEITEM_MASK : 0 ), + mnSize ); +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::Expand( sal_uInt16 nGrowSize ) +{ + const sal_uLong nDX = nGrowSize * maSize.Width(); + const sal_uInt16 nOldSize = mnSize; + sal_uInt8* pNewAry = new sal_uInt8[ mnSize = sal::static_int_cast<sal_uInt16>(mnSize+nGrowSize) ]; + + maBmpEx.Expand( nDX, 0UL ); + + if( !maDisabledBmpEx.IsEmpty() ) + maDisabledBmpEx.Expand( nDX, 0UL ); + + delete mpDisplayBmp; + mpDisplayBmp = NULL; + + memset( pNewAry, 0, mnSize ); + memcpy( pNewAry, mpInfoAry, nOldSize ); + delete[] mpInfoAry; + mpInfoAry = pNewAry; +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::Invert() +{ + delete mpDisplayBmp; + mpDisplayBmp = NULL; + + maBmpEx.Invert(); +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::Replace( sal_uInt16 nPos, sal_uInt16 nSrcPos ) +{ + const Point aSrcPos( nSrcPos * maSize.Width(), 0L ), aPos( nPos * maSize.Width(), 0L ); + const Rectangle aSrcRect( aSrcPos, maSize ); + const Rectangle aDstRect( aPos, maSize ); + + maBmpEx.CopyPixel( aDstRect, aSrcRect ); + + if( !maDisabledBmpEx.IsEmpty() ) + maDisabledBmpEx.CopyPixel( aDstRect, aSrcRect ); + + delete mpDisplayBmp; + mpDisplayBmp = NULL; + + mpInfoAry[ nPos ] = mpInfoAry[ nSrcPos ]; +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::Replace( sal_uInt16 nPos, const ImplImageBmp& rImageBmp, sal_uInt16 nSrcPos ) +{ + const Point aSrcPos( nSrcPos * maSize.Width(), 0L ), aPos( nPos * maSize.Width(), 0L ); + const Rectangle aSrcRect( aSrcPos, maSize ); + const Rectangle aDstRect( aPos, maSize ); + + maBmpEx.CopyPixel( aDstRect, aSrcRect, &rImageBmp.maBmpEx ); + + ImplUpdateDisabledBmpEx( nPos ); + delete mpDisplayBmp; + mpDisplayBmp = NULL; + + mpInfoAry[ nPos ] = rImageBmp.mpInfoAry[ nSrcPos ]; +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::Replace( sal_uInt16 nPos, const BitmapEx& rBmpEx ) +{ + const Point aNullPos, aPos( nPos * maSize.Width(), 0L ); + const Rectangle aSrcRect( aNullPos, maSize ); + const Rectangle aDstRect( aPos, maSize ); + + maBmpEx.CopyPixel( aDstRect, aSrcRect, &rBmpEx ); + + ImplUpdateDisabledBmpEx( nPos ); + delete mpDisplayBmp; + mpDisplayBmp = NULL; + + mpInfoAry[ nPos ] &= ~( IMPSYSIMAGEITEM_MASK | IMPSYSIMAGEITEM_ALPHA ); + mpInfoAry[ nPos ] |= ( rBmpEx.IsAlpha() ? IMPSYSIMAGEITEM_ALPHA : ( rBmpEx.IsTransparent() ? IMPSYSIMAGEITEM_MASK : 0 ) ); +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::ReplaceColors( const Color* pSrcColors, const Color* pDstColors, sal_uLong nColorCount ) +{ + maBmpEx.Replace( pSrcColors, pDstColors, nColorCount ); + delete mpDisplayBmp; + mpDisplayBmp = NULL; +} + +// ----------------------------------------------------------------------- + +BitmapEx ImplImageBmp::GetBitmapEx( sal_uInt16 nPosCount, sal_uInt16* pPosAry ) const +{ + const Bitmap aNewBmp( Size( nPosCount * maSize.Width(), maSize.Height() ), maBmpEx.GetBitmap().GetBitCount() ); + BitmapEx aRet; + if( maBmpEx.IsAlpha() ) + { + // initialize target bitmap with an empty alpha mask + // which allows for using an optimized copypixel later on (see AlphaMask::CopyPixel) + // that avoids palette lookups + AlphaMask aAlpha( Size( nPosCount * maSize.Width(), maSize.Height() ) ); + aRet = BitmapEx( aNewBmp, aAlpha ); + } + else + aRet = BitmapEx( aNewBmp ); + + for( sal_uInt16 i = 0; i < nPosCount; i++ ) + { + const Point aSrcPos( pPosAry[ i ] * maSize.Width(), 0L ); + const Point aPos( i * maSize.Width(), 0L ); + const Rectangle aSrcRect( aSrcPos, maSize ); + const Rectangle aDstRect( aPos, maSize ); + + aRet.CopyPixel( aDstRect, aSrcRect, &maBmpEx ); + } + + return aRet; +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::Draw( sal_uInt16 nPos, OutputDevice* pOutDev, + const Point& rPos, sal_uInt16 nStyle, + const Size* pSize ) +{ + if( pOutDev->IsDeviceOutputNecessary() ) + { + const Point aSrcPos( nPos * maSize.Width(), 0 ); + Size aOutSize; + + aOutSize = ( pSize ? *pSize : pOutDev->PixelToLogic( maSize ) ); + + if( nStyle & IMAGE_DRAW_DISABLE ) + { + ImplUpdateDisabledBmpEx( nPos); + pOutDev->DrawBitmapEx( rPos, aOutSize, aSrcPos, maSize, maDisabledBmpEx ); + } + else + { + if( nStyle & ( IMAGE_DRAW_COLORTRANSFORM | + IMAGE_DRAW_HIGHLIGHT | IMAGE_DRAW_DEACTIVE | IMAGE_DRAW_SEMITRANSPARENT ) ) + { + BitmapEx aTmpBmpEx; + const Rectangle aCropRect( aSrcPos, maSize ); + + if( mpInfoAry[ nPos ] & ( IMPSYSIMAGEITEM_MASK | IMPSYSIMAGEITEM_ALPHA ) ) + aTmpBmpEx = maBmpEx; + else + aTmpBmpEx = maBmpEx.GetBitmap(); + + aTmpBmpEx.Crop( aCropRect ); + + Bitmap aTmpBmp( aTmpBmpEx.GetBitmap() ); + + if( nStyle & ( IMAGE_DRAW_HIGHLIGHT | IMAGE_DRAW_DEACTIVE ) ) + { + BitmapWriteAccess* pAcc = aTmpBmp.AcquireWriteAccess(); + + if( pAcc ) + { + const StyleSettings& rSettings = pOutDev->GetSettings().GetStyleSettings(); + Color aColor; + BitmapColor aCol; + const long nW = pAcc->Width(); + const long nH = pAcc->Height(); + sal_uInt8* pMapR = new sal_uInt8[ 256 ]; + sal_uInt8* pMapG = new sal_uInt8[ 256 ]; + sal_uInt8* pMapB = new sal_uInt8[ 256 ]; + long nX, nY; + + if( nStyle & IMAGE_DRAW_HIGHLIGHT ) + aColor = rSettings.GetHighlightColor(); + else + aColor = rSettings.GetDeactiveColor(); + + const sal_uInt8 cR = aColor.GetRed(); + const sal_uInt8 cG = aColor.GetGreen(); + const sal_uInt8 cB = aColor.GetBlue(); + + for( nX = 0L; nX < 256L; nX++ ) + { + pMapR[ nX ] = (sal_uInt8) ( ( ( nY = ( nX + cR ) >> 1 ) > 255 ) ? 255 : nY ); + pMapG[ nX ] = (sal_uInt8) ( ( ( nY = ( nX + cG ) >> 1 ) > 255 ) ? 255 : nY ); + pMapB[ nX ] = (sal_uInt8) ( ( ( nY = ( nX + cB ) >> 1 ) > 255 ) ? 255 : nY ); + } + + if( pAcc->HasPalette() ) + { + for( sal_uInt16 i = 0, nCount = pAcc->GetPaletteEntryCount(); i < nCount; i++ ) + { + const BitmapColor& rCol = pAcc->GetPaletteColor( i ); + aCol.SetRed( pMapR[ rCol.GetRed() ] ); + aCol.SetGreen( pMapG[ rCol.GetGreen() ] ); + aCol.SetBlue( pMapB[ rCol.GetBlue() ] ); + pAcc->SetPaletteColor( i, aCol ); + } + } + else if( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR ) + { + for( nY = 0L; nY < nH; nY++ ) + { + Scanline pScan = pAcc->GetScanline( nY ); + + for( nX = 0L; nX < nW; nX++ ) + { + *pScan = pMapB[ *pScan ]; pScan++; + *pScan = pMapG[ *pScan ]; pScan++; + *pScan = pMapR[ *pScan ]; pScan++; + } + } + } + else + { + for( nY = 0L; nY < nH; nY++ ) + { + for( nX = 0L; nX < nW; nX++ ) + { + aCol = pAcc->GetPixel( nY, nX ); + aCol.SetRed( pMapR[ aCol.GetRed() ] ); + aCol.SetGreen( pMapG[ aCol.GetGreen() ] ); + aCol.SetBlue( pMapB[ aCol.GetBlue() ] ); + pAcc->SetPixel( nY, nX, aCol ); + } + } + } + + delete[] pMapR; + delete[] pMapG; + delete[] pMapB; + aTmpBmp.ReleaseAccess( pAcc ); + } + } + + if( nStyle & IMAGE_DRAW_SEMITRANSPARENT ) + { + if( aTmpBmpEx.IsTransparent() ) + { + Bitmap aAlphaBmp( aTmpBmpEx.GetAlpha().GetBitmap() ); + + aAlphaBmp.Adjust( 50 ); + aTmpBmpEx = BitmapEx( aTmpBmp, AlphaMask( aAlphaBmp ) ); + } + else + { + sal_uInt8 cErase = 128; + aTmpBmpEx = BitmapEx( aTmpBmp, AlphaMask( aTmpBmp.GetSizePixel(), &cErase ) ); + } + } + else + { + if( aTmpBmpEx.IsAlpha() ) + aTmpBmpEx = BitmapEx( aTmpBmp, aTmpBmpEx.GetAlpha() ); + else if( aTmpBmpEx.IsAlpha() ) + aTmpBmpEx = BitmapEx( aTmpBmp, aTmpBmpEx.GetMask() ); + } + + pOutDev->DrawBitmapEx( rPos, aOutSize, aTmpBmpEx ); + } + else + { + const BitmapEx* pOutputBmp; + + if( pOutDev->GetOutDevType() == OUTDEV_WINDOW ) + { + ImplUpdateDisplayBmp( pOutDev ); + pOutputBmp = mpDisplayBmp; + } + else + pOutputBmp = &maBmpEx; + + if( pOutputBmp ) + pOutDev->DrawBitmapEx( rPos, aOutSize, aSrcPos, maSize, *pOutputBmp ); + } + } + } +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::ImplUpdateDisplayBmp( OutputDevice* +#if defined WNT +pOutDev +#endif +) +{ + if( !mpDisplayBmp && !maBmpEx.IsEmpty() ) + { +#if defined WNT + if( maBmpEx.IsAlpha() ) + mpDisplayBmp = new BitmapEx( maBmpEx ); + else + { + const Bitmap aBmp( maBmpEx.GetBitmap().CreateDisplayBitmap( pOutDev ) ); + + if( maBmpEx.IsTransparent() ) + mpDisplayBmp = new BitmapEx( aBmp, maBmpEx.GetMask().CreateDisplayBitmap( pOutDev ) ); + else + mpDisplayBmp = new BitmapEx( aBmp ); + } +#else + mpDisplayBmp = new BitmapEx( maBmpEx ); +#endif + } +} + +// ----------------------------------------------------------------------- + +void ImplImageBmp::ImplUpdateDisabledBmpEx( int nPos ) +{ + const Size aTotalSize( maBmpEx.GetSizePixel() ); + + if( maDisabledBmpEx.IsEmpty() ) + { + Bitmap aGrey( aTotalSize, 8, &Bitmap::GetGreyPalette( 256 ) ); + AlphaMask aGreyAlphaMask( aTotalSize ); + + maDisabledBmpEx = BitmapEx( aGrey, aGreyAlphaMask ); + nPos = -1; + } + + Bitmap aBmp( maBmpEx.GetBitmap() ); + BitmapReadAccess* pBmp( aBmp.AcquireReadAccess() ); + AlphaMask aBmpAlphaMask( maBmpEx.GetAlpha() ); + BitmapReadAccess* pBmpAlphaMask( aBmpAlphaMask.AcquireReadAccess() ); + Bitmap aGrey( maDisabledBmpEx.GetBitmap() ); + BitmapWriteAccess* pGrey( aGrey.AcquireWriteAccess() ); + AlphaMask aGreyAlphaMask( maDisabledBmpEx.GetAlpha() ); + BitmapWriteAccess* pGreyAlphaMask( aGreyAlphaMask.AcquireWriteAccess() ); + + if( pBmp && pBmpAlphaMask && pGrey && pGreyAlphaMask ) + { + BitmapColor aGreyVal( 0 ); + BitmapColor aGreyAlphaMaskVal( 0 ); + const Point aPos( ( nPos < 0 ) ? 0 : ( nPos * maSize.Width() ), 0 ); + const int nLeft = aPos.X(), nRight = nLeft + ( ( nPos < 0 ) ? aTotalSize.Width() : maSize.Width() ); + const int nTop = aPos.Y(), nBottom = nTop + maSize.Height(); + + for( int nY = nTop; nY < nBottom; ++nY ) + { + for( int nX = nLeft; nX < nRight; ++nX ) + { + aGreyVal.SetIndex( pBmp->GetLuminance( nY, nX ) ); + pGrey->SetPixel( nY, nX, aGreyVal ); + + const BitmapColor aBmpAlphaMaskVal( pBmpAlphaMask->GetPixel( nY, nX ) ); + + aGreyAlphaMaskVal.SetIndex( static_cast< sal_uInt8 >( ::std::min( aBmpAlphaMaskVal.GetIndex() + 178ul, 255ul ) ) ); + pGreyAlphaMask->SetPixel( nY, nX, aGreyAlphaMaskVal ); + } + } + } + + aBmp.ReleaseAccess( pBmp ); + aBmpAlphaMask.ReleaseAccess( pBmpAlphaMask ); + aGrey.ReleaseAccess( pGrey ); + aGreyAlphaMask.ReleaseAccess( pGreyAlphaMask ); + + maDisabledBmpEx = BitmapEx( aGrey, aGreyAlphaMask ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/impimagetree.cxx b/vcl/source/gdi/impimagetree.cxx new file mode 100644 index 000000000000..88b465c89f7d --- /dev/null +++ b/vcl/source/gdi/impimagetree.cxx @@ -0,0 +1,390 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ***********************************************************************/ + +#include "precompiled_vcl.hxx" +#include "sal/config.h" + +#include <list> +#include <memory> +#include <utility> +#include <vector> + +#include <boost/unordered_map.hpp> + +#include "com/sun/star/container/XNameAccess.hpp" +#include "com/sun/star/io/XInputStream.hpp" +#include "com/sun/star/lang/Locale.hpp" +#include "com/sun/star/uno/Any.hxx" +#include "com/sun/star/uno/Exception.hpp" +#include "com/sun/star/uno/Reference.hxx" +#include "com/sun/star/uno/RuntimeException.hpp" +#include "com/sun/star/uno/Sequence.hxx" +#include "comphelper/processfactory.hxx" +#include "osl/file.hxx" +#include "osl/diagnose.h" +#include "rtl/bootstrap.hxx" +#include "rtl/string.h" +#include "rtl/textenc.h" +#include "rtl/ustrbuf.hxx" +#include "rtl/ustring.h" +#include "rtl/ustring.hxx" +#include "sal/types.h" +#include "tools/stream.hxx" +#include "tools/urlobj.hxx" +#include "vcl/bitmapex.hxx" +#include "vcl/impimagetree.hxx" +#include "vcl/pngread.hxx" +#include "vcl/settings.hxx" +#include "vcl/svapp.hxx" + +namespace { + +namespace css = com::sun::star; + +rtl::OUString createPath( + rtl::OUString const & name, sal_Int32 pos, rtl::OUString const & locale) +{ + rtl::OUStringBuffer b(name.copy(0, pos + 1)); + b.append(locale); + b.append(name.copy(pos)); + return b.makeStringAndClear(); +} + +std::auto_ptr< SvStream > wrapStream( + css::uno::Reference< css::io::XInputStream > const & stream) +{ + // This could use SvInputStream instead if that did not have a broken + // SeekPos implementation for an XInputStream that is not also XSeekable + // (cf. "@@@" at tags/DEV300_m37/svtools/source/misc1/strmadpt.cxx@264807 + // l. 593): + OSL_ASSERT(stream.is()); + std::auto_ptr< SvStream > s(new SvMemoryStream); + for (;;) { + sal_Int32 const size = 2048; + css::uno::Sequence< sal_Int8 > data(size); + sal_Int32 n = stream->readBytes(data, size); + s->Write(data.getConstArray(), n); + if (n < size) { + break; + } + } + s->Seek(0); + return s; +} + +void loadFromStream( + css::uno::Reference< css::io::XInputStream > const & stream, + rtl::OUString const & path, BitmapEx & bitmap) +{ + std::auto_ptr< SvStream > s(wrapStream(stream)); + if (path.endsWithAsciiL(RTL_CONSTASCII_STRINGPARAM(".png"))) + { + vcl::PNGReader aPNGReader( *s ); + aPNGReader.SetIgnoreGammaChunk( sal_True ); + bitmap = aPNGReader.Read(); + } else { + *s >> bitmap; + } +} + +} + +ImplImageTree::ImplImageTree() {} + +ImplImageTree::~ImplImageTree() {} + +bool ImplImageTree::checkStyle(rtl::OUString const & style) +{ + bool exists; + + // using cache because setStyle is an expensive operation + // setStyle calls resetZips => closes any opened zip files with icons, cleans the icon cache, ... + if (checkStyleCacheLookup(style, exists)) { + return exists; + } + + setStyle(style); + + exists = false; + const rtl::OUString sBrandURLSuffix(RTL_CONSTASCII_USTRINGPARAM("_brand.zip")); + for (Zips::iterator i(m_zips.begin()); i != m_zips.end() && !exists;) { + ::rtl::OUString aZipURL = i->first; + sal_Int32 nFromIndex = aZipURL.getLength() - sBrandURLSuffix.getLength(); + // skip brand-specific icon themes; they are incomplete and thus not useful for this check + if (nFromIndex < 0 || !aZipURL.match(sBrandURLSuffix, nFromIndex)) { + osl::File aZip(aZipURL); + if (aZip.open(osl_File_OpenFlag_Read) == ::osl::FileBase::E_None) { + aZip.close(); + exists = true; + } + } + ++i; + } + m_checkStyleCache[style] = exists; + return exists; +} + +bool ImplImageTree::loadImage( + rtl::OUString const & name, rtl::OUString const & style, BitmapEx & bitmap, + bool localized, bool loadMissing ) +{ + bool found = false; + try { + found = doLoadImage(name, style, bitmap, localized); + } catch (css::uno::RuntimeException &) { + if (!loadMissing) + throw; + } + if (found || !loadMissing) + return found; + + try { + OSL_TRACE( + "ImplImageTree::loadImage exception couldn't load \"%s\", fetching missing_icon.png", + rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8).getStr()); + found = loadDefaultImage(style, bitmap); + } catch (css::uno::RuntimeException &) { + throw; + } + return found; +} + +bool ImplImageTree::loadDefaultImage( + rtl::OUString const & style, + BitmapEx& bitmap) +{ + return doLoadImage( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("res/grafikde.png")), + style, bitmap, false); +} + + +bool ImplImageTree::doLoadImage( + rtl::OUString const & name, rtl::OUString const & style, BitmapEx & bitmap, + bool localized) +{ + setStyle(style); + if (iconCacheLookup(name, localized, bitmap)) { + return true; + } + if (!bitmap.IsEmpty()) { + bitmap.SetEmpty(); + } + std::vector< rtl::OUString > paths; + paths.push_back(name); + if (localized) { + sal_Int32 pos = name.lastIndexOf('/'); + if (pos != -1) { + css::lang::Locale const & loc = + Application::GetSettings().GetUILocale(); + paths.push_back(createPath(name, pos, loc.Language)); + if (loc.Country.getLength() != 0) { + rtl::OUStringBuffer b(loc.Language); + b.append(sal_Unicode('-')); + b.append(loc.Country); + rtl::OUString p(createPath(name, pos, b.makeStringAndClear())); + paths.push_back(p); + if (loc.Variant.getLength() != 0) { + b.append(p); + b.append(sal_Unicode('-')); + b.append(loc.Variant); + paths.push_back( + createPath(name, pos, b.makeStringAndClear())); + } + } + } + } + bool found = false; + try { + found = find(paths, bitmap); + } catch (css::uno::RuntimeException &) { + throw; + } catch (css::uno::Exception & e) { + OSL_TRACE( + "ImplImageTree::loadImage exception \"%s\"", + rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr()); + } + if (found) { + m_iconCache[name.intern()] = std::make_pair(localized, bitmap); + } + return found; +} + +void ImplImageTree::shutDown() { + m_style = rtl::OUString(); + // for safety; empty m_style means "not initialized" + m_zips.clear(); + m_iconCache.clear(); + m_checkStyleCache.clear(); +} + +void ImplImageTree::setStyle(rtl::OUString const & style) { + OSL_ASSERT(style.getLength() != 0); // empty m_style means "not initialized" + if (style != m_style) { + m_style = style; + resetZips(); + m_iconCache.clear(); + } +} + +void ImplImageTree::resetZips() { + m_zips.clear(); + { + rtl::OUString url( + RTL_CONSTASCII_USTRINGPARAM("$BRAND_BASE_DIR/program/edition/images.zip")); + rtl::Bootstrap::expandMacros(url); + INetURLObject u(url); + OSL_ASSERT(!u.HasError()); + m_zips.push_back( + std::make_pair( + u.GetMainURL(INetURLObject::NO_DECODE), + css::uno::Reference< css::container::XNameAccess >())); + } + { + rtl::OUString url( + RTL_CONSTASCII_USTRINGPARAM("$BRAND_BASE_DIR/share/config")); + rtl::Bootstrap::expandMacros(url); + INetURLObject u(url); + OSL_ASSERT(!u.HasError()); + rtl::OUStringBuffer b; + b.appendAscii(RTL_CONSTASCII_STRINGPARAM("images_")); + b.append(m_style); + b.appendAscii(RTL_CONSTASCII_STRINGPARAM("_brand.zip")); + bool ok = u.Append(b.makeStringAndClear(), INetURLObject::ENCODE_ALL); + OSL_ASSERT(ok); (void) ok; + m_zips.push_back( + std::make_pair( + u.GetMainURL(INetURLObject::NO_DECODE), + css::uno::Reference< css::container::XNameAccess >())); + } + { + rtl::OUString url( + RTL_CONSTASCII_USTRINGPARAM( + "$BRAND_BASE_DIR/share/config/images_brand.zip")); + rtl::Bootstrap::expandMacros(url); + m_zips.push_back( + std::make_pair( + url, css::uno::Reference< css::container::XNameAccess >())); + } + { + rtl::OUString url( + RTL_CONSTASCII_USTRINGPARAM("$OOO_BASE_DIR/share/config")); + rtl::Bootstrap::expandMacros(url); + INetURLObject u(url); + OSL_ASSERT(!u.HasError()); + rtl::OUStringBuffer b; + b.appendAscii(RTL_CONSTASCII_STRINGPARAM("images_")); + b.append(m_style); + b.appendAscii(RTL_CONSTASCII_STRINGPARAM(".zip")); + bool ok = u.Append(b.makeStringAndClear(), INetURLObject::ENCODE_ALL); + OSL_ASSERT(ok); (void) ok; + m_zips.push_back( + std::make_pair( + u.GetMainURL(INetURLObject::NO_DECODE), + css::uno::Reference< css::container::XNameAccess >())); + } + if ( m_style.equals(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("default"))) ) + { + rtl::OUString url( + RTL_CONSTASCII_USTRINGPARAM( + "$OOO_BASE_DIR/share/config/images.zip")); + rtl::Bootstrap::expandMacros(url); + m_zips.push_back( + std::make_pair( + url, css::uno::Reference< css::container::XNameAccess >())); + } +} + +bool ImplImageTree::checkStyleCacheLookup( + rtl::OUString const & style, bool &exists) +{ + CheckStyleCache::iterator i(m_checkStyleCache.find(style)); + if (i != m_checkStyleCache.end()) { + exists = i->second; + return true; + } else { + return false; + } +} + +bool ImplImageTree::iconCacheLookup( + rtl::OUString const & name, bool localized, BitmapEx & bitmap) +{ + IconCache::iterator i(m_iconCache.find(name)); + if (i != m_iconCache.end() && i->second.first == localized) { + bitmap = i->second.second; + return true; + } else { + return false; + } +} + +bool ImplImageTree::find( + std::vector< rtl::OUString > const & paths, BitmapEx & bitmap) +{ + for (Zips::iterator i(m_zips.begin()); i != m_zips.end();) { + if (!i->second.is()) { + css::uno::Sequence< css::uno::Any > args(1); + args[0] <<= i->first; + try { + i->second.set( + comphelper::createProcessComponentWithArguments( + rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.packages.zip.ZipFileAccess")), + args), + css::uno::UNO_QUERY_THROW); + } catch (css::uno::RuntimeException &) { + throw; + } catch (css::uno::Exception & e) { + OSL_TRACE( + "ImplImageTree::find exception \"%s\"", + rtl::OUStringToOString( + e.Message, RTL_TEXTENCODING_UTF8).getStr()); + i = m_zips.erase(i); + continue; + } + } + for (std::vector< rtl::OUString >::const_reverse_iterator j( + paths.rbegin()); + j != paths.rend(); ++j) + { + if (i->second->hasByName(*j)) { + css::uno::Reference< css::io::XInputStream > s; + bool ok = i->second->getByName(*j) >>= s; + OSL_ASSERT(ok); (void) ok; + loadFromStream(s, *j, bitmap); + return true; + } + } + ++i; + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/impvect.cxx b/vcl/source/gdi/impvect.cxx new file mode 100644 index 000000000000..21c0cbb0abd4 --- /dev/null +++ b/vcl/source/gdi/impvect.cxx @@ -0,0 +1,1205 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdlib.h> +#include <vcl/bmpacc.hxx> +#include <tools/poly.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/metaact.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/virdev.hxx> +#include <impvect.hxx> + +// ----------- +// - Defines - +// ----------- + +#define VECT_POLY_MAX 8192 + +// ----------------------------------------------------------------------------- + +#define VECT_FREE_INDEX 0 +#define VECT_CONT_INDEX 1 +#define VECT_DONE_INDEX 2 + +// ----------------------------------------------------------------------------- + +#define VECT_POLY_INLINE_INNER 1UL +#define VECT_POLY_INLINE_OUTER 2UL +#define VECT_POLY_OUTLINE_INNER 4UL +#define VECT_POLY_OUTLINE_OUTER 8UL + +// ----------------------------------------------------------------------------- + +#define VECT_MAP( _def_pIn, _def_pOut, _def_nVal ) _def_pOut[_def_nVal]=(_def_pIn[_def_nVal]=((_def_nVal)*4L)+1L)+5L; +#define BACK_MAP( _def_nVal ) ((((_def_nVal)+2)>>2)-1) +#define VECT_PROGRESS( _def_pProgress, _def_nVal ) if(_def_pProgress&&_def_pProgress->IsSet())(_def_pProgress->Call((void*)_def_nVal)); + +// ----------- +// - statics - +// ----------- + +struct ChainMove { long nDX; long nDY; }; + +static ChainMove aImplMove[ 8 ] = { + { 1L, 0L }, + { 0L, -1L }, + { -1L, 0L }, + { 0L, 1L }, + { 1L, -1L }, + { -1, -1L }, + { -1L, 1L }, + { 1L, 1L } + }; + +static ChainMove aImplMoveInner[ 8 ] = { + { 0L, 1L }, + { 1L, 0L }, + { 0L, -1L }, + { -1L, 0L }, + { 0L, 1L }, + { 1L, 0L }, + { 0L, -1L }, + { -1L, 0L } + }; + +static ChainMove aImplMoveOuter[ 8 ] = { + { 0L, -1L }, + { -1L, 0L }, + { 0L, 1L }, + { 1L, 0L }, + { -1L, 0L }, + { 0L, 1L }, + { 1L, 0L }, + { 0L, -1L } + }; + +// ---------------- +// - ImplColorSet - +// ---------------- + +struct ImplColorSet +{ + BitmapColor maColor; + sal_uInt16 mnIndex; + sal_Bool mbSet; + + sal_Bool operator<( const ImplColorSet& rSet ) const; + sal_Bool operator>( const ImplColorSet& rSet ) const; +}; + +// ---------------------------------------------------------------------------- + +inline sal_Bool ImplColorSet::operator<( const ImplColorSet& rSet ) const +{ + return( mbSet && ( !rSet.mbSet || ( maColor.GetLuminance() > rSet.maColor.GetLuminance() ) ) ); +} + +// ---------------------------------------------------------------------------- + +inline sal_Bool ImplColorSet::operator>( const ImplColorSet& rSet ) const +{ + return( !mbSet || ( rSet.mbSet && maColor.GetLuminance() < rSet.maColor.GetLuminance() ) ); +} + +// ---------------------------------------------------------------------------- + +extern "C" int __LOADONCALLAPI ImplColorSetCmpFnc( const void* p1, const void* p2 ) +{ + ImplColorSet* pSet1 = (ImplColorSet*) p1; + ImplColorSet* pSet2 = (ImplColorSet*) p2; + int nRet; + + if( pSet1->mbSet && pSet2->mbSet ) + { + const sal_uInt8 cLum1 = pSet1->maColor.GetLuminance(); + const sal_uInt8 cLum2 = pSet2->maColor.GetLuminance(); + nRet = ( ( cLum1 > cLum2 ) ? -1 : ( ( cLum1 == cLum2 ) ? 0 : 1 ) ); + } + else if( pSet1->mbSet ) + nRet = -1; + else if( pSet2->mbSet ) + nRet = 1; + else + nRet = 0; + + return nRet; +} + +// ------------------ +// - ImplPointArray - +// ------------------ + +class ImplPointArray +{ + Point* mpArray; + sal_uLong mnSize; + sal_uLong mnRealSize; + +public: + + ImplPointArray(); + ~ImplPointArray(); + + void ImplSetSize( sal_uLong nSize ); + + sal_uLong ImplGetRealSize() const { return mnRealSize; } + void ImplSetRealSize( sal_uLong nRealSize ) { mnRealSize = nRealSize; } + + inline Point& operator[]( sal_uLong nPos ); + inline const Point& operator[]( sal_uLong nPos ) const; + + void ImplCreatePoly( Polygon& rPoly ) const; +}; + +// ----------------------------------------------------------------------------- + +ImplPointArray::ImplPointArray() : + mpArray ( NULL ), + mnSize ( 0UL ), + mnRealSize ( 0UL ) + +{ +} + +// ----------------------------------------------------------------------------- + +ImplPointArray::~ImplPointArray() +{ + if( mpArray ) + rtl_freeMemory( mpArray ); +} + +// ----------------------------------------------------------------------------- + +void ImplPointArray::ImplSetSize( sal_uLong nSize ) +{ + const sal_uLong nTotal = nSize * sizeof( Point ); + + mnSize = nSize; + mnRealSize = 0UL; + + if( mpArray ) + rtl_freeMemory( mpArray ); + + mpArray = (Point*) rtl_allocateMemory( nTotal ); + memset( (HPBYTE) mpArray, 0, nTotal ); +} + +// ----------------------------------------------------------------------------- + +inline Point& ImplPointArray::operator[]( sal_uLong nPos ) +{ + DBG_ASSERT( nPos < mnSize, "ImplPointArray::operator[]: nPos out of range!" ); + return mpArray[ nPos ]; +} + +// ----------------------------------------------------------------------------- + +inline const Point& ImplPointArray::operator[]( sal_uLong nPos ) const +{ + DBG_ASSERT( nPos < mnSize, "ImplPointArray::operator[]: nPos out of range!" ); + return mpArray[ nPos ]; +} + +// ----------------------------------------------------------------------------- + +void ImplPointArray::ImplCreatePoly( Polygon& rPoly ) const +{ + rPoly = Polygon( sal::static_int_cast<sal_uInt16>(mnRealSize), mpArray ); +} + +// --------------- +// - ImplVectMap - +// --------------- + +class ImplVectMap +{ +private: + + Scanline mpBuf; + Scanline* mpScan; + long mnWidth; + long mnHeight; + + ImplVectMap() {}; + +public: + + ImplVectMap( long nWidth, long nHeight ); + ~ImplVectMap(); + + inline long Width() const { return mnWidth; } + inline long Height() const { return mnHeight; } + + inline void Set( long nY, long nX, sal_uInt8 cVal ); + inline sal_uInt8 Get( long nY, long nX ) const; + + inline sal_Bool IsFree( long nY, long nX ) const; + inline sal_Bool IsCont( long nY, long nX ) const; + inline sal_Bool IsDone( long nY, long nX ) const; + +}; + +// ----------------------------------------------------------------------------- + +ImplVectMap::ImplVectMap( long nWidth, long nHeight ) : + mnWidth ( nWidth ), + mnHeight( nHeight ) +{ + const long nWidthAl = ( nWidth >> 2L ) + 1L; + const long nSize = nWidthAl * nHeight; + Scanline pTmp = mpBuf = (Scanline) rtl_allocateMemory( nSize ); + + memset( mpBuf, 0, nSize ); + mpScan = (Scanline*) rtl_allocateMemory( nHeight * sizeof( Scanline ) ); + + for( long nY = 0L; nY < nHeight; pTmp += nWidthAl ) + mpScan[ nY++ ] = pTmp; +} + +// ----------------------------------------------------------------------------- + + +ImplVectMap::~ImplVectMap() +{ + rtl_freeMemory( mpBuf ); + rtl_freeMemory( mpScan ); +} + +// ----------------------------------------------------------------------------- + +inline void ImplVectMap::Set( long nY, long nX, sal_uInt8 cVal ) +{ + const sal_uInt8 cShift = sal::static_int_cast<sal_uInt8>(6 - ( ( nX & 3 ) << 1 )); + ( ( mpScan[ nY ][ nX >> 2 ] ) &= ~( 3 << cShift ) ) |= ( cVal << cShift ); +} + +// ----------------------------------------------------------------------------- + +inline sal_uInt8 ImplVectMap::Get( long nY, long nX ) const +{ + return sal::static_int_cast<sal_uInt8>( ( ( mpScan[ nY ][ nX >> 2 ] ) >> ( 6 - ( ( nX & 3 ) << 1 ) ) ) & 3 ); +} + +// ----------------------------------------------------------------------------- + +inline sal_Bool ImplVectMap::IsFree( long nY, long nX ) const +{ + return( VECT_FREE_INDEX == Get( nY, nX ) ); +} + +// ----------------------------------------------------------------------------- + +inline sal_Bool ImplVectMap::IsCont( long nY, long nX ) const +{ + return( VECT_CONT_INDEX == Get( nY, nX ) ); +} + +// ----------------------------------------------------------------------------- + +inline sal_Bool ImplVectMap::IsDone( long nY, long nX ) const +{ + return( VECT_DONE_INDEX == Get( nY, nX ) ); +} + +// ------------- +// - ImplChain - +// ------------- + +class ImplChain +{ +private: + + Polygon maPoly; + Point maStartPt; + sal_uLong mnArraySize; + sal_uLong mnCount; + long mnResize; + sal_uInt8* mpCodes; + + void ImplGetSpace(); + + void ImplCreate(); + void ImplCreateInner(); + void ImplCreateOuter(); + void ImplPostProcess( const ImplPointArray& rArr ); + +public: + + ImplChain( sal_uLong nInitCount = 1024UL, long nResize = -1L ); + ~ImplChain(); + + void ImplBeginAdd( const Point& rStartPt ); + inline void ImplAdd( sal_uInt8 nCode ); + void ImplEndAdd( sal_uLong nTypeFlag ); + + const Polygon& ImplGetPoly() const { return maPoly; } +}; + +// ----------------------------------------------------------------------------- + +ImplChain::ImplChain( sal_uLong nInitCount, long nResize ) : + mnArraySize ( nInitCount ), + mnCount ( 0UL ), + mnResize ( nResize ) +{ + DBG_ASSERT( nInitCount && nResize, "ImplChain::ImplChain(): invalid parameters!" ); + mpCodes = new sal_uInt8[ mnArraySize ]; +} + +// ----------------------------------------------------------------------------- + +ImplChain::~ImplChain() +{ + delete[] mpCodes; +} + +// ----------------------------------------------------------------------------- + +void ImplChain::ImplGetSpace() +{ + const sal_uLong nOldArraySize = mnArraySize; + sal_uInt8* pNewCodes; + + mnArraySize = ( mnResize < 0L ) ? ( mnArraySize << 1UL ) : ( mnArraySize + (sal_uLong) mnResize ); + pNewCodes = new sal_uInt8[ mnArraySize ]; + memcpy( pNewCodes, mpCodes, nOldArraySize ); + delete[] mpCodes; + mpCodes = pNewCodes; +} + +// ----------------------------------------------------------------------------- + +void ImplChain::ImplBeginAdd( const Point& rStartPt ) +{ + maPoly = Polygon(); + maStartPt = rStartPt; + mnCount = 0UL; +} + +// ----------------------------------------------------------------------------- + +inline void ImplChain::ImplAdd( sal_uInt8 nCode ) +{ + if( mnCount == mnArraySize ) + ImplGetSpace(); + + mpCodes[ mnCount++ ] = nCode; +} + +// ----------------------------------------------------------------------------- + +void ImplChain::ImplEndAdd( sal_uLong nFlag ) +{ + if( mnCount ) + { + ImplPointArray aArr; + + if( nFlag & VECT_POLY_INLINE_INNER ) + { + long nFirstX, nFirstY; + long nLastX, nLastY; + + nFirstX = nLastX = maStartPt.X(); + nFirstY = nLastY = maStartPt.Y(); + aArr.ImplSetSize( mnCount << 1 ); + + sal_uInt16 i, nPolyPos; + for( i = 0, nPolyPos = 0; i < ( mnCount - 1 ); i++ ) + { + const sal_uInt8 cMove = mpCodes[ i ]; + const sal_uInt8 cNextMove = mpCodes[ i + 1 ]; + const ChainMove& rMove = aImplMove[ cMove ]; + const ChainMove& rMoveInner = aImplMoveInner[ cMove ]; +// Point& rPt = aArr[ nPolyPos ]; + sal_Bool bDone = sal_True; + + nLastX += rMove.nDX; + nLastY += rMove.nDY; + + if( cMove < 4 ) + { + if( ( cMove == 0 && cNextMove == 3 ) || + ( cMove == 3 && cNextMove == 2 ) || + ( cMove == 2 && cNextMove == 1 ) || + ( cMove == 1 && cNextMove == 0 ) ) + { + } + else if( cMove == 2 && cNextMove == 3 ) + { + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY - 1; + + aArr[ nPolyPos ].X() = nLastX - 1; + aArr[ nPolyPos++ ].Y() = nLastY - 1; + + aArr[ nPolyPos ].X() = nLastX - 1; + aArr[ nPolyPos++ ].Y() = nLastY; + } + else if( cMove == 3 && cNextMove == 0 ) + { + aArr[ nPolyPos ].X() = nLastX - 1; + aArr[ nPolyPos++ ].Y() = nLastY; + + aArr[ nPolyPos ].X() = nLastX - 1; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + } + else if( cMove == 0 && cNextMove == 1 ) + { + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY; + } + else if( cMove == 1 && cNextMove == 2 ) + { + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY - 1; + + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY - 1; + } + else + bDone = sal_False; + } + else if( cMove == 7 && cNextMove == 0 ) + { + aArr[ nPolyPos ].X() = nLastX - 1; + aArr[ nPolyPos++ ].Y() = nLastY; + + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + } + else if( cMove == 4 && cNextMove == 1 ) + { + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY; + } + else + bDone = sal_False; + + if( !bDone ) + { + aArr[ nPolyPos ].X() = nLastX + rMoveInner.nDX; + aArr[ nPolyPos++ ].Y() = nLastY + rMoveInner.nDY; + } + } + + aArr[ nPolyPos ].X() = nFirstX + 1L; + aArr[ nPolyPos++ ].Y() = nFirstY + 1L; + aArr.ImplSetRealSize( nPolyPos ); + } + else if( nFlag & VECT_POLY_INLINE_OUTER ) + { + long nFirstX, nFirstY; + long nLastX, nLastY; + + nFirstX = nLastX = maStartPt.X(); + nFirstY = nLastY = maStartPt.Y(); + aArr.ImplSetSize( mnCount << 1 ); + + sal_uInt16 i, nPolyPos; + for( i = 0, nPolyPos = 0; i < ( mnCount - 1 ); i++ ) + { + const sal_uInt8 cMove = mpCodes[ i ]; + const sal_uInt8 cNextMove = mpCodes[ i + 1 ]; + const ChainMove& rMove = aImplMove[ cMove ]; + const ChainMove& rMoveOuter = aImplMoveOuter[ cMove ]; +// Point& rPt = aArr[ nPolyPos ]; + sal_Bool bDone = sal_True; + + nLastX += rMove.nDX; + nLastY += rMove.nDY; + + if( cMove < 4 ) + { + if( ( cMove == 0 && cNextMove == 1 ) || + ( cMove == 1 && cNextMove == 2 ) || + ( cMove == 2 && cNextMove == 3 ) || + ( cMove == 3 && cNextMove == 0 ) ) + { + } + else if( cMove == 0 && cNextMove == 3 ) + { + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY - 1; + + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY - 1; + + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY; + } + else if( cMove == 3 && cNextMove == 2 ) + { + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY; + + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + } + else if( cMove == 2 && cNextMove == 1 ) + { + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + + aArr[ nPolyPos ].X() = nLastX - 1; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + + aArr[ nPolyPos ].X() = nLastX - 1; + aArr[ nPolyPos++ ].Y() = nLastY; + } + else if( cMove == 1 && cNextMove == 0 ) + { + aArr[ nPolyPos ].X() = nLastX - 1; + aArr[ nPolyPos++ ].Y() = nLastY; + + aArr[ nPolyPos ].X() = nLastX - 1; + aArr[ nPolyPos++ ].Y() = nLastY - 1; + + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY - 1; + } + else + bDone = sal_False; + } + else if( cMove == 7 && cNextMove == 3 ) + { + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY - 1; + + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY; + } + else if( cMove == 6 && cNextMove == 2 ) + { + aArr[ nPolyPos ].X() = nLastX + 1; + aArr[ nPolyPos++ ].Y() = nLastY; + + aArr[ nPolyPos ].X() = nLastX; + aArr[ nPolyPos++ ].Y() = nLastY + 1; + } + else + bDone = sal_False; + + if( !bDone ) + { + aArr[ nPolyPos ].X() = nLastX + rMoveOuter.nDX; + aArr[ nPolyPos++ ].Y() = nLastY + rMoveOuter.nDY; + } + } + + aArr[ nPolyPos ].X() = nFirstX - 1L; + aArr[ nPolyPos++ ].Y() = nFirstY - 1L; + aArr.ImplSetRealSize( nPolyPos ); + } + else + { + long nLastX = maStartPt.X(), nLastY = maStartPt.Y(); + + aArr.ImplSetSize( mnCount + 1 ); + aArr[ 0 ] = Point( nLastX, nLastY ); + + for( sal_uLong i = 0; i < mnCount; ) + { + const ChainMove& rMove = aImplMove[ mpCodes[ i ] ]; + aArr[ ++i ] = Point( nLastX += rMove.nDX, nLastY += rMove.nDY ); + } + + aArr.ImplSetRealSize( mnCount + 1 ); + } + + ImplPostProcess( aArr ); + } + else + maPoly.SetSize( 0 ); +} + +// ----------------------------------------------------------------------------- + +void ImplChain::ImplPostProcess( const ImplPointArray& rArr ) +{ + ImplPointArray aNewArr1; + ImplPointArray aNewArr2; + Point* pLast; + Point* pLeast; + sal_uLong nNewPos; + sal_uLong nCount = rArr.ImplGetRealSize(); + sal_uLong n; + + // pass 1 + aNewArr1.ImplSetSize( nCount ); + pLast = &( aNewArr1[ 0 ] ); + pLast->X() = BACK_MAP( rArr[ 0 ].X() ); + pLast->Y() = BACK_MAP( rArr[ 0 ].Y() ); + + for( n = nNewPos = 1; n < nCount; ) + { + const Point& rPt = rArr[ n++ ]; + const long nX = BACK_MAP( rPt.X() ); + const long nY = BACK_MAP( rPt.Y() ); + + if( nX != pLast->X() || nY != pLast->Y() ) + { + pLast = pLeast = &( aNewArr1[ nNewPos++ ] ); + pLeast->X() = nX; + pLeast->Y() = nY; + } + } + + aNewArr1.ImplSetRealSize( nCount = nNewPos ); + + // pass 2 + aNewArr2.ImplSetSize( nCount ); + pLast = &( aNewArr2[ 0 ] ); + *pLast = aNewArr1[ 0 ]; + + for( n = nNewPos = 1; n < nCount; ) + { + pLeast = &( aNewArr1[ n++ ] ); + + if( pLeast->X() == pLast->X() ) + { + while( n < nCount && aNewArr1[ n ].X() == pLast->X() ) + pLeast = &( aNewArr1[ n++ ] ); + } + else if( pLeast->Y() == pLast->Y() ) + { + while( n < nCount && aNewArr1[ n ].Y() == pLast->Y() ) + pLeast = &( aNewArr1[ n++ ] ); + } + + aNewArr2[ nNewPos++ ] = *( pLast = pLeast ); + } + + aNewArr2.ImplSetRealSize( nNewPos ); + aNewArr2.ImplCreatePoly( maPoly ); +} + +// ------------------ +// - ImplVectorizer - +// ------------------ + +ImplVectorizer::ImplVectorizer() +{ +} + +// ----------------------------------------------------------------------------- + +ImplVectorizer::~ImplVectorizer() +{ +} + +// ----------------------------------------------------------------------------- + +sal_Bool ImplVectorizer::ImplVectorize( const Bitmap& rColorBmp, GDIMetaFile& rMtf, + sal_uInt8 cReduce, sal_uLong nFlags, const Link* pProgress ) +{ + sal_Bool bRet = sal_False; + + VECT_PROGRESS( pProgress, 0 ); + + Bitmap* pBmp = new Bitmap( rColorBmp ); + BitmapReadAccess* pRAcc = pBmp->AcquireReadAccess(); + + if( pRAcc ) + { + PolyPolygon aPolyPoly; + double fPercent = 0.0; + double fPercentStep_2 = 0.0; + const long nWidth = pRAcc->Width(); + const long nHeight = pRAcc->Height(); + const sal_uInt16 nColorCount = pRAcc->GetPaletteEntryCount(); + sal_uInt16 n; + ImplColorSet* pColorSet = (ImplColorSet*) new sal_uInt8[ 256 * sizeof( ImplColorSet ) ]; + + memset( pColorSet, 0, 256 * sizeof( ImplColorSet ) ); + rMtf.Clear(); + + // get used palette colors and sort them from light to dark colors + for( n = 0; n < nColorCount; n++ ) + { + pColorSet[ n ].mnIndex = n; + pColorSet[ n ].maColor = pRAcc->GetPaletteColor( n ); + } + + for( long nY = 0L; nY < nHeight; nY++ ) + for( long nX = 0L; nX < nWidth; nX++ ) + pColorSet[ pRAcc->GetPixel( nY, nX ).GetIndex() ].mbSet = 1; + + qsort( pColorSet, 256, sizeof( ImplColorSet ), ImplColorSetCmpFnc ); + + for( n = 0; n < 256; n++ ) + if( !pColorSet[ n ].mbSet ) + break; + + if( n ) + fPercentStep_2 = 45.0 / n; + + VECT_PROGRESS( pProgress, FRound( fPercent += 10.0 ) ); + + for( sal_uInt16 i = 0; i < n; i++ ) + { + const BitmapColor aBmpCol( pRAcc->GetPaletteColor( pColorSet[ i ].mnIndex ) ); + const Color aFindColor( aBmpCol.GetRed(), aBmpCol.GetGreen(), aBmpCol.GetBlue() ); +// const sal_uInt8 cLum = aFindColor.GetLuminance(); + ImplVectMap* pMap = ImplExpand( pRAcc, aFindColor ); + + VECT_PROGRESS( pProgress, FRound( fPercent += fPercentStep_2 ) ); + + if( pMap ) + { + aPolyPoly.Clear(); + ImplCalculate( pMap, aPolyPoly, cReduce, nFlags ); + delete pMap; + + if( aPolyPoly.Count() ) + { + ImplLimitPolyPoly( aPolyPoly ); + + if( nFlags & BMP_VECTORIZE_REDUCE_EDGES ) + aPolyPoly.Optimize( POLY_OPTIMIZE_EDGES ); + + if( aPolyPoly.Count() ) + { + rMtf.AddAction( new MetaLineColorAction( aFindColor, sal_True ) ); + rMtf.AddAction( new MetaFillColorAction( aFindColor, sal_True ) ); + rMtf.AddAction( new MetaPolyPolygonAction( aPolyPoly ) ); + } + } + } + + VECT_PROGRESS( pProgress, FRound( fPercent += fPercentStep_2 ) ); + } + + delete[] (sal_uInt8*) pColorSet; + + if( rMtf.GetActionCount() ) + { + MapMode aMap( MAP_100TH_MM ); + VirtualDevice aVDev; + const Size aLogSize1( aVDev.PixelToLogic( Size( 1, 1 ), aMap ) ); + + rMtf.SetPrefMapMode( aMap ); + rMtf.SetPrefSize( Size( nWidth + 2, nHeight + 2 ) ); + rMtf.Move( 1, 1 ); + rMtf.Scale( aLogSize1.Width(), aLogSize1.Height() ); + bRet = sal_True; + } + } + + pBmp->ReleaseAccess( pRAcc ); + delete pBmp; + VECT_PROGRESS( pProgress, 100 ); + + return bRet; +} + +// ----------------------------------------------------------------------------- + +sal_Bool ImplVectorizer::ImplVectorize( const Bitmap& rMonoBmp, + PolyPolygon& rPolyPoly, + sal_uLong nFlags, const Link* pProgress ) +{ + Bitmap* pBmp = new Bitmap( rMonoBmp ); + BitmapReadAccess* pRAcc; + ImplVectMap* pMap; + sal_Bool bRet = sal_False; + + VECT_PROGRESS( pProgress, 10 ); + + if( pBmp->GetBitCount() > 1 ) + pBmp->Convert( BMP_CONVERSION_1BIT_THRESHOLD ); + + VECT_PROGRESS( pProgress, 30 ); + + pRAcc = pBmp->AcquireReadAccess(); + pMap = ImplExpand( pRAcc, COL_BLACK ); + pBmp->ReleaseAccess( pRAcc ); + delete pBmp; + + VECT_PROGRESS( pProgress, 60 ); + + if( pMap ) + { + rPolyPoly.Clear(); + ImplCalculate( pMap, rPolyPoly, 0, nFlags ); + delete pMap; + ImplLimitPolyPoly( rPolyPoly ); + + if( nFlags & BMP_VECTORIZE_REDUCE_EDGES ) + rPolyPoly.Optimize( POLY_OPTIMIZE_EDGES ); + + // #i14895#:setting the correct direction for polygons + // that represent holes and non-holes; non-hole polygons + // need to have a right orientation, holes need to have a + // left orientation in order to be treated correctly by + // several external tools like Flash viewers + sal_Int32 nFirstPoly = -1; + sal_uInt16 nCurPoly( 0 ), nCount( rPolyPoly.Count() ); + + for( ; nCurPoly < nCount; ++nCurPoly ) + { + const Polygon& rPoly = rPolyPoly.GetObject( nCurPoly ); + const sal_uInt16 nSize( rPoly.GetSize() ); + sal_uInt16 nDepth( 0 ), i( 0 ); + const bool bRight( rPoly.IsRightOrientated() ); + + for( ; i < nCount; ++i ) + if( ( i != nCurPoly ) && rPolyPoly.GetObject( i ).IsInside( rPoly[ 0 ] ) ) + ++nDepth; + + const bool bHole( ( nDepth & 0x0001 ) == 1 ); + + if( nSize && ( ( !bRight && !bHole ) || ( bRight && bHole ) ) ) + { + Polygon aNewPoly( nSize ); + sal_uInt16 nPrim( 0 ), nSec( nSize - 1 ); + + if( rPoly.HasFlags() ) + { + while( nPrim < nSize ) + { + aNewPoly.SetPoint( rPoly.GetPoint( nSec ), nPrim ); + aNewPoly.SetFlags( nPrim++, rPoly.GetFlags( nSec-- ) ); + } + } + else + while( nPrim < nSize ) + aNewPoly.SetPoint( rPoly.GetPoint( nSec-- ), nPrim++ ); + + rPolyPoly.Replace( aNewPoly, nCurPoly ); + } + + if( ( 0 == nDepth ) && ( -1 == nFirstPoly ) ) + nFirstPoly = nCurPoly; + } + + // put outmost polygon to the front + if( nFirstPoly > 0 ) + { + const Polygon aFirst( rPolyPoly.GetObject( static_cast< sal_uInt16 >( nFirstPoly ) ) ); + + rPolyPoly.Remove( static_cast< sal_uInt16 >( nFirstPoly ) ); + rPolyPoly.Insert( aFirst, 0 ); + } + + bRet = sal_True; + } + + VECT_PROGRESS( pProgress, 100 ); + + return bRet; +} + +// ----------------------------------------------------------------------------- + +void ImplVectorizer::ImplLimitPolyPoly( PolyPolygon& rPolyPoly ) +{ + if( rPolyPoly.Count() > VECT_POLY_MAX ) + { + PolyPolygon aNewPolyPoly; + long nReduce = 0; + sal_uInt16 nNewCount; + + do + { + aNewPolyPoly.Clear(); + nReduce++; + + for( sal_uInt16 i = 0, nCount = rPolyPoly.Count(); i < nCount; i++ ) + { + const Rectangle aBound( rPolyPoly[ i ].GetBoundRect() ); + + if( aBound.GetWidth() > nReduce && aBound.GetHeight() > nReduce ) + { + if( rPolyPoly[ i ].GetSize() ) + aNewPolyPoly.Insert( rPolyPoly[ i ] ); + } + } + + nNewCount = aNewPolyPoly.Count(); + } + while( nNewCount > VECT_POLY_MAX ); + + rPolyPoly = aNewPolyPoly; + } +} + +// ----------------------------------------------------------------------------- + +ImplVectMap* ImplVectorizer::ImplExpand( BitmapReadAccess* pRAcc, const Color& rColor ) +{ + ImplVectMap* pMap = NULL; + + if( pRAcc && pRAcc->Width() && pRAcc->Height() ) + { + const long nOldWidth = pRAcc->Width(); + const long nOldHeight = pRAcc->Height(); + const long nNewWidth = ( nOldWidth << 2L ) + 4L; + const long nNewHeight = ( nOldHeight << 2L ) + 4L; + const BitmapColor aTest( pRAcc->GetBestMatchingColor( rColor ) ); + long* pMapIn = new long[ Max( nOldWidth, nOldHeight ) ]; + long* pMapOut = new long[ Max( nOldWidth, nOldHeight ) ]; + long nX, nY, nTmpX, nTmpY; + + pMap = new ImplVectMap( nNewWidth, nNewHeight ); + + for( nX = 0L; nX < nOldWidth; nX++ ) + VECT_MAP( pMapIn, pMapOut, nX ); + + for( nY = 0L, nTmpY = 5L; nY < nOldHeight; nY++, nTmpY += 4L ) + { + for( nX = 0L; nX < nOldWidth; ) + { + if( pRAcc->GetPixel( nY, nX ) == aTest ) + { + nTmpX = pMapIn[ nX++ ]; + nTmpY -= 3L; + + pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX ); + pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX ); + pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX ); + pMap->Set( nTmpY, nTmpX, VECT_CONT_INDEX ); + + while( nX < nOldWidth && pRAcc->GetPixel( nY, nX ) == aTest ) + nX++; + + nTmpX = pMapOut[ nX - 1L ]; + nTmpY -= 3L; + + pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX ); + pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX ); + pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX ); + pMap->Set( nTmpY, nTmpX, VECT_CONT_INDEX ); + } + else + nX++; + } + } + + for( nY = 0L; nY < nOldHeight; nY++ ) + VECT_MAP( pMapIn, pMapOut, nY ); + + for( nX = 0L, nTmpX = 5L; nX < nOldWidth; nX++, nTmpX += 4L ) + { + for( nY = 0L; nY < nOldHeight; ) + { + if( pRAcc->GetPixel( nY, nX ) == aTest ) + { + nTmpX -= 3L; + nTmpY = pMapIn[ nY++ ]; + + pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX ); + pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX ); + pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX ); + pMap->Set( nTmpY, nTmpX, VECT_CONT_INDEX ); + + while( nY < nOldHeight && pRAcc->GetPixel( nY, nX ) == aTest ) + nY++; + + nTmpX -= 3L; + nTmpY = pMapOut[ nY - 1L ]; + + pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX ); + pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX ); + pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX ); + pMap->Set( nTmpY, nTmpX, VECT_CONT_INDEX ); + } + else + nY++; + } + } + + // cleanup + delete[] pMapIn; + delete[] pMapOut; + } + + return pMap; +} + +// ----------------------------------------------------------------------------- + +void ImplVectorizer::ImplCalculate( ImplVectMap* pMap, PolyPolygon& rPolyPoly, sal_uInt8 cReduce, sal_uLong nFlags ) +{ + const long nWidth = pMap->Width(), nHeight= pMap->Height(); + + for( long nY = 0L; nY < nHeight; nY++ ) + { + long nX = 0L; + sal_Bool bInner = sal_True; + + while( nX < nWidth ) + { + // skip free + while( ( nX < nWidth ) && pMap->IsFree( nY, nX ) ) + nX++; + + if( nX == nWidth ) + break; + + if( pMap->IsCont( nY, nX ) ) + { + // new contour + ImplChain aChain; + const Point aStartPt( nX++, nY ); + + // get chain code + aChain.ImplBeginAdd( aStartPt ); + ImplGetChain( pMap, aStartPt, aChain ); + + if( nFlags & BMP_VECTORIZE_INNER ) + aChain.ImplEndAdd( bInner ? VECT_POLY_INLINE_INNER : VECT_POLY_INLINE_OUTER ); + else + aChain.ImplEndAdd( bInner ? VECT_POLY_OUTLINE_INNER : VECT_POLY_OUTLINE_OUTER ); + + const Polygon& rPoly = aChain.ImplGetPoly(); + + if( rPoly.GetSize() > 2 ) + { + if( cReduce ) + { + const Rectangle aBound( rPoly.GetBoundRect() ); + + if( aBound.GetWidth() > cReduce && aBound.GetHeight() > cReduce ) + rPolyPoly.Insert( rPoly ); + } + else + rPolyPoly.Insert( rPoly ); + } + + // skip rest of detected contour + while( pMap->IsCont( nY, nX ) ) + nX++; + } + else + { + // process done segment + const long nStartSegX = nX++; + + while( pMap->IsDone( nY, nX ) ) + nX++; + + if( ( ( nX - nStartSegX ) == 1L ) || ( ImplIsUp( pMap, nY, nStartSegX ) != ImplIsUp( pMap, nY, nX - 1L ) ) ) + bInner = !bInner; + } + } + } +} + +// ----------------------------------------------------------------------------- + +sal_Bool ImplVectorizer::ImplGetChain( ImplVectMap* pMap, const Point& rStartPt, ImplChain& rChain ) +{ + long nActX = rStartPt.X(); + long nActY = rStartPt.Y(); + long nTryX; + long nTryY; + sal_uLong nFound; + sal_uLong nLastDir = 0UL; + sal_uLong nDir; + + do + { + nFound = 0UL; + + // first try last direction + nTryX = nActX + aImplMove[ nLastDir ].nDX; + nTryY = nActY + aImplMove[ nLastDir ].nDY; + + if( pMap->IsCont( nTryY, nTryX ) ) + { + rChain.ImplAdd( (sal_uInt8) nLastDir ); + pMap->Set( nActY = nTryY, nActX = nTryX, VECT_DONE_INDEX ); + nFound = 1UL; + } + else + { + // try other directions + for( nDir = 0UL; nDir < 8UL; nDir++ ) + { + // we already tried nLastDir + if( nDir != nLastDir ) + { + nTryX = nActX + aImplMove[ nDir ].nDX; + nTryY = nActY + aImplMove[ nDir ].nDY; + + if( pMap->IsCont( nTryY, nTryX ) ) + { + rChain.ImplAdd( (sal_uInt8) nDir ); + pMap->Set( nActY = nTryY, nActX = nTryX, VECT_DONE_INDEX ); + nFound = 1UL; + nLastDir = nDir; + break; + } + } + } + } + } + while( nFound ); + + return sal_True; +} + +// ----------------------------------------------------------------------------- + +sal_Bool ImplVectorizer::ImplIsUp( ImplVectMap* pMap, long nY, long nX ) const +{ + if( pMap->IsDone( nY - 1L, nX ) ) + return sal_True; + else if( pMap->IsDone( nY + 1L, nX ) ) + return sal_False; + else if( pMap->IsDone( nY - 1L, nX - 1L ) || pMap->IsDone( nY - 1L, nX + 1L ) ) + return sal_True; + else + return sal_False; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/impvect.hxx b/vcl/source/gdi/impvect.hxx new file mode 100644 index 000000000000..56591e969b8b --- /dev/null +++ b/vcl/source/gdi/impvect.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_IMPVECT_HXX +#define _SV_IMPVECT_HXX + +#include <tools/poly.hxx> +#include <vcl/gdimtf.hxx> + +// -------------- +// - Vectorizer - +// -------------- + +class BitmapReadAccess; +class ImplChain; +class ImplVectMap; + +class ImplVectorizer +{ +private: + + ImplVectMap* ImplExpand( BitmapReadAccess* pRAcc, const Color& rColor ); + void ImplCalculate( ImplVectMap* pMap, PolyPolygon& rPolyPoly, sal_uInt8 cReduce, sal_uLong nFlags ); + sal_Bool ImplGetChain( ImplVectMap* pMap, const Point& rStartPt, ImplChain& rChain ); + sal_Bool ImplIsUp( ImplVectMap* pMap, long nY, long nX ) const; + void ImplLimitPolyPoly( PolyPolygon& rPolyPoly ); + +public: + + ImplVectorizer(); + ~ImplVectorizer(); + + sal_Bool ImplVectorize( const Bitmap& rColorBmp, GDIMetaFile& rMtf, + sal_uInt8 cReduce, sal_uLong nFlags, const Link* pProgress ); + sal_Bool ImplVectorize( const Bitmap& rMonoBmp, PolyPolygon& rPolyPoly, + sal_uLong nFlags, const Link* pProgress ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/jobset.cxx b/vcl/source/gdi/jobset.cxx new file mode 100644 index 000000000000..bcf7986c70b2 --- /dev/null +++ b/vcl/source/gdi/jobset.cxx @@ -0,0 +1,455 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <rtl/alloc.h> +#include <vcl/jobset.hxx> +#include <vcl/jobset.h> + +// ======================================================================= + +DBG_NAME( JobSetup ) + +#define JOBSET_FILEFORMAT2 3780 +#define JOBSET_FILE364_SYSTEM ((sal_uInt16)0xFFFF) +#define JOBSET_FILE605_SYSTEM ((sal_uInt16)0xFFFE) + +struct ImplOldJobSetupData +{ + char cPrinterName[64]; + char cDeviceName[32]; + char cPortName[32]; + char cDriverName[32]; +}; + +struct Impl364JobSetupData +{ + SVBT16 nSize; + SVBT16 nSystem; + SVBT32 nDriverDataLen; + SVBT16 nOrientation; + SVBT16 nPaperBin; + SVBT16 nPaperFormat; + SVBT32 nPaperWidth; + SVBT32 nPaperHeight; +}; + +// ======================================================================= + +ImplJobSetup::ImplJobSetup() +{ + mnRefCount = 1; + mnSystem = 0; + meOrientation = ORIENTATION_PORTRAIT; + meDuplexMode = DUPLEX_UNKNOWN; + mnPaperBin = 0; + mePaperFormat = PAPER_USER; + mnPaperWidth = 0; + mnPaperHeight = 0; + mnDriverDataLen = 0; + mpDriverData = NULL; +} + +// ----------------------------------------------------------------------- + +ImplJobSetup::ImplJobSetup( const ImplJobSetup& rJobSetup ) : + maPrinterName( rJobSetup.maPrinterName ), + maDriver( rJobSetup.maDriver ) +{ + mnRefCount = 1; + mnSystem = rJobSetup.mnSystem; + meOrientation = rJobSetup.meOrientation; + meDuplexMode = rJobSetup.meDuplexMode; + mnPaperBin = rJobSetup.mnPaperBin; + mePaperFormat = rJobSetup.mePaperFormat; + mnPaperWidth = rJobSetup.mnPaperWidth; + mnPaperHeight = rJobSetup.mnPaperHeight; + mnDriverDataLen = rJobSetup.mnDriverDataLen; + if ( rJobSetup.mpDriverData ) + { + mpDriverData = (sal_uInt8*)rtl_allocateMemory( mnDriverDataLen ); + memcpy( mpDriverData, rJobSetup.mpDriverData, mnDriverDataLen ); + } + else + mpDriverData = NULL; + maValueMap = rJobSetup.maValueMap; +} + +// ----------------------------------------------------------------------- + +ImplJobSetup::~ImplJobSetup() +{ + rtl_freeMemory( mpDriverData ); +} + +// ======================================================================= + +ImplJobSetup* JobSetup::ImplGetData() +{ + if ( !mpData ) + mpData = new ImplJobSetup; + else if ( mpData->mnRefCount != 1 ) + { + mpData->mnRefCount--; + mpData = new ImplJobSetup( *mpData ); + } + + return mpData; +} + +// ----------------------------------------------------------------------- + +ImplJobSetup* JobSetup::ImplGetConstData() +{ + if ( !mpData ) + mpData = new ImplJobSetup; + return mpData; +} + +// ----------------------------------------------------------------------- + +const ImplJobSetup* JobSetup::ImplGetConstData() const +{ + if ( !mpData ) + ((JobSetup*)this)->mpData = new ImplJobSetup; + return mpData; +} + +// ======================================================================= + +JobSetup::JobSetup() +{ + DBG_CTOR( JobSetup, NULL ); + + mpData = NULL; +} + +// ----------------------------------------------------------------------- + +JobSetup::JobSetup( const JobSetup& rJobSetup ) +{ + DBG_CTOR( JobSetup, NULL ); + DBG_CHKOBJ( &rJobSetup, JobSetup, NULL ); + DBG_ASSERT( !rJobSetup.mpData || (rJobSetup.mpData->mnRefCount < 0xFFFE), "JobSetup: RefCount overflow" ); + + mpData = rJobSetup.mpData; + if ( mpData ) + mpData->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +JobSetup::~JobSetup() +{ + DBG_DTOR( JobSetup, NULL ); + + if ( mpData ) + { + if ( mpData->mnRefCount == 1 ) + delete mpData; + else + mpData->mnRefCount--; + } +} + +// ----------------------------------------------------------------------- + +XubString JobSetup::GetPrinterName() const +{ + if ( mpData ) + return mpData->maPrinterName; + else + { + XubString aName; + return aName; + } +} + +// ----------------------------------------------------------------------- + +XubString JobSetup::GetDriverName() const +{ + if ( mpData ) + return mpData->maDriver; + else + { + XubString aDriver; + return aDriver; + } +} + +// ----------------------------------------------------------------------- + +String JobSetup::GetValue( const String& rKey ) const +{ + if( mpData ) + { + ::boost::unordered_map< ::rtl::OUString, ::rtl::OUString, ::rtl::OUStringHash >::const_iterator it; + it = mpData->maValueMap.find( rKey ); + return it != mpData->maValueMap.end() ? String( it->second ) : String(); + } + return String(); +} + +// ----------------------------------------------------------------------- + +void JobSetup::SetValue( const String& rKey, const String& rValue ) +{ + if( ! mpData ) + mpData = new ImplJobSetup(); + + mpData->maValueMap[ rKey ] = rValue; +} + +// ----------------------------------------------------------------------- + +JobSetup& JobSetup::operator=( const JobSetup& rJobSetup ) +{ + DBG_CHKTHIS( JobSetup, NULL ); + DBG_CHKOBJ( &rJobSetup, JobSetup, NULL ); + DBG_ASSERT( !rJobSetup.mpData || (rJobSetup.mpData->mnRefCount) < 0xFFFE, "JobSetup: RefCount overflow" ); + + // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann + if ( rJobSetup.mpData ) + rJobSetup.mpData->mnRefCount++; + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpData ) + { + if ( mpData->mnRefCount == 1 ) + delete mpData; + else + mpData->mnRefCount--; + } + + mpData = rJobSetup.mpData; + + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool JobSetup::operator==( const JobSetup& rJobSetup ) const +{ + DBG_CHKTHIS( JobSetup, NULL ); + DBG_CHKOBJ( &rJobSetup, JobSetup, NULL ); + + if ( mpData == rJobSetup.mpData ) + return sal_True; + + if ( !mpData || !rJobSetup.mpData ) + return sal_False; + + ImplJobSetup* pData1 = mpData; + ImplJobSetup* pData2 = rJobSetup.mpData; + if ( (pData1->mnSystem == pData2->mnSystem) && + (pData1->maPrinterName == pData2->maPrinterName) && + (pData1->maDriver == pData2->maDriver) && + (pData1->meOrientation == pData2->meOrientation) && + (pData1->meDuplexMode == pData2->meDuplexMode) && + (pData1->mnPaperBin == pData2->mnPaperBin) && + (pData1->mePaperFormat == pData2->mePaperFormat) && + (pData1->mnPaperWidth == pData2->mnPaperWidth) && + (pData1->mnPaperHeight == pData2->mnPaperHeight) && + (pData1->mnDriverDataLen == pData2->mnDriverDataLen) && + (memcmp( pData1->mpDriverData, pData2->mpDriverData, pData1->mnDriverDataLen ) == 0) && + (pData1->maValueMap == pData2->maValueMap) + ) + return sal_True; + + return sal_False; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStream, JobSetup& rJobSetup ) +{ + DBG_ASSERTWARNING( rIStream.GetVersion(), "JobSetup::>> - Solar-Version not set on rOStream" ); + + { + sal_Size nFirstPos = rIStream.Tell(); + + sal_uInt16 nLen = 0; + rIStream >> nLen; + if ( !nLen ) + return rIStream; + + sal_uInt16 nSystem = 0; + rIStream >> nSystem; + + char* pTempBuf = new char[nLen]; + rIStream.Read( pTempBuf, nLen - sizeof( nLen ) - sizeof( nSystem ) ); + if ( nLen >= sizeof(ImplOldJobSetupData)+4 ) + { + ImplOldJobSetupData* pData = (ImplOldJobSetupData*)pTempBuf; + if ( rJobSetup.mpData ) + { + if ( rJobSetup.mpData->mnRefCount == 1 ) + delete rJobSetup.mpData; + else + rJobSetup.mpData->mnRefCount--; + } + + rtl_TextEncoding aStreamEncoding = RTL_TEXTENCODING_UTF8; + if( nSystem == JOBSET_FILE364_SYSTEM ) + aStreamEncoding = rIStream.GetStreamCharSet(); + + rJobSetup.mpData = new ImplJobSetup; + ImplJobSetup* pJobData = rJobSetup.mpData; + pJobData->maPrinterName = UniString( pData->cPrinterName, aStreamEncoding ); + pJobData->maDriver = UniString( pData->cDriverName, aStreamEncoding ); + + // Sind es unsere neuen JobSetup-Daten? + if ( nSystem == JOBSET_FILE364_SYSTEM || + nSystem == JOBSET_FILE605_SYSTEM ) + { + Impl364JobSetupData* pOldJobData = (Impl364JobSetupData*)(pTempBuf + sizeof( ImplOldJobSetupData )); + sal_uInt16 nOldJobDataSize = SVBT16ToShort( pOldJobData->nSize ); + pJobData->mnSystem = SVBT16ToShort( pOldJobData->nSystem ); + pJobData->mnDriverDataLen = SVBT32ToUInt32( pOldJobData->nDriverDataLen ); + pJobData->meOrientation = (Orientation)SVBT16ToShort( pOldJobData->nOrientation ); + pJobData->meDuplexMode = DUPLEX_UNKNOWN; + pJobData->mnPaperBin = SVBT16ToShort( pOldJobData->nPaperBin ); + pJobData->mePaperFormat = (Paper)SVBT16ToShort( pOldJobData->nPaperFormat ); + pJobData->mnPaperWidth = (long)SVBT32ToUInt32( pOldJobData->nPaperWidth ); + pJobData->mnPaperHeight = (long)SVBT32ToUInt32( pOldJobData->nPaperHeight ); + if ( pJobData->mnDriverDataLen ) + { + sal_uInt8* pDriverData = ((sal_uInt8*)pOldJobData) + nOldJobDataSize; + pJobData->mpDriverData = (sal_uInt8*)rtl_allocateMemory( pJobData->mnDriverDataLen ); + memcpy( pJobData->mpDriverData, pDriverData, pJobData->mnDriverDataLen ); + } + if( nSystem == JOBSET_FILE605_SYSTEM ) + { + rIStream.Seek( nFirstPos + sizeof( ImplOldJobSetupData ) + 4 + sizeof( Impl364JobSetupData ) + pJobData->mnDriverDataLen ); + while( rIStream.Tell() < nFirstPos + nLen ) + { + String aKey, aValue; + rIStream.ReadByteString( aKey, RTL_TEXTENCODING_UTF8 ); + rIStream.ReadByteString( aValue, RTL_TEXTENCODING_UTF8 ); + if( aKey.EqualsAscii( "COMPAT_DUPLEX_MODE" ) ) + { + if( aValue.EqualsAscii( "DUPLEX_UNKNOWN" ) ) + pJobData->meDuplexMode = DUPLEX_UNKNOWN; + else if( aValue.EqualsAscii( "DUPLEX_OFF" ) ) + pJobData->meDuplexMode = DUPLEX_OFF; + else if( aValue.EqualsAscii( "DUPLEX_SHORTEDGE" ) ) + pJobData->meDuplexMode = DUPLEX_SHORTEDGE; + else if( aValue.EqualsAscii( "DUPLEX_LONGEDGE" ) ) + pJobData->meDuplexMode = DUPLEX_LONGEDGE; + } + else + pJobData->maValueMap[ aKey ] = aValue; + } + DBG_ASSERT( rIStream.Tell() == nFirstPos+nLen, "corrupted job setup" ); + // ensure correct stream position + rIStream.Seek( nFirstPos + nLen ); + } + } + } + delete[] pTempBuf; + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const JobSetup& rJobSetup ) +{ + DBG_ASSERTWARNING( rOStream.GetVersion(), "JobSetup::<< - Solar-Version not set on rOStream" ); + + // Zur Zeit haben wir noch kein neues FileFormat +// if ( rOStream.GetVersion() < JOBSET_FILEFORMAT2 ) + { + sal_uInt16 nLen = 0; + if ( !rJobSetup.mpData ) + rOStream << nLen; + else + { + sal_uInt16 nSystem = JOBSET_FILE605_SYSTEM; + + const ImplJobSetup* pJobData = rJobSetup.ImplGetConstData(); + Impl364JobSetupData aOldJobData; + sal_uInt16 nOldJobDataSize = sizeof( aOldJobData ); + ShortToSVBT16( nOldJobDataSize, aOldJobData.nSize ); + ShortToSVBT16( pJobData->mnSystem, aOldJobData.nSystem ); + UInt32ToSVBT32( pJobData->mnDriverDataLen, aOldJobData.nDriverDataLen ); + ShortToSVBT16( (sal_uInt16)(pJobData->meOrientation), aOldJobData.nOrientation ); + ShortToSVBT16( pJobData->mnPaperBin, aOldJobData.nPaperBin ); + ShortToSVBT16( (sal_uInt16)(pJobData->mePaperFormat), aOldJobData.nPaperFormat ); + UInt32ToSVBT32( (sal_uLong)(pJobData->mnPaperWidth), aOldJobData.nPaperWidth ); + UInt32ToSVBT32( (sal_uLong)(pJobData->mnPaperHeight), aOldJobData.nPaperHeight ); + + ImplOldJobSetupData aOldData; + memset( &aOldData, 0, sizeof( aOldData ) ); + ByteString aPrnByteName( rJobSetup.GetPrinterName(), RTL_TEXTENCODING_UTF8 ); + strncpy( aOldData.cPrinterName, aPrnByteName.GetBuffer(), 63 ); + ByteString aDriverByteName( rJobSetup.GetDriverName(), RTL_TEXTENCODING_UTF8 ); + strncpy( aOldData.cDriverName, aDriverByteName.GetBuffer(), 31 ); +// nLen = sizeof( aOldData ) + 4 + nOldJobDataSize + pJobData->mnDriverDataLen; + int nPos = rOStream.Tell(); + rOStream << nLen; + rOStream << nSystem; + rOStream.Write( (char*)&aOldData, sizeof( aOldData ) ); + rOStream.Write( (char*)&aOldJobData, nOldJobDataSize ); + rOStream.Write( (char*)pJobData->mpDriverData, pJobData->mnDriverDataLen ); + ::boost::unordered_map< ::rtl::OUString, ::rtl::OUString, ::rtl::OUStringHash >::const_iterator it; + for( it = pJobData->maValueMap.begin(); it != pJobData->maValueMap.end(); ++it ) + { + rOStream.WriteByteString( it->first, RTL_TEXTENCODING_UTF8 ); + rOStream.WriteByteString( it->second, RTL_TEXTENCODING_UTF8 ); + } + rOStream.WriteByteString( "COMPAT_DUPLEX_MODE" ) ; + switch( pJobData->meDuplexMode ) + { + case DUPLEX_UNKNOWN: rOStream.WriteByteString( "DUPLEX_UNKNOWN" );break; + case DUPLEX_OFF: rOStream.WriteByteString( "DUPLEX_OFF" );break; + case DUPLEX_SHORTEDGE: rOStream.WriteByteString( "DUPLEX_SHORTEDGE" );break; + case DUPLEX_LONGEDGE: rOStream.WriteByteString( "DUPLEX_LONGEDGE" );break; + } + nLen = sal::static_int_cast<sal_uInt16>(rOStream.Tell() - nPos); + rOStream.Seek( nPos ); + rOStream << nLen; + rOStream.Seek( nPos + nLen ); + } + } +/* + else + { + } +*/ + + return rOStream; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/lineinfo.cxx b/vcl/source/gdi/lineinfo.cxx new file mode 100644 index 000000000000..542c3103eb27 --- /dev/null +++ b/vcl/source/gdi/lineinfo.cxx @@ -0,0 +1,372 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <tools/debug.hxx> +#include <vcl/lineinfo.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dlinegeometry.hxx> +#include <numeric> + +DBG_NAME( LineInfo ) + +// ---------------- +// - ImplLineInfo - +// ---------------- + +ImplLineInfo::ImplLineInfo() : + mnRefCount ( 1 ), + meStyle ( LINE_SOLID ), + mnWidth ( 0 ), + mnDashCount ( 0 ), + mnDashLen ( 0 ), + mnDotCount ( 0 ), + mnDotLen ( 0 ), + mnDistance ( 0 ), + meLineJoin ( basegfx::B2DLINEJOIN_ROUND ) +{ +} + +// ----------------------------------------------------------------------- + +ImplLineInfo::ImplLineInfo( const ImplLineInfo& rImplLineInfo ) : + mnRefCount ( 1 ), + meStyle ( rImplLineInfo.meStyle ), + mnWidth ( rImplLineInfo.mnWidth ), + mnDashCount ( rImplLineInfo.mnDashCount ), + mnDashLen ( rImplLineInfo.mnDashLen ), + mnDotCount ( rImplLineInfo.mnDotCount ), + mnDotLen ( rImplLineInfo.mnDotLen ), + mnDistance ( rImplLineInfo.mnDistance ), + meLineJoin ( rImplLineInfo.meLineJoin ) +{ +} + +// ----------------------------------------------------------------------- + +inline bool ImplLineInfo::operator==( const ImplLineInfo& rB ) const +{ + return(meStyle == rB.meStyle + && mnWidth == rB.mnWidth + && mnDashCount == rB.mnDashCount + && mnDashLen == rB.mnDashLen + && mnDotCount == rB.mnDotCount + && mnDotLen == rB.mnDotLen + && mnDistance == rB.mnDistance + && meLineJoin == rB.meLineJoin); +} + +// ------------ +// - LineInfo - +// ------------ + +LineInfo::LineInfo( LineStyle eStyle, long nWidth ) +{ + DBG_CTOR( LineInfo, NULL ); + mpImplLineInfo = new ImplLineInfo; + mpImplLineInfo->meStyle = eStyle; + mpImplLineInfo->mnWidth = nWidth; +} + +// ----------------------------------------------------------------------- + +LineInfo::LineInfo( const LineInfo& rLineInfo ) +{ + DBG_CTOR( LineInfo, NULL ); + DBG_CHKOBJ( &rLineInfo, LineInfo, NULL ); + mpImplLineInfo = rLineInfo.mpImplLineInfo; + mpImplLineInfo->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +LineInfo::~LineInfo() +{ + DBG_DTOR( LineInfo, NULL ); + if( !( --mpImplLineInfo->mnRefCount ) ) + delete mpImplLineInfo; +} + +// ----------------------------------------------------------------------- + +LineInfo& LineInfo::operator=( const LineInfo& rLineInfo ) +{ + DBG_CHKTHIS( LineInfo, NULL ); + DBG_CHKOBJ( &rLineInfo, LineInfo, NULL ); + + rLineInfo.mpImplLineInfo->mnRefCount++; + + if( !( --mpImplLineInfo->mnRefCount ) ) + delete mpImplLineInfo; + + mpImplLineInfo = rLineInfo.mpImplLineInfo; + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool LineInfo::operator==( const LineInfo& rLineInfo ) const +{ + DBG_CHKTHIS( LineInfo, NULL ); + DBG_CHKOBJ( &rLineInfo, LineInfo, NULL ); + + return( mpImplLineInfo == rLineInfo.mpImplLineInfo || + *mpImplLineInfo == *rLineInfo.mpImplLineInfo ); +} + +// ----------------------------------------------------------------------- + +void LineInfo::ImplMakeUnique() +{ + if( mpImplLineInfo->mnRefCount != 1 ) + { + if( mpImplLineInfo->mnRefCount ) + mpImplLineInfo->mnRefCount--; + + mpImplLineInfo = new ImplLineInfo( *mpImplLineInfo ); + } +} + +// ----------------------------------------------------------------------- + +void LineInfo::SetStyle( LineStyle eStyle ) +{ + DBG_CHKTHIS( LineInfo, NULL ); + ImplMakeUnique(); + mpImplLineInfo->meStyle = eStyle; +} + +// ----------------------------------------------------------------------- + +void LineInfo::SetWidth( long nWidth ) +{ + DBG_CHKTHIS( LineInfo, NULL ); + ImplMakeUnique(); + mpImplLineInfo->mnWidth = nWidth; +} + +// ----------------------------------------------------------------------- + +void LineInfo::SetDashCount( sal_uInt16 nDashCount ) +{ + DBG_CHKTHIS( LineInfo, NULL ); + ImplMakeUnique(); + mpImplLineInfo->mnDashCount = nDashCount; +} + +// ----------------------------------------------------------------------- + +void LineInfo::SetDashLen( long nDashLen ) +{ + DBG_CHKTHIS( LineInfo, NULL ); + ImplMakeUnique(); + mpImplLineInfo->mnDashLen = nDashLen; +} + +// ----------------------------------------------------------------------- + +void LineInfo::SetDotCount( sal_uInt16 nDotCount ) +{ + DBG_CHKTHIS( LineInfo, NULL ); + ImplMakeUnique(); + mpImplLineInfo->mnDotCount = nDotCount; +} + +// ----------------------------------------------------------------------- + +void LineInfo::SetDotLen( long nDotLen ) +{ + DBG_CHKTHIS( LineInfo, NULL ); + ImplMakeUnique(); + mpImplLineInfo->mnDotLen = nDotLen; +} + +// ----------------------------------------------------------------------- + +void LineInfo::SetDistance( long nDistance ) +{ + DBG_CHKTHIS( LineInfo, NULL ); + ImplMakeUnique(); + mpImplLineInfo->mnDistance = nDistance; +} + +// ----------------------------------------------------------------------- + +void LineInfo::SetLineJoin(basegfx::B2DLineJoin eLineJoin) +{ + DBG_CHKTHIS( LineInfo, NULL ); + + if(eLineJoin != mpImplLineInfo->meLineJoin) + { + ImplMakeUnique(); + mpImplLineInfo->meLineJoin = eLineJoin; + } +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, ImplLineInfo& rImplLineInfo ) +{ + VersionCompat aCompat( rIStm, STREAM_READ ); + sal_uInt16 nTmp16; + + rIStm >> nTmp16; rImplLineInfo.meStyle = (LineStyle) nTmp16; + rIStm >> rImplLineInfo.mnWidth; + + if( aCompat.GetVersion() >= 2 ) + { + // version 2 + rIStm >> rImplLineInfo.mnDashCount >> rImplLineInfo.mnDashLen; + rIStm >> rImplLineInfo.mnDotCount >> rImplLineInfo.mnDotLen; + rIStm >> rImplLineInfo.mnDistance; + } + + if( aCompat.GetVersion() >= 3 ) + { + // version 3 + rIStm >> nTmp16; rImplLineInfo.meLineJoin = (basegfx::B2DLineJoin) nTmp16; + } + + return rIStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const ImplLineInfo& rImplLineInfo ) +{ + VersionCompat aCompat( rOStm, STREAM_WRITE, 3 ); + + // version 1 + rOStm << (sal_uInt16) rImplLineInfo.meStyle << rImplLineInfo.mnWidth; + + // since version2 + rOStm << rImplLineInfo.mnDashCount << rImplLineInfo.mnDashLen; + rOStm << rImplLineInfo.mnDotCount << rImplLineInfo.mnDotLen; + rOStm << rImplLineInfo.mnDistance; + + // since version3 + rOStm << (sal_uInt16) rImplLineInfo.meLineJoin; + + return rOStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, LineInfo& rLineInfo ) +{ + rLineInfo.ImplMakeUnique(); + return( rIStm >> *rLineInfo.mpImplLineInfo ); +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const LineInfo& rLineInfo ) +{ + return( rOStm << *rLineInfo.mpImplLineInfo ); +} + +// ----------------------------------------------------------------------- + +bool LineInfo::isDashDotOrFatLineUsed() const +{ + return (LINE_DASH == GetStyle() || GetWidth() > 1); +} + +// ----------------------------------------------------------------------- + +void LineInfo::applyToB2DPolyPolygon( + basegfx::B2DPolyPolygon& io_rLinePolyPolygon, + basegfx::B2DPolyPolygon& o_rFillPolyPolygon) const +{ + o_rFillPolyPolygon.clear(); + + if(io_rLinePolyPolygon.count()) + { + if(LINE_DASH == GetStyle()) + { + ::std::vector< double > fDotDashArray; + const double fDashLen(GetDashLen()); + const double fDotLen(GetDotLen()); + const double fDistance(GetDistance()); + + for(sal_uInt16 a(0); a < GetDashCount(); a++) + { + fDotDashArray.push_back(fDashLen); + fDotDashArray.push_back(fDistance); + } + + for(sal_uInt16 b(0); b < GetDotCount(); b++) + { + fDotDashArray.push_back(fDotLen); + fDotDashArray.push_back(fDistance); + } + + const double fAccumulated(::std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0)); + + if(fAccumulated > 0.0) + { + basegfx::B2DPolyPolygon aResult; + + for(sal_uInt32 c(0); c < io_rLinePolyPolygon.count(); c++) + { + basegfx::B2DPolyPolygon aLineTraget; + basegfx::tools::applyLineDashing( + io_rLinePolyPolygon.getB2DPolygon(c), + fDotDashArray, + &aLineTraget); + aResult.append(aLineTraget); + } + + io_rLinePolyPolygon = aResult; + } + } + + if(GetWidth() > 1 && io_rLinePolyPolygon.count()) + { + const double fHalfLineWidth((GetWidth() * 0.5) + 0.5); + + for(sal_uInt32 a(0); a < io_rLinePolyPolygon.count(); a++) + { + o_rFillPolyPolygon.append(basegfx::tools::createAreaGeometry( + io_rLinePolyPolygon.getB2DPolygon(a), + fHalfLineWidth, + GetLineJoin())); + } + + io_rLinePolyPolygon.clear(); + } + } +} + +// ----------------------------------------------------------------------- + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/makefile.mk b/vcl/source/gdi/makefile.mk new file mode 100755 index 000000000000..d01c46a77ca0 --- /dev/null +++ b/vcl/source/gdi/makefile.mk @@ -0,0 +1,125 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=vcl +TARGET=gdi + +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +.IF "$(COM)"=="ICC" +CDEFS+=-D_STD_NO_NAMESPACE -D_VOS_NO_NAMESPACE -D_UNO_NO_NAMESPACE +.ENDIF +.IF "$(ENABLE_GRAPHITE)" == "TRUE" +CDEFS+=-DENABLE_GRAPHITE +.IF "$(SYSTEM_GRAPHITE)" != "YES" +CDEFS+=-DGR2_STATIC +.ENDIF +.ENDIF + +# --- Files -------------------------------------------------------- + +EXCEPTIONSFILES= $(SLO)$/salmisc.obj \ + $(SLO)$/animate.obj \ + $(SLO)$/base14.obj \ + $(SLO)$/bitmap.obj \ + $(SLO)$/bitmap2.obj \ + $(SLO)$/bitmapex.obj \ + $(SLO)$/bmpconv.obj \ + $(SLO)$/configsettings.obj \ + $(SLO)$/cvtgrf.obj \ + $(SLO)$/cvtsvm.obj \ + $(SLO)$/gdimtf.obj \ + $(SLO)$/gfxlink.obj \ + $(SLO)$/graph.obj \ + $(SLO)$/graphictools.obj \ + $(SLO)$/image.obj \ + $(SLO)$/imagerepository.obj \ + $(SLO)$/impanmvw.obj \ + $(SLO)$/impgraph.obj \ + $(SLO)$/impimage.obj \ + $(SLO)$/impimagetree.obj \ + $(SLO)$/impvect.obj \ + $(SLO)$/jobset.obj \ + $(SLO)$/lineinfo.obj \ + $(SLO)$/metaact.obj \ + $(SLO)$/metric.obj \ + $(SLO)$/oldprintadaptor.obj \ + $(SLO)$/outdev.obj \ + $(SLO)$/outdev2.obj \ + $(SLO)$/outdev3.obj \ + $(SLO)$/outdev4.obj \ + $(SLO)$/outdev5.obj \ + $(SLO)$/outdev6.obj \ + $(SLO)$/outdevnative.obj \ + $(SLO)$/outmap.obj \ + $(SLO)$/pdfextoutdevdata.obj \ + $(SLO)$/pdffontcache.obj\ + $(SLO)$/pdfwriter.obj \ + $(SLO)$/pdfwriter_impl.obj \ + $(SLO)$/pdfwriter_impl2.obj \ + $(SLO)$/pngread.obj \ + $(SLO)$/pngwrite.obj \ + $(SLO)$/print.obj \ + $(SLO)$/print2.obj \ + $(SLO)$/print3.obj \ + $(SLO)$/salgdilayout.obj \ + $(SLO)$/sallayout.obj \ + $(SLO)$/salnativewidgets-none.obj \ + $(SLO)$/textlayout.obj \ + $(SLO)$/virdev.obj \ + $(SLO)$/wall.obj + +SLOFILES= $(EXCEPTIONSFILES) \ + $(SLO)$/bitmap3.obj \ + $(SLO)$/bitmap4.obj \ + $(SLO)$/alpha.obj \ + $(SLO)$/bmpacc.obj \ + $(SLO)$/bmpacc2.obj \ + $(SLO)$/bmpacc3.obj \ + $(SLO)$/bmpfast.obj \ + $(SLO)$/font.obj \ + $(SLO)$/gradient.obj \ + $(SLO)$/hatch.obj \ + $(SLO)$/impbmp.obj \ + $(SLO)$/mapmod.obj \ + $(SLO)$/octree.obj \ + $(SLO)$/regband.obj \ + $(SLO)$/region.obj \ + $(SLO)$/extoutdevdata.obj + + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/vcl/source/gdi/mapmod.cxx b/vcl/source/gdi/mapmod.cxx new file mode 100644 index 000000000000..ae7b13ca25b7 --- /dev/null +++ b/vcl/source/gdi/mapmod.cxx @@ -0,0 +1,314 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <tools/debug.hxx> +#include <vcl/mapmod.hxx> + +// ======================================================================= + +DBG_NAME( MapMode ) + +// ----------------------------------------------------------------------- + +ImplMapMode::ImplMapMode() : + maOrigin( 0, 0 ), + maScaleX( 1, 1 ), + maScaleY( 1, 1 ) +{ + mnRefCount = 1; + meUnit = MAP_PIXEL; + mbSimple = sal_False; +} + +// ----------------------------------------------------------------------- + +ImplMapMode::ImplMapMode( const ImplMapMode& rImplMapMode ) : + maOrigin( rImplMapMode.maOrigin ), + maScaleX( rImplMapMode.maScaleX ), + maScaleY( rImplMapMode.maScaleY ) +{ + mnRefCount = 1; + meUnit = rImplMapMode.meUnit; + mbSimple = sal_False; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, ImplMapMode& rImplMapMode ) +{ + VersionCompat aCompat( rIStm, STREAM_READ ); + sal_uInt16 nTmp16; + + rIStm >> nTmp16; rImplMapMode.meUnit = (MapUnit) nTmp16; + rIStm >> rImplMapMode.maOrigin >> rImplMapMode.maScaleX >> + rImplMapMode.maScaleY >> rImplMapMode.mbSimple; + + return rIStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const ImplMapMode& rImplMapMode ) +{ + VersionCompat aCompat( rOStm, STREAM_WRITE, 1 ); + + rOStm << (sal_uInt16) rImplMapMode.meUnit << + rImplMapMode.maOrigin << + rImplMapMode.maScaleX << + rImplMapMode.maScaleY << + rImplMapMode.mbSimple; + + return rOStm; +} + +// ----------------------------------------------------------------------- + +ImplMapMode* ImplMapMode::ImplGetStaticMapMode( MapUnit eUnit ) +{ + static long aStaticImplMapModeAry[(MAP_LASTENUMDUMMY)*sizeof(ImplMapMode)/sizeof(long)]; + + // #i19496 check for out-of-bounds + if( eUnit >= MAP_LASTENUMDUMMY ) + return (ImplMapMode*)aStaticImplMapModeAry; + + ImplMapMode* pImplMapMode = ((ImplMapMode*)aStaticImplMapModeAry)+eUnit; + if ( !pImplMapMode->mbSimple ) + { + Fraction aDefFraction( 1, 1 ); + pImplMapMode->maScaleX = aDefFraction; + pImplMapMode->maScaleY = aDefFraction; + pImplMapMode->meUnit = eUnit; + pImplMapMode->mbSimple = sal_True; + } + + return pImplMapMode; +} + +// ----------------------------------------------------------------------- + +inline void MapMode::ImplMakeUnique() +{ + // Falls noch andere Referenzen bestehen, dann kopieren + if ( mpImplMapMode->mnRefCount != 1 ) + { + if ( mpImplMapMode->mnRefCount ) + mpImplMapMode->mnRefCount--; + mpImplMapMode = new ImplMapMode( *mpImplMapMode ); + } +} + +// ----------------------------------------------------------------------- + +MapMode::MapMode() +{ + DBG_CTOR( MapMode, NULL ); + + mpImplMapMode = ImplMapMode::ImplGetStaticMapMode( MAP_PIXEL ); +} + +// ----------------------------------------------------------------------- + +MapMode::MapMode( const MapMode& rMapMode ) +{ + DBG_CTOR( MapMode, NULL ); + DBG_CHKOBJ( &rMapMode, MapMode, NULL ); + DBG_ASSERT( rMapMode.mpImplMapMode->mnRefCount < 0xFFFFFFFE, "MapMode: RefCount overflow" ); + + // shared Instance Daten uebernehmen und Referenzcounter erhoehen + mpImplMapMode = rMapMode.mpImplMapMode; + // RefCount == 0 fuer statische Objekte + if ( mpImplMapMode->mnRefCount ) + mpImplMapMode->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +MapMode::MapMode( MapUnit eUnit ) +{ + DBG_CTOR( MapMode, NULL ); + + mpImplMapMode = ImplMapMode::ImplGetStaticMapMode( eUnit ); +} + +// ----------------------------------------------------------------------- + +MapMode::MapMode( MapUnit eUnit, const Point& rLogicOrg, + const Fraction& rScaleX, const Fraction& rScaleY ) +{ + DBG_CTOR( MapMode, NULL ); + + mpImplMapMode = new ImplMapMode; + mpImplMapMode->meUnit = eUnit; + mpImplMapMode->maOrigin = rLogicOrg; + mpImplMapMode->maScaleX = rScaleX; + mpImplMapMode->maScaleY = rScaleY; +} + +// ----------------------------------------------------------------------- + +MapMode::~MapMode() +{ + DBG_DTOR( MapMode, NULL ); + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplMapMode->mnRefCount ) + { + if ( mpImplMapMode->mnRefCount == 1 ) + delete mpImplMapMode; + else + mpImplMapMode->mnRefCount--; + } +} + +// ----------------------------------------------------------------------- + +void MapMode::SetMapUnit( MapUnit eUnit ) +{ + DBG_CHKTHIS( MapMode, NULL ); + + ImplMakeUnique(); + mpImplMapMode->meUnit = eUnit; +} + +// ----------------------------------------------------------------------- + +void MapMode::SetOrigin( const Point& rLogicOrg ) +{ + DBG_CHKTHIS( MapMode, NULL ); + + ImplMakeUnique(); + mpImplMapMode->maOrigin = rLogicOrg; +} + +// ----------------------------------------------------------------------- + +void MapMode::SetScaleX( const Fraction& rScaleX ) +{ + DBG_CHKTHIS( MapMode, NULL ); + + ImplMakeUnique(); + mpImplMapMode->maScaleX = rScaleX; +} + +// ----------------------------------------------------------------------- + +void MapMode::SetScaleY( const Fraction& rScaleY ) +{ + DBG_CHKTHIS( MapMode, NULL ); + + ImplMakeUnique(); + mpImplMapMode->maScaleY = rScaleY; +} + +// ----------------------------------------------------------------------- + +MapMode& MapMode::operator=( const MapMode& rMapMode ) +{ + DBG_CHKTHIS( MapMode, NULL ); + DBG_CHKOBJ( &rMapMode, MapMode, NULL ); + DBG_ASSERT( rMapMode.mpImplMapMode->mnRefCount < 0xFFFFFFFE, "MapMode: RefCount overflow" ); + + // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann + // RefCount == 0 fuer statische Objekte + if ( rMapMode.mpImplMapMode->mnRefCount ) + rMapMode.mpImplMapMode->mnRefCount++; + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplMapMode->mnRefCount ) + { + if ( mpImplMapMode->mnRefCount == 1 ) + delete mpImplMapMode; + else + mpImplMapMode->mnRefCount--; + } + + mpImplMapMode = rMapMode.mpImplMapMode; + + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool MapMode::operator==( const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( MapMode, NULL ); + DBG_CHKOBJ( &rMapMode, MapMode, NULL ); + + if ( mpImplMapMode == rMapMode.mpImplMapMode ) + return sal_True; + + if ( (mpImplMapMode->meUnit == rMapMode.mpImplMapMode->meUnit) && + (mpImplMapMode->maOrigin == rMapMode.mpImplMapMode->maOrigin) && + (mpImplMapMode->maScaleX == rMapMode.mpImplMapMode->maScaleX) && + (mpImplMapMode->maScaleY == rMapMode.mpImplMapMode->maScaleY) ) + return sal_True; + else + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool MapMode::IsDefault() const +{ + DBG_CHKTHIS( MapMode, NULL ); + + ImplMapMode* pDefMapMode = ImplMapMode::ImplGetStaticMapMode( MAP_PIXEL ); + if ( mpImplMapMode == pDefMapMode ) + return sal_True; + + if ( (mpImplMapMode->meUnit == pDefMapMode->meUnit) && + (mpImplMapMode->maOrigin == pDefMapMode->maOrigin) && + (mpImplMapMode->maScaleX == pDefMapMode->maScaleX) && + (mpImplMapMode->maScaleY == pDefMapMode->maScaleY) ) + return sal_True; + else + return sal_False; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, MapMode& rMapMode ) +{ + rMapMode.ImplMakeUnique(); + return (rIStm >> *rMapMode.mpImplMapMode); +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const MapMode& rMapMode ) +{ + return (rOStm << *rMapMode.mpImplMapMode); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/metaact.cxx b/vcl/source/gdi/metaact.cxx new file mode 100644 index 000000000000..feba437c7c2f --- /dev/null +++ b/vcl/source/gdi/metaact.cxx @@ -0,0 +1,4326 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#define ENABLE_BYTESTRING_STREAM_OPERATORS + +#include <algorithm> +#include <string.h> +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <vcl/outdev.hxx> +#include <vcl/salbtype.hxx> +#include <vcl/metaact.hxx> +#include <vcl/graphictools.hxx> + +// ======================================================================== + +inline void ImplScalePoint( Point& rPt, double fScaleX, double fScaleY ) +{ + rPt.X() = FRound( fScaleX * rPt.X() ); + rPt.Y() = FRound( fScaleY * rPt.Y() ); +} + +// ------------------------------------------------------------------------ + +inline void ImplScaleRect( Rectangle& rRect, double fScaleX, double fScaleY ) +{ + Point aTL( rRect.TopLeft() ); + Point aBR( rRect.BottomRight() ); + + ImplScalePoint( aTL, fScaleX, fScaleY ); + ImplScalePoint( aBR, fScaleX, fScaleY ); + + rRect = Rectangle( aTL, aBR ); + rRect.Justify(); +} + +// ------------------------------------------------------------------------ + +inline void ImplScalePoly( Polygon& rPoly, double fScaleX, double fScaleY ) +{ + for( sal_uInt16 i = 0, nCount = rPoly.GetSize(); i < nCount; i++ ) + ImplScalePoint( rPoly[ i ], fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +inline void ImplScaleLineInfo( LineInfo& rLineInfo, double fScaleX, double fScaleY ) +{ + if( !rLineInfo.IsDefault() ) + { + const double fScale = ( fabs(fScaleX) + fabs(fScaleY) ) * 0.5; + + rLineInfo.SetWidth( FRound( fScale * rLineInfo.GetWidth() ) ); + rLineInfo.SetDashLen( FRound( fScale * rLineInfo.GetDashLen() ) ); + rLineInfo.SetDotLen( FRound( fScale * rLineInfo.GetDotLen() ) ); + rLineInfo.SetDistance( FRound( fScale * rLineInfo.GetDistance() ) ); + } +} + +// ======================================================================== + +#define COMPAT( _def_rIStm ) VersionCompat aCompat( ( _def_rIStm ), STREAM_READ ); +#define COMPAT_VERSION() aCompat.GetVersion() +#define WRITE_BASE_COMPAT( _def_rOStm, _def_nVer, _pWriteData ) \ + MetaAction::Write( ( _def_rOStm ), _pWriteData ); \ + VersionCompat aCompat( ( _def_rOStm ), STREAM_WRITE, ( _def_nVer ) ); + +// ======================================================================== + +MetaAction::MetaAction() : + mnRefCount( 1 ), + mnType( META_NULL_ACTION ) +{ +} + +// ------------------------------------------------------------------------ + +MetaAction::MetaAction( sal_uInt16 nType ) : + mnRefCount( 1 ), + mnType( nType ) +{ +} + +// ------------------------------------------------------------------------ + +MetaAction::~MetaAction() +{ +} + +// ------------------------------------------------------------------------ + +void MetaAction::Execute( OutputDevice* ) +{ +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaAction::Clone() +{ + return new MetaAction; +} + +// ------------------------------------------------------------------------ + +void MetaAction::Move( long, long ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaAction::Scale( double, double ) +{ +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaAction::Compare( const MetaAction& ) const +{ + return sal_True; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaAction::IsEqual( const MetaAction& rMetaAction ) const +{ + if ( mnType != rMetaAction.mnType ) + return sal_False; + else + return Compare( rMetaAction ); +} + +// ------------------------------------------------------------------------ + +void MetaAction::Write( SvStream& rOStm, ImplMetaWriteData* ) +{ + rOStm << mnType; +} + +// ------------------------------------------------------------------------ + +void MetaAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + rIStm >> mnType; +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaAction::ReadMetaAction( SvStream& rIStm, ImplMetaReadData* pData ) +{ + MetaAction* pAction = NULL; + sal_uInt16 nType; + + rIStm >> nType; + + switch( nType ) + { + case( META_NULL_ACTION ): pAction = new MetaAction; break; + case( META_PIXEL_ACTION ): pAction = new MetaPixelAction; break; + case( META_POINT_ACTION ): pAction = new MetaPointAction; break; + case( META_LINE_ACTION ): pAction = new MetaLineAction; break; + case( META_RECT_ACTION ): pAction = new MetaRectAction; break; + case( META_ROUNDRECT_ACTION ): pAction = new MetaRoundRectAction; break; + case( META_ELLIPSE_ACTION ): pAction = new MetaEllipseAction; break; + case( META_ARC_ACTION ): pAction = new MetaArcAction; break; + case( META_PIE_ACTION ): pAction = new MetaPieAction; break; + case( META_CHORD_ACTION ): pAction = new MetaChordAction; break; + case( META_POLYLINE_ACTION ): pAction = new MetaPolyLineAction; break; + case( META_POLYGON_ACTION ): pAction = new MetaPolygonAction; break; + case( META_POLYPOLYGON_ACTION ): pAction = new MetaPolyPolygonAction; break; + case( META_TEXT_ACTION ): pAction = new MetaTextAction; break; + case( META_TEXTARRAY_ACTION ): pAction = new MetaTextArrayAction; break; + case( META_STRETCHTEXT_ACTION ): pAction = new MetaStretchTextAction; break; + case( META_TEXTRECT_ACTION ): pAction = new MetaTextRectAction; break; + case( META_TEXTLINE_ACTION ): pAction = new MetaTextLineAction; break; + case( META_BMP_ACTION ): pAction = new MetaBmpAction; break; + case( META_BMPSCALE_ACTION ): pAction = new MetaBmpScaleAction; break; + case( META_BMPSCALEPART_ACTION ): pAction = new MetaBmpScalePartAction; break; + case( META_BMPEX_ACTION ): pAction = new MetaBmpExAction; break; + case( META_BMPEXSCALE_ACTION ): pAction = new MetaBmpExScaleAction; break; + case( META_BMPEXSCALEPART_ACTION ): pAction = new MetaBmpExScalePartAction; break; + case( META_MASK_ACTION ): pAction = new MetaMaskAction; break; + case( META_MASKSCALE_ACTION ): pAction = new MetaMaskScaleAction; break; + case( META_MASKSCALEPART_ACTION ): pAction = new MetaMaskScalePartAction; break; + case( META_GRADIENT_ACTION ): pAction = new MetaGradientAction; break; + case( META_GRADIENTEX_ACTION ): pAction = new MetaGradientExAction; break; + case( META_HATCH_ACTION ): pAction = new MetaHatchAction; break; + case( META_WALLPAPER_ACTION ): pAction = new MetaWallpaperAction; break; + case( META_CLIPREGION_ACTION ): pAction = new MetaClipRegionAction; break; + case( META_ISECTRECTCLIPREGION_ACTION ): pAction = new MetaISectRectClipRegionAction; break; + case( META_ISECTREGIONCLIPREGION_ACTION ): pAction = new MetaISectRegionClipRegionAction; break; + case( META_MOVECLIPREGION_ACTION ): pAction = new MetaMoveClipRegionAction; break; + case( META_LINECOLOR_ACTION ): pAction = new MetaLineColorAction; break; + case( META_FILLCOLOR_ACTION ): pAction = new MetaFillColorAction; break; + case( META_TEXTCOLOR_ACTION ): pAction = new MetaTextColorAction; break; + case( META_TEXTFILLCOLOR_ACTION ): pAction = new MetaTextFillColorAction; break; + case( META_TEXTLINECOLOR_ACTION ): pAction = new MetaTextLineColorAction; break; + case( META_OVERLINECOLOR_ACTION ): pAction = new MetaOverlineColorAction; break; + case( META_TEXTALIGN_ACTION ): pAction = new MetaTextAlignAction; break; + case( META_MAPMODE_ACTION ): pAction = new MetaMapModeAction; break; + case( META_FONT_ACTION ): pAction = new MetaFontAction; break; + case( META_PUSH_ACTION ): pAction = new MetaPushAction; break; + case( META_POP_ACTION ): pAction = new MetaPopAction; break; + case( META_RASTEROP_ACTION ): pAction = new MetaRasterOpAction; break; + case( META_TRANSPARENT_ACTION ): pAction = new MetaTransparentAction; break; + case( META_FLOATTRANSPARENT_ACTION ): pAction = new MetaFloatTransparentAction; break; + case( META_EPS_ACTION ): pAction = new MetaEPSAction; break; + case( META_REFPOINT_ACTION ): pAction = new MetaRefPointAction; break; + case( META_COMMENT_ACTION ): pAction = new MetaCommentAction; break; + case( META_LAYOUTMODE_ACTION ): pAction = new MetaLayoutModeAction; break; + case( META_TEXTLANGUAGE_ACTION ): pAction = new MetaTextLanguageAction; break; + + default: + { + // Action ueberlesen durch Kombination Ctor/Dtor, + // new/delete, weil Compiler sonst vielleicht wegoptimieren + delete ( new VersionCompat( rIStm, STREAM_READ ) ); + } + break; + } + + if( pAction ) + pAction->Read( rIStm, pData ); + + return pAction; +} + +// ======================================================================== + +IMPL_META_ACTION( Pixel, META_PIXEL_ACTION ) + +// ------------------------------------------------------------------------ + +MetaPixelAction::MetaPixelAction( const Point& rPt, const Color& rColor ) : + MetaAction ( META_PIXEL_ACTION ), + maPt ( rPt ), + maColor ( rColor ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaPixelAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawPixel( maPt, maColor ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaPixelAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaPixelAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaPixelAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaPixelAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maPt, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaPixelAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maPt == ((MetaPixelAction&)rMetaAction).maPt ) && + ( maColor == ((MetaPixelAction&)rMetaAction).maColor ); +} + +// ------------------------------------------------------------------------ + +void MetaPixelAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maPt; + maColor.Write( rOStm, sal_True ); +} + +// ------------------------------------------------------------------------ + +void MetaPixelAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maPt; + maColor.Read( rIStm, sal_True ); +} + +// ======================================================================== + +IMPL_META_ACTION( Point, META_POINT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaPointAction::MetaPointAction( const Point& rPt ) : + MetaAction ( META_POINT_ACTION ), + maPt ( rPt ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaPointAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawPixel( maPt ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaPointAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaPointAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaPointAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaPointAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maPt, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaPointAction::Compare( const MetaAction& rMetaAction ) const +{ + return maPt == ((MetaPointAction&)rMetaAction).maPt; +} + +// ------------------------------------------------------------------------ + +void MetaPointAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maPt; +} + +// ------------------------------------------------------------------------ + +void MetaPointAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maPt; +} + +// ======================================================================== + +IMPL_META_ACTION( Line, META_LINE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaLineAction::MetaLineAction( const Point& rStart, const Point& rEnd ) : + MetaAction ( META_LINE_ACTION ), + maStartPt ( rStart ), + maEndPt ( rEnd ) +{ +} + +// ------------------------------------------------------------------------ + +MetaLineAction::MetaLineAction( const Point& rStart, const Point& rEnd, + const LineInfo& rLineInfo ) : + MetaAction ( META_LINE_ACTION ), + maLineInfo ( rLineInfo ), + maStartPt ( rStart ), + maEndPt ( rEnd ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaLineAction::Execute( OutputDevice* pOut ) +{ + if( maLineInfo.IsDefault() ) + pOut->DrawLine( maStartPt, maEndPt ); + else + pOut->DrawLine( maStartPt, maEndPt, maLineInfo ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaLineAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaLineAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaLineAction::Move( long nHorzMove, long nVertMove ) +{ + maStartPt.Move( nHorzMove, nVertMove ); + maEndPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaLineAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maStartPt, fScaleX, fScaleY ); + ImplScalePoint( maEndPt, fScaleX, fScaleY ); + ImplScaleLineInfo( maLineInfo, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaLineAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maLineInfo == ((MetaLineAction&)rMetaAction).maLineInfo ) && + ( maStartPt == ((MetaLineAction&)rMetaAction).maStartPt ) && + ( maEndPt == ((MetaLineAction&)rMetaAction).maEndPt ); +} + +// ------------------------------------------------------------------------ + +void MetaLineAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 2, pData ); + + rOStm << maStartPt << maEndPt; // Version 1 + rOStm << maLineInfo; // Version 2 +} + +// ------------------------------------------------------------------------ + +void MetaLineAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + + // Version 1 + rIStm >> maStartPt >> maEndPt; + + // Version 2 + if( aCompat.GetVersion() >= 2 ) + { + rIStm >> maLineInfo; + } +} + +// ======================================================================== + +IMPL_META_ACTION( Rect, META_RECT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaRectAction::MetaRectAction( const Rectangle& rRect ) : + MetaAction ( META_RECT_ACTION ), + maRect ( rRect ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaRectAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawRect( maRect ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaRectAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaRectAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaRectAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaRectAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaRectAction::Compare( const MetaAction& rMetaAction ) const +{ + return maRect == ((MetaRectAction&)rMetaAction).maRect; +} + +// ------------------------------------------------------------------------ + +void MetaRectAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRect; +} + +// ------------------------------------------------------------------------ + +void MetaRectAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRect; +} + +// ======================================================================== + +IMPL_META_ACTION( RoundRect, META_ROUNDRECT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaRoundRectAction::MetaRoundRectAction( const Rectangle& rRect, + sal_uInt32 nHorzRound, sal_uInt32 nVertRound ) : + MetaAction ( META_ROUNDRECT_ACTION ), + maRect ( rRect ), + mnHorzRound ( nHorzRound ), + mnVertRound ( nVertRound ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaRoundRectAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawRect( maRect, mnHorzRound, mnVertRound ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaRoundRectAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaRoundRectAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaRoundRectAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaRoundRectAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); + mnHorzRound = FRound( mnHorzRound * fabs(fScaleX) ); + mnVertRound = FRound( mnVertRound * fabs(fScaleY) ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaRoundRectAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maRect == ((MetaRoundRectAction&)rMetaAction).maRect ) && + ( mnHorzRound == ((MetaRoundRectAction&)rMetaAction).mnHorzRound ) && + ( mnVertRound == ((MetaRoundRectAction&)rMetaAction).mnVertRound ); +} + +// ------------------------------------------------------------------------ + +void MetaRoundRectAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRect << mnHorzRound << mnVertRound; +} + +// ------------------------------------------------------------------------ + +void MetaRoundRectAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRect >> mnHorzRound >> mnVertRound; +} + +// ======================================================================== + +IMPL_META_ACTION( Ellipse, META_ELLIPSE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaEllipseAction::MetaEllipseAction( const Rectangle& rRect ) : + MetaAction ( META_ELLIPSE_ACTION ), + maRect ( rRect ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaEllipseAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawEllipse( maRect ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaEllipseAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaEllipseAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaEllipseAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaEllipseAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaEllipseAction::Compare( const MetaAction& rMetaAction ) const +{ + return maRect == ((MetaEllipseAction&)rMetaAction).maRect; +} + +// ------------------------------------------------------------------------ + +void MetaEllipseAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRect; +} + +// ------------------------------------------------------------------------ + +void MetaEllipseAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRect; +} + +// ======================================================================== + +IMPL_META_ACTION( Arc, META_ARC_ACTION ) + +// ------------------------------------------------------------------------ + +MetaArcAction::MetaArcAction( const Rectangle& rRect, + const Point& rStart, const Point& rEnd ) : + MetaAction ( META_ARC_ACTION ), + maRect ( rRect ), + maStartPt ( rStart ), + maEndPt ( rEnd ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaArcAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawArc( maRect, maStartPt, maEndPt ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaArcAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaArcAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaArcAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); + maStartPt.Move( nHorzMove, nVertMove ); + maEndPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaArcAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); + ImplScalePoint( maStartPt, fScaleX, fScaleY ); + ImplScalePoint( maEndPt, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaArcAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maRect == ((MetaArcAction&)rMetaAction).maRect ) && + ( maStartPt == ((MetaArcAction&)rMetaAction).maStartPt ) && + ( maEndPt == ((MetaArcAction&)rMetaAction).maEndPt ); +} + +// ------------------------------------------------------------------------ + +void MetaArcAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRect << maStartPt << maEndPt; +} + +// ------------------------------------------------------------------------ + +void MetaArcAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRect >> maStartPt >> maEndPt; +} + +// ======================================================================== + +IMPL_META_ACTION( Pie, META_PIE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaPieAction::MetaPieAction( const Rectangle& rRect, + const Point& rStart, const Point& rEnd ) : + MetaAction ( META_PIE_ACTION ), + maRect ( rRect ), + maStartPt ( rStart ), + maEndPt ( rEnd ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaPieAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawPie( maRect, maStartPt, maEndPt ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaPieAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaPieAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaPieAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); + maStartPt.Move( nHorzMove, nVertMove ); + maEndPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaPieAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); + ImplScalePoint( maStartPt, fScaleX, fScaleY ); + ImplScalePoint( maEndPt, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaPieAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maRect == ((MetaPieAction&)rMetaAction).maRect ) && + ( maStartPt == ((MetaPieAction&)rMetaAction).maStartPt ) && + ( maEndPt == ((MetaPieAction&)rMetaAction).maEndPt ); +} + +// ------------------------------------------------------------------------ + +void MetaPieAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRect << maStartPt << maEndPt; +} + +// ------------------------------------------------------------------------ + +void MetaPieAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRect >> maStartPt >> maEndPt; +} + +// ======================================================================== + +IMPL_META_ACTION( Chord, META_CHORD_ACTION ) + +// ------------------------------------------------------------------------ + +MetaChordAction::MetaChordAction( const Rectangle& rRect, + const Point& rStart, const Point& rEnd ) : + MetaAction ( META_CHORD_ACTION ), + maRect ( rRect ), + maStartPt ( rStart ), + maEndPt ( rEnd ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaChordAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawChord( maRect, maStartPt, maEndPt ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaChordAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaChordAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaChordAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); + maStartPt.Move( nHorzMove, nVertMove ); + maEndPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaChordAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); + ImplScalePoint( maStartPt, fScaleX, fScaleY ); + ImplScalePoint( maEndPt, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaChordAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maRect == ((MetaChordAction&)rMetaAction).maRect ) && + ( maStartPt == ((MetaChordAction&)rMetaAction).maStartPt ) && + ( maEndPt == ((MetaChordAction&)rMetaAction).maEndPt ); +} + +// ------------------------------------------------------------------------ + +void MetaChordAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRect << maStartPt << maEndPt; +} + +// ------------------------------------------------------------------------ + +void MetaChordAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRect >> maStartPt >> maEndPt; +} + +// ======================================================================== + +IMPL_META_ACTION( PolyLine, META_POLYLINE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaPolyLineAction::MetaPolyLineAction( const Polygon& rPoly ) : + MetaAction ( META_POLYLINE_ACTION ), + maPoly ( rPoly ) +{ +} + +// ------------------------------------------------------------------------ + +MetaPolyLineAction::MetaPolyLineAction( const Polygon& rPoly, const LineInfo& rLineInfo ) : + MetaAction ( META_POLYLINE_ACTION ), + maLineInfo ( rLineInfo ), + maPoly ( rPoly ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaPolyLineAction::Execute( OutputDevice* pOut ) +{ + if( maLineInfo.IsDefault() ) + pOut->DrawPolyLine( maPoly ); + else + pOut->DrawPolyLine( maPoly, maLineInfo ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaPolyLineAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaPolyLineAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaPolyLineAction::Move( long nHorzMove, long nVertMove ) +{ + maPoly.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaPolyLineAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoly( maPoly, fScaleX, fScaleY ); + ImplScaleLineInfo( maLineInfo, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaPolyLineAction::Compare( const MetaAction& rMetaAction ) const +{ + sal_Bool bIsEqual = sal_True;; + if ( maLineInfo != ((MetaPolyLineAction&)rMetaAction).maLineInfo ) + bIsEqual = sal_False; + else + bIsEqual = maPoly.IsEqual(((MetaPolyLineAction&)rMetaAction).maPoly ); + return bIsEqual; + +} + +// ------------------------------------------------------------------------ + +void MetaPolyLineAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 3, pData ); + + Polygon aSimplePoly; + maPoly.AdaptiveSubdivide( aSimplePoly ); + + rOStm << aSimplePoly; // Version 1 + rOStm << maLineInfo; // Version 2 + + sal_uInt8 bHasPolyFlags = maPoly.HasFlags(); // Version 3 + rOStm << bHasPolyFlags; + if ( bHasPolyFlags ) + maPoly.Write( rOStm ); +} + +// ------------------------------------------------------------------------ + +void MetaPolyLineAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + + // Version 1 + rIStm >> maPoly; + + // Version 2 + if( aCompat.GetVersion() >= 2 ) + rIStm >> maLineInfo; + if ( aCompat.GetVersion() >= 3 ) + { + sal_uInt8 bHasPolyFlags; + rIStm >> bHasPolyFlags; + if ( bHasPolyFlags ) + maPoly.Read( rIStm ); + } +} + +// ======================================================================== + +IMPL_META_ACTION( Polygon, META_POLYGON_ACTION ) + +// ------------------------------------------------------------------------ + +MetaPolygonAction::MetaPolygonAction( const Polygon& rPoly ) : + MetaAction ( META_POLYGON_ACTION ), + maPoly ( rPoly ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaPolygonAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawPolygon( maPoly ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaPolygonAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaPolygonAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaPolygonAction::Move( long nHorzMove, long nVertMove ) +{ + maPoly.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaPolygonAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoly( maPoly, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaPolygonAction::Compare( const MetaAction& rMetaAction ) const +{ + return maPoly.IsEqual(((MetaPolygonAction&)rMetaAction).maPoly ); +} + +// ------------------------------------------------------------------------ + +void MetaPolygonAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 2, pData ); + + Polygon aSimplePoly; // Version 1 + maPoly.AdaptiveSubdivide( aSimplePoly ); + rOStm << aSimplePoly; + + sal_uInt8 bHasPolyFlags = maPoly.HasFlags(); // Version 2 + rOStm << bHasPolyFlags; + if ( bHasPolyFlags ) + maPoly.Write( rOStm ); +} + +// ------------------------------------------------------------------------ + +void MetaPolygonAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + + rIStm >> maPoly; // Version 1 + + if( aCompat.GetVersion() >= 2 ) // Version 2 + { + sal_uInt8 bHasPolyFlags; + rIStm >> bHasPolyFlags; + if ( bHasPolyFlags ) + maPoly.Read( rIStm ); + } +} + +// ======================================================================== + +IMPL_META_ACTION( PolyPolygon, META_POLYPOLYGON_ACTION ) + +// ------------------------------------------------------------------------ + +MetaPolyPolygonAction::MetaPolyPolygonAction( const PolyPolygon& rPolyPoly ) : + MetaAction ( META_POLYPOLYGON_ACTION ), + maPolyPoly ( rPolyPoly ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaPolyPolygonAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawPolyPolygon( maPolyPoly ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaPolyPolygonAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaPolyPolygonAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaPolyPolygonAction::Move( long nHorzMove, long nVertMove ) +{ + maPolyPoly.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaPolyPolygonAction::Scale( double fScaleX, double fScaleY ) +{ + for( sal_uInt16 i = 0, nCount = maPolyPoly.Count(); i < nCount; i++ ) + ImplScalePoly( maPolyPoly[ i ], fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaPolyPolygonAction::Compare( const MetaAction& rMetaAction ) const +{ + return maPolyPoly.IsEqual(((MetaPolyPolygonAction&)rMetaAction).maPolyPoly ); +} + +// ------------------------------------------------------------------------ + +void MetaPolyPolygonAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 2, pData ); + + sal_uInt16 nNumberOfComplexPolygons = 0; + sal_uInt16 i, nPolyCount = maPolyPoly.Count(); + + Polygon aSimplePoly; // Version 1 + rOStm << nPolyCount; + for ( i = 0; i < nPolyCount; i++ ) + { + const Polygon& rPoly = maPolyPoly.GetObject( i ); + if ( rPoly.HasFlags() ) + nNumberOfComplexPolygons++; + rPoly.AdaptiveSubdivide( aSimplePoly ); + rOStm << aSimplePoly; + } + + rOStm << nNumberOfComplexPolygons; // Version 2 + for ( i = 0; nNumberOfComplexPolygons && ( i < nPolyCount ); i++ ) + { + const Polygon& rPoly = maPolyPoly.GetObject( i ); + if ( rPoly.HasFlags() ) + { + rOStm << i; + rPoly.Write( rOStm ); + + nNumberOfComplexPolygons--; + } + } +} + +// ------------------------------------------------------------------------ + +void MetaPolyPolygonAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maPolyPoly; // Version 1 + + if ( aCompat.GetVersion() >= 2 ) // Version 2 + { + sal_uInt16 i, nIndex, nNumberOfComplexPolygons; + rIStm >> nNumberOfComplexPolygons; + for ( i = 0; i < nNumberOfComplexPolygons; i++ ) + { + rIStm >> nIndex; + Polygon aPoly; + aPoly.Read( rIStm ); + maPolyPoly.Replace( aPoly, nIndex ); + } + } +} + +// ======================================================================== + +IMPL_META_ACTION( Text, META_TEXT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaTextAction::MetaTextAction( const Point& rPt, const XubString& rStr, + sal_uInt16 nIndex, sal_uInt16 nLen ) : + MetaAction ( META_TEXT_ACTION ), + maPt ( rPt ), + maStr ( rStr ), + mnIndex ( nIndex ), + mnLen ( nLen ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaTextAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawText( maPt, maStr, mnIndex, mnLen ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTextAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaTextAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaTextAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaTextAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maPt, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTextAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maPt == ((MetaTextAction&)rMetaAction).maPt ) && + ( maStr == ((MetaTextAction&)rMetaAction).maStr ) && + ( mnIndex == ((MetaTextAction&)rMetaAction).mnIndex ) && + ( mnLen == ((MetaTextAction&)rMetaAction).mnLen ); +} + +// ------------------------------------------------------------------------ + +void MetaTextAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 2, pData ); + rOStm << maPt; + rOStm.WriteByteString( maStr, pData->meActualCharSet ); + rOStm << mnIndex; + rOStm << mnLen; + + sal_uInt16 i, nLen = maStr.Len(); // version 2 + rOStm << nLen; + for ( i = 0; i < nLen; i++ ) + { + sal_Unicode nUni = maStr.GetChar( i ); + rOStm << nUni; + } +} + +// ------------------------------------------------------------------------ + +void MetaTextAction::Read( SvStream& rIStm, ImplMetaReadData* pData ) +{ + COMPAT( rIStm ); + rIStm >> maPt; + rIStm.ReadByteString( maStr, pData->meActualCharSet ); + rIStm >> mnIndex; + rIStm >> mnLen; + + if ( aCompat.GetVersion() >= 2 ) // Version 2 + { + sal_uInt16 nLen; + rIStm >> nLen; + sal_Unicode* pBuffer = maStr.AllocBuffer( nLen ); + while ( nLen-- ) + rIStm >> *pBuffer++; + } +} + +// ======================================================================== + +MetaTextArrayAction::MetaTextArrayAction() : + MetaAction ( META_TEXTARRAY_ACTION ), + mpDXAry ( NULL ), + mnIndex ( 0 ), + mnLen ( 0 ) +{ +} + +// ------------------------------------------------------------------------ + +MetaTextArrayAction::MetaTextArrayAction( const MetaTextArrayAction& rAction ) : + MetaAction ( META_TEXTARRAY_ACTION ), + maStartPt ( rAction.maStartPt ), + maStr ( rAction.maStr ), + mnIndex ( rAction.mnIndex ), + mnLen ( rAction.mnLen ) +{ + if( rAction.mpDXAry ) + { + const sal_uLong nAryLen = mnLen; + + mpDXAry = new sal_Int32[ nAryLen ]; + memcpy( mpDXAry, rAction.mpDXAry, nAryLen * sizeof( sal_Int32 ) ); + } + else + mpDXAry = NULL; +} + +// ------------------------------------------------------------------------ + +MetaTextArrayAction::MetaTextArrayAction( const Point& rStartPt, + const XubString& rStr, + const sal_Int32* pDXAry, + sal_uInt16 nIndex, + sal_uInt16 nLen ) : + MetaAction ( META_TEXTARRAY_ACTION ), + maStartPt ( rStartPt ), + maStr ( rStr ), + mnIndex ( nIndex ), + mnLen ( ( nLen == STRING_LEN ) ? rStr.Len() : nLen ) +{ + const sal_uLong nAryLen = pDXAry ? mnLen : 0; + + if( nAryLen ) + { + mpDXAry = new sal_Int32[ nAryLen ]; + memcpy( mpDXAry, pDXAry, nAryLen * sizeof( sal_Int32 ) ); + } + else + mpDXAry = NULL; +} + +// ------------------------------------------------------------------------ + +MetaTextArrayAction::~MetaTextArrayAction() +{ + delete[] mpDXAry; +} + +// ------------------------------------------------------------------------ + +void MetaTextArrayAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawTextArray( maStartPt, maStr, mpDXAry, mnIndex, mnLen ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTextArrayAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaTextArrayAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaTextArrayAction::Move( long nHorzMove, long nVertMove ) +{ + maStartPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaTextArrayAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maStartPt, fScaleX, fScaleY ); + + if ( mpDXAry && mnLen ) + { + for ( sal_uInt16 i = 0, nCount = mnLen; i < nCount; i++ ) + mpDXAry[ i ] = FRound( mpDXAry[ i ] * fabs(fScaleX) ); + } +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTextArrayAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maStartPt == ((MetaTextArrayAction&)rMetaAction).maStartPt ) && + ( maStr == ((MetaTextArrayAction&)rMetaAction).maStr ) && + ( mnIndex == ((MetaTextArrayAction&)rMetaAction).mnIndex ) && + ( mnLen == ((MetaTextArrayAction&)rMetaAction).mnLen ) && + ( memcmp( mpDXAry, ((MetaTextArrayAction&)rMetaAction).mpDXAry, mnLen ) == 0 ); +} + +// ------------------------------------------------------------------------ + +void MetaTextArrayAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + const sal_uInt32 nAryLen = mpDXAry ? mnLen : 0; + + WRITE_BASE_COMPAT( rOStm, 2, pData ); + rOStm << maStartPt; + rOStm.WriteByteString( maStr, pData->meActualCharSet ); + rOStm << mnIndex; + rOStm << mnLen; + rOStm << nAryLen; + + for( sal_uLong i = 0UL; i < nAryLen; i++ ) + rOStm << mpDXAry[ i ]; + + sal_uInt16 j, nLen = maStr.Len(); // version 2 + rOStm << nLen; + for ( j = 0; j < nLen; j++ ) + { + sal_Unicode nUni = maStr.GetChar( j ); + rOStm << nUni; + } +} + +// ------------------------------------------------------------------------ + +void MetaTextArrayAction::Read( SvStream& rIStm, ImplMetaReadData* pData ) +{ + sal_uInt32 nAryLen; + + delete[] mpDXAry; + + COMPAT( rIStm ); + rIStm >> maStartPt; + rIStm.ReadByteString( maStr, pData->meActualCharSet ); + rIStm >> mnIndex; + rIStm >> mnLen; + rIStm >> nAryLen; + + if ( mnIndex + mnLen > maStr.Len() ) + { + mnIndex = 0; + mpDXAry = 0; + return; + } + + if( nAryLen ) + { + // #i9762#, #106172# Ensure that DX array is at least mnLen entries long + if ( mnLen >= nAryLen ) + { + mpDXAry = new (std::nothrow)sal_Int32[ mnLen ]; + if ( mpDXAry ) + { + sal_uLong i; + for( i = 0UL; i < nAryLen; i++ ) + rIStm >> mpDXAry[ i ]; + + // #106172# setup remainder + for( ; i < mnLen; i++ ) + mpDXAry[ i ] = 0; + } + } + else + { + mpDXAry = NULL; + return; + } + } + else + mpDXAry = NULL; + + if ( aCompat.GetVersion() >= 2 ) // Version 2 + { + sal_uInt16 nLen; + rIStm >> nLen; + sal_Unicode* pBuffer = maStr.AllocBuffer( nLen ); + while ( nLen-- ) + rIStm >> *pBuffer++; + + if ( mnIndex + mnLen > maStr.Len() ) + { + mnIndex = 0; + delete[] mpDXAry, mpDXAry = NULL; + } + } +} + +// ======================================================================== + +IMPL_META_ACTION( StretchText, META_STRETCHTEXT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaStretchTextAction::MetaStretchTextAction( const Point& rPt, sal_uInt32 nWidth, + const XubString& rStr, + sal_uInt16 nIndex, sal_uInt16 nLen ) : + MetaAction ( META_STRETCHTEXT_ACTION ), + maPt ( rPt ), + maStr ( rStr ), + mnWidth ( nWidth ), + mnIndex ( nIndex ), + mnLen ( nLen ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaStretchTextAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawStretchText( maPt, mnWidth, maStr, mnIndex, mnLen ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaStretchTextAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaStretchTextAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaStretchTextAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaStretchTextAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maPt, fScaleX, fScaleY ); + mnWidth = (sal_uLong)FRound( mnWidth * fabs(fScaleX) ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaStretchTextAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maPt == ((MetaStretchTextAction&)rMetaAction).maPt ) && + ( maStr == ((MetaStretchTextAction&)rMetaAction).maStr ) && + ( mnWidth == ((MetaStretchTextAction&)rMetaAction).mnWidth ) && + ( mnIndex == ((MetaStretchTextAction&)rMetaAction).mnIndex ) && + ( mnLen == ((MetaStretchTextAction&)rMetaAction).mnLen ); +} + +// ------------------------------------------------------------------------ + +void MetaStretchTextAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 2, pData ); + rOStm << maPt; + rOStm.WriteByteString( maStr, pData->meActualCharSet ); + rOStm << mnWidth; + rOStm << mnIndex; + rOStm << mnLen; + + sal_uInt16 i, nLen = maStr.Len(); // version 2 + rOStm << nLen; + for ( i = 0; i < nLen; i++ ) + { + sal_Unicode nUni = maStr.GetChar( i ); + rOStm << nUni; + } +} + +// ------------------------------------------------------------------------ + +void MetaStretchTextAction::Read( SvStream& rIStm, ImplMetaReadData* pData ) +{ + COMPAT( rIStm ); + rIStm >> maPt; + rIStm.ReadByteString( maStr, pData->meActualCharSet ); + rIStm >> mnWidth; + rIStm >> mnIndex; + rIStm >> mnLen; + + if ( aCompat.GetVersion() >= 2 ) // Version 2 + { + sal_uInt16 nLen; + rIStm >> nLen; + sal_Unicode* pBuffer = maStr.AllocBuffer( nLen ); + while ( nLen-- ) + rIStm >> *pBuffer++; + } +} + +// ======================================================================== + +IMPL_META_ACTION( TextRect, META_TEXTRECT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaTextRectAction::MetaTextRectAction( const Rectangle& rRect, + const XubString& rStr, sal_uInt16 nStyle ) : + MetaAction ( META_TEXTRECT_ACTION ), + maRect ( rRect ), + maStr ( rStr ), + mnStyle ( nStyle ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaTextRectAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawText( maRect, maStr, mnStyle ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTextRectAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaTextRectAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaTextRectAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaTextRectAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTextRectAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maRect == ((MetaTextRectAction&)rMetaAction).maRect ) && + ( maStr == ((MetaTextRectAction&)rMetaAction).maStr ) && + ( mnStyle == ((MetaTextRectAction&)rMetaAction).mnStyle ); +} + +// ------------------------------------------------------------------------ + +void MetaTextRectAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 2, pData ); + rOStm << maRect; + rOStm.WriteByteString( maStr, pData->meActualCharSet ); + rOStm << mnStyle; + + sal_uInt16 i, nLen = maStr.Len(); // version 2 + rOStm << nLen; + for ( i = 0; i < nLen; i++ ) + { + sal_Unicode nUni = maStr.GetChar( i ); + rOStm << nUni; + } +} + +// ------------------------------------------------------------------------ + +void MetaTextRectAction::Read( SvStream& rIStm, ImplMetaReadData* pData ) +{ + COMPAT( rIStm ); + rIStm >> maRect; + rIStm.ReadByteString( maStr, pData->meActualCharSet ); + rIStm >> mnStyle; + + if ( aCompat.GetVersion() >= 2 ) // Version 2 + { + sal_uInt16 nLen; + rIStm >> nLen; + sal_Unicode* pBuffer = maStr.AllocBuffer( nLen ); + while ( nLen-- ) + rIStm >> *pBuffer++; + } +} + +// ======================================================================== + +IMPL_META_ACTION( TextLine, META_TEXTLINE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaTextLineAction::MetaTextLineAction( const Point& rPos, long nWidth, + FontStrikeout eStrikeout, + FontUnderline eUnderline, + FontUnderline eOverline ) : + MetaAction ( META_TEXTLINE_ACTION ), + maPos ( rPos ), + mnWidth ( nWidth ), + meStrikeout ( eStrikeout ), + meUnderline ( eUnderline ), + meOverline ( eOverline ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaTextLineAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawTextLine( maPos, mnWidth, meStrikeout, meUnderline, meOverline ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTextLineAction::Clone() +{ + MetaAction* pClone = (MetaAction*)new MetaTextLineAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaTextLineAction::Move( long nHorzMove, long nVertMove ) +{ + maPos.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaTextLineAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maPos, fScaleX, fScaleY ); + mnWidth = FRound( mnWidth * fabs(fScaleX) ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTextLineAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maPos == ((MetaTextLineAction&)rMetaAction).maPos ) && + ( mnWidth == ((MetaTextLineAction&)rMetaAction).mnWidth ) && + ( meStrikeout == ((MetaTextLineAction&)rMetaAction).meStrikeout ) && + ( meUnderline == ((MetaTextLineAction&)rMetaAction).meUnderline ) && + ( meOverline == ((MetaTextLineAction&)rMetaAction).meOverline ); +} + +// ------------------------------------------------------------------------ + +void MetaTextLineAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 2, pData ); + + rOStm << maPos; + rOStm << mnWidth; + rOStm << static_cast<sal_uInt32>(meStrikeout); + rOStm << static_cast<sal_uInt32>(meUnderline); + // new in version 2 + rOStm << static_cast<sal_uInt32>(meOverline); +} + +// ------------------------------------------------------------------------ + +void MetaTextLineAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + + sal_uInt32 nTemp; + rIStm >> maPos; + rIStm >> mnWidth; + rIStm >> nTemp; + meStrikeout = (FontStrikeout)nTemp; + rIStm >> nTemp; + meUnderline = (FontUnderline)nTemp; + if ( aCompat.GetVersion() >= 2 ) { + rIStm >> nTemp; + meUnderline = (FontUnderline)nTemp; + } +} + +// ======================================================================== + +IMPL_META_ACTION( Bmp, META_BMP_ACTION ) + +// ------------------------------------------------------------------------ + +MetaBmpAction::MetaBmpAction( const Point& rPt, const Bitmap& rBmp ) : + MetaAction ( META_BMP_ACTION ), + maBmp ( rBmp ), + maPt ( rPt ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaBmpAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawBitmap( maPt, maBmp ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaBmpAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaBmpAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaBmpAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maPt, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaBmpAction::Compare( const MetaAction& rMetaAction ) const +{ + return maBmp.IsEqual(((MetaBmpAction&)rMetaAction).maBmp ) && + ( maPt == ((MetaBmpAction&)rMetaAction).maPt ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + if( !!maBmp ) + { + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maBmp << maPt; + } +} + +// ------------------------------------------------------------------------ + +void MetaBmpAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maBmp >> maPt; +} + +// ======================================================================== + +IMPL_META_ACTION( BmpScale, META_BMPSCALE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaBmpScaleAction::MetaBmpScaleAction( const Point& rPt, const Size& rSz, + const Bitmap& rBmp ) : + MetaAction ( META_BMPSCALE_ACTION ), + maBmp ( rBmp ), + maPt ( rPt ), + maSz ( rSz ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaBmpScaleAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawBitmap( maPt, maSz, maBmp ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaBmpScaleAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaBmpScaleAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaBmpScaleAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpScaleAction::Scale( double fScaleX, double fScaleY ) +{ + Rectangle aRectangle(maPt, maSz); + ImplScaleRect( aRectangle, fScaleX, fScaleY ); + maPt = aRectangle.TopLeft(); + maSz = aRectangle.GetSize(); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaBmpScaleAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maBmp.IsEqual(((MetaBmpScaleAction&)rMetaAction).maBmp )) && + ( maPt == ((MetaBmpScaleAction&)rMetaAction).maPt ) && + ( maSz == ((MetaBmpScaleAction&)rMetaAction).maSz ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpScaleAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + if( !!maBmp ) + { + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maBmp << maPt << maSz; + } +} + +// ------------------------------------------------------------------------ + +void MetaBmpScaleAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maBmp >> maPt >> maSz; +} + +// ======================================================================== + +IMPL_META_ACTION( BmpScalePart, META_BMPSCALEPART_ACTION ) + +// ------------------------------------------------------------------------ + +MetaBmpScalePartAction::MetaBmpScalePartAction( const Point& rDstPt, const Size& rDstSz, + const Point& rSrcPt, const Size& rSrcSz, + const Bitmap& rBmp ) : + MetaAction ( META_BMPSCALEPART_ACTION ), + maBmp ( rBmp ), + maDstPt ( rDstPt ), + maDstSz ( rDstSz ), + maSrcPt ( rSrcPt ), + maSrcSz ( rSrcSz ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaBmpScalePartAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawBitmap( maDstPt, maDstSz, maSrcPt, maSrcSz, maBmp ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaBmpScalePartAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaBmpScalePartAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaBmpScalePartAction::Move( long nHorzMove, long nVertMove ) +{ + maDstPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpScalePartAction::Scale( double fScaleX, double fScaleY ) +{ + Rectangle aRectangle(maDstPt, maDstSz); + ImplScaleRect( aRectangle, fScaleX, fScaleY ); + maDstPt = aRectangle.TopLeft(); + maDstSz = aRectangle.GetSize(); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaBmpScalePartAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maBmp.IsEqual(((MetaBmpScalePartAction&)rMetaAction).maBmp )) && + ( maDstPt == ((MetaBmpScalePartAction&)rMetaAction).maDstPt ) && + ( maDstSz == ((MetaBmpScalePartAction&)rMetaAction).maDstSz ) && + ( maSrcPt == ((MetaBmpScalePartAction&)rMetaAction).maSrcPt ) && + ( maSrcSz == ((MetaBmpScalePartAction&)rMetaAction).maSrcSz ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpScalePartAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + if( !!maBmp ) + { + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maBmp << maDstPt << maDstSz << maSrcPt << maSrcSz; + } +} + +// ------------------------------------------------------------------------ + +void MetaBmpScalePartAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maBmp >> maDstPt >> maDstSz >> maSrcPt >> maSrcSz; +} + +// ======================================================================== + +IMPL_META_ACTION( BmpEx, META_BMPEX_ACTION ) + +// ------------------------------------------------------------------------ + +MetaBmpExAction::MetaBmpExAction( const Point& rPt, const BitmapEx& rBmpEx ) : + MetaAction ( META_BMPEX_ACTION ), + maBmpEx ( rBmpEx ), + maPt ( rPt ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaBmpExAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawBitmapEx( maPt, maBmpEx ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaBmpExAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaBmpExAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaBmpExAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpExAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maPt, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaBmpExAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maBmpEx.IsEqual(((MetaBmpExAction&)rMetaAction).maBmpEx )) && + ( maPt == ((MetaBmpExAction&)rMetaAction).maPt ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpExAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + if( !!maBmpEx.GetBitmap() ) + { + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maBmpEx << maPt; + } +} + +// ------------------------------------------------------------------------ + +void MetaBmpExAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maBmpEx >> maPt; +} + +// ======================================================================== + +IMPL_META_ACTION( BmpExScale, META_BMPEXSCALE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaBmpExScaleAction::MetaBmpExScaleAction( const Point& rPt, const Size& rSz, + const BitmapEx& rBmpEx ) : + MetaAction ( META_BMPEXSCALE_ACTION ), + maBmpEx ( rBmpEx ), + maPt ( rPt ), + maSz ( rSz ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScaleAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawBitmapEx( maPt, maSz, maBmpEx ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaBmpExScaleAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaBmpExScaleAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScaleAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScaleAction::Scale( double fScaleX, double fScaleY ) +{ + Rectangle aRectangle(maPt, maSz); + ImplScaleRect( aRectangle, fScaleX, fScaleY ); + maPt = aRectangle.TopLeft(); + maSz = aRectangle.GetSize(); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaBmpExScaleAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maBmpEx.IsEqual(((MetaBmpExScaleAction&)rMetaAction).maBmpEx )) && + ( maPt == ((MetaBmpExScaleAction&)rMetaAction).maPt ) && + ( maSz == ((MetaBmpExScaleAction&)rMetaAction).maSz ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScaleAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + if( !!maBmpEx.GetBitmap() ) + { + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maBmpEx << maPt << maSz; + } +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScaleAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maBmpEx >> maPt >> maSz; +} + +// ======================================================================== + +IMPL_META_ACTION( BmpExScalePart, META_BMPEXSCALEPART_ACTION ) + +// ------------------------------------------------------------------------ + +MetaBmpExScalePartAction::MetaBmpExScalePartAction( const Point& rDstPt, const Size& rDstSz, + const Point& rSrcPt, const Size& rSrcSz, + const BitmapEx& rBmpEx ) : + MetaAction ( META_BMPEXSCALEPART_ACTION ), + maBmpEx ( rBmpEx ), + maDstPt ( rDstPt ), + maDstSz ( rDstSz ), + maSrcPt ( rSrcPt ), + maSrcSz ( rSrcSz ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScalePartAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawBitmapEx( maDstPt, maDstSz, maSrcPt, maSrcSz, maBmpEx ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaBmpExScalePartAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaBmpExScalePartAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScalePartAction::Move( long nHorzMove, long nVertMove ) +{ + maDstPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScalePartAction::Scale( double fScaleX, double fScaleY ) +{ + Rectangle aRectangle(maDstPt, maDstSz); + ImplScaleRect( aRectangle, fScaleX, fScaleY ); + maDstPt = aRectangle.TopLeft(); + maDstSz = aRectangle.GetSize(); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaBmpExScalePartAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maBmpEx.IsEqual(((MetaBmpExScalePartAction&)rMetaAction).maBmpEx )) && + ( maDstPt == ((MetaBmpExScalePartAction&)rMetaAction).maDstPt ) && + ( maDstSz == ((MetaBmpExScalePartAction&)rMetaAction).maDstSz ) && + ( maSrcPt == ((MetaBmpExScalePartAction&)rMetaAction).maSrcPt ) && + ( maSrcSz == ((MetaBmpExScalePartAction&)rMetaAction).maSrcSz ); +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScalePartAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + if( !!maBmpEx.GetBitmap() ) + { + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maBmpEx << maDstPt << maDstSz << maSrcPt << maSrcSz; + } +} + +// ------------------------------------------------------------------------ + +void MetaBmpExScalePartAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maBmpEx >> maDstPt >> maDstSz >> maSrcPt >> maSrcSz; +} + +// ======================================================================== + +IMPL_META_ACTION( Mask, META_MASK_ACTION ) + +// ------------------------------------------------------------------------ + +MetaMaskAction::MetaMaskAction( const Point& rPt, + const Bitmap& rBmp, + const Color& rColor ) : + MetaAction ( META_MASK_ACTION ), + maBmp ( rBmp ), + maColor ( rColor ), + maPt ( rPt ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaMaskAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawMask( maPt, maBmp, maColor ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaMaskAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaMaskAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaMaskAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaMaskAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScalePoint( maPt, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaMaskAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maBmp.IsEqual(((MetaMaskAction&)rMetaAction).maBmp )) && + ( maColor == ((MetaMaskAction&)rMetaAction).maColor ) && + ( maPt == ((MetaMaskAction&)rMetaAction).maPt ); +} + +// ------------------------------------------------------------------------ + +void MetaMaskAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + if( !!maBmp ) + { + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maBmp << maPt; + } +} + +// ------------------------------------------------------------------------ + +void MetaMaskAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maBmp >> maPt; +} + +// ======================================================================== + +IMPL_META_ACTION( MaskScale, META_MASKSCALE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaMaskScaleAction::MetaMaskScaleAction( const Point& rPt, const Size& rSz, + const Bitmap& rBmp, + const Color& rColor ) : + MetaAction ( META_MASKSCALE_ACTION ), + maBmp ( rBmp ), + maColor ( rColor ), + maPt ( rPt ), + maSz ( rSz ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaMaskScaleAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawMask( maPt, maSz, maBmp, maColor ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaMaskScaleAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaMaskScaleAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaMaskScaleAction::Move( long nHorzMove, long nVertMove ) +{ + maPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaMaskScaleAction::Scale( double fScaleX, double fScaleY ) +{ + Rectangle aRectangle(maPt, maSz); + ImplScaleRect( aRectangle, fScaleX, fScaleY ); + maPt = aRectangle.TopLeft(); + maSz = aRectangle.GetSize(); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaMaskScaleAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maBmp.IsEqual(((MetaMaskScaleAction&)rMetaAction).maBmp )) && + ( maColor == ((MetaMaskScaleAction&)rMetaAction).maColor ) && + ( maPt == ((MetaMaskScaleAction&)rMetaAction).maPt ) && + ( maSz == ((MetaMaskScaleAction&)rMetaAction).maSz ); +} + +// ------------------------------------------------------------------------ + +void MetaMaskScaleAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + if( !!maBmp ) + { + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maBmp << maPt << maSz; + } +} + +// ------------------------------------------------------------------------ + +void MetaMaskScaleAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maBmp >> maPt >> maSz; +} + +// ======================================================================== + +IMPL_META_ACTION( MaskScalePart, META_MASKSCALEPART_ACTION ) + +// ------------------------------------------------------------------------ + +MetaMaskScalePartAction::MetaMaskScalePartAction( const Point& rDstPt, const Size& rDstSz, + const Point& rSrcPt, const Size& rSrcSz, + const Bitmap& rBmp, + const Color& rColor ) : + MetaAction ( META_MASKSCALEPART_ACTION ), + maBmp ( rBmp ), + maColor ( rColor ), + maDstPt ( rDstPt ), + maDstSz ( rDstSz ), + maSrcPt ( rSrcPt ), + maSrcSz ( rSrcSz ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaMaskScalePartAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawMask( maDstPt, maDstSz, maSrcPt, maSrcSz, maBmp, maColor ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaMaskScalePartAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaMaskScalePartAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaMaskScalePartAction::Move( long nHorzMove, long nVertMove ) +{ + maDstPt.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaMaskScalePartAction::Scale( double fScaleX, double fScaleY ) +{ + Rectangle aRectangle(maDstPt, maDstSz); + ImplScaleRect( aRectangle, fScaleX, fScaleY ); + maDstPt = aRectangle.TopLeft(); + maDstSz = aRectangle.GetSize(); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaMaskScalePartAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maBmp.IsEqual(((MetaMaskScalePartAction&)rMetaAction).maBmp )) && + ( maColor == ((MetaMaskScalePartAction&)rMetaAction).maColor ) && + ( maDstPt == ((MetaMaskScalePartAction&)rMetaAction).maDstPt ) && + ( maDstSz == ((MetaMaskScalePartAction&)rMetaAction).maDstSz ) && + ( maSrcPt == ((MetaMaskScalePartAction&)rMetaAction).maSrcPt ) && + ( maSrcSz == ((MetaMaskScalePartAction&)rMetaAction).maSrcSz ); +} + +// ------------------------------------------------------------------------ + +void MetaMaskScalePartAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + if( !!maBmp ) + { + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maBmp; + maColor.Write( rOStm, sal_True ); + rOStm << maDstPt << maDstSz << maSrcPt << maSrcSz; + } +} + +// ------------------------------------------------------------------------ + +void MetaMaskScalePartAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maBmp; + maColor.Read( rIStm, sal_True ); + rIStm >> maDstPt >> maDstSz >> maSrcPt >> maSrcSz; +} + +// ======================================================================== + +IMPL_META_ACTION( Gradient, META_GRADIENT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaGradientAction::MetaGradientAction( const Rectangle& rRect, const Gradient& rGradient ) : + MetaAction ( META_GRADIENT_ACTION ), + maRect ( rRect ), + maGradient ( rGradient ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaGradientAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawGradient( maRect, maGradient ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaGradientAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaGradientAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaGradientAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaGradientAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaGradientAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maRect == ((MetaGradientAction&)rMetaAction).maRect ) && + ( maGradient == ((MetaGradientAction&)rMetaAction).maGradient ); +} + +// ------------------------------------------------------------------------ + +void MetaGradientAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRect << maGradient; +} + +// ------------------------------------------------------------------------ + +void MetaGradientAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRect >> maGradient; +} + +// ======================================================================== + +MetaGradientExAction::MetaGradientExAction() : + MetaAction ( META_GRADIENTEX_ACTION ) +{ +} + +// ------------------------------------------------------------------------ + +MetaGradientExAction::MetaGradientExAction( const PolyPolygon& rPolyPoly, const Gradient& rGradient ) : + MetaAction ( META_GRADIENTEX_ACTION ), + maPolyPoly ( rPolyPoly ), + maGradient ( rGradient ) +{ +} + +// ------------------------------------------------------------------------ + +MetaGradientExAction::~MetaGradientExAction() +{ +} + +// ------------------------------------------------------------------------ + +void MetaGradientExAction::Execute( OutputDevice* pOut ) +{ + if( pOut->GetConnectMetaFile() ) + pOut->GetConnectMetaFile()->AddAction( Clone() ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaGradientExAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaGradientExAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaGradientExAction::Move( long nHorzMove, long nVertMove ) +{ + maPolyPoly.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaGradientExAction::Scale( double fScaleX, double fScaleY ) +{ + for( sal_uInt16 i = 0, nCount = maPolyPoly.Count(); i < nCount; i++ ) + ImplScalePoly( maPolyPoly[ i ], fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaGradientExAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maPolyPoly == ((MetaGradientExAction&)rMetaAction).maPolyPoly ) && + ( maGradient == ((MetaGradientExAction&)rMetaAction).maGradient ); +} + +// ------------------------------------------------------------------------ + +void MetaGradientExAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + + // #i105373# see comment at MetaTransparentAction::Write + PolyPolygon aNoCurvePolyPolygon; + maPolyPoly.AdaptiveSubdivide(aNoCurvePolyPolygon); + + rOStm << aNoCurvePolyPolygon; + rOStm << maGradient; +} + +// ------------------------------------------------------------------------ + +void MetaGradientExAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maPolyPoly >> maGradient; +} + +// ======================================================================== + +IMPL_META_ACTION( Hatch, META_HATCH_ACTION ) + +// ------------------------------------------------------------------------ + +MetaHatchAction::MetaHatchAction( const PolyPolygon& rPolyPoly, const Hatch& rHatch ) : + MetaAction ( META_HATCH_ACTION ), + maPolyPoly ( rPolyPoly ), + maHatch ( rHatch ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaHatchAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawHatch( maPolyPoly, maHatch ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaHatchAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaHatchAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaHatchAction::Move( long nHorzMove, long nVertMove ) +{ + maPolyPoly.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaHatchAction::Scale( double fScaleX, double fScaleY ) +{ + for( sal_uInt16 i = 0, nCount = maPolyPoly.Count(); i < nCount; i++ ) + ImplScalePoly( maPolyPoly[ i ], fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaHatchAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maPolyPoly == ((MetaHatchAction&)rMetaAction).maPolyPoly ) && + ( maHatch == ((MetaHatchAction&)rMetaAction).maHatch ); +} + +// ------------------------------------------------------------------------ + +void MetaHatchAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + + // #i105373# see comment at MetaTransparentAction::Write + PolyPolygon aNoCurvePolyPolygon; + maPolyPoly.AdaptiveSubdivide(aNoCurvePolyPolygon); + + rOStm << aNoCurvePolyPolygon; + rOStm << maHatch; +} + +// ------------------------------------------------------------------------ + +void MetaHatchAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maPolyPoly >> maHatch; +} + +// ======================================================================== + +IMPL_META_ACTION( Wallpaper, META_WALLPAPER_ACTION ) + +// ------------------------------------------------------------------------ + +MetaWallpaperAction::MetaWallpaperAction( const Rectangle& rRect, + const Wallpaper& rPaper ) : + MetaAction ( META_WALLPAPER_ACTION ), + maRect ( rRect ), + maWallpaper ( rPaper ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaWallpaperAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawWallpaper( maRect, maWallpaper ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaWallpaperAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaWallpaperAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaWallpaperAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaWallpaperAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaWallpaperAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maRect == ((MetaWallpaperAction&)rMetaAction).maRect ) && + ( maWallpaper == ((MetaWallpaperAction&)rMetaAction).maWallpaper ); +} + +// ------------------------------------------------------------------------ + +void MetaWallpaperAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maWallpaper; +} + +// ------------------------------------------------------------------------ + +void MetaWallpaperAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maWallpaper; +} + +// ======================================================================== + +IMPL_META_ACTION( ClipRegion, META_CLIPREGION_ACTION ) + +// ------------------------------------------------------------------------ + +MetaClipRegionAction::MetaClipRegionAction( const Region& rRegion, sal_Bool bClip ) : + MetaAction ( META_CLIPREGION_ACTION ), + maRegion ( rRegion ), + mbClip ( bClip ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaClipRegionAction::Execute( OutputDevice* pOut ) +{ + if( mbClip ) + pOut->SetClipRegion( maRegion ); + else + pOut->SetClipRegion(); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaClipRegionAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaClipRegionAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaClipRegionAction::Move( long nHorzMove, long nVertMove ) +{ + maRegion.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaClipRegionAction::Scale( double fScaleX, double fScaleY ) +{ + maRegion.Scale( fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaClipRegionAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maRegion == ((MetaClipRegionAction&)rMetaAction).maRegion ) && + ( mbClip == ((MetaClipRegionAction&)rMetaAction).mbClip ); +} + +// ------------------------------------------------------------------------ + +void MetaClipRegionAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRegion << mbClip; +} + +// ------------------------------------------------------------------------ + +void MetaClipRegionAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRegion >> mbClip; +} + +// ======================================================================== + +IMPL_META_ACTION( ISectRectClipRegion, META_ISECTRECTCLIPREGION_ACTION ) + +// ------------------------------------------------------------------------ + +MetaISectRectClipRegionAction::MetaISectRectClipRegionAction( const Rectangle& rRect ) : + MetaAction ( META_ISECTRECTCLIPREGION_ACTION ), + maRect ( rRect ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaISectRectClipRegionAction::Execute( OutputDevice* pOut ) +{ + pOut->IntersectClipRegion( maRect ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaISectRectClipRegionAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaISectRectClipRegionAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaISectRectClipRegionAction::Move( long nHorzMove, long nVertMove ) +{ + maRect.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaISectRectClipRegionAction::Scale( double fScaleX, double fScaleY ) +{ + ImplScaleRect( maRect, fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaISectRectClipRegionAction::Compare( const MetaAction& rMetaAction ) const +{ + return maRect == ((MetaISectRectClipRegionAction&)rMetaAction).maRect; +} + +// ------------------------------------------------------------------------ + +void MetaISectRectClipRegionAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRect; +} + +// ------------------------------------------------------------------------ + +void MetaISectRectClipRegionAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRect; +} + +// ======================================================================== + +IMPL_META_ACTION( ISectRegionClipRegion, META_ISECTREGIONCLIPREGION_ACTION ) + +// ------------------------------------------------------------------------ + +MetaISectRegionClipRegionAction::MetaISectRegionClipRegionAction( const Region& rRegion ) : + MetaAction ( META_ISECTREGIONCLIPREGION_ACTION ), + maRegion ( rRegion ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaISectRegionClipRegionAction::Execute( OutputDevice* pOut ) +{ + pOut->IntersectClipRegion( maRegion ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaISectRegionClipRegionAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaISectRegionClipRegionAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaISectRegionClipRegionAction::Move( long nHorzMove, long nVertMove ) +{ + maRegion.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaISectRegionClipRegionAction::Scale( double fScaleX, double fScaleY ) +{ + maRegion.Scale( fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaISectRegionClipRegionAction::Compare( const MetaAction& rMetaAction ) const +{ + return maRegion == ((MetaISectRegionClipRegionAction&)rMetaAction).maRegion; +} + +// ------------------------------------------------------------------------ + +void MetaISectRegionClipRegionAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRegion; +} + +// ------------------------------------------------------------------------ + +void MetaISectRegionClipRegionAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRegion; +} + +// ======================================================================== + +IMPL_META_ACTION( MoveClipRegion, META_MOVECLIPREGION_ACTION ) + +// ------------------------------------------------------------------------ + +MetaMoveClipRegionAction::MetaMoveClipRegionAction( long nHorzMove, long nVertMove ) : + MetaAction ( META_MOVECLIPREGION_ACTION ), + mnHorzMove ( nHorzMove ), + mnVertMove ( nVertMove ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaMoveClipRegionAction::Execute( OutputDevice* pOut ) +{ + pOut->MoveClipRegion( mnHorzMove, mnVertMove ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaMoveClipRegionAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaMoveClipRegionAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaMoveClipRegionAction::Scale( double fScaleX, double fScaleY ) +{ + mnHorzMove = FRound( mnHorzMove * fScaleX ); + mnVertMove = FRound( mnVertMove * fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaMoveClipRegionAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( mnHorzMove == ((MetaMoveClipRegionAction&)rMetaAction).mnHorzMove ) && + ( mnVertMove == ((MetaMoveClipRegionAction&)rMetaAction).mnVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaMoveClipRegionAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << mnHorzMove << mnVertMove; +} + +// ------------------------------------------------------------------------ + +void MetaMoveClipRegionAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> mnHorzMove >> mnVertMove; +} + +// ======================================================================== + +IMPL_META_ACTION( LineColor, META_LINECOLOR_ACTION ) + +// ------------------------------------------------------------------------ + +MetaLineColorAction::MetaLineColorAction( const Color& rColor, sal_Bool bSet ) : + MetaAction ( META_LINECOLOR_ACTION ), + maColor ( rColor ), + mbSet ( bSet ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaLineColorAction::Execute( OutputDevice* pOut ) +{ + if( mbSet ) + pOut->SetLineColor( maColor ); + else + pOut->SetLineColor(); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaLineColorAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaLineColorAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaLineColorAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maColor == ((MetaLineColorAction&)rMetaAction).maColor ) && + ( mbSet == ((MetaLineColorAction&)rMetaAction).mbSet ); +} + +// ------------------------------------------------------------------------ + +void MetaLineColorAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + maColor.Write( rOStm, sal_True ); + rOStm << mbSet; +} + +// ------------------------------------------------------------------------ + +void MetaLineColorAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + maColor.Read( rIStm, sal_True ); + rIStm >> mbSet; +} + +// ======================================================================== + +IMPL_META_ACTION( FillColor, META_FILLCOLOR_ACTION ) + +// ------------------------------------------------------------------------ + +MetaFillColorAction::MetaFillColorAction( const Color& rColor, sal_Bool bSet ) : + MetaAction ( META_FILLCOLOR_ACTION ), + maColor ( rColor ), + mbSet ( bSet ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaFillColorAction::Execute( OutputDevice* pOut ) +{ + if( mbSet ) + pOut->SetFillColor( maColor ); + else + pOut->SetFillColor(); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaFillColorAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaFillColorAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaFillColorAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maColor == ((MetaFillColorAction&)rMetaAction).maColor ) && + ( mbSet == ((MetaFillColorAction&)rMetaAction).mbSet ); +} + +// ------------------------------------------------------------------------ + +void MetaFillColorAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + maColor.Write( rOStm, sal_True ); + rOStm << mbSet; +} + +// ------------------------------------------------------------------------ + +void MetaFillColorAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + maColor.Read( rIStm, sal_True ); + rIStm >> mbSet; +} + +// ======================================================================== + +IMPL_META_ACTION( TextColor, META_TEXTCOLOR_ACTION ) + +// ------------------------------------------------------------------------ + +MetaTextColorAction::MetaTextColorAction( const Color& rColor ) : + MetaAction ( META_TEXTCOLOR_ACTION ), + maColor ( rColor ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaTextColorAction::Execute( OutputDevice* pOut ) +{ + pOut->SetTextColor( maColor ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTextColorAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaTextColorAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTextColorAction::Compare( const MetaAction& rMetaAction ) const +{ + return maColor == ((MetaTextColorAction&)rMetaAction).maColor; +} + +// ------------------------------------------------------------------------ + +void MetaTextColorAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + maColor.Write( rOStm, sal_True ); +} + +// ------------------------------------------------------------------------ + +void MetaTextColorAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + maColor.Read( rIStm, sal_True ); +} + +// ======================================================================== + +IMPL_META_ACTION( TextFillColor, META_TEXTFILLCOLOR_ACTION ) + +// ------------------------------------------------------------------------ + +MetaTextFillColorAction::MetaTextFillColorAction( const Color& rColor, sal_Bool bSet ) : + MetaAction ( META_TEXTFILLCOLOR_ACTION ), + maColor ( rColor ), + mbSet ( bSet ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaTextFillColorAction::Execute( OutputDevice* pOut ) +{ + if( mbSet ) + pOut->SetTextFillColor( maColor ); + else + pOut->SetTextFillColor(); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTextFillColorAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaTextFillColorAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTextFillColorAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maColor == ((MetaTextFillColorAction&)rMetaAction).maColor ) && + ( mbSet == ((MetaTextFillColorAction&)rMetaAction).mbSet ); +} + +// ------------------------------------------------------------------------ + +void MetaTextFillColorAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + maColor.Write( rOStm, sal_True ); + rOStm << mbSet; +} + +// ------------------------------------------------------------------------ + +void MetaTextFillColorAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + maColor.Read( rIStm, sal_True ); + rIStm >> mbSet; +} + +// ======================================================================== + +IMPL_META_ACTION( TextLineColor, META_TEXTLINECOLOR_ACTION ) + +// ------------------------------------------------------------------------ + +MetaTextLineColorAction::MetaTextLineColorAction( const Color& rColor, sal_Bool bSet ) : + MetaAction ( META_TEXTLINECOLOR_ACTION ), + maColor ( rColor ), + mbSet ( bSet ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaTextLineColorAction::Execute( OutputDevice* pOut ) +{ + if( mbSet ) + pOut->SetTextLineColor( maColor ); + else + pOut->SetTextLineColor(); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTextLineColorAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaTextLineColorAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTextLineColorAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maColor == ((MetaTextLineColorAction&)rMetaAction).maColor ) && + ( mbSet == ((MetaTextLineColorAction&)rMetaAction).mbSet ); +} + +// ------------------------------------------------------------------------ + +void MetaTextLineColorAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + maColor.Write( rOStm, sal_True ); + rOStm << mbSet; +} + +// ------------------------------------------------------------------------ + +void MetaTextLineColorAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + maColor.Read( rIStm, sal_True ); + rIStm >> mbSet; +} + +// ======================================================================== + +IMPL_META_ACTION( OverlineColor, META_OVERLINECOLOR_ACTION ) + +// ------------------------------------------------------------------------ + +MetaOverlineColorAction::MetaOverlineColorAction( const Color& rColor, sal_Bool bSet ) : + MetaAction ( META_OVERLINECOLOR_ACTION ), + maColor ( rColor ), + mbSet ( bSet ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaOverlineColorAction::Execute( OutputDevice* pOut ) +{ + if( mbSet ) + pOut->SetOverlineColor( maColor ); + else + pOut->SetOverlineColor(); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaOverlineColorAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaOverlineColorAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaOverlineColorAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maColor == ((MetaOverlineColorAction&)rMetaAction).maColor ) && + ( mbSet == ((MetaOverlineColorAction&)rMetaAction).mbSet ); +} + +// ------------------------------------------------------------------------ + +void MetaOverlineColorAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + maColor.Write( rOStm, sal_True ); + rOStm << mbSet; +} + +// ------------------------------------------------------------------------ + +void MetaOverlineColorAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + maColor.Read( rIStm, sal_True ); + rIStm >> mbSet; +} + +// ======================================================================== + +IMPL_META_ACTION( TextAlign, META_TEXTALIGN_ACTION ) + +// ------------------------------------------------------------------------ + +MetaTextAlignAction::MetaTextAlignAction( TextAlign aAlign ) : + MetaAction ( META_TEXTALIGN_ACTION ), + maAlign ( aAlign ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaTextAlignAction::Execute( OutputDevice* pOut ) +{ + pOut->SetTextAlign( maAlign ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTextAlignAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaTextAlignAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTextAlignAction::Compare( const MetaAction& rMetaAction ) const +{ + return maAlign == ((MetaTextAlignAction&)rMetaAction).maAlign; +} + +// ------------------------------------------------------------------------ + +void MetaTextAlignAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << (sal_uInt16) maAlign; +} + +// ------------------------------------------------------------------------ + +void MetaTextAlignAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + sal_uInt16 nTmp16; + + COMPAT( rIStm ); + rIStm >> nTmp16; maAlign = (TextAlign) nTmp16; +} + +// ======================================================================== + +IMPL_META_ACTION( MapMode, META_MAPMODE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaMapModeAction::MetaMapModeAction( const MapMode& rMapMode ) : + MetaAction ( META_MAPMODE_ACTION ), + maMapMode ( rMapMode ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaMapModeAction::Execute( OutputDevice* pOut ) +{ + pOut->SetMapMode( maMapMode ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaMapModeAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaMapModeAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaMapModeAction::Scale( double fScaleX, double fScaleY ) +{ + Point aPoint( maMapMode.GetOrigin() ); + + ImplScalePoint( aPoint, fScaleX, fScaleY ); + maMapMode.SetOrigin( aPoint ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaMapModeAction::Compare( const MetaAction& rMetaAction ) const +{ + return maMapMode == ((MetaMapModeAction&)rMetaAction).maMapMode; +} + +// ------------------------------------------------------------------------ + +void MetaMapModeAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maMapMode; +} + +// ------------------------------------------------------------------------ + +void MetaMapModeAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maMapMode; +} + +// ======================================================================== + +IMPL_META_ACTION( Font, META_FONT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaFontAction::MetaFontAction( const Font& rFont ) : + MetaAction ( META_FONT_ACTION ), + maFont ( rFont ) +{ + // #96876: because RTL_TEXTENCODING_SYMBOL is often set at the StarSymbol font, + // we change the textencoding to RTL_TEXTENCODING_UNICODE here, which seems + // to be the right way; changing the textencoding at other sources + // is too dangerous at the moment + if( ( ( maFont.GetName().SearchAscii( "StarSymbol" ) != STRING_NOTFOUND ) + || ( maFont.GetName().SearchAscii( "OpenSymbol" ) != STRING_NOTFOUND ) ) + && ( maFont.GetCharSet() != RTL_TEXTENCODING_UNICODE ) ) + { + maFont.SetCharSet( RTL_TEXTENCODING_UNICODE ); + } +} + +// ------------------------------------------------------------------------ + +void MetaFontAction::Execute( OutputDevice* pOut ) +{ + pOut->SetFont( maFont ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaFontAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaFontAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaFontAction::Scale( double fScaleX, double fScaleY ) +{ + const Size aSize( + FRound(maFont.GetSize().Width() * fabs(fScaleX)), + FRound(maFont.GetSize().Height() * fabs(fScaleY))); + maFont.SetSize( aSize ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaFontAction::Compare( const MetaAction& rMetaAction ) const +{ + return maFont == ((MetaFontAction&)rMetaAction).maFont; +} + +// ------------------------------------------------------------------------ + +void MetaFontAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maFont; + pData->meActualCharSet = maFont.GetCharSet(); + if ( pData->meActualCharSet == RTL_TEXTENCODING_DONTKNOW ) + pData->meActualCharSet = gsl_getSystemTextEncoding(); +} + +// ------------------------------------------------------------------------ + +void MetaFontAction::Read( SvStream& rIStm, ImplMetaReadData* pData ) +{ + COMPAT( rIStm ); + rIStm >> maFont; + pData->meActualCharSet = maFont.GetCharSet(); + if ( pData->meActualCharSet == RTL_TEXTENCODING_DONTKNOW ) + pData->meActualCharSet = gsl_getSystemTextEncoding(); +} + +// ======================================================================== + +IMPL_META_ACTION( Push, META_PUSH_ACTION ) + +// ------------------------------------------------------------------------ + +MetaPushAction::MetaPushAction( sal_uInt16 nFlags ) : + MetaAction ( META_PUSH_ACTION ), + mnFlags ( nFlags ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaPushAction::Execute( OutputDevice* pOut ) +{ + pOut->Push( mnFlags ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaPushAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaPushAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaPushAction::Compare( const MetaAction& rMetaAction ) const +{ + return mnFlags == ((MetaPushAction&)rMetaAction).mnFlags; +} + +// ------------------------------------------------------------------------ + +void MetaPushAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << mnFlags; +} + +// ------------------------------------------------------------------------ + +void MetaPushAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> mnFlags; +} + +// ======================================================================== + +IMPL_META_ACTION( Pop, META_POP_ACTION ) + +// ------------------------------------------------------------------------ + +void MetaPopAction::Execute( OutputDevice* pOut ) +{ + pOut->Pop(); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaPopAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaPopAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaPopAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); +} + +// ------------------------------------------------------------------------ + +void MetaPopAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); +} + +// ======================================================================== + +IMPL_META_ACTION( RasterOp, META_RASTEROP_ACTION ) + +// ------------------------------------------------------------------------ + +MetaRasterOpAction::MetaRasterOpAction( RasterOp eRasterOp ) : + MetaAction ( META_RASTEROP_ACTION ), + meRasterOp ( eRasterOp ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaRasterOpAction::Execute( OutputDevice* pOut ) +{ + pOut->SetRasterOp( meRasterOp ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaRasterOpAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaRasterOpAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaRasterOpAction::Compare( const MetaAction& rMetaAction ) const +{ + return meRasterOp == ((MetaRasterOpAction&)rMetaAction).meRasterOp; +} + +// ------------------------------------------------------------------------ + +void MetaRasterOpAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << (sal_uInt16) meRasterOp; +} + +// ------------------------------------------------------------------------ + +void MetaRasterOpAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + sal_uInt16 nTmp16; + + COMPAT( rIStm ); + rIStm >> nTmp16; meRasterOp = (RasterOp) nTmp16; +} + +// ======================================================================== + +IMPL_META_ACTION( Transparent, META_TRANSPARENT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaTransparentAction::MetaTransparentAction( const PolyPolygon& rPolyPoly, sal_uInt16 nTransPercent ) : + MetaAction ( META_TRANSPARENT_ACTION ), + maPolyPoly ( rPolyPoly ), + mnTransPercent ( nTransPercent ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaTransparentAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawTransparent( maPolyPoly, mnTransPercent ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTransparentAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaTransparentAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaTransparentAction::Move( long nHorzMove, long nVertMove ) +{ + maPolyPoly.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaTransparentAction::Scale( double fScaleX, double fScaleY ) +{ + for( sal_uInt16 i = 0, nCount = maPolyPoly.Count(); i < nCount; i++ ) + ImplScalePoly( maPolyPoly[ i ], fScaleX, fScaleY ); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTransparentAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maPolyPoly == ((MetaTransparentAction&)rMetaAction).maPolyPoly ) && + ( mnTransPercent == ((MetaTransparentAction&)rMetaAction).mnTransPercent ); +} + +// ------------------------------------------------------------------------ + +void MetaTransparentAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + + // #i105373# The PolyPolygon in this action may be a curve; this + // was ignored until now what is an error. To make older office + // versions work with MetaFiles, i opt for applying AdaptiveSubdivide + // to the PolyPoylgon. + // The alternative would be to really write the curve information + // like in MetaPolyPolygonAction::Write (where someone extended it + // correctly, but not here :-( ). + // The golden solution would be to combine both, but i think it's + // not necessary; a good subdivision will be sufficient. + PolyPolygon aNoCurvePolyPolygon; + maPolyPoly.AdaptiveSubdivide(aNoCurvePolyPolygon); + + rOStm << aNoCurvePolyPolygon; + rOStm << mnTransPercent; +} + +// ------------------------------------------------------------------------ + +void MetaTransparentAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maPolyPoly; + rIStm >> mnTransPercent; +} + +// ======================================================================== + +IMPL_META_ACTION( FloatTransparent, META_FLOATTRANSPARENT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaFloatTransparentAction::MetaFloatTransparentAction( const GDIMetaFile& rMtf, const Point& rPos, + const Size& rSize, const Gradient& rGradient ) : + MetaAction ( META_FLOATTRANSPARENT_ACTION ), + maMtf ( rMtf ), + maPoint ( rPos ), + maSize ( rSize ), + maGradient ( rGradient ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaFloatTransparentAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawTransparent( maMtf, maPoint, maSize, maGradient ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaFloatTransparentAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaFloatTransparentAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaFloatTransparentAction::Move( long nHorzMove, long nVertMove ) +{ + maPoint.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaFloatTransparentAction::Scale( double fScaleX, double fScaleY ) +{ + Rectangle aRectangle(maPoint, maSize); + ImplScaleRect( aRectangle, fScaleX, fScaleY ); + maPoint = aRectangle.TopLeft(); + maSize = aRectangle.GetSize(); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaFloatTransparentAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maMtf == ((MetaFloatTransparentAction&)rMetaAction).maMtf ) && + ( maPoint == ((MetaFloatTransparentAction&)rMetaAction).maPoint ) && + ( maSize == ((MetaFloatTransparentAction&)rMetaAction).maSize ) && + ( maGradient == ((MetaFloatTransparentAction&)rMetaAction).maGradient ); +} + +// ------------------------------------------------------------------------ + +void MetaFloatTransparentAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + + maMtf.Write( rOStm ); + rOStm << maPoint << maSize << maGradient; +} + +// ------------------------------------------------------------------------ + +void MetaFloatTransparentAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maMtf >> maPoint >> maSize >> maGradient; +} + +// ======================================================================== + +IMPL_META_ACTION( EPS, META_EPS_ACTION ) + +// ------------------------------------------------------------------------ + +MetaEPSAction::MetaEPSAction( const Point& rPoint, const Size& rSize, + const GfxLink& rGfxLink, const GDIMetaFile& rSubst ) : + MetaAction ( META_EPS_ACTION ), + maGfxLink ( rGfxLink ), + maSubst ( rSubst ), + maPoint ( rPoint ), + maSize ( rSize ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaEPSAction::Execute( OutputDevice* pOut ) +{ + pOut->DrawEPS( maPoint, maSize, maGfxLink, &maSubst ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaEPSAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaEPSAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +void MetaEPSAction::Move( long nHorzMove, long nVertMove ) +{ + maPoint.Move( nHorzMove, nVertMove ); +} + +// ------------------------------------------------------------------------ + +void MetaEPSAction::Scale( double fScaleX, double fScaleY ) +{ + Rectangle aRectangle(maPoint, maSize); + ImplScaleRect( aRectangle, fScaleX, fScaleY ); + maPoint = aRectangle.TopLeft(); + maSize = aRectangle.GetSize(); +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaEPSAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maGfxLink.IsEqual(((MetaEPSAction&)rMetaAction).maGfxLink )) && + ( maSubst == ((MetaEPSAction&)rMetaAction).maSubst ) && + ( maPoint == ((MetaEPSAction&)rMetaAction).maPoint ) && + ( maSize == ((MetaEPSAction&)rMetaAction).maSize ); +} + +// ------------------------------------------------------------------------ + +void MetaEPSAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maGfxLink; + rOStm << maPoint; + rOStm << maSize; + maSubst.Write( rOStm ); +} + +// ------------------------------------------------------------------------ + +void MetaEPSAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maGfxLink; + rIStm >> maPoint; + rIStm >> maSize; + rIStm >> maSubst; +} + +// ======================================================================== + +IMPL_META_ACTION( RefPoint, META_REFPOINT_ACTION ) + +// ------------------------------------------------------------------------ + +MetaRefPointAction::MetaRefPointAction( const Point& rRefPoint, sal_Bool bSet ) : + MetaAction ( META_REFPOINT_ACTION ), + maRefPoint ( rRefPoint ), + mbSet ( bSet ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaRefPointAction::Execute( OutputDevice* pOut ) +{ + if( mbSet ) + pOut->SetRefPoint( maRefPoint ); + else + pOut->SetRefPoint(); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaRefPointAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaRefPointAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaRefPointAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maRefPoint == ((MetaRefPointAction&)rMetaAction).maRefPoint ) && + ( mbSet == ((MetaRefPointAction&)rMetaAction).mbSet ); +} + +// ------------------------------------------------------------------------ + +void MetaRefPointAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maRefPoint << mbSet; +} + +// ------------------------------------------------------------------------ + +void MetaRefPointAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maRefPoint >> mbSet; +} + +// ======================================================================== + +MetaCommentAction::MetaCommentAction( sal_Int32 nValue ) : + MetaAction ( META_COMMENT_ACTION ), + mnValue ( nValue ) +{ + ImplInitDynamicData( NULL, 0UL ); +} + +// ------------------------------------------------------------------------ + +MetaCommentAction::MetaCommentAction( const MetaCommentAction& rAct ) : + MetaAction ( META_COMMENT_ACTION ), + maComment ( rAct.maComment ), + mnValue ( rAct.mnValue ) +{ + ImplInitDynamicData( rAct.mpData, rAct.mnDataSize ); +} + +// ------------------------------------------------------------------------ + +MetaCommentAction::MetaCommentAction( const ByteString& rComment, sal_Int32 nValue, const sal_uInt8* pData, sal_uInt32 nDataSize ) : + MetaAction ( META_COMMENT_ACTION ), + maComment ( rComment ), + mnValue ( nValue ) +{ + ImplInitDynamicData( pData, nDataSize ); +} + +// ------------------------------------------------------------------------ + +MetaCommentAction::MetaCommentAction( const sal_uInt8* pData, sal_uInt32 nDataSize ) : + MetaAction ( META_COMMENT_ACTION ), + mnValue ( 0L ) +{ + ImplInitDynamicData( pData, nDataSize ); +} + +// ------------------------------------------------------------------------ + +MetaCommentAction::~MetaCommentAction() +{ + if ( mpData ) + delete[] mpData; +} + +// ------------------------------------------------------------------------ + +void MetaCommentAction::ImplInitDynamicData( const sal_uInt8* pData, sal_uInt32 nDataSize ) +{ + if ( nDataSize && pData ) + { + mnDataSize = nDataSize, mpData = new sal_uInt8[ mnDataSize ]; + memcpy( mpData, pData, mnDataSize ); + } + else + { + mnDataSize = 0; + mpData = NULL; + } +} + +// ------------------------------------------------------------------------ + +void MetaCommentAction::Execute( OutputDevice* pOut ) +{ + if ( pOut->GetConnectMetaFile() ) + pOut->GetConnectMetaFile()->AddAction( Clone() ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaCommentAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaCommentAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +void MetaCommentAction::Move( long nXMove, long nYMove ) +{ + if ( nXMove || nYMove ) + { + if ( mnDataSize && mpData ) + { + sal_Bool bPathStroke = maComment.Equals( "XPATHSTROKE_SEQ_BEGIN" ); + if ( bPathStroke || maComment.Equals( "XPATHFILL_SEQ_BEGIN" ) ) + { + SvMemoryStream aMemStm( (void*)mpData, mnDataSize, STREAM_READ ); + SvMemoryStream aDest; + if ( bPathStroke ) + { + SvtGraphicStroke aStroke; + aMemStm >> aStroke; + Polygon aPath; + aStroke.getPath( aPath ); + aPath.Move( nXMove, nYMove ); + aStroke.setPath( aPath ); + aDest << aStroke; + } + else + { + SvtGraphicFill aFill; + aMemStm >> aFill; + PolyPolygon aPath; + aFill.getPath( aPath ); + aPath.Move( nXMove, nYMove ); + aFill.setPath( aPath ); + aDest << aFill; + } + delete[] mpData; + ImplInitDynamicData( static_cast<const sal_uInt8*>( aDest.GetData() ), aDest.Tell() ); + } + } + } +} + +// ------------------------------------------------------------------------ +// SJ: 25.07.06 #i56656# we are not able to mirrorcertain kind of +// comments properly, especially the XPATHSTROKE and XPATHFILL lead to +// problems, so it is better to remove these comments when mirroring +// FIXME: fake comment to apply the next hunk in the right location +void MetaCommentAction::Scale( double fXScale, double fYScale ) +{ + if ( ( fXScale != 1.0 ) || ( fYScale != 1.0 ) ) + { + if ( mnDataSize && mpData ) + { + sal_Bool bPathStroke = maComment.Equals( "XPATHSTROKE_SEQ_BEGIN" ); + if ( bPathStroke || maComment.Equals( "XPATHFILL_SEQ_BEGIN" ) ) + { + SvMemoryStream aMemStm( (void*)mpData, mnDataSize, STREAM_READ ); + SvMemoryStream aDest; + if ( bPathStroke ) + { + SvtGraphicStroke aStroke; + aMemStm >> aStroke; + Polygon aPath; + aStroke.getPath( aPath ); + aPath.Scale( fXScale, fYScale ); + aStroke.setPath( aPath ); + aDest << aStroke; + } + else + { + SvtGraphicFill aFill; + aMemStm >> aFill; + PolyPolygon aPath; + aFill.getPath( aPath ); + aPath.Scale( fXScale, fYScale ); + aFill.setPath( aPath ); + aDest << aFill; + } + delete[] mpData; + ImplInitDynamicData( static_cast<const sal_uInt8*>( aDest.GetData() ), aDest.Tell() ); + } else if( maComment.Equals( "EMF_PLUS_HEADER_INFO" ) ) { + SvMemoryStream aMemStm( (void*)mpData, mnDataSize, STREAM_READ ); + SvMemoryStream aDest; + + sal_Int32 nLeft, nRight, nTop, nBottom; + sal_Int32 nPixX, nPixY, nMillX, nMillY; + float m11, m12, m21, m22, mdx, mdy; + + // read data + aMemStm >> nLeft >> nTop >> nRight >> nBottom; + aMemStm >> nPixX >> nPixY >> nMillX >> nMillY; + aMemStm >> m11 >> m12 >> m21 >> m22 >> mdx >> mdy; + + // add scale to the transformation + m11 *= fXScale; + m12 *= fXScale; + m22 *= fYScale; + m21 *= fYScale; + + // prepare new data + aDest << nLeft << nTop << nRight << nBottom; + aDest << nPixX << nPixY << nMillX << nMillY; + aDest << m11 << m12 << m21 << m22 << mdx << mdy; + + // save them + ImplInitDynamicData( static_cast<const sal_uInt8*>( aDest.GetData() ), aDest.Tell() ); + } + } + } +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaCommentAction::Compare( const MetaAction& rMetaAction ) const +{ + return ( maComment == ((MetaCommentAction&)rMetaAction).maComment ) && + ( mnValue == ((MetaCommentAction&)rMetaAction).mnValue ) && + ( mnDataSize == ((MetaCommentAction&)rMetaAction).mnDataSize ) && + ( memcmp( mpData, ((MetaCommentAction&)rMetaAction).mpData, mnDataSize ) == 0 ); +} + +// ------------------------------------------------------------------------ + +void MetaCommentAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << maComment << mnValue << mnDataSize; + + if ( mnDataSize ) + rOStm.Write( mpData, mnDataSize ); +} + +// ------------------------------------------------------------------------ + +void MetaCommentAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> maComment >> mnValue >> mnDataSize; + + if( mpData ) + delete[] mpData; + + if( mnDataSize ) + { + mpData = new sal_uInt8[ mnDataSize ]; + rIStm.Read( mpData, mnDataSize ); + } + else + mpData = NULL; +} + +// ======================================================================== + +IMPL_META_ACTION( LayoutMode, META_LAYOUTMODE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaLayoutModeAction::MetaLayoutModeAction( sal_uInt32 nLayoutMode ) : + MetaAction ( META_LAYOUTMODE_ACTION ), + mnLayoutMode( nLayoutMode ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaLayoutModeAction::Execute( OutputDevice* pOut ) +{ + pOut->SetLayoutMode( mnLayoutMode ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaLayoutModeAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaLayoutModeAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaLayoutModeAction::Compare( const MetaAction& rMetaAction ) const +{ + return mnLayoutMode == ((MetaLayoutModeAction&)rMetaAction).mnLayoutMode; +} + +// ------------------------------------------------------------------------ + +void MetaLayoutModeAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << mnLayoutMode; +} + +// ------------------------------------------------------------------------ + +void MetaLayoutModeAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> mnLayoutMode; +} + +// ======================================================================== + +IMPL_META_ACTION( TextLanguage, META_TEXTLANGUAGE_ACTION ) + +// ------------------------------------------------------------------------ + +MetaTextLanguageAction::MetaTextLanguageAction( LanguageType eTextLanguage ) : + MetaAction ( META_TEXTLANGUAGE_ACTION ), + meTextLanguage( eTextLanguage ) +{ +} + +// ------------------------------------------------------------------------ + +void MetaTextLanguageAction::Execute( OutputDevice* pOut ) +{ + pOut->SetDigitLanguage( meTextLanguage ); +} + +// ------------------------------------------------------------------------ + +MetaAction* MetaTextLanguageAction::Clone() +{ + MetaAction* pClone = (MetaAction*) new MetaTextLanguageAction( *this ); + pClone->ResetRefCount(); + return pClone; +} + +// ------------------------------------------------------------------------ + +sal_Bool MetaTextLanguageAction::Compare( const MetaAction& rMetaAction ) const +{ + return meTextLanguage == ((MetaTextLanguageAction&)rMetaAction).meTextLanguage; +} + +// ------------------------------------------------------------------------ + +void MetaTextLanguageAction::Write( SvStream& rOStm, ImplMetaWriteData* pData ) +{ + WRITE_BASE_COMPAT( rOStm, 1, pData ); + rOStm << meTextLanguage; +} + +// ------------------------------------------------------------------------ + +void MetaTextLanguageAction::Read( SvStream& rIStm, ImplMetaReadData* ) +{ + COMPAT( rIStm ); + rIStm >> meTextLanguage; +} + +// ======================================================================== + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/metric.cxx b/vcl/source/gdi/metric.cxx new file mode 100644 index 000000000000..4814d6e3114e --- /dev/null +++ b/vcl/source/gdi/metric.cxx @@ -0,0 +1,929 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <vcl/impfont.hxx> +#include <vcl/metric.hxx> + +#include <vector> +#include <set> + +#include <cstdio> + +// ======================================================================= + +ImplFontMetric::ImplFontMetric() +: mnAscent( 0 ), + mnDescent( 0 ), + mnIntLeading( 0 ), + mnExtLeading( 0 ), + mnLineHeight( 0 ), + mnSlant( 0 ), + mnMiscFlags( 0 ), + mnRefCount( 1 ) +{} + +// ----------------------------------------------------------------------- + +inline void ImplFontMetric::AddReference() +{ + // TODO: disable refcounting on the default maps? + ++mnRefCount; +} + +// ----------------------------------------------------------------------- + +inline void ImplFontMetric::DeReference() +{ + // TODO: disable refcounting on the default maps? + if( --mnRefCount <= 0 ) + delete this; +} + +// ----------------------------------------------------------------------- + +bool ImplFontMetric::operator==( const ImplFontMetric& r ) const +{ + if( mnMiscFlags != r.mnMiscFlags ) + return false; + if( mnAscent != r.mnAscent ) + return false; + if( mnDescent != r.mnDescent ) + return false; + if( mnIntLeading != r.mnIntLeading ) + return false; + if( mnExtLeading != r.mnExtLeading ) + return false; + if( mnSlant != r.mnSlant ) + return false; + + return true; +} + +// ======================================================================= + +FontInfo::FontInfo() +: mpImplMetric( new ImplFontMetric ) +{} + +// ----------------------------------------------------------------------- + +FontInfo::FontInfo( const FontInfo& rInfo ) +: Font( rInfo ) +{ + mpImplMetric = rInfo.mpImplMetric; + mpImplMetric->AddReference(); +} + +// ----------------------------------------------------------------------- + +FontInfo::~FontInfo() +{ + mpImplMetric->DeReference(); +} + +// ----------------------------------------------------------------------- + +FontInfo& FontInfo::operator=( const FontInfo& rInfo ) +{ + Font::operator=( rInfo ); + + if( mpImplMetric != rInfo.mpImplMetric ) + { + mpImplMetric->DeReference(); + mpImplMetric = rInfo.mpImplMetric; + mpImplMetric->AddReference(); + } + + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool FontInfo::operator==( const FontInfo& rInfo ) const +{ + if( !Font::operator==( rInfo ) ) + return sal_False; + if( mpImplMetric == rInfo.mpImplMetric ) + return sal_True; + if( *mpImplMetric == *rInfo.mpImplMetric ) + return sal_True; + return sal_False; +} + +// ----------------------------------------------------------------------- + +FontType FontInfo::GetType() const +{ + return (mpImplMetric->IsScalable() ? TYPE_SCALABLE : TYPE_RASTER); +} + +// ----------------------------------------------------------------------- + +sal_Bool FontInfo::IsDeviceFont() const +{ + return mpImplMetric->IsDeviceFont(); +} + +// ----------------------------------------------------------------------- + +sal_Bool FontInfo::SupportsLatin() const +{ + return mpImplMetric->SupportsLatin(); +} + +// ----------------------------------------------------------------------- + +sal_Bool FontInfo::SupportsCJK() const +{ + return mpImplMetric->SupportsCJK(); +} + +// ----------------------------------------------------------------------- + +sal_Bool FontInfo::SupportsCTL() const +{ + return mpImplMetric->SupportsCTL(); +} + +// ======================================================================= + +FontMetric::FontMetric( const FontMetric& rMetric ) +: FontInfo( rMetric ) +{} + +// ----------------------------------------------------------------------- + +long FontMetric::GetAscent() const +{ + return mpImplMetric->GetAscent(); +} + +// ----------------------------------------------------------------------- + +long FontMetric::GetDescent() const +{ + return mpImplMetric->GetDescent(); +} + +// ----------------------------------------------------------------------- + +long FontMetric::GetIntLeading() const +{ + return mpImplMetric->GetIntLeading(); +} + +// ----------------------------------------------------------------------- + +long FontMetric::GetExtLeading() const +{ + return mpImplMetric->GetExtLeading(); +} + +// ----------------------------------------------------------------------- + +long FontMetric::GetLineHeight() const +{ + return mpImplMetric->GetLineHeight(); +} + +// ----------------------------------------------------------------------- + +long FontMetric::GetSlant() const +{ + return mpImplMetric->GetSlant(); +} + +// ----------------------------------------------------------------------- + +FontMetric& FontMetric::operator =( const FontMetric& rMetric ) +{ + FontInfo::operator=( rMetric ); + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool FontMetric::operator==( const FontMetric& rMetric ) const +{ + return FontInfo::operator==( rMetric ); +} + +// ======================================================================= + +CmapResult::CmapResult( bool bSymbolic, + const sal_uInt32* pRangeCodes, int nRangeCount, + const int* pStartGlyphs, const sal_uInt16* pExtraGlyphIds ) +: mpRangeCodes( pRangeCodes) +, mpStartGlyphs( pStartGlyphs) +, mpGlyphIds( pExtraGlyphIds) +, mnRangeCount( nRangeCount) +, mbSymbolic( bSymbolic) +, mbRecoded( false) +{} + +// ======================================================================= + +ImplFontCharMap::ImplFontCharMap( const CmapResult& rCR ) +: mpRangeCodes( rCR.mpRangeCodes ) +, mpStartGlyphs( rCR.mpStartGlyphs ) +, mpGlyphIds( rCR.mpGlyphIds ) +, mnRangeCount( rCR.mnRangeCount ) +, mnCharCount( 0 ) +, mnRefCount( 0 ) +{ + const sal_uInt32* pRangePtr = mpRangeCodes; + for( int i = mnRangeCount; --i >= 0; pRangePtr += 2 ) + { + sal_uInt32 cFirst = pRangePtr[0]; + sal_uInt32 cLast = pRangePtr[1]; + mnCharCount += cLast - cFirst; + } +} + +static ImplFontCharMap* pDefaultUnicodeImplFontCharMap = NULL; +static ImplFontCharMap* pDefaultSymbolImplFontCharMap = NULL; +static const sal_uInt32 aDefaultUnicodeRanges[] = {0x0020,0xD800, 0xE000,0xFFF0}; +static const sal_uInt32 aDefaultSymbolRanges[] = {0x0020,0x0100, 0xF020,0xF100}; + +// ----------------------------------------------------------------------- + +bool ImplFontCharMap::IsDefaultMap() const +{ + const bool bIsDefault = (mpRangeCodes == aDefaultUnicodeRanges) || (mpRangeCodes == aDefaultSymbolRanges); + return bIsDefault; +} + +// ----------------------------------------------------------------------- + +ImplFontCharMap::~ImplFontCharMap() +{ + if( IsDefaultMap() ) + return; + delete[] mpRangeCodes; + delete[] mpStartGlyphs; + delete[] mpGlyphIds; + } + +// ----------------------------------------------------------------------- + +namespace +{ + ImplFontCharMap *GetDefaultUnicodeMap() + { + if( !pDefaultUnicodeImplFontCharMap ) + { + const sal_uInt32* pRangeCodes = aDefaultUnicodeRanges; + int nCodesCount = sizeof(aDefaultUnicodeRanges) / sizeof(*pRangeCodes); + CmapResult aDefaultCR( false, pRangeCodes, nCodesCount/2 ); + pDefaultUnicodeImplFontCharMap = new ImplFontCharMap( aDefaultCR ); + pDefaultUnicodeImplFontCharMap->AddReference(); + } + + return pDefaultUnicodeImplFontCharMap; + } + + ImplFontCharMap *GetDefaultSymbolMap() + { + if( !pDefaultSymbolImplFontCharMap ) + { + const sal_uInt32* pRangeCodes = aDefaultSymbolRanges; + int nCodesCount = sizeof(aDefaultSymbolRanges) / sizeof(*pRangeCodes); + CmapResult aDefaultCR( true, pRangeCodes, nCodesCount/2 ); + pDefaultSymbolImplFontCharMap = new ImplFontCharMap( aDefaultCR ); + pDefaultSymbolImplFontCharMap->AddReference(); + } + + return pDefaultSymbolImplFontCharMap; + } +} + +ImplFontCharMap* ImplFontCharMap::GetDefaultMap( bool bSymbols) +{ + return bSymbols ? GetDefaultSymbolMap() : GetDefaultUnicodeMap(); +} + +// ----------------------------------------------------------------------- + +void ImplFontCharMap::AddReference( void ) const +{ + // TODO: disable refcounting on the default maps? + ++mnRefCount; +} + +// ----------------------------------------------------------------------- + +void ImplFontCharMap::DeReference( void ) const +{ + if( --mnRefCount <= 0 ) + if( (this != pDefaultUnicodeImplFontCharMap) && (this != pDefaultSymbolImplFontCharMap) ) + delete this; +} + +// ----------------------------------------------------------------------- + +int ImplFontCharMap::GetCharCount() const +{ + return mnCharCount; +} + +// ----------------------------------------------------------------------- + +int ImplFontCharMap::ImplFindRangeIndex( sal_uInt32 cChar ) const +{ + int nLower = 0; + int nMid = mnRangeCount; + int nUpper = 2 * mnRangeCount - 1; + while( nLower < nUpper ) + { + if( cChar >= mpRangeCodes[ nMid ] ) + nLower = nMid; + else + nUpper = nMid - 1; + nMid = (nLower + nUpper + 1) / 2; + } + + return nMid; +} + +// ----------------------------------------------------------------------- + +bool ImplFontCharMap::HasChar( sal_uInt32 cChar ) const +{ + bool bHasChar = false; + + if( mpStartGlyphs == NULL ) { // only the char-ranges are known + const int nRange = ImplFindRangeIndex( cChar ); + if( nRange==0 && cChar<mpRangeCodes[0] ) + return false; + bHasChar = ((nRange & 1) == 0); // inside a range + } else { // glyph mapping is available + const int nGlyphIndex = GetGlyphIndex( cChar ); + bHasChar = (nGlyphIndex != 0); // not the notdef-glyph + } + + return bHasChar; +} + +// ----------------------------------------------------------------------- + +int ImplFontCharMap::GetGlyphIndex( sal_uInt32 cChar ) const +{ + // return -1 if the object doesn't know the glyph ids + if( !mpStartGlyphs ) + return -1; + + // return 0 if the unicode doesn't have a matching glyph + int nRange = ImplFindRangeIndex( cChar ); + // check that we are inside any range + if( (nRange == 0) && (cChar < mpRangeCodes[0]) ) { + // symbol aliasing gives symbol fonts a second chance + const bool bSymbolic = (mpRangeCodes[0]>=0xF000) & (mpRangeCodes[1]<=0xF0FF); + if( !bSymbolic ) + return 0; + // check for symbol aliasing (U+00xx <-> U+F0xx) + cChar |= 0xF000; + nRange = ImplFindRangeIndex( cChar ); + } + // check that we are inside a range + if( (nRange & 1) != 0 ) + return 0; + + // get glyph index directly or indirectly + int nGlyphIndex = cChar - mpRangeCodes[ nRange ]; + const int nStartIndex = mpStartGlyphs[ nRange/2 ]; + if( nStartIndex >= 0 ) { + // the glyph index can be calculated + nGlyphIndex += nStartIndex; + } else { + // the glyphid array has the glyph index + nGlyphIndex = mpGlyphIds[ nGlyphIndex - nStartIndex ]; + } + + return nGlyphIndex; +} + +// ----------------------------------------------------------------------- + +// returns the number of chars supported by the font, which +// are inside the unicode range from cMin to cMax (inclusive) +int ImplFontCharMap::CountCharsInRange( sal_uInt32 cMin, sal_uInt32 cMax ) const +{ + int nCount = 0; + + // find and adjust range and char count for cMin + int nRangeMin = ImplFindRangeIndex( cMin ); + if( nRangeMin & 1 ) + ++nRangeMin; + else if( cMin > mpRangeCodes[ nRangeMin ] ) + nCount -= cMin - mpRangeCodes[ nRangeMin ]; + + // find and adjust range and char count for cMax + int nRangeMax = ImplFindRangeIndex( cMax ); + if( nRangeMax & 1 ) + --nRangeMax; + else + nCount -= mpRangeCodes[ nRangeMax+1 ] - cMax - 1; + + // count chars in complete ranges between cMin and cMax + for( int i = nRangeMin; i <= nRangeMax; i+=2 ) + nCount += mpRangeCodes[i+1] - mpRangeCodes[i]; + + return nCount; +} + +// ----------------------------------------------------------------------- + +sal_uInt32 ImplFontCharMap::GetFirstChar() const +{ + return mpRangeCodes[0]; +} + +// ----------------------------------------------------------------------- + +sal_uInt32 ImplFontCharMap::GetLastChar() const +{ + return (mpRangeCodes[ 2*mnRangeCount-1 ] - 1); +} + +// ----------------------------------------------------------------------- + +sal_uInt32 ImplFontCharMap::GetNextChar( sal_uInt32 cChar ) const +{ + if( cChar < GetFirstChar() ) + return GetFirstChar(); + if( cChar >= GetLastChar() ) + return GetLastChar(); + + int nRange = ImplFindRangeIndex( cChar + 1 ); + if( nRange & 1 ) // outside of range? + return mpRangeCodes[ nRange + 1 ]; // => first in next range + return (cChar + 1); +} + +// ----------------------------------------------------------------------- + +sal_uInt32 ImplFontCharMap::GetPrevChar( sal_uInt32 cChar ) const +{ + if( cChar <= GetFirstChar() ) + return GetFirstChar(); + if( cChar > GetLastChar() ) + return GetLastChar(); + + int nRange = ImplFindRangeIndex( cChar - 1 ); + if( nRange & 1 ) // outside a range? + return (mpRangeCodes[ nRange ] - 1); // => last in prev range + return (cChar - 1); +} + +// ----------------------------------------------------------------------- + +int ImplFontCharMap::GetIndexFromChar( sal_uInt32 cChar ) const +{ + // TODO: improve linear walk? + int nCharIndex = 0; + const sal_uInt32* pRange = &mpRangeCodes[0]; + for( int i = 0; i < mnRangeCount; ++i ) + { + sal_uInt32 cFirst = *(pRange++); + sal_uInt32 cLast = *(pRange++); + if( cChar >= cLast ) + nCharIndex += cLast - cFirst; + else if( cChar >= cFirst ) + return nCharIndex + (cChar - cFirst); + else + break; + } + + return -1; +} + +// ----------------------------------------------------------------------- + +sal_uInt32 ImplFontCharMap::GetCharFromIndex( int nCharIndex ) const +{ + // TODO: improve linear walk? + const sal_uInt32* pRange = &mpRangeCodes[0]; + for( int i = 0; i < mnRangeCount; ++i ) + { + sal_uInt32 cFirst = *(pRange++); + sal_uInt32 cLast = *(pRange++); + nCharIndex -= cLast - cFirst; + if( nCharIndex < 0 ) + return (cLast + nCharIndex); + } + + // we can only get here with an out-of-bounds charindex + return mpRangeCodes[0]; +} + +// ======================================================================= + +static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);} +static unsigned GetUShort( const unsigned char* p ){ return((p[0]<<8) | p[1]);} +static int GetSShort( const unsigned char* p ){ return((static_cast<signed char>(p[0])<<8)|p[1]);} + +// TODO: move CMAP parsing directly into the ImplFontCharMap class +bool ParseCMAP( const unsigned char* pCmap, int nLength, CmapResult& rResult ) +{ + rResult.mpRangeCodes = NULL; + rResult.mpStartGlyphs= NULL; + rResult.mpGlyphIds = NULL; + rResult.mnRangeCount = 0; + rResult.mbRecoded = false; + rResult.mbSymbolic = false; + + // parse the table header and check for validity + if( !pCmap || (nLength < 24) ) + return false; + + if( GetUShort( pCmap ) != 0x0000 ) // simple check for CMAP corruption + return false; + + int nSubTables = GetUShort( pCmap + 2 ); + if( (nSubTables <= 0) || (nLength < (24 + 8*nSubTables)) ) + return false; + + // find the most interesting subtable in the CMAP + rtl_TextEncoding eRecodeFrom = RTL_TEXTENCODING_UNICODE; + int nOffset = 0; + int nFormat = -1; + int nBestVal = 0; + for( const unsigned char* p = pCmap + 4; --nSubTables >= 0; p += 8 ) + { + int nPlatform = GetUShort( p ); + int nEncoding = GetUShort( p+2 ); + int nPlatformEncoding = (nPlatform << 8) + nEncoding; + + int nValue; + rtl_TextEncoding eTmpEncoding = RTL_TEXTENCODING_UNICODE; + switch( nPlatformEncoding ) + { + case 0x000: nValue = 20; break; // Unicode 1.0 + case 0x001: nValue = 21; break; // Unicode 1.1 + case 0x002: nValue = 22; break; // iso10646_1993 + case 0x003: nValue = 23; break; // UCS-2 + case 0x004: nValue = 24; break; // UCS-4 + case 0x100: nValue = 22; break; // Mac Unicode<2.0 + case 0x103: nValue = 23; break; // Mac Unicode>2.0 + case 0x300: nValue = 5; rResult.mbSymbolic = true; break; // Win Symbol + case 0x301: nValue = 28; break; // Win UCS-2 + case 0x30A: nValue = 29; break; // Win-UCS-4 + case 0x302: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_SHIFT_JIS; break; + case 0x303: nValue = 12; eTmpEncoding = RTL_TEXTENCODING_GB_18030; break; + case 0x304: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_BIG5; break; + case 0x305: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_MS_949; break; + case 0x306: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_MS_1361; break; + default: nValue = 0; break; + } + + if( nValue <= 0 ) // ignore unknown encodings + continue; + + int nTmpOffset = GetUInt( p+4 ); + int nTmpFormat = GetUShort( pCmap + nTmpOffset ); + if( nTmpFormat == 12 ) // 32bit code -> glyph map format + nValue += 3; + else if( nTmpFormat != 4 ) // 16bit code -> glyph map format + continue; // ignore other formats + + if( nBestVal < nValue ) + { + nBestVal = nValue; + nOffset = nTmpOffset; + nFormat = nTmpFormat; + eRecodeFrom = eTmpEncoding; + } + } + + // parse the best CMAP subtable + int nRangeCount = 0; + sal_uInt32* pCodePairs = NULL; + int* pStartGlyphs = NULL; + + typedef std::vector<sal_uInt16> U16Vector; + U16Vector aGlyphIdArray; + aGlyphIdArray.reserve( 0x1000 ); + aGlyphIdArray.push_back( 0 ); + + // format 4, the most common 16bit char mapping table + if( (nFormat == 4) && ((nOffset+16) < nLength) ) + { + int nSegCountX2 = GetUShort( pCmap + nOffset + 6 ); + nRangeCount = nSegCountX2/2 - 1; + pCodePairs = new sal_uInt32[ nRangeCount * 2 ]; + pStartGlyphs = new int[ nRangeCount ]; + const unsigned char* pLimitBase = pCmap + nOffset + 14; + const unsigned char* pBeginBase = pLimitBase + nSegCountX2 + 2; + const unsigned char* pDeltaBase = pBeginBase + nSegCountX2; + const unsigned char* pOffsetBase = pDeltaBase + nSegCountX2; + sal_uInt32* pCP = pCodePairs; + for( int i = 0; i < nRangeCount; ++i ) + { + const sal_uInt32 cMinChar = GetUShort( pBeginBase + 2*i ); + const sal_uInt32 cMaxChar = GetUShort( pLimitBase + 2*i ); + const int nGlyphDelta = GetSShort( pDeltaBase + 2*i ); + const int nRangeOffset = GetUShort( pOffsetBase + 2*i ); + if( cMinChar > cMaxChar ) // no sane font should trigger this + break; + if( cMaxChar == 0xFFFF ) + break; + *(pCP++) = cMinChar; + *(pCP++) = cMaxChar + 1; + if( !nRangeOffset ) { + // glyphid can be calculated directly + pStartGlyphs[i] = (cMinChar + nGlyphDelta) & 0xFFFF; + } else { + // update the glyphid-array with the glyphs in this range + pStartGlyphs[i] = -(int)aGlyphIdArray.size(); + const unsigned char* pGlyphIdPtr = pOffsetBase + 2*i + nRangeOffset; + for( sal_uInt32 c = cMinChar; c <= cMaxChar; ++c, pGlyphIdPtr+=2 ) { + const int nGlyphIndex = GetUShort( pGlyphIdPtr ) + nGlyphDelta; + aGlyphIdArray.push_back( static_cast<sal_uInt16>(nGlyphIndex) ); + } + } + } + nRangeCount = (pCP - pCodePairs) / 2; + } + // format 12, the most common 32bit char mapping table + else if( (nFormat == 12) && ((nOffset+16) < nLength) ) + { + nRangeCount = GetUInt( pCmap + nOffset + 12 ); + pCodePairs = new sal_uInt32[ nRangeCount * 2 ]; + pStartGlyphs = new int[ nRangeCount ]; + const unsigned char* pGroup = pCmap + nOffset + 16; + sal_uInt32* pCP = pCodePairs; + for( int i = 0; i < nRangeCount; ++i ) + { + sal_uInt32 cMinChar = GetUInt( pGroup + 0 ); + sal_uInt32 cMaxChar = GetUInt( pGroup + 4 ); + int nGlyphId = GetUInt( pGroup + 8 ); + pGroup += 12; + if( cMinChar > cMaxChar ) // no sane font should trigger this + break; + *(pCP++) = cMinChar; + *(pCP++) = cMaxChar + 1; + pStartGlyphs[i] = nGlyphId; + } + nRangeCount = (pCP - pCodePairs) / 2; + } + + // check if any subtable resulted in something usable + if( nRangeCount <= 0 ) + { + delete[] pCodePairs; + delete[] pStartGlyphs; + + // even when no CMAP is available we know it for symbol fonts + if( rResult.mbSymbolic ) + { + pCodePairs = new sal_uInt32[4]; + pCodePairs[0] = 0x0020; // aliased symbols + pCodePairs[1] = 0x0100; + pCodePairs[2] = 0xF020; // original symbols + pCodePairs[3] = 0xF100; + rResult.mpRangeCodes = pCodePairs; + rResult.mnRangeCount = 2; + return true; + } + + return false; + } + + // recode the code ranges to their unicode encoded ranges if needed + rtl_TextToUnicodeConverter aConverter = NULL; + rtl_UnicodeToTextContext aCvtContext = NULL; + + rResult.mbRecoded = ( eRecodeFrom != RTL_TEXTENCODING_UNICODE ); + if( rResult.mbRecoded ) + { + aConverter = rtl_createTextToUnicodeConverter( eRecodeFrom ); + aCvtContext = rtl_createTextToUnicodeContext( aConverter ); + } + + if( aConverter && aCvtContext ) + { + // determine the set of supported unicodes from encoded ranges + typedef std::set<sal_uInt32> IntSet; + IntSet aSupportedUnicodes; + + static const int NINSIZE = 64; + static const int NOUTSIZE = 64; + sal_Char cCharsInp[ NINSIZE ]; + sal_Unicode cCharsOut[ NOUTSIZE ]; + sal_uInt32* pCP = pCodePairs; + for( int i = 0; i < nRangeCount; ++i ) + { + sal_uInt32 cMin = *(pCP++); + sal_uInt32 cEnd = *(pCP++); + while( cMin < cEnd ) + { + int j = 0; + for(; (cMin < cEnd) && (j < NINSIZE); ++cMin ) + { + if( cMin >= 0x0100 ) + cCharsInp[ j++ ] = static_cast<sal_Char>(cMin >> 8); + if( (cMin >= 0x0100) || (cMin < 0x00A0) ) + cCharsInp[ j++ ] = static_cast<sal_Char>(cMin); + } + + sal_uInt32 nCvtInfo; + sal_Size nSrcCvtBytes; + int nOutLen = rtl_convertTextToUnicode( + aConverter, aCvtContext, + cCharsInp, j, cCharsOut, NOUTSIZE, + RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE + | RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE, + &nCvtInfo, &nSrcCvtBytes ); + + for( j = 0; j < nOutLen; ++j ) + aSupportedUnicodes.insert( cCharsOut[j] ); + } + } + + rtl_destroyTextToUnicodeConverter( aCvtContext ); + rtl_destroyTextToUnicodeConverter( aConverter ); + + // convert the set of supported unicodes to ranges + typedef std::vector<sal_uInt32> IntVector; + IntVector aSupportedRanges; + + IntSet::const_iterator itChar = aSupportedUnicodes.begin(); + for(; itChar != aSupportedUnicodes.end(); ++itChar ) + { + if( aSupportedRanges.empty() + || (aSupportedRanges.back() != *itChar) ) + { + // add new range beginning with current unicode + aSupportedRanges.push_back( *itChar ); + aSupportedRanges.push_back( 0 ); + } + + // extend existing range to include current unicode + aSupportedRanges.back() = *itChar + 1; + } + + // glyph mapping for non-unicode fonts not implemented + delete[] pStartGlyphs; + pStartGlyphs = NULL; + aGlyphIdArray.clear(); + + // make a pCodePairs array using the vector from above + delete[] pCodePairs; + nRangeCount = aSupportedRanges.size() / 2; + if( nRangeCount <= 0 ) + return false; + pCodePairs = new sal_uInt32[ nRangeCount * 2 ]; + IntVector::const_iterator itInt = aSupportedRanges.begin(); + for( pCP = pCodePairs; itInt != aSupportedRanges.end(); ++itInt ) + *(pCP++) = *itInt; + } + + // prepare the glyphid-array if needed + // TODO: merge ranges if they are close enough? + sal_uInt16* pGlyphIds = NULL; + if( !aGlyphIdArray.empty()) + { + pGlyphIds = new sal_uInt16[ aGlyphIdArray.size() ]; + sal_uInt16* pOut = pGlyphIds; + U16Vector::const_iterator it = aGlyphIdArray.begin(); + while( it != aGlyphIdArray.end() ) + *(pOut++) = *(it++); + } + + // update the result struct + rResult.mpRangeCodes = pCodePairs; + rResult.mpStartGlyphs = pStartGlyphs; + rResult.mnRangeCount = nRangeCount; + rResult.mpGlyphIds = pGlyphIds; + return true; +} + +// ======================================================================= + +FontCharMap::FontCharMap() +: mpImpl( ImplFontCharMap::GetDefaultMap() ) +{ + mpImpl->AddReference(); +} + +// ----------------------------------------------------------------------- + +FontCharMap::~FontCharMap() +{ + mpImpl->DeReference(); + mpImpl = NULL; +} + +// ----------------------------------------------------------------------- + +int FontCharMap::GetCharCount() const +{ + return mpImpl->GetCharCount(); +} + +// ----------------------------------------------------------------------- + +int FontCharMap::CountCharsInRange( sal_uInt32 cMin, sal_uInt32 cMax ) const +{ + return mpImpl->CountCharsInRange( cMin, cMax ); +} + +// ----------------------------------------------------------------------- + +void FontCharMap::Reset( const ImplFontCharMap* pNewMap ) +{ + mpImpl->DeReference(); + if( pNewMap == NULL ) + mpImpl = ImplFontCharMap::GetDefaultMap(); + else if( pNewMap != mpImpl ) + mpImpl = pNewMap; + mpImpl->AddReference(); +} + +// ----------------------------------------------------------------------- + +sal_Bool FontCharMap::IsDefaultMap() const +{ + return mpImpl->IsDefaultMap(); +} + +// ----------------------------------------------------------------------- + +sal_Bool FontCharMap::HasChar( sal_uInt32 cChar ) const +{ + return mpImpl->HasChar( cChar ); +} + +// ----------------------------------------------------------------------- + +sal_uInt32 FontCharMap::GetFirstChar() const +{ + return mpImpl->GetFirstChar(); +} + +// ----------------------------------------------------------------------- + +sal_uInt32 FontCharMap::GetLastChar() const +{ + return mpImpl->GetLastChar(); +} + +// ----------------------------------------------------------------------- + +sal_uInt32 FontCharMap::GetNextChar( sal_uInt32 cChar ) const +{ + return mpImpl->GetNextChar( cChar ); +} + +// ----------------------------------------------------------------------- + +sal_uInt32 FontCharMap::GetPrevChar( sal_uInt32 cChar ) const +{ + return mpImpl->GetPrevChar( cChar ); +} + +// ----------------------------------------------------------------------- + +int FontCharMap::GetIndexFromChar( sal_uInt32 cChar ) const +{ + return mpImpl->GetIndexFromChar( cChar ); +} + +// ----------------------------------------------------------------------- + +sal_uInt32 FontCharMap::GetCharFromIndex( int nIndex ) const +{ + return mpImpl->GetCharFromIndex( nIndex ); +} + +// ======================================================================= + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/octree.cxx b/vcl/source/gdi/octree.cxx new file mode 100644 index 000000000000..c2f0d92f03c3 --- /dev/null +++ b/vcl/source/gdi/octree.cxx @@ -0,0 +1,372 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <limits.h> +#include <vcl/bmpacc.hxx> +#include <vcl/impoct.hxx> +#include <vcl/octree.hxx> + +// --------- +// - pMask - +// --------- + +static sal_uInt8 pImplMask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + +// ------------- +// - NodeCache - +// ------------- + +ImpNodeCache::ImpNodeCache( const sal_uLong nInitSize ) : + pActNode( NULL ) +{ + const sal_uLong nSize = nInitSize + 4; + + for( sal_uLong i = 0; i < nSize; i++ ) + { + OctreeNode* pNewNode = new NODE; + + pNewNode->pNextInCache = pActNode; + pActNode = pNewNode; + } +} + +// ------------------------------------------------------------------------ + +ImpNodeCache::~ImpNodeCache() +{ + while( pActNode ) + { + OctreeNode* pNode = pActNode; + + pActNode = pNode->pNextInCache; + delete pNode; + } +} + +// ---------- +// - Octree - +// ---------- + +Octree::Octree( sal_uLong nColors ) : + nMax ( nColors ), + nLeafCount ( 0L ), + pTree ( NULL ), + pAcc ( NULL ) +{ + pNodeCache = new ImpNodeCache( nColors ); + memset( pReduce, 0, ( OCTREE_BITS + 1 ) * sizeof( PNODE ) ); +} + +// ------------------------------------------------------------------------ + +Octree::Octree( const BitmapReadAccess& rReadAcc, sal_uLong nColors ) : + nMax ( nColors ), + nLeafCount ( 0L ), + pTree ( NULL ), + pAcc ( &rReadAcc ) +{ + pNodeCache = new ImpNodeCache( nColors ); + memset( pReduce, 0, ( OCTREE_BITS + 1 ) * sizeof( PNODE ) ); + ImplCreateOctree(); +} + +// ------------------------------------------------------------------------ + +Octree::~Octree() +{ + ImplDeleteOctree( &pTree ); + delete pNodeCache; +} + +// ------------------------------------------------------------------------ + +void Octree::AddColor( const BitmapColor& rColor ) +{ + pColor = &(BitmapColor&) rColor; + nLevel = 0L; + ImplAdd( &pTree ); + + while( nLeafCount > nMax ) + ImplReduce(); +} + +// ------------------------------------------------------------------------ + +void Octree::ImplCreateOctree() +{ + if( !!*pAcc ) + { + const long nWidth = pAcc->Width(); + const long nHeight = pAcc->Height(); + + if( pAcc->HasPalette() ) + { + for( long nY = 0; nY < nHeight; nY++ ) + { + for( long nX = 0; nX < nWidth; nX++ ) + { + pColor = &(BitmapColor&) pAcc->GetPaletteColor( pAcc->GetPixel( nY, nX ) ); + nLevel = 0L; + ImplAdd( &pTree ); + + while( nLeafCount > nMax ) + ImplReduce(); + } + } + } + else + { + BitmapColor aColor; + + pColor = &aColor; + + for( long nY = 0; nY < nHeight; nY++ ) + { + for( long nX = 0; nX < nWidth; nX++ ) + { + aColor = pAcc->GetPixel( nY, nX ); + nLevel = 0L; + ImplAdd( &pTree ); + + while( nLeafCount > nMax ) + ImplReduce(); + } + } + } + } +} + +// ------------------------------------------------------------------------ + +void Octree::ImplDeleteOctree( PPNODE ppNode ) +{ + for ( sal_uLong i = 0UL; i < 8UL; i++ ) + { + if ( (*ppNode)->pChild[ i ] ) + ImplDeleteOctree( &(*ppNode)->pChild[ i ] ); + } + + pNodeCache->ImplReleaseNode( *ppNode ); + *ppNode = NULL; +} + +// ------------------------------------------------------------------------ + +void Octree::ImplAdd( PPNODE ppNode ) +{ + // ggf. neuen Knoten erzeugen + if( !*ppNode ) + { + *ppNode = pNodeCache->ImplGetFreeNode(); + (*ppNode)->bLeaf = ( OCTREE_BITS == nLevel ); + + if( (*ppNode)->bLeaf ) + nLeafCount++; + else + { + (*ppNode)->pNext = pReduce[ nLevel ]; + pReduce[ nLevel ] = *ppNode; + } + } + + if( (*ppNode)->bLeaf ) + { + (*ppNode)->nCount++; + (*ppNode)->nRed += pColor->GetRed(); + (*ppNode)->nGreen += pColor->GetGreen(); + (*ppNode)->nBlue += pColor->GetBlue(); + } + else + { + const sal_uLong nShift = 7 - nLevel; + const sal_uInt8 cMask = pImplMask[ nLevel ]; + const sal_uLong nIndex = ( ( ( pColor->GetRed() & cMask ) >> nShift ) << 2 ) | + ( ( ( pColor->GetGreen() & cMask ) >> nShift ) << 1 ) | + ( ( pColor->GetBlue() & cMask ) >> nShift ); + + nLevel++; + ImplAdd( &(*ppNode)->pChild[ nIndex ] ); + } +} + +// ------------------------------------------------------------------------ + +void Octree::ImplReduce() +{ + sal_uLong i; + PNODE pNode; + sal_uLong nRedSum = 0L; + sal_uLong nGreenSum = 0L; + sal_uLong nBlueSum = 0L; + sal_uLong nChilds = 0L; + + for ( i = OCTREE_BITS - 1; i && !pReduce[i]; i-- ) {} + + pNode = pReduce[ i ]; + pReduce[ i ] = pNode->pNext; + + for ( i = 0; i < 8; i++ ) + { + if ( pNode->pChild[ i ] ) + { + PNODE pChild = pNode->pChild[ i ]; + + nRedSum += pChild->nRed; + nGreenSum += pChild->nGreen; + nBlueSum += pChild->nBlue; + pNode->nCount += pChild->nCount; + + pNodeCache->ImplReleaseNode( pNode->pChild[ i ] ); + pNode->pChild[ i ] = NULL; + nChilds++; + } + } + + pNode->bLeaf = sal_True; + pNode->nRed = nRedSum; + pNode->nGreen = nGreenSum; + pNode->nBlue = nBlueSum; + nLeafCount -= --nChilds; +} + +// ------------------------------------------------------------------------ + +void Octree::CreatePalette( PNODE pNode ) +{ + if( pNode->bLeaf ) + { + pNode->nPalIndex = nPalIndex; + aPal[ nPalIndex++ ] = BitmapColor( (sal_uInt8) ( (double) pNode->nRed / pNode->nCount ), + (sal_uInt8) ( (double) pNode->nGreen / pNode->nCount ), + (sal_uInt8) ( (double) pNode->nBlue / pNode->nCount ) ); + } + else for( sal_uLong i = 0UL; i < 8UL; i++ ) + if( pNode->pChild[ i ] ) + CreatePalette( pNode->pChild[ i ] ); + +} + +// ------------------------------------------------------------------------ + +void Octree::GetPalIndex( PNODE pNode ) +{ + if ( pNode->bLeaf ) + nPalIndex = pNode->nPalIndex; + else + { + const sal_uLong nShift = 7 - nLevel; + const sal_uInt8 cMask = pImplMask[ nLevel++ ]; + const sal_uLong nIndex = ( ( ( pColor->GetRed() & cMask ) >> nShift ) << 2 ) | + ( ( ( pColor->GetGreen() & cMask ) >> nShift ) << 1 ) | + ( ( pColor->GetBlue() & cMask ) >> nShift ); + + GetPalIndex( pNode->pChild[ nIndex ] ); + } +} + +// ------------------- +// - InverseColorMap - +// ------------------- + +InverseColorMap::InverseColorMap( const BitmapPalette& rPal ) : + nBits( 8 - OCTREE_BITS ) +{ + sal_uLong* cdp; + sal_uInt8* crgbp; + const sal_uLong nColorMax = 1 << OCTREE_BITS; + const sal_uLong xsqr = 1 << ( nBits << 1 ); + const sal_uLong xsqr2 = xsqr << 1; + const sal_uLong nColors = rPal.GetEntryCount(); + const long x = 1L << nBits; + const long x2 = x >> 1L; + sal_uLong r, g, b; + long rxx, gxx, bxx; + long rdist, gdist, bdist; + long crinc, cginc, cbinc; + + ImplCreateBuffers( nColorMax ); + + for( sal_uLong nIndex = 0; nIndex < nColors; nIndex++ ) + { + const BitmapColor& rColor = rPal[ (sal_uInt16) nIndex ]; + const sal_uInt8 cRed = rColor.GetRed(); + const sal_uInt8 cGreen = rColor.GetGreen(); + const sal_uInt8 cBlue = rColor.GetBlue(); + + rdist = cRed - x2; + gdist = cGreen - x2; + bdist = cBlue - x2; + rdist = rdist*rdist + gdist*gdist + bdist*bdist; + + crinc = ( xsqr - ( cRed << nBits ) ) << 1L; + cginc = ( xsqr - ( cGreen << nBits ) ) << 1L; + cbinc = ( xsqr - ( cBlue << nBits ) ) << 1L; + + cdp = (sal_uLong*) pBuffer; + crgbp = pMap; + + for( r = 0, rxx = crinc; r < nColorMax; rdist += rxx, r++, rxx += xsqr2 ) + { + for( g = 0, gdist = rdist, gxx = cginc; g < nColorMax; gdist += gxx, g++, gxx += xsqr2 ) + { + for( b = 0, bdist = gdist, bxx = cbinc; b < nColorMax; bdist += bxx, b++, cdp++, crgbp++, bxx += xsqr2 ) + if ( !nIndex || ( (long) *cdp ) > bdist ) + { + *cdp = bdist; + *crgbp = (sal_uInt8) nIndex; + } + } + } + } +} + +// ------------------------------------------------------------------------ + +InverseColorMap::~InverseColorMap() +{ + rtl_freeMemory( pBuffer ); + rtl_freeMemory( pMap ); +} + +// ------------------------------------------------------------------------ + +void InverseColorMap::ImplCreateBuffers( const sal_uLong nMax ) +{ + const sal_uLong nCount = nMax * nMax * nMax; + const sal_uLong nSize = nCount * sizeof( sal_uLong ); + + pMap = (sal_uInt8*) rtl_allocateMemory( nCount ); + memset( pMap, 0x00, nCount ); + + pBuffer = (sal_uInt8*) rtl_allocateMemory( nSize ); + memset( pBuffer, 0xff, nSize ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/oldprintadaptor.cxx b/vcl/source/gdi/oldprintadaptor.cxx new file mode 100644 index 000000000000..4d87c06471ee --- /dev/null +++ b/vcl/source/gdi/oldprintadaptor.cxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_vcl.hxx" + +#include "vcl/oldprintadaptor.hxx" +#include "vcl/gdimtf.hxx" + +#include "com/sun/star/awt/Size.hpp" + +#include <vector> + +namespace vcl +{ + struct AdaptorPage + { + GDIMetaFile maPage; + com::sun::star::awt::Size maPageSize; + }; + + struct ImplOldStyleAdaptorData + { + std::vector< AdaptorPage > maPages; + }; +} + +using namespace vcl; +using namespace cppu; +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; + +OldStylePrintAdaptor::OldStylePrintAdaptor( const boost::shared_ptr< Printer >& i_pPrinter ) + : PrinterController( i_pPrinter ) + , mpData( new ImplOldStyleAdaptorData() ) +{ +} + +OldStylePrintAdaptor::~OldStylePrintAdaptor() +{ +} + +void OldStylePrintAdaptor::StartPage() +{ + Size aPaperSize( getPrinter()->PixelToLogic( getPrinter()->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) ) ); + mpData->maPages.push_back( AdaptorPage() ); + mpData->maPages.back().maPageSize.Width = aPaperSize.getWidth(); + mpData->maPages.back().maPageSize.Height = aPaperSize.getHeight(); + getPrinter()->SetConnectMetaFile( &mpData->maPages.back().maPage ); + + // copy state into metafile + boost::shared_ptr<Printer> pPrinter( getPrinter() ); + pPrinter->SetMapMode( pPrinter->GetMapMode() ); + pPrinter->SetFont( pPrinter->GetFont() ); + pPrinter->SetDrawMode( pPrinter->GetDrawMode() ); + pPrinter->SetLineColor( pPrinter->GetLineColor() ); + pPrinter->SetFillColor( pPrinter->GetFillColor() ); +} + +void OldStylePrintAdaptor::EndPage() +{ + getPrinter()->SetConnectMetaFile( NULL ); + mpData->maPages.back().maPage.WindStart(); +} + +int OldStylePrintAdaptor::getPageCount() const +{ + return int(mpData->maPages.size()); +} + +Sequence< PropertyValue > OldStylePrintAdaptor::getPageParameters( int i_nPage ) const +{ + Sequence< PropertyValue > aRet( 1 ); + aRet[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("PageSize") ); + if( i_nPage < int(mpData->maPages.size() ) ) + aRet[0].Value = makeAny( mpData->maPages[i_nPage].maPageSize ); + else + { + awt::Size aEmpty( 0, 0 ); + aRet[0].Value = makeAny( aEmpty ); + } + return aRet; +} + +void OldStylePrintAdaptor::printPage( int i_nPage ) const +{ + if( i_nPage < int(mpData->maPages.size()) ) + { + mpData->maPages[ i_nPage ].maPage.WindStart(); + mpData->maPages[ i_nPage ].maPage.Play( getPrinter().get() ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/outdev.cxx b/vcl/source/gdi/outdev.cxx new file mode 100644 index 000000000000..efd5484dad01 --- /dev/null +++ b/vcl/source/gdi/outdev.cxx @@ -0,0 +1,2694 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <tools/ref.hxx> +#include <svsys.h> +#include <vcl/salgdi.hxx> +#include <vcl/sallayout.hxx> +#include <vcl/salframe.hxx> +#include <vcl/salvd.hxx> +#include <vcl/salprn.hxx> +#include <tools/debug.hxx> +#include <vcl/svdata.hxx> +#include <vcl/svapp.hxx> +#include <vcl/ctrl.hxx> +#include <tools/poly.hxx> +#include <vcl/region.hxx> +#include <vcl/region.h> +#include <vcl/virdev.hxx> +#include <vcl/window.h> +#include <vcl/window.hxx> +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/outdata.hxx> +#include <vcl/print.hxx> +#include <vcl/outdev.h> +#include <vcl/outdev.hxx> +#include <vcl/unowrap.hxx> +#include <vcl/sysdata.hxx> + +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dlinegeometry.hxx> + +#include <com/sun/star/awt/XGraphics.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <vcl/unohelp.hxx> + +#include <numeric> + +using namespace ::com::sun::star; + +DBG_NAME( OutputDevice ) +DBG_NAME( Polygon ) +DBG_NAME( PolyPolygon ) +DBG_NAMEEX( Region ) + +// ----------------------------------------------------------------------- + +#ifdef DBG_UTIL +const char* ImplDbgCheckOutputDevice( const void* pObj ) +{ + DBG_TESTSOLARMUTEX(); + + const OutputDevice* pOutDev = (OutputDevice*)pObj; + + if ( (pOutDev->GetOutDevType() != OUTDEV_DONTKNOW) && + (pOutDev->GetOutDevType() != OUTDEV_WINDOW) && + (pOutDev->GetOutDevType() != OUTDEV_PRINTER) && + (pOutDev->GetOutDevType() != OUTDEV_VIRDEV) ) + return "OutputDevice data overwrite"; + + return NULL; +} +#endif + +// ======================================================================= + +#define OUTDEV_POLYPOLY_STACKBUF 32 + +// ======================================================================= + +struct ImplObjStack +{ + ImplObjStack* mpPrev; + MapMode* mpMapMode; + Region* mpClipRegion; + Color* mpLineColor; + Color* mpFillColor; + Font* mpFont; + Color* mpTextColor; + Color* mpTextFillColor; + Color* mpTextLineColor; + Color* mpOverlineColor; + Point* mpRefPoint; + TextAlign meTextAlign; + RasterOp meRasterOp; + sal_uLong mnTextLayoutMode; + LanguageType meTextLanguage; + sal_uInt16 mnFlags; +}; + +// ----------------------------------------------------------------------- + +static void ImplDeleteObjStack( ImplObjStack* pObjStack ) +{ + if ( pObjStack->mnFlags & PUSH_LINECOLOR ) + { + if ( pObjStack->mpLineColor ) + delete pObjStack->mpLineColor; + } + if ( pObjStack->mnFlags & PUSH_FILLCOLOR ) + { + if ( pObjStack->mpFillColor ) + delete pObjStack->mpFillColor; + } + if ( pObjStack->mnFlags & PUSH_FONT ) + delete pObjStack->mpFont; + if ( pObjStack->mnFlags & PUSH_TEXTCOLOR ) + delete pObjStack->mpTextColor; + if ( pObjStack->mnFlags & PUSH_TEXTFILLCOLOR ) + { + if ( pObjStack->mpTextFillColor ) + delete pObjStack->mpTextFillColor; + } + if ( pObjStack->mnFlags & PUSH_TEXTLINECOLOR ) + { + if ( pObjStack->mpTextLineColor ) + delete pObjStack->mpTextLineColor; + } + if ( pObjStack->mnFlags & PUSH_OVERLINECOLOR ) + { + if ( pObjStack->mpOverlineColor ) + delete pObjStack->mpOverlineColor; + } + if ( pObjStack->mnFlags & PUSH_MAPMODE ) + { + if ( pObjStack->mpMapMode ) + delete pObjStack->mpMapMode; + } + if ( pObjStack->mnFlags & PUSH_CLIPREGION ) + { + if ( pObjStack->mpClipRegion ) + delete pObjStack->mpClipRegion; + } + if ( pObjStack->mnFlags & PUSH_REFPOINT ) + { + if ( pObjStack->mpRefPoint ) + delete pObjStack->mpRefPoint; + } + + delete pObjStack; +} + +// ----------------------------------------------------------------------- + +bool OutputDevice::ImplIsAntiparallel() const +{ + bool bRet = false; + if( ImplGetGraphics() ) + { + if( ( (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) && ! IsRTLEnabled() ) || + ( ! (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) && IsRTLEnabled() ) ) + { + bRet = true; + } + } + return bRet; +} + +// ----------------------------------------------------------------------- + + +bool OutputDevice::ImplSelectClipRegion( const Region& rRegion, SalGraphics* pGraphics ) +{ + DBG_TESTSOLARMUTEX(); + + if( !pGraphics ) + { + if( !mpGraphics ) + if( !ImplGetGraphics() ) + return false; + pGraphics = mpGraphics; + } + + bool bClipRegion = pGraphics->SetClipRegion( rRegion, this ); + OSL_ENSURE( bClipRegion, "OutputDevice::ImplSelectClipRegion() - can't cerate region" ); + return bClipRegion; +} + + +// ======================================================================= + +Polygon ImplSubdivideBezier( const Polygon& rPoly ) +{ + Polygon aPoly; + + // #100127# Use adaptive subdivide instead of fixed 25 segments + rPoly.AdaptiveSubdivide( aPoly ); + + return aPoly; +} + +// ======================================================================= + +PolyPolygon ImplSubdivideBezier( const PolyPolygon& rPolyPoly ) +{ + sal_uInt16 i, nPolys = rPolyPoly.Count(); + PolyPolygon aPolyPoly( nPolys ); + for( i=0; i<nPolys; ++i ) + aPolyPoly.Insert( ImplSubdivideBezier( rPolyPoly.GetObject(i) ) ); + + return aPolyPoly; +} + +// ======================================================================= + +// #100127# Extracted from OutputDevice::DrawPolyPolygon() +void OutputDevice::ImplDrawPolyPolygon( sal_uInt16 nPoly, const PolyPolygon& rPolyPoly ) +{ + // AW: This crashes on empty PolyPolygons, avoid that + if(!nPoly) + return; + + sal_uInt32 aStackAry1[OUTDEV_POLYPOLY_STACKBUF]; + PCONSTSALPOINT aStackAry2[OUTDEV_POLYPOLY_STACKBUF]; + sal_uInt8* aStackAry3[OUTDEV_POLYPOLY_STACKBUF]; + sal_uInt32* pPointAry; + PCONSTSALPOINT* pPointAryAry; + const sal_uInt8** pFlagAryAry; + sal_uInt16 i = 0, j = 0, last = 0; + sal_Bool bHaveBezier = sal_False; + if ( nPoly > OUTDEV_POLYPOLY_STACKBUF ) + { + pPointAry = new sal_uInt32[nPoly]; + pPointAryAry = new PCONSTSALPOINT[nPoly]; + pFlagAryAry = new const sal_uInt8*[nPoly]; + } + else + { + pPointAry = aStackAry1; + pPointAryAry = aStackAry2; + pFlagAryAry = (const sal_uInt8**)aStackAry3; + } + do + { + const Polygon& rPoly = rPolyPoly.GetObject( i ); + sal_uInt16 nSize = rPoly.GetSize(); + if ( nSize ) + { + pPointAry[j] = nSize; + pPointAryAry[j] = (PCONSTSALPOINT)rPoly.GetConstPointAry(); + pFlagAryAry[j] = rPoly.GetConstFlagAry(); + last = i; + + if( pFlagAryAry[j] ) + bHaveBezier = sal_True; + + ++j; + } + + ++i; + } + while ( i < nPoly ); + + if ( j == 1 ) + { + // #100127# Forward beziers to sal, if any + if( bHaveBezier ) + { + if( !mpGraphics->DrawPolygonBezier( *pPointAry, *pPointAryAry, *pFlagAryAry, this ) ) + { + Polygon aPoly = ImplSubdivideBezier( rPolyPoly.GetObject( last ) ); + mpGraphics->DrawPolygon( aPoly.GetSize(), (const SalPoint*)aPoly.GetConstPointAry(), this ); + } + } + else + { + mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this ); + } + } + else + { + // #100127# Forward beziers to sal, if any + if( bHaveBezier ) + { + if( !mpGraphics->DrawPolyPolygonBezier( j, pPointAry, pPointAryAry, pFlagAryAry, this ) ) + { + PolyPolygon aPolyPoly = ImplSubdivideBezier( rPolyPoly ); + ImplDrawPolyPolygon( aPolyPoly.Count(), aPolyPoly ); + } + } + else + { + mpGraphics->DrawPolyPolygon( j, pPointAry, pPointAryAry, this ); + } + } + + if ( pPointAry != aStackAry1 ) + { + delete[] pPointAry; + delete[] pPointAryAry; + delete[] pFlagAryAry; + } +} + +// ======================================================================= + +OutputDevice::OutputDevice() : + maRegion( REGION_NULL ), + maFillColor( COL_WHITE ), + maTextLineColor( COL_TRANSPARENT ), + maSettings( Application::GetSettings() ) +{ + DBG_CTOR( OutputDevice, ImplDbgCheckOutputDevice ); + + mpGraphics = NULL; + mpUnoGraphicsList = NULL; + mpPrevGraphics = NULL; + mpNextGraphics = NULL; + mpMetaFile = NULL; + mpFontEntry = NULL; + mpFontCache = NULL; + mpFontList = NULL; + mpGetDevFontList = NULL; + mpGetDevSizeList = NULL; + mpObjStack = NULL; + mpOutDevData = NULL; + mpPDFWriter = NULL; + mpAlphaVDev = NULL; + mpExtOutDevData = NULL; + mnOutOffX = 0; + mnOutOffY = 0; + mnOutWidth = 0; + mnOutHeight = 0; + mnDPIX = 0; + mnDPIY = 0; + mnTextOffX = 0; + mnTextOffY = 0; + mnOutOffOrigX = 0; + mnOutOffLogicX = 0; + mnOutOffOrigY = 0; + mnOutOffLogicY = 0; + mnEmphasisAscent = 0; + mnEmphasisDescent = 0; + mnDrawMode = 0; + mnTextLayoutMode = TEXT_LAYOUT_DEFAULT; + if( Application::GetSettings().GetLayoutRTL() ) //#i84553# tip BiDi preference to RTL + mnTextLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_LEFT; + meOutDevType = OUTDEV_DONTKNOW; + meOutDevViewType = OUTDEV_VIEWTYPE_DONTKNOW; + mbMap = sal_False; + mbMapIsDefault = sal_True; + mbClipRegion = sal_False; + mbBackground = sal_False; + mbOutput = sal_True; + mbDevOutput = sal_False; + mbOutputClipped = sal_False; + maTextColor = Color( COL_BLACK ); + maOverlineColor = Color( COL_TRANSPARENT ); + meTextAlign = maFont.GetAlign(); + meRasterOp = ROP_OVERPAINT; + mnAntialiasing = 0; + meTextLanguage = 0; // TODO: get default from configuration? + mbLineColor = sal_True; + mbFillColor = sal_True; + mbInitLineColor = sal_True; + mbInitFillColor = sal_True; + mbInitFont = sal_True; + mbInitTextColor = sal_True; + mbInitClipRegion = sal_True; + mbClipRegionSet = sal_False; + mbKerning = sal_False; + mbNewFont = sal_True; + mbTextLines = sal_False; + mbTextSpecial = sal_False; + mbRefPoint = sal_False; + mbEnableRTL = sal_False; // mirroring must be explicitly allowed (typically for windows only) + + // struct ImplMapRes + maMapRes.mnMapOfsX = 0; + maMapRes.mnMapOfsY = 0; + maMapRes.mnMapScNumX = 1; + maMapRes.mnMapScNumY = 1; + maMapRes.mnMapScDenomX = 1; + maMapRes.mnMapScDenomY = 1; + // struct ImplThresholdRes + maThresRes.mnThresLogToPixX = 0; + maThresRes.mnThresLogToPixY = 0; + maThresRes.mnThresPixToLogX = 0; + maThresRes.mnThresPixToLogY = 0; +} + +// ----------------------------------------------------------------------- + +OutputDevice::~OutputDevice() +{ + DBG_DTOR( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( GetUnoGraphicsList() ) + { + UnoWrapperBase* pWrapper = Application::GetUnoWrapper( sal_False ); + if ( pWrapper ) + pWrapper->ReleaseAllGraphics( this ); + delete mpUnoGraphicsList; + mpUnoGraphicsList = NULL; + } + + if ( mpOutDevData ) + ImplDeInitOutDevData(); + + ImplObjStack* pData = mpObjStack; + if ( pData ) + { + DBG_ERRORFILE( "OutputDevice::~OutputDevice(): OutputDevice::Push() calls != OutputDevice::Pop() calls" ); + while ( pData ) + { + ImplObjStack* pTemp = pData; + pData = pData->mpPrev; + ImplDeleteObjStack( pTemp ); + } + } + + // release the active font instance + if( mpFontEntry ) + mpFontCache->Release( mpFontEntry ); + // remove cached results of GetDevFontList/GetDevSizeList + // TODO: use smart pointers for them + if( mpGetDevFontList ) + delete mpGetDevFontList; + if( mpGetDevSizeList ) + delete mpGetDevSizeList; + + // release ImplFontCache specific to this OutputDevice + // TODO: refcount ImplFontCache + if( mpFontCache + && (mpFontCache != ImplGetSVData()->maGDIData.mpScreenFontCache) + && (ImplGetSVData()->maGDIData.mpScreenFontCache != NULL) ) + { + delete mpFontCache; + mpFontCache = NULL; + } + + // release ImplFontList specific to this OutputDevice + // TODO: refcount ImplFontList + if( mpFontList + && (mpFontList != ImplGetSVData()->maGDIData.mpScreenFontList) + && (ImplGetSVData()->maGDIData.mpScreenFontList != NULL) ) + { + mpFontList->Clear(); + delete mpFontList; + mpFontList = NULL; + } + + delete mpAlphaVDev; +} + +bool OutputDevice::supportsOperation( OutDevSupportType eType ) const +{ + if( !mpGraphics ) + if( !ImplGetGraphics() ) + return false; + const bool bHasSupport = mpGraphics->supportsOperation( eType ); + return bHasSupport; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::EnableRTL( sal_Bool bEnable ) +{ + mbEnableRTL = (bEnable != 0); + if( meOutDevType == OUTDEV_VIRDEV ) + { + // virdevs default to not mirroring, they will only be set to mirroring + // under rare circumstances in the UI, eg the valueset control + // because each virdev has its own SalGraphics we can safely switch the SalGraphics here + // ...hopefully + if( ImplGetGraphics() ) + mpGraphics->SetLayout( mbEnableRTL ? SAL_LAYOUT_BIDI_RTL : 0 ); + } + + // convenience: for controls also switch layout mode + if( dynamic_cast<Control*>(this) != 0 ) + SetLayoutMode( bEnable ? TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_LEFT : TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_TEXTORIGIN_LEFT); + + Window* pWin = dynamic_cast<Window*>(this); + if( pWin ) + pWin->StateChanged( STATE_CHANGE_MIRRORING ); + + if( mpAlphaVDev ) + mpAlphaVDev->EnableRTL( bEnable ); +} + +sal_Bool OutputDevice::ImplHasMirroredGraphics() +{ + // HOTFIX for #i55719# + if( meOutDevType == OUTDEV_PRINTER ) + return sal_False; + + return ( ImplGetGraphics() && (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) ); +} + +// note: the coordiantes to be remirrored are in frame coordiantes ! + +void OutputDevice::ImplReMirror( Point &rPoint ) const +{ + rPoint.X() = mnOutOffX + mnOutWidth - 1 - rPoint.X() + mnOutOffX; +} +void OutputDevice::ImplReMirror( Rectangle &rRect ) const +{ + long nWidth = rRect.nRight - rRect.nLeft; + + //long lc_x = rRect.nLeft - mnOutOffX; // normalize + //lc_x = mnOutWidth - nWidth - 1 - lc_x; // mirror + //rRect.nLeft = lc_x + mnOutOffX; // re-normalize + + rRect.nLeft = mnOutOffX + mnOutWidth - nWidth - 1 - rRect.nLeft + mnOutOffX; + rRect.nRight = rRect.nLeft + nWidth; +} +void OutputDevice::ImplReMirror( Region &rRegion ) const +{ + long nX; + long nY; + long nWidth; + long nHeight; + ImplRegionInfo aInfo; + sal_Bool bRegionRect; + Region aMirroredRegion; + + bRegionRect = rRegion.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight ); + while ( bRegionRect ) + { + Rectangle aRect( Point(nX, nY), Size(nWidth, nHeight) ); + ImplReMirror( aRect ); + aMirroredRegion.Union( aRect ); + bRegionRect = rRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight ); + } + rRegion = aMirroredRegion; +} + + +// ----------------------------------------------------------------------- + +int OutputDevice::ImplGetGraphics() const +{ + DBG_TESTSOLARMUTEX(); + + if ( mpGraphics ) + return sal_True; + + mbInitLineColor = sal_True; + mbInitFillColor = sal_True; + mbInitFont = sal_True; + mbInitTextColor = sal_True; + mbInitClipRegion = sal_True; + + ImplSVData* pSVData = ImplGetSVData(); + if ( meOutDevType == OUTDEV_WINDOW ) + { + Window* pWindow = (Window*)this; + + mpGraphics = pWindow->mpWindowImpl->mpFrame->GetGraphics(); + // try harder if no wingraphics was available directly + if ( !mpGraphics ) + { + // find another output device in the same frame + OutputDevice* pReleaseOutDev = pSVData->maGDIData.mpLastWinGraphics; + while ( pReleaseOutDev ) + { + if ( ((Window*)pReleaseOutDev)->mpWindowImpl->mpFrame == pWindow->mpWindowImpl->mpFrame ) + break; + pReleaseOutDev = pReleaseOutDev->mpPrevGraphics; + } + + if ( pReleaseOutDev ) + { + // steal the wingraphics from the other outdev + mpGraphics = pReleaseOutDev->mpGraphics; + pReleaseOutDev->ImplReleaseGraphics( sal_False ); + } + else + { + // if needed retry after releasing least recently used wingraphics + while ( !mpGraphics ) + { + if ( !pSVData->maGDIData.mpLastWinGraphics ) + break; + pSVData->maGDIData.mpLastWinGraphics->ImplReleaseGraphics(); + mpGraphics = pWindow->mpWindowImpl->mpFrame->GetGraphics(); + } + } + } + + // update global LRU list of wingraphics + if ( mpGraphics ) + { + mpNextGraphics = pSVData->maGDIData.mpFirstWinGraphics; + pSVData->maGDIData.mpFirstWinGraphics = const_cast<OutputDevice*>(this); + if ( mpNextGraphics ) + mpNextGraphics->mpPrevGraphics = const_cast<OutputDevice*>(this); + if ( !pSVData->maGDIData.mpLastWinGraphics ) + pSVData->maGDIData.mpLastWinGraphics = const_cast<OutputDevice*>(this); + } + } + else if ( meOutDevType == OUTDEV_VIRDEV ) + { + const VirtualDevice* pVirDev = (const VirtualDevice*)this; + + if ( pVirDev->mpVirDev ) + { + mpGraphics = pVirDev->mpVirDev->GetGraphics(); + // if needed retry after releasing least recently used virtual device graphics + while ( !mpGraphics ) + { + if ( !pSVData->maGDIData.mpLastVirGraphics ) + break; + pSVData->maGDIData.mpLastVirGraphics->ImplReleaseGraphics(); + mpGraphics = pVirDev->mpVirDev->GetGraphics(); + } + // update global LRU list of virtual device graphics + if ( mpGraphics ) + { + mpNextGraphics = pSVData->maGDIData.mpFirstVirGraphics; + pSVData->maGDIData.mpFirstVirGraphics = const_cast<OutputDevice*>(this); + if ( mpNextGraphics ) + mpNextGraphics->mpPrevGraphics = const_cast<OutputDevice*>(this); + if ( !pSVData->maGDIData.mpLastVirGraphics ) + pSVData->maGDIData.mpLastVirGraphics = const_cast<OutputDevice*>(this); + } + } + } + else if ( meOutDevType == OUTDEV_PRINTER ) + { + const Printer* pPrinter = (const Printer*)this; + + if ( pPrinter->mpJobGraphics ) + mpGraphics = pPrinter->mpJobGraphics; + else if ( pPrinter->mpDisplayDev ) + { + const VirtualDevice* pVirDev = pPrinter->mpDisplayDev; + mpGraphics = pVirDev->mpVirDev->GetGraphics(); + // if needed retry after releasing least recently used virtual device graphics + while ( !mpGraphics ) + { + if ( !pSVData->maGDIData.mpLastVirGraphics ) + break; + pSVData->maGDIData.mpLastVirGraphics->ImplReleaseGraphics(); + mpGraphics = pVirDev->mpVirDev->GetGraphics(); + } + // update global LRU list of virtual device graphics + if ( mpGraphics ) + { + mpNextGraphics = pSVData->maGDIData.mpFirstVirGraphics; + pSVData->maGDIData.mpFirstVirGraphics = const_cast<OutputDevice*>(this); + if ( mpNextGraphics ) + mpNextGraphics->mpPrevGraphics = const_cast<OutputDevice*>(this); + if ( !pSVData->maGDIData.mpLastVirGraphics ) + pSVData->maGDIData.mpLastVirGraphics = const_cast<OutputDevice*>(this); + } + } + else + { + mpGraphics = pPrinter->mpInfoPrinter->GetGraphics(); + // if needed retry after releasing least recently used printer graphics + while ( !mpGraphics ) + { + if ( !pSVData->maGDIData.mpLastPrnGraphics ) + break; + pSVData->maGDIData.mpLastPrnGraphics->ImplReleaseGraphics(); + mpGraphics = pPrinter->mpInfoPrinter->GetGraphics(); + } + // update global LRU list of printer graphics + if ( mpGraphics ) + { + mpNextGraphics = pSVData->maGDIData.mpFirstPrnGraphics; + pSVData->maGDIData.mpFirstPrnGraphics = const_cast<OutputDevice*>(this); + if ( mpNextGraphics ) + mpNextGraphics->mpPrevGraphics = const_cast<OutputDevice*>(this); + if ( !pSVData->maGDIData.mpLastPrnGraphics ) + pSVData->maGDIData.mpLastPrnGraphics = const_cast<OutputDevice*>(this); + } + } + } + + if ( mpGraphics ) + { + mpGraphics->SetXORMode( (ROP_INVERT == meRasterOp) || (ROP_XOR == meRasterOp), ROP_INVERT == meRasterOp ); + mpGraphics->setAntiAliasB2DDraw(mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW); + return sal_True; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplReleaseGraphics( sal_Bool bRelease ) +{ + DBG_TESTSOLARMUTEX(); + + if ( !mpGraphics ) + return; + + // release the fonts of the physically released graphics device + if( bRelease ) + { +#ifndef UNX + // HACK to fix an urgent P1 printing issue fast + // WinSalPrinter does not respect GetGraphics/ReleaseGraphics conventions + // so Printer::mpGraphics often points to a dead WinSalGraphics + // TODO: fix WinSalPrinter's GetGraphics/ReleaseGraphics handling + if( meOutDevType != OUTDEV_PRINTER ) +#endif + mpGraphics->ReleaseFonts(); + + mbNewFont = true; + mbInitFont = true; + + if ( mpFontEntry ) + { + mpFontCache->Release( mpFontEntry ); + mpFontEntry = NULL; + } + + if ( mpGetDevFontList ) + { + delete mpGetDevFontList; + mpGetDevFontList = NULL; + } + + if ( mpGetDevSizeList ) + { + delete mpGetDevSizeList; + mpGetDevSizeList = NULL; + } + } + + ImplSVData* pSVData = ImplGetSVData(); + if ( meOutDevType == OUTDEV_WINDOW ) + { + Window* pWindow = (Window*)this; + + if ( bRelease ) + pWindow->mpWindowImpl->mpFrame->ReleaseGraphics( mpGraphics ); + // remove from global LRU list of window graphics + if ( mpPrevGraphics ) + mpPrevGraphics->mpNextGraphics = mpNextGraphics; + else + pSVData->maGDIData.mpFirstWinGraphics = mpNextGraphics; + if ( mpNextGraphics ) + mpNextGraphics->mpPrevGraphics = mpPrevGraphics; + else + pSVData->maGDIData.mpLastWinGraphics = mpPrevGraphics; + } + else if ( meOutDevType == OUTDEV_VIRDEV ) + { + VirtualDevice* pVirDev = (VirtualDevice*)this; + + if ( bRelease ) + pVirDev->mpVirDev->ReleaseGraphics( mpGraphics ); + // remove from global LRU list of virtual device graphics + if ( mpPrevGraphics ) + mpPrevGraphics->mpNextGraphics = mpNextGraphics; + else + pSVData->maGDIData.mpFirstVirGraphics = mpNextGraphics; + if ( mpNextGraphics ) + mpNextGraphics->mpPrevGraphics = mpPrevGraphics; + else + pSVData->maGDIData.mpLastVirGraphics = mpPrevGraphics; + } + else if ( meOutDevType == OUTDEV_PRINTER ) + { + Printer* pPrinter = (Printer*)this; + + if ( !pPrinter->mpJobGraphics ) + { + if ( pPrinter->mpDisplayDev ) + { + VirtualDevice* pVirDev = pPrinter->mpDisplayDev; + if ( bRelease ) + pVirDev->mpVirDev->ReleaseGraphics( mpGraphics ); + // remove from global LRU list of virtual device graphics + if ( mpPrevGraphics ) + mpPrevGraphics->mpNextGraphics = mpNextGraphics; + else + pSVData->maGDIData.mpFirstVirGraphics = mpNextGraphics; + if ( mpNextGraphics ) + mpNextGraphics->mpPrevGraphics = mpPrevGraphics; + else + pSVData->maGDIData.mpLastVirGraphics = mpPrevGraphics; + } + else + { + if ( bRelease ) + pPrinter->mpInfoPrinter->ReleaseGraphics( mpGraphics ); + // remove from global LRU list of printer graphics + if ( mpPrevGraphics ) + mpPrevGraphics->mpNextGraphics = mpNextGraphics; + else + pSVData->maGDIData.mpFirstPrnGraphics = mpNextGraphics; + if ( mpNextGraphics ) + mpNextGraphics->mpPrevGraphics = mpPrevGraphics; + else + pSVData->maGDIData.mpLastPrnGraphics = mpPrevGraphics; + } + } + } + + mpGraphics = NULL; + mpPrevGraphics = NULL; + mpNextGraphics = NULL; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitOutDevData() +{ + if ( !mpOutDevData ) + { + mpOutDevData = new ImplOutDevData; + mpOutDevData->mpRotateDev = NULL; + mpOutDevData->mpRecordLayout = NULL; + + // #i75163# + mpOutDevData->mpViewTransform = NULL; + mpOutDevData->mpInverseViewTransform = NULL; + } +} + +// ----------------------------------------------------------------------- + +// #i75163# +void OutputDevice::ImplInvalidateViewTransform() +{ + if(mpOutDevData) + { + if(mpOutDevData->mpViewTransform) + { + delete mpOutDevData->mpViewTransform; + mpOutDevData->mpViewTransform = NULL; + } + + if(mpOutDevData->mpInverseViewTransform) + { + delete mpOutDevData->mpInverseViewTransform; + mpOutDevData->mpInverseViewTransform = NULL; + } + } +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::ImplIsRecordLayout() const +{ + return mpOutDevData && mpOutDevData->mpRecordLayout; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDeInitOutDevData() +{ + if ( mpOutDevData ) + { + if ( mpOutDevData->mpRotateDev ) + delete mpOutDevData->mpRotateDev; + + // #i75163# + ImplInvalidateViewTransform(); + + delete mpOutDevData; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitLineColor() +{ + DBG_TESTSOLARMUTEX(); + + if( mbLineColor ) + { + if( ROP_0 == meRasterOp ) + mpGraphics->SetROPLineColor( SAL_ROP_0 ); + else if( ROP_1 == meRasterOp ) + mpGraphics->SetROPLineColor( SAL_ROP_1 ); + else if( ROP_INVERT == meRasterOp ) + mpGraphics->SetROPLineColor( SAL_ROP_INVERT ); + else + mpGraphics->SetLineColor( ImplColorToSal( maLineColor ) ); + } + else + mpGraphics->SetLineColor(); + + mbInitLineColor = sal_False; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitFillColor() +{ + DBG_TESTSOLARMUTEX(); + + if( mbFillColor ) + { + if( ROP_0 == meRasterOp ) + mpGraphics->SetROPFillColor( SAL_ROP_0 ); + else if( ROP_1 == meRasterOp ) + mpGraphics->SetROPFillColor( SAL_ROP_1 ); + else if( ROP_INVERT == meRasterOp ) + mpGraphics->SetROPFillColor( SAL_ROP_INVERT ); + else + mpGraphics->SetFillColor( ImplColorToSal( maFillColor ) ); + } + else + mpGraphics->SetFillColor(); + + mbInitFillColor = sal_False; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitClipRegion() +{ + DBG_TESTSOLARMUTEX(); + + if ( GetOutDevType() == OUTDEV_WINDOW ) + { + Window* pWindow = (Window*)this; + Region aRegion; + + // Hintergrund-Sicherung zuruecksetzen + if ( pWindow->mpWindowImpl->mpFrameData->mpFirstBackWin ) + pWindow->ImplInvalidateAllOverlapBackgrounds(); + if ( pWindow->mpWindowImpl->mbInPaint ) + aRegion = *(pWindow->mpWindowImpl->mpPaintRegion); + else + { + aRegion = *(pWindow->ImplGetWinChildClipRegion()); + // --- RTL -- only this region is in frame coordinates, so re-mirror it + // the mpWindowImpl->mpPaintRegion above is already correct (see ImplCallPaint()) ! + if( ImplIsAntiparallel() ) + ImplReMirror ( aRegion ); + } + if ( mbClipRegion ) + aRegion.Intersect( ImplPixelToDevicePixel( maRegion ) ); + if ( aRegion.IsEmpty() ) + mbOutputClipped = sal_True; + else + { + mbOutputClipped = sal_False; + ImplSelectClipRegion( aRegion ); + } + mbClipRegionSet = sal_True; + } + else + { + if ( mbClipRegion ) + { + if ( maRegion.IsEmpty() ) + mbOutputClipped = sal_True; + else + { + mbOutputClipped = sal_False; + + // #102532# Respect output offset also for clip region + Region aRegion( ImplPixelToDevicePixel( maRegion ) ); + const bool bClipDeviceBounds( ! GetPDFWriter() + && GetOutDevType() != OUTDEV_PRINTER ); + if( bClipDeviceBounds ) + { + // #b6520266# Perform actual rect clip against outdev + // dimensions, to generate empty clips whenever one of the + // values is completely off the device. + Rectangle aDeviceBounds( mnOutOffX, mnOutOffY, + mnOutOffX+GetOutputWidthPixel()-1, + mnOutOffY+GetOutputHeightPixel()-1 ); + aRegion.Intersect( aDeviceBounds ); + } + ImplSelectClipRegion( aRegion ); + } + + mbClipRegionSet = sal_True; + } + else + { + if ( mbClipRegionSet ) + { + mpGraphics->ResetClipRegion(); + mbClipRegionSet = sal_False; + } + + mbOutputClipped = sal_False; + } + } + + mbInitClipRegion = sal_False; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplSetClipRegion( const Region* pRegion ) +{ + DBG_TESTSOLARMUTEX(); + + if ( !pRegion ) + { + if ( mbClipRegion ) + { + maRegion = Region( REGION_NULL ); + mbClipRegion = sal_False; + mbInitClipRegion = sal_True; + } + } + else + { + maRegion = *pRegion; + mbClipRegion = sal_True; + mbInitClipRegion = sal_True; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetClipRegion() +{ + OSL_TRACE( "OutputDevice::SetClipRegion()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaClipRegionAction( Region(), sal_False ) ); + + ImplSetClipRegion( NULL ); + + if( mpAlphaVDev ) + mpAlphaVDev->SetClipRegion(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetClipRegion( const Region& rRegion ) +{ + OSL_TRACE( "OutputDevice::SetClipRegion( rRegion )" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaClipRegionAction( rRegion, sal_True ) ); + + if ( rRegion.GetType() == REGION_NULL ) + ImplSetClipRegion( NULL ); + else + { + Region aRegion = LogicToPixel( rRegion ); + ImplSetClipRegion( &aRegion ); + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetClipRegion( rRegion ); +} + +// ----------------------------------------------------------------------- + +Region OutputDevice::GetClipRegion() const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + return PixelToLogic( maRegion ); +} + +// ----------------------------------------------------------------------- + +Region OutputDevice::GetActiveClipRegion() const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( GetOutDevType() == OUTDEV_WINDOW ) + { + Region aRegion( REGION_NULL ); + Window* pWindow = (Window*)this; + if ( pWindow->mpWindowImpl->mbInPaint ) + { + aRegion = *(pWindow->mpWindowImpl->mpPaintRegion); + aRegion.Move( -mnOutOffX, -mnOutOffY ); + } + if ( mbClipRegion ) + aRegion.Intersect( maRegion ); + return PixelToLogic( aRegion ); + } + else + return GetClipRegion(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::MoveClipRegion( long nHorzMove, long nVertMove ) +{ + OSL_TRACE( "OutputDevice::MoveClipRegion()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mbClipRegion ) + { + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaMoveClipRegionAction( nHorzMove, nVertMove ) ); + + maRegion.Move( ImplLogicWidthToDevicePixel( nHorzMove ), + ImplLogicHeightToDevicePixel( nVertMove ) ); + mbInitClipRegion = sal_True; + } + + if( mpAlphaVDev ) + mpAlphaVDev->MoveClipRegion( nHorzMove, nVertMove ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::IntersectClipRegion( const Rectangle& rRect ) +{ + OSL_TRACE( "OutputDevice::IntersectClipRegion( rRect )" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaISectRectClipRegionAction( rRect ) ); + + Rectangle aRect = LogicToPixel( rRect ); + maRegion.Intersect( aRect ); + mbClipRegion = sal_True; + mbInitClipRegion = sal_True; + + if( mpAlphaVDev ) + mpAlphaVDev->IntersectClipRegion( rRect ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::IntersectClipRegion( const Region& rRegion ) +{ + OSL_TRACE( "OutputDevice::IntersectClipRegion( rRegion )" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); + + RegionType eType = rRegion.GetType(); + + if ( eType != REGION_NULL ) + { + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaISectRegionClipRegionAction( rRegion ) ); + + Region aRegion = LogicToPixel( rRegion ); + maRegion.Intersect( aRegion ); + mbClipRegion = sal_True; + mbInitClipRegion = sal_True; + } + + if( mpAlphaVDev ) + mpAlphaVDev->IntersectClipRegion( rRegion ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetDrawMode( sal_uLong nDrawMode ) +{ + OSL_TRACE( "OutputDevice::SetDrawMode( %lx )", nDrawMode ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + mnDrawMode = nDrawMode; + + if( mpAlphaVDev ) + mpAlphaVDev->SetDrawMode( nDrawMode ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetRasterOp( RasterOp eRasterOp ) +{ + OSL_TRACE( "OutputDevice::SetRasterOp( %d )", (int)eRasterOp ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaRasterOpAction( eRasterOp ) ); + + if ( meRasterOp != eRasterOp ) + { + meRasterOp = eRasterOp; + mbInitLineColor = mbInitFillColor = sal_True; + + if( mpGraphics || ImplGetGraphics() ) + mpGraphics->SetXORMode( (ROP_INVERT == meRasterOp) || (ROP_XOR == meRasterOp), ROP_INVERT == meRasterOp ); + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetRasterOp( eRasterOp ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetLineColor() +{ + OSL_TRACE( "OutputDevice::SetLineColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaLineColorAction( Color(), sal_False ) ); + + if ( mbLineColor ) + { + mbInitLineColor = sal_True; + mbLineColor = sal_False; + maLineColor = Color( COL_TRANSPARENT ); + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetLineColor(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetLineColor( const Color& rColor ) +{ + OSL_TRACE( "OutputDevice::SetLineColor( %lx )", rColor.GetColor() ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + + if( mnDrawMode & ( DRAWMODE_BLACKLINE | DRAWMODE_WHITELINE | + DRAWMODE_GRAYLINE | DRAWMODE_GHOSTEDLINE | + DRAWMODE_SETTINGSLINE ) ) + { + if( !ImplIsColorTransparent( aColor ) ) + { + if( mnDrawMode & DRAWMODE_BLACKLINE ) + { + aColor = Color( COL_BLACK ); + } + else if( mnDrawMode & DRAWMODE_WHITELINE ) + { + aColor = Color( COL_WHITE ); + } + else if( mnDrawMode & DRAWMODE_GRAYLINE ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if( mnDrawMode & DRAWMODE_SETTINGSLINE ) + { + aColor = GetSettings().GetStyleSettings().GetFontColor(); + } + + if( mnDrawMode & DRAWMODE_GHOSTEDLINE ) + { + aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80, + ( aColor.GetGreen() >> 1 ) | 0x80, + ( aColor.GetBlue() >> 1 ) | 0x80); + } + } + } + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaLineColorAction( aColor, sal_True ) ); + + if( ImplIsColorTransparent( aColor ) ) + { + if ( mbLineColor ) + { + mbInitLineColor = sal_True; + mbLineColor = sal_False; + maLineColor = Color( COL_TRANSPARENT ); + } + } + else + { + if( maLineColor != aColor ) + { + mbInitLineColor = sal_True; + mbLineColor = sal_True; + maLineColor = aColor; + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetLineColor( COL_BLACK ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetFillColor() +{ + OSL_TRACE( "OutputDevice::SetFillColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaFillColorAction( Color(), sal_False ) ); + + if ( mbFillColor ) + { + mbInitFillColor = sal_True; + mbFillColor = sal_False; + maFillColor = Color( COL_TRANSPARENT ); + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetFillColor(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetFillColor( const Color& rColor ) +{ + OSL_TRACE( "OutputDevice::SetFillColor( %lx )", rColor.GetColor() ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + + if( mnDrawMode & ( DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | + DRAWMODE_GRAYFILL | DRAWMODE_NOFILL | + DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) ) + { + if( !ImplIsColorTransparent( aColor ) ) + { + if( mnDrawMode & DRAWMODE_BLACKFILL ) + { + aColor = Color( COL_BLACK ); + } + else if( mnDrawMode & DRAWMODE_WHITEFILL ) + { + aColor = Color( COL_WHITE ); + } + else if( mnDrawMode & DRAWMODE_GRAYFILL ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if( mnDrawMode & DRAWMODE_NOFILL ) + { + aColor = Color( COL_TRANSPARENT ); + } + else if( mnDrawMode & DRAWMODE_SETTINGSFILL ) + { + aColor = GetSettings().GetStyleSettings().GetWindowColor(); + } + + if( mnDrawMode & DRAWMODE_GHOSTEDFILL ) + { + aColor = Color( (aColor.GetRed() >> 1) | 0x80, + (aColor.GetGreen() >> 1) | 0x80, + (aColor.GetBlue() >> 1) | 0x80); + } + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaFillColorAction( aColor, sal_True ) ); + + if ( ImplIsColorTransparent( aColor ) ) + { + if ( mbFillColor ) + { + mbInitFillColor = sal_True; + mbFillColor = sal_False; + maFillColor = Color( COL_TRANSPARENT ); + } + } + else + { + if ( maFillColor != aColor ) + { + mbInitFillColor = sal_True; + mbFillColor = sal_True; + maFillColor = aColor; + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetFillColor( COL_BLACK ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetBackground() +{ + OSL_TRACE( "OutputDevice::SetBackground()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + maBackground = Wallpaper(); + mbBackground = sal_False; + + if( mpAlphaVDev ) + mpAlphaVDev->SetBackground(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetBackground( const Wallpaper& rBackground ) +{ + OSL_TRACE( "OutputDevice::SetBackground( rBackground )" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + maBackground = rBackground; + + if( rBackground.GetStyle() == WALLPAPER_NULL ) + mbBackground = sal_False; + else + mbBackground = sal_True; + + if( mpAlphaVDev ) + mpAlphaVDev->SetBackground( rBackground ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetRefPoint() +{ + OSL_TRACE( "OutputDevice::SetRefPoint()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaRefPointAction( Point(), sal_False ) ); + + mbRefPoint = sal_False; + maRefPoint.X() = maRefPoint.Y() = 0L; + + if( mpAlphaVDev ) + mpAlphaVDev->SetRefPoint(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetRefPoint( const Point& rRefPoint ) +{ + OSL_TRACE( "OutputDevice::SetRefPoint( rRefPoint )" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaRefPointAction( rRefPoint, sal_True ) ); + + mbRefPoint = sal_True; + maRefPoint = rRefPoint; + + if( mpAlphaVDev ) + mpAlphaVDev->SetRefPoint( rRefPoint ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt ) +{ + OSL_TRACE( "OutputDevice::DrawLine()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaLineAction( rStartPt, rEndPt ) ); + + if ( !IsDeviceOutputNecessary() || !mbLineColor || ImplIsRecordLayout() ) + return; + + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + + // #i101598# support AA and snap for lines, too + if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) + && mpGraphics->supportsOperation(OutDevSupport_B2DDraw) + && ROP_OVERPAINT == GetRasterOp() + && IsLineColor()) + { + // at least transform with double precision to device coordinates; this will + // avoid pixel snap of single, appended lines + const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation()); + const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 ); + basegfx::B2DPolygon aB2DPolyLine; + + aB2DPolyLine.append(basegfx::B2DPoint(rStartPt.X(), rStartPt.Y())); + aB2DPolyLine.append(basegfx::B2DPoint(rEndPt.X(), rEndPt.Y())); + aB2DPolyLine.transform( aTransform ); + + if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE) + { + aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine); + } + + if( mpGraphics->DrawPolyLine( aB2DPolyLine, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this)) + { + return; + } + } + + const Point aStartPt(ImplLogicToDevicePixel(rStartPt)); + const Point aEndPt(ImplLogicToDevicePixel(rEndPt)); + + mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), this ); + + if( mpAlphaVDev ) + mpAlphaVDev->DrawLine( rStartPt, rEndPt ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::impPaintLineGeometryWithEvtlExpand( + const LineInfo& rInfo, + basegfx::B2DPolyPolygon aLinePolyPolygon) +{ + const bool bTryAA((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) + && mpGraphics->supportsOperation(OutDevSupport_B2DDraw) + && ROP_OVERPAINT == GetRasterOp() + && IsLineColor()); + basegfx::B2DPolyPolygon aFillPolyPolygon; + const bool bDashUsed(LINE_DASH == rInfo.GetStyle()); + const bool bLineWidthUsed(rInfo.GetWidth() > 1); + + if(bDashUsed && aLinePolyPolygon.count()) + { + ::std::vector< double > fDotDashArray; + const double fDashLen(rInfo.GetDashLen()); + const double fDotLen(rInfo.GetDotLen()); + const double fDistance(rInfo.GetDistance()); + + for(sal_uInt16 a(0); a < rInfo.GetDashCount(); a++) + { + fDotDashArray.push_back(fDashLen); + fDotDashArray.push_back(fDistance); + } + + for(sal_uInt16 b(0); b < rInfo.GetDotCount(); b++) + { + fDotDashArray.push_back(fDotLen); + fDotDashArray.push_back(fDistance); + } + + const double fAccumulated(::std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0)); + + if(fAccumulated > 0.0) + { + basegfx::B2DPolyPolygon aResult; + + for(sal_uInt32 c(0); c < aLinePolyPolygon.count(); c++) + { + basegfx::B2DPolyPolygon aLineTraget; + basegfx::tools::applyLineDashing( + aLinePolyPolygon.getB2DPolygon(c), + fDotDashArray, + &aLineTraget); + aResult.append(aLineTraget); + } + + aLinePolyPolygon = aResult; + } + } + + if(bLineWidthUsed && aLinePolyPolygon.count()) + { + const double fHalfLineWidth((rInfo.GetWidth() * 0.5) + 0.5); + + if(aLinePolyPolygon.areControlPointsUsed()) + { + // #i110768# When area geometry has to be created, do not + // use the fallback bezier decomposition inside createAreaGeometry, + // but one that is at least as good as ImplSubdivideBezier was. + // There, Polygon::AdaptiveSubdivide was used with default parameter + // 1.0 as quality index. + aLinePolyPolygon = basegfx::tools::adaptiveSubdivideByDistance(aLinePolyPolygon, 1.0); + } + + for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++) + { + aFillPolyPolygon.append(basegfx::tools::createAreaGeometry( + aLinePolyPolygon.getB2DPolygon(a), + fHalfLineWidth, + rInfo.GetLineJoin())); + } + + aLinePolyPolygon.clear(); + } + + GDIMetaFile* pOldMetaFile = mpMetaFile; + mpMetaFile = NULL; + + if(aLinePolyPolygon.count()) + { + for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++) + { + const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a)); + bool bDone(false); + + if(bTryAA) + { + bDone = mpGraphics->DrawPolyLine( aCandidate, 0.0, basegfx::B2DVector(1.0,1.0), basegfx::B2DLINEJOIN_NONE, this); + } + + if(!bDone) + { + const Polygon aPolygon(aCandidate); + mpGraphics->DrawPolyLine(aPolygon.GetSize(), (const SalPoint*)aPolygon.GetConstPointAry(), this); + } + } + } + + if(aFillPolyPolygon.count()) + { + const Color aOldLineColor( maLineColor ); + const Color aOldFillColor( maFillColor ); + + SetLineColor(); + ImplInitLineColor(); + SetFillColor( aOldLineColor ); + ImplInitFillColor(); + + bool bDone(false); + + if(bTryAA) + { + bDone = mpGraphics->DrawPolyPolygon(aFillPolyPolygon, 0.0, this); + } + + if(!bDone) + { + for(sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++) + { + const Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a)); + mpGraphics->DrawPolygon(aPolygon.GetSize(), (const SalPoint*)aPolygon.GetConstPointAry(), this); + } + } + + SetFillColor( aOldFillColor ); + SetLineColor( aOldLineColor ); + } + + mpMetaFile = pOldMetaFile; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt, + const LineInfo& rLineInfo ) +{ + OSL_TRACE( "OutputDevice::DrawLine()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( rLineInfo.IsDefault() ) + { + DrawLine( rStartPt, rEndPt ); + return; + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaLineAction( rStartPt, rEndPt, rLineInfo ) ); + + if ( !IsDeviceOutputNecessary() || !mbLineColor || ( LINE_NONE == rLineInfo.GetStyle() ) || ImplIsRecordLayout() ) + return; + + if( !mpGraphics && !ImplGetGraphics() ) + return; + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + + if ( mbOutputClipped ) + return; + + const Point aStartPt( ImplLogicToDevicePixel( rStartPt ) ); + const Point aEndPt( ImplLogicToDevicePixel( rEndPt ) ); + const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) ); + const bool bDashUsed(LINE_DASH == aInfo.GetStyle()); + const bool bLineWidthUsed(aInfo.GetWidth() > 1); + + if ( mbInitLineColor ) + ImplInitLineColor(); + + if(bDashUsed || bLineWidthUsed) + { + basegfx::B2DPolygon aLinePolygon; + aLinePolygon.append(basegfx::B2DPoint(aStartPt.X(), aStartPt.Y())); + aLinePolygon.append(basegfx::B2DPoint(aEndPt.X(), aEndPt.Y())); + + impPaintLineGeometryWithEvtlExpand(aInfo, basegfx::B2DPolyPolygon(aLinePolygon)); + } + else + { + mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), this ); + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawLine( rStartPt, rEndPt, rLineInfo ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawRect( const Rectangle& rRect ) +{ + OSL_TRACE( "OutputDevice::DrawRect()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaRectAction( rRect ) ); + + if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || ImplIsRecordLayout() ) + return; + + Rectangle aRect( ImplLogicToDevicePixel( rRect ) ); + + if ( aRect.IsEmpty() ) + return; + aRect.Justify(); + + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); + + mpGraphics->DrawRect( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), this ); + + if( mpAlphaVDev ) + mpAlphaVDev->DrawRect( rRect ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPolyLine( const Polygon& rPoly ) +{ + OSL_TRACE( "OutputDevice::DrawPolyLine()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaPolyLineAction( rPoly ) ); + + sal_uInt16 nPoints = rPoly.GetSize(); + + if ( !IsDeviceOutputNecessary() || !mbLineColor || (nPoints < 2) || ImplIsRecordLayout() ) + return; + + // we need a graphics + if ( !mpGraphics ) + if ( !ImplGetGraphics() ) + return; + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + + const bool bTryAA((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) + && mpGraphics->supportsOperation(OutDevSupport_B2DDraw) + && ROP_OVERPAINT == GetRasterOp() + && IsLineColor()); + + // use b2dpolygon drawing if possible + if(bTryAA && ImpTryDrawPolyLineDirect(rPoly.getB2DPolygon(), 0.0, basegfx::B2DLINEJOIN_NONE)) + { + basegfx::B2DPolygon aB2DPolyLine(rPoly.getB2DPolygon()); + const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation(); + const ::basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 ); + + // transform the polygon + aB2DPolyLine.transform( aTransform ); + + if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE) + { + aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine); + } + + if(mpGraphics->DrawPolyLine( aB2DPolyLine, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this)) + { + return; + } + } + + Polygon aPoly = ImplLogicToDevicePixel( rPoly ); + const SalPoint* pPtAry = (const SalPoint*)aPoly.GetConstPointAry(); + + // #100127# Forward beziers to sal, if any + if( aPoly.HasFlags() ) + { + const sal_uInt8* pFlgAry = aPoly.GetConstFlagAry(); + if( !mpGraphics->DrawPolyLineBezier( nPoints, pPtAry, pFlgAry, this ) ) + { + aPoly = ImplSubdivideBezier(aPoly); + pPtAry = (const SalPoint*)aPoly.GetConstPointAry(); + mpGraphics->DrawPolyLine( aPoly.GetSize(), pPtAry, this ); + } + } + else + { + mpGraphics->DrawPolyLine( nPoints, pPtAry, this ); + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawPolyLine( rPoly ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPolyLine( const Polygon& rPoly, const LineInfo& rLineInfo ) +{ + OSL_TRACE( "OutputDevice::DrawPolyLine()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + + if ( rLineInfo.IsDefault() ) + { + DrawPolyLine( rPoly ); + return; + } + + // #i101491# + // Try direct Fallback to B2D-Version of DrawPolyLine + if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) + && LINE_SOLID == rLineInfo.GetStyle()) + { + DrawPolyLine( rPoly.getB2DPolygon(), (double)rLineInfo.GetWidth(), rLineInfo.GetLineJoin()); + return; + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaPolyLineAction( rPoly, rLineInfo ) ); + + ImpDrawPolyLineWithLineInfo(rPoly, rLineInfo); +} + +void OutputDevice::ImpDrawPolyLineWithLineInfo(const Polygon& rPoly, const LineInfo& rLineInfo) +{ + sal_uInt16 nPoints(rPoly.GetSize()); + + if ( !IsDeviceOutputNecessary() || !mbLineColor || ( nPoints < 2 ) || ( LINE_NONE == rLineInfo.GetStyle() ) || ImplIsRecordLayout() ) + return; + + Polygon aPoly = ImplLogicToDevicePixel( rPoly ); + + // #100127# LineInfo is not curve-safe, subdivide always + // + // What shall this mean? It's wrong to subdivide here when the + // polygon is a fat line. In that case, the painted geometry + // WILL be much different. + // I also have no idea how this could be related to the given ID + // which reads 'consolidate boost versions' in the task description. + // Removing. + // + //if( aPoly.HasFlags() ) + //{ + // aPoly = ImplSubdivideBezier( aPoly ); + // nPoints = aPoly.GetSize(); + //} + + // we need a graphics + if ( !mpGraphics && !ImplGetGraphics() ) + return; + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + + const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) ); + const bool bDashUsed(LINE_DASH == aInfo.GetStyle()); + const bool bLineWidthUsed(aInfo.GetWidth() > 1); + + if(bDashUsed || bLineWidthUsed) + { + impPaintLineGeometryWithEvtlExpand(aInfo, basegfx::B2DPolyPolygon(aPoly.getB2DPolygon())); + } + else + { + // #100127# the subdivision HAS to be done here since only a pointer + // to an array of points is given to the DrawPolyLine method, there is + // NO way to find out there that it's a curve. + if( aPoly.HasFlags() ) + { + aPoly = ImplSubdivideBezier( aPoly ); + nPoints = aPoly.GetSize(); + } + + mpGraphics->DrawPolyLine(nPoints, (const SalPoint*)aPoly.GetConstPointAry(), this); + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawPolyLine( rPoly, rLineInfo ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPolygon( const Polygon& rPoly ) +{ + OSL_TRACE( "OutputDevice::DrawPolygon()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) ); + + sal_uInt16 nPoints = rPoly.GetSize(); + + if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || (nPoints < 2) || ImplIsRecordLayout() ) + return; + + // we need a graphics + if ( !mpGraphics ) + if ( !ImplGetGraphics() ) + return; + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); + + // use b2dpolygon drawing if possible + if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) + && mpGraphics->supportsOperation(OutDevSupport_B2DDraw) + && ROP_OVERPAINT == GetRasterOp() + && (IsLineColor() || IsFillColor())) + { + const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation(); + basegfx::B2DPolygon aB2DPolygon(rPoly.getB2DPolygon()); + bool bSuccess(true); + + // transform the polygon and ensure closed + aB2DPolygon.transform(aTransform); + aB2DPolygon.setClosed(true); + + if(IsFillColor()) + { + bSuccess = mpGraphics->DrawPolyPolygon(basegfx::B2DPolyPolygon(aB2DPolygon), 0.0, this); + } + + if(bSuccess && IsLineColor()) + { + const ::basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 ); + + if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE) + { + aB2DPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon); + } + + bSuccess = mpGraphics->DrawPolyLine( aB2DPolygon, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this); + } + + if(bSuccess) + { + return; + } + } + + Polygon aPoly = ImplLogicToDevicePixel( rPoly ); + const SalPoint* pPtAry = (const SalPoint*)aPoly.GetConstPointAry(); + + // #100127# Forward beziers to sal, if any + if( aPoly.HasFlags() ) + { + const sal_uInt8* pFlgAry = aPoly.GetConstFlagAry(); + if( !mpGraphics->DrawPolygonBezier( nPoints, pPtAry, pFlgAry, this ) ) + { + aPoly = ImplSubdivideBezier(aPoly); + pPtAry = (const SalPoint*)aPoly.GetConstPointAry(); + mpGraphics->DrawPolygon( aPoly.GetSize(), pPtAry, this ); + } + } + else + { + mpGraphics->DrawPolygon( nPoints, pPtAry, this ); + } + if( mpAlphaVDev ) + mpAlphaVDev->DrawPolygon( rPoly ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPolyPolygon( const PolyPolygon& rPolyPoly ) +{ + OSL_TRACE( "OutputDevice::DrawPolyPolygon()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) ); + + sal_uInt16 nPoly = rPolyPoly.Count(); + + if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || !nPoly || ImplIsRecordLayout() ) + return; + + // we need a graphics + if ( !mpGraphics ) + if ( !ImplGetGraphics() ) + return; + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); + + // use b2dpolygon drawing if possible + if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) + && mpGraphics->supportsOperation(OutDevSupport_B2DDraw) + && ROP_OVERPAINT == GetRasterOp() + && (IsLineColor() || IsFillColor())) + { + const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation(); + basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon()); + bool bSuccess(true); + + // transform the polygon and ensure closed + aB2DPolyPolygon.transform(aTransform); + aB2DPolyPolygon.setClosed(true); + + if(IsFillColor()) + { + bSuccess = mpGraphics->DrawPolyPolygon(aB2DPolyPolygon, 0.0, this); + } + + if(bSuccess && IsLineColor()) + { + const ::basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 ); + + if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE) + { + aB2DPolyPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon); + } + + for(sal_uInt32 a(0); bSuccess && a < aB2DPolyPolygon.count(); a++) + { + bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a), 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this); + } + } + + if(bSuccess) + { + return; + } + } + + if ( nPoly == 1 ) + { + // #100127# Map to DrawPolygon + Polygon aPoly = rPolyPoly.GetObject( 0 ); + if( aPoly.GetSize() >= 2 ) + { + GDIMetaFile* pOldMF = mpMetaFile; + mpMetaFile = NULL; + + DrawPolygon( aPoly ); + + mpMetaFile = pOldMF; + } + } + else + { + // #100127# moved real PolyPolygon draw to separate method, + // have to call recursively, avoiding duplicate + // ImplLogicToDevicePixel calls + ImplDrawPolyPolygon( nPoly, ImplLogicToDevicePixel( rPolyPoly ) ); + } + if( mpAlphaVDev ) + mpAlphaVDev->DrawPolyPolygon( rPolyPoly ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPolygon( const ::basegfx::B2DPolygon& rB2DPolygon) +{ + // AW: Do NOT paint empty polygons + if(rB2DPolygon.count()) + { + ::basegfx::B2DPolyPolygon aPP( rB2DPolygon ); + DrawPolyPolygon( aPP ); + } +} + +// ----------------------------------------------------------------------- +// Caution: This method is nearly the same as +// OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, double fTransparency), +// so when changes are made here do not forget to make change sthere, too + +void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly ) +{ + OSL_TRACE( "OutputDevice::DrawPolyPolygon(B2D&)" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaPolyPolygonAction( PolyPolygon( rB2DPolyPoly ) ) ); + + // call helper + ImpDrawPolyPolygonWithB2DPolyPolygon(rB2DPolyPoly); +} + +void OutputDevice::ImpDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPolygon& rB2DPolyPoly) +{ + // AW: Do NOT paint empty PolyPolygons + if(!rB2DPolyPoly.count()) + return; + + // we need a graphics + if( !mpGraphics ) + if( !ImplGetGraphics() ) + return; + + if( mbInitClipRegion ) + ImplInitClipRegion(); + if( mbOutputClipped ) + return; + + if( mbInitLineColor ) + ImplInitLineColor(); + if( mbInitFillColor ) + ImplInitFillColor(); + + if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) + && mpGraphics->supportsOperation(OutDevSupport_B2DDraw) + && ROP_OVERPAINT == GetRasterOp() + && (IsLineColor() || IsFillColor())) + { + const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation()); + basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly); + bool bSuccess(true); + + // transform the polygon and ensure closed + aB2DPolyPolygon.transform(aTransform); + aB2DPolyPolygon.setClosed(true); + + if(IsFillColor()) + { + bSuccess = mpGraphics->DrawPolyPolygon(aB2DPolyPolygon, 0.0, this); + } + + if(bSuccess && IsLineColor()) + { + const ::basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 ); + + if(mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE) + { + aB2DPolyPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon); + } + + for(sal_uInt32 a(0);bSuccess && a < aB2DPolyPolygon.count(); a++) + { + bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a), 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this); + } + } + + if(bSuccess) + { + return; + } + } + + // fallback to old polygon drawing if needed + const PolyPolygon aToolsPolyPolygon( rB2DPolyPoly ); + const PolyPolygon aPixelPolyPolygon = ImplLogicToDevicePixel( aToolsPolyPolygon ); + ImplDrawPolyPolygon( aPixelPolyPolygon.Count(), aPixelPolyPolygon ); +} + +// ----------------------------------------------------------------------- + +bool OutputDevice::ImpTryDrawPolyLineDirect( + const basegfx::B2DPolygon& rB2DPolygon, + double fLineWidth, + basegfx::B2DLineJoin eLineJoin) +{ + const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation(); + basegfx::B2DVector aB2DLineWidth(1.0, 1.0); + + // transform the line width if used + if( fLineWidth != 0.0 ) + { + aB2DLineWidth = aTransform * ::basegfx::B2DVector( fLineWidth, fLineWidth ); + } + + // transform the polygon + basegfx::B2DPolygon aB2DPolygon(rB2DPolygon); + aB2DPolygon.transform(aTransform); + + if((mnAntialiasing & ANTIALIASING_PIXELSNAPHAIRLINE) + && aB2DPolygon.count() < 1000) + { + // #i98289#, #i101491# + // better to remove doubles on device coordinates. Also assume from a given amount + // of points that the single edges are not long enough to smooth + aB2DPolygon.removeDoublePoints(); + aB2DPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon); + } + + // draw the polyline + return mpGraphics->DrawPolyLine( aB2DPolygon, 0.0, aB2DLineWidth, eLineJoin, this); +} + +void OutputDevice::DrawPolyLine( + const basegfx::B2DPolygon& rB2DPolygon, + double fLineWidth, + basegfx::B2DLineJoin eLineJoin) +{ + OSL_TRACE( "OutputDevice::DrawPolyLine(B2D&)" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + (void)eLineJoin; // ATM used in UNX, but not in WNT, access it for warning-free + + if( mpMetaFile ) + { + LineInfo aLineInfo; + if( fLineWidth != 0.0 ) + aLineInfo.SetWidth( static_cast<long>(fLineWidth+0.5) ); + const Polygon aToolsPolygon( rB2DPolygon ); + mpMetaFile->AddAction( new MetaPolyLineAction( aToolsPolygon, aLineInfo ) ); + } + + + // AW: Do NOT paint empty PolyPolygons + if(!rB2DPolygon.count()) + return; + + // we need a graphics + if( !mpGraphics ) + if( !ImplGetGraphics() ) + return; + + if( mbInitClipRegion ) + ImplInitClipRegion(); + if( mbOutputClipped ) + return; + + if( mbInitLineColor ) + ImplInitLineColor(); + + const bool bTryAA((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) + && mpGraphics->supportsOperation(OutDevSupport_B2DDraw) + && ROP_OVERPAINT == GetRasterOp() + && IsLineColor()); + + // use b2dpolygon drawing if possible + if(bTryAA && ImpTryDrawPolyLineDirect(rB2DPolygon, fLineWidth, eLineJoin)) + { + return; + } + + // #i101491# + // no output yet; fallback to geometry decomposition and use filled polygon paint + // when line is fat and not too complex. ImpDrawPolyPolygonWithB2DPolyPolygon + // will do internal needed AA checks etc. + if(fLineWidth >= 2.5 + && rB2DPolygon.count() + && rB2DPolygon.count() <= 1000) + { + const double fHalfLineWidth((fLineWidth * 0.5) + 0.5); + const basegfx::B2DPolyPolygon aAreaPolyPolygon(basegfx::tools::createAreaGeometry( + rB2DPolygon, fHalfLineWidth, eLineJoin)); + + const Color aOldLineColor(maLineColor); + const Color aOldFillColor(maFillColor); + + SetLineColor(); + ImplInitLineColor(); + SetFillColor(aOldLineColor); + ImplInitFillColor(); + + // draw usig a loop; else the topology will paint a PolyPolygon + for(sal_uInt32 a(0); a < aAreaPolyPolygon.count(); a++) + { + ImpDrawPolyPolygonWithB2DPolyPolygon( + basegfx::B2DPolyPolygon(aAreaPolyPolygon.getB2DPolygon(a))); + } + + SetLineColor(aOldLineColor); + ImplInitLineColor(); + SetFillColor(aOldFillColor); + ImplInitFillColor(); + + if(bTryAA) + { + // when AA it is necessary to also paint the filled polygon's outline + // to avoid optical gaps + for(sal_uInt32 a(0); a < aAreaPolyPolygon.count(); a++) + { + ImpTryDrawPolyLineDirect(aAreaPolyPolygon.getB2DPolygon(a), 0.0, basegfx::B2DLINEJOIN_NONE); + } + } + } + else + { + // fallback to old polygon drawing if needed + const Polygon aToolsPolygon( rB2DPolygon ); + LineInfo aLineInfo; + if( fLineWidth != 0.0 ) + aLineInfo.SetWidth( static_cast<long>(fLineWidth+0.5) ); + ImpDrawPolyLineWithLineInfo( aToolsPolygon, aLineInfo ); + } +} + +// ----------------------------------------------------------------------- + +sal_uInt32 OutputDevice::GetGCStackDepth() const +{ + const ImplObjStack* pData = mpObjStack; + sal_uInt32 nDepth = 0; + while( pData ) + { + nDepth++; + pData = pData->mpPrev; + } + return nDepth; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::Push( sal_uInt16 nFlags ) +{ + OSL_TRACE( "OutputDevice::Push()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaPushAction( nFlags ) ); + + ImplObjStack* pData = new ImplObjStack; + pData->mpPrev = mpObjStack; + mpObjStack = pData; + + pData->mnFlags = nFlags; + + if ( nFlags & PUSH_LINECOLOR ) + { + if ( mbLineColor ) + pData->mpLineColor = new Color( maLineColor ); + else + pData->mpLineColor = NULL; + } + if ( nFlags & PUSH_FILLCOLOR ) + { + if ( mbFillColor ) + pData->mpFillColor = new Color( maFillColor ); + else + pData->mpFillColor = NULL; + } + if ( nFlags & PUSH_FONT ) + pData->mpFont = new Font( maFont ); + if ( nFlags & PUSH_TEXTCOLOR ) + pData->mpTextColor = new Color( GetTextColor() ); + if ( nFlags & PUSH_TEXTFILLCOLOR ) + { + if ( IsTextFillColor() ) + pData->mpTextFillColor = new Color( GetTextFillColor() ); + else + pData->mpTextFillColor = NULL; + } + if ( nFlags & PUSH_TEXTLINECOLOR ) + { + if ( IsTextLineColor() ) + pData->mpTextLineColor = new Color( GetTextLineColor() ); + else + pData->mpTextLineColor = NULL; + } + if ( nFlags & PUSH_OVERLINECOLOR ) + { + if ( IsOverlineColor() ) + pData->mpOverlineColor = new Color( GetOverlineColor() ); + else + pData->mpOverlineColor = NULL; + } + if ( nFlags & PUSH_TEXTALIGN ) + pData->meTextAlign = GetTextAlign(); + if( nFlags & PUSH_TEXTLAYOUTMODE ) + pData->mnTextLayoutMode = GetLayoutMode(); + if( nFlags & PUSH_TEXTLANGUAGE ) + pData->meTextLanguage = GetDigitLanguage(); + if ( nFlags & PUSH_RASTEROP ) + pData->meRasterOp = GetRasterOp(); + if ( nFlags & PUSH_MAPMODE ) + { + if ( mbMap ) + pData->mpMapMode = new MapMode( maMapMode ); + else + pData->mpMapMode = NULL; + } + if ( nFlags & PUSH_CLIPREGION ) + { + if ( mbClipRegion ) + pData->mpClipRegion = new Region( maRegion ); + else + pData->mpClipRegion = NULL; + } + if ( nFlags & PUSH_REFPOINT ) + { + if ( mbRefPoint ) + pData->mpRefPoint = new Point( maRefPoint ); + else + pData->mpRefPoint = NULL; + } + + if( mpAlphaVDev ) + mpAlphaVDev->Push(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::Pop() +{ + OSL_TRACE( "OutputDevice::Pop()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaPopAction() ); + + GDIMetaFile* pOldMetaFile = mpMetaFile; + ImplObjStack* pData = mpObjStack; + mpMetaFile = NULL; + + if ( !pData ) + { + DBG_ERRORFILE( "OutputDevice::Pop() without OutputDevice::Push()" ); + return; + } + + if( mpAlphaVDev ) + mpAlphaVDev->Pop(); + + mpObjStack = pData->mpPrev; + + if ( pData->mnFlags & PUSH_LINECOLOR ) + { + if ( pData->mpLineColor ) + SetLineColor( *pData->mpLineColor ); + else + SetLineColor(); + } + if ( pData->mnFlags & PUSH_FILLCOLOR ) + { + if ( pData->mpFillColor ) + SetFillColor( *pData->mpFillColor ); + else + SetFillColor(); + } + if ( pData->mnFlags & PUSH_FONT ) + SetFont( *pData->mpFont ); + if ( pData->mnFlags & PUSH_TEXTCOLOR ) + SetTextColor( *pData->mpTextColor ); + if ( pData->mnFlags & PUSH_TEXTFILLCOLOR ) + { + if ( pData->mpTextFillColor ) + SetTextFillColor( *pData->mpTextFillColor ); + else + SetTextFillColor(); + } + if ( pData->mnFlags & PUSH_TEXTLINECOLOR ) + { + if ( pData->mpTextLineColor ) + SetTextLineColor( *pData->mpTextLineColor ); + else + SetTextLineColor(); + } + if ( pData->mnFlags & PUSH_OVERLINECOLOR ) + { + if ( pData->mpOverlineColor ) + SetOverlineColor( *pData->mpOverlineColor ); + else + SetOverlineColor(); + } + if ( pData->mnFlags & PUSH_TEXTALIGN ) + SetTextAlign( pData->meTextAlign ); + if( pData->mnFlags & PUSH_TEXTLAYOUTMODE ) + SetLayoutMode( pData->mnTextLayoutMode ); + if( pData->mnFlags & PUSH_TEXTLANGUAGE ) + SetDigitLanguage( pData->meTextLanguage ); + if ( pData->mnFlags & PUSH_RASTEROP ) + SetRasterOp( pData->meRasterOp ); + if ( pData->mnFlags & PUSH_MAPMODE ) + { + if ( pData->mpMapMode ) + SetMapMode( *pData->mpMapMode ); + else + SetMapMode(); + } + if ( pData->mnFlags & PUSH_CLIPREGION ) + ImplSetClipRegion( pData->mpClipRegion ); + if ( pData->mnFlags & PUSH_REFPOINT ) + { + if ( pData->mpRefPoint ) + SetRefPoint( *pData->mpRefPoint ); + else + SetRefPoint(); + } + + ImplDeleteObjStack( pData ); + + mpMetaFile = pOldMetaFile; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetConnectMetaFile( GDIMetaFile* pMtf ) +{ + mpMetaFile = pMtf; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::EnableOutput( sal_Bool bEnable ) +{ + mbOutput = (bEnable != 0); + + if( mpAlphaVDev ) + mpAlphaVDev->EnableOutput( bEnable ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetSettings( const AllSettings& rSettings ) +{ + maSettings = rSettings; + + if( mpAlphaVDev ) + mpAlphaVDev->SetSettings( rSettings ); +} + +// ----------------------------------------------------------------------- + +sal_uInt16 OutputDevice::GetBitCount() const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( meOutDevType == OUTDEV_VIRDEV ) + return ((VirtualDevice*)this)->mnBitCount; + + // we need a graphics + if ( !mpGraphics ) + { + if ( !((OutputDevice*)this)->ImplGetGraphics() ) + return 0; + } + + return (sal_uInt16)mpGraphics->GetBitCount(); +} + +// ----------------------------------------------------------------------- + +sal_uInt16 OutputDevice::GetAlphaBitCount() const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( meOutDevType == OUTDEV_VIRDEV && + mpAlphaVDev != NULL ) + { + return mpAlphaVDev->GetBitCount(); + } + + return 0; +} + +// ----------------------------------------------------------------------- + +sal_uLong OutputDevice::GetColorCount() const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + const sal_uInt16 nBitCount = GetBitCount(); + return( ( nBitCount > 31 ) ? ULONG_MAX : ( ( (sal_uLong) 1 ) << nBitCount) ); +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::HasAlpha() +{ + return mpAlphaVDev != NULL; +} + +// ----------------------------------------------------------------------- + +::com::sun::star::uno::Reference< ::com::sun::star::awt::XGraphics > OutputDevice::CreateUnoGraphics() +{ + UnoWrapperBase* pWrapper = Application::GetUnoWrapper(); + return pWrapper ? pWrapper->CreateGraphics( this ) : ::com::sun::star::uno::Reference< ::com::sun::star::awt::XGraphics >(); +} + +// ----------------------------------------------------------------------- + +SystemGraphicsData OutputDevice::GetSystemGfxData() const +{ + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return SystemGraphicsData(); + } + + return mpGraphics->GetGraphicsData(); +} + +// ----------------------------------------------------------------------- + +::com::sun::star::uno::Any OutputDevice::GetSystemGfxDataAny() const +{ + ::com::sun::star::uno::Any aRet; + const SystemGraphicsData aSysData = GetSystemGfxData(); + ::com::sun::star::uno::Sequence< sal_Int8 > aSeq( (sal_Int8*)&aSysData, + aSysData.nSize ); + + return uno::makeAny(aSeq); +} + +// ----------------------------------------------------------------------- + +::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCanvas > OutputDevice::GetCanvas() const +{ + uno::Sequence< uno::Any > aArg(6); + + aArg[ 0 ] = uno::makeAny( reinterpret_cast<sal_Int64>(this) ); + aArg[ 2 ] = uno::makeAny( ::com::sun::star::awt::Rectangle( mnOutOffX, mnOutOffY, mnOutWidth, mnOutHeight ) ); + aArg[ 3 ] = uno::makeAny( sal_False ); + aArg[ 5 ] = GetSystemGfxDataAny(); + + uno::Reference<lang::XMultiServiceFactory> xFactory = vcl::unohelper::GetMultiServiceFactory(); + + uno::Reference<rendering::XCanvas> xCanvas; + + // Create canvas instance with window handle + // ========================================= + if ( xFactory.is() ) + { + static uno::Reference<lang::XMultiServiceFactory> xCanvasFactory( + xFactory->createInstance( + OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star." + "rendering.CanvasFactory") ) ), + uno::UNO_QUERY ); + if(xCanvasFactory.is()) + { + xCanvas.set( + xCanvasFactory->createInstanceWithArguments( + OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.rendering.Canvas" )), + aArg ), + uno::UNO_QUERY ); + } + } + + return xCanvas; +} + +// ----------------------------------------------------------------------- + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/outdev2.cxx b/vcl/source/gdi/outdev2.cxx new file mode 100644 index 000000000000..356542952360 --- /dev/null +++ b/vcl/source/gdi/outdev2.cxx @@ -0,0 +1,2271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <svsys.h> +#include <vcl/salbmp.hxx> +#include <vcl/salgdi.hxx> +#include <vcl/impbmp.hxx> +#include <tools/debug.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/window.hxx> +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/virdev.hxx> +#include <vcl/outdata.hxx> +#include <vcl/outdev.h> +#include <vcl/bmpacc.hxx> +#include <vcl/region.h> +#include <vcl/outdev.hxx> +#include <vcl/window.hxx> +#include <vcl/window.h> +#include <vcl/sallayout.hxx> +#include <vcl/image.h> +#include <vcl/image.hxx> +#include <vcl/bmpfast.hxx> + +#define BAND_MAX_SIZE 512000 + +// ======================================================================= + +DBG_NAMEEX( OutputDevice ) + +// ======================================================================= + +// ----------- +// - Defines - +// ----------- + +#define OUTDEV_INIT() \ +{ \ + if ( !IsDeviceOutputNecessary() ) \ + return; \ + \ + if ( !mpGraphics ) \ + if ( !ImplGetGraphics() ) \ + return; \ + \ + if ( mbInitClipRegion ) \ + ImplInitClipRegion(); \ + \ + if ( mbOutputClipped ) \ + return; \ +} + +#define TwoRect SalTwoRect + +// ------------- +// - externals - +// ------------- + +extern sal_uLong nVCLRLut[ 6 ]; +extern sal_uLong nVCLGLut[ 6 ]; +extern sal_uLong nVCLBLut[ 6 ]; +extern sal_uLong nVCLDitherLut[ 256 ]; +extern sal_uLong nVCLLut[ 256 ]; + +// ======================================================================= + +sal_uLong ImplAdjustTwoRect( TwoRect& rTwoRect, const Size& rSizePix ) +{ + sal_uLong nMirrFlags = 0; + + if ( rTwoRect.mnDestWidth < 0 ) + { + rTwoRect.mnSrcX = rSizePix.Width() - rTwoRect.mnSrcX - rTwoRect.mnSrcWidth; + rTwoRect.mnDestWidth = -rTwoRect.mnDestWidth; + rTwoRect.mnDestX -= rTwoRect.mnDestWidth-1; + nMirrFlags |= BMP_MIRROR_HORZ; + } + + if ( rTwoRect.mnDestHeight < 0 ) + { + rTwoRect.mnSrcY = rSizePix.Height() - rTwoRect.mnSrcY - rTwoRect.mnSrcHeight; + rTwoRect.mnDestHeight = -rTwoRect.mnDestHeight; + rTwoRect.mnDestY -= rTwoRect.mnDestHeight-1; + nMirrFlags |= BMP_MIRROR_VERT; + } + + if( ( rTwoRect.mnSrcX < 0 ) || ( rTwoRect.mnSrcX >= rSizePix.Width() ) || + ( rTwoRect.mnSrcY < 0 ) || ( rTwoRect.mnSrcY >= rSizePix.Height() ) || + ( ( rTwoRect.mnSrcX + rTwoRect.mnSrcWidth ) > rSizePix.Width() ) || + ( ( rTwoRect.mnSrcY + rTwoRect.mnSrcHeight ) > rSizePix.Height() ) ) + { + const Rectangle aSourceRect( Point( rTwoRect.mnSrcX, rTwoRect.mnSrcY ), + Size( rTwoRect.mnSrcWidth, rTwoRect.mnSrcHeight ) ); + Rectangle aCropRect( aSourceRect ); + + aCropRect.Intersection( Rectangle( Point(), rSizePix ) ); + + if( aCropRect.IsEmpty() ) + rTwoRect.mnSrcWidth = rTwoRect.mnSrcHeight = rTwoRect.mnDestWidth = rTwoRect.mnDestHeight = 0; + else + { + const double fFactorX = ( rTwoRect.mnSrcWidth > 1 ) ? (double) ( rTwoRect.mnDestWidth - 1 ) / ( rTwoRect.mnSrcWidth - 1 ) : 0.0; + const double fFactorY = ( rTwoRect.mnSrcHeight > 1 ) ? (double) ( rTwoRect.mnDestHeight - 1 ) / ( rTwoRect.mnSrcHeight - 1 ) : 0.0; + + const long nDstX1 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Left() - rTwoRect.mnSrcX ) ); + const long nDstY1 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Top() - rTwoRect.mnSrcY ) ); + const long nDstX2 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Right() - rTwoRect.mnSrcX ) ); + const long nDstY2 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Bottom() - rTwoRect.mnSrcY ) ); + + rTwoRect.mnSrcX = aCropRect.Left(); + rTwoRect.mnSrcY = aCropRect.Top(); + rTwoRect.mnSrcWidth = aCropRect.GetWidth(); + rTwoRect.mnSrcHeight = aCropRect.GetHeight(); + rTwoRect.mnDestX = nDstX1; + rTwoRect.mnDestY = nDstY1; + rTwoRect.mnDestWidth = nDstX2 - nDstX1 + 1; + rTwoRect.mnDestHeight = nDstY2 - nDstY1 + 1; + } + } + + return nMirrFlags; +} + +// ======================================================================= + +void OutputDevice::ImplDrawOutDevDirect( const OutputDevice* pSrcDev, void* pVoidPosAry ) +{ + TwoRect* pPosAry = (TwoRect*)pVoidPosAry; + SalGraphics* pGraphics2; + + if ( pPosAry->mnSrcWidth && pPosAry->mnSrcHeight && pPosAry->mnDestWidth && pPosAry->mnDestHeight ) + { + if ( this == pSrcDev ) + pGraphics2 = NULL; + else + { + if ( (GetOutDevType() != pSrcDev->GetOutDevType()) || + (GetOutDevType() != OUTDEV_WINDOW) ) + { + if ( !pSrcDev->mpGraphics ) + { + if ( !((OutputDevice*)pSrcDev)->ImplGetGraphics() ) + return; + } + pGraphics2 = pSrcDev->mpGraphics; + } + else + { + if ( ((Window*)this)->mpWindowImpl->mpFrameWindow == ((Window*)pSrcDev)->mpWindowImpl->mpFrameWindow ) + pGraphics2 = NULL; + else + { + if ( !pSrcDev->mpGraphics ) + { + if ( !((OutputDevice*)pSrcDev)->ImplGetGraphics() ) + return; + } + pGraphics2 = pSrcDev->mpGraphics; + + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + DBG_ASSERT( mpGraphics && pSrcDev->mpGraphics, + "OutputDevice::DrawOutDev(): We need more than one Graphics" ); + } + } + } + + // #102532# Offset only has to be pseudo window offset + Rectangle aSrcOutRect( Point( pSrcDev->mnOutOffX, pSrcDev->mnOutOffY ), + Size( pSrcDev->mnOutWidth, pSrcDev->mnOutHeight ) ); + Rectangle aSrcRect( Point( pPosAry->mnSrcX, pPosAry->mnSrcY ), + Size( pPosAry->mnSrcWidth, pPosAry->mnSrcHeight ) ); + const long nOldRight = aSrcRect.Right(); + const long nOldBottom = aSrcRect.Bottom(); + + if ( !aSrcRect.Intersection( aSrcOutRect ).IsEmpty() ) + { + if ( (pPosAry->mnSrcX+pPosAry->mnSrcWidth-1) > aSrcOutRect.Right() ) + { + const long nOldWidth = pPosAry->mnSrcWidth; + pPosAry->mnSrcWidth -= (nOldRight - aSrcRect.Right()); + pPosAry->mnDestWidth = pPosAry->mnDestWidth * pPosAry->mnSrcWidth / nOldWidth; + } + + if ( (pPosAry->mnSrcY+pPosAry->mnSrcHeight-1) > aSrcOutRect.Bottom() ) + { + const long nOldHeight = pPosAry->mnSrcHeight; + pPosAry->mnSrcHeight -= (nOldBottom - aSrcRect.Bottom()); + pPosAry->mnDestHeight = pPosAry->mnDestHeight * pPosAry->mnSrcHeight / nOldHeight; + } + + // --- RTL --- if this is no window, but pSrcDev is a window + // mirroring may be required + // because only windows have a SalGraphicsLayout + // mirroring is performed here + if( (GetOutDevType() != OUTDEV_WINDOW) && pGraphics2 && (pGraphics2->GetLayout() & SAL_LAYOUT_BIDI_RTL) ) + { + SalTwoRect pPosAry2 = *pPosAry; + pGraphics2->mirror( pPosAry2.mnSrcX, pPosAry2.mnSrcWidth, pSrcDev ); + mpGraphics->CopyBits( &pPosAry2, pGraphics2, this, pSrcDev ); + } + else + mpGraphics->CopyBits( pPosAry, pGraphics2, this, pSrcDev ); + } + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawOutDev( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPt, const Size& rSrcSize ) +{ + OSL_TRACE( "OutputDevice::DrawOutDev()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_ASSERT( meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::DrawOutDev(...) with printer devices!" ); + + if( ImplIsRecordLayout() ) + return; + + if ( meOutDevType == OUTDEV_PRINTER ) + return; + + if ( ROP_INVERT == meRasterOp ) + { + DrawRect( Rectangle( rDestPt, rDestSize ) ); + return; + } + + if ( mpMetaFile ) + { + const Bitmap aBmp( GetBitmap( rSrcPt, rSrcSize ) ); + mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) ); + } + + OUTDEV_INIT(); + + TwoRect aPosAry; + aPosAry.mnSrcWidth = ImplLogicWidthToDevicePixel( rSrcSize.Width() ); + aPosAry.mnSrcHeight = ImplLogicHeightToDevicePixel( rSrcSize.Height() ); + aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() ); + aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() ); + + if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) + { + aPosAry.mnSrcX = ImplLogicXToDevicePixel( rSrcPt.X() ); + aPosAry.mnSrcY = ImplLogicYToDevicePixel( rSrcPt.Y() ); + aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() ); + aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() ); + + Rectangle aSrcOutRect( Point( mnOutOffX, mnOutOffY ), + Size( mnOutWidth, mnOutHeight ) ); + Rectangle aSrcRect( Point( aPosAry.mnSrcX, aPosAry.mnSrcY ), + Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ) ); + long nOldRight = aSrcRect.Right(); + long nOldBottom = aSrcRect.Bottom(); + + if ( !aSrcRect.Intersection( aSrcOutRect ).IsEmpty() ) + { + if ( (aPosAry.mnSrcX+aPosAry.mnSrcWidth-1) > aSrcOutRect.Right() ) + { + long nOldWidth = aPosAry.mnSrcWidth; + aPosAry.mnSrcWidth -= nOldRight-aSrcRect.Right(); + aPosAry.mnDestWidth = aPosAry.mnDestWidth*aPosAry.mnSrcWidth/nOldWidth; + } + + if ( (aPosAry.mnSrcY+aPosAry.mnSrcHeight-1) > aSrcOutRect.Bottom() ) + { + long nOldHeight = aPosAry.mnSrcHeight; + aPosAry.mnSrcHeight -= nOldBottom-aSrcRect.Bottom(); + aPosAry.mnDestHeight = aPosAry.mnDestHeight*aPosAry.mnSrcHeight/nOldHeight; + } + + mpGraphics->CopyBits( &aPosAry, NULL, this, NULL ); + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawOutDev( rDestPt, rDestSize, rSrcPt, rSrcSize ); +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawOutDev( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPt, const Size& rSrcSize, + const OutputDevice& rOutDev ) +{ + OSL_TRACE( "OutputDevice::DrawOutDev()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rOutDev, OutputDevice, ImplDbgCheckOutputDevice ); + DBG_ASSERT( meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::DrawOutDev(...) with printer devices!" ); + DBG_ASSERT( rOutDev.meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::DrawOutDev(...) with printer devices!" ); + + if ( (meOutDevType == OUTDEV_PRINTER) || (rOutDev.meOutDevType == OUTDEV_PRINTER) || ImplIsRecordLayout() ) + return; + + if ( ROP_INVERT == meRasterOp ) + { + DrawRect( Rectangle( rDestPt, rDestSize ) ); + return; + } + + if ( mpMetaFile ) + { + const Bitmap aBmp( rOutDev.GetBitmap( rSrcPt, rSrcSize ) ); + mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) ); + } + + OUTDEV_INIT(); + + TwoRect aPosAry; + aPosAry.mnSrcX = rOutDev.ImplLogicXToDevicePixel( rSrcPt.X() ); + aPosAry.mnSrcY = rOutDev.ImplLogicYToDevicePixel( rSrcPt.Y() ); + aPosAry.mnSrcWidth = rOutDev.ImplLogicWidthToDevicePixel( rSrcSize.Width() ); + aPosAry.mnSrcHeight = rOutDev.ImplLogicHeightToDevicePixel( rSrcSize.Height() ); + aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() ); + aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() ); + aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() ); + aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() ); + + if( mpAlphaVDev ) + { + if( rOutDev.mpAlphaVDev ) + { + // alpha-blend source over destination + DrawBitmapEx( rDestPt, rDestSize, rOutDev.GetBitmapEx(rSrcPt, rSrcSize) ); + + // This would be mode SOURCE: + // copy source alpha channel to our alpha channel + //mpAlphaVDev->DrawOutDev( rDestPt, rDestSize, rSrcPt, rSrcSize, *rOutDev.mpAlphaVDev ); + } + else + { + ImplDrawOutDevDirect( &rOutDev, &aPosAry ); + + // #i32109#: make destination rectangle opaque - source has no alpha + mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, rDestSize) ); + } + } + else + { + if( rOutDev.mpAlphaVDev ) + { + // alpha-blend source over destination + DrawBitmapEx( rDestPt, rDestSize, rOutDev.GetBitmapEx(rSrcPt, rSrcSize) ); + } + else + { + // no alpha at all, neither in source nor destination device + ImplDrawOutDevDirect( &rOutDev, &aPosAry ); + } + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::CopyArea( const Point& rDestPt, + const Point& rSrcPt, const Size& rSrcSize, + sal_uInt16 nFlags ) +{ + OSL_TRACE( "OutputDevice::CopyArea()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_ASSERT( meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::CopyArea(...) with printer devices!" ); + + if ( meOutDevType == OUTDEV_PRINTER || ImplIsRecordLayout() ) + return; + + RasterOp eOldRop = GetRasterOp(); + SetRasterOp( ROP_OVERPAINT ); + + OUTDEV_INIT(); + + TwoRect aPosAry; + aPosAry.mnSrcWidth = ImplLogicWidthToDevicePixel( rSrcSize.Width() ); + aPosAry.mnSrcHeight = ImplLogicHeightToDevicePixel( rSrcSize.Height() ); + + if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight ) + { + aPosAry.mnSrcX = ImplLogicXToDevicePixel( rSrcPt.X() ); + aPosAry.mnSrcY = ImplLogicYToDevicePixel( rSrcPt.Y() ); + aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() ); + aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() ); + + Rectangle aSrcOutRect( Point( mnOutOffX, mnOutOffY ), + Size( mnOutWidth, mnOutHeight ) ); + Rectangle aSrcRect( Point( aPosAry.mnSrcX, aPosAry.mnSrcY ), + Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ) ); + long nOldRight = aSrcRect.Right(); + long nOldBottom = aSrcRect.Bottom(); + + if ( !aSrcRect.Intersection( aSrcOutRect ).IsEmpty() ) + { + if ( (aPosAry.mnSrcX+aPosAry.mnSrcWidth-1) > aSrcOutRect.Right() ) + aPosAry.mnSrcWidth -= nOldRight-aSrcRect.Right(); + + if ( (aPosAry.mnSrcY+aPosAry.mnSrcHeight-1) > aSrcOutRect.Bottom() ) + aPosAry.mnSrcHeight -= nOldBottom-aSrcRect.Bottom(); + + if ( (meOutDevType == OUTDEV_WINDOW) && (nFlags & COPYAREA_WINDOWINVALIDATE) ) + { + ((Window*)this)->ImplMoveAllInvalidateRegions( aSrcRect, + aPosAry.mnDestX-aPosAry.mnSrcX, + aPosAry.mnDestY-aPosAry.mnSrcY, + sal_False ); + + mpGraphics->CopyArea( aPosAry.mnDestX, aPosAry.mnDestY, + aPosAry.mnSrcX, aPosAry.mnSrcY, + aPosAry.mnSrcWidth, aPosAry.mnSrcHeight, + SAL_COPYAREA_WINDOWINVALIDATE, this ); + } + else + { + aPosAry.mnDestWidth = aPosAry.mnSrcWidth; + aPosAry.mnDestHeight = aPosAry.mnSrcHeight; + mpGraphics->CopyBits( &aPosAry, NULL, this, NULL ); + } + } + } + + SetRasterOp( eOldRop ); + + if( mpAlphaVDev ) + mpAlphaVDev->CopyArea( rDestPt, rSrcPt, rSrcSize, nFlags ); +} + +// ------------------------------------------------------------------ + +void OutputDevice::ImplDrawFrameDev( const Point& rPt, const Point& rDevPt, const Size& rDevSize, + const OutputDevice& rOutDev, const Region& rRegion ) +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + GDIMetaFile* pOldMetaFile = mpMetaFile; + sal_Bool bOldMap = mbMap; + RasterOp eOldROP = GetRasterOp(); + mpMetaFile = NULL; + mbMap = sal_False; + SetRasterOp( ROP_OVERPAINT ); + + if ( !IsDeviceOutputNecessary() ) + return; + + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + // ClipRegion zuruecksetzen + if ( rRegion.IsNull() ) + mpGraphics->ResetClipRegion(); + else + ImplSelectClipRegion( rRegion ); + + TwoRect aPosAry; + aPosAry.mnSrcX = rDevPt.X(); + aPosAry.mnSrcY = rDevPt.Y(); + aPosAry.mnSrcWidth = rDevSize.Width(); + aPosAry.mnSrcHeight = rDevSize.Height(); + aPosAry.mnDestX = rPt.X(); + aPosAry.mnDestY = rPt.Y(); + aPosAry.mnDestWidth = rDevSize.Width(); + aPosAry.mnDestHeight = rDevSize.Height(); + ImplDrawOutDevDirect( &rOutDev, &aPosAry ); + + // Dafuer sorgen, das ClipRegion neu berechnet und gesetzt wird + mbInitClipRegion = sal_True; + + SetRasterOp( eOldROP ); + mbMap = bOldMap; + mpMetaFile = pOldMetaFile; +} + +// ------------------------------------------------------------------ + +void OutputDevice::ImplGetFrameDev( const Point& rPt, const Point& rDevPt, const Size& rDevSize, + OutputDevice& rDev ) +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + sal_Bool bOldMap = mbMap; + mbMap = sal_False; + rDev.DrawOutDev( rDevPt, rDevSize, rPt, rDevSize, *this ); + mbMap = bOldMap; +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawBitmap( const Point& rDestPt, const Bitmap& rBitmap ) +{ + OSL_TRACE( "OutputDevice::DrawBitmap()" ); + + if( ImplIsRecordLayout() ) + return; + + const Size aSizePix( rBitmap.GetSizePixel() ); + ImplDrawBitmap( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, META_BMP_ACTION ); + + if( mpAlphaVDev ) + { + // #i32109#: Make bitmap area opaque + mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, PixelToLogic( aSizePix )) ); + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize, const Bitmap& rBitmap ) +{ + OSL_TRACE( "OutputDevice::DrawBitmap( Size )" ); + + if( ImplIsRecordLayout() ) + return; + + ImplDrawBitmap( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, META_BMPSCALE_ACTION ); + + if( mpAlphaVDev ) + { + // #i32109#: Make bitmap area opaque + mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, rDestSize) ); + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel, + const Bitmap& rBitmap ) +{ + OSL_TRACE( "OutputDevice::DrawBitmap( Point, Size )" ); + + if( ImplIsRecordLayout() ) + return; + + ImplDrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, META_BMPSCALEPART_ACTION ); + + if( mpAlphaVDev ) + { + // #i32109#: Make bitmap area opaque + mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, rDestSize) ); + } +} + +// ----------------------------------------------------------------------------- + +void OutputDevice::ImplDrawBitmap( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel, + const Bitmap& rBitmap, const sal_uLong nAction ) +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Bitmap aBmp( rBitmap ); + + if ( ( mnDrawMode & DRAWMODE_NOBITMAP ) ) + return; + else if ( ROP_INVERT == meRasterOp ) + { + DrawRect( Rectangle( rDestPt, rDestSize ) ); + return; + } + else if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP | + DRAWMODE_GRAYBITMAP | DRAWMODE_GHOSTEDBITMAP ) ) + { + if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP ) ) + { + sal_uInt8 cCmpVal; + + if ( mnDrawMode & DRAWMODE_BLACKBITMAP ) + cCmpVal = ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) ? 0x80 : 0; + else + cCmpVal = 255; + + Color aCol( cCmpVal, cCmpVal, cCmpVal ); + Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + SetLineColor( aCol ); + SetFillColor( aCol ); + DrawRect( Rectangle( rDestPt, rDestSize ) ); + Pop(); + return; + } + else if( !!aBmp ) + { + if ( mnDrawMode & DRAWMODE_GRAYBITMAP ) + aBmp.Convert( BMP_CONVERSION_8BIT_GREYS ); + + if ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) + aBmp.Convert( BMP_CONVERSION_GHOSTED ); + } + } + + if ( mpMetaFile ) + { + switch( nAction ) + { + case( META_BMP_ACTION ): + mpMetaFile->AddAction( new MetaBmpAction( rDestPt, aBmp ) ); + break; + + case( META_BMPSCALE_ACTION ): + mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) ); + break; + + case( META_BMPSCALEPART_ACTION ): + mpMetaFile->AddAction( new MetaBmpScalePartAction( + rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmp ) ); + break; + } + } + + OUTDEV_INIT(); + + if( !aBmp.IsEmpty() ) + { + TwoRect aPosAry; + + aPosAry.mnSrcX = rSrcPtPixel.X(); + aPosAry.mnSrcY = rSrcPtPixel.Y(); + aPosAry.mnSrcWidth = rSrcSizePixel.Width(); + aPosAry.mnSrcHeight = rSrcSizePixel.Height(); + aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() ); + aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() ); + aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() ); + aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() ); + + const sal_uLong nMirrFlags = ImplAdjustTwoRect( aPosAry, aBmp.GetSizePixel() ); + + if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) + { + if ( nMirrFlags ) + aBmp.Mirror( nMirrFlags ); + + /* #i75264# (corrected with #i81576#) + * sometimes a bitmap is scaled to a ridiculous size and drawn + * to a quite normal VDev, so only a very small part of + * the scaled bitmap will be visible. However actually scaling + * the bitmap will use so much memory that we end with a crash. + * Workaround: since only a small part of the scaled bitmap will + * be actually drawn anyway (because of clipping on the device + * boundary), limit the destination and source rectangles so + * that the destination rectangle will overlap the device but only + * be reasonably (say factor 2) larger than the device itself. + */ + if( aPosAry.mnDestWidth > 2048 || aPosAry.mnDestHeight > 2048 ) + { + if( meOutDevType == OUTDEV_WINDOW || + (meOutDevType == OUTDEV_VIRDEV && mpPDFWriter == 0 ) ) + { + // #i81576# do the following trick only if there is overlap at all + // else the formulae don't work + // theoretically in this case we wouldn't need to draw the bitmap at all + // however there are some esoteric case where that is needed + if( aPosAry.mnDestX + aPosAry.mnDestWidth >= 0 + && aPosAry.mnDestX < mnOutWidth + && aPosAry.mnDestY + aPosAry.mnDestHeight >= 0 + && aPosAry.mnDestY < mnOutHeight ) + { + // reduce scaling to something reasonable taking into account the output size + if( aPosAry.mnDestWidth > 3*mnOutWidth && aPosAry.mnSrcWidth ) + { + const double nScaleX = aPosAry.mnDestWidth/double(aPosAry.mnSrcWidth); + + if( aPosAry.mnDestX + aPosAry.mnDestWidth > mnOutWidth ) + { + aPosAry.mnDestWidth = Max(long(0),mnOutWidth-aPosAry.mnDestX); + } + if( aPosAry.mnDestX < 0 ) + { + aPosAry.mnDestWidth += aPosAry.mnDestX; + aPosAry.mnSrcX -= sal::static_int_cast<long>(aPosAry.mnDestX / nScaleX); + aPosAry.mnDestX = 0; + } + + aPosAry.mnSrcWidth = sal::static_int_cast<long>(aPosAry.mnDestWidth / nScaleX); + } + + if( aPosAry.mnDestHeight > 3*mnOutHeight && aPosAry.mnSrcHeight != 0 ) + { + const double nScaleY = aPosAry.mnDestHeight/double(aPosAry.mnSrcHeight); + + if( aPosAry.mnDestY + aPosAry.mnDestHeight > mnOutHeight ) + { + aPosAry.mnDestHeight = Max(long(0),mnOutHeight-aPosAry.mnDestY); + } + if( aPosAry.mnDestY < 0 ) + { + aPosAry.mnDestHeight += aPosAry.mnDestY; + aPosAry.mnSrcY -= sal::static_int_cast<long>(aPosAry.mnDestY / nScaleY); + aPosAry.mnDestY = 0; + } + + aPosAry.mnSrcHeight = sal::static_int_cast<long>(aPosAry.mnDestHeight / nScaleY); + } + } + } + } + + if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) + mpGraphics->DrawBitmap( &aPosAry, *aBmp.ImplGetImpBitmap()->ImplGetSalBitmap(), this ); + } + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawBitmapEx( const Point& rDestPt, + const BitmapEx& rBitmapEx ) +{ + OSL_TRACE( "OutputDevice::DrawBitmapEx()" ); + + if( ImplIsRecordLayout() ) + return; + + if( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() ) + DrawBitmap( rDestPt, rBitmapEx.GetBitmap() ); + else + { + const Size aSizePix( rBitmapEx.GetSizePixel() ); + ImplDrawBitmapEx( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmapEx, META_BMPEX_ACTION ); + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize, + const BitmapEx& rBitmapEx ) +{ + OSL_TRACE( "OutputDevice::DrawBitmapEx( Size )" ); + + if( ImplIsRecordLayout() ) + return; + + if ( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() ) + DrawBitmap( rDestPt, rDestSize, rBitmapEx.GetBitmap() ); + else + ImplDrawBitmapEx( rDestPt, rDestSize, Point(), rBitmapEx.GetSizePixel(), rBitmapEx, META_BMPEXSCALE_ACTION ); +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel, + const BitmapEx& rBitmapEx ) +{ + OSL_TRACE( "OutputDevice::DrawBitmapEx( Point, Size )" ); + + if( ImplIsRecordLayout() ) + return; + + if( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() ) + DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx.GetBitmap() ); + else + ImplDrawBitmapEx( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx, META_BMPEXSCALEPART_ACTION ); +} + +// ------------------------------------------------------------------ + +void OutputDevice::ImplDrawBitmapEx( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel, + const BitmapEx& rBitmapEx, const sal_uLong nAction ) +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + BitmapEx aBmpEx( rBitmapEx ); + + if ( mnDrawMode & DRAWMODE_NOBITMAP ) + return; + else if ( ROP_INVERT == meRasterOp ) + { + DrawRect( Rectangle( rDestPt, rDestSize ) ); + return; + } + else if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP | + DRAWMODE_GRAYBITMAP | DRAWMODE_GHOSTEDBITMAP ) ) + { + if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP ) ) + { + Bitmap aColorBmp( aBmpEx.GetSizePixel(), ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) ? 4 : 1 ); + sal_uInt8 cCmpVal; + + if ( mnDrawMode & DRAWMODE_BLACKBITMAP ) + cCmpVal = ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) ? 0x80 : 0; + else + cCmpVal = 255; + + aColorBmp.Erase( Color( cCmpVal, cCmpVal, cCmpVal ) ); + + if( aBmpEx.IsAlpha() ) + { + // Create one-bit mask out of alpha channel, by + // thresholding it at alpha=0.5. As + // DRAWMODE_BLACK/WHITEBITMAP requires monochrome + // output, having alpha-induced grey levels is not + // acceptable. + Bitmap aMask( aBmpEx.GetAlpha().GetBitmap() ); + aMask.MakeMono( 128 ); + aBmpEx = BitmapEx( aColorBmp, aMask ); + } + else + { + aBmpEx = BitmapEx( aColorBmp, aBmpEx.GetMask() ); + } + } + else if( !!aBmpEx ) + { + if ( mnDrawMode & DRAWMODE_GRAYBITMAP ) + aBmpEx.Convert( BMP_CONVERSION_8BIT_GREYS ); + + if ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) + aBmpEx.Convert( BMP_CONVERSION_GHOSTED ); + } + } + + if ( mpMetaFile ) + { + switch( nAction ) + { + case( META_BMPEX_ACTION ): + mpMetaFile->AddAction( new MetaBmpExAction( rDestPt, aBmpEx ) ); + break; + + case( META_BMPEXSCALE_ACTION ): + mpMetaFile->AddAction( new MetaBmpExScaleAction( rDestPt, rDestSize, aBmpEx ) ); + break; + + case( META_BMPEXSCALEPART_ACTION ): + mpMetaFile->AddAction( new MetaBmpExScalePartAction( rDestPt, rDestSize, + rSrcPtPixel, rSrcSizePixel, aBmpEx ) ); + break; + } + } + + OUTDEV_INIT(); + + if( OUTDEV_PRINTER == meOutDevType ) + { + if( aBmpEx.IsAlpha() ) + { + // #107169# For true alpha bitmaps, no longer masking the + // bitmap, but perform a full alpha blend against a white + // background here. + Bitmap aBmp( aBmpEx.GetBitmap() ); + aBmp.Blend( aBmpEx.GetAlpha(), Color( COL_WHITE) ); + DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmp ); + } + else + { + Bitmap aBmp( aBmpEx.GetBitmap() ), aMask( aBmpEx.GetMask() ); + aBmp.Replace( aMask, Color( COL_WHITE ) ); + ImplPrintTransparent( aBmp, aMask, rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel ); + } + return; + } + else if( aBmpEx.IsAlpha() ) + { + ImplDrawAlpha( aBmpEx.GetBitmap(), aBmpEx.GetAlpha(), rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel ); + return; + } + + if( !( !aBmpEx ) ) + { + TwoRect aPosAry; + + aPosAry.mnSrcX = rSrcPtPixel.X(); + aPosAry.mnSrcY = rSrcPtPixel.Y(); + aPosAry.mnSrcWidth = rSrcSizePixel.Width(); + aPosAry.mnSrcHeight = rSrcSizePixel.Height(); + aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() ); + aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() ); + aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() ); + aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() ); + + const sal_uLong nMirrFlags = ImplAdjustTwoRect( aPosAry, aBmpEx.GetSizePixel() ); + + if( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) + { + + if( nMirrFlags ) + aBmpEx.Mirror( nMirrFlags ); + + const ImpBitmap* pImpBmp = aBmpEx.ImplGetBitmapImpBitmap(); + const ImpBitmap* pMaskBmp = aBmpEx.ImplGetMaskImpBitmap(); + + if ( pMaskBmp ) + { + // #4919452# reduce operation area to bounds of + // cliprect. since masked transparency involves + // creation of a large vdev and copying the screen + // content into that (slooow read from framebuffer), + // that should considerably increase performance for + // large bitmaps and small clippings. + + // Note that this optimisation is a workaround for a + // Writer peculiarity, namely, to decompose background + // graphics into myriads of disjunct, tiny + // rectangles. That otherwise kills us here, since for + // transparent output, SAL always prepares the whole + // bitmap, if aPosAry contains the whole bitmap (and + // it's _not_ to blame for that). + + // Note the call to ImplPixelToDevicePixel(), since + // aPosAry already contains the mnOutOff-offsets, they + // also have to be applied to the region + Rectangle aClipRegionBounds( ImplPixelToDevicePixel(maRegion).GetBoundRect() ); + + // TODO: Also respect scaling (that's a bit tricky, + // since the source points have to move fractional + // amounts (which is not possible, thus has to be + // emulated by increases copy area) + // const double nScaleX( aPosAry.mnDestWidth / aPosAry.mnSrcWidth ); + // const double nScaleY( aPosAry.mnDestHeight / aPosAry.mnSrcHeight ); + + // for now, only identity scales allowed + if( !aClipRegionBounds.IsEmpty() && + aPosAry.mnDestWidth == aPosAry.mnSrcWidth && + aPosAry.mnDestHeight == aPosAry.mnSrcHeight ) + { + // now intersect dest rect with clip region + aClipRegionBounds.Intersection( Rectangle( aPosAry.mnDestX, + aPosAry.mnDestY, + aPosAry.mnDestX + aPosAry.mnDestWidth - 1, + aPosAry.mnDestY + aPosAry.mnDestHeight - 1 ) ); + + // Note: I could theoretically optimize away the + // DrawBitmap below, if the region is empty + // here. Unfortunately, cannot rule out that + // somebody relies on the side effects. + if( !aClipRegionBounds.IsEmpty() ) + { + aPosAry.mnSrcX += aClipRegionBounds.Left() - aPosAry.mnDestX; + aPosAry.mnSrcY += aClipRegionBounds.Top() - aPosAry.mnDestY; + aPosAry.mnSrcWidth = aClipRegionBounds.GetWidth(); + aPosAry.mnSrcHeight = aClipRegionBounds.GetHeight(); + + aPosAry.mnDestX = aClipRegionBounds.Left(); + aPosAry.mnDestY = aClipRegionBounds.Top(); + aPosAry.mnDestWidth = aClipRegionBounds.GetWidth(); + aPosAry.mnDestHeight = aClipRegionBounds.GetHeight(); + } + } + + mpGraphics->DrawBitmap( &aPosAry, *pImpBmp->ImplGetSalBitmap(), + *pMaskBmp->ImplGetSalBitmap(), + this ); + + // #110958# Paint mask to alpha channel. Luckily, the + // black and white representation of the mask maps to + // the alpha channel + + // #i25167# Restrict mask painting to _opaque_ areas + // of the mask, otherwise we spoil areas where no + // bitmap content was ever visible. Interestingly + // enough, this can be achieved by taking the mask as + // the transparency mask of itself + if( mpAlphaVDev ) + mpAlphaVDev->DrawBitmapEx( rDestPt, + rDestSize, + BitmapEx( aBmpEx.GetMask(), + aBmpEx.GetMask() ) ); + } + else + { + mpGraphics->DrawBitmap( &aPosAry, *pImpBmp->ImplGetSalBitmap(), this ); + + if( mpAlphaVDev ) + { + // #i32109#: Make bitmap area opaque + mpAlphaVDev->ImplFillOpaqueRectangle( Rectangle(rDestPt, rDestSize) ); + } + } + } + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawMask( const Point& rDestPt, + const Bitmap& rBitmap, const Color& rMaskColor ) +{ + OSL_TRACE( "OutputDevice::DrawMask()" ); + + if( ImplIsRecordLayout() ) + return; + + const Size aSizePix( rBitmap.GetSizePixel() ); + ImplDrawMask( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, rMaskColor, META_MASK_ACTION ); + + if( mpAlphaVDev ) + { + const Bitmap& rMask( rBitmap.CreateMask( rMaskColor ) ); + + // #i25167# Restrict mask painting to _opaque_ areas + // of the mask, otherwise we spoil areas where no + // bitmap content was ever visible. Interestingly + // enough, this can be achieved by taking the mask as + // the transparency mask of itself + mpAlphaVDev->DrawBitmapEx( rDestPt, + PixelToLogic( aSizePix ), + BitmapEx( rMask, rMask ) ); + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize, + const Bitmap& rBitmap, const Color& rMaskColor ) +{ + OSL_TRACE( "OutputDevice::DrawMask( Size )" ); + + if( ImplIsRecordLayout() ) + return; + + ImplDrawMask( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, rMaskColor, META_MASKSCALE_ACTION ); + + // TODO: Use mask here + if( mpAlphaVDev ) + { + const Bitmap& rMask( rBitmap.CreateMask( rMaskColor ) ); + + // #i25167# Restrict mask painting to _opaque_ areas + // of the mask, otherwise we spoil areas where no + // bitmap content was ever visible. Interestingly + // enough, this can be achieved by taking the mask as + // the transparency mask of itself + mpAlphaVDev->DrawBitmapEx( rDestPt, + rDestSize, + BitmapEx( rMask, rMask ) ); + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel, + const Bitmap& rBitmap, const Color& rMaskColor ) +{ + OSL_TRACE( "OutputDevice::DrawMask( Point, Size )" ); + + if( ImplIsRecordLayout() ) + return; + + ImplDrawMask( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, rMaskColor, META_MASKSCALEPART_ACTION ); + + // TODO: Use mask here + if( mpAlphaVDev ) + { + const Bitmap& rMask( rBitmap.CreateMask( rMaskColor ) ); + + // #i25167# Restrict mask painting to _opaque_ areas + // of the mask, otherwise we spoil areas where no + // bitmap content was ever visible. Interestingly + // enough, this can be achieved by taking the mask as + // the transparency mask of itself + mpAlphaVDev->DrawBitmapEx( rDestPt, + rDestSize, + rSrcPtPixel, + rSrcSizePixel, + BitmapEx( rMask, rMask ) ); + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::ImplDrawMask( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel, + const Bitmap& rBitmap, const Color& rMaskColor, + const sal_uLong nAction ) +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if( ROP_INVERT == meRasterOp ) + { + DrawRect( Rectangle( rDestPt, rDestSize ) ); + return; + } + + if ( mpMetaFile ) + { + switch( nAction ) + { + case( META_MASK_ACTION ): + mpMetaFile->AddAction( new MetaMaskAction( rDestPt, + rBitmap, rMaskColor ) ); + break; + + case( META_MASKSCALE_ACTION ): + mpMetaFile->AddAction( new MetaMaskScaleAction( rDestPt, + rDestSize, rBitmap, rMaskColor ) ); + break; + + case( META_MASKSCALEPART_ACTION ): + mpMetaFile->AddAction( new MetaMaskScalePartAction( rDestPt, rDestSize, + rSrcPtPixel, rSrcSizePixel, rBitmap, rMaskColor ) ); + break; + } + } + + OUTDEV_INIT(); + + if ( OUTDEV_PRINTER == meOutDevType ) + { + ImplPrintMask( rBitmap, rMaskColor, rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel ); + return; + } + + const ImpBitmap* pImpBmp = rBitmap.ImplGetImpBitmap(); + if ( pImpBmp ) + { + TwoRect aPosAry; + + aPosAry.mnSrcX = rSrcPtPixel.X(); + aPosAry.mnSrcY = rSrcPtPixel.Y(); + aPosAry.mnSrcWidth = rSrcSizePixel.Width(); + aPosAry.mnSrcHeight = rSrcSizePixel.Height(); + aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() ); + aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() ); + aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() ); + aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() ); + + // spiegeln via Koordinaten wollen wir nicht + const sal_uLong nMirrFlags = ImplAdjustTwoRect( aPosAry, pImpBmp->ImplGetSize() ); + + // check if output is necessary + if( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) + { + + if( nMirrFlags ) + { + Bitmap aTmp( rBitmap ); + aTmp.Mirror( nMirrFlags ); + mpGraphics->DrawMask( &aPosAry, *aTmp.ImplGetImpBitmap()->ImplGetSalBitmap(), + ImplColorToSal( rMaskColor ) , this); + } + else + mpGraphics->DrawMask( &aPosAry, *pImpBmp->ImplGetSalBitmap(), + ImplColorToSal( rMaskColor ), this ); + + } + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawImage( const Point& rPos, const Image& rImage, sal_uInt16 nStyle ) +{ + DBG_ASSERT( GetOutDevType() != OUTDEV_PRINTER, "DrawImage(): Images can't be drawn on any mprinter" ); + + if( !rImage.mpImplData || ImplIsRecordLayout() ) + return; + + switch( rImage.mpImplData->meType ) + { + case IMAGETYPE_BITMAP: + DrawBitmap( rPos, *static_cast< Bitmap* >( rImage.mpImplData->mpData ) ); + break; + + case IMAGETYPE_IMAGE: + { + ImplImageData* pData = static_cast< ImplImageData* >( rImage.mpImplData->mpData ); + + if( !pData->mpImageBitmap ) + { + const Size aSize( pData->maBmpEx.GetSizePixel() ); + + pData->mpImageBitmap = new ImplImageBmp; + pData->mpImageBitmap->Create( pData->maBmpEx, aSize.Width(), aSize.Height(), 1 ); + } + + pData->mpImageBitmap->Draw( 0, this, rPos, nStyle ); + } + break; + + default: + break; + } +} + +// ------------------------------------------------------------------ + +void OutputDevice::DrawImage( const Point& rPos, const Size& rSize, + const Image& rImage, sal_uInt16 nStyle ) +{ + DBG_ASSERT( GetOutDevType() != OUTDEV_PRINTER, "DrawImage(): Images can't be drawn on any mprinter" ); + + if( rImage.mpImplData && !ImplIsRecordLayout() ) + { + switch( rImage.mpImplData->meType ) + { + case IMAGETYPE_BITMAP: + DrawBitmap( rPos, rSize, *static_cast< Bitmap* >( rImage.mpImplData->mpData ) ); + break; + + case IMAGETYPE_IMAGE: + { + ImplImageData* pData = static_cast< ImplImageData* >( rImage.mpImplData->mpData ); + + if ( !pData->mpImageBitmap ) + { + const Size aSize( pData->maBmpEx.GetSizePixel() ); + + pData->mpImageBitmap = new ImplImageBmp; + pData->mpImageBitmap->Create( pData->maBmpEx, aSize.Width(), aSize.Height(), 1 ); + } + + pData->mpImageBitmap->Draw( 0, this, rPos, nStyle, &rSize ); + } + break; + + default: + break; + } + } +} + +// ------------------------------------------------------------------ + +Bitmap OutputDevice::GetBitmap( const Point& rSrcPt, const Size& rSize ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Bitmap aBmp; + long nX = ImplLogicXToDevicePixel( rSrcPt.X() ); + long nY = ImplLogicYToDevicePixel( rSrcPt.Y() ); + long nWidth = ImplLogicWidthToDevicePixel( rSize.Width() ); + long nHeight = ImplLogicHeightToDevicePixel( rSize.Height() ); + + if ( mpGraphics || ( (OutputDevice*) this )->ImplGetGraphics() ) + { + if ( nWidth && nHeight ) + { + Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + sal_Bool bClipped = sal_False; + + // X-Koordinate ausserhalb des Bereichs? + if ( nX < mnOutOffX ) + { + nWidth -= ( mnOutOffX - nX ); + nX = mnOutOffX; + bClipped = sal_True; + } + + // Y-Koordinate ausserhalb des Bereichs? + if ( nY < mnOutOffY ) + { + nHeight -= ( mnOutOffY - nY ); + nY = mnOutOffY; + bClipped = sal_True; + } + + // Breite ausserhalb des Bereichs? + if ( (nWidth + nX) > (mnOutWidth + mnOutOffX) ) + { + nWidth = mnOutOffX + mnOutWidth - nX; + bClipped = sal_True; + } + + // Hoehe ausserhalb des Bereichs? + if ( (nHeight + nY) > (mnOutHeight + mnOutOffY) ) + { + nHeight = mnOutOffY + mnOutHeight - nY; + bClipped = sal_True; + } + + if ( bClipped ) + { + // Falls auf den sichtbaren Bereich geclipped wurde, + // muessen wir eine Bitmap in der rchtigen Groesse + // erzeugen, in die die geclippte Bitmap an die angepasste + // Position kopiert wird + VirtualDevice aVDev( *this ); + + if ( aVDev.SetOutputSizePixel( aRect.GetSize() ) ) + { + if ( ((OutputDevice*)&aVDev)->mpGraphics || ((OutputDevice*)&aVDev)->ImplGetGraphics() ) + { + TwoRect aPosAry; + + aPosAry.mnSrcX = nX; + aPosAry.mnSrcY = nY; + aPosAry.mnSrcWidth = nWidth; + aPosAry.mnSrcHeight = nHeight; + aPosAry.mnDestX = ( aRect.Left() < mnOutOffX ) ? ( mnOutOffX - aRect.Left() ) : 0L; + aPosAry.mnDestY = ( aRect.Top() < mnOutOffY ) ? ( mnOutOffY - aRect.Top() ) : 0L; + aPosAry.mnDestWidth = nWidth; + aPosAry.mnDestHeight = nHeight; + + if ( (nWidth > 0) && (nHeight > 0) ) + (((OutputDevice*)&aVDev)->mpGraphics)->CopyBits( &aPosAry, mpGraphics, this, this ); + + aBmp = aVDev.GetBitmap( Point(), aVDev.GetOutputSizePixel() ); + } + else + bClipped = sal_False; + } + else + bClipped = sal_False; + } + + if ( !bClipped ) + { + SalBitmap* pSalBmp = mpGraphics->GetBitmap( nX, nY, nWidth, nHeight, this ); + + if( pSalBmp ) + { + ImpBitmap* pImpBmp = new ImpBitmap; + pImpBmp->ImplSetSalBitmap( pSalBmp ); + aBmp.ImplSetImpBitmap( pImpBmp ); + } + } + } + } + + return aBmp; +} + +// ------------------------------------------------------------------ + +BitmapEx OutputDevice::GetBitmapEx( const Point& rSrcPt, const Size& rSize ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // #110958# Extract alpha value from VDev, if any + if( mpAlphaVDev ) + { + Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( rSrcPt, rSize ) ); + + // ensure 8 bit alpha + if( aAlphaBitmap.GetBitCount() > 8 ) + aAlphaBitmap.Convert( BMP_CONVERSION_8BIT_GREYS ); + + return BitmapEx(GetBitmap( rSrcPt, rSize ), AlphaMask( aAlphaBitmap ) ); + } + else + return GetBitmap( rSrcPt, rSize ); +} + +// ------------------------------------------------------------------ + +void OutputDevice::ImplGetFrameBitmap( const Point& rDestPt, const Size& rSize, + Bitmap& rBitmap ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + sal_Bool bOldMap = mbMap; + ((OutputDevice*)this)->mbMap = sal_False; + rBitmap = GetBitmap( rDestPt, rSize ); + ((OutputDevice*)this)->mbMap = bOldMap; +} + +// ------------------------------------------------------------------ + +Color OutputDevice::GetPixel( const Point& rPt ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor; + + if ( mpGraphics || ((OutputDevice*)this)->ImplGetGraphics() ) + { + if ( mbInitClipRegion ) + ((OutputDevice*)this)->ImplInitClipRegion(); + + if ( !mbOutputClipped ) + { + const long nX = ImplLogicXToDevicePixel( rPt.X() ); + const long nY = ImplLogicYToDevicePixel( rPt.Y() ); + const SalColor aSalCol = mpGraphics->GetPixel( nX, nY, this ); + aColor.SetRed( SALCOLOR_RED( aSalCol ) ); + aColor.SetGreen( SALCOLOR_GREEN( aSalCol ) ); + aColor.SetBlue( SALCOLOR_BLUE( aSalCol ) ); + } + } + return aColor; +} + +// ------------------------------------------------------------------ + +Color* OutputDevice::GetPixel( const Polygon& rPts ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color* pColors = NULL; + const sal_uInt16 nSize = rPts.GetSize(); + + if( nSize ) + { + if ( mpGraphics || ((OutputDevice*)this)->ImplGetGraphics() ) + { + if ( mbInitClipRegion ) + ((OutputDevice*)this)->ImplInitClipRegion(); + + if ( !mbOutputClipped ) + { + pColors = new Color[ nSize ]; + + for( sal_uInt16 i = 0; i < nSize; i++ ) + { + Color& rCol = pColors[ i ]; + const Point& rPt = rPts[ i ]; + const SalColor aSalCol( mpGraphics->GetPixel( ImplLogicXToDevicePixel( rPt.X() ), + ImplLogicYToDevicePixel( rPt.Y() ) , this) ); + + rCol.SetRed( SALCOLOR_RED( aSalCol ) ); + rCol.SetGreen( SALCOLOR_GREEN( aSalCol ) ); + rCol.SetBlue( SALCOLOR_BLUE( aSalCol ) ); + } + } + } + } + + return pColors; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPixel( const Point& rPt ) +{ + OSL_TRACE( "OutputDevice::DrawPixel()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaPointAction( rPt ) ); + + if ( !IsDeviceOutputNecessary() || !mbLineColor || ImplIsRecordLayout() ) + return; + + Point aPt = ImplLogicToDevicePixel( rPt ); + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + + mpGraphics->DrawPixel( aPt.X(), aPt.Y(), this ); + + if( mpAlphaVDev ) + mpAlphaVDev->DrawPixel( rPt ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPixel( const Point& rPt, const Color& rColor ) +{ + OSL_TRACE( "OutputDevice::DrawPixel()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + + if( mnDrawMode & ( DRAWMODE_BLACKLINE | DRAWMODE_WHITELINE | + DRAWMODE_GRAYLINE | DRAWMODE_GHOSTEDLINE | + DRAWMODE_SETTINGSLINE ) ) + { + if( !ImplIsColorTransparent( aColor ) ) + { + if( mnDrawMode & DRAWMODE_BLACKLINE ) + { + aColor = Color( COL_BLACK ); + } + else if( mnDrawMode & DRAWMODE_WHITELINE ) + { + aColor = Color( COL_WHITE ); + } + else if( mnDrawMode & DRAWMODE_GRAYLINE ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if( mnDrawMode & DRAWMODE_SETTINGSLINE ) + { + aColor = GetSettings().GetStyleSettings().GetFontColor(); + } + + if( mnDrawMode & DRAWMODE_GHOSTEDLINE ) + { + aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80, + ( aColor.GetGreen() >> 1 ) | 0x80, + ( aColor.GetBlue() >> 1 ) | 0x80 ); + } + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaPixelAction( rPt, aColor ) ); + + if ( !IsDeviceOutputNecessary() || ImplIsColorTransparent( aColor ) || ImplIsRecordLayout() ) + return; + + Point aPt = ImplLogicToDevicePixel( rPt ); + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + mpGraphics->DrawPixel( aPt.X(), aPt.Y(), ImplColorToSal( aColor ), this ); + + if( mpAlphaVDev ) + mpAlphaVDev->DrawPixel( rPt ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPixel( const Polygon& rPts, const Color* pColors ) +{ + if ( !pColors ) + DrawPixel( rPts, GetLineColor() ); + else + { + OSL_TRACE( "OutputDevice::DrawPixel()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_ASSERT( pColors, "OutputDevice::DrawPixel: No color array specified" ); + + const sal_uInt16 nSize = rPts.GetSize(); + + if ( nSize ) + { + if ( mpMetaFile ) + for ( sal_uInt16 i = 0; i < nSize; i++ ) + mpMetaFile->AddAction( new MetaPixelAction( rPts[ i ], pColors[ i ] ) ); + + if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + // we need a graphics + if ( mpGraphics || ImplGetGraphics() ) + { + if ( mbInitClipRegion ) + ImplInitClipRegion(); + + if ( mbOutputClipped ) + return; + + for ( sal_uInt16 i = 0; i < nSize; i++ ) + { + const Point aPt( ImplLogicToDevicePixel( rPts[ i ] ) ); + mpGraphics->DrawPixel( aPt.X(), aPt.Y(), ImplColorToSal( pColors[ i ] ), this ); + } + } + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawPixel( rPts, pColors ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPixel( const Polygon& rPts, const Color& rColor ) +{ + if( rColor != COL_TRANSPARENT && ! ImplIsRecordLayout() ) + { + const sal_uInt16 nSize = rPts.GetSize(); + Color* pColArray = new Color[ nSize ]; + + for( sal_uInt16 i = 0; i < nSize; i++ ) + pColArray[ i ] = rColor; + + DrawPixel( rPts, pColArray ); + delete[] pColArray; + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawPixel( rPts, rColor ); +} + +// ------------------------------------------------------------------------ + +namespace +{ + sal_uInt8 lcl_calcColor( const sal_uInt8 nSourceColor, const sal_uInt8 nSourceOpaq, const sal_uInt8 nDestColor ) + { + int c = ( (int)nDestColor * ( 255 - nSourceOpaq ) ) + + (int)nSourceOpaq * (int)nSourceColor; + return sal_uInt8( c / 255 ); + } +} + +// ------------------------------------------------------------------------ + +Bitmap OutputDevice::ImplBlendWithAlpha( Bitmap aBmp, + BitmapReadAccess* pP, + BitmapReadAccess* pA, + const Rectangle& aDstRect, + const sal_Int32 nOffY, + const sal_Int32 nDstHeight, + const sal_Int32 nOffX, + const sal_Int32 nDstWidth, + const long* pMapX, + const long* pMapY ) +{ + BitmapColor aDstCol,aSrcCol; + Bitmap res; + int nX, nOutX, nY, nOutY; + + OSL_ENSURE(mpAlphaVDev, + "ImplBlendWithAlpha(): call me only with valid alpha VDev!" ); + + sal_Bool bOldMapMode( mpAlphaVDev->IsMapModeEnabled() ); + mpAlphaVDev->EnableMapMode(sal_False); + + Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) ); + BitmapWriteAccess* pAlphaW = aAlphaBitmap.AcquireWriteAccess(); + + if( GetBitCount() <= 8 ) + { + Bitmap aDither( aBmp.GetSizePixel(), 8 ); + BitmapColor aIndex( 0 ); + BitmapReadAccess* pB = aBmp.AcquireReadAccess(); + BitmapWriteAccess* pW = aDither.AcquireWriteAccess(); + + if( pB && pP && pA && pW && pAlphaW ) + { + for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ ) + { + const long nMapY = pMapY[ nY ]; + const long nModY = ( nOutY & 0x0FL ) << 4L; + + for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ ) + { + const long nMapX = pMapX[ nX ]; + const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ]; + + aSrcCol = pP->GetColor( nMapY, nMapX ); + aDstCol = pB->GetColor( nY, nX ); + const sal_uInt8 nSrcOpaq = 255 - pA->GetPixel( nMapY, nMapX ).GetBlueOrIndex(); + const sal_uInt8 nDstOpaq = 255 - pAlphaW->GetPixel( nY, nX ).GetBlueOrIndex(); + + aDstCol.SetRed( lcl_calcColor( aSrcCol.GetRed(), nSrcOpaq, aDstCol.GetRed() ) ); + aDstCol.SetBlue( lcl_calcColor( aSrcCol.GetBlue(), nSrcOpaq, aDstCol.GetBlue() ) ); + aDstCol.SetGreen( lcl_calcColor( aSrcCol.GetGreen(), nSrcOpaq, aDstCol.GetGreen() ) ); + + aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16UL ] + + nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16UL ] + + nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16UL ] ) ); + pW->SetPixel( nY, nX, aIndex ); + + // Have to perform the compositing 'algebra' in + // the inverse alpha space (with 255 meaning + // opaque), otherwise, transitivity is not + // achieved. + const sal_uInt8 nSrcAlpha = 255-COLOR_CHANNEL_MERGE( 255, (sal_uInt8)nDstOpaq, nSrcOpaq ); + + aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] + + nVCLGLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] + + nVCLBLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] ) ); + pAlphaW->SetPixel( nY, nX, aIndex ); + } + } + } + + aBmp.ReleaseAccess( pB ); + aDither.ReleaseAccess( pW ); + res = aDither; + } + else + { + BitmapWriteAccess* pB = aBmp.AcquireWriteAccess(); + if( pP && pA && pB ) + { + for( nY = 0; nY < nDstHeight; nY++ ) + { + const long nMapY = pMapY[ nY ]; + + for( nX = 0; nX < nDstWidth; nX++ ) + { + const long nMapX = pMapX[ nX ]; + + aSrcCol = pP->GetColor( nMapY, nMapX ); + aDstCol = pB->GetColor( nY, nX ); + const sal_uInt8 nSrcOpaq = 255 - pA->GetPixel( nMapY, nMapX ).GetBlueOrIndex(); + const sal_uInt8 nDstOpaq = 255 - pAlphaW->GetPixel( nY, nX ).GetBlueOrIndex(); + + aDstCol.SetRed( lcl_calcColor( aSrcCol.GetRed(), nSrcOpaq, aDstCol.GetRed() ) ); + aDstCol.SetBlue( lcl_calcColor( aSrcCol.GetBlue(), nSrcOpaq, aDstCol.GetBlue() ) ); + aDstCol.SetGreen( lcl_calcColor( aSrcCol.GetGreen(), nSrcOpaq, aDstCol.GetGreen() ) ); + + pB->SetPixel( nY, nX, aDstCol ); + + // Have to perform the compositing 'algebra' in + // the inverse alpha space (with 255 meaning + // opaque), otherwise, transitivity is not + // achieved. + const sal_uInt8 nSrcAlpha = 255-COLOR_CHANNEL_MERGE( 255, (sal_uInt8)nDstOpaq, nSrcOpaq ); + + pAlphaW->SetPixel( nY, nX, Color(nSrcAlpha, nSrcAlpha, nSrcAlpha) ); + } + } + } + + aBmp.ReleaseAccess( pB ); + res = aBmp; + } + + aAlphaBitmap.ReleaseAccess( pAlphaW ); + mpAlphaVDev->DrawBitmap( aDstRect.TopLeft(), aAlphaBitmap ); + mpAlphaVDev->EnableMapMode( bOldMapMode ); + + return res; +} + +// ------------------------------------------------------------------------ + +Bitmap OutputDevice::ImplBlend( Bitmap aBmp, + BitmapReadAccess* pP, + BitmapReadAccess* pA, + const sal_Int32 nOffY, + const sal_Int32 nDstHeight, + const sal_Int32 nOffX, + const sal_Int32 nDstWidth, + const Rectangle& aBmpRect, + const Size& aOutSz, + const bool bHMirr, + const bool bVMirr, + const long* pMapX, + const long* pMapY ) +{ + BitmapColor aDstCol; + Bitmap res; + int nX, nOutX, nY, nOutY; + + if( GetBitCount() <= 8 ) + { + Bitmap aDither( aBmp.GetSizePixel(), 8 ); + BitmapColor aIndex( 0 ); + BitmapReadAccess* pB = aBmp.AcquireReadAccess(); + BitmapWriteAccess* pW = aDither.AcquireWriteAccess(); + + if( pB && pP && pA && pW ) + { + for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ ) + { + const long nMapY = pMapY[ nY ]; + const long nModY = ( nOutY & 0x0FL ) << 4L; + + for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ ) + { + const long nMapX = pMapX[ nX ]; + const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ]; + + aDstCol = pB->GetColor( nY, nX ); + aDstCol.Merge( pP->GetColor( nMapY, nMapX ), (sal_uInt8) pA->GetPixel( nMapY, nMapX ) ); + aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16UL ] + + nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16UL ] + + nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16UL ] ) ); + pW->SetPixel( nY, nX, aIndex ); + } + } + } + + aBmp.ReleaseAccess( pB ); + aDither.ReleaseAccess( pW ); + res = aDither; + } + else + { + BitmapWriteAccess* pB = aBmp.AcquireWriteAccess(); + + bool bFastBlend = false; + if( pP && pA && pB ) + { + SalTwoRect aTR; + aTR.mnSrcX = aBmpRect.Left(); + aTR.mnSrcY = aBmpRect.Top(); + aTR.mnSrcWidth = aBmpRect.GetWidth(); + aTR.mnSrcHeight = aBmpRect.GetHeight(); + aTR.mnDestX = nOffX; + aTR.mnDestY = nOffY; + aTR.mnDestWidth = aOutSz.Width(); + aTR.mnDestHeight= aOutSz.Height(); + + if( !bHMirr || !bVMirr ) + bFastBlend = ImplFastBitmapBlending( *pB,*pP,*pA, aTR ); + } + + if( pP && pA && pB && !bFastBlend ) + { + switch( pP->GetScanlineFormat() ) + { + case( BMP_FORMAT_8BIT_PAL ): + { + for( nY = 0; nY < nDstHeight; nY++ ) + { + const long nMapY = pMapY[ nY ]; + Scanline pPScan = pP->GetScanline( nMapY ); + Scanline pAScan = pA->GetScanline( nMapY ); + + for( nX = 0; nX < nDstWidth; nX++ ) + { + const long nMapX = pMapX[ nX ]; + aDstCol = pB->GetPixel( nY, nX ); + pB->SetPixel( nY, nX, aDstCol.Merge( pP->GetPaletteColor( pPScan[ nMapX ] ), + pAScan[ nMapX ] ) ); + } + } + } + break; + + case( BMP_FORMAT_24BIT_TC_BGR ): + { + for( nY = 0; nY < nDstHeight; nY++ ) + { + const long nMapY = pMapY[ nY ]; + Scanline pPScan = pP->GetScanline( nMapY ); + Scanline pAScan = pA->GetScanline( nMapY ); + + for( nX = 0; nX < nDstWidth; nX++ ) + { + const long nMapX = pMapX[ nX ]; + Scanline pTmp = pPScan + nMapX * 3; + + aDstCol = pB->GetPixel( nY, nX ); + pB->SetPixel( nY, nX, aDstCol.Merge( pTmp[ 2 ], pTmp[ 1 ], pTmp[ 0 ], + pAScan[ nMapX ] ) ); + } + } + } + break; + + case( BMP_FORMAT_24BIT_TC_RGB ): + { + for( nY = 0; nY < nDstHeight; nY++ ) + { + const long nMapY = pMapY[ nY ]; + Scanline pPScan = pP->GetScanline( nMapY ); + Scanline pAScan = pA->GetScanline( nMapY ); + + for( nX = 0; nX < nDstWidth; nX++ ) + { + const long nMapX = pMapX[ nX ]; + Scanline pTmp = pPScan + nMapX * 3; + + aDstCol = pB->GetPixel( nY, nX ); + pB->SetPixel( nY, nX, aDstCol.Merge( pTmp[ 0 ], pTmp[ 1 ], pTmp[ 2 ], + pAScan[ nMapX ] ) ); + } + } + } + break; + + default: + { + for( nY = 0; nY < nDstHeight; nY++ ) + { + const long nMapY = pMapY[ nY ]; + Scanline pAScan = pA->GetScanline( nMapY ); + + for( nX = 0; nX < nDstWidth; nX++ ) + { + const long nMapX = pMapX[ nX ]; + aDstCol = pB->GetPixel( nY, nX ); + pB->SetPixel( nY, nX, aDstCol.Merge( pP->GetColor( nMapY, nMapX ), + pAScan[ nMapX ] ) ); + } + } + } + break; + } + } + + aBmp.ReleaseAccess( pB ); + res = aBmp; + } + + return res; +} + +// ------------------------------------------------------------------------ + +void OutputDevice::ImplDrawAlpha( const Bitmap& rBmp, const AlphaMask& rAlpha, + const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel ) +{ + const Point aNullPt; + Point aOutPt( LogicToPixel( rDestPt ) ); + Size aOutSz( LogicToPixel( rDestSize ) ); + Rectangle aDstRect( aNullPt, GetOutputSizePixel() ); + const sal_Bool bHMirr = aOutSz.Width() < 0, bVMirr = aOutSz.Height() < 0; + + if( OUTDEV_WINDOW == meOutDevType ) + { + const Region aPaintRgn( ( (Window*) this )->GetPaintRegion() ); + + if( !aPaintRgn.IsNull() ) + aDstRect.Intersection( LogicToPixel( aPaintRgn.GetBoundRect() ) ); + } + + if( bHMirr ) + { + aOutSz.Width() = -aOutSz.Width(); + aOutPt.X() -= ( aOutSz.Width() - 1L ); + } + + if( bVMirr ) + { + aOutSz.Height() = -aOutSz.Height(); + aOutPt.Y() -= ( aOutSz.Height() - 1L ); + } + + if( !aDstRect.Intersection( Rectangle( aOutPt, aOutSz ) ).IsEmpty() ) + { + bool bNativeAlpha = false; + static const char* pDisableNative = getenv( "SAL_DISABLE_NATIVE_ALPHA"); + // #i83087# Naturally, system alpha blending cannot work with + // separate alpha VDev + if( !mpAlphaVDev && !pDisableNative && !bHMirr && !bVMirr ) + { + Point aRelPt = aOutPt + Point( mnOutOffX, mnOutOffY ); + SalTwoRect aTR = { + rSrcPtPixel.X(), rSrcPtPixel.Y(), + rSrcSizePixel.Width(), rSrcSizePixel.Height(), + aRelPt.X(), aRelPt.Y(), + aOutSz.Width(), aOutSz.Height() + }; + SalBitmap* pSalSrcBmp = rBmp.ImplGetImpBitmap()->ImplGetSalBitmap(); + SalBitmap* pSalAlphaBmp = rAlpha.ImplGetImpBitmap()->ImplGetSalBitmap(); + bNativeAlpha = mpGraphics->DrawAlphaBitmap( aTR, *pSalSrcBmp, *pSalAlphaBmp, this ); + } + + VirtualDevice* pOldVDev = mpAlphaVDev; + + Rectangle aBmpRect( aNullPt, rBmp.GetSizePixel() ); + if( !bNativeAlpha + && !aBmpRect.Intersection( Rectangle( rSrcPtPixel, rSrcSizePixel ) ).IsEmpty() ) + { + GDIMetaFile* pOldMetaFile = mpMetaFile; mpMetaFile = NULL; + const sal_Bool bOldMap = mbMap; mbMap = sal_False; + Bitmap aBmp( GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) ); + + // #109044# The generated bitmap need not necessarily be + // of aDstRect dimensions, it's internally clipped to + // window bounds. Thus, we correct the dest size here, + // since we later use it (in nDstWidth/Height) for pixel + // access) + // #i38887# reading from screen may sometimes fail + if( aBmp.ImplGetImpBitmap() ) + aDstRect.SetSize( aBmp.GetSizePixel() ); + + BitmapColor aDstCol; + const long nSrcWidth = aBmpRect.GetWidth(), nSrcHeight = aBmpRect.GetHeight(); + const long nDstWidth = aDstRect.GetWidth(), nDstHeight = aDstRect.GetHeight(); + const long nOutWidth = aOutSz.Width(), nOutHeight = aOutSz.Height(); + // calculate offset in original bitmap + // in RTL case this is a little more complicated since the contents of the + // bitmap is not mirrored (it never is), however the paint region and bmp region + // are in mirrored coordinates, so the intersection of (aOutPt,aOutSz) with these + // is content wise somewhere else and needs to take mirroring into account + const long nOffX = IsRTLEnabled() + ? aOutSz.Width() - aDstRect.GetWidth() - (aDstRect.Left() - aOutPt.X()) + : aDstRect.Left() - aOutPt.X(), + nOffY = aDstRect.Top() - aOutPt.Y(); + long nX, nOutX, nY, nOutY; + long nMirrOffX = 0; + long nMirrOffY = 0; + long* pMapX = new long[ nDstWidth ]; + long* pMapY = new long[ nDstHeight ]; + + // create horizontal mapping table + if( bHMirr ) + nMirrOffX = ( aBmpRect.Left() << 1 ) + nSrcWidth - 1; + + for( nX = 0L, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ ) + { + pMapX[ nX ] = aBmpRect.Left() + nOutX * nSrcWidth / nOutWidth; + if( bHMirr ) + pMapX[ nX ] = nMirrOffX - pMapX[ nX ]; + } + + // create vertical mapping table + if( bVMirr ) + nMirrOffY = ( aBmpRect.Top() << 1 ) + nSrcHeight - 1; + + for( nY = 0L, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ ) + { + pMapY[ nY ] = aBmpRect.Top() + nOutY * nSrcHeight / nOutHeight; + + if( bVMirr ) + pMapY[ nY ] = nMirrOffY - pMapY[ nY ]; + } + + BitmapReadAccess* pP = ( (Bitmap&) rBmp ).AcquireReadAccess(); + BitmapReadAccess* pA = ( (AlphaMask&) rAlpha ).AcquireReadAccess(); + + DBG_ASSERT( pA->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL || + pA->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK, + "OutputDevice::ImplDrawAlpha(): non-8bit alpha no longer supported!" ); + + // #i38887# reading from screen may sometimes fail + if( aBmp.ImplGetImpBitmap() ) + { + Bitmap aTmp; + + if( mpAlphaVDev ) + { + aTmp = ImplBlendWithAlpha( + aBmp,pP,pA, + aDstRect, + nOffY,nDstHeight, + nOffX,nDstWidth, + pMapX,pMapY ); + } + else + { + aTmp = ImplBlend( + aBmp,pP,pA, + nOffY,nDstHeight, + nOffX,nDstWidth, + aBmpRect,aOutSz, + bHMirr,bVMirr, + pMapX,pMapY ); + } + + // #110958# Disable alpha VDev, we're doing the necessary + // stuff explicitely furher below + if( mpAlphaVDev ) + mpAlphaVDev = NULL; + + DrawBitmap( aDstRect.TopLeft(), + aTmp ); + + // #110958# Enable alpha VDev again + mpAlphaVDev = pOldVDev; + } + + ( (Bitmap&) rBmp ).ReleaseAccess( pP ); + ( (AlphaMask&) rAlpha ).ReleaseAccess( pA ); + + delete[] pMapX; + delete[] pMapY; + mbMap = bOldMap; + mpMetaFile = pOldMetaFile; + } + } +} + +// ------------------------------------------------------------------------ + +void OutputDevice::ImplPrintTransparent( const Bitmap& rBmp, const Bitmap& rMask, + const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel ) +{ + Point aPt; + Point aDestPt( LogicToPixel( rDestPt ) ); + Size aDestSz( LogicToPixel( rDestSize ) ); + Rectangle aSrcRect( rSrcPtPixel, rSrcSizePixel ); + + aSrcRect.Justify(); + + if( !rBmp.IsEmpty() && aSrcRect.GetWidth() && aSrcRect.GetHeight() && aDestSz.Width() && aDestSz.Height() ) + { + Bitmap aPaint( rBmp ), aMask( rMask ); + sal_uLong nMirrFlags = 0UL; + + if( aMask.GetBitCount() > 1 ) + aMask.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); + + // mirrored horizontically + if( aDestSz.Width() < 0L ) + { + aDestSz.Width() = -aDestSz.Width(); + aDestPt.X() -= ( aDestSz.Width() - 1L ); + nMirrFlags |= BMP_MIRROR_HORZ; + } + + // mirrored vertically + if( aDestSz.Height() < 0L ) + { + aDestSz.Height() = -aDestSz.Height(); + aDestPt.Y() -= ( aDestSz.Height() - 1L ); + nMirrFlags |= BMP_MIRROR_VERT; + } + + // source cropped? + if( aSrcRect != Rectangle( aPt, aPaint.GetSizePixel() ) ) + { + aPaint.Crop( aSrcRect ); + aMask.Crop( aSrcRect ); + } + + // destination mirrored + if( nMirrFlags ) + { + aPaint.Mirror( nMirrFlags ); + aMask.Mirror( nMirrFlags ); + } + + // we always want to have a mask + if( aMask.IsEmpty() ) + { + aMask = Bitmap( aSrcRect.GetSize(), 1 ); + aMask.Erase( Color( COL_BLACK ) ); + } + + // do painting + const long nSrcWidth = aSrcRect.GetWidth(), nSrcHeight = aSrcRect.GetHeight(); + long nX, nY, nWorkX, nWorkY, nWorkWidth, nWorkHeight; + long* pMapX = new long[ nSrcWidth + 1 ]; + long* pMapY = new long[ nSrcHeight + 1 ]; + const sal_Bool bOldMap = mbMap; + + mbMap = sal_False; + + // create forward mapping tables + for( nX = 0L; nX <= nSrcWidth; nX++ ) + pMapX[ nX ] = aDestPt.X() + FRound( (double) aDestSz.Width() * nX / nSrcWidth ); + + for( nY = 0L; nY <= nSrcHeight; nY++ ) + pMapY[ nY ] = aDestPt.Y() + FRound( (double) aDestSz.Height() * nY / nSrcHeight ); + + // walk through all rectangles of mask + Region aWorkRgn( aMask.CreateRegion( COL_BLACK, Rectangle( Point(), aMask.GetSizePixel() ) ) ); + ImplRegionInfo aInfo; + sal_Bool bRgnRect = aWorkRgn.ImplGetFirstRect( aInfo, nWorkX, nWorkY, nWorkWidth, nWorkHeight ); + + while( bRgnRect ) + { + Bitmap aBandBmp( aPaint ); + const Rectangle aBandRect( Point( nWorkX, nWorkY ), Size( nWorkWidth, nWorkHeight ) ); + const Point aMapPt( pMapX[ nWorkX ], pMapY[ nWorkY ] ); + const Size aMapSz( pMapX[ nWorkX + nWorkWidth ] - aMapPt.X(), pMapY[ nWorkY + nWorkHeight ] - aMapPt.Y() ); + + aBandBmp.Crop( aBandRect ); + ImplDrawBitmap( aMapPt, aMapSz, Point(), aBandBmp.GetSizePixel(), aBandBmp, META_BMPSCALEPART_ACTION ); + bRgnRect = aWorkRgn.ImplGetNextRect( aInfo, nWorkX, nWorkY, nWorkWidth, nWorkHeight ); + } + + mbMap = bOldMap; + + delete[] pMapX; + delete[] pMapY; + } +} + +// ------------------------------------------------------------------------ + +void OutputDevice::ImplPrintMask( const Bitmap& rMask, const Color& rMaskColor, + const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel ) +{ + Point aPt; + Point aDestPt( LogicToPixel( rDestPt ) ); + Size aDestSz( LogicToPixel( rDestSize ) ); + Rectangle aSrcRect( rSrcPtPixel, rSrcSizePixel ); + + aSrcRect.Justify(); + + if( !rMask.IsEmpty() && aSrcRect.GetWidth() && aSrcRect.GetHeight() && aDestSz.Width() && aDestSz.Height() ) + { + Bitmap aMask( rMask ); + sal_uLong nMirrFlags = 0UL; + + if( aMask.GetBitCount() > 1 ) + aMask.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); + + // mirrored horizontically + if( aDestSz.Width() < 0L ) + { + aDestSz.Width() = -aDestSz.Width(); + aDestPt.X() -= ( aDestSz.Width() - 1L ); + nMirrFlags |= BMP_MIRROR_HORZ; + } + + // mirrored vertically + if( aDestSz.Height() < 0L ) + { + aDestSz.Height() = -aDestSz.Height(); + aDestPt.Y() -= ( aDestSz.Height() - 1L ); + nMirrFlags |= BMP_MIRROR_VERT; + } + + // source cropped? + if( aSrcRect != Rectangle( aPt, aMask.GetSizePixel() ) ) + aMask.Crop( aSrcRect ); + + // destination mirrored + if( nMirrFlags ) + aMask.Mirror( nMirrFlags ); + + // do painting + const long nSrcWidth = aSrcRect.GetWidth(), nSrcHeight = aSrcRect.GetHeight(); + long nX, nY, nWorkX, nWorkY, nWorkWidth, nWorkHeight; + long* pMapX = new long[ nSrcWidth + 1 ]; + long* pMapY = new long[ nSrcHeight + 1 ]; + GDIMetaFile* pOldMetaFile = mpMetaFile; + const sal_Bool bOldMap = mbMap; + + mpMetaFile = NULL; + mbMap = sal_False; + Push( PUSH_FILLCOLOR | PUSH_LINECOLOR ); + SetLineColor( rMaskColor ); + SetFillColor( rMaskColor ); + ImplInitLineColor(); + ImplInitFillColor(); + + // create forward mapping tables + for( nX = 0L; nX <= nSrcWidth; nX++ ) + pMapX[ nX ] = aDestPt.X() + FRound( (double) aDestSz.Width() * nX / nSrcWidth ); + + for( nY = 0L; nY <= nSrcHeight; nY++ ) + pMapY[ nY ] = aDestPt.Y() + FRound( (double) aDestSz.Height() * nY / nSrcHeight ); + + // walk through all rectangles of mask + Region aWorkRgn( aMask.CreateRegion( COL_BLACK, Rectangle( Point(), aMask.GetSizePixel() ) ) ); + ImplRegionInfo aInfo; + sal_Bool bRgnRect = aWorkRgn.ImplGetFirstRect( aInfo, nWorkX, nWorkY, nWorkWidth, nWorkHeight ); + + while( bRgnRect ) + { + const Point aMapPt( pMapX[ nWorkX ], pMapY[ nWorkY ] ); + const Size aMapSz( pMapX[ nWorkX + nWorkWidth ] - aMapPt.X(), pMapY[ nWorkY + nWorkHeight ] - aMapPt.Y() ); + + DrawRect( Rectangle( aMapPt, aMapSz ) ); + bRgnRect = aWorkRgn.ImplGetNextRect( aInfo, nWorkX, nWorkY, nWorkWidth, nWorkHeight ); + } + + Pop(); + delete[] pMapX; + delete[] pMapY; + mbMap = bOldMap; + mpMetaFile = pOldMetaFile; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/outdev3.cxx b/vcl/source/gdi/outdev3.cxx new file mode 100644 index 000000000000..11da7ab19086 --- /dev/null +++ b/vcl/source/gdi/outdev3.cxx @@ -0,0 +1,8150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "i18npool/mslangid.hxx" + +#include "svsys.h" +#include "vcl/salgdi.hxx" +#include "vcl/sallayout.hxx" +#include "rtl/tencinfo.h" +#include "tools/debug.hxx" +#include "vcl/svdata.hxx" +#include "vcl/metric.hxx" +#include "vcl/impfont.hxx" +#include "vcl/metaact.hxx" +#include "vcl/gdimtf.hxx" +#include "vcl/outdata.hxx" +#include "vcl/outfont.hxx" +#include "basegfx/polygon/b2dpolygon.hxx" +#include "basegfx/polygon/b2dpolypolygon.hxx" +#include "basegfx/matrix/b2dhommatrix.hxx" +#include "tools/poly.hxx" +#include "vcl/outdev.h" +#include "vcl/virdev.hxx" +#include "vcl/print.hxx" +#include "vcl/event.hxx" +#include "vcl/window.h" +#include "vcl/window.hxx" +#include "vcl/svapp.hxx" +#include "vcl/bmpacc.hxx" +#include "unotools/fontcvt.hxx" +#include "vcl/outdev.hxx" +#include "vcl/edit.hxx" +#include "unotools/fontcfg.hxx" +#include "vcl/sysdata.hxx" +#include "vcl/textlayout.hxx" +#include "vcl/svids.hrc" +#include "osl/file.h" +#ifdef ENABLE_GRAPHITE +#include "vcl/graphite_features.hxx" +#endif +#ifdef USE_BUILTIN_RASTERIZER +#include "vcl/glyphcache.hxx" +#endif + +#include "vcl/unohelp.hxx" +#include "pdfwriter_impl.hxx" +#include "vcl/controllayout.hxx" +#include "rtl/logfile.hxx" + +#include "com/sun/star/beans/PropertyValues.hpp" +#include "com/sun/star/i18n/XBreakIterator.hpp" +#include "com/sun/star/i18n/WordType.hpp" +#include "com/sun/star/linguistic2/XLinguServiceManager.hpp" + +#if defined UNX +#define GLYPH_FONT_HEIGHT 128 +#elif defined OS2 +#define GLYPH_FONT_HEIGHT 176 +#else +#define GLYPH_FONT_HEIGHT 256 +#endif + +#include "sal/alloca.h" + +#include <cmath> +#include <cstring> + +#include <memory> +#include <algorithm> + + +// ======================================================================= + +DBG_NAMEEX( OutputDevice ) +DBG_NAMEEX( Font ) + +// ======================================================================= + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::rtl; +using namespace ::vcl; +using namespace ::utl; + +// ======================================================================= + +#define TEXT_DRAW_ELLIPSIS (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) + +// ======================================================================= + +#define UNDERLINE_LAST UNDERLINE_BOLDWAVE +#define STRIKEOUT_LAST STRIKEOUT_X + +// ======================================================================= + +static void ImplRotatePos( long nOriginX, long nOriginY, long& rX, long& rY, + int nOrientation ) +{ + if ( (nOrientation >= 0) && !(nOrientation % 900) ) + { + if ( (nOrientation >= 3600) ) + nOrientation %= 3600; + + if ( nOrientation ) + { + rX -= nOriginX; + rY -= nOriginY; + + if ( nOrientation == 900 ) + { + long nTemp = rX; + rX = rY; + rY = -nTemp; + } + else if ( nOrientation == 1800 ) + { + rX = -rX; + rY = -rY; + } + else /* ( nOrientation == 2700 ) */ + { + long nTemp = rX; + rX = -rY; + rY = nTemp; + } + + rX += nOriginX; + rY += nOriginY; + } + } + else + { + double nRealOrientation = nOrientation*F_PI1800; + double nCos = cos( nRealOrientation ); + double nSin = sin( nRealOrientation ); + + // Translation... + long nX = rX-nOriginX; + long nY = rY-nOriginY; + + // Rotation... + rX = +((long)(nCos*nX + nSin*nY)) + nOriginX; + rY = -((long)(nSin*nX - nCos*nY)) + nOriginY; + } +} + +// ======================================================================= + +void OutputDevice::ImplUpdateFontData( bool bNewFontLists ) +{ + // the currently selected logical font is no longer needed + if ( mpFontEntry ) + { + mpFontCache->Release( mpFontEntry ); + mpFontEntry = NULL; + } + + mbInitFont = true; + mbNewFont = true; + + if ( bNewFontLists ) + { + if ( mpGetDevFontList ) + { + delete mpGetDevFontList; + mpGetDevFontList = NULL; + } + if ( mpGetDevSizeList ) + { + delete mpGetDevSizeList; + mpGetDevSizeList = NULL; + } + + // release all physically selected fonts on this device + if( ImplGetGraphics() ) + mpGraphics->ReleaseFonts(); + } + + if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter ) + { + ImplSVData* pSVData = ImplGetSVData(); + + if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache ) + mpFontCache->Invalidate(); + + if ( bNewFontLists ) + { + // we need a graphics + if ( ImplGetGraphics() ) + { + if( mpFontList && mpFontList != pSVData->maGDIData.mpScreenFontList ) + mpFontList->Clear(); + + if( mpPDFWriter ) + { + if( mpFontList && mpFontList != pSVData->maGDIData.mpScreenFontList ) + delete mpFontList; + if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache ) + delete mpFontCache; + mpFontList = mpPDFWriter->filterDevFontList( pSVData->maGDIData.mpScreenFontList ); + mpFontCache = new ImplFontCache( sal_False ); + } + else + { + if( mpOutDevData ) + mpOutDevData->maDevFontSubst.Clear(); + mpGraphics->GetDevFontList( mpFontList ); + mpGraphics->GetDevFontSubstList( this ); + } + } + } + } + + // also update child windows if needed + if ( GetOutDevType() == OUTDEV_WINDOW ) + { + Window* pChild = ((Window*)this)->mpWindowImpl->mpFirstChild; + while ( pChild ) + { + pChild->ImplUpdateFontData( true ); + pChild = pChild->mpWindowImpl->mpNext; + } + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplUpdateAllFontData( bool bNewFontLists ) +{ + ImplSVData* pSVData = ImplGetSVData(); + + // update all windows + Window* pFrame = pSVData->maWinData.mpFirstFrame; + while ( pFrame ) + { + pFrame->ImplUpdateFontData( bNewFontLists ); + + Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap; + while ( pSysWin ) + { + pSysWin->ImplUpdateFontData( bNewFontLists ); + pSysWin = pSysWin->mpWindowImpl->mpNextOverlap; + } + + pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame; + } + + // update all virtual devices + VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev; + while ( pVirDev ) + { + pVirDev->ImplUpdateFontData( bNewFontLists ); + pVirDev = pVirDev->mpNext; + } + + // update all printers + Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter; + while ( pPrinter ) + { + pPrinter->ImplUpdateFontData( bNewFontLists ); + pPrinter = pPrinter->mpNext; + } + + // clear global font lists to have them updated + pSVData->maGDIData.mpScreenFontCache->Invalidate(); + if ( bNewFontLists ) + { + pSVData->maGDIData.mpScreenFontList->Clear(); + pFrame = pSVData->maWinData.mpFirstFrame; + if ( pFrame ) + { + if ( pFrame->ImplGetGraphics() ) + // MT: Stupid typecast here and somewhere ((OutputDevice*)&aVDev)->, because bug in .NET2002 compiler. + ((OutputDevice*)pFrame)->mpGraphics->GetDevFontList( pFrame->mpWindowImpl->mpFrameData->mpFontList ); + } + } +} + +// ======================================================================= + + +// ======================================================================= + +// TODO: remove this method when the CWS-gfbfcfg dust has settled +void ImplFreeOutDevFontData() +{} + +// ======================================================================= + +void OutputDevice::BeginFontSubstitution() +{ + ImplSVData* pSVData = ImplGetSVData(); + pSVData->maGDIData.mbFontSubChanged = sal_False; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::EndFontSubstitution() +{ + ImplSVData* pSVData = ImplGetSVData(); + if ( pSVData->maGDIData.mbFontSubChanged ) + { + ImplUpdateAllFontData( false ); + + Application* pApp = GetpApp(); + DataChangedEvent aDCEvt( DATACHANGED_FONTSUBSTITUTION ); + pApp->DataChanged( aDCEvt ); + pApp->NotifyAllWindows( aDCEvt ); + pSVData->maGDIData.mbFontSubChanged = sal_False; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::AddFontSubstitute( const XubString& rFontName, + const XubString& rReplaceFontName, + sal_uInt16 nFlags ) +{ + ImplDirectFontSubstitution*& rpSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; + if( !rpSubst ) + rpSubst = new ImplDirectFontSubstitution(); + rpSubst->AddFontSubstitute( rFontName, rReplaceFontName, nFlags ); + ImplGetSVData()->maGDIData.mbFontSubChanged = sal_True; +} + +// ----------------------------------------------------------------------- + +void ImplDirectFontSubstitution::AddFontSubstitute( const String& rFontName, + const String& rSubstFontName, sal_uInt16 nFlags ) +{ + maFontSubstList.push_back( ImplFontSubstEntry( rFontName, rSubstFontName, nFlags ) ); +} + +// ----------------------------------------------------------------------- + +ImplFontSubstEntry::ImplFontSubstEntry( const String& rFontName, + const String& rSubstFontName, sal_uInt16 nSubstFlags ) +: maName( rFontName ) +, maReplaceName( rSubstFontName ) +, mnFlags( nSubstFlags ) +{ + maSearchName = rFontName; + maSearchReplaceName = rSubstFontName; + GetEnglishSearchFontName( maSearchName ); + GetEnglishSearchFontName( maSearchReplaceName ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplAddDevFontSubstitute( const XubString& rFontName, + const XubString& rReplaceFontName, + sal_uInt16 nFlags ) +{ + ImplInitOutDevData(); + mpOutDevData->maDevFontSubst.AddFontSubstitute( rFontName, rReplaceFontName, nFlags ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::RemoveFontSubstitute( sal_uInt16 n ) +{ + ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; + if( pSubst ) + pSubst->RemoveFontSubstitute( n ); +} + +// ----------------------------------------------------------------------- + +void ImplDirectFontSubstitution::RemoveFontSubstitute( int nIndex ) +{ + FontSubstList::iterator it = maFontSubstList.begin(); + for( int nCount = 0; (it != maFontSubstList.end()) && (nCount++ != nIndex); ++it ) ; + if( it != maFontSubstList.end() ) + maFontSubstList.erase( it ); +} + +// ----------------------------------------------------------------------- + +sal_uInt16 OutputDevice::GetFontSubstituteCount() +{ + const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; + if( !pSubst ) + return 0; + int nCount = pSubst->GetFontSubstituteCount(); + return (sal_uInt16)nCount; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::GetFontSubstitute( sal_uInt16 n, + XubString& rFontName, + XubString& rReplaceFontName, + sal_uInt16& rFlags ) +{ + const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; + if( pSubst ) + pSubst->GetFontSubstitute( n, rFontName, rReplaceFontName, rFlags ); +} + +// ----------------------------------------------------------------------- + +bool ImplDirectFontSubstitution::GetFontSubstitute( int nIndex, + String& rFontName, String& rSubstFontName, sal_uInt16& rFlags ) const +{ + FontSubstList::const_iterator it = maFontSubstList.begin(); + for( int nCount = 0; (it != maFontSubstList.end()) && (nCount++ != nIndex); ++it ) ; + if( it == maFontSubstList.end() ) + return false; + + const ImplFontSubstEntry* pEntry = &(*it); + rFontName = pEntry->maName; + rSubstFontName = pEntry->maReplaceName; + rFlags = pEntry->mnFlags; + return true; +} + +// ----------------------------------------------------------------------- + +bool ImplDirectFontSubstitution::FindFontSubstitute( String& rSubstName, + const String& rSearchName, sal_uInt16 nFlags ) const +{ + // TODO: get rid of O(N) searches + FontSubstList::const_iterator it = maFontSubstList.begin(); + for(; it != maFontSubstList.end(); ++it ) + { + const ImplFontSubstEntry& rEntry = *it; + if( ((rEntry.mnFlags & nFlags) || !nFlags) + && (rEntry.maSearchName == rSearchName) ) + { + rSubstName = rEntry.maSearchReplaceName; + return true; + } + } + + return false; +} + +// ----------------------------------------------------------------------- + +static void ImplFontSubstitute( String& rFontName, + sal_uInt16 nFlags, ImplDirectFontSubstitution* pDevSpecific ) +{ +#ifdef DBG_UTIL + String aTempName = rFontName; + GetEnglishSearchFontName( aTempName ); + DBG_ASSERT( aTempName == rFontName, "ImplFontSubstitute() called without a searchname" ); +#endif + + String aSubstFontName; + + // apply user-configurable font replacement (eg, from the list in Tools->Options) + const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; + if( pSubst && pSubst->FindFontSubstitute( aSubstFontName, rFontName, FONT_SUBSTITUTE_ALWAYS ) ) + { + rFontName = aSubstFontName; + return; + } + + // apply device specific font replacement (e.g. to use printer builtin fonts) + if( !pDevSpecific ) + return; + + if( pDevSpecific->FindFontSubstitute( aSubstFontName, rFontName, nFlags ) ) + { + rFontName = aSubstFontName; + return; + } +} + +// ----------------------------------------------------------------------- + +Font OutputDevice::GetDefaultFont( sal_uInt16 nType, LanguageType eLang, + sal_uLong nFlags, const OutputDevice* pOutDev ) +{ + OSL_TRACE( "OutputDevice::GetDefaultFont()" ); + + com::sun::star::lang::Locale aLocale; + if( eLang == LANGUAGE_NONE || eLang == LANGUAGE_SYSTEM || eLang == LANGUAGE_DONTKNOW ) + { + aLocale = Application::GetSettings().GetUILocale(); + } + else + { + MsLangId::convertLanguageToLocale( eLang, aLocale ); + } + + utl::DefaultFontConfiguration& rDefaults = *utl::DefaultFontConfiguration::get(); + String aSearch = rDefaults.getUserInterfaceFont( aLocale ); // ensure a fallback + String aDefault = rDefaults.getDefaultFont( aLocale, nType ); + if( aDefault.Len() ) + aSearch = aDefault; + + int nDefaultHeight = 12; + + Font aFont; + aFont.SetPitch( PITCH_VARIABLE ); + + switch ( nType ) + { + case DEFAULTFONT_SANS_UNICODE: + case DEFAULTFONT_UI_SANS: + aFont.SetFamily( FAMILY_SWISS ); + break; + + case DEFAULTFONT_SANS: + case DEFAULTFONT_LATIN_HEADING: + case DEFAULTFONT_LATIN_SPREADSHEET: + case DEFAULTFONT_LATIN_DISPLAY: + aFont.SetFamily( FAMILY_SWISS ); + break; + + case DEFAULTFONT_SERIF: + case DEFAULTFONT_LATIN_TEXT: + case DEFAULTFONT_LATIN_PRESENTATION: + aFont.SetFamily( FAMILY_ROMAN ); + break; + + case DEFAULTFONT_FIXED: + case DEFAULTFONT_LATIN_FIXED: + case DEFAULTFONT_UI_FIXED: + aFont.SetPitch( PITCH_FIXED ); + aFont.SetFamily( FAMILY_MODERN ); + break; + + case DEFAULTFONT_SYMBOL: + aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL ); + break; + + case DEFAULTFONT_CJK_TEXT: + case DEFAULTFONT_CJK_PRESENTATION: + case DEFAULTFONT_CJK_SPREADSHEET: + case DEFAULTFONT_CJK_HEADING: + case DEFAULTFONT_CJK_DISPLAY: + aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later... + break; + + case DEFAULTFONT_CTL_TEXT: + case DEFAULTFONT_CTL_PRESENTATION: + case DEFAULTFONT_CTL_SPREADSHEET: + case DEFAULTFONT_CTL_HEADING: + case DEFAULTFONT_CTL_DISPLAY: + aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later... + break; + } + + if ( aSearch.Len() ) + { + aFont.SetHeight( nDefaultHeight ); + aFont.SetWeight( WEIGHT_NORMAL ); + aFont.SetLanguage( eLang ); + + if ( aFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW ) + aFont.SetCharSet( gsl_getSystemTextEncoding() ); + + // Should we only return available fonts on the given device + if ( pOutDev ) + { + pOutDev->ImplInitFontList(); + + // Search Font in the FontList + String aName; + String aSearchName; + xub_StrLen nIndex = 0; + do + { + aSearchName = GetNextFontToken( aSearch, nIndex ); + GetEnglishSearchFontName( aSearchName ); + ImplDevFontListData* pFontFamily = pOutDev->mpFontList->ImplFindBySearchName( aSearchName ); + if( pFontFamily ) + { + AddTokenFontName( aName, pFontFamily->GetFamilyName() ); + if( nFlags & DEFAULTFONT_FLAGS_ONLYONE ) + break; + } + } + while ( nIndex != STRING_NOTFOUND ); + aFont.SetName( aName ); + } + + // No Name, than set all names + if ( !aFont.GetName().Len() ) + { + if ( nFlags & DEFAULTFONT_FLAGS_ONLYONE ) + { + xub_StrLen nIndex = 0; + + if( !pOutDev ) + pOutDev = (const OutputDevice *)ImplGetSVData()->mpDefaultWin; + if( !pOutDev ) + aFont.SetName( aSearch.GetToken( 0, ';', nIndex ) ); + else + { + pOutDev->ImplInitFontList(); + + aFont.SetName( aSearch ); + + // convert to pixel height + Size aSize = pOutDev->ImplLogicToDevicePixel( aFont.GetSize() ); + if ( !aSize.Height() ) + { + // use default pixel height only when logical height is zero + if ( aFont.GetHeight() ) + aSize.Height() = 1; + else + aSize.Height() = (12*pOutDev->mnDPIY)/72; + } + + // use default width only when logical width is zero + if( (0 == aSize.Width()) && (0 != aFont.GetSize().Width()) ) + aSize.Width() = 1; + + // get the name of the first available font + float fExactHeight = static_cast<float>(aSize.Height()); + ImplFontEntry* pEntry = pOutDev->mpFontCache->GetFontEntry( pOutDev->mpFontList, aFont, aSize, fExactHeight, pOutDev->mpOutDevData ? &pOutDev->mpOutDevData->maDevFontSubst : NULL ); + if( pEntry->maFontSelData.mpFontData ) + aFont.SetName( pEntry->maFontSelData.mpFontData->maName ); + else + aFont.SetName( pEntry->maFontSelData.maTargetName ); + } + } + else + aFont.SetName( aSearch ); + } + } + +#if OSL_DEBUG_LEVEL > 2 + const char* s = "DEFAULTFONT_SANS_UNKNOWN"; + switch ( nType ) + { + case DEFAULTFONT_SANS_UNICODE: s = "DEFAULTFONT_SANS_UNICODE"; break; + case DEFAULTFONT_UI_SANS: s = "DEFAULTFONT_UI_SANS"; break; + + case DEFAULTFONT_SANS: s = "DEFAULTFONT_SANS"; break; + case DEFAULTFONT_LATIN_HEADING: s = "DEFAULTFONT_LATIN_HEADING"; break; + case DEFAULTFONT_LATIN_SPREADSHEET: s = "DEFAULTFONT_LATIN_SPREADSHEET"; break; + case DEFAULTFONT_LATIN_DISPLAY: s = "DEFAULTFONT_LATIN_DISPLAY"; break; + + case DEFAULTFONT_SERIF: s = "DEFAULTFONT_SERIF"; break; + case DEFAULTFONT_LATIN_TEXT: s = "DEFAULTFONT_LATIN_TEXT"; break; + case DEFAULTFONT_LATIN_PRESENTATION: s = "DEFAULTFONT_LATIN_PRESENTATION"; break; + + case DEFAULTFONT_FIXED: s = "DEFAULTFONT_FIXED"; break; + case DEFAULTFONT_LATIN_FIXED: s = "DEFAULTFONT_LATIN_FIXED"; break; + case DEFAULTFONT_UI_FIXED: s = "DEFAULTFONT_UI_FIXED"; break; + + case DEFAULTFONT_SYMBOL: s = "DEFAULTFONT_SYMBOL"; break; + + case DEFAULTFONT_CJK_TEXT: s = "DEFAULTFONT_CJK_TEXT"; break; + case DEFAULTFONT_CJK_PRESENTATION: s = "DEFAULTFONT_CJK_PRESENTATION"; break; + case DEFAULTFONT_CJK_SPREADSHEET: s = "DEFAULTFONT_CJK_SPREADSHEET"; break; + case DEFAULTFONT_CJK_HEADING: s = "DEFAULTFONT_CJK_HEADING"; break; + case DEFAULTFONT_CJK_DISPLAY: s = "DEFAULTFONT_CJK_DISPLAY"; break; + + case DEFAULTFONT_CTL_TEXT: s = "DEFAULTFONT_CTL_TEXT"; break; + case DEFAULTFONT_CTL_PRESENTATION: s = "DEFAULTFONT_CTL_PRESENTATION"; break; + case DEFAULTFONT_CTL_SPREADSHEET: s = "DEFAULTFONT_CTL_SPREADSHEET"; break; + case DEFAULTFONT_CTL_HEADING: s = "DEFAULTFONT_CTL_HEADING"; break; + case DEFAULTFONT_CTL_DISPLAY: s = "DEFAULTFONT_CTL_DISPLAY"; break; + } + fprintf( stderr, " OutputDevice::GetDefaultFont() Type=\"%s\" lang=%d flags=%ld FontName=\"%s\"\n", + s, eLang, nFlags, + OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr() + ); +#endif + + return aFont; +} + +// ======================================================================= + +static unsigned ImplIsCJKFont( const String& rFontName ) +{ + // Test, if Fontname includes CJK characters --> In this case we + // mention that it is a CJK font + const sal_Unicode* pStr = rFontName.GetBuffer(); + while ( *pStr ) + { + // japanese + if ( ((*pStr >= 0x3040) && (*pStr <= 0x30FF)) || + ((*pStr >= 0x3190) && (*pStr <= 0x319F)) ) + return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_JP; + + // korean + if ( ((*pStr >= 0xAC00) && (*pStr <= 0xD7AF)) || + ((*pStr >= 0x3130) && (*pStr <= 0x318F)) || + ((*pStr >= 0x1100) && (*pStr <= 0x11FF)) ) + return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_KR; + + // chinese + if ( ((*pStr >= 0x3400) && (*pStr <= 0x9FFF)) ) + return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_TC|IMPL_FONT_ATTR_CJK_SC; + + // cjk + if ( ((*pStr >= 0x3000) && (*pStr <= 0xD7AF)) || + ((*pStr >= 0xFF00) && (*pStr <= 0xFFEE)) ) + return IMPL_FONT_ATTR_CJK; + + pStr++; + } + + return 0; +} + +// ----------------------------------------------------------------------- + +static void ImplCalcType( sal_uLong& rType, FontWeight& rWeight, FontWidth& rWidth, + FontFamily eFamily, const FontNameAttr* pFontAttr ) +{ + if ( eFamily != FAMILY_DONTKNOW ) + { + if ( eFamily == FAMILY_SWISS ) + rType |= IMPL_FONT_ATTR_SANSSERIF; + else if ( eFamily == FAMILY_ROMAN ) + rType |= IMPL_FONT_ATTR_SERIF; + else if ( eFamily == FAMILY_SCRIPT ) + rType |= IMPL_FONT_ATTR_SCRIPT; + else if ( eFamily == FAMILY_MODERN ) + rType |= IMPL_FONT_ATTR_FIXED; + else if ( eFamily == FAMILY_DECORATIVE ) + rType |= IMPL_FONT_ATTR_DECORATIVE; + } + + if ( pFontAttr ) + { + rType |= pFontAttr->Type; + + if ( ((rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL)) && + (pFontAttr->Weight != WEIGHT_DONTKNOW) ) + rWeight = pFontAttr->Weight; + if ( ((rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL)) && + (pFontAttr->Width != WIDTH_DONTKNOW) ) + rWidth = pFontAttr->Width; + } +} + +// ======================================================================= + +ImplFontData::ImplFontData( const ImplDevFontAttributes& rDFA, int nMagic ) +: ImplDevFontAttributes( rDFA ), + mnWidth(0), + mnHeight(0), + mnMagic( nMagic ), + mpNext( NULL ) +{ + // StarSymbol is a unicode font, but it still deserves the symbol flag + if( !mbSymbolFlag ) + if( 0 == GetFamilyName().CompareIgnoreCaseToAscii( "starsymbol", 10) + || 0 == GetFamilyName().CompareIgnoreCaseToAscii( "opensymbol", 10) ) + mbSymbolFlag = true; +} + +// ----------------------------------------------------------------------- + +StringCompare ImplFontData::CompareIgnoreSize( const ImplFontData& rOther ) const +{ + // compare their width, weight, italic and style name + if( meWidthType < rOther.meWidthType ) + return COMPARE_LESS; + else if( meWidthType > rOther.meWidthType ) + return COMPARE_GREATER; + + if( meWeight < rOther.meWeight ) + return COMPARE_LESS; + else if( meWeight > rOther.meWeight ) + return COMPARE_GREATER; + + if( meItalic < rOther.meItalic ) + return COMPARE_LESS; + else if( meItalic > rOther.meItalic ) + return COMPARE_GREATER; + + StringCompare eCompare = maName.CompareTo( rOther.maName ); + return eCompare; +} + +// ----------------------------------------------------------------------- + +StringCompare ImplFontData::CompareWithSize( const ImplFontData& rOther ) const +{ + StringCompare eCompare = CompareIgnoreSize( rOther ); + if( eCompare != COMPARE_EQUAL ) + return eCompare; + + if( mnHeight < rOther.mnHeight ) + return COMPARE_LESS; + else if( mnHeight > rOther.mnHeight ) + return COMPARE_GREATER; + + if( mnWidth < rOther.mnWidth ) + return COMPARE_LESS; + else if( mnWidth > rOther.mnWidth ) + return COMPARE_GREATER; + + return COMPARE_EQUAL; +} + +// ----------------------------------------------------------------------- + +struct FontMatchStatus +{ +public: + int mnFaceMatch; + int mnHeightMatch; + int mnWidthMatch; + const xub_Unicode* mpTargetStyleName; +}; + +bool ImplFontData::IsBetterMatch( const ImplFontSelectData& rFSD, FontMatchStatus& rStatus ) const +{ + int nMatch = 0; + + const String& rFontName = rFSD.maTargetName; + if( (rFontName == maName) || rFontName.EqualsIgnoreCaseAscii( maName ) ) + nMatch += 240000; + + if( rStatus.mpTargetStyleName + && maStyleName.EqualsIgnoreCaseAscii( rStatus.mpTargetStyleName ) ) + nMatch += 120000; + + if( (rFSD.mePitch != PITCH_DONTKNOW) && (rFSD.mePitch == mePitch) ) + nMatch += 20000; + + // prefer NORMAL font width + // TODO: change when the upper layers can tell their width preference + if( meWidthType == WIDTH_NORMAL ) + nMatch += 400; + else if( (meWidthType == WIDTH_SEMI_EXPANDED) || (meWidthType == WIDTH_SEMI_CONDENSED) ) + nMatch += 300; + + if( rFSD.meWeight != WEIGHT_DONTKNOW ) + { + // if not bold prefer light fonts to bold fonts + int nReqWeight = (int)rFSD.meWeight; + if ( rFSD.meWeight > WEIGHT_MEDIUM ) + nReqWeight += 100; + + int nGivenWeight = (int)meWeight; + if( meWeight > WEIGHT_MEDIUM ) + nGivenWeight += 100; + + int nWeightDiff = nReqWeight - nGivenWeight; + + if ( nWeightDiff == 0 ) + nMatch += 1000; + else if ( nWeightDiff == +1 || nWeightDiff == -1 ) + nMatch += 700; + else if ( nWeightDiff < +50 && nWeightDiff > -50) + nMatch += 200; + } + else // requested weight == WEIGHT_DONTKNOW + { + // prefer NORMAL font weight + // TODO: change when the upper layers can tell their weight preference + if( meWeight == WEIGHT_NORMAL ) + nMatch += 450; + else if( meWeight == WEIGHT_MEDIUM ) + nMatch += 350; + else if( (meWeight == WEIGHT_SEMILIGHT) || (meWeight == WEIGHT_SEMIBOLD) ) + nMatch += 200; + else if( meWeight == WEIGHT_LIGHT ) + nMatch += 150; + } + + if ( rFSD.meItalic == ITALIC_NONE ) + { + if( meItalic == ITALIC_NONE ) + nMatch += 900; + } + else + { + if( rFSD.meItalic == meItalic ) + nMatch += 900; + else if( meItalic != ITALIC_NONE ) + nMatch += 600; + } + + if( mbDevice ) + nMatch += 1; + + int nHeightMatch = 0; + int nWidthMatch = 0; + + if( IsScalable() ) + { + if( rFSD.mnOrientation != 0 ) + nMatch += 80; + else if( rFSD.mnWidth != 0 ) + nMatch += 25; + else + nMatch += 5; + } + else + { + if( rFSD.mnHeight == mnHeight ) + { + nMatch += 20; + if( rFSD.mnWidth == mnWidth ) + nMatch += 10; + } + else + { + // for non-scalable fonts the size difference is very important + // prefer the smaller font face because of clipping/overlapping issues + int nHeightDiff = (rFSD.mnHeight - mnHeight) * 1000; + nHeightMatch = (nHeightDiff >= 0) ? -nHeightDiff : 100+nHeightDiff; + if( rFSD.mnHeight ) + nHeightMatch /= rFSD.mnHeight; + + if( (rFSD.mnWidth != 0) && (mnWidth != 0) && (rFSD.mnWidth != mnWidth) ) + { + int nWidthDiff = (rFSD.mnWidth - mnWidth) * 100; + nWidthMatch = (nWidthDiff >= 0) ? -nWidthDiff : +nWidthDiff; + } + } + } + + if( rStatus.mnFaceMatch > nMatch ) + return false; + else if( rStatus.mnFaceMatch < nMatch ) + { + rStatus.mnFaceMatch = nMatch; + rStatus.mnHeightMatch = nHeightMatch; + rStatus.mnWidthMatch = nWidthMatch; + return true; + } + + // when two fonts are still competing prefer the + // one with the best matching height + if( rStatus.mnHeightMatch > nHeightMatch ) + return false; + else if( rStatus.mnHeightMatch < nHeightMatch ) + { + rStatus.mnHeightMatch = nHeightMatch; + rStatus.mnWidthMatch = nWidthMatch; + return true; + } + + if( rStatus.mnWidthMatch > nWidthMatch ) + return false; + + rStatus.mnWidthMatch = nWidthMatch; + return true; +} + +// ======================================================================= + +ImplFontEntry::ImplFontEntry( const ImplFontSelectData& rFontSelData ) +: maFontSelData( rFontSelData ), + maMetric( rFontSelData ), + mpConversion( NULL ), + mnRefCount( 1 ), + mnSetFontFlags( 0 ), + mnOwnOrientation( 0 ), + mnOrientation( 0 ), + mbInit( false ), + mpUnicodeFallbackList( NULL ) +{ + maFontSelData.mpFontEntry = this; +} + +// ----------------------------------------------------------------------- + +ImplFontEntry::~ImplFontEntry() +{ + delete mpUnicodeFallbackList; +} + +// ----------------------------------------------------------------------- + +size_t ImplFontEntry::GFBCacheKey_Hash::operator()( const GFBCacheKey& rData ) const +{ + boost::hash<sal_UCS4> a; + boost::hash<int > b; + return a(rData.first) ^ b(rData.second); +} + +inline void ImplFontEntry::AddFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, const String& rFontName ) +{ + if( !mpUnicodeFallbackList ) + mpUnicodeFallbackList = new UnicodeFallbackList; + (*mpUnicodeFallbackList)[ GFBCacheKey(cChar,eWeight) ] = rFontName; +} + +// ----------------------------------------------------------------------- + +inline bool ImplFontEntry::GetFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, String* pFontName ) const +{ + if( !mpUnicodeFallbackList ) + return false; + + UnicodeFallbackList::const_iterator it = mpUnicodeFallbackList->find( GFBCacheKey(cChar,eWeight) ); + if( it == mpUnicodeFallbackList->end() ) + return false; + + *pFontName = (*it).second; + return true; +} + +// ----------------------------------------------------------------------- + +inline void ImplFontEntry::IgnoreFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, const String& rFontName ) +{ +// DBG_ASSERT( mpUnicodeFallbackList, "ImplFontEntry::IgnoreFallbackForUnicode no list" ); + UnicodeFallbackList::iterator it = mpUnicodeFallbackList->find( GFBCacheKey(cChar,eWeight) ); +// DBG_ASSERT( it != mpUnicodeFallbackList->end(), "ImplFontEntry::IgnoreFallbackForUnicode no match" ); + if( it == mpUnicodeFallbackList->end() ) + return; + if( (*it).second == rFontName ) + mpUnicodeFallbackList->erase( it ); +} + +// ======================================================================= + +ImplDevFontListData::ImplDevFontListData( const String& rSearchName ) +: mpFirst( NULL ), + maSearchName( rSearchName ), + mnTypeFaces( 0 ), + mnMatchType( 0 ), + meMatchWeight( WEIGHT_DONTKNOW ), + meMatchWidth( WIDTH_DONTKNOW ), + meFamily( FAMILY_DONTKNOW ), + mePitch( PITCH_DONTKNOW ), + mnMinQuality( -1 ) +{} + +// ----------------------------------------------------------------------- + +ImplDevFontListData::~ImplDevFontListData() +{ + // release all physical font faces + while( mpFirst ) + { + ImplFontData* pFace = mpFirst; + mpFirst = pFace->GetNextFace(); + delete pFace; + } +} + +// ----------------------------------------------------------------------- + +bool ImplDevFontListData::AddFontFace( ImplFontData* pNewData ) +{ + pNewData->mpNext = NULL; + + if( !mpFirst ) + { + maName = pNewData->maName; + maMapNames = pNewData->maMapNames; + meFamily = pNewData->meFamily; + mePitch = pNewData->mePitch; + mnMinQuality = pNewData->mnQuality; + } + else + { + if( meFamily == FAMILY_DONTKNOW ) + meFamily = pNewData->meFamily; + if( mePitch == PITCH_DONTKNOW ) + mePitch = pNewData->mePitch; + if( mnMinQuality > pNewData->mnQuality ) + mnMinQuality = pNewData->mnQuality; + } + + // set attributes for attribute based font matching + if( pNewData->IsScalable() ) + mnTypeFaces |= IMPL_DEVFONT_SCALABLE; + + if( pNewData->IsSymbolFont() ) + mnTypeFaces |= IMPL_DEVFONT_SYMBOL; + else + mnTypeFaces |= IMPL_DEVFONT_NONESYMBOL; + + if( pNewData->meWeight != WEIGHT_DONTKNOW ) + { + if( pNewData->meWeight >= WEIGHT_SEMIBOLD ) + mnTypeFaces |= IMPL_DEVFONT_BOLD; + else if( pNewData->meWeight <= WEIGHT_SEMILIGHT ) + mnTypeFaces |= IMPL_DEVFONT_LIGHT; + else + mnTypeFaces |= IMPL_DEVFONT_NORMAL; + } + + if( pNewData->meItalic == ITALIC_NONE ) + mnTypeFaces |= IMPL_DEVFONT_NONEITALIC; + else if( (pNewData->meItalic == ITALIC_NORMAL) + || (pNewData->meItalic == ITALIC_OBLIQUE) ) + mnTypeFaces |= IMPL_DEVFONT_ITALIC; + + if( (meMatchWeight == WEIGHT_DONTKNOW) + || (meMatchWidth == WIDTH_DONTKNOW) + || (mnMatchType == 0) ) + { + // TODO: is it cheaper to calc matching attributes now or on demand? + // calc matching attributes if other entries are already initialized + + // MT: Perform05: Do lazy, quite expensive, not needed in start-up! + // const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get(); + // InitMatchData( rFontSubst, maSearchName ); + // mbMatchData=true; // Somewhere else??? + } + + // reassign name (sharing saves memory) + if( pNewData->maName == maName ) + pNewData->maName = maName; + + // insert new physical font face into linked list + // TODO: get rid of linear search? + ImplFontData* pData; + ImplFontData** ppHere = &mpFirst; + for(; (pData=*ppHere) != NULL; ppHere=&pData->mpNext ) + { + StringCompare eComp = pNewData->CompareWithSize( *pData ); + if( eComp == COMPARE_GREATER ) + continue; + if( eComp == COMPARE_LESS ) + break; + + // ignore duplicate if its quality is worse + if( pNewData->mnQuality < pData->mnQuality ) + return false; + + // keep the device font if its quality is good enough + if( (pNewData->mnQuality == pData->mnQuality) + && (pData->mbDevice || !pNewData->mbDevice) ) + return false; + + // replace existing font face with a better one + pNewData->mpNext = pData->mpNext; + *ppHere = pNewData; + delete pData; + return true; + } + + // insert into or append to list of physical font faces + pNewData->mpNext = pData; + *ppHere = pNewData; + return true; +} + +// ----------------------------------------------------------------------- + +// get font attributes using the normalized font family name +void ImplDevFontListData::InitMatchData( const utl::FontSubstConfiguration& rFontSubst, + const String& rSearchName ) +{ + String aShortName; + // get font attributes from the decorated font name + rFontSubst.getMapName( rSearchName, aShortName, maMatchFamilyName, + meMatchWeight, meMatchWidth, mnMatchType ); + const FontNameAttr* pFontAttr = rFontSubst.getSubstInfo( rSearchName ); + // eventually use the stripped name + if( !pFontAttr ) + if( aShortName != rSearchName ) + pFontAttr = rFontSubst.getSubstInfo( aShortName ); + ImplCalcType( mnMatchType, meMatchWeight, meMatchWidth, meFamily, pFontAttr ); + mnMatchType |= ImplIsCJKFont( maName ); +} + +// ----------------------------------------------------------------------- + +ImplFontData* ImplDevFontListData::FindBestFontFace( const ImplFontSelectData& rFSD ) const +{ + if( !mpFirst ) + return NULL; + if( !mpFirst->GetNextFace() ) + return mpFirst; + + // FontName+StyleName should map to FamilyName+StyleName + const String& rSearchName = rFSD.maTargetName; + const xub_Unicode* pTargetStyleName = NULL; + if( (rSearchName.Len() > maSearchName.Len()) + && rSearchName.Equals( maSearchName, 0, maSearchName.Len() ) ) + pTargetStyleName = rSearchName.GetBuffer() + maSearchName.Len() + 1; + + // linear search, TODO: improve? + ImplFontData* pFontFace = mpFirst; + ImplFontData* pBestFontFace = pFontFace; + FontMatchStatus aFontMatchStatus = {0,0,0, pTargetStyleName}; + for(; pFontFace; pFontFace = pFontFace->GetNextFace() ) + if( pFontFace->IsBetterMatch( rFSD, aFontMatchStatus ) ) + pBestFontFace = pFontFace; + + return pBestFontFace; +} + +// ----------------------------------------------------------------------- + +// update device font list with unique font faces, with uniqueness +// meaning different font attributes, but not different fonts sizes +void ImplDevFontListData::UpdateDevFontList( ImplGetDevFontList& rDevFontList ) const +{ + ImplFontData* pPrevFace = NULL; + for( ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() ) + { + if( !pPrevFace || pFace->CompareIgnoreSize( *pPrevFace ) ) + rDevFontList.Add( pFace ); + pPrevFace = pFace; + } +} + +// ----------------------------------------------------------------------- + +void ImplDevFontListData::GetFontHeights( std::set<int>& rHeights ) const +{ + // add all available font heights + for( const ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() ) + rHeights.insert( pFace->GetHeight() ); +} + +// ----------------------------------------------------------------------- + +void ImplDevFontListData::UpdateCloneFontList( ImplDevFontList& rDevFontList, + bool bScalable, bool bEmbeddable ) const +{ + for( ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() ) + { + if( bScalable && !pFace->IsScalable() ) + continue; + if( bEmbeddable && !pFace->IsEmbeddable() && !pFace->IsSubsettable() ) + continue; + + ImplFontData* pClonedFace = pFace->Clone(); + rDevFontList.Add( pClonedFace ); + } +} + +// ======================================================================= + +ImplDevFontList::ImplDevFontList() +: mbMatchData( false ) +, mbMapNames( false ) +, mpPreMatchHook( NULL ) +, mpFallbackHook( NULL ) +, mpFallbackList( NULL ) +, mnFallbackCount( -1 ) +{} + +// ----------------------------------------------------------------------- + +ImplDevFontList::~ImplDevFontList() +{ + Clear(); +} + +// ----------------------------------------------------------------------- + +void ImplDevFontList::SetPreMatchHook( ImplPreMatchFontSubstitution* pHook ) +{ + mpPreMatchHook = pHook; +} + +// ----------------------------------------------------------------------- + +void ImplDevFontList::SetFallbackHook( ImplGlyphFallbackFontSubstitution* pHook ) +{ + mpFallbackHook = pHook; +} + +// ----------------------------------------------------------------------- + +void ImplDevFontList::Clear() +{ + // remove fallback lists + delete[] mpFallbackList; + mpFallbackList = NULL; + mnFallbackCount = -1; + + // clear all entries in the device font list + DevFontList::iterator it = maDevFontList.begin(); + for(; it != maDevFontList.end(); ++it ) + { + ImplDevFontListData* pEntry = (*it).second; + delete pEntry; + } + + maDevFontList.clear(); + + // match data must be recalculated too + mbMatchData = false; +} + + +// ----------------------------------------------------------------------- + +void ImplDevFontList::InitGenericGlyphFallback( void ) const +{ + // normalized family names of fonts suited for glyph fallback + // if a font is available related fonts can be ignored + // TODO: implement dynamic lists + static const char* aGlyphFallbackList[] = { + // empty strings separate the names of unrelated fonts + "eudc", "", + "arialunicodems", "cyberbit", "code2000", "", + "andalesansui", "", + "starsymbol", "opensymbol", "", + "msmincho", "fzmingti", "fzheiti", "ipamincho", "sazanamimincho", "kochimincho", "", + "sunbatang", "sundotum", "baekmukdotum", "gulim", "batang", "dotum", "", + "hgmincholightj", "msunglightsc", "msunglighttc", "hymyeongjolightk", "", + "tahoma", "dejavusans", "timesnewroman", "liberationsans", "", + "shree", "mangal", "", + "raavi", "shruti", "tunga", "", + "latha", "gautami", "kartika", "vrinda", "", + "shayyalmt", "naskmt", "scheherazade", "", + "david", "nachlieli", "lucidagrande", "", + "norasi", "angsanaupc", "", + "khmerossystem", "", + "muktinarrow", "", + "phetsarathot", "", + "padauk", "pinlonmyanmar", "", + "iskoolapota", "lklug", "", + 0 + }; + + bool bHasEudc = false; + int nMaxLevel = 0; + int nBestQuality = 0; + ImplDevFontListData** pFallbackList = NULL; + for( const char** ppNames = &aGlyphFallbackList[0];; ++ppNames ) + { + // advance to next sub-list when end-of-sublist marker + if( !**ppNames ) // #i46456# check for empty string, i.e., deref string itself not only ptr to it + { + if( nBestQuality > 0 ) + if( ++nMaxLevel >= MAX_FALLBACK ) + break; + if( !ppNames[1] ) + break; + nBestQuality = 0; + continue; + } + + // test if the glyph fallback candidate font is available and scalable + String aTokenName( *ppNames, RTL_TEXTENCODING_UTF8 ); + ImplDevFontListData* pFallbackFont = FindFontFamily( aTokenName ); + if( !pFallbackFont ) + continue; + if( !pFallbackFont->IsScalable() ) + continue; + + // keep the best font of the glyph fallback sub-list + if( nBestQuality < pFallbackFont->GetMinQuality() ) + { + nBestQuality = pFallbackFont->GetMinQuality(); + // store available glyph fallback fonts + if( !pFallbackList ) + pFallbackList = new ImplDevFontListData*[ MAX_FALLBACK ]; + pFallbackList[ nMaxLevel ] = pFallbackFont; + if( !bHasEudc && !nMaxLevel ) + bHasEudc = !strncmp( *ppNames, "eudc", 5 ); + } + } + +#ifdef SAL_FONTENUM_STABLE_ON_PLATFORM // #i113472# + // sort the list of fonts for glyph fallback by quality (highest first) + // #i33947# keep the EUDC font at the front of the list + // an insertion sort is good enough for this short list + const int nSortStart = bHasEudc ? 1 : 0; + for( int i = nSortStart+1, j; i < nMaxLevel; ++i ) + { + ImplDevFontListData* pTestFont = pFallbackList[ i ]; + int nTestQuality = pTestFont->GetMinQuality(); + for( j = i; --j >= nSortStart; ) + if( nTestQuality > pFallbackList[j]->GetMinQuality() ) + pFallbackList[ j+1 ] = pFallbackList[ j ]; + else + break; + pFallbackList[ j+1 ] = pTestFont; + } +#endif + + mnFallbackCount = nMaxLevel; + mpFallbackList = pFallbackList; +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::GetGlyphFallbackFont( ImplFontSelectData& rFontSelData, + rtl::OUString& rMissingCodes, int nFallbackLevel ) const +{ + ImplDevFontListData* pFallbackData = NULL; + + // find a matching font candidate for platform specific glyph fallback + if( mpFallbackHook ) + { + // check cache for the first matching entry + // to avoid calling the expensive fallback hook (#i83491#) + sal_UCS4 cChar = 0; + bool bCached = true; + sal_Int32 nStrIndex = 0; + while( nStrIndex < rMissingCodes.getLength() ) + { + cChar = rMissingCodes.iterateCodePoints( &nStrIndex ); + bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName ); + // ignore entries which don't have a fallback + if( !bCached || (rFontSelData.maSearchName.Len() != 0) ) + break; + } + + if( bCached ) + { + // there is a matching fallback in the cache + // so update rMissingCodes with codepoints not yet resolved by this fallback + int nRemainingLength = 0; + sal_UCS4* pRemainingCodes = (sal_UCS4*)alloca( rMissingCodes.getLength() * sizeof(sal_UCS4) ); + String aFontName; + while( nStrIndex < rMissingCodes.getLength() ) + { + cChar = rMissingCodes.iterateCodePoints( &nStrIndex ); + bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &aFontName ); + if( !bCached || (rFontSelData.maSearchName != aFontName) ) + pRemainingCodes[ nRemainingLength++ ] = cChar; + } + rMissingCodes = rtl::OUString( pRemainingCodes, nRemainingLength ); + } + else + { + rtl::OUString aOldMissingCodes = rMissingCodes; + // call the hook to query the best matching glyph fallback font + if( mpFallbackHook->FindFontSubstitute( rFontSelData, rMissingCodes ) ) + // apply outdev3.cxx specific fontname normalization + GetEnglishSearchFontName( rFontSelData.maSearchName ); + else + rFontSelData.maSearchName = String(); + + // cache the result even if there was no match + for(;;) + { + if( !rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName ) ) + rFontSelData.mpFontEntry->AddFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName ); + if( nStrIndex >= aOldMissingCodes.getLength() ) + break; + cChar = aOldMissingCodes.iterateCodePoints( &nStrIndex ); + } + if( rFontSelData.maSearchName.Len() != 0 ) + { + // remove cache entries that were still not resolved + for( nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); ) + { + cChar = rMissingCodes.iterateCodePoints( &nStrIndex ); + rFontSelData.mpFontEntry->IgnoreFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName ); + } + } + } + + // find the matching device font + if( rFontSelData.maSearchName.Len() != 0 ) + pFallbackData = FindFontFamily( rFontSelData.maSearchName ); + } + + // else find a matching font candidate for generic glyph fallback + if( !pFallbackData ) + { + // initialize font candidates for generic glyph fallback if needed + if( mnFallbackCount < 0 ) + InitGenericGlyphFallback(); + // TODO: adjust nFallbackLevel by number of levels resolved by the fallback hook + if( nFallbackLevel < mnFallbackCount ) + pFallbackData = mpFallbackList[ nFallbackLevel ]; + } + + return pFallbackData; +} + +// ----------------------------------------------------------------------- + +void ImplDevFontList::Add( ImplFontData* pNewData ) +{ + int nAliasQuality = pNewData->mnQuality - 100; + String aMapNames = pNewData->maMapNames; + pNewData->maMapNames = String(); + + bool bKeepNewData = false; + xub_StrLen nMapNameIndex = 0; + while( true ) + { + String aSearchName = pNewData->maName; + GetEnglishSearchFontName( aSearchName ); + + DevFontList::const_iterator it = maDevFontList.find( aSearchName ); + ImplDevFontListData* pFoundData = NULL; + if( it != maDevFontList.end() ) + pFoundData = (*it).second; + + if( !pFoundData ) + { + pFoundData = new ImplDevFontListData( aSearchName ); + maDevFontList[ aSearchName ] = pFoundData; + } + + bKeepNewData = pFoundData->AddFontFace( pNewData ); + + // add (another) font alias if available + // a font alias should never win against an original font with similar quality + if( aMapNames.Len() <= nMapNameIndex ) + break; + if( bKeepNewData ) // try to recycle obsoleted object + pNewData = pNewData->CreateAlias(); + bKeepNewData = false; + pNewData->mnQuality = nAliasQuality; + pNewData->maName = GetNextFontToken( aMapNames, nMapNameIndex ); + } + + if( !bKeepNewData ) + delete pNewData; +} + +// ----------------------------------------------------------------------- + +// find the font from the normalized font family name +ImplDevFontListData* ImplDevFontList::ImplFindBySearchName( const String& rSearchName ) const +{ +#ifdef DEBUG + String aTempName = rSearchName; + GetEnglishSearchFontName( aTempName ); + DBG_ASSERT( aTempName == rSearchName, "ImplDevFontList::ImplFindBySearchName() called with non-normalized name" ); +#endif + + DevFontList::const_iterator it = maDevFontList.find( rSearchName ); + if( it == maDevFontList.end() ) + return NULL; + + ImplDevFontListData* pFoundData = (*it).second; + return pFoundData; +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::ImplFindByAliasName( const String& rSearchName, const String& rShortName ) const +{ + // short circuit for impossible font name alias + if( !rSearchName.Len() ) + return NULL; + + // short circuit if no alias names are available + if( !mbMapNames ) + return NULL; + + // use the font's alias names to find the font + // TODO: get rid of linear search + DevFontList::const_iterator it = maDevFontList.begin(); + while( it != maDevFontList.end() ) + { + ImplDevFontListData* pData = (*it).second; + if( !pData->maMapNames.Len() ) + continue; + + // if one alias name matches we found a matching font + String aTempName; + xub_StrLen nIndex = 0; + do + { + aTempName = GetNextFontToken( pData->maMapNames, nIndex ); + // Test, if the Font name match with one of the mapping names + if ( (aTempName == rSearchName) || (aTempName == rShortName) ) + return pData; + } + while ( nIndex != STRING_NOTFOUND ); + } + + return NULL; +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::FindFontFamily( const String& rFontName ) const +{ + // normalize the font fomily name and + String aName = rFontName; + GetEnglishSearchFontName( aName ); + ImplDevFontListData* pFound = ImplFindBySearchName( aName ); + return pFound; +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::ImplFindByTokenNames( const String& rTokenStr ) const +{ + ImplDevFontListData* pFoundData = NULL; + + // use normalized font name tokens to find the font + for( xub_StrLen nTokenPos = 0; nTokenPos != STRING_NOTFOUND; ) + { + String aSearchName = GetNextFontToken( rTokenStr, nTokenPos ); + if( !aSearchName.Len() ) + continue; + GetEnglishSearchFontName( aSearchName ); + pFoundData = ImplFindBySearchName( aSearchName ); + if( pFoundData ) + break; + } + + return pFoundData; +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::ImplFindBySubstFontAttr( const utl::FontNameAttr& rFontAttr ) const +{ + ImplDevFontListData* pFoundData = NULL; + + // use the font substitutions suggested by the FontNameAttr to find the font + ::std::vector< String >::const_iterator it = rFontAttr.Substitutions.begin(); + for(; it != rFontAttr.Substitutions.end(); ++it ) + { + String aSearchName( *it ); + GetEnglishSearchFontName( aSearchName ); + + pFoundData = ImplFindBySearchName( aSearchName ); + if( pFoundData ) + return pFoundData; + } + + // use known attributes from the configuration to find a matching substitute + const sal_uLong nSearchType = rFontAttr.Type; + if( nSearchType != 0 ) + { + const FontWeight eSearchWeight = rFontAttr.Weight; + const FontWidth eSearchWidth = rFontAttr.Width; + const FontItalic eSearchSlant = ITALIC_DONTKNOW; + const FontFamily eSearchFamily = FAMILY_DONTKNOW; + const String aSearchName; + pFoundData = ImplFindByAttributes( nSearchType, + eSearchWeight, eSearchWidth, eSearchFamily, eSearchSlant, aSearchName ); + if( pFoundData ) + return pFoundData; + } + + return NULL; +} + +// ----------------------------------------------------------------------- + +void ImplDevFontList::InitMatchData() const +{ + // short circuit if already done + if( mbMatchData ) + return; + mbMatchData = true; + + // calculate MatchData for all entries + const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get(); + + DevFontList::const_iterator it = maDevFontList.begin(); + for(; it != maDevFontList.end(); ++it ) + { + const String& rSearchName = (*it).first; + ImplDevFontListData* pEntry = (*it).second; + + pEntry->InitMatchData( rFontSubst, rSearchName ); + } +} + +//---------------------------------------------------------------------------- +ImplDevFontListData* ImplDevFontList::ImplFindByLocale( com::sun::star::lang::Locale& rLocale ) const +{ + // get the default font for a specified locale + const DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get(); + const String aDefault = rDefaults.getUserInterfaceFont( rLocale ); + ImplDevFontListData* pFontData = ImplFindByTokenNames( aDefault ); + if( pFontData ) + return pFontData; + return NULL; +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::ImplFindByAttributes( sal_uLong nSearchType, + FontWeight eSearchWeight, FontWidth eSearchWidth, FontFamily /*eSearchFamily*/, + FontItalic eSearchItalic, const String& rSearchFamilyName ) const +{ + if( (eSearchItalic != ITALIC_NONE) && (eSearchItalic != ITALIC_DONTKNOW) ) + nSearchType |= IMPL_FONT_ATTR_ITALIC; + + // don't bother to match attributes if the attributes aren't worth matching + if( !nSearchType + && ((eSearchWeight == WEIGHT_DONTKNOW) || (eSearchWeight == WEIGHT_NORMAL)) + && ((eSearchWidth == WIDTH_DONTKNOW) || (eSearchWidth == WIDTH_NORMAL)) ) + return NULL; + + InitMatchData(); + ImplDevFontListData* pFoundData = NULL; + + long nTestMatch; + long nBestMatch = 40000; + sal_uLong nBestType = 0; + + DevFontList::const_iterator it = maDevFontList.begin(); + for(; it != maDevFontList.end(); ++it ) + { + ImplDevFontListData* pData = (*it).second; + + // Get all information about the matching font + sal_uLong nMatchType = pData->mnMatchType; + FontWeight eMatchWeight= pData->meMatchWeight; + FontWidth eMatchWidth = pData->meMatchWidth; + + // Calculate Match Value + // 1000000000 + // 100000000 + // 10000000 CJK, CTL, None-Latin, Symbol + // 1000000 FamilyName, Script, Fixed, -Special, -Decorative, + // Titling, Capitals, Outline, Shadow + // 100000 Match FamilyName, Serif, SansSerif, Italic, + // Width, Weight + // 10000 Scalable, Standard, Default, + // full, Normal, Knownfont, + // Otherstyle, +Special, +Decorative, + // 1000 Typewriter, Rounded, Gothic, Schollbook + // 100 + nTestMatch = 0; + + // test CJK script attributes + if ( nSearchType & IMPL_FONT_ATTR_CJK ) + { + // Matching language + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_CJK_ALLLANG) ) + nTestMatch += 10000000*3; + if( nMatchType & IMPL_FONT_ATTR_CJK ) + nTestMatch += 10000000*2; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000000; + } + else if ( nMatchType & IMPL_FONT_ATTR_CJK ) + nTestMatch -= 10000000; + + // test CTL script attributes + if( nSearchType & IMPL_FONT_ATTR_CTL ) + { + if( nMatchType & IMPL_FONT_ATTR_CTL ) + nTestMatch += 10000000*2; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000000; + } + else if ( nMatchType & IMPL_FONT_ATTR_CTL ) + nTestMatch -= 10000000; + + // test LATIN script attributes + if( nSearchType & IMPL_FONT_ATTR_NONELATIN ) + { + if( nMatchType & IMPL_FONT_ATTR_NONELATIN ) + nTestMatch += 10000000*2; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000000; + } + + // test SYMBOL attributes + if ( nSearchType & IMPL_FONT_ATTR_SYMBOL ) + { + const String& rSearchName = it->first; + // prefer some special known symbol fonts + if ( rSearchName.EqualsAscii( "starsymbol" ) ) + nTestMatch += 10000000*6+(10000*3); + else if ( rSearchName.EqualsAscii( "opensymbol" ) ) + nTestMatch += 10000000*6; + else if ( rSearchName.EqualsAscii( "starbats" ) + || rSearchName.EqualsAscii( "wingdings" ) + || rSearchName.EqualsAscii( "monotypesorts" ) + || rSearchName.EqualsAscii( "dingbats" ) + || rSearchName.EqualsAscii( "zapfdingbats" ) ) + nTestMatch += 10000000*5; + else if ( pData->mnTypeFaces & IMPL_DEVFONT_SYMBOL ) + nTestMatch += 10000000*4; + else + { + if( nMatchType & IMPL_FONT_ATTR_SYMBOL ) + nTestMatch += 10000000*2; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000000; + } + } + else if ( (pData->mnTypeFaces & (IMPL_DEVFONT_SYMBOL | IMPL_DEVFONT_NONESYMBOL)) == IMPL_DEVFONT_SYMBOL ) + nTestMatch -= 10000000; + else if ( nMatchType & IMPL_FONT_ATTR_SYMBOL ) + nTestMatch -= 10000; + + // match stripped family name + if( rSearchFamilyName.Len() && (rSearchFamilyName == pData->maMatchFamilyName) ) + nTestMatch += 1000000*3; + + // match ALLSCRIPT? attribute + if( nSearchType & IMPL_FONT_ATTR_ALLSCRIPT ) + { + if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT ) + nTestMatch += 1000000*2; + if( nSearchType & IMPL_FONT_ATTR_ALLSUBSCRIPT ) + { + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ALLSUBSCRIPT) ) + nTestMatch += 1000000*2; + if( 0 != ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_BRUSHSCRIPT) ) + nTestMatch -= 1000000; + } + } + else if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT ) + nTestMatch -= 1000000; + + // test MONOSPACE+TYPEWRITER attributes + if( nSearchType & IMPL_FONT_ATTR_FIXED ) + { + if( nMatchType & IMPL_FONT_ATTR_FIXED ) + nTestMatch += 1000000*2; + // a typewriter attribute is even better + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) ) + nTestMatch += 10000*2; + } + else if( nMatchType & IMPL_FONT_ATTR_FIXED ) + nTestMatch -= 1000000; + + // test SPECIAL attribute + if( nSearchType & IMPL_FONT_ATTR_SPECIAL ) + { + if( nMatchType & IMPL_FONT_ATTR_SPECIAL ) + nTestMatch += 10000; + else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) ) + { + if( nMatchType & IMPL_FONT_ATTR_SERIF ) + nTestMatch += 1000*2; + else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + nTestMatch += 1000; + } + } + else if( (nMatchType & IMPL_FONT_ATTR_SPECIAL) && !(nSearchType & IMPL_FONT_ATTR_SYMBOL) ) + nTestMatch -= 1000000; + + // test DECORATIVE attribute + if( nSearchType & IMPL_FONT_ATTR_DECORATIVE ) + { + if( nMatchType & IMPL_FONT_ATTR_DECORATIVE ) + nTestMatch += 10000; + else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) ) + { + if( nMatchType & IMPL_FONT_ATTR_SERIF ) + nTestMatch += 1000*2; + else if ( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + nTestMatch += 1000; + } + } + else if( nMatchType & IMPL_FONT_ATTR_DECORATIVE ) + nTestMatch -= 1000000; + + // test TITLE+CAPITALS attributes + if( nSearchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) ) + { + if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) ) + nTestMatch += 1000000*2; + if( 0 == ((nSearchType^nMatchType) & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS))) + nTestMatch += 1000000; + else if( (nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS)) + && (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) ) + nTestMatch += 1000000; + } + else if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) ) + nTestMatch -= 1000000; + + // test OUTLINE+SHADOW attributes + if( nSearchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) ) + { + if( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) ) + nTestMatch += 1000000*2; + if( 0 == ((nSearchType ^ nMatchType) & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW)) ) + nTestMatch += 1000000; + else if( (nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW)) + && (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) ) + nTestMatch += 1000000; + } + else if ( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) ) + nTestMatch -= 1000000; + + // test font name substrings + // TODO: calculate name matching score using e.g. Levenstein distance + if( (rSearchFamilyName.Len() >= 4) && (pData->maMatchFamilyName.Len() >= 4) + && ((rSearchFamilyName.Search( pData->maMatchFamilyName ) != STRING_NOTFOUND) + || (pData->maMatchFamilyName.Search( rSearchFamilyName ) != STRING_NOTFOUND)) ) + nTestMatch += 5000; + + // test SERIF attribute + if( nSearchType & IMPL_FONT_ATTR_SERIF ) + { + if( nMatchType & IMPL_FONT_ATTR_SERIF ) + nTestMatch += 1000000*2; + else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + nTestMatch -= 1000000; + } + + // test SANSERIF attribute + if( nSearchType & IMPL_FONT_ATTR_SANSSERIF ) + { + if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + nTestMatch += 1000000; + else if ( nMatchType & IMPL_FONT_ATTR_SERIF ) + nTestMatch -= 1000000; + } + + // test ITALIC attribute + if( nSearchType & IMPL_FONT_ATTR_ITALIC ) + { + if( pData->mnTypeFaces & IMPL_DEVFONT_ITALIC ) + nTestMatch += 1000000*3; + if( nMatchType & IMPL_FONT_ATTR_ITALIC ) + nTestMatch += 1000000; + } + else if( !(nSearchType & IMPL_FONT_ATTR_ALLSCRIPT) + && ((nMatchType & IMPL_FONT_ATTR_ITALIC) + || !(pData->mnTypeFaces & IMPL_DEVFONT_NONEITALIC)) ) + nTestMatch -= 1000000*2; + + // test WIDTH attribute + if( (eSearchWidth != WIDTH_DONTKNOW) && (eSearchWidth != WIDTH_NORMAL) ) + { + if( eSearchWidth < WIDTH_NORMAL ) + { + if( eSearchWidth == eMatchWidth ) + nTestMatch += 1000000*3; + else if( (eMatchWidth < WIDTH_NORMAL) && (eMatchWidth != WIDTH_DONTKNOW) ) + nTestMatch += 1000000; + } + else + { + if( eSearchWidth == eMatchWidth ) + nTestMatch += 1000000*3; + else if( eMatchWidth > WIDTH_NORMAL ) + nTestMatch += 1000000; + } + } + else if( (eMatchWidth != WIDTH_DONTKNOW) && (eMatchWidth != WIDTH_NORMAL) ) + nTestMatch -= 1000000; + + // test WEIGHT attribute + if( (eSearchWeight != WEIGHT_DONTKNOW) && (eSearchWeight != WEIGHT_NORMAL) && (eSearchWeight != WEIGHT_MEDIUM) ) + { + if( eSearchWeight < WEIGHT_NORMAL ) + { + if( pData->mnTypeFaces & IMPL_DEVFONT_LIGHT ) + nTestMatch += 1000000; + if( (eMatchWeight < WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_DONTKNOW) ) + nTestMatch += 1000000; + } + else + { + if( pData->mnTypeFaces & IMPL_DEVFONT_BOLD ) + nTestMatch += 1000000; + if( eMatchWeight > WEIGHT_BOLD ) + nTestMatch += 1000000; + } + } + else if( ((eMatchWeight != WEIGHT_DONTKNOW) && (eMatchWeight != WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_MEDIUM)) + || !(pData->mnTypeFaces & IMPL_DEVFONT_NORMAL) ) + nTestMatch -= 1000000; + + // prefer scalable fonts + if( pData->mnTypeFaces & IMPL_DEVFONT_SCALABLE ) + nTestMatch += 10000*4; + else + nTestMatch -= 10000*4; + + // test STANDARD+DEFAULT+FULL+NORMAL attributes + if( nMatchType & IMPL_FONT_ATTR_STANDARD ) + nTestMatch += 10000*2; + if( nMatchType & IMPL_FONT_ATTR_DEFAULT ) + nTestMatch += 10000; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000; + if( nMatchType & IMPL_FONT_ATTR_NORMAL ) + nTestMatch += 10000; + + // test OTHERSTYLE attribute + if( nMatchType & IMPL_FONT_ATTR_OTHERSTYLE ) + { + if( !(nMatchType & IMPL_FONT_ATTR_OTHERSTYLE) ) + nTestMatch -= 10000; + } + else if( nMatchType & IMPL_FONT_ATTR_OTHERSTYLE ) + nTestMatch -= 10000; + + // test ROUNDED attribute + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ROUNDED) ) + nTestMatch += 1000; + + // test TYPEWRITER attribute + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) ) + nTestMatch += 1000; + + // test GOTHIC attribute + if( nSearchType & IMPL_FONT_ATTR_GOTHIC ) + { + if( nMatchType & IMPL_FONT_ATTR_GOTHIC ) + nTestMatch += 1000*3; + if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + nTestMatch += 1000*2; + } + + // test SCHOOLBOOK attribute + if( nSearchType & IMPL_FONT_ATTR_SCHOOLBOOK ) + { + if( nMatchType & IMPL_FONT_ATTR_SCHOOLBOOK ) + nTestMatch += 1000*3; + if( nMatchType & IMPL_FONT_ATTR_SERIF ) + nTestMatch += 1000*2; + } + + // compare with best matching font yet + if ( nTestMatch > nBestMatch ) + { + pFoundData = pData; + nBestMatch = nTestMatch; + nBestType = nMatchType; + } + else if( nTestMatch == nBestMatch ) + { + // some fonts are more suitable defaults + if( nMatchType & IMPL_FONT_ATTR_DEFAULT ) + { + pFoundData = pData; + nBestType = nMatchType; + } + else if( (nMatchType & IMPL_FONT_ATTR_STANDARD) && + !(nBestType & IMPL_FONT_ATTR_DEFAULT) ) + { + pFoundData = pData; + nBestType = nMatchType; + } + } + } + + return pFoundData; +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::FindDefaultFont() const +{ + // try to find one of the default fonts of the + // UNICODE, SANSSERIF, SERIF or FIXED default font lists + const DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get(); + com::sun::star::lang::Locale aLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() ); + String aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS_UNICODE ); + ImplDevFontListData* pFoundData = ImplFindByTokenNames( aFontname ); + if( pFoundData ) + return pFoundData; + + aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS ); + pFoundData = ImplFindByTokenNames( aFontname ); + if( pFoundData ) + return pFoundData; + + aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SERIF ); + pFoundData = ImplFindByTokenNames( aFontname ); + if( pFoundData ) + return pFoundData; + + aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_FIXED ); + pFoundData = ImplFindByTokenNames( aFontname ); + if( pFoundData ) + return pFoundData; + + // now try to find a reasonable non-symbol font + + InitMatchData(); + + DevFontList::const_iterator it = maDevFontList.begin(); + for(; it != maDevFontList.end(); ++it ) + { + ImplDevFontListData* pData = (*it).second; + if( pData->mnMatchType & IMPL_FONT_ATTR_SYMBOL ) + continue; + pFoundData = pData; + if( pData->mnMatchType & (IMPL_FONT_ATTR_DEFAULT|IMPL_FONT_ATTR_STANDARD) ) + break; + } + if( pFoundData ) + return pFoundData; + + // finding any font is better than finding no font at all + it = maDevFontList.begin(); + if( it != maDevFontList.end() ) + pFoundData = (*it).second; + + return pFoundData; +} + +// ----------------------------------------------------------------------- + +ImplDevFontList* ImplDevFontList::Clone( bool bScalable, bool bEmbeddable ) const +{ + ImplDevFontList* pClonedList = new ImplDevFontList; +// pClonedList->mbMatchData = mbMatchData; + pClonedList->mbMapNames = mbMapNames; + pClonedList->mpPreMatchHook = mpPreMatchHook; + pClonedList->mpFallbackHook = mpFallbackHook; + + // TODO: clone the config-font attributes too? + pClonedList->mbMatchData = false; + + DevFontList::const_iterator it = maDevFontList.begin(); + for(; it != maDevFontList.end(); ++it ) + { + const ImplDevFontListData* pFontFace = (*it).second; + pFontFace->UpdateCloneFontList( *pClonedList, bScalable, bEmbeddable ); + } + + return pClonedList; +} + +// ----------------------------------------------------------------------- + +ImplGetDevFontList* ImplDevFontList::GetDevFontList() const +{ + ImplGetDevFontList* pGetDevFontList = new ImplGetDevFontList; + + DevFontList::const_iterator it = maDevFontList.begin(); + for(; it != maDevFontList.end(); ++it ) + { + const ImplDevFontListData* pFontFamily = (*it).second; + pFontFamily->UpdateDevFontList( *pGetDevFontList ); + } + + return pGetDevFontList; +} + +// ----------------------------------------------------------------------- + +ImplGetDevSizeList* ImplDevFontList::GetDevSizeList( const String& rFontName ) const +{ + ImplGetDevSizeList* pGetDevSizeList = new ImplGetDevSizeList( rFontName ); + + ImplDevFontListData* pFontFamily = FindFontFamily( rFontName ); + if( pFontFamily != NULL ) + { + std::set<int> rHeights; + pFontFamily->GetFontHeights( rHeights ); + + std::set<int>::const_iterator it = rHeights.begin(); + for(; it != rHeights.begin(); ++it ) + pGetDevSizeList->Add( *it ); + } + + return pGetDevSizeList; +} + +// ======================================================================= + +ImplFontSelectData::ImplFontSelectData( const Font& rFont, + const String& rSearchName, const Size& rSize, float fExactHeight) +: maSearchName( rSearchName ), + mnWidth( rSize.Width() ), + mnHeight( rSize.Height() ), + mfExactHeight( fExactHeight), + mnOrientation( rFont.GetOrientation() ), + meLanguage( rFont.GetLanguage() ), + mbVertical( rFont.IsVertical() ), + mbNonAntialiased( false ), + mpFontData( NULL ), + mpFontEntry( NULL ) +{ + maTargetName = maName; + + rFont.GetFontAttributes( *this ); + + // normalize orientation between 0 and 3600 + if( 3600 <= (unsigned)mnOrientation ) + { + if( mnOrientation >= 0 ) + mnOrientation %= 3600; + else + mnOrientation = 3600 - (-mnOrientation % 3600); + } + + // normalize width and height + if( mnHeight < 0 ) + mnHeight = -mnHeight; + if( mnWidth < 0 ) + mnWidth = -mnWidth; +} + +// ----------------------------------------------------------------------- + +ImplFontSelectData::ImplFontSelectData( const ImplFontData& rFontData, + const Size& rSize, float fExactHeight, int nOrientation, bool bVertical ) +: ImplFontAttributes( rFontData ), + mnWidth( rSize.Width() ), + mnHeight( rSize.Height() ), + mfExactHeight( fExactHeight ), + mnOrientation( nOrientation ), + meLanguage( 0 ), + mbVertical( bVertical ), + mbNonAntialiased( false ), + mpFontData( &rFontData ), + mpFontEntry( NULL ) +{ + maTargetName = maSearchName = maName; + // NOTE: no normalization for width/height/orientation +} + +// ======================================================================= + +size_t ImplFontCache::IFSD_Hash::operator()( const ImplFontSelectData& rFSD ) const +{ + // TODO: does it pay off to improve this hash function? + static FontNameHash aFontNameHash; + size_t nHash = aFontNameHash( rFSD.maSearchName ); +#ifdef ENABLE_GRAPHITE + // check for features and generate a unique hash if necessary + if (rFSD.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX) + != STRING_NOTFOUND) + { + nHash = aFontNameHash( rFSD.maTargetName ); + } +#endif + nHash += 11 * rFSD.mnHeight; + nHash += 19 * rFSD.meWeight; + nHash += 29 * rFSD.meItalic; + nHash += 37 * rFSD.mnOrientation; + nHash += 41 * rFSD.meLanguage; + if( rFSD.mbVertical ) + nHash += 53; + return nHash; +} + +// ----------------------------------------------------------------------- + +bool ImplFontCache::IFSD_Equal::operator()(const ImplFontSelectData& rA, const ImplFontSelectData& rB) const +{ + // check normalized font family name + if( rA.maSearchName != rB.maSearchName ) + return false; + + // check font transformation + if( (rA.mnHeight != rB.mnHeight) + || (rA.mnWidth != rB.mnWidth) + || (rA.mnOrientation != rB.mnOrientation) ) + return false; + + // check mapping relevant attributes + if( (rA.mbVertical != rB.mbVertical) + || (rA.meLanguage != rB.meLanguage) ) + return false; + + // check font face attributes + if( (rA.meWeight != rB.meWeight) + || (rA.meItalic != rB.meItalic) +// || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member + || (rA.mePitch != rB.mePitch) ) + return false; + + // check style name + if( rA.maStyleName != rB.maStyleName) + return false; + + // Symbol fonts may recode from one type to another So they are only + // safely equivalent for equal targets + if ( + (rA.mpFontData && rA.mpFontData->IsSymbolFont()) || + (rB.mpFontData && rB.mpFontData->IsSymbolFont()) + ) + { + if (rA.maTargetName != rB.maTargetName) + return false; + } + +#ifdef ENABLE_GRAPHITE + // check for features + if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX) + != STRING_NOTFOUND || + rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX) + != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName) + return false; +#endif + + return true; +} + +// ----------------------------------------------------------------------- + +ImplFontCache::ImplFontCache( bool bPrinter ) +: mpFirstEntry( NULL ), + mnRef0Count( 0 ), + mbPrinter( bPrinter ) +{} + +// ----------------------------------------------------------------------- + +ImplFontCache::~ImplFontCache() +{ + FontInstanceList::iterator it = maFontInstanceList.begin(); + for(; it != maFontInstanceList.end(); ++it ) + { + ImplFontEntry* pEntry = (*it).second; + delete pEntry; + } +} + +// ----------------------------------------------------------------------- + +ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList, + const Font& rFont, const Size& rSize, float fExactHeight, ImplDirectFontSubstitution* pDevSpecific ) +{ + String aSearchName = rFont.GetName(); + + // TODO: also add device specific name caching + if( !pDevSpecific ) + { + // check if the requested font name is already known + // if it is already known get its normalized search name + FontNameList::const_iterator it_name = maFontNameList.find( aSearchName ); + if( it_name != maFontNameList.end() ) + if( !(*it_name).second.EqualsAscii( "hg", 0, 2) +#ifdef ENABLE_GRAPHITE + && (aSearchName.Search(grutils::GrFeatureParser::FEAT_PREFIX) + == STRING_NOTFOUND) +#endif + ) + aSearchName = (*it_name).second; + } + + // initialize internal font request object + ImplFontSelectData aFontSelData( rFont, aSearchName, rSize, fExactHeight ); + return GetFontEntry( pFontList, aFontSelData, pDevSpecific ); +} + +// ----------------------------------------------------------------------- + +ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList, + ImplFontSelectData& aFontSelData, ImplDirectFontSubstitution* pDevSpecific ) +{ + // check if a directly matching logical font instance is already cached, + // the most recently used font usually has a hit rate of >50% + ImplFontEntry *pEntry = NULL; + ImplDevFontListData* pFontFamily = NULL; + IFSD_Equal aIFSD_Equal; + if( mpFirstEntry && aIFSD_Equal( aFontSelData, mpFirstEntry->maFontSelData ) ) + pEntry = mpFirstEntry; + else + { + FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData ); + if( it != maFontInstanceList.end() ) + pEntry = (*it).second; + } + + if( !pEntry ) // no direct cache hit + { + // find the best matching logical font family and update font selector accordingly + pFontFamily = pFontList->ImplFindByFont( aFontSelData, mbPrinter, pDevSpecific ); + DBG_ASSERT( (pFontFamily != NULL), "ImplFontCache::Get() No logical font found!" ); + if( pFontFamily ) + aFontSelData.maSearchName = pFontFamily->GetSearchName(); + + // check if an indirectly matching logical font instance is already cached + FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData ); + if( it != maFontInstanceList.end() ) + { + // we have an indirect cache hit + pEntry = (*it).second; + // cache the requested and the selected font names + // => next time there is a good chance for a direct cache hit + // don't allow the cache to grow too big + // TODO: implement some fancy LRU caching? + if( maFontNameList.size() >= 4000 ) + maFontNameList.clear(); + // TODO: also add device specific name caching + if( !pDevSpecific ) + if( aFontSelData.maName != aFontSelData.maSearchName ) + maFontNameList[ aFontSelData.maName ] = aFontSelData.maSearchName; + } + } + + if( pEntry ) // cache hit => use existing font instance + { + // increase the font instance's reference count + if( !pEntry->mnRefCount++ ) + --mnRef0Count; + } + else // no cache hit => create a new font instance + { + // find the best matching physical font face + ImplFontData* pFontData = pFontFamily->FindBestFontFace( aFontSelData ); + aFontSelData.mpFontData = pFontData; + + // create a new logical font instance from this physical font face + pEntry = pFontData->CreateFontInstance( aFontSelData ); + + // if we found a different symbol font we need a symbol conversion table + if( pFontData->IsSymbolFont() ) + if( aFontSelData.maTargetName != aFontSelData.maSearchName ) + pEntry->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName ); + + // add the new entry to the cache + maFontInstanceList[ aFontSelData ] = pEntry; + } + + mpFirstEntry = pEntry; + return pEntry; +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::ImplFindByFont( ImplFontSelectData& rFSD, + bool bPrinter, ImplDirectFontSubstitution* pDevSpecific ) const +{ + // give up if no fonts are available + if( !Count() ) + return NULL; + + // test if a font in the token list is available + // substitute the font if this was requested + sal_uInt16 nSubstFlags = FONT_SUBSTITUTE_ALWAYS; + if ( bPrinter ) + nSubstFlags |= FONT_SUBSTITUTE_SCREENONLY; + + bool bMultiToken = false; + xub_StrLen nTokenPos = 0; + String& aSearchName = rFSD.maSearchName; // TODO: get rid of reference + for(;;) + { + rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos ); + aSearchName = rFSD.maTargetName; + +#ifdef ENABLE_GRAPHITE + // Until features are properly supported, they are appended to the + // font name, so we need to strip them off so the font is found. + xub_StrLen nFeat = aSearchName.Search(grutils::GrFeatureParser::FEAT_PREFIX); + String aOrigName = rFSD.maTargetName; + String aBaseFontName(aSearchName, 0, (nFeat != STRING_NOTFOUND)?nFeat:aSearchName.Len()); + if (nFeat != STRING_NOTFOUND && STRING_NOTFOUND != + aSearchName.Search(grutils::GrFeatureParser::FEAT_ID_VALUE_SEPARATOR, nFeat)) + { + aSearchName = aBaseFontName; + rFSD.maTargetName = aBaseFontName; + } + +#endif + + GetEnglishSearchFontName( aSearchName ); + ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific ); + // #114999# special emboldening for Ricoh fonts + // TODO: smarter check for special cases by using PreMatch infrastructure? + if( (rFSD.meWeight > WEIGHT_MEDIUM) + && aSearchName.EqualsAscii( "hg", 0, 2) ) + { + String aBoldName; + if( aSearchName.EqualsAscii( "hggothicb", 0, 9) ) + aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hggothice")); + else if( aSearchName.EqualsAscii( "hgpgothicb", 0, 10) ) + aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpgothice")); + else if( aSearchName.EqualsAscii( "hgminchol", 0, 9) ) + aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchob")); + else if( aSearchName.EqualsAscii( "hgpminchol", 0, 10) ) + aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchob")); + else if( aSearchName.EqualsAscii( "hgminchob" ) ) + aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchoe")); + else if( aSearchName.EqualsAscii( "hgpminchob" ) ) + aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchoe")); + + if( aBoldName.Len() && ImplFindBySearchName( aBoldName ) ) + { + // the other font is available => use it + aSearchName = aBoldName; + // prevent synthetic emboldening of bold version + rFSD.meWeight = WEIGHT_DONTKNOW; + } + } + +#ifdef ENABLE_GRAPHITE + // restore the features to make the font selection data unique + rFSD.maTargetName = aOrigName; +#endif + // check if the current font name token or its substitute is valid + ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName ); + if( pFoundData ) + return pFoundData; + + // some systems provide special customization + // e.g. they suggest "serif" as UI-font, but this name cannot be used directly + // because the system wants to map it to another font first, e.g. "Helvetica" +#ifdef ENABLE_GRAPHITE + // use the target name to search in the prematch hook + rFSD.maTargetName = aBaseFontName; +#endif + if( mpPreMatchHook ) + if( mpPreMatchHook->FindFontSubstitute( rFSD ) ) + GetEnglishSearchFontName( aSearchName ); +#ifdef ENABLE_GRAPHITE + // the prematch hook uses the target name to search, but we now need + // to restore the features to make the font selection data unique + rFSD.maTargetName = aOrigName; +#endif + pFoundData = ImplFindBySearchName( aSearchName ); + if( pFoundData ) + return pFoundData; + + // break after last font name token was checked unsuccessfully + if( nTokenPos == STRING_NOTFOUND) + break; + bMultiToken = true; + } + + // if the first font was not available find the next available font in + // the semicolon separated list of font names. A font is also considered + // available when there is a matching entry in the Tools->Options->Fonts + // dialog witho neither ALWAYS nor SCREENONLY flags set and the substitution + // font is available + for( nTokenPos = 0; nTokenPos != STRING_NOTFOUND; ) + { + if( bMultiToken ) + { + rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos ); + aSearchName = rFSD.maTargetName; + GetEnglishSearchFontName( aSearchName ); + } + else + nTokenPos = STRING_NOTFOUND; + if( mpPreMatchHook ) + if( mpPreMatchHook->FindFontSubstitute( rFSD ) ) + GetEnglishSearchFontName( aSearchName ); + ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific ); + ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName ); + if( pFoundData ) + return pFoundData; + } + + // if no font with a directly matching name is available use the + // first font name token and get its attributes to find a replacement + if ( bMultiToken ) + { + nTokenPos = 0; + rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos ); + aSearchName = rFSD.maTargetName; + GetEnglishSearchFontName( aSearchName ); + } + + String aSearchShortName; + String aSearchFamilyName; + FontWeight eSearchWeight = rFSD.meWeight; + FontWidth eSearchWidth = rFSD.meWidthType; + sal_uLong nSearchType = 0; + FontSubstConfiguration::getMapName( aSearchName, aSearchShortName, aSearchFamilyName, + eSearchWeight, eSearchWidth, nSearchType ); + + // note: the search name was already translated to english (if possible) + + // use the font's shortened name if needed + if ( aSearchShortName != aSearchName ) + { + ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchShortName ); + if( pFoundData ) + { +#ifdef UNX + /* #96738# don't use mincho as an replacement for "MS Mincho" on X11: Mincho is + a korean bitmap font that is not suitable here. Use the font replacement table, + that automatically leads to the desired "HG Mincho Light J". Same story for + MS Gothic, there are thai and korean "Gothic" fonts, so we even prefer Andale */ + static String aMS_Mincho( RTL_CONSTASCII_USTRINGPARAM("msmincho") ); + static String aMS_Gothic( RTL_CONSTASCII_USTRINGPARAM("msgothic") ); + if ((aSearchName != aMS_Mincho) && (aSearchName != aMS_Gothic)) + // TODO: add heuristic to only throw out the fake ms* fonts +#endif + { + return pFoundData; + } + } + } + + // use font fallback + const FontNameAttr* pFontAttr = NULL; + if( aSearchName.Len() ) + { + // get fallback info using FontSubstConfiguration and + // the target name, it's shortened name and family name in that order + const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get(); + pFontAttr = rFontSubst.getSubstInfo( aSearchName ); + if ( !pFontAttr && (aSearchShortName != aSearchName) ) + pFontAttr = rFontSubst.getSubstInfo( aSearchShortName ); + if ( !pFontAttr && (aSearchFamilyName != aSearchShortName) ) + pFontAttr = rFontSubst.getSubstInfo( aSearchFamilyName ); + + // try the font substitutions suggested by the fallback info + if( pFontAttr ) + { + ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pFontAttr ); + if( pFoundData ) + return pFoundData; + } + } + + // if a target symbol font is not available use a default symbol font + if( rFSD.IsSymbolFont() ) + { + com::sun::star::lang::Locale aDefaultLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() ); + aSearchName = DefaultFontConfiguration::get()->getDefaultFont( aDefaultLocale, DEFAULTFONT_SYMBOL ); + ImplDevFontListData* pFoundData = ImplFindByTokenNames( aSearchName ); + if( pFoundData ) + return pFoundData; + } + + // now try the other font name tokens + while( nTokenPos != STRING_NOTFOUND ) + { + rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos ); + if( !rFSD.maTargetName.Len() ) + continue; + + aSearchName = rFSD.maTargetName; + GetEnglishSearchFontName( aSearchName ); + + String aTempShortName; + String aTempFamilyName; + sal_uLong nTempType = 0; + FontWeight eTempWeight = rFSD.meWeight; + FontWidth eTempWidth = WIDTH_DONTKNOW; + FontSubstConfiguration::getMapName( aSearchName, aTempShortName, aTempFamilyName, + eTempWeight, eTempWidth, nTempType ); + + // use a shortend token name if available + if( aTempShortName != aSearchName ) + { + ImplDevFontListData* pFoundData = ImplFindBySearchName( aTempShortName ); + if( pFoundData ) + return pFoundData; + } + + // use a font name from font fallback list to determine font attributes + + // get fallback info using FontSubstConfiguration and + // the target name, it's shortened name and family name in that order + const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get(); + const FontNameAttr* pTempFontAttr = rFontSubst.getSubstInfo( aSearchName ); + if ( !pTempFontAttr && (aTempShortName != aSearchName) ) + pTempFontAttr = rFontSubst.getSubstInfo( aTempShortName ); + if ( !pTempFontAttr && (aTempFamilyName != aTempShortName) ) + pTempFontAttr = rFontSubst.getSubstInfo( aTempFamilyName ); + + // try the font substitutions suggested by the fallback info + if( pTempFontAttr ) + { + ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pTempFontAttr ); + if( pFoundData ) + return pFoundData; + if( !pFontAttr ) + pFontAttr = pTempFontAttr; + } + } + + // if still needed use the alias names of the installed fonts + if( mbMapNames ) + { + ImplDevFontListData* pFoundData = ImplFindByAliasName( rFSD.maTargetName, aSearchShortName ); + if( pFoundData ) + return pFoundData; + } + + // if still needed use the font request's attributes to find a good match + switch( rFSD.meLanguage ) + { + case LANGUAGE_CHINESE: + case LANGUAGE_CHINESE_SIMPLIFIED: + case LANGUAGE_CHINESE_SINGAPORE: + nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_SC; + break; + case LANGUAGE_CHINESE_TRADITIONAL: + case LANGUAGE_CHINESE_HONGKONG: + case LANGUAGE_CHINESE_MACAU: + nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_TC; + break; + case LANGUAGE_KOREAN: + case LANGUAGE_KOREAN_JOHAB: + nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_KR; + break; + case LANGUAGE_JAPANESE: + nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_JP; + break; + default: + nSearchType |= ImplIsCJKFont( rFSD.maName ); + if( rFSD.IsSymbolFont() ) + nSearchType |= IMPL_FONT_ATTR_SYMBOL; + break; + } + + ImplCalcType( nSearchType, eSearchWeight, eSearchWidth, rFSD.meFamily, pFontAttr ); + ImplDevFontListData* pFoundData = ImplFindByAttributes( nSearchType, + eSearchWeight, eSearchWidth, rFSD.meFamily, rFSD.meItalic, aSearchFamilyName ); + + if( pFoundData ) + { + // overwrite font selection attributes using info from the typeface flags + if( (eSearchWeight >= WEIGHT_BOLD) + && (eSearchWeight > rFSD.meWeight) + && (pFoundData->mnTypeFaces & IMPL_DEVFONT_BOLD) ) + rFSD.meWeight = eSearchWeight; + else if( (eSearchWeight < WEIGHT_NORMAL) + && (eSearchWeight < rFSD.meWeight) + && (eSearchWeight != WEIGHT_DONTKNOW) + && (pFoundData->mnTypeFaces & IMPL_DEVFONT_LIGHT) ) + rFSD.meWeight = eSearchWeight; + + if( (nSearchType & IMPL_FONT_ATTR_ITALIC) + && ((rFSD.meItalic == ITALIC_DONTKNOW) || (rFSD.meItalic == ITALIC_NONE)) + && (pFoundData->mnTypeFaces & IMPL_DEVFONT_ITALIC) ) + rFSD.meItalic = ITALIC_NORMAL; + } + else + { + // if still needed fall back to default fonts + pFoundData = FindDefaultFont(); + } + + return pFoundData; +} + +// ----------------------------------------------------------------------- + +ImplFontEntry* ImplFontCache::GetGlyphFallbackFont( ImplDevFontList* pFontList, + ImplFontSelectData& rFontSelData, int nFallbackLevel, rtl::OUString& rMissingCodes ) +{ + // get a candidate font for glyph fallback + // unless the previously selected font got a device specific substitution + // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it + if( nFallbackLevel >= 1) + { + ImplDevFontListData* pFallbackData = NULL; + + //fdo#33898 If someone has EUDC installed then they really want that to + //be used as the first-choice glyph fallback seeing as it's filled with + //private area codes with don't make any sense in any other font so + //prioritise it here if it's available. Ideally we would remove from + //rMissingCodes all the glyphs which it is able to resolve as an + //optimization, but that's tricky to achieve cross-platform without + //sufficient heavy-weight code that's likely to undo the value of the + //optimization + if (nFallbackLevel == 1) + pFallbackData = pFontList->FindFontFamily(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("EUDC"))); + if (!pFallbackData) + pFallbackData = pFontList->GetGlyphFallbackFont(rFontSelData, rMissingCodes, nFallbackLevel-1); + // escape when there are no font candidates + if( !pFallbackData ) + return NULL; + // override the font name + rFontSelData.maName = pFallbackData->GetFamilyName(); + // clear the cached normalized name + rFontSelData.maSearchName = String(); + } + + // get device font without doing device specific substitutions + ImplFontEntry* pFallbackFont = GetFontEntry( pFontList, rFontSelData, NULL ); + return pFallbackFont; +} + +// ----------------------------------------------------------------------- + +void ImplFontCache::Release( ImplFontEntry* pEntry ) +{ + static const int FONTCACHE_MAX = 50; + + DBG_ASSERT( (pEntry->mnRefCount > 0), "ImplFontCache::Release() - font refcount underflow" ); + if( --pEntry->mnRefCount > 0 ) + return; + + if( ++mnRef0Count < FONTCACHE_MAX ) + return; + + // remove unused entries from font instance cache + FontInstanceList::iterator it_next = maFontInstanceList.begin(); + while( it_next != maFontInstanceList.end() ) + { + FontInstanceList::iterator it = it_next++; + ImplFontEntry* pFontEntry = (*it).second; + if( pFontEntry->mnRefCount > 0 ) + continue; + + maFontInstanceList.erase( it ); + delete pFontEntry; + --mnRef0Count; + DBG_ASSERT( (mnRef0Count>=0), "ImplFontCache::Release() - refcount0 underflow" ); + + if( mpFirstEntry == pFontEntry ) + mpFirstEntry = NULL; + } + + DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Release() - refcount0 mismatch" ); +} + +// ----------------------------------------------------------------------- + +void ImplFontCache::Invalidate() +{ + // delete unreferenced entries + FontInstanceList::iterator it = maFontInstanceList.begin(); + for(; it != maFontInstanceList.end(); ++it ) + { + ImplFontEntry* pFontEntry = (*it).second; + if( pFontEntry->mnRefCount > 0 ) + continue; + + delete pFontEntry; + --mnRef0Count; + } + + // #112304# make sure the font cache is really clean + mpFirstEntry = NULL; + maFontInstanceList.clear(); + + DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Invalidate() - mnRef0Count non-zero" ); + +#ifdef USE_BUILTIN_RASTERIZER + // TODO: eventually move into SalGraphics layer + GlyphCache::GetInstance().InvalidateAllGlyphs(); +#endif +} + +// ======================================================================= + +ImplMultiTextLineInfo::ImplMultiTextLineInfo() +{ + mpLines = new PImplTextLineInfo[MULTITEXTLINEINFO_RESIZE]; + mnLines = 0; + mnSize = MULTITEXTLINEINFO_RESIZE; +} + + +ImplMultiTextLineInfo::~ImplMultiTextLineInfo() +{ + for ( xub_StrLen i = 0; i < mnLines; i++ ) + delete mpLines[i]; + delete [] mpLines; +} + +void ImplMultiTextLineInfo::AddLine( ImplTextLineInfo* pLine ) +{ + if ( mnSize == mnLines ) + { + mnSize += MULTITEXTLINEINFO_RESIZE; + PImplTextLineInfo* pNewLines = new PImplTextLineInfo[mnSize]; + memcpy( pNewLines, mpLines, mnLines*sizeof(PImplTextLineInfo) ); + mpLines = pNewLines; + } + + mpLines[mnLines] = pLine; + mnLines++; +} + +void ImplMultiTextLineInfo::Clear() +{ + for ( xub_StrLen i = 0; i < mnLines; i++ ) + delete mpLines[i]; + mnLines = 0; +} + +// ======================================================================= + +FontEmphasisMark OutputDevice::ImplGetEmphasisMarkStyle( const Font& rFont ) +{ + FontEmphasisMark nEmphasisMark = rFont.GetEmphasisMark(); + + // If no Position is set, then calculate the default position, which + // depends on the language + if ( !(nEmphasisMark & (EMPHASISMARK_POS_ABOVE | EMPHASISMARK_POS_BELOW)) ) + { + LanguageType eLang = rFont.GetLanguage(); + // In Chinese Simplified the EmphasisMarks are below/left + if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) || + (eLang == LANGUAGE_CHINESE_SINGAPORE) ) + nEmphasisMark |= EMPHASISMARK_POS_BELOW; + else + { + eLang = rFont.GetCJKContextLanguage(); + // In Chinese Simplified the EmphasisMarks are below/left + if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) || + (eLang == LANGUAGE_CHINESE_SINGAPORE) ) + nEmphasisMark |= EMPHASISMARK_POS_BELOW; + else + nEmphasisMark |= EMPHASISMARK_POS_ABOVE; + } + } + + return nEmphasisMark; +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::ImplIsUnderlineAbove( const Font& rFont ) +{ + if ( !rFont.IsVertical() ) + return sal_False; + + if( (LANGUAGE_JAPANESE == rFont.GetLanguage()) + || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()) ) + // the underline is right for Japanese only + return sal_True; + + return sal_False; +} + +// ======================================================================= + +void OutputDevice::ImplInitFontList() const +{ + if( ! mpFontList->Count() ) + { + if( mpGraphics || ImplGetGraphics() ) + { + RTL_LOGFILE_CONTEXT( aLog, "OutputDevice::ImplInitFontList()" ); + mpGraphics->GetDevFontList( mpFontList ); + } + } + if( meOutDevType == OUTDEV_WINDOW && ! mpFontList->Count() ) + { + String aError( RTL_CONSTASCII_USTRINGPARAM( "Application error: no fonts and no vcl resource found on your system" ) ); + ResMgr* pMgr = ImplGetResMgr(); + if( pMgr ) + { + String aResStr( ResId( SV_ACCESSERROR_NO_FONTS, *pMgr ) ); + if( aResStr.Len() ) + aError = aResStr; + } + Application::Abort( aError ); + } +} + +// ======================================================================= + +void OutputDevice::ImplInitFont() const +{ + DBG_TESTSOLARMUTEX(); + + if ( mbInitFont ) + { + if ( meOutDevType != OUTDEV_PRINTER ) + { + // decide if antialiasing is appropriate + bool bNonAntialiased = (GetAntialiasing() & ANTIALIASING_DISABLE_TEXT) != 0; + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + bNonAntialiased |= ((rStyleSettings.GetDisplayOptions() & DISPLAY_OPTION_AA_DISABLE) != 0); + bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > mpFontEntry->maFontSelData.mnHeight); + mpFontEntry->maFontSelData.mbNonAntialiased = bNonAntialiased; + } + + if( !mpPDFWriter || !mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) ) + { + // select font in the device layers + mpFontEntry->mnSetFontFlags = mpGraphics->SetFont( &(mpFontEntry->maFontSelData), 0 ); + } + mbInitFont = false; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitTextColor() +{ + DBG_TESTSOLARMUTEX(); + + if ( mbInitTextColor ) + { + mpGraphics->SetTextColor( ImplColorToSal( GetTextColor() ) ); + mbInitTextColor = sal_False; + } +} + +// ----------------------------------------------------------------------- + +bool OutputDevice::ImplNewFont() const +{ + DBG_TESTSOLARMUTEX(); + + // get correct font list on the PDF writer if necessary + if( mpPDFWriter ) + { + const ImplSVData* pSVData = ImplGetSVData(); + if( mpFontList == pSVData->maGDIData.mpScreenFontList + || mpFontCache == pSVData->maGDIData.mpScreenFontCache ) + const_cast<OutputDevice&>(*this).ImplUpdateFontData( true ); + } + + if ( !mbNewFont ) + return true; + + // we need a graphics + if ( !mpGraphics && !ImplGetGraphics() ) + return false; + SalGraphics* pGraphics = mpGraphics; + ImplInitFontList(); + + // convert to pixel height + // TODO: replace integer based aSize completely with subpixel accurate type + float fExactHeight = ImplFloatLogicHeightToDevicePixel( static_cast<float>(maFont.GetHeight()) ); + Size aSize = ImplLogicToDevicePixel( maFont.GetSize() ); + if ( !aSize.Height() ) + { + // use default pixel height only when logical height is zero + if ( maFont.GetSize().Height() ) + aSize.Height() = 1; + else + aSize.Height() = (12*mnDPIY)/72; + fExactHeight = static_cast<float>(aSize.Height()); + } + + // select the default width only when logical width is zero + if( (0 == aSize.Width()) && (0 != maFont.GetSize().Width()) ) + aSize.Width() = 1; + + // get font entry + ImplDirectFontSubstitution* pDevSpecificSubst = NULL; + if( mpOutDevData ) + pDevSpecificSubst = &mpOutDevData->maDevFontSubst; + ImplFontEntry* pOldEntry = mpFontEntry; + mpFontEntry = mpFontCache->GetFontEntry( mpFontList, maFont, aSize, fExactHeight, pDevSpecificSubst ); + if( pOldEntry ) + mpFontCache->Release( pOldEntry ); + + ImplFontEntry* pFontEntry = mpFontEntry; + // mark when lower layers need to get involved + mbNewFont = sal_False; + if( pFontEntry != pOldEntry ) + mbInitFont = sal_True; + + // select font when it has not been initialized yet + if ( !pFontEntry->mbInit ) + { + ImplInitFont(); + + // get metric data from device layers + if ( pGraphics ) + { + pFontEntry->mbInit = true; + + pFontEntry->maMetric.mnOrientation = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation); + if( mpPDFWriter && mpPDFWriter->isBuiltinFont( pFontEntry->maFontSelData.mpFontData ) ) + mpPDFWriter->getFontMetric( &pFontEntry->maFontSelData, &(pFontEntry->maMetric) ); + else + pGraphics->GetFontMetric( &(pFontEntry->maMetric) ); + + pFontEntry->maMetric.ImplInitTextLineSize( this ); + pFontEntry->maMetric.ImplInitAboveTextLineSize(); + + pFontEntry->mnLineHeight = pFontEntry->maMetric.mnAscent + pFontEntry->maMetric.mnDescent; + + if( pFontEntry->maFontSelData.mnOrientation + && !pFontEntry->maMetric.mnOrientation + && (meOutDevType != OUTDEV_PRINTER) ) + { + pFontEntry->mnOwnOrientation = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation); + pFontEntry->mnOrientation = pFontEntry->mnOwnOrientation; + } + else + pFontEntry->mnOrientation = pFontEntry->maMetric.mnOrientation; + } + } + + // enable kerning array if requested + if ( maFont.GetKerning() & KERNING_FONTSPECIFIC ) + { + // TODO: test if physical font supports kerning and disable if not + if( pFontEntry->maMetric.mbKernableFont ) + mbKerning = true; + } + else + mbKerning = false; + if ( maFont.GetKerning() & KERNING_ASIAN ) + mbKerning = true; + + // calculate EmphasisArea + mnEmphasisAscent = 0; + mnEmphasisDescent = 0; + if ( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) + { + FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont ); + long nEmphasisHeight = (pFontEntry->mnLineHeight*250)/1000; + if ( nEmphasisHeight < 1 ) + nEmphasisHeight = 1; + if ( nEmphasisMark & EMPHASISMARK_POS_BELOW ) + mnEmphasisDescent = nEmphasisHeight; + else + mnEmphasisAscent = nEmphasisHeight; + } + + // calculate text offset depending on TextAlignment + TextAlign eAlign = maFont.GetAlign(); + if ( eAlign == ALIGN_BASELINE ) + { + mnTextOffX = 0; + mnTextOffY = 0; + } + else if ( eAlign == ALIGN_TOP ) + { + mnTextOffX = 0; + mnTextOffY = +pFontEntry->maMetric.mnAscent + mnEmphasisAscent; + if ( pFontEntry->mnOrientation ) + ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation ); + } + else // eAlign == ALIGN_BOTTOM + { + mnTextOffX = 0; + mnTextOffY = -pFontEntry->maMetric.mnDescent + mnEmphasisDescent; + if ( pFontEntry->mnOrientation ) + ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation ); + } + + mbTextLines = ((maFont.GetUnderline() != UNDERLINE_NONE) && (maFont.GetUnderline() != UNDERLINE_DONTKNOW)) || + ((maFont.GetOverline() != UNDERLINE_NONE) && (maFont.GetOverline() != UNDERLINE_DONTKNOW)) || + ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW)); + mbTextSpecial = maFont.IsShadow() || maFont.IsOutline() || + (maFont.GetRelief() != RELIEF_NONE); + + // #95414# fix for OLE objects which use scale factors very creatively + if( mbMap && !aSize.Width() ) + { + int nOrigWidth = pFontEntry->maMetric.mnWidth; + float fStretch = (float)maMapRes.mnMapScNumX * maMapRes.mnMapScDenomY; + fStretch /= (float)maMapRes.mnMapScNumY * maMapRes.mnMapScDenomX; + int nNewWidth = (int)(nOrigWidth * fStretch + 0.5); + if( (nNewWidth != nOrigWidth) && (nNewWidth != 0) ) + { + Size aOrigSize = maFont.GetSize(); + const_cast<Font&>(maFont).SetSize( Size( nNewWidth, aSize.Height() ) ); + mbMap = sal_False; + mbNewFont = sal_True; + ImplNewFont(); // recurse once using stretched width + mbMap = sal_True; + const_cast<Font&>(maFont).SetSize( aOrigSize ); + } + } + + return true; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplGetTextWidth( const SalLayout& rSalLayout ) const +{ + long nWidth = rSalLayout.GetTextWidth(); + nWidth /= rSalLayout.GetUnitsPerPixel(); + return nWidth; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextRect( long nBaseX, long nBaseY, + long nDistX, long nDistY, long nWidth, long nHeight ) +{ + long nX = nDistX; + long nY = nDistY; + + short nOrientation = mpFontEntry->mnOrientation; + if ( nOrientation ) + { + // Rotate rect without rounding problems for 90 degree rotations + if ( !(nOrientation % 900) ) + { + if ( nOrientation == 900 ) + { + long nTemp = nX; + nX = nY; + nY = -nTemp; + nTemp = nWidth; + nWidth = nHeight; + nHeight = nTemp; + nY -= nHeight; + } + else if ( nOrientation == 1800 ) + { + nX = -nX; + nY = -nY; + nX -= nWidth; + nY -= nHeight; + } + else /* ( nOrientation == 2700 ) */ + { + long nTemp = nX; + nX = -nY; + nY = nTemp; + nTemp = nWidth; + nWidth = nHeight; + nHeight = nTemp; + nX -= nWidth; + } + } + else + { + nX += nBaseX; + nY += nBaseY; + // inflate because polygons are drawn smaller + Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) ); + Polygon aPoly( aRect ); + aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation ); + ImplDrawPolygon( aPoly ); + return; + } + } + + nX += nBaseX; + nY += nBaseY; + mpGraphics->DrawRect( nX, nY, nWidth, nHeight, this ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout ) +{ + const long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel(); + const Point aBase = rSalLayout.DrawBase(); + const long nX = aBase.X(); + const long nY = aBase.Y(); + + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = sal_True; + } + mpGraphics->SetFillColor( ImplColorToSal( GetTextFillColor() ) ); + mbInitFillColor = sal_True; + + ImplDrawTextRect( nX, nY, 0, -(mpFontEntry->maMetric.mnAscent + mnEmphasisAscent), + nWidth, + mpFontEntry->mnLineHeight+mnEmphasisAscent+mnEmphasisDescent ); +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::ImplGetTextBoundRect( const SalLayout& rSalLayout ) +{ + Point aPoint = rSalLayout.GetDrawPosition(); + long nX = aPoint.X(); + long nY = aPoint.Y(); + + long nWidth = rSalLayout.GetTextWidth(); + long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; + + nY -= mpFontEntry->maMetric.mnAscent + mnEmphasisAscent; + + if ( mpFontEntry->mnOrientation ) + { + long nBaseX = nX, nBaseY = nY; + if ( !(mpFontEntry->mnOrientation % 900) ) + { + long nX2 = nX+nWidth; + long nY2 = nY+nHeight; + ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation ); + ImplRotatePos( nBaseX, nBaseY, nX2, nY2, mpFontEntry->mnOrientation ); + nWidth = nX2-nX; + nHeight = nY2-nY; + } + else + { + // inflate by +1+1 because polygons are drawn smaller + Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) ); + Polygon aPoly( aRect ); + aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation ); + return aPoly.GetBoundRect(); + } + } + + return Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitTextLineSize() +{ + mpFontEntry->maMetric.ImplInitTextLineSize( this ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitAboveTextLineSize() +{ + mpFontEntry->maMetric.ImplInitAboveTextLineSize(); +} + +// ----------------------------------------------------------------------- + +ImplFontMetricData::ImplFontMetricData( const ImplFontSelectData& rFontSelData ) +: ImplFontAttributes( rFontSelData ) +{ + // initialize the members provided by the font request + mnWidth = rFontSelData.mnWidth; + mnOrientation = sal::static_int_cast<short>(rFontSelData.mnOrientation); + + // intialize the used font name + if( rFontSelData.mpFontData ) + { + maName = rFontSelData.mpFontData->maName; + maStyleName= rFontSelData.mpFontData->maStyleName; + mbDevice = rFontSelData.mpFontData->mbDevice; + mbKernableFont = true; + } + else + { + xub_StrLen nTokenPos = 0; + maName = GetNextFontToken( rFontSelData.maName, nTokenPos ); + maStyleName= rFontSelData.maStyleName; + mbDevice = false; + mbKernableFont = false; + } + + // reset metrics that are usually measured for the font instance + mnAscent = 0; + mnDescent = 0; + mnIntLeading = 0; + mnExtLeading = 0; + mnSlant = 0; + mnMinKashida = 0; + + // reset metrics that are usually derived from the measurements + mnUnderlineSize = 0; + mnUnderlineOffset = 0; + mnBUnderlineSize = 0; + mnBUnderlineOffset = 0; + mnDUnderlineSize = 0; + mnDUnderlineOffset1 = 0; + mnDUnderlineOffset2 = 0; + mnWUnderlineSize = 0; + mnWUnderlineOffset = 0; + mnAboveUnderlineSize = 0; + mnAboveUnderlineOffset = 0; + mnAboveBUnderlineSize = 0; + mnAboveBUnderlineOffset = 0; + mnAboveDUnderlineSize = 0; + mnAboveDUnderlineOffset1 = 0; + mnAboveDUnderlineOffset2 = 0; + mnAboveWUnderlineSize = 0; + mnAboveWUnderlineOffset = 0; + mnStrikeoutSize = 0; + mnStrikeoutOffset = 0; + mnBStrikeoutSize = 0; + mnBStrikeoutOffset = 0; + mnDStrikeoutSize = 0; + mnDStrikeoutOffset1 = 0; + mnDStrikeoutOffset2 = 0; +} + +// ----------------------------------------------------------------------- + +void ImplFontMetricData::ImplInitTextLineSize( const OutputDevice* pDev ) +{ + long nDescent = mnDescent; + if ( nDescent <= 0 ) + { + nDescent = mnAscent / 10; + if ( !nDescent ) + nDescent = 1; + } + + // #i55341# for some fonts it is not a good idea to calculate + // their text line metrics from the real font descent + // => work around this problem just for these fonts + if( 3*nDescent > mnAscent ) + nDescent = mnAscent / 3; + + long nLineHeight = ((nDescent*25)+50) / 100; + if ( !nLineHeight ) + nLineHeight = 1; + long nLineHeight2 = nLineHeight / 2; + if ( !nLineHeight2 ) + nLineHeight2 = 1; + + long nBLineHeight = ((nDescent*50)+50) / 100; + if ( nBLineHeight == nLineHeight ) + nBLineHeight++; + long nBLineHeight2 = nBLineHeight/2; + if ( !nBLineHeight2 ) + nBLineHeight2 = 1; + + long n2LineHeight = ((nDescent*16)+50) / 100; + if ( !n2LineHeight ) + n2LineHeight = 1; + long n2LineDY = n2LineHeight; + /* #117909# + * add some pixels to minimum double line distance on higher resolution devices + */ + long nMin2LineDY = 1 + pDev->ImplGetDPIY()/150; + if ( n2LineDY < nMin2LineDY ) + n2LineDY = nMin2LineDY; + long n2LineDY2 = n2LineDY/2; + if ( !n2LineDY2 ) + n2LineDY2 = 1; + + long nUnderlineOffset = mnDescent/2 + 1; + long nStrikeoutOffset = -((mnAscent - mnIntLeading) / 3); + + mnUnderlineSize = nLineHeight; + mnUnderlineOffset = nUnderlineOffset - nLineHeight2; + + mnBUnderlineSize = nBLineHeight; + mnBUnderlineOffset = nUnderlineOffset - nBLineHeight2; + + mnDUnderlineSize = n2LineHeight; + mnDUnderlineOffset1 = nUnderlineOffset - n2LineDY2 - n2LineHeight; + mnDUnderlineOffset2 = mnDUnderlineOffset1 + n2LineDY + n2LineHeight; + + long nWCalcSize = mnDescent; + if ( nWCalcSize < 6 ) + { + if ( (nWCalcSize == 1) || (nWCalcSize == 2) ) + mnWUnderlineSize = nWCalcSize; + else + mnWUnderlineSize = 3; + } + else + mnWUnderlineSize = ((nWCalcSize*50)+50) / 100; + + // #109280# the following line assures that wavelnes are never placed below the descent, however + // for most fonts the waveline then is drawn into the text, so we better keep the old solution + // pFontEntry->maMetric.mnWUnderlineOffset = pFontEntry->maMetric.mnDescent + 1 - pFontEntry->maMetric.mnWUnderlineSize; + mnWUnderlineOffset = nUnderlineOffset; + + mnStrikeoutSize = nLineHeight; + mnStrikeoutOffset = nStrikeoutOffset - nLineHeight2; + + mnBStrikeoutSize = nBLineHeight; + mnBStrikeoutOffset = nStrikeoutOffset - nBLineHeight2; + + mnDStrikeoutSize = n2LineHeight; + mnDStrikeoutOffset1 = nStrikeoutOffset - n2LineDY2 - n2LineHeight; + mnDStrikeoutOffset2 = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight; +} + +// ----------------------------------------------------------------------- + +void ImplFontMetricData::ImplInitAboveTextLineSize() +{ + long nIntLeading = mnIntLeading; + // TODO: assess usage of nLeading below (changed in extleading CWS) + // if no leading is available, we assume 15% of the ascent + if ( nIntLeading <= 0 ) + { + nIntLeading = mnAscent*15/100; + if ( !nIntLeading ) + nIntLeading = 1; + } + + long nLineHeight = ((nIntLeading*25)+50) / 100; + if ( !nLineHeight ) + nLineHeight = 1; + + long nBLineHeight = ((nIntLeading*50)+50) / 100; + if ( nBLineHeight == nLineHeight ) + nBLineHeight++; + + long n2LineHeight = ((nIntLeading*16)+50) / 100; + if ( !n2LineHeight ) + n2LineHeight = 1; + + long nCeiling = -mnAscent; + + mnAboveUnderlineSize = nLineHeight; + mnAboveUnderlineOffset = nCeiling + (nIntLeading - nLineHeight + 1) / 2; + + mnAboveBUnderlineSize = nBLineHeight; + mnAboveBUnderlineOffset = nCeiling + (nIntLeading - nBLineHeight + 1) / 2; + + mnAboveDUnderlineSize = n2LineHeight; + mnAboveDUnderlineOffset1 = nCeiling + (nIntLeading - 3*n2LineHeight + 1) / 2; + mnAboveDUnderlineOffset2 = nCeiling + (nIntLeading + n2LineHeight + 1) / 2; + + long nWCalcSize = nIntLeading; + if ( nWCalcSize < 6 ) + { + if ( (nWCalcSize == 1) || (nWCalcSize == 2) ) + mnAboveWUnderlineSize = nWCalcSize; + else + mnAboveWUnderlineSize = 3; + } + else + mnAboveWUnderlineSize = ((nWCalcSize*50)+50) / 100; + + mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2; +} + +// ----------------------------------------------------------------------- + +static void ImplDrawWavePixel( long nOriginX, long nOriginY, + long nCurX, long nCurY, + short nOrientation, + SalGraphics* pGraphics, + OutputDevice* pOutDev, + sal_Bool bDrawPixAsRect, + + long nPixWidth, long nPixHeight ) +{ + if ( nOrientation ) + ImplRotatePos( nOriginX, nOriginY, nCurX, nCurY, nOrientation ); + + if ( bDrawPixAsRect ) + { + + pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, pOutDev ); + } + else + { + pGraphics->DrawPixel( nCurX, nCurY, pOutDev ); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawWaveLine( long nBaseX, long nBaseY, + long nDistX, long nDistY, + long nWidth, long nHeight, + long nLineWidth, short nOrientation, + const Color& rColor ) +{ + if ( !nHeight ) + return; + + long nStartX = nBaseX + nDistX; + long nStartY = nBaseY + nDistY; + + // Bei Hoehe von 1 Pixel reicht es, eine Linie auszugeben + if ( (nLineWidth == 1) && (nHeight == 1) ) + { + mpGraphics->SetLineColor( ImplColorToSal( rColor ) ); + mbInitLineColor = sal_True; + + long nEndX = nStartX+nWidth; + long nEndY = nStartY; + if ( nOrientation ) + { + ImplRotatePos( nBaseX, nBaseY, nStartX, nStartY, nOrientation ); + ImplRotatePos( nBaseX, nBaseY, nEndX, nEndY, nOrientation ); + } + mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, this ); + } + else + { + long nCurX = nStartX; + long nCurY = nStartY; + long nDiffY = nHeight-1; + long nCount = nWidth; + long nPixWidth; + long nPixHeight; + sal_Bool bDrawPixAsRect; + // Auf Druckern die Pixel per DrawRect() ausgeben + if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) ) + { + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = sal_True; + } + mpGraphics->SetFillColor( ImplColorToSal( rColor ) ); + mbInitFillColor = sal_True; + bDrawPixAsRect = sal_True; + nPixWidth = nLineWidth; + nPixHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY; + } + else + { + mpGraphics->SetLineColor( ImplColorToSal( rColor ) ); + mbInitLineColor = sal_True; + nPixWidth = 1; + nPixHeight = 1; + bDrawPixAsRect = sal_False; + } + + if ( !nDiffY ) + { + while ( nWidth ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + nWidth--; + } + } + else + { + long nDiffX = 2; + long nOffY = -1; + long i; + nCurY += nDiffY; + long nFreq = nCount / (nDiffX+nDiffY); + while ( nFreq-- ) + { + for( i = nDiffY; i; --i ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + nCurY += nOffY; + } + for( i = nDiffX; i; --i ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + } + nOffY = -nOffY; + } + nFreq = nCount % (nDiffX+nDiffY); + if ( nFreq ) + { + for( i = nDiffY; i && nFreq; --i, --nFreq ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + nCurY += nOffY; + + } + for( i = nDiffX; i && nFreq; --i, --nFreq ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + } + } + } + + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawWaveTextLine( long nBaseX, long nBaseY, + long nDistX, long nDistY, long nWidth, + FontUnderline eTextLine, + Color aColor, + sal_Bool bIsAbove ) +{ + ImplFontEntry* pFontEntry = mpFontEntry; + long nLineHeight; + long nLinePos; + + if ( bIsAbove ) + { + nLineHeight = pFontEntry->maMetric.mnAboveWUnderlineSize; + nLinePos = pFontEntry->maMetric.mnAboveWUnderlineOffset; + } + else + { + nLineHeight = pFontEntry->maMetric.mnWUnderlineSize; + nLinePos = pFontEntry->maMetric.mnWUnderlineOffset; + } + if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) ) + nLineHeight = 3; + long nLineWidth = (mnDPIX/300); + if ( !nLineWidth ) + nLineWidth = 1; + if ( eTextLine == UNDERLINE_BOLDWAVE ) + nLineWidth *= 2; + nLinePos += nDistY - (nLineHeight / 2); + long nLineWidthHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY; + if ( eTextLine == UNDERLINE_DOUBLEWAVE ) + { + long nOrgLineHeight = nLineHeight; + nLineHeight /= 3; + if ( nLineHeight < 2 ) + { + if ( nOrgLineHeight > 1 ) + nLineHeight = 2; + else + nLineHeight = 1; + } + long nLineDY = nOrgLineHeight-(nLineHeight*2); + if ( nLineDY < nLineWidthHeight ) + nLineDY = nLineWidthHeight; + long nLineDY2 = nLineDY/2; + if ( !nLineDY2 ) + nLineDY2 = 1; + + nLinePos -= nLineWidthHeight-nLineDY2; + ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, + nLineWidth, mpFontEntry->mnOrientation, aColor ); + nLinePos += nLineWidthHeight+nLineDY; + ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, + nLineWidth, mpFontEntry->mnOrientation, aColor ); + } + else + { + nLinePos -= nLineWidthHeight/2; + ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, + nLineWidth, mpFontEntry->mnOrientation, aColor ); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawStraightTextLine( long nBaseX, long nBaseY, + long nDistX, long nDistY, long nWidth, + FontUnderline eTextLine, + Color aColor, + sal_Bool bIsAbove ) +{ + ImplFontEntry* pFontEntry = mpFontEntry; + long nLineHeight = 0; + long nLinePos = 0; + long nLinePos2 = 0; + + const long nY = nDistY; + + if ( eTextLine > UNDERLINE_LAST ) + eTextLine = UNDERLINE_SINGLE; + + switch ( eTextLine ) + { + case UNDERLINE_SINGLE: + case UNDERLINE_DOTTED: + case UNDERLINE_DASH: + case UNDERLINE_LONGDASH: + case UNDERLINE_DASHDOT: + case UNDERLINE_DASHDOTDOT: + if ( bIsAbove ) + { + nLineHeight = pFontEntry->maMetric.mnAboveUnderlineSize; + nLinePos = nY + pFontEntry->maMetric.mnAboveUnderlineOffset; + } + else + { + nLineHeight = pFontEntry->maMetric.mnUnderlineSize; + nLinePos = nY + pFontEntry->maMetric.mnUnderlineOffset; + } + break; + case UNDERLINE_BOLD: + case UNDERLINE_BOLDDOTTED: + case UNDERLINE_BOLDDASH: + case UNDERLINE_BOLDLONGDASH: + case UNDERLINE_BOLDDASHDOT: + case UNDERLINE_BOLDDASHDOTDOT: + if ( bIsAbove ) + { + nLineHeight = pFontEntry->maMetric.mnAboveBUnderlineSize; + nLinePos = nY + pFontEntry->maMetric.mnAboveBUnderlineOffset; + } + else + { + nLineHeight = pFontEntry->maMetric.mnBUnderlineSize; + nLinePos = nY + pFontEntry->maMetric.mnBUnderlineOffset; + } + break; + case UNDERLINE_DOUBLE: + if ( bIsAbove ) + { + nLineHeight = pFontEntry->maMetric.mnAboveDUnderlineSize; + nLinePos = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset1; + nLinePos2 = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset2; + } + else + { + nLineHeight = pFontEntry->maMetric.mnDUnderlineSize; + nLinePos = nY + pFontEntry->maMetric.mnDUnderlineOffset1; + nLinePos2 = nY + pFontEntry->maMetric.mnDUnderlineOffset2; + } + break; + default: + break; + } + + if ( nLineHeight ) + { + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = sal_True; + } + mpGraphics->SetFillColor( ImplColorToSal( aColor ) ); + mbInitFillColor = sal_True; + + long nLeft = nDistX; + + switch ( eTextLine ) + { + case UNDERLINE_SINGLE: + case UNDERLINE_BOLD: + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + break; + case UNDERLINE_DOUBLE: + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); + break; + case UNDERLINE_DOTTED: + case UNDERLINE_BOLDDOTTED: + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + long nTempWidth = nDotWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempWidth > nEnd ) + nTempWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); + nLeft += nDotWidth*2; + } + } + break; + case UNDERLINE_DASH: + case UNDERLINE_LONGDASH: + case UNDERLINE_BOLDDASH: + case UNDERLINE_BOLDLONGDASH: + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + long nMinDashWidth; + long nMinSpaceWidth; + long nSpaceWidth; + long nDashWidth; + if ( (eTextLine == UNDERLINE_LONGDASH) || + (eTextLine == UNDERLINE_BOLDLONGDASH) ) + { + nMinDashWidth = nDotWidth*6; + nMinSpaceWidth = nDotWidth*2; + nDashWidth = 200; + nSpaceWidth = 100; + } + else + { + nMinDashWidth = nDotWidth*4; + nMinSpaceWidth = (nDotWidth*150)/100; + nDashWidth = 100; + nSpaceWidth = 50; + } + nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540; + nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540; + // DashWidth wird gegebenenfalls verbreitert, wenn + // die dicke der Linie im Verhaeltnis zur Laenge + // zu dick wird + if ( nDashWidth < nMinDashWidth ) + nDashWidth = nMinDashWidth; + if ( nSpaceWidth < nMinSpaceWidth ) + nSpaceWidth = nMinSpaceWidth; + long nTempWidth = nDashWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempWidth > nEnd ) + nTempWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); + nLeft += nDashWidth+nSpaceWidth; + } + } + break; + case UNDERLINE_DASHDOT: + case UNDERLINE_BOLDDASHDOT: + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + long nDashWidth = ((100*mnDPIX)+1270)/2540; + long nMinDashWidth = nDotWidth*4; + // DashWidth wird gegebenenfalls verbreitert, wenn + // die dicke der Linie im Verhaeltnis zur Laenge + // zu dick wird + if ( nDashWidth < nMinDashWidth ) + nDashWidth = nMinDashWidth; + long nTempDotWidth = nDotWidth; + long nTempDashWidth = nDashWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempDotWidth > nEnd ) + nTempDotWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); + nLeft += nDotWidth*2; + if ( nLeft > nEnd ) + break; + if ( nLeft+nTempDashWidth > nEnd ) + nTempDashWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); + nLeft += nDashWidth+nDotWidth; + } + } + break; + case UNDERLINE_DASHDOTDOT: + case UNDERLINE_BOLDDASHDOTDOT: + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + long nDashWidth = ((100*mnDPIX)+1270)/2540; + long nMinDashWidth = nDotWidth*4; + // DashWidth wird gegebenenfalls verbreitert, wenn + // die dicke der Linie im Verhaeltnis zur Laenge + // zu dick wird + if ( nDashWidth < nMinDashWidth ) + nDashWidth = nMinDashWidth; + long nTempDotWidth = nDotWidth; + long nTempDashWidth = nDashWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempDotWidth > nEnd ) + nTempDotWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); + nLeft += nDotWidth*2; + if ( nLeft > nEnd ) + break; + if ( nLeft+nTempDotWidth > nEnd ) + nTempDotWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); + nLeft += nDotWidth*2; + if ( nLeft > nEnd ) + break; + if ( nLeft+nTempDashWidth > nEnd ) + nTempDashWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); + nLeft += nDashWidth+nDotWidth; + } + } + break; + default: + break; + } + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawStrikeoutLine( long nBaseX, long nBaseY, + long nDistX, long nDistY, long nWidth, + FontStrikeout eStrikeout, + Color aColor ) +{ + ImplFontEntry* pFontEntry = mpFontEntry; + long nLineHeight = 0; + long nLinePos = 0; + long nLinePos2 = 0; + + long nY = nDistY; + + if ( eStrikeout > STRIKEOUT_LAST ) + eStrikeout = STRIKEOUT_SINGLE; + + switch ( eStrikeout ) + { + case STRIKEOUT_SINGLE: + nLineHeight = pFontEntry->maMetric.mnStrikeoutSize; + nLinePos = nY + pFontEntry->maMetric.mnStrikeoutOffset; + break; + case STRIKEOUT_BOLD: + nLineHeight = pFontEntry->maMetric.mnBStrikeoutSize; + nLinePos = nY + pFontEntry->maMetric.mnBStrikeoutOffset; + break; + case STRIKEOUT_DOUBLE: + nLineHeight = pFontEntry->maMetric.mnDStrikeoutSize; + nLinePos = nY + pFontEntry->maMetric.mnDStrikeoutOffset1; + nLinePos2 = nY + pFontEntry->maMetric.mnDStrikeoutOffset2; + break; + default: + break; + } + + if ( nLineHeight ) + { + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = sal_True; + } + mpGraphics->SetFillColor( ImplColorToSal( aColor ) ); + mbInitFillColor = sal_True; + + const long& nLeft = nDistX; + + switch ( eStrikeout ) + { + case STRIKEOUT_SINGLE: + case STRIKEOUT_BOLD: + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + break; + case STRIKEOUT_DOUBLE: + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); + break; + default: + break; + } + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawStrikeoutChar( long nBaseX, long nBaseY, + long nDistX, long nDistY, long nWidth, + FontStrikeout eStrikeout, + Color aColor ) +{ + //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need + //to tweak this + if (!nWidth) + return; + + // PDF-export does its own strikeout drawing... why again? + if( mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) + return; + + // prepare string for strikeout measurement + static char cStrikeoutChar; + if ( eStrikeout == STRIKEOUT_SLASH ) + cStrikeoutChar = '/'; + else // ( eStrikeout == STRIKEOUT_X ) + cStrikeoutChar = 'X'; + static const int nTestStrLen = 4; + static const int nMaxStrikeStrLen = 2048; + xub_Unicode aChars[nMaxStrikeStrLen+1]; // +1 for valgrind... + for( int i = 0; i < nTestStrLen; ++i) + aChars[i] = cStrikeoutChar; + const String aStrikeoutTest( aChars, nTestStrLen ); + + // calculate approximation of strikeout atom size + long nStrikeoutWidth = 0; + SalLayout* pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen ); + if( pLayout ) + { + nStrikeoutWidth = pLayout->GetTextWidth() / (nTestStrLen * pLayout->GetUnitsPerPixel()); + pLayout->Release(); + } + if( nStrikeoutWidth <= 0 ) // sanity check + return; + + int nStrikeStrLen = (nWidth+(nStrikeoutWidth-1)) / nStrikeoutWidth; + if( nStrikeStrLen > nMaxStrikeStrLen ) + nStrikeStrLen = nMaxStrikeStrLen; + + // build the strikeout string + for( int i = nTestStrLen; i < nStrikeStrLen; ++i) + aChars[i] = cStrikeoutChar; + const String aStrikeoutText( aChars, xub_StrLen(nStrikeStrLen) ); + + if( mpFontEntry->mnOrientation ) + ImplRotatePos( 0, 0, nDistX, nDistY, mpFontEntry->mnOrientation ); + nBaseX += nDistX; + nBaseY += nDistY; + + // strikeout text has to be left aligned + sal_uLong nOrigTLM = mnTextLayoutMode; + mnTextLayoutMode = TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_COMPLEX_DISABLED; + pLayout = ImplLayout( aStrikeoutText, 0, STRING_LEN ); + mnTextLayoutMode = nOrigTLM; + + if( !pLayout ) + return; + + // draw the strikeout text + const Color aOldColor = GetTextColor(); + SetTextColor( aColor ); + ImplInitTextColor(); + + pLayout->DrawBase() = Point( nBaseX+mnTextOffX, nBaseY+mnTextOffY ); + + Rectangle aPixelRect; + aPixelRect.nLeft = nBaseX+mnTextOffX; + aPixelRect.nRight = aPixelRect.nLeft+nWidth; + aPixelRect.nBottom = nBaseY+mpFontEntry->maMetric.mnDescent; + aPixelRect.nTop = nBaseY-mpFontEntry->maMetric.mnAscent; + + if (mpFontEntry->mnOrientation) + { + Polygon aPoly( aPixelRect ); + aPoly.Rotate( Point(nBaseX+mnTextOffX, nBaseY+mnTextOffY), mpFontEntry->mnOrientation); + aPixelRect = aPoly.GetBoundRect(); + } + + Push( PUSH_CLIPREGION ); + IntersectClipRegion( PixelToLogic(aPixelRect) ); + if( mbInitClipRegion ) + ImplInitClipRegion(); + + pLayout->DrawText( *mpGraphics ); + + pLayout->Release(); + Pop(); + + SetTextColor( aOldColor ); + ImplInitTextColor(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextLine( long nX, long nY, + long nDistX, long nWidth, + FontStrikeout eStrikeout, + FontUnderline eUnderline, + FontUnderline eOverline, + sal_Bool bUnderlineAbove ) +{ + if ( !nWidth ) + return; + + Color aStrikeoutColor = GetTextColor(); + Color aUnderlineColor = GetTextLineColor(); + Color aOverlineColor = GetOverlineColor(); + sal_Bool bStrikeoutDone = sal_False; + sal_Bool bUnderlineDone = sal_False; + sal_Bool bOverlineDone = sal_False; + + if ( IsRTLEnabled() ) + { + // --- RTL --- mirror at basex + long nXAdd = nWidth - nDistX; + if( mpFontEntry->mnOrientation ) + nXAdd = FRound( nXAdd * cos( mpFontEntry->mnOrientation * F_PI1800 ) ); + nX += nXAdd - 1; + } + + if ( !IsTextLineColor() ) + aUnderlineColor = GetTextColor(); + + if ( !IsOverlineColor() ) + aOverlineColor = GetTextColor(); + + if ( (eUnderline == UNDERLINE_SMALLWAVE) || + (eUnderline == UNDERLINE_WAVE) || + (eUnderline == UNDERLINE_DOUBLEWAVE) || + (eUnderline == UNDERLINE_BOLDWAVE) ) + { + ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); + bUnderlineDone = sal_True; + } + if ( (eOverline == UNDERLINE_SMALLWAVE) || + (eOverline == UNDERLINE_WAVE) || + (eOverline == UNDERLINE_DOUBLEWAVE) || + (eOverline == UNDERLINE_BOLDWAVE) ) + { + ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, sal_True ); + bOverlineDone = sal_True; + } + + if ( (eStrikeout == STRIKEOUT_SLASH) || + (eStrikeout == STRIKEOUT_X) ) + { + ImplDrawStrikeoutChar( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor ); + bStrikeoutDone = sal_True; + } + + if ( !bUnderlineDone ) + ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); + + if ( !bOverlineDone ) + ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, sal_True ); + + if ( !bStrikeoutDone ) + ImplDrawStrikeoutLine( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, + FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, sal_Bool bWordLine, sal_Bool bUnderlineAbove ) +{ + if( bWordLine ) + { + // draw everything relative to the layout base point + const Point aStartPt = rSalLayout.DrawBase(); + // calculate distance of each word from the base point + Point aPos; + sal_Int32 nDist = 0, nWidth = 0, nAdvance=0; + for( int nStart = 0;;) + { + // iterate through the layouted glyphs + sal_GlyphId nGlyphIndex; + if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) + break; + + // calculate the boundaries of each word + if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) ) + { + if( !nWidth ) + { + // get the distance to the base point (as projected to baseline) + nDist = aPos.X() - aStartPt.X(); + if( mpFontEntry->mnOrientation ) + { + const long nDY = aPos.Y() - aStartPt.Y(); + const double fRad = mpFontEntry->mnOrientation * F_PI1800; + nDist = FRound( nDist*cos(fRad) - nDY*sin(fRad) ); + } + } + + // update the length of the textline + nWidth += nAdvance; + } + else if( nWidth > 0 ) + { + // draw the textline for each word + ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth, + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + nWidth = 0; + } + } + + // draw textline for the last word + if( nWidth > 0 ) + { + ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth, + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + } + } + else + { + Point aStartPt = rSalLayout.GetDrawPosition(); + int nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel(); + ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), 0, nWidth, + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawMnemonicLine( long nX, long nY, long nWidth ) +{ + long nBaseX = nX; + if( /*ImplHasMirroredGraphics() &&*/ IsRTLEnabled() ) + { + // --- RTL --- + // add some strange offset + nX += 2; + // revert the hack that will be done later in ImplDrawTextLine + nX = nBaseX - nWidth - (nX - nBaseX - 1); + } + + ImplDrawTextLine( nX, nY, 0, nWidth, STRIKEOUT_NONE, UNDERLINE_SINGLE, UNDERLINE_NONE, sal_False ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplGetEmphasisMark( PolyPolygon& rPolyPoly, sal_Bool& rPolyLine, + Rectangle& rRect1, Rectangle& rRect2, + long& rYOff, long& rWidth, + FontEmphasisMark eEmphasis, + long nHeight, short /*nOrient*/ ) +{ + static const sal_uInt8 aAccentPolyFlags[24] = + { + 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 0, 2, 0, 2, 2 + }; + + static const long aAccentPos[48] = + { + 78, 0, + 348, 79, + 599, 235, + 843, 469, + 938, 574, + 990, 669, + 990, 773, + 990, 843, + 964, 895, + 921, 947, + 886, 982, + 860, 999, + 825, 999, + 764, 999, + 721, 964, + 686, 895, + 625, 791, + 556, 660, + 469, 504, + 400, 400, + 261, 252, + 61, 61, + 0, 27, + 9, 0 + }; + + rWidth = 0; + rYOff = 0; + rPolyLine = sal_False; + + if ( !nHeight ) + return; + + FontEmphasisMark nEmphasisStyle = eEmphasis & EMPHASISMARK_STYLE; + long nDotSize = 0; + switch ( nEmphasisStyle ) + { + case EMPHASISMARK_DOT: + // Dot has 55% of the height + nDotSize = (nHeight*550)/1000; + if ( !nDotSize ) + nDotSize = 1; + if ( nDotSize <= 2 ) + rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) ); + else + { + long nRad = nDotSize/2; + Polygon aPoly( Point( nRad, nRad ), nRad, nRad ); + rPolyPoly.Insert( aPoly ); + } + rYOff = ((nHeight*250)/1000)/2; // Center to the anthoer EmphasisMarks + rWidth = nDotSize; + break; + + case EMPHASISMARK_CIRCLE: + // Dot has 80% of the height + nDotSize = (nHeight*800)/1000; + if ( !nDotSize ) + nDotSize = 1; + if ( nDotSize <= 2 ) + rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) ); + else + { + long nRad = nDotSize/2; + Polygon aPoly( Point( nRad, nRad ), nRad, nRad ); + rPolyPoly.Insert( aPoly ); + // BorderWidth is 15% + long nBorder = (nDotSize*150)/1000; + if ( nBorder <= 1 ) + rPolyLine = sal_True; + else + { + Polygon aPoly2( Point( nRad, nRad ), + nRad-nBorder, nRad-nBorder ); + rPolyPoly.Insert( aPoly2 ); + } + } + rWidth = nDotSize; + break; + + case EMPHASISMARK_DISC: + // Dot has 80% of the height + nDotSize = (nHeight*800)/1000; + if ( !nDotSize ) + nDotSize = 1; + if ( nDotSize <= 2 ) + rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) ); + else + { + long nRad = nDotSize/2; + Polygon aPoly( Point( nRad, nRad ), nRad, nRad ); + rPolyPoly.Insert( aPoly ); + } + rWidth = nDotSize; + break; + + case EMPHASISMARK_ACCENT: + // Dot has 80% of the height + nDotSize = (nHeight*800)/1000; + if ( !nDotSize ) + nDotSize = 1; + if ( nDotSize <= 2 ) + { + if ( nDotSize == 1 ) + { + rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) ); + rWidth = nDotSize; + } + else + { + rRect1 = Rectangle( Point(), Size( 1, 1 ) ); + rRect2 = Rectangle( Point( 1, 1 ), Size( 1, 1 ) ); + } + } + else + { + Polygon aPoly( sizeof( aAccentPos ) / sizeof( long ) / 2, + (const Point*)aAccentPos, + aAccentPolyFlags ); + double dScale = ((double)nDotSize)/1000.0; + aPoly.Scale( dScale, dScale ); + Polygon aTemp; + aPoly.AdaptiveSubdivide( aTemp ); + Rectangle aBoundRect = aTemp.GetBoundRect(); + rWidth = aBoundRect.GetWidth(); + nDotSize = aBoundRect.GetHeight(); + rPolyPoly.Insert( aTemp ); + } + break; + } + + // calculate position + long nOffY = 1+(mnDPIY/300); // one visible pixel space + long nSpaceY = nHeight-nDotSize; + if ( nSpaceY >= nOffY*2 ) + rYOff += nOffY; + if ( !(eEmphasis & EMPHASISMARK_POS_BELOW) ) + rYOff += nDotSize; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawEmphasisMark( long nBaseX, long nX, long nY, + const PolyPolygon& rPolyPoly, sal_Bool bPolyLine, + const Rectangle& rRect1, const Rectangle& rRect2 ) +{ + // TODO: pass nWidth as width of this mark + long nWidth = 0; + + if( IsRTLEnabled() ) + // --- RTL --- mirror at basex + nX = nBaseX - nWidth - (nX - nBaseX - 1); + + nX -= mnOutOffX; + nY -= mnOutOffY; + + if ( rPolyPoly.Count() ) + { + if ( bPolyLine ) + { + Polygon aPoly = rPolyPoly.GetObject( 0 ); + aPoly.Move( nX, nY ); + DrawPolyLine( aPoly ); + } + else + { + PolyPolygon aPolyPoly = rPolyPoly; + aPolyPoly.Move( nX, nY ); + DrawPolyPolygon( aPolyPoly ); + } + } + + if ( !rRect1.IsEmpty() ) + { + Rectangle aRect( Point( nX+rRect1.Left(), + nY+rRect1.Top() ), rRect1.GetSize() ); + DrawRect( aRect ); + } + + if ( !rRect2.IsEmpty() ) + { + Rectangle aRect( Point( nX+rRect2.Left(), + nY+rRect2.Top() ), rRect2.GetSize() ); + + DrawRect( aRect ); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout ) +{ + Color aOldLineColor = GetLineColor(); + Color aOldFillColor = GetFillColor(); + sal_Bool bOldMap = mbMap; + GDIMetaFile* pOldMetaFile = mpMetaFile; + mpMetaFile = NULL; + EnableMapMode( sal_False ); + + FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont ); + PolyPolygon aPolyPoly; + Rectangle aRect1; + Rectangle aRect2; + long nEmphasisYOff; + long nEmphasisWidth; + long nEmphasisHeight; + sal_Bool bPolyLine; + + if ( nEmphasisMark & EMPHASISMARK_POS_BELOW ) + nEmphasisHeight = mnEmphasisDescent; + else + nEmphasisHeight = mnEmphasisAscent; + + ImplGetEmphasisMark( aPolyPoly, bPolyLine, + aRect1, aRect2, + nEmphasisYOff, nEmphasisWidth, + nEmphasisMark, + nEmphasisHeight, mpFontEntry->mnOrientation ); + + if ( bPolyLine ) + { + SetLineColor( GetTextColor() ); + SetFillColor(); + } + else + { + SetLineColor(); + SetFillColor( GetTextColor() ); + } + + Point aOffset = Point(0,0); + + if ( nEmphasisMark & EMPHASISMARK_POS_BELOW ) + aOffset.Y() += mpFontEntry->maMetric.mnDescent + nEmphasisYOff; + else + aOffset.Y() -= mpFontEntry->maMetric.mnAscent + nEmphasisYOff; + + long nEmphasisWidth2 = nEmphasisWidth / 2; + long nEmphasisHeight2 = nEmphasisHeight / 2; + aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 ); + + Point aOutPoint; + Rectangle aRectangle; + for( int nStart = 0;;) + { + sal_GlyphId nGlyphIndex; + if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aOutPoint, nStart ) ) + break; + + if( !mpGraphics->GetGlyphBoundRect( nGlyphIndex, aRectangle ) ) + continue; + + if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) ) + { + Point aAdjPoint = aOffset; + aAdjPoint.X() += aRectangle.Left() + (aRectangle.GetWidth() - nEmphasisWidth) / 2; + if ( mpFontEntry->mnOrientation ) + ImplRotatePos( 0, 0, aAdjPoint.X(), aAdjPoint.Y(), mpFontEntry->mnOrientation ); + aOutPoint += aAdjPoint; + aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 ); + ImplDrawEmphasisMark( rSalLayout.DrawBase().X(), + aOutPoint.X(), aOutPoint.Y(), + aPolyPoly, bPolyLine, aRect1, aRect2 ); + } + } + + SetLineColor( aOldLineColor ); + SetFillColor( aOldFillColor ); + EnableMapMode( bOldMap ); + mpMetaFile = pOldMetaFile; +} + +// ----------------------------------------------------------------------- + +bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout ) +{ + int nX = rSalLayout.DrawBase().X(); + int nY = rSalLayout.DrawBase().Y(); + + Rectangle aBoundRect; + rSalLayout.DrawBase() = Point( 0, 0 ); + rSalLayout.DrawOffset() = Point( 0, 0 ); + if( !rSalLayout.GetBoundRect( *mpGraphics, aBoundRect ) ) + { + // guess vertical text extents if GetBoundRect failed + int nRight = rSalLayout.GetTextWidth(); + int nTop = mpFontEntry->maMetric.mnAscent + mnEmphasisAscent; + long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; + aBoundRect = Rectangle( 0, -nTop, nRight, nHeight - nTop ); + } + + // cache virtual device for rotation + if ( !mpOutDevData ) + ImplInitOutDevData(); + if ( !mpOutDevData->mpRotateDev ) + mpOutDevData->mpRotateDev = new VirtualDevice( *this, 1 ); + VirtualDevice* pVDev = mpOutDevData->mpRotateDev; + + // size it accordingly + if( !pVDev->SetOutputSizePixel( aBoundRect.GetSize() ) ) + return false; + + Font aFont( GetFont() ); + aFont.SetOrientation( 0 ); + aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) ); + pVDev->SetFont( aFont ); + pVDev->SetTextColor( Color( COL_BLACK ) ); + pVDev->SetTextFillColor(); + pVDev->ImplNewFont(); + pVDev->ImplInitFont(); + pVDev->ImplInitTextColor(); + + // draw text into upper left corner + rSalLayout.DrawBase() -= aBoundRect.TopLeft(); + rSalLayout.DrawText( *((OutputDevice*)pVDev)->mpGraphics ); + + Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() ); + if ( !aBmp || !aBmp.Rotate( mpFontEntry->mnOwnOrientation, COL_WHITE ) ) + return false; + + // calculate rotation offset + Polygon aPoly( aBoundRect ); + aPoly.Rotate( Point(), mpFontEntry->mnOwnOrientation ); + Point aPoint = aPoly.GetBoundRect().TopLeft(); + aPoint += Point( nX, nY ); + + // mask output with text colored bitmap + GDIMetaFile* pOldMetaFile = mpMetaFile; + long nOldOffX = mnOutOffX; + long nOldOffY = mnOutOffY; + sal_Bool bOldMap = mbMap; + + mnOutOffX = 0L; + mnOutOffY = 0L; + mpMetaFile = NULL; + EnableMapMode( sal_False ); + + DrawMask( aPoint, aBmp, GetTextColor() ); + + EnableMapMode( bOldMap ); + mnOutOffX = nOldOffX; + mnOutOffY = nOldOffY; + mpMetaFile = pOldMetaFile; + + return true; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout, sal_Bool bTextLines ) +{ + if( mpFontEntry->mnOwnOrientation ) + if( ImplDrawRotateText( rSalLayout ) ) + return; + + long nOldX = rSalLayout.DrawBase().X(); + if( ! (mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) ) + { + if( ImplHasMirroredGraphics() ) + { + long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth(); + long x = rSalLayout.DrawBase().X(); + rSalLayout.DrawBase().X() = w - 1 - x; + if( !IsRTLEnabled() ) + { + OutputDevice *pOutDevRef = (OutputDevice *)this; + // mirror this window back + long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX + rSalLayout.DrawBase().X() = devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) ) ; + } + } + else if( IsRTLEnabled() ) + { + //long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth(); + //long x = rSalLayout.DrawBase().X(); + OutputDevice *pOutDevRef = (OutputDevice *)this; + // mirror this window back + long devX = pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX + rSalLayout.DrawBase().X() = pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) + devX; + } + + rSalLayout.DrawText( *mpGraphics ); + } + + rSalLayout.DrawBase().X() = nOldX; + + if( bTextLines ) + ImplDrawTextLines( rSalLayout, + maFont.GetStrikeout(), maFont.GetUnderline(), maFont.GetOverline(), + maFont.IsWordLineMode(), ImplIsUnderlineAbove( maFont ) ); + + // emphasis marks + if( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) + ImplDrawEmphasisMarks( rSalLayout ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout ) +{ + Color aOldColor = GetTextColor(); + Color aOldTextLineColor = GetTextLineColor(); + Color aOldOverlineColor = GetOverlineColor(); + FontRelief eRelief = maFont.GetRelief(); + + Point aOrigPos = rSalLayout.DrawBase(); + if ( eRelief != RELIEF_NONE ) + { + Color aReliefColor( COL_LIGHTGRAY ); + Color aTextColor( aOldColor ); + + Color aTextLineColor( aOldTextLineColor ); + Color aOverlineColor( aOldOverlineColor ); + + // we don't have a automatic color, so black is always drawn on white + if ( aTextColor.GetColor() == COL_BLACK ) + aTextColor = Color( COL_WHITE ); + if ( aTextLineColor.GetColor() == COL_BLACK ) + aTextLineColor = Color( COL_WHITE ); + if ( aOverlineColor.GetColor() == COL_BLACK ) + aOverlineColor = Color( COL_WHITE ); + + // relief-color is black for white text, in all other cases + // we set this to LightGray + if ( aTextColor.GetColor() == COL_WHITE ) + aReliefColor = Color( COL_BLACK ); + SetTextLineColor( aReliefColor ); + SetOverlineColor( aReliefColor ); + SetTextColor( aReliefColor ); + ImplInitTextColor(); + + // calculate offset - for high resolution printers the offset + // should be greater so that the effect is visible + long nOff = 1; + nOff += mnDPIX/300; + + if ( eRelief == RELIEF_ENGRAVED ) + nOff = -nOff; + rSalLayout.DrawOffset() += Point( nOff, nOff); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawOffset() -= Point( nOff, nOff); + + SetTextLineColor( aTextLineColor ); + SetOverlineColor( aOverlineColor ); + SetTextColor( aTextColor ); + ImplInitTextColor(); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + + SetTextLineColor( aOldTextLineColor ); + SetOverlineColor( aOldOverlineColor ); + + if ( aTextColor != aOldColor ) + { + SetTextColor( aOldColor ); + ImplInitTextColor(); + } + } + else + { + if ( maFont.IsShadow() ) + { + long nOff = 1 + ((mpFontEntry->mnLineHeight-24)/24); + if ( maFont.IsOutline() ) + nOff++; + SetTextLineColor(); + SetOverlineColor(); + if ( (GetTextColor().GetColor() == COL_BLACK) + || (GetTextColor().GetLuminance() < 8) ) + SetTextColor( Color( COL_LIGHTGRAY ) ); + else + SetTextColor( Color( COL_BLACK ) ); + ImplInitTextColor(); + rSalLayout.DrawBase() += Point( nOff, nOff ); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() -= Point( nOff, nOff ); + SetTextColor( aOldColor ); + SetTextLineColor( aOldTextLineColor ); + SetOverlineColor( aOldOverlineColor ); + ImplInitTextColor(); + + if ( !maFont.IsOutline() ) + ImplDrawTextDirect( rSalLayout, mbTextLines ); + } + + if ( maFont.IsOutline() ) + { + rSalLayout.DrawBase() = aOrigPos + Point(-1,-1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() = aOrigPos + Point(+1,+1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() = aOrigPos + Point(-1,+0); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() = aOrigPos + Point(-1,+1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() = aOrigPos + Point(+0,+1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() = aOrigPos + Point(+0,-1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() = aOrigPos + Point(+1,-1); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() = aOrigPos + Point(+1,+0); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + rSalLayout.DrawBase() = aOrigPos; + + SetTextColor( Color( COL_WHITE ) ); + SetTextLineColor( Color( COL_WHITE ) ); + SetOverlineColor( Color( COL_WHITE ) ); + ImplInitTextColor(); + ImplDrawTextDirect( rSalLayout, mbTextLines ); + SetTextColor( aOldColor ); + SetTextLineColor( aOldTextLineColor ); + SetOverlineColor( aOldOverlineColor ); + ImplInitTextColor(); + } + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawText( SalLayout& rSalLayout ) +{ + if( mbInitClipRegion ) + ImplInitClipRegion(); + if( mbOutputClipped ) + return; + if( mbInitTextColor ) + ImplInitTextColor(); + + rSalLayout.DrawBase() += Point( mnTextOffX, mnTextOffY ); + + if( IsTextFillColor() ) + ImplDrawTextBackground( rSalLayout ); + + if( mbTextSpecial ) + ImplDrawSpecialText( rSalLayout ); + else + ImplDrawTextDirect( rSalLayout, mbTextLines ); +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo, + long nWidth, const XubString& rStr, + sal_uInt16 nStyle, const ::vcl::ITextLayout& _rLayout ) +{ + DBG_ASSERTWARNING( nWidth >= 0, "ImplGetTextLines: nWidth <= 0!" ); + + if ( nWidth <= 0 ) + nWidth = 1; + + long nMaxLineWidth = 0; + rLineInfo.Clear(); + if ( rStr.Len() && (nWidth > 0) ) + { + ::rtl::OUString aText( rStr ); + uno::Reference < i18n::XBreakIterator > xBI; + // get service provider + uno::Reference< lang::XMultiServiceFactory > xSMgr( unohelper::GetMultiServiceFactory() ); + + uno::Reference< linguistic2::XHyphenator > xHyph; + if( xSMgr.is() ) + { + uno::Reference< linguistic2::XLinguServiceManager> xLinguMgr(xSMgr->createInstance(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.linguistic2.LinguServiceManager"))),uno::UNO_QUERY); + if ( xLinguMgr.is() ) + { + xHyph = xLinguMgr->getHyphenator(); + } + } + + i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, uno::Sequence <beans::PropertyValue>(), 1 ); + i18n::LineBreakUserOptions aUserOptions; + + xub_StrLen nPos = 0; + xub_StrLen nLen = rStr.Len(); + while ( nPos < nLen ) + { + xub_StrLen nBreakPos = nPos; + + while ( ( nBreakPos < nLen ) && ( rStr.GetChar( nBreakPos ) != _CR ) && ( rStr.GetChar( nBreakPos ) != _LF ) ) + nBreakPos++; + + long nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); + if ( ( nLineWidth > nWidth ) && ( nStyle & TEXT_DRAW_WORDBREAK ) ) + { + if ( !xBI.is() ) + xBI = vcl::unohelper::CreateBreakIterator(); + + if ( xBI.is() ) + { + const com::sun::star::lang::Locale& rDefLocale(Application::GetSettings().GetUILocale()); + xub_StrLen nSoftBreak = _rLayout.GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos ); + DBG_ASSERT( nSoftBreak < nBreakPos, "Break?!" ); + //aHyphOptions.hyphenIndex = nSoftBreak; + i18n::LineBreakResults aLBR = xBI->getLineBreak( aText, nSoftBreak, rDefLocale, nPos, aHyphOptions, aUserOptions ); + nBreakPos = (xub_StrLen)aLBR.breakIndex; + if ( nBreakPos <= nPos ) + nBreakPos = nSoftBreak; + if ( (nStyle & TEXT_DRAW_WORDBREAK_HYPHENATION) == TEXT_DRAW_WORDBREAK_HYPHENATION ) + { + // Egal ob Trenner oder nicht: Das Wort nach dem Trenner durch + // die Silbentrennung jagen... + // nMaxBreakPos ist das letzte Zeichen was in die Zeile passt, + // nBreakPos ist der Wort-Anfang + // Ein Problem gibt es, wenn das Dok so schmal ist, dass ein Wort + // auf mehr als Zwei Zeilen gebrochen wird... + if ( xHyph.is() ) + { + i18n::Boundary aBoundary = xBI->getWordBoundary( aText, nBreakPos, rDefLocale, ::com::sun::star::i18n::WordType::DICTIONARY_WORD, sal_True ); + // sal_uInt16 nWordStart = nBreakPos; + // sal_uInt16 nBreakPos_OLD = nBreakPos; + sal_uInt16 nWordStart = nPos; + sal_uInt16 nWordEnd = (sal_uInt16) aBoundary.endPos; + DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" ); + + sal_uInt16 nWordLen = nWordEnd - nWordStart; + if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) ) + { + // #104415# May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD + // DBG_ASSERT( nWordEnd >= nMaxBreakPos, "Hyph: Break?" ); + String aWord( aText, nWordStart, nWordLen ); + sal_uInt16 nMinTrail = static_cast<sal_uInt16>(nWordEnd-nSoftBreak+1); //+1: Vor dem angeknacksten Buchstaben + uno::Reference< linguistic2::XHyphenatedWord > xHyphWord; + if (xHyph.is()) + xHyphWord = xHyph->hyphenate( aWord, rDefLocale, aWord.Len() - nMinTrail, uno::Sequence< beans::PropertyValue >() ); + if (xHyphWord.is()) + { + sal_Bool bAlternate = xHyphWord->isAlternativeSpelling(); + sal_uInt16 _nWordLen = 1 + xHyphWord->getHyphenPos(); + + if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= ( 2 ) ) ) + { + if ( !bAlternate ) + { + nBreakPos = nWordStart + _nWordLen; + } + else + { + String aAlt( xHyphWord->getHyphenatedWord() ); + + // Wir gehen von zwei Faellen aus, die nun + // vorliegen koennen: + // 1) packen wird zu pak-ken + // 2) Schiffahrt wird zu Schiff-fahrt + // In Fall 1 muss ein Zeichen ersetzt werden, + // in Fall 2 wird ein Zeichen hinzugefuegt. + // Die Identifikation wird erschwert durch Worte wie + // "Schiffahrtsbrennesseln", da der Hyphenator alle + // Position des Wortes auftrennt und "Schifffahrtsbrennnesseln" + // ermittelt. Wir koennen also eigentlich nicht unmittelbar vom + // Index des AlternativWord auf aWord schliessen. + + // Das ganze geraffel wird durch eine Funktion am + // Hyphenator vereinfacht werden, sobald AMA sie einbaut... + sal_uInt16 nAltStart = _nWordLen - 1; + sal_uInt16 nTxtStart = nAltStart - (aAlt.Len() - aWord.Len()); + sal_uInt16 nTxtEnd = nTxtStart; + sal_uInt16 nAltEnd = nAltStart; + + // Die Bereiche zwischen den nStart und nEnd ist + // die Differenz zwischen Alternativ- und OriginalString. + while( nTxtEnd < aWord.Len() && nAltEnd < aAlt.Len() && + aWord.GetChar(nTxtEnd) != aAlt.GetChar(nAltEnd) ) + { + ++nTxtEnd; + ++nAltEnd; + } + + // Wenn ein Zeichen hinzugekommen ist, dann bemerken wir es jetzt: + if( nAltEnd > nTxtEnd && nAltStart == nAltEnd && + aWord.GetChar( nTxtEnd ) == aAlt.GetChar(nAltEnd) ) + { + ++nAltEnd; + ++nTxtStart; + ++nTxtEnd; + } + + DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Falsche Annahme!" ); + + sal_Unicode cAlternateReplChar = 0; + + if ( nTxtEnd > nTxtStart ) + cAlternateReplChar = aAlt.GetChar( nAltStart ); + + nBreakPos = nWordStart + nTxtStart; + if ( cAlternateReplChar ) + nBreakPos++; + } + } // if (xHyphWord.is()) + } // if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) ) + } // if ( xHyph.is() ) + } // if ( (nStyle & TEXT_DRAW_WORDBREAK_HYPHENATION) == TEXT_DRAW_WORDBREAK_HYPHENATION ) + } + nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); + } + else + { + // fallback to something really simple + sal_uInt16 nSpacePos = STRING_LEN; + long nW = 0; + do + { + nSpacePos = rStr.SearchBackward( sal_Unicode(' '), nSpacePos ); + if( nSpacePos != STRING_NOTFOUND ) + { + if( nSpacePos > nPos ) + nSpacePos--; + nW = _rLayout.GetTextWidth( rStr, nPos, nSpacePos-nPos ); + } + } while( nW > nWidth ); + + if( nSpacePos != STRING_NOTFOUND ) + { + nBreakPos = nSpacePos; + nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); + if( nBreakPos < rStr.Len()-1 ) + nBreakPos++; + } + } + } + + if ( nLineWidth > nMaxLineWidth ) + nMaxLineWidth = nLineWidth; + + rLineInfo.AddLine( new ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) ); + + if ( nBreakPos == nPos ) + nBreakPos++; + nPos = nBreakPos; + + if ( ( rStr.GetChar( nPos ) == _CR ) || ( rStr.GetChar( nPos ) == _LF ) ) + { + nPos++; + // CR/LF? + if ( ( nPos < nLen ) && ( rStr.GetChar( nPos ) == _LF ) && ( rStr.GetChar( nPos-1 ) == _CR ) ) + nPos++; + } + } + } +#ifdef DBG_UTIL + for ( sal_uInt16 nL = 0; nL < rLineInfo.Count(); nL++ ) + { + ImplTextLineInfo* pLine = rLineInfo.GetLine( nL ); + String aLine( rStr, pLine->GetIndex(), pLine->GetLen() ); + DBG_ASSERT( aLine.Search( _CR ) == STRING_NOTFOUND, "ImplGetTextLines - Found CR!" ); + DBG_ASSERT( aLine.Search( _LF ) == STRING_NOTFOUND, "ImplGetTextLines - Found LF!" ); + } +#endif + + return nMaxLineWidth; +} + +// ======================================================================= + +void OutputDevice::SetAntialiasing( sal_uInt16 nMode ) +{ + if ( mnAntialiasing != nMode ) + { + mnAntialiasing = nMode; + mbInitFont = sal_True; + + if(mpGraphics) + { + mpGraphics->setAntiAliasB2DDraw(mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW); + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetAntialiasing( nMode ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetFont( const Font& rNewFont ) +{ + OSL_TRACE( "OutputDevice::SetFont()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rNewFont, Font, NULL ); + + Font aFont( rNewFont ); + aFont.SetLanguage(rNewFont.GetLanguage()); + if ( mnDrawMode & (DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | DRAWMODE_SETTINGSTEXT | + DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | DRAWMODE_GRAYFILL | DRAWMODE_NOFILL | + DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) ) + { + Color aTextColor( aFont.GetColor() ); + + if ( mnDrawMode & DRAWMODE_BLACKTEXT ) + aTextColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITETEXT ) + aTextColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) + { + const sal_uInt8 cLum = aTextColor.GetLuminance(); + aTextColor = Color( cLum, cLum, cLum ); + } + else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT ) + aTextColor = GetSettings().GetStyleSettings().GetFontColor(); + + if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT ) + { + aTextColor = Color( (aTextColor.GetRed() >> 1 ) | 0x80, + (aTextColor.GetGreen() >> 1 ) | 0x80, + (aTextColor.GetBlue() >> 1 ) | 0x80 ); + } + + aFont.SetColor( aTextColor ); + + sal_Bool bTransFill = aFont.IsTransparent(); + if ( !bTransFill ) + { + Color aTextFillColor( aFont.GetFillColor() ); + + if ( mnDrawMode & DRAWMODE_BLACKFILL ) + aTextFillColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITEFILL ) + aTextFillColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYFILL ) + { + const sal_uInt8 cLum = aTextFillColor.GetLuminance(); + aTextFillColor = Color( cLum, cLum, cLum ); + } + else if( mnDrawMode & DRAWMODE_SETTINGSFILL ) + aTextFillColor = GetSettings().GetStyleSettings().GetWindowColor(); + else if ( mnDrawMode & DRAWMODE_NOFILL ) + { + aTextFillColor = Color( COL_TRANSPARENT ); + bTransFill = sal_True; + } + + if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) ) + { + aTextFillColor = Color( (aTextFillColor.GetRed() >> 1) | 0x80, + (aTextFillColor.GetGreen() >> 1) | 0x80, + (aTextFillColor.GetBlue() >> 1) | 0x80 ); + } + + aFont.SetFillColor( aTextFillColor ); + } + } + + if ( mpMetaFile ) + { + mpMetaFile->AddAction( new MetaFontAction( aFont ) ); + // the color and alignment actions don't belong here + // TODO: get rid of them without breaking anything... + mpMetaFile->AddAction( new MetaTextAlignAction( aFont.GetAlign() ) ); + mpMetaFile->AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) ); + } + + if ( !maFont.IsSameInstance( aFont ) ) + { + // Optimization MT/HDU: COL_TRANSPARENT means SetFont should ignore the font color, + // because SetTextColor() is used for this. + // #i28759# maTextColor might have been changed behind our back, commit then, too. + if( aFont.GetColor() != COL_TRANSPARENT + && (aFont.GetColor() != maFont.GetColor() || aFont.GetColor() != maTextColor ) ) + { + maTextColor = aFont.GetColor(); + mbInitTextColor = sal_True; + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextColorAction( aFont.GetColor() ) ); + } + maFont = aFont; + mbNewFont = sal_True; + + if( mpAlphaVDev ) + { + // #i30463# + // Since SetFont might change the text color, apply that only + // selectively to alpha vdev (which normally paints opaque text + // with COL_BLACK) + if( aFont.GetColor() != COL_TRANSPARENT ) + { + mpAlphaVDev->SetTextColor( COL_BLACK ); + aFont.SetColor( COL_TRANSPARENT ); + } + + mpAlphaVDev->SetFont( aFont ); + } + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetLayoutMode( sal_uLong nTextLayoutMode ) +{ + OSL_TRACE( "OutputDevice::SetTextLayoutMode()" ); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaLayoutModeAction( nTextLayoutMode ) ); + + mnTextLayoutMode = nTextLayoutMode; + + if( mpAlphaVDev ) + mpAlphaVDev->SetLayoutMode( nTextLayoutMode ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetDigitLanguage( LanguageType eTextLanguage ) +{ + OSL_TRACE( "OutputDevice::SetTextLanguage()" ); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLanguageAction( eTextLanguage ) ); + + meTextLanguage = eTextLanguage; + + if( mpAlphaVDev ) + mpAlphaVDev->SetDigitLanguage( eTextLanguage ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextColor( const Color& rColor ) +{ + OSL_TRACE( "OutputDevice::SetTextColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + + if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | + DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | + DRAWMODE_SETTINGSTEXT ) ) + { + if ( mnDrawMode & DRAWMODE_BLACKTEXT ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITETEXT ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT ) + aColor = GetSettings().GetStyleSettings().GetFontColor(); + + if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT ) + { + aColor = Color( (aColor.GetRed() >> 1) | 0x80, + (aColor.GetGreen() >> 1) | 0x80, + (aColor.GetBlue() >> 1) | 0x80 ); + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextColorAction( aColor ) ); + + if ( maTextColor != aColor ) + { + maTextColor = aColor; + mbInitTextColor = sal_True; + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetTextColor( COL_BLACK ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextFillColor() +{ + OSL_TRACE( "OutputDevice::SetTextFillColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextFillColorAction( Color(), sal_False ) ); + + if ( maFont.GetColor() != Color( COL_TRANSPARENT ) ) + maFont.SetFillColor( Color( COL_TRANSPARENT ) ); + if ( !maFont.IsTransparent() ) + maFont.SetTransparent( sal_True ); + + if( mpAlphaVDev ) + mpAlphaVDev->SetTextFillColor(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextFillColor( const Color& rColor ) +{ + OSL_TRACE( "OutputDevice::SetTextFillColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + sal_Bool bTransFill = ImplIsColorTransparent( aColor ) ? sal_True : sal_False; + + if ( !bTransFill ) + { + if ( mnDrawMode & ( DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | + DRAWMODE_GRAYFILL | DRAWMODE_NOFILL | + DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) ) + { + if ( mnDrawMode & DRAWMODE_BLACKFILL ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITEFILL ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYFILL ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if( mnDrawMode & DRAWMODE_SETTINGSFILL ) + aColor = GetSettings().GetStyleSettings().GetWindowColor(); + else if ( mnDrawMode & DRAWMODE_NOFILL ) + { + aColor = Color( COL_TRANSPARENT ); + bTransFill = sal_True; + } + + if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) ) + { + aColor = Color( (aColor.GetRed() >> 1) | 0x80, + (aColor.GetGreen() >> 1) | 0x80, + (aColor.GetBlue() >> 1) | 0x80 ); + } + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextFillColorAction( aColor, sal_True ) ); + + if ( maFont.GetFillColor() != aColor ) + maFont.SetFillColor( aColor ); + if ( maFont.IsTransparent() != bTransFill ) + maFont.SetTransparent( bTransFill ); + + if( mpAlphaVDev ) + mpAlphaVDev->SetTextFillColor( COL_BLACK ); +} + +// ----------------------------------------------------------------------- + +Color OutputDevice::GetTextFillColor() const +{ + if ( maFont.IsTransparent() ) + return Color( COL_TRANSPARENT ); + else + return maFont.GetFillColor(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextLineColor() +{ + OSL_TRACE( "OutputDevice::SetTextLineColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), sal_False ) ); + + maTextLineColor = Color( COL_TRANSPARENT ); + + if( mpAlphaVDev ) + mpAlphaVDev->SetTextLineColor(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextLineColor( const Color& rColor ) +{ + OSL_TRACE( "OutputDevice::SetTextLineColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + + if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | + DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | + DRAWMODE_SETTINGSTEXT ) ) + { + if ( mnDrawMode & DRAWMODE_BLACKTEXT ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITETEXT ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT ) + aColor = GetSettings().GetStyleSettings().GetFontColor(); + + if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT) + && (aColor.GetColor() != COL_TRANSPARENT) ) + { + aColor = Color( (aColor.GetRed() >> 1) | 0x80, + (aColor.GetGreen() >> 1) | 0x80, + (aColor.GetBlue() >> 1) | 0x80 ); + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, sal_True ) ); + + maTextLineColor = aColor; + + if( mpAlphaVDev ) + mpAlphaVDev->SetTextLineColor( COL_BLACK ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetOverlineColor() +{ + OSL_TRACE( "OutputDevice::SetOverlineColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaOverlineColorAction( Color(), sal_False ) ); + + maOverlineColor = Color( COL_TRANSPARENT ); + + if( mpAlphaVDev ) + mpAlphaVDev->SetOverlineColor(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetOverlineColor( const Color& rColor ) +{ + OSL_TRACE( "OutputDevice::SetOverlineColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + + if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | + DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | + DRAWMODE_SETTINGSTEXT ) ) + { + if ( mnDrawMode & DRAWMODE_BLACKTEXT ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITETEXT ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT ) + aColor = GetSettings().GetStyleSettings().GetFontColor(); + + if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT) + && (aColor.GetColor() != COL_TRANSPARENT) ) + { + aColor = Color( (aColor.GetRed() >> 1) | 0x80, + (aColor.GetGreen() >> 1) | 0x80, + (aColor.GetBlue() >> 1) | 0x80 ); + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, sal_True ) ); + + maOverlineColor = aColor; + + if( mpAlphaVDev ) + mpAlphaVDev->SetOverlineColor( COL_BLACK ); +} + +// ----------------------------------------------------------------------- + + +void OutputDevice::SetTextAlign( TextAlign eAlign ) +{ + OSL_TRACE( "OutputDevice::SetTextAlign()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextAlignAction( eAlign ) ); + + if ( maFont.GetAlign() != eAlign ) + { + maFont.SetAlign( eAlign ); + mbNewFont = sal_True; + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetTextAlign( eAlign ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawTextLine( const Point& rPos, long nWidth, + FontStrikeout eStrikeout, + FontUnderline eUnderline, + FontUnderline eOverline, + sal_Bool bUnderlineAbove ) +{ + OSL_TRACE( "OutputDevice::DrawTextLine()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) ); + + if ( ((eUnderline == UNDERLINE_NONE) || (eUnderline == UNDERLINE_DONTKNOW)) && + ((eOverline == UNDERLINE_NONE) || (eOverline == UNDERLINE_DONTKNOW)) && + ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) ) + return; + + if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + // we need a graphics + if( !mpGraphics && !ImplGetGraphics() ) + return; + if( mbInitClipRegion ) + ImplInitClipRegion(); + if( mbOutputClipped ) + return; + + // initialize font if needed to get text offsets + // TODO: only needed for mnTextOff!=(0,0) + if( mbNewFont ) + if( !ImplNewFont() ) + return; + if( mbInitFont ) + ImplInitFont(); + + Point aPos = ImplLogicToDevicePixel( rPos ); + nWidth = ImplLogicWidthToDevicePixel( nWidth ); + aPos += Point( mnTextOffX, mnTextOffY ); + ImplDrawTextLine( aPos.X(), aPos.X(), 0, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + + if( mpAlphaVDev ) + mpAlphaVDev->DrawTextLine( rPos, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove ); +} + +// ------------------------------------------------------------------------ + +sal_Bool OutputDevice::IsTextUnderlineAbove( const Font& rFont ) +{ + return ImplIsUnderlineAbove( rFont ); +} + +// ------------------------------------------------------------------------ + +void OutputDevice::DrawWaveLine( const Point& rStartPos, const Point& rEndPos, + sal_uInt16 nStyle ) +{ + OSL_TRACE( "OutputDevice::DrawWaveLine()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + // we need a graphics + if( !mpGraphics ) + if( !ImplGetGraphics() ) + return; + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if( mbNewFont ) + if( !ImplNewFont() ) + return; + + Point aStartPt = ImplLogicToDevicePixel( rStartPos ); + Point aEndPt = ImplLogicToDevicePixel( rEndPos ); + long nStartX = aStartPt.X(); + long nStartY = aStartPt.Y(); + long nEndX = aEndPt.X(); + long nEndY = aEndPt.Y(); + short nOrientation = 0; + + // when rotated + if ( (nStartY != nEndY) || (nStartX > nEndX) ) + { + long nDX = nEndX - nStartX; + double nO = atan2( -nEndY + nStartY, ((nDX == 0L) ? 0.000000001 : nDX) ); + nO /= F_PI1800; + nOrientation = (short)nO; + ImplRotatePos( nStartX, nStartY, nEndX, nEndY, -nOrientation ); + } + + long nWaveHeight; + if ( nStyle == WAVE_NORMAL ) + { + nWaveHeight = 3; + nStartY++; + nEndY++; + } + else if( nStyle == WAVE_SMALL ) + { + nWaveHeight = 2; + nStartY++; + nEndY++; + } + else // WAVE_FLAT + nWaveHeight = 1; + + // #109280# make sure the waveline does not exceed the descent to avoid paint problems + ImplFontEntry* pFontEntry = mpFontEntry; + if( nWaveHeight > pFontEntry->maMetric.mnWUnderlineSize ) + nWaveHeight = pFontEntry->maMetric.mnWUnderlineSize; + + ImplDrawWaveLine( nStartX, nStartY, 0, 0, + nEndX-nStartX, nWaveHeight, 1, + nOrientation, GetLineColor() ); + if( mpAlphaVDev ) + mpAlphaVDev->DrawWaveLine( rStartPos, rEndPos, nStyle ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawText( const Point& rStartPt, const String& rStr, + xub_StrLen nIndex, xub_StrLen nLen, + MetricVector* pVector, String* pDisplayText + ) +{ + if( mpOutDevData && mpOutDevData->mpRecordLayout ) + { + pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects; + pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText; + } + + OSL_TRACE( "OutputDevice::DrawText()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, " OutputDevice::DrawText(\"%s\")\n", + OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ).getStr() ); +#endif + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) ); + if( pVector ) + { + Region aClip( GetClipRegion() ); + if( meOutDevType == OUTDEV_WINDOW ) + aClip.Intersect( Rectangle( Point(), GetOutputSize() ) ); + if( mpOutDevData && mpOutDevData->mpRecordLayout ) + { + mpOutDevData->mpRecordLayout->m_aLineIndices.push_back( mpOutDevData->mpRecordLayout->m_aDisplayText.Len() ); + aClip.Intersect( mpOutDevData->maRecordRect ); + } + if( ! aClip.IsNull() ) + { + MetricVector aTmp; + GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, aTmp ); + + bool bInserted = false; + for( MetricVector::const_iterator it = aTmp.begin(); it != aTmp.end(); ++it, nIndex++ ) + { + bool bAppend = false; + + if( aClip.IsOver( *it ) ) + bAppend = true; + else if( rStr.GetChar( nIndex ) == ' ' && bInserted ) + { + MetricVector::const_iterator next = it; + ++next; + if( next != aTmp.end() && aClip.IsOver( *next ) ) + bAppend = true; + } + + if( bAppend ) + { + pVector->push_back( *it ); + if( pDisplayText ) + pDisplayText->Append( rStr.GetChar( nIndex ) ); + bInserted = true; + } + } + } + else + { + GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, *pVector ); + if( pDisplayText ) + pDisplayText->Append( rStr.Copy( nIndex, nLen ) ); + } + } + + if ( !IsDeviceOutputNecessary() || pVector ) + return; + + SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, NULL, true ); + if( pSalLayout ) + { + ImplDrawText( *pSalLayout ); + pSalLayout->Release(); + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawText( rStartPt, rStr, nIndex, nLen, pVector, pDisplayText ); +} + +// ----------------------------------------------------------------------- + +long OutputDevice::GetTextWidth( const String& rStr, + xub_StrLen nIndex, xub_StrLen nLen ) const +{ + OSL_TRACE( "OutputDevice::GetTextWidth()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + long nWidth = GetTextArray( rStr, NULL, nIndex, nLen ); + return nWidth; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::GetTextHeight() const +{ + OSL_TRACE( "OutputDevice::GetTextHeight()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if( mbNewFont ) + if( !ImplNewFont() ) + return 0; + if( mbInitFont ) + if( !ImplNewFont() ) + return 0; + + long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; + + if ( mbMap ) + nHeight = ImplDevicePixelToLogicHeight( nHeight ); + + return nHeight; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawTextArray( const Point& rStartPt, const String& rStr, + const sal_Int32* pDXAry, + xub_StrLen nIndex, xub_StrLen nLen ) +{ + OSL_TRACE( "OutputDevice::DrawTextArray()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) ); + + if ( !IsDeviceOutputNecessary() ) + return; + if( !mpGraphics && !ImplGetGraphics() ) + return; + if( mbInitClipRegion ) + ImplInitClipRegion(); + if( mbOutputClipped ) + return; + + SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true ); + if( pSalLayout ) + { + ImplDrawText( *pSalLayout ); + pSalLayout->Release(); + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen ); +} + +// ----------------------------------------------------------------------- + +long OutputDevice::GetTextArray( const String& rStr, sal_Int32* pDXAry, + xub_StrLen nIndex, xub_StrLen nLen ) const +{ + OSL_TRACE( "OutputDevice::GetTextArray()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if( nIndex >= rStr.Len() ) + return 0; + if( (sal_uLong)nIndex+nLen >= rStr.Len() ) + nLen = rStr.Len() - nIndex; + + // do layout + SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen ); + if( !pSalLayout ) + return 0; + + long nWidth = pSalLayout->FillDXArray( pDXAry ); + int nWidthFactor = pSalLayout->GetUnitsPerPixel(); + pSalLayout->Release(); + + // convert virtual char widths to virtual absolute positions + if( pDXAry ) + for( int i = 1; i < nLen; ++i ) + pDXAry[ i ] += pDXAry[ i-1 ]; + + // convert from font units to logical units + if( mbMap ) + { + if( pDXAry ) + for( int i = 0; i < nLen; ++i ) + pDXAry[i] = ImplDevicePixelToLogicWidth( pDXAry[i] ); + nWidth = ImplDevicePixelToLogicWidth( nWidth ); + } + + if( nWidthFactor > 1 ) + { + if( pDXAry ) + for( int i = 0; i < nLen; ++i ) + pDXAry[i] /= nWidthFactor; + nWidth /= nWidthFactor; + } + + return nWidth; +} + +// ----------------------------------------------------------------------- + +bool OutputDevice::GetCaretPositions( const XubString& rStr, sal_Int32* pCaretXArray, + xub_StrLen nIndex, xub_StrLen nLen, + sal_Int32* pDXAry, long nLayoutWidth, + sal_Bool bCellBreaking ) const +{ + OSL_TRACE( "OutputDevice::GetCaretPositions()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if( nIndex >= rStr.Len() ) + return false; + if( (sal_uLong)nIndex+nLen >= rStr.Len() ) + nLen = rStr.Len() - nIndex; + + // layout complex text + SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, + Point(0,0), nLayoutWidth, pDXAry ); + if( !pSalLayout ) + return false; + + int nWidthFactor = pSalLayout->GetUnitsPerPixel(); + pSalLayout->GetCaretPositions( 2*nLen, pCaretXArray ); + long nWidth = pSalLayout->GetTextWidth(); + pSalLayout->Release(); + + // fixup unknown caret positions + int i; + for( i = 0; i < 2 * nLen; ++i ) + if( pCaretXArray[ i ] >= 0 ) + break; + long nXPos = pCaretXArray[ i ]; + for( i = 0; i < 2 * nLen; ++i ) + { + if( pCaretXArray[ i ] >= 0 ) + nXPos = pCaretXArray[ i ]; + else + pCaretXArray[ i ] = nXPos; + } + + // handle window mirroring + if( IsRTLEnabled() ) + { + for( i = 0; i < 2 * nLen; ++i ) + pCaretXArray[i] = nWidth - pCaretXArray[i] - 1; + } + + // convert from font units to logical units + if( mbMap ) + { + for( i = 0; i < 2*nLen; ++i ) + pCaretXArray[i] = ImplDevicePixelToLogicWidth( pCaretXArray[i] ); + } + + if( nWidthFactor != 1 ) + { + for( i = 0; i < 2*nLen; ++i ) + pCaretXArray[i] /= nWidthFactor; + } + + // if requested move caret position to cell limits + if( bCellBreaking ) + { + ; // TODO + } + + return true; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawStretchText( const Point& rStartPt, sal_uLong nWidth, + const String& rStr, + xub_StrLen nIndex, xub_StrLen nLen ) +{ + OSL_TRACE( "OutputDevice::DrawStretchText()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaStretchTextAction( rStartPt, nWidth, rStr, nIndex, nLen ) ); + + if ( !IsDeviceOutputNecessary() ) + return; + + SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, nWidth, NULL, true ); + if( pSalLayout ) + { + ImplDrawText( *pSalLayout ); + pSalLayout->Release(); + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawStretchText( rStartPt, nWidth, rStr, nIndex, nLen ); +} + +// ----------------------------------------------------------------------- + +ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( String& rStr, + xub_StrLen nMinIndex, xub_StrLen nLen, + long nPixelWidth, const sal_Int32* pDXArray ) const +{ + // get string length for calculating extents + xub_StrLen nEndIndex = rStr.Len(); + if( (sal_uLong)nMinIndex + nLen < nEndIndex ) + nEndIndex = nMinIndex + nLen; + + // don't bother if there is nothing to do + if( nEndIndex < nMinIndex ) + nEndIndex = nMinIndex; + + int nLayoutFlags = 0; + if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL ) + nLayoutFlags |= SAL_LAYOUT_BIDI_RTL; + if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_STRONG ) + nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG; + else if( 0 == (mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) ) + { + // disable Bidi if no RTL hint and no RTL codes used + const xub_Unicode* pStr = rStr.GetBuffer() + nMinIndex; + const xub_Unicode* pEnd = rStr.GetBuffer() + nEndIndex; + for( ; pStr < pEnd; ++pStr ) + if( ((*pStr >= 0x0580) && (*pStr < 0x0800)) // middle eastern scripts + || ((*pStr >= 0xFB18) && (*pStr < 0xFE00)) // hebrew + arabic A presentation forms + || ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation forms B + break; + if( pStr >= pEnd ) + nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG; + } + + if( mbKerning ) + nLayoutFlags |= SAL_LAYOUT_KERNING_PAIRS; + if( maFont.GetKerning() & KERNING_ASIAN ) + nLayoutFlags |= SAL_LAYOUT_KERNING_ASIAN; + if( maFont.IsVertical() ) + nLayoutFlags |= SAL_LAYOUT_VERTICAL; + + if( mnTextLayoutMode & TEXT_LAYOUT_ENABLE_LIGATURES ) + nLayoutFlags |= SAL_LAYOUT_ENABLE_LIGATURES; + else if( mnTextLayoutMode & TEXT_LAYOUT_COMPLEX_DISABLED ) + nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED; + else + { + // disable CTL for non-CTL text + const sal_Unicode* pStr = rStr.GetBuffer() + nMinIndex; + const sal_Unicode* pEnd = rStr.GetBuffer() + nEndIndex; + for( ; pStr < pEnd; ++pStr ) + if( ((*pStr >= 0x0300) && (*pStr < 0x0370)) // diacritical marks + || ((*pStr >= 0x0590) && (*pStr < 0x10A0)) // many CTL scripts + || ((*pStr >= 0x1100) && (*pStr < 0x1200)) // hangul jamo + || ((*pStr >= 0x1700) && (*pStr < 0x1900)) // many CTL scripts + || ((*pStr >= 0xFB1D) && (*pStr < 0xFE00)) // middle east presentation + || ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation B + break; + if( pStr >= pEnd ) + nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED; + } + + if( meTextLanguage ) //TODO: (mnTextLayoutMode & TEXT_LAYOUT_SUBSTITUTE_DIGITS) + { + // disable character localization when no digits used + const sal_Unicode* pBase = rStr.GetBuffer(); + const sal_Unicode* pStr = pBase + nMinIndex; + const sal_Unicode* pEnd = pBase + nEndIndex; + for( ; pStr < pEnd; ++pStr ) + { + // TODO: are there non-digit localizations? + if( (*pStr >= '0') && (*pStr <= '9') ) + { + // translate characters to local preference + sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage ); + if( cChar != *pStr ) + // TODO: are the localized digit surrogates? + rStr.SetChar( static_cast<sal_uInt16>(pStr - pBase), + static_cast<sal_Unicode>(cChar) ); + } + } + } + + // right align for RTL text, DRAWPOS_REVERSED, RTL window style + bool bRightAlign = ((mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) != 0); + if( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) + bRightAlign = false; + else if ( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT ) + bRightAlign = true; + // SSA: hack for western office, ie text get right aligned + // for debugging purposes of mirrored UI + //static const char* pEnv = getenv( "SAL_RTL_MIRRORTEXT" ); + bool bRTLWindow = IsRTLEnabled(); + bRightAlign ^= bRTLWindow; + if( bRightAlign ) + nLayoutFlags |= SAL_LAYOUT_RIGHT_ALIGN; + + // set layout options + ImplLayoutArgs aLayoutArgs( rStr.GetBuffer(), rStr.Len(), nMinIndex, nEndIndex, nLayoutFlags ); + + int nOrientation = mpFontEntry ? mpFontEntry->mnOrientation : 0; + aLayoutArgs.SetOrientation( nOrientation ); + + aLayoutArgs.SetLayoutWidth( nPixelWidth ); + aLayoutArgs.SetDXArray( pDXArray ); + + return aLayoutArgs; +} + +// ----------------------------------------------------------------------- + +SalLayout* OutputDevice::ImplLayout( const String& rOrigStr, + xub_StrLen nMinIndex, + xub_StrLen nLen, + const Point& rLogicalPos, + long nLogicalWidth, + const sal_Int32* pDXArray, + bool bFilter ) const +{ + // we need a graphics + if( !mpGraphics ) + if( !ImplGetGraphics() ) + return NULL; + + // initialize font if needed + if( mbNewFont ) + if( !ImplNewFont() ) + return NULL; + if( mbInitFont ) + ImplInitFont(); + + // check string index and length + if( (unsigned)nMinIndex + nLen > rOrigStr.Len() ) + { + const int nNewLen = (int)rOrigStr.Len() - nMinIndex; + if( nNewLen <= 0 ) + return NULL; + nLen = static_cast<xub_StrLen>(nNewLen); + } + + String aStr = rOrigStr; + + // filter out special markers + if( bFilter ) + { + xub_StrLen nCutStart, nCutStop, nOrgLen = nLen; + bool bFiltered = mpGraphics->filterText( rOrigStr, aStr, nMinIndex, nLen, nCutStart, nCutStop ); + if( !nLen ) + return NULL; + + if( bFiltered && nCutStop != nCutStart && pDXArray ) + { + if( !nLen ) + pDXArray = NULL; + else + { + sal_Int32* pAry = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen); + if( nCutStart > nMinIndex ) + memcpy( pAry, pDXArray, sizeof(sal_Int32)*(nCutStart-nMinIndex) ); + // note: nCutStart will never be smaller than nMinIndex + memcpy( pAry+nCutStart-nMinIndex, + pDXArray + nOrgLen - (nCutStop-nMinIndex), + sizeof(sal_Int32)*(nLen - (nCutStart-nMinIndex)) ); + pDXArray = pAry; + } + } + } + + // convert from logical units to physical units + // recode string if needed + if( mpFontEntry->mpConversion ) + mpFontEntry->mpConversion->RecodeString( aStr, 0, aStr.Len() ); + + long nPixelWidth = nLogicalWidth; + if( nLogicalWidth && mbMap ) + nPixelWidth = ImplLogicWidthToDevicePixel( nLogicalWidth ); + if( pDXArray && mbMap ) + { + // convert from logical units to font units using a temporary array + sal_Int32* pTempDXAry = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) ); + // using base position for better rounding a.k.a. "dancing characters" + int nPixelXOfs = ImplLogicWidthToDevicePixel( rLogicalPos.X() ); + for( int i = 0; i < nLen; ++i ) + pTempDXAry[i] = ImplLogicWidthToDevicePixel( rLogicalPos.X() + pDXArray[i] ) - nPixelXOfs; + + pDXArray = pTempDXAry; + } + + ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXArray ); + + // get matching layout object for base font + SalLayout* pSalLayout = NULL; + if( mpPDFWriter ) + pSalLayout = mpPDFWriter->GetTextLayout( aLayoutArgs, &mpFontEntry->maFontSelData ); + + if( !pSalLayout ) + pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 ); + + // layout text + if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs ) ) + { + pSalLayout->Release(); + pSalLayout = NULL; + } + + if( !pSalLayout ) + return NULL; + + // do glyph fallback if needed + // #105768# avoid fallback for very small font sizes + if( aLayoutArgs.NeedFallback() ) + if( mpFontEntry && (mpFontEntry->maFontSelData.mnHeight >= 3) ) + pSalLayout = ImplGlyphFallbackLayout( pSalLayout, aLayoutArgs ); + + // position, justify, etc. the layout + pSalLayout->AdjustLayout( aLayoutArgs ); + pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos ); + // adjust to right alignment if necessary + if( aLayoutArgs.mnFlags & SAL_LAYOUT_RIGHT_ALIGN ) + { + long nRTLOffset; + if( pDXArray ) + nRTLOffset = pDXArray[ nLen - 1 ]; + else if( nPixelWidth ) + nRTLOffset = nPixelWidth; + else + nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel(); + pSalLayout->DrawOffset().X() = 1 - nRTLOffset; + } + + return pSalLayout; +} + +void OutputDevice::forceFallbackFontToFit(SalLayout &rFallback, ImplFontEntry &rFallbackFont, + ImplFontSelectData &rFontSelData, int nFallbackLevel, + ImplLayoutArgs& rLayoutArgs, const ImplFontMetricData& rOrigMetric) const +{ + Rectangle aBoundRect; + bool bHaveBounding = false; + Rectangle aRectangle; + + rFallback.AdjustLayout( rLayoutArgs ); + + //All we care about here is getting the vertical bounds of this text and + //make sure it will fit inside the available space + Point aPos; + for( int nStart = 0;;) + { + sal_GlyphId nLGlyph; + if( !rFallback.GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) ) + break; + + sal_GlyphId nFontTag = nFallbackLevel << GF_FONTSHIFT; + nLGlyph |= nFontTag; + + // get bounding rectangle of individual glyph + if( mpGraphics->GetGlyphBoundRect( nLGlyph, aRectangle ) ) + { + // merge rectangle + aRectangle += aPos; + aBoundRect.Union( aRectangle ); + bHaveBounding = true; + } + } + + //Shrink it down if it won't fit + if (bHaveBounding) + { + long nGlyphsAscent = -aBoundRect.Top(); + float fScaleTop = nGlyphsAscent > rOrigMetric.mnAscent ? + rOrigMetric.mnAscent/(float)nGlyphsAscent : 1; + long nGlyphsDescent = aBoundRect.Bottom(); + float fScaleBottom = nGlyphsDescent > rOrigMetric.mnDescent ? + rOrigMetric.mnDescent/(float)nGlyphsDescent : 1; + float fScale = fScaleBottom < fScaleTop ? fScaleBottom : fScaleTop; + if (fScale < 1) + { + long nOrigHeight = rFontSelData.mnHeight; + rFontSelData.mnHeight *= fScale; + rFallbackFont.mnSetFontFlags = mpGraphics->SetFont( &rFontSelData, nFallbackLevel ); + rFontSelData.mnHeight = nOrigHeight; + } + } +} + +// ----------------------------------------------------------------------- + +SalLayout* OutputDevice::ImplGlyphFallbackLayout( SalLayout* pSalLayout, ImplLayoutArgs& rLayoutArgs ) const +{ + // prepare multi level glyph fallback + MultiSalLayout* pMultiSalLayout = NULL; + ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns; + rLayoutArgs.PrepareFallback(); + rLayoutArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK; + + // get list of unicodes that need glyph fallback + int nCharPos = -1; + bool bRTL = false; + rtl::OUStringBuffer aMissingCodeBuf; + while( rLayoutArgs.GetNextPos( &nCharPos, &bRTL) ) + aMissingCodeBuf.append( rLayoutArgs.mpStr[ nCharPos ] ); + rLayoutArgs.ResetPos(); + rtl::OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear(); + + ImplFontSelectData aFontSelData = mpFontEntry->maFontSelData; + + ImplFontMetricData aOrigMetric( aFontSelData ); + // TODO: use cached metric in fontentry + mpGraphics->GetFontMetric( &aOrigMetric ); + + // when device specific font substitution may have been performed for + // the originally selected font then make sure that a fallback to that + // font is performed first + int nDevSpecificFallback = 0; + if( mpOutDevData && !mpOutDevData->maDevFontSubst.Empty() ) + nDevSpecificFallback = 1; + + // try if fallback fonts support the missing unicodes + for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel ) + { + // find a font family suited for glyph fallback +#ifndef FONTFALLBACK_HOOKS_DISABLED + // GetGlyphFallbackFont() needs a valid aFontSelData.mpFontEntry + // if the system-specific glyph fallback is active + aFontSelData.mpFontEntry = mpFontEntry; // reset the fontentry to base-level +#endif + ImplFontEntry* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontList, + aFontSelData, nFallbackLevel-nDevSpecificFallback, aMissingCodes ); + if( !pFallbackFont ) + break; + + aFontSelData.mpFontEntry = pFallbackFont; + aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData; + if( mpFontEntry && nFallbackLevel < MAX_FALLBACK-1) + { + // ignore fallback font if it is the same as the original font + if( mpFontEntry->maFontSelData.mpFontData == aFontSelData.mpFontData ) + { + mpFontCache->Release( pFallbackFont ); + continue; + } + } + + pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel ); + + // create and add glyph fallback layout to multilayout + rLayoutArgs.ResetPos(); + SalLayout* pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel ); + if( pFallback ) + { + if( pFallback->LayoutText( rLayoutArgs ) ) + { + forceFallbackFontToFit(*pFallback, *pFallbackFont, aFontSelData, + nFallbackLevel, rLayoutArgs, aOrigMetric); + + if( !pMultiSalLayout ) + pMultiSalLayout = new MultiSalLayout( *pSalLayout ); + pMultiSalLayout->AddFallback( *pFallback, + rLayoutArgs.maRuns, aFontSelData.mpFontData ); + if (nFallbackLevel == MAX_FALLBACK-1) + pMultiSalLayout->SetInComplete(); + } + else + { + // there is no need for a font that couldn't resolve anything + pFallback->Release(); + } + } + + mpFontCache->Release( pFallbackFont ); + + // break when this fallback was sufficient + if( !rLayoutArgs.PrepareFallback() ) + break; + } + + if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) ) + pSalLayout = pMultiSalLayout; + + // restore orig font settings + pSalLayout->InitFont(); + rLayoutArgs.maRuns = aLayoutRuns; + + return pSalLayout; +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::GetTextIsRTL( + const String& rString, + xub_StrLen nIndex, xub_StrLen nLen ) const +{ + String aStr( rString ); + ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL ); + bool bRTL = false; + int nCharPos = -1; + aArgs.GetNextPos( &nCharPos, &bRTL ); + return (nCharPos != nIndex) ? sal_True : sal_False; +} + +// ----------------------------------------------------------------------- + +xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth, + xub_StrLen nIndex, xub_StrLen nLen, + long nCharExtra, sal_Bool /*TODO: bCellBreaking*/ ) const +{ + OSL_TRACE( "OutputDevice::GetTextBreak()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen ); + xub_StrLen nRetVal = STRING_LEN; + if( pSalLayout ) + { + // convert logical widths into layout units + // NOTE: be very careful to avoid rounding errors for nCharExtra case + // problem with rounding errors especially for small nCharExtras + // TODO: remove when layout units have subpixel granularity + long nWidthFactor = pSalLayout->GetUnitsPerPixel(); + long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1; + nTextWidth *= nWidthFactor * nSubPixelFactor; + long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth ); + long nExtraPixelWidth = 0; + if( nCharExtra != 0 ) + { + nCharExtra *= nWidthFactor * nSubPixelFactor; + nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra ); + } + nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor )); + + pSalLayout->Release(); + } + + return nRetVal; +} + +// ----------------------------------------------------------------------- + +xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth, + sal_Unicode nHyphenatorChar, xub_StrLen& rHyphenatorPos, + xub_StrLen nIndex, xub_StrLen nLen, + long nCharExtra ) const +{ + OSL_TRACE( "OutputDevice::GetTextBreak()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + rHyphenatorPos = STRING_LEN; + + SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen ); + if( !pSalLayout ) + return STRING_LEN; + + // convert logical widths into layout units + // NOTE: be very careful to avoid rounding errors for nCharExtra case + // problem with rounding errors especially for small nCharExtras + // TODO: remove when layout units have subpixel granularity + long nWidthFactor = pSalLayout->GetUnitsPerPixel(); + long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1; + + nTextWidth *= nWidthFactor * nSubPixelFactor; + long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth ); + long nExtraPixelWidth = 0; + if( nCharExtra != 0 ) + { + nCharExtra *= nWidthFactor * nSubPixelFactor; + nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra ); + } + + // calculate un-hyphenated break position + xub_StrLen nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor )); + + // calculate hyphenated break position + String aHyphenatorStr( &nHyphenatorChar, 1 ); + xub_StrLen nTempLen = 1; + SalLayout* pHyphenatorLayout = ImplLayout( aHyphenatorStr, 0, nTempLen ); + if( pHyphenatorLayout ) + { + // calculate subpixel width of hyphenation character + long nHyphenatorPixelWidth = pHyphenatorLayout->GetTextWidth() * nSubPixelFactor; + pHyphenatorLayout->Release(); + + // calculate hyphenated break position + nTextPixelWidth -= nHyphenatorPixelWidth; + if( nExtraPixelWidth > 0 ) + nTextPixelWidth -= nExtraPixelWidth; + + rHyphenatorPos = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor )); + + if( rHyphenatorPos > nRetVal ) + rHyphenatorPos = nRetVal; + } + + pSalLayout->Release(); + return nRetVal; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawText( OutputDevice& rTargetDevice, const Rectangle& rRect, + const String& rOrigStr, sal_uInt16 nStyle, + MetricVector* pVector, String* pDisplayText, + ::vcl::ITextLayout& _rLayout ) +{ + Color aOldTextColor; + Color aOldTextFillColor; + sal_Bool bRestoreFillColor = false; + if ( (nStyle & TEXT_DRAW_DISABLE) && ! pVector ) + { + sal_Bool bHighContrastBlack = sal_False; + sal_Bool bHighContrastWhite = sal_False; + const StyleSettings& rStyleSettings( rTargetDevice.GetSettings().GetStyleSettings() ); + if( rStyleSettings.GetHighContrastMode() ) + { + Color aCol; + if( rTargetDevice.IsBackground() ) + aCol = rTargetDevice.GetBackground().GetColor(); + else + // best guess is the face color here + // but it may be totally wrong. the background color + // was typically already reset + aCol = rStyleSettings.GetFaceColor(); + + bHighContrastBlack = aCol.IsDark(); + bHighContrastWhite = aCol.IsBright(); + } + + aOldTextColor = rTargetDevice.GetTextColor(); + if ( rTargetDevice.IsTextFillColor() ) + { + bRestoreFillColor = sal_True; + aOldTextFillColor = rTargetDevice.GetTextFillColor(); + } + if( bHighContrastBlack ) + rTargetDevice.SetTextColor( COL_GREEN ); + else if( bHighContrastWhite ) + rTargetDevice.SetTextColor( COL_LIGHTGREEN ); + else + { + // draw disabled text always without shadow + // as it fits better with native look + /* + SetTextColor( GetSettings().GetStyleSettings().GetLightColor() ); + Rectangle aRect = rRect; + aRect.Move( 1, 1 ); + DrawText( aRect, rOrigStr, nStyle & ~TEXT_DRAW_DISABLE ); + */ + rTargetDevice.SetTextColor( rTargetDevice.GetSettings().GetStyleSettings().GetDisableColor() ); + } + } + + long nWidth = rRect.GetWidth(); + long nHeight = rRect.GetHeight(); + + if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) ) + return; + + Point aPos = rRect.TopLeft(); + + long nTextHeight = rTargetDevice.GetTextHeight(); + TextAlign eAlign = rTargetDevice.GetTextAlign(); + xub_StrLen nMnemonicPos = STRING_NOTFOUND; + + String aStr = rOrigStr; + if ( nStyle & TEXT_DRAW_MNEMONIC ) + aStr = GetNonMnemonicString( aStr, nMnemonicPos ); + + const bool bDrawMnemonics = !(rTargetDevice.GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector; + + // Mehrzeiligen Text behandeln wir anders + if ( nStyle & TEXT_DRAW_MULTILINE ) + { + + XubString aLastLine; + ImplMultiTextLineInfo aMultiLineInfo; + ImplTextLineInfo* pLineInfo; + long nMaxTextWidth; + xub_StrLen i; + xub_StrLen nLines; + xub_StrLen nFormatLines; + + if ( nTextHeight ) + { + nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _rLayout ); + nLines = (xub_StrLen)(nHeight/nTextHeight); + nFormatLines = aMultiLineInfo.Count(); + if ( !nLines ) + nLines = 1; + if ( nFormatLines > nLines ) + { + if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) + { + // Letzte Zeile zusammenbauen und kuerzen + nFormatLines = nLines-1; + + pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); + aLastLine = aStr.Copy( pLineInfo->GetIndex() ); + aLastLine.ConvertLineEnd( LINEEND_LF ); + // Alle LineFeed's durch Spaces ersetzen + xub_StrLen nLastLineLen = aLastLine.Len(); + for ( i = 0; i < nLastLineLen; i++ ) + { + if ( aLastLine.GetChar( i ) == _LF ) + aLastLine.SetChar( i, ' ' ); + } + aLastLine = ImplGetEllipsisString( rTargetDevice, aLastLine, nWidth, nStyle, _rLayout ); + nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM); + nStyle |= TEXT_DRAW_TOP; + } + } + else + { + if ( nMaxTextWidth <= nWidth ) + nStyle &= ~TEXT_DRAW_CLIP; + } + + // Muss in der Hoehe geclippt werden? + if ( nFormatLines*nTextHeight > nHeight ) + nStyle |= TEXT_DRAW_CLIP; + + // Clipping setzen + if ( nStyle & TEXT_DRAW_CLIP ) + { + rTargetDevice.Push( PUSH_CLIPREGION ); + rTargetDevice.IntersectClipRegion( rRect ); + } + + // Vertikales Alignment + if ( nStyle & TEXT_DRAW_BOTTOM ) + aPos.Y() += nHeight-(nFormatLines*nTextHeight); + else if ( nStyle & TEXT_DRAW_VCENTER ) + aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; + + // Font Alignment + if ( eAlign == ALIGN_BOTTOM ) + aPos.Y() += nTextHeight; + else if ( eAlign == ALIGN_BASELINE ) + aPos.Y() += rTargetDevice.GetFontMetric().GetAscent(); + + // Alle Zeilen ausgeben, bis auf die letzte + for ( i = 0; i < nFormatLines; i++ ) + { + pLineInfo = aMultiLineInfo.GetLine( i ); + if ( nStyle & TEXT_DRAW_RIGHT ) + aPos.X() += nWidth-pLineInfo->GetWidth(); + else if ( nStyle & TEXT_DRAW_CENTER ) + aPos.X() += (nWidth-pLineInfo->GetWidth())/2; + xub_StrLen nIndex = pLineInfo->GetIndex(); + xub_StrLen nLineLen = pLineInfo->GetLen(); + _rLayout.DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText ); + if ( bDrawMnemonics ) + { + if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) ) + { + long nMnemonicX; + long nMnemonicY; + long nMnemonicWidth; + + sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * nLineLen ); + /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray, + nIndex, nLineLen ); + long lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)]; + long lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1]; + nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) ); + + Point aTempPos = rTargetDevice.LogicToPixel( aPos ); + nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( Min( lc_x1, lc_x2 ) ); + nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() ); + rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); + } + } + aPos.Y() += nTextHeight; + aPos.X() = rRect.Left(); + } + + + // Gibt es noch eine letzte Zeile, dann diese linksbuendig ausgeben, + // da die Zeile gekuerzt wurde + if ( aLastLine.Len() ) + _rLayout.DrawText( aPos, aLastLine, 0, STRING_LEN, pVector, pDisplayText ); + + // Clipping zuruecksetzen + if ( nStyle & TEXT_DRAW_CLIP ) + rTargetDevice.Pop(); + } + } + else + { + long nTextWidth = _rLayout.GetTextWidth( aStr, 0, STRING_LEN ); + + // Evt. Text kuerzen + if ( nTextWidth > nWidth ) + { + if ( nStyle & TEXT_DRAW_ELLIPSIS ) + { + aStr = ImplGetEllipsisString( rTargetDevice, aStr, nWidth, nStyle, _rLayout ); + nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT); + nStyle |= TEXT_DRAW_LEFT; + nTextWidth = _rLayout.GetTextWidth( aStr, 0, aStr.Len() ); + } + } + else + { + if ( nTextHeight <= nHeight ) + nStyle &= ~TEXT_DRAW_CLIP; + } + + // horizontal text alignment + if ( nStyle & TEXT_DRAW_RIGHT ) + aPos.X() += nWidth-nTextWidth; + else if ( nStyle & TEXT_DRAW_CENTER ) + aPos.X() += (nWidth-nTextWidth)/2; + + // vertical font alignment + if ( eAlign == ALIGN_BOTTOM ) + aPos.Y() += nTextHeight; + else if ( eAlign == ALIGN_BASELINE ) + aPos.Y() += rTargetDevice.GetFontMetric().GetAscent(); + + if ( nStyle & TEXT_DRAW_BOTTOM ) + aPos.Y() += nHeight-nTextHeight; + else if ( nStyle & TEXT_DRAW_VCENTER ) + aPos.Y() += (nHeight-nTextHeight)/2; + + long nMnemonicX = 0; + long nMnemonicY = 0; + long nMnemonicWidth = 0; + if ( nMnemonicPos != STRING_NOTFOUND ) + { + sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * aStr.Len() ); + /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray, 0, aStr.Len() ); + long lc_x1 = pCaretXArray[2*(nMnemonicPos)]; + long lc_x2 = pCaretXArray[2*(nMnemonicPos)+1]; + nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) ); + + Point aTempPos = rTargetDevice.LogicToPixel( aPos ); + nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( Min(lc_x1, lc_x2) ); + nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() ); + } + + if ( nStyle & TEXT_DRAW_CLIP ) + { + rTargetDevice.Push( PUSH_CLIPREGION ); + rTargetDevice.IntersectClipRegion( rRect ); + _rLayout.DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText ); + if ( bDrawMnemonics ) + { + if ( nMnemonicPos != STRING_NOTFOUND ) + rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); + } + rTargetDevice.Pop(); + } + else + { + _rLayout.DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText ); + if ( bDrawMnemonics ) + { + if ( nMnemonicPos != STRING_NOTFOUND ) + rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); + } + } + } + + if ( nStyle & TEXT_DRAW_DISABLE && !pVector ) + { + rTargetDevice.SetTextColor( aOldTextColor ); + if ( bRestoreFillColor ) + rTargetDevice.SetTextFillColor( aOldTextFillColor ); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::AddTextRectActions( const Rectangle& rRect, + const String& rOrigStr, + sal_uInt16 nStyle, + GDIMetaFile& rMtf ) +{ + OSL_TRACE( "OutputDevice::AddTextRectActions( const Rectangle& )" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !rOrigStr.Len() || rRect.IsEmpty() ) + return; + + // we need a graphics + if( !mpGraphics && !ImplGetGraphics() ) + return; + if( mbInitClipRegion ) + ImplInitClipRegion(); + + // temporarily swap in passed mtf for action generation, and + // disable output generation. + const sal_Bool bOutputEnabled( IsOutputEnabled() ); + GDIMetaFile* pMtf = mpMetaFile; + + mpMetaFile = &rMtf; + EnableOutput( sal_False ); + + // #i47157# Factored out to ImplDrawTextRect(), to be shared + // between us and DrawText() + DefaultTextLayout aLayout( *this ); + ImplDrawText( *this, rRect, rOrigStr, nStyle, NULL, NULL, aLayout ); + + // and restore again + EnableOutput( bOutputEnabled ); + mpMetaFile = pMtf; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, + MetricVector* pVector, String* pDisplayText, + ::vcl::ITextLayout* _pTextLayout ) +{ + if( mpOutDevData && mpOutDevData->mpRecordLayout ) + { + pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects; + pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText; + } + + OSL_TRACE( "OutputDevice::DrawText( const Rectangle& )" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + bool bDecomposeTextRectAction = ( _pTextLayout != NULL ) && _pTextLayout->DecomposeTextRectAction(); + if ( mpMetaFile && !bDecomposeTextRectAction ) + mpMetaFile->AddAction( new MetaTextRectAction( rRect, rOrigStr, nStyle ) ); + + if ( ( !IsDeviceOutputNecessary() && !pVector && !bDecomposeTextRectAction ) || !rOrigStr.Len() || rRect.IsEmpty() ) + return; + + // we need a graphics + if( !mpGraphics && !ImplGetGraphics() ) + return; + if( mbInitClipRegion ) + ImplInitClipRegion(); + if( mbOutputClipped && !bDecomposeTextRectAction ) + return; + + // temporarily disable mtf action generation (ImplDrawText _does_ + // create META_TEXT_ACTIONs otherwise) + GDIMetaFile* pMtf = mpMetaFile; + if ( !bDecomposeTextRectAction ) + mpMetaFile = NULL; + + // #i47157# Factored out to ImplDrawText(), to be used also + // from AddTextRectActions() + DefaultTextLayout aDefaultLayout( *this ); + ImplDrawText( *this, rRect, rOrigStr, nStyle, pVector, pDisplayText, _pTextLayout ? *_pTextLayout : aDefaultLayout ); + + // and enable again + mpMetaFile = pMtf; + + if( mpAlphaVDev ) + mpAlphaVDev->DrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText ); +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::GetTextRect( const Rectangle& rRect, + const XubString& rStr, sal_uInt16 nStyle, + TextRectInfo* pInfo, + const ::vcl::ITextLayout* _pTextLayout ) const +{ + OSL_TRACE( "OutputDevice::GetTextRect()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Rectangle aRect = rRect; + xub_StrLen nLines; + long nWidth = rRect.GetWidth(); + long nMaxWidth; + long nTextHeight = GetTextHeight(); + + String aStr = rStr; + if ( nStyle & TEXT_DRAW_MNEMONIC ) + aStr = GetNonMnemonicString( aStr ); + + if ( nStyle & TEXT_DRAW_MULTILINE ) + { + ImplMultiTextLineInfo aMultiLineInfo; + ImplTextLineInfo* pLineInfo; + xub_StrLen nFormatLines; + xub_StrLen i; + + nMaxWidth = 0; + DefaultTextLayout aDefaultLayout( *const_cast< OutputDevice* >( this ) ); + ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _pTextLayout ? *_pTextLayout : aDefaultLayout ); + nFormatLines = aMultiLineInfo.Count(); + if ( !nTextHeight ) + nTextHeight = 1; + nLines = (sal_uInt16)(aRect.GetHeight()/nTextHeight); + if ( pInfo ) + pInfo->mnLineCount = nFormatLines; + if ( !nLines ) + nLines = 1; + if ( nFormatLines <= nLines ) + nLines = nFormatLines; + else + { + if ( !(nStyle & TEXT_DRAW_ENDELLIPSIS) ) + nLines = nFormatLines; + else + { + if ( pInfo ) + pInfo->mbEllipsis = sal_True; + nMaxWidth = nWidth; + } + } + if ( pInfo ) + { + sal_Bool bMaxWidth = nMaxWidth == 0; + pInfo->mnMaxWidth = 0; + for ( i = 0; i < nLines; i++ ) + { + pLineInfo = aMultiLineInfo.GetLine( i ); + if ( bMaxWidth && (pLineInfo->GetWidth() > nMaxWidth) ) + nMaxWidth = pLineInfo->GetWidth(); + if ( pLineInfo->GetWidth() > pInfo->mnMaxWidth ) + pInfo->mnMaxWidth = pLineInfo->GetWidth(); + } + } + else if ( !nMaxWidth ) + { + for ( i = 0; i < nLines; i++ ) + { + pLineInfo = aMultiLineInfo.GetLine( i ); + if ( pLineInfo->GetWidth() > nMaxWidth ) + nMaxWidth = pLineInfo->GetWidth(); + } + } + } + else + { + nLines = 1; + nMaxWidth = _pTextLayout ? _pTextLayout->GetTextWidth( aStr, 0, aStr.Len() ) : GetTextWidth( aStr ); + + if ( pInfo ) + { + pInfo->mnLineCount = 1; + pInfo->mnMaxWidth = nMaxWidth; + } + + if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) ) + { + if ( pInfo ) + pInfo->mbEllipsis = sal_True; + nMaxWidth = nWidth; + } + } + + if ( nStyle & TEXT_DRAW_RIGHT ) + aRect.Left() = aRect.Right()-nMaxWidth+1; + else if ( nStyle & TEXT_DRAW_CENTER ) + { + aRect.Left() += (nWidth-nMaxWidth)/2; + aRect.Right() = aRect.Left()+nMaxWidth-1; + } + else + aRect.Right() = aRect.Left()+nMaxWidth-1; + + if ( nStyle & TEXT_DRAW_BOTTOM ) + aRect.Top() = aRect.Bottom()-(nTextHeight*nLines)+1; + else if ( nStyle & TEXT_DRAW_VCENTER ) + { + aRect.Top() += (aRect.GetHeight()-(nTextHeight*nLines))/2; + aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1; + } + else + aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1; + + aRect.Right()++; // #99188# get rid of rounding problems when using this rect later + return aRect; +} + +// ----------------------------------------------------------------------- + +static sal_Bool ImplIsCharIn( xub_Unicode c, const sal_Char* pStr ) +{ + while ( *pStr ) + { + if ( *pStr == c ) + return sal_True; + pStr++; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +String OutputDevice::GetEllipsisString( const String& rOrigStr, long nMaxWidth, + sal_uInt16 nStyle ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DefaultTextLayout aTextLayout( *const_cast< OutputDevice* >( this ) ); + return ImplGetEllipsisString( *this, rOrigStr, nMaxWidth, nStyle, aTextLayout ); +} + +// ----------------------------------------------------------------------- + +String OutputDevice::ImplGetEllipsisString( const OutputDevice& rTargetDevice, const XubString& rOrigStr, long nMaxWidth, + sal_uInt16 nStyle, const ::vcl::ITextLayout& _rLayout ) +{ + OSL_TRACE( "OutputDevice::ImplGetEllipsisString()" ); + + String aStr = rOrigStr; + xub_StrLen nIndex = _rLayout.GetTextBreak( aStr, nMaxWidth, 0, aStr.Len() ); + + + if ( nIndex != STRING_LEN ) + { + if( (nStyle & TEXT_DRAW_CENTERELLIPSIS) == TEXT_DRAW_CENTERELLIPSIS ) + { + String aTmpStr( aStr ); + xub_StrLen nEraseChars = 4; + while( nEraseChars < aStr.Len() && _rLayout.GetTextWidth( aTmpStr, 0, aTmpStr.Len() ) > nMaxWidth ) + { + aTmpStr = aStr; + xub_StrLen i = (aTmpStr.Len() - nEraseChars)/2; + aTmpStr.Erase( i, nEraseChars++ ); + aTmpStr.InsertAscii( "...", i ); + } + aStr = aTmpStr; + } + else if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) + { + aStr.Erase( nIndex ); + if ( nIndex > 1 ) + { + aStr.AppendAscii( "..." ); + while ( aStr.Len() && (_rLayout.GetTextWidth( aStr, 0, aStr.Len() ) > nMaxWidth) ) + { + if ( (nIndex > 1) || (nIndex == aStr.Len()) ) + nIndex--; + aStr.Erase( nIndex, 1 ); + } + } + + if ( !aStr.Len() && (nStyle & TEXT_DRAW_CLIP) ) + aStr += rOrigStr.GetChar( 0 ); + } + else if ( nStyle & TEXT_DRAW_PATHELLIPSIS ) + { + rtl::OUString aPath( rOrigStr ); + rtl::OUString aAbbreviatedPath; + osl_abbreviateSystemPath( aPath.pData, &aAbbreviatedPath.pData, nIndex, NULL ); + aStr = aAbbreviatedPath; + } + else if ( nStyle & TEXT_DRAW_NEWSELLIPSIS ) + { + static sal_Char const pSepChars[] = "."; + // Letztes Teilstueck ermitteln + xub_StrLen nLastContent = aStr.Len(); + while ( nLastContent ) + { + nLastContent--; + if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) ) + break; + } + while ( nLastContent && + ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) ) + nLastContent--; + + XubString aLastStr( aStr, nLastContent, aStr.Len() ); + XubString aTempLastStr1( RTL_CONSTASCII_USTRINGPARAM( "..." ) ); + aTempLastStr1 += aLastStr; + if ( _rLayout.GetTextWidth( aTempLastStr1, 0, aTempLastStr1.Len() ) > nMaxWidth ) + aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout ); + else + { + sal_uInt16 nFirstContent = 0; + while ( nFirstContent < nLastContent ) + { + nFirstContent++; + if ( ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) ) + break; + } + while ( (nFirstContent < nLastContent) && + ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) ) + nFirstContent++; + + if ( nFirstContent >= nLastContent ) + aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout ); + else + { + if ( nFirstContent > 4 ) + nFirstContent = 4; + XubString aFirstStr( aStr, 0, nFirstContent ); + aFirstStr.AppendAscii( "..." ); + XubString aTempStr = aFirstStr; + aTempStr += aLastStr; + if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.Len() ) > nMaxWidth ) + aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout ); + else + { + do + { + aStr = aTempStr; + if( nLastContent > aStr.Len() ) + nLastContent = aStr.Len(); + while ( nFirstContent < nLastContent ) + { + nLastContent--; + if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) ) + break; + + } + while ( (nFirstContent < nLastContent) && + ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) ) + nLastContent--; + + if ( nFirstContent < nLastContent ) + { + XubString aTempLastStr( aStr, nLastContent, aStr.Len() ); + aTempStr = aFirstStr; + aTempStr += aTempLastStr; + if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.Len() ) > nMaxWidth ) + break; + } + } + while ( nFirstContent < nLastContent ); + } + } + } + } + } + + return aStr; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawCtrlText( const Point& rPos, const XubString& rStr, + xub_StrLen nIndex, xub_StrLen nLen, + sal_uInt16 nStyle, MetricVector* pVector, String* pDisplayText ) +{ + OSL_TRACE( "OutputDevice::DrawCtrlText()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.Len()) ) + return; + + // better get graphics here because ImplDrawMnemonicLine() will not + // we need a graphics + if( !mpGraphics && !ImplGetGraphics() ) + return; + if( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if( nIndex >= rStr.Len() ) + return; + if( (sal_uLong)nIndex+nLen >= rStr.Len() ) + nLen = rStr.Len() - nIndex; + + XubString aStr = rStr; + xub_StrLen nMnemonicPos = STRING_NOTFOUND; + + long nMnemonicX = 0; + long nMnemonicY = 0; + long nMnemonicWidth = 0; + if ( (nStyle & TEXT_DRAW_MNEMONIC) && nLen > 1 ) + { + aStr = GetNonMnemonicString( aStr, nMnemonicPos ); + if ( nMnemonicPos != STRING_NOTFOUND ) + { + if( nMnemonicPos < nIndex ) + --nIndex; + else if( nLen < STRING_LEN ) + { + if( nMnemonicPos < (nIndex+nLen) ) + --nLen; + DBG_ASSERT( nMnemonicPos < (nIndex+nLen), "Mnemonic underline marker after last character" ); + } + sal_Bool bInvalidPos = sal_False; + + if( nMnemonicPos >= nLen ) + { + // #106952# + // may occur in BiDi-Strings: the '~' is sometimes found behind the last char + // due to some strange BiDi text editors + // ->place the underline behind the string to indicate a failure + bInvalidPos = sal_True; + nMnemonicPos = nLen-1; + } + + sal_Int32* pCaretXArray = (sal_Int32*)alloca( 2 * sizeof(sal_Int32) * nLen ); + /*sal_Bool bRet =*/ GetCaretPositions( aStr, pCaretXArray, nIndex, nLen ); + long lc_x1 = pCaretXArray[ 2*(nMnemonicPos - nIndex) ]; + long lc_x2 = pCaretXArray[ 2*(nMnemonicPos - nIndex)+1 ]; + nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2)); + + Point aTempPos( Min(lc_x1,lc_x2), GetFontMetric().GetAscent() ); + if( bInvalidPos ) // #106952#, place behind the (last) character + aTempPos = Point( Max(lc_x1,lc_x2), GetFontMetric().GetAscent() ); + + aTempPos += rPos; + aTempPos = LogicToPixel( aTempPos ); + nMnemonicX = mnOutOffX + aTempPos.X(); + nMnemonicY = mnOutOffY + aTempPos.Y(); + } + } + + if ( nStyle & TEXT_DRAW_DISABLE && ! pVector ) + { + Color aOldTextColor; + Color aOldTextFillColor; + sal_Bool bRestoreFillColor; + sal_Bool bHighContrastBlack = sal_False; + sal_Bool bHighContrastWhite = sal_False; + const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() ); + if( rStyleSettings.GetHighContrastMode() ) + { + if( IsBackground() ) + { + Wallpaper aWall = GetBackground(); + Color aCol = aWall.GetColor(); + bHighContrastBlack = aCol.IsDark(); + bHighContrastWhite = aCol.IsBright(); + } + } + + aOldTextColor = GetTextColor(); + if ( IsTextFillColor() ) + { + bRestoreFillColor = sal_True; + aOldTextFillColor = GetTextFillColor(); + } + else + bRestoreFillColor = sal_False; + + if( bHighContrastBlack ) + SetTextColor( COL_GREEN ); + else if( bHighContrastWhite ) + SetTextColor( COL_LIGHTGREEN ); + else + SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() ); + + DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText ); + if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector ) + { + if ( nMnemonicPos != STRING_NOTFOUND ) + ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); + } + SetTextColor( aOldTextColor ); + if ( bRestoreFillColor ) + SetTextFillColor( aOldTextFillColor ); + } + else + { + DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText ); + if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector ) + { + if ( nMnemonicPos != STRING_NOTFOUND ) + ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawCtrlText( rPos, rStr, nIndex, nLen, nStyle, pVector, pDisplayText ); +} + +// ----------------------------------------------------------------------- + +long OutputDevice::GetCtrlTextWidth( const String& rStr, + xub_StrLen nIndex, xub_StrLen nLen, + sal_uInt16 nStyle ) const +{ + OSL_TRACE( "OutputDevice::GetCtrlTextSize()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( nStyle & TEXT_DRAW_MNEMONIC ) + { + xub_StrLen nMnemonicPos; + XubString aStr = GetNonMnemonicString( rStr, nMnemonicPos ); + if ( nMnemonicPos != STRING_NOTFOUND ) + { + if ( nMnemonicPos < nIndex ) + nIndex--; + else if ( (nLen < STRING_LEN) && + (nMnemonicPos >= nIndex) && (nMnemonicPos < (sal_uLong)(nIndex+nLen)) ) + nLen--; + } + return GetTextWidth( aStr, nIndex, nLen ); + } + else + return GetTextWidth( rStr, nIndex, nLen ); +} + +// ----------------------------------------------------------------------- + +String OutputDevice::GetNonMnemonicString( const String& rStr, xub_StrLen& rMnemonicPos ) +{ + String aStr = rStr; + xub_StrLen nLen = aStr.Len(); + xub_StrLen i = 0; + + rMnemonicPos = STRING_NOTFOUND; + while ( i < nLen ) + { + if ( aStr.GetChar( i ) == '~' ) + { + if ( aStr.GetChar( i+1 ) != '~' ) + { + if ( rMnemonicPos == STRING_NOTFOUND ) + rMnemonicPos = i; + aStr.Erase( i, 1 ); + nLen--; + } + else + { + aStr.Erase( i, 1 ); + nLen--; + i++; + } + } + else + i++; + } + + return aStr; +} + +// ----------------------------------------------------------------------- + +int OutputDevice::GetDevFontCount() const +{ + OSL_TRACE( "OutputDevice::GetDevFontCount()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if( !mpGetDevFontList ) + mpGetDevFontList = mpFontList->GetDevFontList(); + return mpGetDevFontList->Count(); +} + +// ----------------------------------------------------------------------- + +FontInfo OutputDevice::GetDevFont( int nDevFontIndex ) const +{ + OSL_TRACE( "OutputDevice::GetDevFont()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + FontInfo aFontInfo; + + ImplInitFontList(); + + int nCount = GetDevFontCount(); + if( nDevFontIndex < nCount ) + { + const ImplFontData& rData = *mpGetDevFontList->Get( nDevFontIndex ); + aFontInfo.SetName( rData.maName ); + aFontInfo.SetStyleName( rData.maStyleName ); + aFontInfo.SetCharSet( rData.mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); + aFontInfo.SetFamily( rData.meFamily ); + aFontInfo.SetPitch( rData.mePitch ); + aFontInfo.SetWeight( rData.meWeight ); + aFontInfo.SetItalic( rData.meItalic ); + aFontInfo.SetWidthType( rData.meWidthType ); + if( rData.IsScalable() ) + aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG; + if( rData.mbDevice ) + aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG; + } + + return aFontInfo; +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::AddTempDevFont( const String& rFileURL, const String& rFontName ) +{ + OSL_TRACE( "OutputDevice::AddTempDevFont()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + ImplInitFontList(); + + if( !mpGraphics && !ImplGetGraphics() ) + return sal_False; + + bool bRC = mpGraphics->AddTempDevFont( mpFontList, rFileURL, rFontName ); + if( !bRC ) + return sal_False; + + if( mpAlphaVDev ) + mpAlphaVDev->AddTempDevFont( rFileURL, rFontName ); + + mpFontCache->Invalidate(); + return sal_True; +} + +// ----------------------------------------------------------------------- + +int OutputDevice::GetDevFontSizeCount( const Font& rFont ) const +{ + OSL_TRACE( "OutputDevice::GetDevFontSizeCount()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + delete mpGetDevSizeList; + + ImplInitFontList(); + mpGetDevSizeList = mpFontList->GetDevSizeList( rFont.GetName() ); + return mpGetDevSizeList->Count(); +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::GetDevFontSize( const Font& rFont, int nSizeIndex ) const +{ + OSL_TRACE( "OutputDevice::GetDevFontSize()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // check range + int nCount = GetDevFontSizeCount( rFont ); + if ( nSizeIndex >= nCount ) + return Size(); + + // when mapping is enabled round to .5 points + Size aSize( 0, mpGetDevSizeList->Get( nSizeIndex ) ); + if ( mbMap ) + { + aSize.Height() *= 10; + MapMode aMap( MAP_10TH_INCH, Point(), Fraction( 1, 72 ), Fraction( 1, 72 ) ); + aSize = PixelToLogic( aSize, aMap ); + aSize.Height() += 5; + aSize.Height() /= 10; + long nRound = aSize.Height() % 5; + if ( nRound >= 3 ) + aSize.Height() += (5-nRound); + else + aSize.Height() -= nRound; + aSize.Height() *= 10; + aSize = LogicToPixel( aSize, aMap ); + aSize = PixelToLogic( aSize ); + aSize.Height() += 5; + aSize.Height() /= 10; + } + return aSize; +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::IsFontAvailable( const String& rFontName ) const +{ + OSL_TRACE( "OutputDevice::IsFontAvailable()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + ImplDevFontListData* pFound = mpFontList->FindFontFamily( rFontName ); + return (pFound != NULL); +} + +// ----------------------------------------------------------------------- + +FontMetric OutputDevice::GetFontMetric() const +{ + OSL_TRACE( "OutputDevice::GetFontMetric()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + FontMetric aMetric; + if( mbNewFont && !ImplNewFont() ) + return aMetric; + + ImplFontEntry* pEntry = mpFontEntry; + ImplFontMetricData* pMetric = &(pEntry->maMetric); + + // prepare metric + aMetric.Font::operator=( maFont ); + + // set aMetric with info from font + aMetric.SetName( maFont.GetName() ); + aMetric.SetStyleName( pMetric->maStyleName ); + aMetric.SetSize( PixelToLogic( Size( pMetric->mnWidth, pMetric->mnAscent+pMetric->mnDescent-pMetric->mnIntLeading ) ) ); + aMetric.SetCharSet( pMetric->mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); + aMetric.SetFamily( pMetric->meFamily ); + aMetric.SetPitch( pMetric->mePitch ); + aMetric.SetWeight( pMetric->meWeight ); + aMetric.SetItalic( pMetric->meItalic ); + aMetric.SetWidthType( pMetric->meWidthType ); + if ( pEntry->mnOwnOrientation ) + aMetric.SetOrientation( pEntry->mnOwnOrientation ); + else + aMetric.SetOrientation( pMetric->mnOrientation ); + if( !pEntry->maMetric.mbKernableFont ) + aMetric.SetKerning( maFont.GetKerning() & ~KERNING_FONTSPECIFIC ); + + // set remaining metric fields + aMetric.mpImplMetric->mnMiscFlags = 0; + if( pMetric->mbDevice ) + aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG; + if( pMetric->mbScalableFont ) + aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG; + aMetric.mpImplMetric->mnAscent = ImplDevicePixelToLogicHeight( pMetric->mnAscent+mnEmphasisAscent ); + aMetric.mpImplMetric->mnDescent = ImplDevicePixelToLogicHeight( pMetric->mnDescent+mnEmphasisDescent ); + aMetric.mpImplMetric->mnIntLeading = ImplDevicePixelToLogicHeight( pMetric->mnIntLeading+mnEmphasisAscent ); + aMetric.mpImplMetric->mnExtLeading = ImplDevicePixelToLogicHeight( pMetric->mnExtLeading ); + aMetric.mpImplMetric->mnLineHeight = ImplDevicePixelToLogicHeight( pMetric->mnAscent+pMetric->mnDescent+mnEmphasisAscent+mnEmphasisDescent ); + aMetric.mpImplMetric->mnSlant = ImplDevicePixelToLogicHeight( pMetric->mnSlant ); + +#ifdef UNX + // backwards compatible line metrics after fixing #i60945# + if( (meOutDevType == OUTDEV_VIRDEV) + && static_cast<const VirtualDevice*>(this)->ForceZeroExtleadBug() ) + aMetric.mpImplMetric->mnExtLeading = 0; +#endif + + return aMetric; +} + +// ----------------------------------------------------------------------- + +FontMetric OutputDevice::GetFontMetric( const Font& rFont ) const +{ + // select font, query metrics, select original font again + Font aOldFont = GetFont(); + const_cast<OutputDevice*>(this)->SetFont( rFont ); + FontMetric aMetric( GetFontMetric() ); + const_cast<OutputDevice*>(this)->SetFont( aOldFont ); + return aMetric; +} + +// ----------------------------------------------------------------------- + +/** OutputDevice::GetSysFontData + * + * @param nFallbacklevel Fallback font level (0 = best matching font) + * + * Retrieve detailed font information in platform independent structure + * + * @return SystemFontData + **/ +SystemFontData OutputDevice::GetSysFontData(int nFallbacklevel) const +{ + SystemFontData aSysFontData; + aSysFontData.nSize = sizeof(aSysFontData); + + if (!mpGraphics) ImplGetGraphics(); + if (mpGraphics) aSysFontData = mpGraphics->GetSysFontData(nFallbacklevel); + + return aSysFontData; +} + + +// ----------------------------------------------------------------------- + +/** OutputDevice::GetSysTextLayoutData + * + * @param rStartPt Start point of the text + * @param rStr Text string that will be transformed into layout of glyphs + * @param nIndex Position in the string from where layout will be done + * @param nLen Length of the string + * @param pDXAry Custom layout adjustment data + * + * Export finalized glyph layout data as platform independent SystemTextLayoutData + * (see vcl/inc/vcl/sysdata.hxx) + * + * Only parameters rStartPt and rStr are mandatory, the rest is optional + * (default values will be used) + * + * @return SystemTextLayoutData + **/ +SystemTextLayoutData OutputDevice::GetSysTextLayoutData(const Point& rStartPt, const XubString& rStr, xub_StrLen nIndex, xub_StrLen nLen, + const sal_Int32* pDXAry) const +{ + OSL_TRACE( "OutputDevice::GetSysTextLayoutData()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + SystemTextLayoutData aSysLayoutData; + aSysLayoutData.nSize = sizeof(aSysLayoutData); + aSysLayoutData.rGlyphData.reserve( 256 ); + + if ( mpMetaFile ) { + if (pDXAry) + mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) ); + else + mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) ); + } + + if ( !IsDeviceOutputNecessary() ) return aSysLayoutData; + + SalLayout* rLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true ); + + // setup glyphs + Point aPos; + sal_GlyphId aGlyphId; + for( int nStart = 0; rLayout->GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); ) + { + // NOTE: Windows backend is producing unicode chars (ucs4), so on windows, + // ETO_GLYPH_INDEX is unusable, unless extra glyph conversion is made. + + SystemGlyphData aGlyph; + aGlyph.index = static_cast<unsigned long> (aGlyphId & GF_IDXMASK); + aGlyph.x = aPos.X(); + aGlyph.y = aPos.Y(); + int nLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT; + aGlyph.fallbacklevel = nLevel < MAX_FALLBACK ? nLevel : 0; + aSysLayoutData.rGlyphData.push_back(aGlyph); + } + + // Get font data + aSysLayoutData.orientation = rLayout->GetOrientation(); + + rLayout->Release(); + + return aSysLayoutData; +} + +// ----------------------------------------------------------------------- + + +long OutputDevice::GetMinKashida() const +{ + OSL_TRACE( "OutputDevice::GetMinKashida()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + if( mbNewFont && !ImplNewFont() ) + return 0; + + ImplFontEntry* pEntry = mpFontEntry; + ImplFontMetricData* pMetric = &(pEntry->maMetric); + return ImplDevicePixelToLogicWidth( pMetric->mnMinKashida ); +} +// ----------------------------------------------------------------------- + +long OutputDevice::GetMinKashida( const Font& rFont ) const +{ + // select font, query Kashida, select original font again + Font aOldFont = GetFont(); + const_cast<OutputDevice*>(this)->SetFont( rFont ); + long aKashida = GetMinKashida(); + const_cast<OutputDevice*>(this)->SetFont( aOldFont ); + return aKashida; +} + +// ----------------------------------------------------------------------- +xub_StrLen OutputDevice::ValidateKashidas ( const String& rTxt, + xub_StrLen nIdx, xub_StrLen nLen, + xub_StrLen nKashCount, + const xub_StrLen* pKashidaPos, + xub_StrLen* pKashidaPosDropped ) const +{ + // do layout + SalLayout* pSalLayout = ImplLayout( rTxt, nIdx, nLen ); + if( !pSalLayout ) + return 0; + xub_StrLen nDropped = 0; + for( int i = 0; i < nKashCount; ++i ) + { + if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] )) + { + pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ]; + ++nDropped; + } + } + pSalLayout->Release(); + return nDropped; +} + + + +// ----------------------------------------------------------------------- + + +// TODO: best is to get rid of this method completely +sal_uLong OutputDevice::GetKerningPairCount() const +{ + OSL_TRACE( "OutputDevice::GetKerningPairCount()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if( mbNewFont && !ImplNewFont() ) + return 0; + if( mbInitFont ) + ImplInitFont(); + + if( mpPDFWriter && mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) ) + return 0; + + // get the kerning pair count from the device layer + int nKernPairs = mpGraphics->GetKernPairs( 0, NULL ); + return nKernPairs; +} + +// ----------------------------------------------------------------------- + +inline bool CmpKernData( const KerningPair& a, const KerningPair& b ) +{ + return (a.nChar1 < b.nChar1) || ((a.nChar1 == a.nChar2) && (a.nChar2 < a.nChar2)); +} + +// TODO: best is to get rid of this method completely +void OutputDevice::GetKerningPairs( sal_uLong nRequestedPairs, KerningPair* pKernPairs ) const +{ + OSL_TRACE( "OutputDevice::GetKerningPairs()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if( mbNewFont && !ImplNewFont() ) + return; + if( mbInitFont ) + ImplInitFont(); + + if( mpPDFWriter && mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) ) + return; + + // get the kerning pairs directly from the device layer + int nKernPairs = mpGraphics->GetKernPairs( nRequestedPairs, (ImplKernPairData*)pKernPairs ); + + // sort kerning pairs + std::sort( pKernPairs, pKernPairs+nKernPairs, CmpKernData ); +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const String& rStr, + int nIndex, int nLen, int nBase, MetricVector& rVector ) +{ + OSL_TRACE( "OutputDevice::GetGlyphBoundRect_CTL()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + rVector.clear(); + + if( nLen == STRING_LEN ) + nLen = rStr.Len() - nIndex; + + Rectangle aRect; + for( int i = 0; i < nLen; i++ ) + { + if( !GetTextBoundRect( aRect, rStr, sal::static_int_cast<xub_StrLen>(nBase), sal::static_int_cast<xub_StrLen>(nIndex+i), 1 ) ) + break; + aRect.Move( rOrigin.X(), rOrigin.Y() ); + rVector.push_back( aRect ); + } + + return (nLen == (int)rVector.size()); +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::GetTextBoundRect( Rectangle& rRect, + const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen, + sal_uLong nLayoutWidth, const sal_Int32* pDXAry ) const +{ + OSL_TRACE( "OutputDevice::GetTextBoundRect()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + sal_Bool bRet = sal_False; + rRect.SetEmpty(); + + SalLayout* pSalLayout = NULL; + const Point aPoint; + // calculate offset when nBase!=nIndex + long nXOffset = 0; + if( nBase != nIndex ) + { + xub_StrLen nStart = Min( nBase, nIndex ); + xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart; + pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry ); + if( pSalLayout ) + { + nXOffset = pSalLayout->GetTextWidth(); + nXOffset /= pSalLayout->GetUnitsPerPixel(); + pSalLayout->Release(); + // TODO: fix offset calculation for Bidi case + if( nBase < nIndex) + nXOffset = -nXOffset; + } + } + + pSalLayout = ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry ); + Rectangle aPixelRect; + if( pSalLayout ) + { + bRet = pSalLayout->GetBoundRect( *mpGraphics, aPixelRect ); + + if( bRet ) + { + int nWidthFactor = pSalLayout->GetUnitsPerPixel(); + + if( nWidthFactor > 1 ) + { + double fFactor = 1.0 / nWidthFactor; + aPixelRect.Left() + = static_cast< long >(aPixelRect.Left() * fFactor); + aPixelRect.Right() + = static_cast< long >(aPixelRect.Right() * fFactor); + aPixelRect.Top() + = static_cast< long >(aPixelRect.Top() * fFactor); + aPixelRect.Bottom() + = static_cast< long >(aPixelRect.Bottom() * fFactor); + } + + Point aRotatedOfs( mnTextOffX, mnTextOffY ); + aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) ); + aPixelRect += aRotatedOfs; + rRect = PixelToLogic( aPixelRect ); + if( mbMap ) + rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY ); + } + + pSalLayout->Release(); + } + + if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry ) + return bRet; + + // fall back to bitmap method to get the bounding rectangle, + // so we need a monochrome virtual device with matching font + VirtualDevice aVDev( 1 ); + Font aFont( GetFont() ); + aFont.SetShadow( sal_False ); + aFont.SetOutline( sal_False ); + aFont.SetRelief( RELIEF_NONE ); + aFont.SetOrientation( 0 ); + aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) ); + aVDev.SetFont( aFont ); + aVDev.SetTextAlign( ALIGN_TOP ); + + // layout the text on the virtual device + pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry ); + if( !pSalLayout ) + return false; + + // make the bitmap big enough + // TODO: use factors when it would get too big + long nWidth = pSalLayout->GetTextWidth(); + long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; + Point aOffset( nWidth/2, 8 ); + Size aOutSize( nWidth + 2*aOffset.X(), nHeight + 2*aOffset.Y() ); + if( !nWidth || !aVDev.SetOutputSizePixel( aOutSize ) ) + return false; + + // draw text in black + pSalLayout->DrawBase() = aOffset; + aVDev.SetTextColor( Color( COL_BLACK ) ); + aVDev.SetTextFillColor(); + aVDev.ImplInitTextColor(); + aVDev.ImplDrawText( *pSalLayout ); + pSalLayout->Release(); + + // find extents using the bitmap + Bitmap aBmp = aVDev.GetBitmap( Point(), aOutSize ); + BitmapReadAccess* pAcc = aBmp.AcquireReadAccess(); + if( !pAcc ) + return sal_False; + const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) ); + const long nW = pAcc->Width(); + const long nH = pAcc->Height(); + long nLeft = 0; + long nRight = 0; + + // find top left point + long nTop = 0; + for(; nTop < nH; ++nTop ) + { + for( nLeft = 0; nLeft < nW; ++nLeft ) + if( pAcc->GetPixel( nTop, nLeft ) == aBlack ) + break; + if( nLeft < nW ) + break; + } + + // find bottom right point + long nBottom = nH; + while( --nBottom >= nTop ) + { + for( nRight = nW; --nRight >= 0; ) + if( pAcc->GetPixel( nBottom, nRight ) == aBlack ) + break; + if( nRight >= 0 ) + break; + } + if( nRight < nLeft ) + { + long nX = nRight; + nRight = nLeft; + nLeft = nX; + } + + for( long nY = nTop; nY <= nBottom; ++nY ) + { + // find leftmost point + long nX; + for( nX = 0; nX < nLeft; ++nX ) + if( pAcc->GetPixel( nY, nX ) == aBlack ) + break; + nLeft = nX; + + // find rightmost point + for( nX = nW; --nX > nRight; ) + if( pAcc->GetPixel( nY, nX ) == aBlack ) + break; + nRight = nX; + } + + aBmp.ReleaseAccess( pAcc ); + + if( nTop <= nBottom ) + { + Size aSize( nRight - nLeft + 1, nBottom - nTop + 1 ); + Point aTopLeft( nLeft, nTop ); + aTopLeft -= aOffset; + // adjust to text alignment + aTopLeft.Y()+= mnTextOffY - (mpFontEntry->maMetric.mnAscent + mnEmphasisAscent); + // convert to logical coordinates + aSize = PixelToLogic( aSize ); + aTopLeft.X() = ImplDevicePixelToLogicWidth( aTopLeft.X() ); + aTopLeft.Y() = ImplDevicePixelToLogicHeight( aTopLeft.Y() ); + rRect = Rectangle( aTopLeft, aSize ); + return sal_True; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::GetTextOutlines( ::basegfx::B2DPolyPolygonVector& rVector, + const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen, + sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const +{ + // the fonts need to be initialized + if( mbNewFont ) + ImplNewFont(); + if( mbInitFont ) + ImplInitFont(); + if( !mpFontEntry ) + return sal_False; + + sal_Bool bRet = sal_False; + rVector.clear(); + if( nLen == STRING_LEN ) + nLen = rStr.Len() - nIndex; + rVector.reserve( nLen ); + + // we want to get the Rectangle in logical units, so to + // avoid rounding errors we just size the font in logical units + sal_Bool bOldMap = mbMap; + if( bOldMap ) + { + const_cast<OutputDevice&>(*this).mbMap = sal_False; + const_cast<OutputDevice&>(*this).mbNewFont = sal_True; + } + + SalLayout* pSalLayout = NULL; + + // calculate offset when nBase!=nIndex + long nXOffset = 0; + if( nBase != nIndex ) + { + xub_StrLen nStart = Min( nBase, nIndex ); + xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart; + pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nTWidth, pDXArray ); + if( pSalLayout ) + { + nXOffset = pSalLayout->GetTextWidth(); + pSalLayout->Release(); + // TODO: fix offset calculation for Bidi case + if( nBase > nIndex) + nXOffset = -nXOffset; + } + } + + pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray ); + if( pSalLayout ) + { + bRet = pSalLayout->GetOutline( *mpGraphics, rVector ); + if( bRet ) + { + // transform polygon to pixel units + ::basegfx::B2DHomMatrix aMatrix; + + int nWidthFactor = pSalLayout->GetUnitsPerPixel(); + if( nXOffset | mnTextOffX | mnTextOffY ) + { + Point aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor ); + aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) ); + aMatrix.translate( aRotatedOfs.X(), aRotatedOfs.Y() ); + } + + if( nWidthFactor > 1 ) + { + double fFactor = 1.0 / nWidthFactor; + aMatrix.scale( fFactor, fFactor ); + } + + if( !aMatrix.isIdentity() ) + { + ::basegfx::B2DPolyPolygonVector::iterator aIt = rVector.begin(); + for(; aIt != rVector.end(); ++aIt ) + (*aIt).transform( aMatrix ); + } + } + + pSalLayout->Release(); + } + + if( bOldMap ) + { + // restore original font size and map mode + const_cast<OutputDevice&>(*this).mbMap = bOldMap; + const_cast<OutputDevice&>(*this).mbNewFont = sal_True; + } + + if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry ) + return bRet; + + // fall back to bitmap conversion ------------------------------------------ + + // Here, we can savely assume that the mapping between characters and glyphs + // is one-to-one. This is most probably valid for the old bitmap fonts. + + // fall back to bitmap method to get the bounding rectangle, + // so we need a monochrome virtual device with matching font + pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray ); + if (pSalLayout == 0) + return false; + long nOrgWidth = pSalLayout->GetTextWidth(); + long nOrgHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + + mnEmphasisDescent; + pSalLayout->Release(); + + VirtualDevice aVDev(1); + + Font aFont(GetFont()); + aFont.SetShadow(false); + aFont.SetOutline(false); + aFont.SetRelief(RELIEF_NONE); + aFont.SetOrientation(0); + if( bOptimize ) + { + aFont.SetSize( Size( 0, GLYPH_FONT_HEIGHT ) ); + aVDev.SetMapMode( MAP_PIXEL ); + } + aVDev.SetFont( aFont ); + aVDev.SetTextAlign( ALIGN_TOP ); + aVDev.SetTextColor( Color(COL_BLACK) ); + aVDev.SetTextFillColor(); + + pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray ); + if (pSalLayout == 0) + return false; + long nWidth = pSalLayout->GetTextWidth(); + long nHeight = ((OutputDevice*)&aVDev)->mpFontEntry->mnLineHeight + ((OutputDevice*)&aVDev)->mnEmphasisAscent + + ((OutputDevice*)&aVDev)->mnEmphasisDescent; + pSalLayout->Release(); + + if( !nWidth || !nHeight ) + return sal_True; + double fScaleX = static_cast< double >(nOrgWidth) / nWidth; + double fScaleY = static_cast< double >(nOrgHeight) / nHeight; + + // calculate offset when nBase!=nIndex + // TODO: fix offset calculation for Bidi case + nXOffset = 0; + if( nBase != nIndex ) + { + xub_StrLen nStart = ((nBase < nIndex) ? nBase : nIndex); + xub_StrLen nLength = ((nBase > nIndex) ? nBase : nIndex) - nStart; + pSalLayout = aVDev.ImplLayout( rStr, nStart, nLength, Point(0,0), nTWidth, pDXArray ); + if( pSalLayout ) + { + nXOffset = pSalLayout->GetTextWidth(); + pSalLayout->Release(); + if( nBase > nIndex) + nXOffset = -nXOffset; + } + } + + bRet = true; + bool bRTL = false; + String aStr( rStr ); // prepare for e.g. localized digits + ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL ); + for( int nCharPos = -1; aLayoutArgs.GetNextPos( &nCharPos, &bRTL);) + { + bool bSuccess = false; + + // draw character into virtual device + pSalLayout = aVDev.ImplLayout( rStr, static_cast< xub_StrLen >(nCharPos), 1, Point(0,0), nTWidth, pDXArray ); + if (pSalLayout == 0) + return false; + long nCharWidth = pSalLayout->GetTextWidth(); + + Point aOffset(nCharWidth / 2, 8); + Size aSize(nCharWidth + 2 * aOffset.X(), nHeight + 2 * aOffset.Y()); + bSuccess = (bool)aVDev.SetOutputSizePixel(aSize); + if( bSuccess ) + { + // draw glyph into virtual device + aVDev.Erase(); + pSalLayout->DrawBase() += aOffset; + pSalLayout->DrawBase() += Point( ((OutputDevice*)&aVDev)->mnTextOffX, ((OutputDevice*)&aVDev)->mnTextOffY ); + pSalLayout->DrawText( *((OutputDevice*)&aVDev)->mpGraphics ); + pSalLayout->Release(); + + // convert character image into outline + Bitmap aBmp( aVDev.GetBitmap(Point(0, 0), aSize)); + + PolyPolygon aPolyPoly; + bool bVectorized = aBmp.Vectorize(aPolyPoly, BMP_VECTORIZE_OUTER | BMP_VECTORIZE_REDUCE_EDGES); + if( !bVectorized ) + bSuccess = false; + else + { + // convert units to logical width + for (sal_uInt16 j = 0; j < aPolyPoly.Count(); ++j) + { + Polygon& rPoly = aPolyPoly[j]; + for (sal_uInt16 k = 0; k < rPoly.GetSize(); ++k) + { + Point& rPt = rPoly[k]; + rPt -= aOffset; + int nPixelX = rPt.X() - ((OutputDevice&)aVDev).mnTextOffX + nXOffset; + int nPixelY = rPt.Y() - ((OutputDevice&)aVDev).mnTextOffY; + rPt.X() = ImplDevicePixelToLogicWidth( nPixelX ); + rPt.Y() = ImplDevicePixelToLogicHeight( nPixelY ); + } + } + + + // ignore "empty" glyphs: + if( aPolyPoly.Count() > 0 ) + { + // convert to B2DPolyPolygon + // TODO: get rid of intermediate tool's PolyPolygon + ::basegfx::B2DPolyPolygon aB2DPolyPoly = aPolyPoly.getB2DPolyPolygon(); + ::basegfx::B2DHomMatrix aMatrix; + aMatrix.scale( fScaleX, fScaleY ); + int nAngle = GetFont().GetOrientation(); + if( nAngle ) + aMatrix.rotate( nAngle * F_PI1800 ); + aB2DPolyPoly.transform( aMatrix ); + rVector.push_back( aB2DPolyPoly ); + } + } + } + + nXOffset += nCharWidth; + bRet = bRet && bSuccess; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::GetTextOutlines( PolyPolyVector& rResultVector, + const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, + xub_StrLen nLen, sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const +{ + rResultVector.clear(); + + // get the basegfx polypolygon vector + ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector; + if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen, + bOptimize, nTWidth, pDXArray ) ) + return sal_False; + + // convert to a tool polypolygon vector + rResultVector.reserve( aB2DPolyPolyVector.size() ); + ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin(); + for(; aIt != aB2DPolyPolyVector.end(); ++aIt ) + rResultVector.push_back(PolyPolygon(*aIt)); // #i76339# + + return sal_True; +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::GetTextOutline( PolyPolygon& rPolyPoly, + const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen, + sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const +{ + rPolyPoly.Clear(); + + // get the basegfx polypolygon vector + ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector; + if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen, + bOptimize, nTWidth, pDXArray ) ) + return sal_False; + + // convert and merge into a tool polypolygon + ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin(); + for(; aIt != aB2DPolyPolyVector.end(); ++aIt ) + for( unsigned int i = 0; i < aIt->count(); ++i ) + rPolyPoly.Insert(Polygon((*aIt).getB2DPolygon( i ))); // #i76339# + + return sal_True; +} + +bool OutputDevice::GetFontCapabilities( FontCapabilities& rFontCapabilities ) const +{ + // we need a graphics + if( !mpGraphics && !ImplGetGraphics() ) + return false; + + if( mbNewFont ) + ImplNewFont(); + if( mbInitFont ) + ImplInitFont(); + if( !mpFontEntry ) + return false; + + return mpGraphics->GetImplFontCapabilities(rFontCapabilities); +} + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::GetFontCharMap( FontCharMap& rFontCharMap ) const +{ + rFontCharMap.Reset(); + + // we need a graphics + if( !mpGraphics && !ImplGetGraphics() ) + return sal_False; + + if( mbNewFont ) + ImplNewFont(); + if( mbInitFont ) + ImplInitFont(); + if( !mpFontEntry ) + return sal_False; + +#ifdef ENABLE_IFC_CACHE // a little font charmap cache helps considerably + static const int NMAXITEMS = 16; + static int nUsedItems = 0, nCurItem = 0; + + struct CharMapCacheItem { const ImplFontData* mpFontData; FontCharMap maCharMap; }; + static CharMapCacheItem aCache[ NMAXITEMS ]; + + const ImplFontData* pFontData = mpFontEntry->maFontSelData.mpFontData; + + int i; + for( i = nUsedItems; --i >= 0; ) + if( pFontData == aCache[i].mpFontData ) + break; + if( i >= 0 ) // found in cache + { + rFontCharMap.Reset( aCache[i].maCharMap.mpImpl ); + } + else // need to cache +#endif // ENABLE_IFC_CACHE + { + const ImplFontCharMap* pNewMap = mpGraphics->GetImplFontCharMap(); + rFontCharMap.Reset( pNewMap ); + +#ifdef ENABLE_IFC_CACHE + // manage cache round-robin and insert data + CharMapCacheItem& rItem = aCache[ nCurItem ]; + rItem.mpFontData = pFontData; + rItem.maCharMap.Reset( pNewMap ); + + if( ++nCurItem >= NMAXITEMS ) + nCurItem = 0; + + if( ++nUsedItems >= NMAXITEMS ) + nUsedItems = NMAXITEMS; +#endif // ENABLE_IFC_CACHE + } + + if( rFontCharMap.IsDefaultMap() ) + return sal_False; + return sal_True; +} + +// ----------------------------------------------------------------------- + +xub_StrLen OutputDevice::HasGlyphs( const Font& rTempFont, const String& rStr, + xub_StrLen nIndex, xub_StrLen nLen ) const +{ + if( nIndex >= rStr.Len() ) + return nIndex; + xub_StrLen nEnd = nIndex + nLen; + if( (sal_uLong)nIndex+nLen > rStr.Len() ) + nEnd = rStr.Len(); + + DBG_ASSERT( nIndex < nEnd, "StartPos >= EndPos?" ); + DBG_ASSERT( nEnd <= rStr.Len(), "String too short" ); + + // to get the map temporarily set font + const Font aOrigFont = GetFont(); + const_cast<OutputDevice&>(*this).SetFont( rTempFont ); + FontCharMap aFontCharMap; + sal_Bool bRet = GetFontCharMap( aFontCharMap ); + const_cast<OutputDevice&>(*this).SetFont( aOrigFont ); + + // if fontmap is unknown assume it doesn't have the glyphs + if( bRet == sal_False ) + return nIndex; + + const sal_Unicode* pStr = rStr.GetBuffer(); + for( pStr += nIndex; nIndex < nEnd; ++pStr, ++nIndex ) + if( ! aFontCharMap.HasChar( *pStr ) ) + return nIndex; + + return STRING_LEN; +} + +// ----------------------------------------------------------------------- + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/outdev4.cxx b/vcl/source/gdi/outdev4.cxx new file mode 100644 index 000000000000..94226e863de5 --- /dev/null +++ b/vcl/source/gdi/outdev4.cxx @@ -0,0 +1,1355 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <svsys.h> +#include <vcl/salgdi.hxx> +#include <tools/debug.hxx> +#include <vcl/svdata.hxx> +#include <vcl/gradient.hxx> +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/outdata.hxx> +#include <tools/poly.hxx> +#include <vcl/salbtype.hxx> +#include <tools/line.hxx> +#include <vcl/hatch.hxx> +#include <vcl/window.hxx> +#include <vcl/virdev.hxx> +#include <vcl/outdev.hxx> + +#include "pdfwriter_impl.hxx" +#include "vcl/window.h" +#include "vcl/salframe.hxx" + +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> + +// ----------- +// - Defines - +// ----------- + +#define HATCH_MAXPOINTS 1024 +#define GRADIENT_DEFAULT_STEPCOUNT 0 + +// ---------------- +// - Cmp-Function - +// ---------------- + +extern "C" int __LOADONCALLAPI ImplHatchCmpFnc( const void* p1, const void* p2 ) +{ + const long nX1 = ( (Point*) p1 )->X(); + const long nX2 = ( (Point*) p2 )->X(); + const long nY1 = ( (Point*) p1 )->Y(); + const long nY2 = ( (Point*) p2 )->Y(); + + return ( nX1 > nX2 ? 1 : nX1 == nX2 ? nY1 > nY2 ? 1: nY1 == nY2 ? 0 : -1 : -1 ); +} + +// ======================================================================= + +DBG_NAMEEX( OutputDevice ) +DBG_NAMEEX( Gradient ) + +// ======================================================================= + +void OutputDevice::ImplDrawPolygon( const Polygon& rPoly, const PolyPolygon* pClipPolyPoly ) +{ + if( pClipPolyPoly ) + ImplDrawPolyPolygon( rPoly, pClipPolyPoly ); + else + { + sal_uInt16 nPoints = rPoly.GetSize(); + + if ( nPoints < 2 ) + return; + + const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry(); + mpGraphics->DrawPolygon( nPoints, pPtAry, this ); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawPolyPolygon( const PolyPolygon& rPolyPoly, const PolyPolygon* pClipPolyPoly ) +{ + PolyPolygon* pPolyPoly; + + if( pClipPolyPoly ) + { + pPolyPoly = new PolyPolygon; + rPolyPoly.GetIntersection( *pClipPolyPoly, *pPolyPoly ); + } + else + pPolyPoly = (PolyPolygon*) &rPolyPoly; + + if( pPolyPoly->Count() == 1 ) + { + const Polygon rPoly = pPolyPoly->GetObject( 0 ); + sal_uInt16 nSize = rPoly.GetSize(); + + if( nSize >= 2 ) + { + const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry(); + mpGraphics->DrawPolygon( nSize, pPtAry, this ); + } + } + else if( pPolyPoly->Count() ) + { + sal_uInt16 nCount = pPolyPoly->Count(); + sal_uInt32* pPointAry = new sal_uInt32[nCount]; + PCONSTSALPOINT* pPointAryAry = new PCONSTSALPOINT[nCount]; + sal_uInt16 i = 0; + do + { + const Polygon& rPoly = pPolyPoly->GetObject( i ); + sal_uInt16 nSize = rPoly.GetSize(); + if ( nSize ) + { + pPointAry[i] = nSize; + pPointAryAry[i] = (PCONSTSALPOINT)rPoly.GetConstPointAry(); + i++; + } + else + nCount--; + } + while( i < nCount ); + + if( nCount == 1 ) + mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this ); + else + mpGraphics->DrawPolyPolygon( nCount, pPointAry, pPointAryAry, this ); + + delete[] pPointAry; + delete[] pPointAryAry; + } + + if( pClipPolyPoly ) + delete pPolyPoly; +} + +// ----------------------------------------------------------------------- + +inline sal_uInt8 ImplGetGradientColorValue( long nValue ) +{ + if ( nValue < 0 ) + return 0; + else if ( nValue > 0xFF ) + return 0xFF; + else + return (sal_uInt8)nValue; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawLinearGradient( const Rectangle& rRect, + const Gradient& rGradient, + sal_Bool bMtf, const PolyPolygon* pClipPolyPoly ) +{ + // rotiertes BoundRect ausrechnen + Rectangle aRect; + Point aCenter; + sal_uInt16 nAngle = rGradient.GetAngle() % 3600; + + rGradient.GetBoundRect( rRect, aRect, aCenter ); + + // Rand berechnen und Rechteck neu setzen + Rectangle aFullRect = aRect; + long nBorder = (long)rGradient.GetBorder() * aRect.GetHeight() / 100; + + // Rand berechnen und Rechteck neu setzen fuer linearen Farbverlauf + bool bLinear = (rGradient.GetStyle() == GRADIENT_LINEAR); + if ( bLinear ) + { + aRect.Top() += nBorder; + } + // Rand berechnen und Rechteck neu setzen fuer axiale Farbverlauf + else + { + nBorder >>= 1; + + aRect.Top() += nBorder; + aRect.Bottom() -= nBorder; + } + + // Top darf nicht groesser als Bottom sein + aRect.Top() = Min( aRect.Top(), (long)(aRect.Bottom() - 1) ); + + long nMinRect = aRect.GetHeight(); + + // Intensitaeten von Start- und Endfarbe ggf. aendern und + // Farbschrittweiten berechnen + long nFactor; + Color aStartCol = rGradient.GetStartColor(); + Color aEndCol = rGradient.GetEndColor(); + long nStartRed = aStartCol.GetRed(); + long nStartGreen = aStartCol.GetGreen(); + long nStartBlue = aStartCol.GetBlue(); + long nEndRed = aEndCol.GetRed(); + long nEndGreen = aEndCol.GetGreen(); + long nEndBlue = aEndCol.GetBlue(); + nFactor = rGradient.GetStartIntensity(); + nStartRed = (nStartRed * nFactor) / 100; + nStartGreen = (nStartGreen * nFactor) / 100; + nStartBlue = (nStartBlue * nFactor) / 100; + nFactor = rGradient.GetEndIntensity(); + nEndRed = (nEndRed * nFactor) / 100; + nEndGreen = (nEndGreen * nFactor) / 100; + nEndBlue = (nEndBlue * nFactor) / 100; + long nRedSteps = nEndRed - nStartRed; + long nGreenSteps = nEndGreen - nStartGreen; + long nBlueSteps = nEndBlue - nStartBlue; + long nStepCount = rGradient.GetSteps(); + + // Bei nicht linearen Farbverlaeufen haben wir nur die halben Steps + // pro Farbe + if ( !bLinear ) + { + nRedSteps <<= 1; + nGreenSteps <<= 1; + nBlueSteps <<= 1; + } + + // Anzahl der Schritte berechnen, falls nichts uebergeben wurde + if ( !nStepCount ) + { + long nInc; + + if ( meOutDevType != OUTDEV_PRINTER && !bMtf ) + { + nInc = (nMinRect < 50) ? 2 : 4; + } + else + { + // #105998# Use display-equivalent step size calculation + nInc = (nMinRect < 800) ? 10 : 20; + } + + if ( !nInc ) + nInc = 1; + + nStepCount = nMinRect / nInc; + } + // minimal drei Schritte und maximal die Anzahl der Farbunterschiede + long nSteps = Max( nStepCount, 2L ); + long nCalcSteps = Abs( nRedSteps ); + long nTempSteps = Abs( nGreenSteps ); + if ( nTempSteps > nCalcSteps ) + nCalcSteps = nTempSteps; + nTempSteps = Abs( nBlueSteps ); + if ( nTempSteps > nCalcSteps ) + nCalcSteps = nTempSteps; + if ( nCalcSteps < nSteps ) + nSteps = nCalcSteps; + if ( !nSteps ) + nSteps = 1; + + // Falls axialer Farbverlauf, muss die Schrittanzahl ungerade sein + if ( !bLinear && !(nSteps & 1) ) + nSteps++; + + // Berechnung ueber Double-Addition wegen Genauigkeit + double fScanLine = aRect.Top(); + double fScanInc = (double)aRect.GetHeight() / (double)nSteps; + + // Startfarbe berechnen und setzen + sal_uInt8 nRed; + sal_uInt8 nGreen; + sal_uInt8 nBlue; + long nSteps2; + long nStepsHalf = 0; + if ( bLinear ) + { + // Um 1 erhoeht, um die Border innerhalb der Schleife + // zeichnen zu koennen + nSteps2 = nSteps + 1; + nRed = (sal_uInt8)nStartRed; + nGreen = (sal_uInt8)nStartGreen; + nBlue = (sal_uInt8)nStartBlue; + } + else + { + // Um 2 erhoeht, um die Border innerhalb der Schleife + // zeichnen zu koennen + nSteps2 = nSteps + 2; + nRed = (sal_uInt8)nEndRed; + nGreen = (sal_uInt8)nEndGreen; + nBlue = (sal_uInt8)nEndBlue; + nStepsHalf = nSteps >> 1; + } + + if ( bMtf ) + mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) ); + else + mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) ); + + // Startpolygon erzeugen (== Borderpolygon) + Polygon aPoly( 4 ); + Polygon aTempPoly( 2 ); + aPoly[0] = aFullRect.TopLeft(); + aPoly[1] = aFullRect.TopRight(); + aPoly[2] = aRect.TopRight(); + aPoly[3] = aRect.TopLeft(); + aPoly.Rotate( aCenter, nAngle ); + + // Schleife, um rotierten Verlauf zu fuellen + for ( long i = 0; i < nSteps2; i++ ) + { + // berechnetesPolygon ausgeben + if ( bMtf ) + mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) ); + else + ImplDrawPolygon( aPoly, pClipPolyPoly ); + + // neues Polygon berechnen + aRect.Top() = (long)(fScanLine += fScanInc); + + // unteren Rand komplett fuellen + if ( i == nSteps ) + { + aTempPoly[0] = aFullRect.BottomLeft(); + aTempPoly[1] = aFullRect.BottomRight(); + } + else + { + aTempPoly[0] = aRect.TopLeft(); + aTempPoly[1] = aRect.TopRight(); + } + aTempPoly.Rotate( aCenter, nAngle ); + + aPoly[0] = aPoly[3]; + aPoly[1] = aPoly[2]; + aPoly[2] = aTempPoly[1]; + aPoly[3] = aTempPoly[0]; + + // Farbintensitaeten aendern... + // fuer lineare FV + if ( bLinear ) + { + nRed = ImplGetGradientColorValue( nStartRed+((nRedSteps*i)/nSteps2) ); + nGreen = ImplGetGradientColorValue( nStartGreen+((nGreenSteps*i)/nSteps2) ); + nBlue = ImplGetGradientColorValue( nStartBlue+((nBlueSteps*i)/nSteps2) ); + } + // fuer radiale FV + else + { + // fuer axiale FV muss die letzte Farbe der ersten + // Farbe entsprechen + // #107350# Setting end color one step earlier, as the + // last time we get here, we drop out of the loop later + // on. + if ( i >= nSteps ) + { + nRed = (sal_uInt8)nEndRed; + nGreen = (sal_uInt8)nEndGreen; + nBlue = (sal_uInt8)nEndBlue; + } + else + { + if ( i <= nStepsHalf ) + { + nRed = ImplGetGradientColorValue( nEndRed-((nRedSteps*i)/nSteps2) ); + nGreen = ImplGetGradientColorValue( nEndGreen-((nGreenSteps*i)/nSteps2) ); + nBlue = ImplGetGradientColorValue( nEndBlue-((nBlueSteps*i)/nSteps2) ); + } + // genau die Mitte und hoeher + else + { + long i2 = i - nStepsHalf; + nRed = ImplGetGradientColorValue( nStartRed+((nRedSteps*i2)/nSteps2) ); + nGreen = ImplGetGradientColorValue( nStartGreen+((nGreenSteps*i2)/nSteps2) ); + nBlue = ImplGetGradientColorValue( nStartBlue+((nBlueSteps*i2)/nSteps2) ); + } + } + } + + if ( bMtf ) + mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) ); + else + mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) ); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawComplexGradient( const Rectangle& rRect, + const Gradient& rGradient, + sal_Bool bMtf, const PolyPolygon* pClipPolyPoly ) +{ + // Feststellen ob Ausgabe ueber Polygon oder PolyPolygon + // Bei Rasteroperationen ungleich Overpaint immer PolyPolygone, + // da es zu falschen Ergebnissen kommt, wenn man mehrfach uebereinander + // ausgibt + // Bei Druckern auch immer PolyPolygone, da nicht alle Drucker + // das Uebereinanderdrucken von Polygonen koennen + // Virtuelle Device werden auch ausgeklammert, da einige Treiber + // ansonsten zu langsam sind + PolyPolygon* pPolyPoly; + Rectangle aRect; + Point aCenter; + Color aStartCol( rGradient.GetStartColor() ); + Color aEndCol( rGradient.GetEndColor() ); + long nStartRed = ( (long) aStartCol.GetRed() * rGradient.GetStartIntensity() ) / 100; + long nStartGreen = ( (long) aStartCol.GetGreen() * rGradient.GetStartIntensity() ) / 100; + long nStartBlue = ( (long) aStartCol.GetBlue() * rGradient.GetStartIntensity() ) / 100; + long nEndRed = ( (long) aEndCol.GetRed() * rGradient.GetEndIntensity() ) / 100; + long nEndGreen = ( (long) aEndCol.GetGreen() * rGradient.GetEndIntensity() ) / 100; + long nEndBlue = ( (long) aEndCol.GetBlue() * rGradient.GetEndIntensity() ) / 100; + long nRedSteps = nEndRed - nStartRed; + long nGreenSteps = nEndGreen - nStartGreen; + long nBlueSteps = nEndBlue - nStartBlue; + long nStepCount = rGradient.GetSteps(); + sal_uInt16 nAngle = rGradient.GetAngle() % 3600; + + rGradient.GetBoundRect( rRect, aRect, aCenter ); + + if( (meRasterOp != ROP_OVERPAINT) || (meOutDevType != OUTDEV_WINDOW) || bMtf ) + pPolyPoly = new PolyPolygon( 2 ); + else + pPolyPoly = NULL; + + long nMinRect = Min( aRect.GetWidth(), aRect.GetHeight() ); + + // Anzahl der Schritte berechnen, falls nichts uebergeben wurde + if( !nStepCount ) + { + long nInc; + + if ( meOutDevType != OUTDEV_PRINTER && !bMtf ) + { + nInc = ( nMinRect < 50 ) ? 2 : 4; + } + else + { + // #105998# Use display-equivalent step size calculation + nInc = (nMinRect < 800) ? 10 : 20; + } + + if( !nInc ) + nInc = 1; + + nStepCount = nMinRect / nInc; + } + + // minimal drei Schritte und maximal die Anzahl der Farbunterschiede + long nSteps = Max( nStepCount, 2L ); + long nCalcSteps = Abs( nRedSteps ); + long nTempSteps = Abs( nGreenSteps ); + if ( nTempSteps > nCalcSteps ) + nCalcSteps = nTempSteps; + nTempSteps = Abs( nBlueSteps ); + if ( nTempSteps > nCalcSteps ) + nCalcSteps = nTempSteps; + if ( nCalcSteps < nSteps ) + nSteps = nCalcSteps; + if ( !nSteps ) + nSteps = 1; + + // Ausgabebegrenzungen und Schrittweite fuer jede Richtung festlegen + Polygon aPoly; + double fScanLeft = aRect.Left(); + double fScanTop = aRect.Top(); + double fScanRight = aRect.Right(); + double fScanBottom = aRect.Bottom(); + double fScanInc = (double) nMinRect / (double) nSteps * 0.5; + sal_uInt8 nRed = (sal_uInt8) nStartRed, nGreen = (sal_uInt8) nStartGreen, nBlue = (sal_uInt8) nStartBlue; + bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output + + if( bMtf ) + mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) ); + else + mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) ); + + if( pPolyPoly ) + { + pPolyPoly->Insert( aPoly = rRect ); + pPolyPoly->Insert( aPoly ); + } + else + { + // extend rect, to avoid missing bounding line + Rectangle aExtRect( rRect ); + + aExtRect.Left() -= 1; + aExtRect.Top() -= 1; + aExtRect.Right() += 1; + aExtRect.Bottom() += 1; + + ImplDrawPolygon( aPoly = aExtRect, pClipPolyPoly ); + } + + // Schleife, um nacheinander die Polygone/PolyPolygone auszugeben + for( long i = 1; i < nSteps; i++ ) + { + // neues Polygon berechnen + aRect.Left() = (long)( fScanLeft += fScanInc ); + aRect.Top() = (long)( fScanTop += fScanInc ); + aRect.Right() = (long)( fScanRight -= fScanInc ); + aRect.Bottom() = (long)( fScanBottom -= fScanInc ); + + if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) ) + break; + + if( rGradient.GetStyle() == GRADIENT_RADIAL || rGradient.GetStyle() == GRADIENT_ELLIPTICAL ) + aPoly = Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 ); + else + aPoly = Polygon( aRect ); + + aPoly.Rotate( aCenter, nAngle ); + + // Farbe entsprechend anpassen + const long nStepIndex = ( ( pPolyPoly != NULL ) ? i : ( i + 1 ) ); + nRed = ImplGetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) ); + nGreen = ImplGetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) ); + nBlue = ImplGetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) ); + + // entweder langsame PolyPolygon-Ausgaben oder schnelles Polygon-Painting + if( pPolyPoly ) + { + bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output + + pPolyPoly->Replace( pPolyPoly->GetObject( 1 ), 0 ); + pPolyPoly->Replace( aPoly, 1 ); + + if( bMtf ) + mpMetaFile->AddAction( new MetaPolyPolygonAction( *pPolyPoly ) ); + else + ImplDrawPolyPolygon( *pPolyPoly, pClipPolyPoly ); + + // #107349# Set fill color _after_ geometry painting: + // pPolyPoly's geometry is the band from last iteration's + // aPoly to current iteration's aPoly. The window outdev + // path (see else below), on the other hand, paints the + // full aPoly. Thus, here, we're painting the band before + // the one painted in the window outdev path below. To get + // matching colors, have to delay color setting here. + if( bMtf ) + mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) ); + else + mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) ); + } + else + { + // #107349# Set fill color _before_ geometry painting + if( bMtf ) + mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) ); + else + mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) ); + + ImplDrawPolygon( aPoly, pClipPolyPoly ); + } + } + + // Falls PolyPolygon-Ausgabe, muessen wir noch ein letztes inneres Polygon zeichnen + if( pPolyPoly ) + { + const Polygon& rPoly = pPolyPoly->GetObject( 1 ); + + if( !rPoly.GetBoundRect().IsEmpty() ) + { + // #107349# Paint last polygon with end color only if loop + // has generated output. Otherwise, the current + // (i.e. start) color is taken, to generate _any_ output. + if( bPaintLastPolygon ) + { + nRed = ImplGetGradientColorValue( nEndRed ); + nGreen = ImplGetGradientColorValue( nEndGreen ); + nBlue = ImplGetGradientColorValue( nEndBlue ); + } + + if( bMtf ) + { + mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), sal_True ) ); + mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) ); + } + else + { + mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) ); + ImplDrawPolygon( rPoly, pClipPolyPoly ); + } + } + + delete pPolyPoly; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawGradient( const Rectangle& rRect, + const Gradient& rGradient ) +{ + OSL_TRACE( "OutputDevice::DrawGradient()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rGradient, Gradient, NULL ); + + if ( mnDrawMode & DRAWMODE_NOGRADIENT ) + return; + else if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) ) + { + Color aColor; + + if ( mnDrawMode & DRAWMODE_BLACKGRADIENT ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITEGRADIENT ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_SETTINGSGRADIENT ) + aColor = GetSettings().GetStyleSettings().GetWindowColor(); + + if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT ) + { + aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80, + ( aColor.GetGreen() >> 1 ) | 0x80, + ( aColor.GetBlue() >> 1 ) | 0x80 ); + } + + Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + SetLineColor( aColor ); + SetFillColor( aColor ); + DrawRect( rRect ); + Pop(); + return; + } + + Gradient aGradient( rGradient ); + + if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) ) + { + Color aStartCol( aGradient.GetStartColor() ); + Color aEndCol( aGradient.GetEndColor() ); + + if ( mnDrawMode & DRAWMODE_GRAYGRADIENT ) + { + sal_uInt8 cStartLum = aStartCol.GetLuminance(), cEndLum = aEndCol.GetLuminance(); + aStartCol = Color( cStartLum, cStartLum, cStartLum ); + aEndCol = Color( cEndLum, cEndLum, cEndLum ); + } + + if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT ) + { + aStartCol = Color( ( aStartCol.GetRed() >> 1 ) | 0x80, + ( aStartCol.GetGreen() >> 1 ) | 0x80, + ( aStartCol.GetBlue() >> 1 ) | 0x80 ); + + aEndCol = Color( ( aEndCol.GetRed() >> 1 ) | 0x80, + ( aEndCol.GetGreen() >> 1 ) | 0x80, + ( aEndCol.GetBlue() >> 1 ) | 0x80 ); + } + + aGradient.SetStartColor( aStartCol ); + aGradient.SetEndColor( aEndCol ); + } + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaGradientAction( rRect, aGradient ) ); + + if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + // Rechteck in Pixel umrechnen + Rectangle aRect( ImplLogicToDevicePixel( rRect ) ); + aRect.Justify(); + + // Wenn Rechteck leer ist, brauchen wir nichts machen + if ( !aRect.IsEmpty() ) + { + // Clip Region sichern + Push( PUSH_CLIPREGION ); + IntersectClipRegion( rRect ); + + // because we draw with no border line, we have to expand gradient + // rect to avoid missing lines on the right and bottom edge + aRect.Left()--; + aRect.Top()--; + aRect.Right()++; + aRect.Bottom()++; + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + + if ( !mbOutputClipped ) + { + // Gradienten werden ohne Umrandung gezeichnet + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = sal_True; + } + + mbInitFillColor = sal_True; + + // calculate step count if neccessary + if ( !aGradient.GetSteps() ) + aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT ); + + if( aGradient.GetStyle() == GRADIENT_LINEAR || aGradient.GetStyle() == GRADIENT_AXIAL ) + ImplDrawLinearGradient( aRect, aGradient, sal_False, NULL ); + else + ImplDrawComplexGradient( aRect, aGradient, sal_False, NULL ); + } + + Pop(); + } + + if( mpAlphaVDev ) + { + // #i32109#: Make gradient area opaque + mpAlphaVDev->ImplFillOpaqueRectangle( rRect ); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly, + const Gradient& rGradient ) +{ + OSL_TRACE( "OutputDevice::DrawGradient()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rGradient, Gradient, NULL ); + + if( mbInitClipRegion ) + ImplInitClipRegion(); + + if( mbOutputClipped ) + return; + + if( !mpGraphics ) + if( !ImplGetGraphics() ) + return; + + if( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() && !( mnDrawMode & DRAWMODE_NOGRADIENT ) ) + { + if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) ) + { + Color aColor; + + if ( mnDrawMode & DRAWMODE_BLACKGRADIENT ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITEGRADIENT ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_SETTINGSGRADIENT ) + aColor = GetSettings().GetStyleSettings().GetWindowColor(); + + if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT ) + { + aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80, + ( aColor.GetGreen() >> 1 ) | 0x80, + ( aColor.GetBlue() >> 1 ) | 0x80 ); + } + + Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + SetLineColor( aColor ); + SetFillColor( aColor ); + DrawPolyPolygon( rPolyPoly ); + Pop(); + return; + } + + if( mpMetaFile ) + { + const Rectangle aRect( rPolyPoly.GetBoundRect() ); + + mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) ); + mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) ); + + if( OUTDEV_PRINTER == meOutDevType ) + { + Push( PUSH_CLIPREGION ); + IntersectClipRegion( rPolyPoly ); + DrawGradient( aRect, rGradient ); + Pop(); + } + else + { + const sal_Bool bOldOutput = IsOutputEnabled(); + + EnableOutput( sal_False ); + Push( PUSH_RASTEROP ); + SetRasterOp( ROP_XOR ); + DrawGradient( aRect, rGradient ); + SetFillColor( COL_BLACK ); + SetRasterOp( ROP_0 ); + DrawPolyPolygon( rPolyPoly ); + SetRasterOp( ROP_XOR ); + DrawGradient( aRect, rGradient ); + Pop(); + EnableOutput( bOldOutput ); + } + + mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) ); + } + + if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + Gradient aGradient( rGradient ); + + if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) ) + { + Color aStartCol( aGradient.GetStartColor() ); + Color aEndCol( aGradient.GetEndColor() ); + + if ( mnDrawMode & DRAWMODE_GRAYGRADIENT ) + { + sal_uInt8 cStartLum = aStartCol.GetLuminance(), cEndLum = aEndCol.GetLuminance(); + aStartCol = Color( cStartLum, cStartLum, cStartLum ); + aEndCol = Color( cEndLum, cEndLum, cEndLum ); + } + + if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT ) + { + aStartCol = Color( ( aStartCol.GetRed() >> 1 ) | 0x80, + ( aStartCol.GetGreen() >> 1 ) | 0x80, + ( aStartCol.GetBlue() >> 1 ) | 0x80 ); + + aEndCol = Color( ( aEndCol.GetRed() >> 1 ) | 0x80, + ( aEndCol.GetGreen() >> 1 ) | 0x80, + ( aEndCol.GetBlue() >> 1 ) | 0x80 ); + } + + aGradient.SetStartColor( aStartCol ); + aGradient.SetEndColor( aEndCol ); + } + + if( OUTDEV_PRINTER == meOutDevType || ImplGetSVData()->maGDIData.mbNoXORClipping ) + { + const Rectangle aBoundRect( rPolyPoly.GetBoundRect() ); + + if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() ) + { + // Rechteck in Pixel umrechnen + Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) ); + aRect.Justify(); + + // Wenn Rechteck leer ist, brauchen wir nichts machen + if ( !aRect.IsEmpty() ) + { + if( !mpGraphics && !ImplGetGraphics() ) + return; + + if( mbInitClipRegion ) + ImplInitClipRegion(); + + if( !mbOutputClipped ) + { + PolyPolygon aClipPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) ); + + // Gradienten werden ohne Umrandung gezeichnet + if( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = sal_True; + } + + mbInitFillColor = sal_True; + + // calculate step count if neccessary + if ( !aGradient.GetSteps() ) + aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT ); + + if( aGradient.GetStyle() == GRADIENT_LINEAR || aGradient.GetStyle() == GRADIENT_AXIAL ) + ImplDrawLinearGradient( aRect, aGradient, sal_False, &aClipPolyPoly ); + else + ImplDrawComplexGradient( aRect, aGradient, sal_False, &aClipPolyPoly ); + } + } + } + } + else + { + const PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) ); + const Rectangle aBoundRect( aPolyPoly.GetBoundRect() ); + Point aPoint; + Rectangle aDstRect( aPoint, GetOutputSizePixel() ); + + aDstRect.Intersection( aBoundRect ); + + if( OUTDEV_WINDOW == meOutDevType ) + { + const Region aPaintRgn( ( (Window*) this )->GetPaintRegion() ); + + if( !aPaintRgn.IsNull() ) + aDstRect.Intersection( LogicToPixel( aPaintRgn ).GetBoundRect() ); + } + + if( !aDstRect.IsEmpty() ) + { + VirtualDevice* pVDev; + const Size aDstSize( aDstRect.GetSize() ); + + if( HasAlpha() ) + { + // #110958# Pay attention to alpha VDevs here, otherwise, + // background will be wrong: Temp VDev has to have alpha, too. + pVDev = new VirtualDevice( *this, 0, GetAlphaBitCount() > 1 ? 0 : 1 ); + } + else + { + // nothing special here. Plain VDev + pVDev = new VirtualDevice(); + } + + if( pVDev->SetOutputSizePixel( aDstSize) ) + { + MapMode aVDevMap; + const sal_Bool bOldMap = mbMap; + + EnableMapMode( sal_False ); + + pVDev->DrawOutDev( Point(), aDstSize, aDstRect.TopLeft(), aDstSize, *this ); + pVDev->SetRasterOp( ROP_XOR ); + aVDevMap.SetOrigin( Point( -aDstRect.Left(), -aDstRect.Top() ) ); + pVDev->SetMapMode( aVDevMap ); + pVDev->DrawGradient( aBoundRect, aGradient ); + pVDev->SetFillColor( COL_BLACK ); + pVDev->SetRasterOp( ROP_0 ); + pVDev->DrawPolyPolygon( aPolyPoly ); + pVDev->SetRasterOp( ROP_XOR ); + pVDev->DrawGradient( aBoundRect, aGradient ); + aVDevMap.SetOrigin( Point() ); + pVDev->SetMapMode( aVDevMap ); + DrawOutDev( aDstRect.TopLeft(), aDstSize, Point(), aDstSize, *pVDev ); + + EnableMapMode( bOldMap ); + } + + delete pVDev; + } + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawPolyPolygon( rPolyPoly ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::AddGradientActions( const Rectangle& rRect, const Gradient& rGradient, + GDIMetaFile& rMtf ) +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rGradient, Gradient, NULL ); + + Rectangle aRect( rRect ); + + aRect.Justify(); + + // Wenn Rechteck leer ist, brauchen wir nichts machen + if ( !aRect.IsEmpty() ) + { + Gradient aGradient( rGradient ); + GDIMetaFile* pOldMtf = mpMetaFile; + + mpMetaFile = &rMtf; + mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) ); + mpMetaFile->AddAction( new MetaISectRectClipRegionAction( aRect ) ); + mpMetaFile->AddAction( new MetaLineColorAction( Color(), sal_False ) ); + + // because we draw with no border line, we have to expand gradient + // rect to avoid missing lines on the right and bottom edge + aRect.Left()--; + aRect.Top()--; + aRect.Right()++; + aRect.Bottom()++; + + // calculate step count if neccessary + if ( !aGradient.GetSteps() ) + aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT ); + + if( aGradient.GetStyle() == GRADIENT_LINEAR || aGradient.GetStyle() == GRADIENT_AXIAL ) + ImplDrawLinearGradient( aRect, aGradient, sal_True, NULL ); + else + ImplDrawComplexGradient( aRect, aGradient, sal_True, NULL ); + + mpMetaFile->AddAction( new MetaPopAction() ); + mpMetaFile = pOldMtf; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch ) +{ + OSL_TRACE( "OutputDevice::DrawHatch()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Hatch aHatch( rHatch ); + + if ( mnDrawMode & ( DRAWMODE_BLACKLINE | DRAWMODE_WHITELINE | + DRAWMODE_GRAYLINE | DRAWMODE_GHOSTEDLINE | + DRAWMODE_SETTINGSLINE ) ) + { + Color aColor( rHatch.GetColor() ); + + if ( mnDrawMode & DRAWMODE_BLACKLINE ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITELINE ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYLINE ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if( mnDrawMode & DRAWMODE_SETTINGSLINE ) + { + aColor = GetSettings().GetStyleSettings().GetFontColor(); + } + + if ( mnDrawMode & DRAWMODE_GHOSTEDLINE ) + { + aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80, + ( aColor.GetGreen() >> 1 ) | 0x80, + ( aColor.GetBlue() >> 1 ) | 0x80); + } + + aHatch.SetColor( aColor ); + } + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaHatchAction( rPolyPoly, aHatch ) ); + + if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + if( !mpGraphics && !ImplGetGraphics() ) + return; + + if( mbInitClipRegion ) + ImplInitClipRegion(); + + if( mbOutputClipped ) + return; + + if( rPolyPoly.Count() ) + { + PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) ); + GDIMetaFile* pOldMetaFile = mpMetaFile; + sal_Bool bOldMap = mbMap; + + aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME ); + aHatch.SetDistance( ImplLogicWidthToDevicePixel( aHatch.GetDistance() ) ); + + mpMetaFile = NULL; + EnableMapMode( sal_False ); + Push( PUSH_LINECOLOR ); + SetLineColor( aHatch.GetColor() ); + ImplInitLineColor(); + ImplDrawHatch( aPolyPoly, aHatch, sal_False ); + Pop(); + EnableMapMode( bOldMap ); + mpMetaFile = pOldMetaFile; + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawHatch( rPolyPoly, rHatch ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::AddHatchActions( const PolyPolygon& rPolyPoly, const Hatch& rHatch, + GDIMetaFile& rMtf ) +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + PolyPolygon aPolyPoly( rPolyPoly ); + aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME | POLY_OPTIMIZE_CLOSE ); + + if( aPolyPoly.Count() ) + { + GDIMetaFile* pOldMtf = mpMetaFile; + + mpMetaFile = &rMtf; + mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) ); + mpMetaFile->AddAction( new MetaLineColorAction( rHatch.GetColor(), sal_True ) ); + ImplDrawHatch( aPolyPoly, rHatch, sal_True ); + mpMetaFile->AddAction( new MetaPopAction() ); + mpMetaFile = pOldMtf; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch, sal_Bool bMtf ) +{ + Rectangle aRect( rPolyPoly.GetBoundRect() ); + const long nLogPixelWidth = ImplDevicePixelToLogicWidth( 1 ); + const long nWidth = ImplDevicePixelToLogicWidth( Max( ImplLogicWidthToDevicePixel( rHatch.GetDistance() ), 3L ) ); + Point* pPtBuffer = new Point[ HATCH_MAXPOINTS ]; + Point aPt1, aPt2, aEndPt1; + Size aInc; + + // Single hatch + aRect.Left() -= nLogPixelWidth; aRect.Top() -= nLogPixelWidth; aRect.Right() += nLogPixelWidth; aRect.Bottom() += nLogPixelWidth; + ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle(), aPt1, aPt2, aInc, aEndPt1 ); + do + { + ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf ); + aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height(); + aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height(); + } + while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) ); + + if( ( rHatch.GetStyle() == HATCH_DOUBLE ) || ( rHatch.GetStyle() == HATCH_TRIPLE ) ) + { + // Double hatch + ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 900, aPt1, aPt2, aInc, aEndPt1 ); + do + { + ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf ); + aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height(); + aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height(); + } + while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) ); + + if( rHatch.GetStyle() == HATCH_TRIPLE ) + { + // Triple hatch + ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 450, aPt1, aPt2, aInc, aEndPt1 ); + do + { + ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf ); + aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height(); + aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height(); + } + while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) ); + } + } + + delete[] pPtBuffer; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplCalcHatchValues( const Rectangle& rRect, long nDist, sal_uInt16 nAngle10, + Point& rPt1, Point& rPt2, Size& rInc, Point& rEndPt1 ) +{ + Point aRef; + long nAngle = nAngle10 % 1800; + long nOffset = 0; + + if( nAngle > 900 ) + nAngle -= 1800; + + aRef = ( !IsRefPoint() ? rRect.TopLeft() : GetRefPoint() ); + + if( 0 == nAngle ) + { + rInc = Size( 0, nDist ); + rPt1 = rRect.TopLeft(); + rPt2 = rRect.TopRight(); + rEndPt1 = rRect.BottomLeft(); + + if( aRef.Y() <= rRect.Top() ) + nOffset = ( ( rRect.Top() - aRef.Y() ) % nDist ); + else + nOffset = ( nDist - ( ( aRef.Y() - rRect.Top() ) % nDist ) ); + + rPt1.Y() -= nOffset; + rPt2.Y() -= nOffset; + } + else if( 900 == nAngle ) + { + rInc = Size( nDist, 0 ); + rPt1 = rRect.TopLeft(); + rPt2 = rRect.BottomLeft(); + rEndPt1 = rRect.TopRight(); + + if( aRef.X() <= rRect.Left() ) + nOffset = ( rRect.Left() - aRef.X() ) % nDist; + else + nOffset = nDist - ( ( aRef.X() - rRect.Left() ) % nDist ); + + rPt1.X() -= nOffset; + rPt2.X() -= nOffset; + } + else if( nAngle >= -450 && nAngle <= 450 ) + { + const double fAngle = F_PI1800 * labs( nAngle ); + const double fTan = tan( fAngle ); + const long nYOff = FRound( ( rRect.Right() - rRect.Left() ) * fTan ); + long nPY; + + rInc = Size( 0, nDist = FRound( nDist / cos( fAngle ) ) ); + + if( nAngle > 0 ) + { + rPt1 = rRect.TopLeft(); + rPt2 = Point( rRect.Right(), rRect.Top() - nYOff ); + rEndPt1 = Point( rRect.Left(), rRect.Bottom() + nYOff ); + nPY = FRound( aRef.Y() - ( ( rPt1.X() - aRef.X() ) * fTan ) ); + } + else + { + rPt1 = rRect.TopRight(); + rPt2 = Point( rRect.Left(), rRect.Top() - nYOff ); + rEndPt1 = Point( rRect.Right(), rRect.Bottom() + nYOff ); + nPY = FRound( aRef.Y() + ( ( rPt1.X() - aRef.X() ) * fTan ) ); + } + + if( nPY <= rPt1.Y() ) + nOffset = ( rPt1.Y() - nPY ) % nDist; + else + nOffset = nDist - ( ( nPY - rPt1.Y() ) % nDist ); + + rPt1.Y() -= nOffset; + rPt2.Y() -= nOffset; + } + else + { + const double fAngle = F_PI1800 * labs( nAngle ); + const double fTan = tan( fAngle ); + const long nXOff = FRound( ( rRect.Bottom() - rRect.Top() ) / fTan ); + long nPX; + + rInc = Size( nDist = FRound( nDist / sin( fAngle ) ), 0 ); + + if( nAngle > 0 ) + { + rPt1 = rRect.TopLeft(); + rPt2 = Point( rRect.Left() - nXOff, rRect.Bottom() ); + rEndPt1 = Point( rRect.Right() + nXOff, rRect.Top() ); + nPX = FRound( aRef.X() - ( ( rPt1.Y() - aRef.Y() ) / fTan ) ); + } + else + { + rPt1 = rRect.BottomLeft(); + rPt2 = Point( rRect.Left() - nXOff, rRect.Top() ); + rEndPt1 = Point( rRect.Right() + nXOff, rRect.Bottom() ); + nPX = FRound( aRef.X() + ( ( rPt1.Y() - aRef.Y() ) / fTan ) ); + } + + if( nPX <= rPt1.X() ) + nOffset = ( rPt1.X() - nPX ) % nDist; + else + nOffset = nDist - ( ( nPX - rPt1.X() ) % nDist ); + + rPt1.X() -= nOffset; + rPt2.X() -= nOffset; + } +} + +// ------------------------------------------------------------------------ + +void OutputDevice::ImplDrawHatchLine( const Line& rLine, const PolyPolygon& rPolyPoly, + Point* pPtBuffer, sal_Bool bMtf ) +{ + double fX, fY; + long nAdd, nPCounter = 0; + + for( long nPoly = 0, nPolyCount = rPolyPoly.Count(); nPoly < nPolyCount; nPoly++ ) + { + const Polygon& rPoly = rPolyPoly[ (sal_uInt16) nPoly ]; + + if( rPoly.GetSize() > 1 ) + { + Line aCurSegment( rPoly[ 0 ], Point() ); + + for( long i = 1, nCount = rPoly.GetSize(); i <= nCount; i++ ) + { + aCurSegment.SetEnd( rPoly[ (sal_uInt16)( i % nCount ) ] ); + nAdd = 0; + + if( rLine.Intersection( aCurSegment, fX, fY ) ) + { + if( ( fabs( fX - aCurSegment.GetStart().X() ) <= 0.0000001 ) && + ( fabs( fY - aCurSegment.GetStart().Y() ) <= 0.0000001 ) ) + { + const Line aPrevSegment( rPoly[ (sal_uInt16)( ( i > 1 ) ? ( i - 2 ) : ( nCount - 1 ) ) ], aCurSegment.GetStart() ); + const double fPrevDistance = rLine.GetDistance( aPrevSegment.GetStart() ); + const double fCurDistance = rLine.GetDistance( aCurSegment.GetEnd() ); + + if( ( fPrevDistance <= 0.0 && fCurDistance > 0.0 ) || + ( fPrevDistance > 0.0 && fCurDistance < 0.0 ) ) + { + nAdd = 1; + } + } + else if( ( fabs( fX - aCurSegment.GetEnd().X() ) <= 0.0000001 ) && + ( fabs( fY - aCurSegment.GetEnd().Y() ) <= 0.0000001 ) ) + { + const Line aNextSegment( aCurSegment.GetEnd(), rPoly[ (sal_uInt16)( ( i + 1 ) % nCount ) ] ); + + if( ( fabs( rLine.GetDistance( aNextSegment.GetEnd() ) ) <= 0.0000001 ) && + ( rLine.GetDistance( aCurSegment.GetStart() ) > 0.0 ) ) + { + nAdd = 1; + } + } + else + nAdd = 1; + + if( nAdd ) + pPtBuffer[ nPCounter++ ] = Point( FRound( fX ), FRound( fY ) ); + } + + aCurSegment.SetStart( aCurSegment.GetEnd() ); + } + } + } + + if( nPCounter > 1 ) + { + qsort( pPtBuffer, nPCounter, sizeof( Point ), ImplHatchCmpFnc ); + + if( nPCounter & 1 ) + nPCounter--; + + if( bMtf ) + { + for( long i = 0; i < nPCounter; i += 2 ) + mpMetaFile->AddAction( new MetaLineAction( pPtBuffer[ i ], pPtBuffer[ i + 1 ] ) ); + } + else + { + for( long i = 0; i < nPCounter; i += 2 ) + { + if( mpPDFWriter ) + { + mpPDFWriter->drawLine( pPtBuffer[ i ], pPtBuffer[ i+1 ] ); + } + else + { + const Point aPt1( ImplLogicToDevicePixel( pPtBuffer[ i ] ) ); + const Point aPt2( ImplLogicToDevicePixel( pPtBuffer[ i + 1 ] ) ); + mpGraphics->DrawLine( aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y(), this ); + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/outdev5.cxx b/vcl/source/gdi/outdev5.cxx new file mode 100644 index 000000000000..2df0ff86dc3e --- /dev/null +++ b/vcl/source/gdi/outdev5.cxx @@ -0,0 +1,314 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <tools/ref.hxx> +#include <svsys.h> +#include <vcl/salgdi.hxx> +#include <tools/debug.hxx> +#include <vcl/svdata.hxx> +#include <tools/poly.hxx> +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/outdata.hxx> +#include <vcl/outdev.h> +#include <vcl/outdev.hxx> +#include <vcl/virdev.hxx> + +// ======================================================================= + +DBG_NAMEEX( OutputDevice ) + +// ======================================================================= + +void OutputDevice::DrawRect( const Rectangle& rRect, + sal_uLong nHorzRound, sal_uLong nVertRound ) +{ + OSL_TRACE( "OutputDevice::DrawRoundRect()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaRoundRectAction( rRect, nHorzRound, nVertRound ) ); + + if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || ImplIsRecordLayout() ) + return; + + const Rectangle aRect( ImplLogicToDevicePixel( rRect ) ); + + if ( aRect.IsEmpty() ) + return; + + nHorzRound = ImplLogicWidthToDevicePixel( nHorzRound ); + nVertRound = ImplLogicHeightToDevicePixel( nVertRound ); + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); + + if ( !nHorzRound && !nVertRound ) + mpGraphics->DrawRect( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), this ); + else + { + const Polygon aRoundRectPoly( aRect, nHorzRound, nVertRound ); + + if ( aRoundRectPoly.GetSize() >= 2 ) + { + const SalPoint* pPtAry = (const SalPoint*) aRoundRectPoly.GetConstPointAry(); + + if ( !mbFillColor ) + mpGraphics->DrawPolyLine( aRoundRectPoly.GetSize(), pPtAry, this ); + else + mpGraphics->DrawPolygon( aRoundRectPoly.GetSize(), pPtAry, this ); + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawRect( rRect, nHorzRound, nVertRound ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawEllipse( const Rectangle& rRect ) +{ + OSL_TRACE( "OutputDevice::DrawEllipse()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaEllipseAction( rRect ) ); + + if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || ImplIsRecordLayout() ) + return; + + Rectangle aRect( ImplLogicToDevicePixel( rRect ) ); + if ( aRect.IsEmpty() ) + return; + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + + Polygon aRectPoly( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 ); + if ( aRectPoly.GetSize() >= 2 ) + { + const SalPoint* pPtAry = (const SalPoint*)aRectPoly.GetConstPointAry(); + if ( !mbFillColor ) + mpGraphics->DrawPolyLine( aRectPoly.GetSize(), pPtAry, this ); + else + { + if ( mbInitFillColor ) + ImplInitFillColor(); + mpGraphics->DrawPolygon( aRectPoly.GetSize(), pPtAry, this ); + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawEllipse( rRect ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawArc( const Rectangle& rRect, + const Point& rStartPt, const Point& rEndPt ) +{ + OSL_TRACE( "OutputDevice::DrawArc()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaArcAction( rRect, rStartPt, rEndPt ) ); + + if ( !IsDeviceOutputNecessary() || !mbLineColor || ImplIsRecordLayout() ) + return; + + Rectangle aRect( ImplLogicToDevicePixel( rRect ) ); + if ( aRect.IsEmpty() ) + return; + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + + const Point aStart( ImplLogicToDevicePixel( rStartPt ) ); + const Point aEnd( ImplLogicToDevicePixel( rEndPt ) ); + Polygon aArcPoly( aRect, aStart, aEnd, POLY_ARC ); + + if ( aArcPoly.GetSize() >= 2 ) + { + const SalPoint* pPtAry = (const SalPoint*)aArcPoly.GetConstPointAry(); + mpGraphics->DrawPolyLine( aArcPoly.GetSize(), pPtAry, this ); + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawArc( rRect, rStartPt, rEndPt ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawPie( const Rectangle& rRect, + const Point& rStartPt, const Point& rEndPt ) +{ + OSL_TRACE( "OutputDevice::DrawPie()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaPieAction( rRect, rStartPt, rEndPt ) ); + + if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || ImplIsRecordLayout() ) + return; + + Rectangle aRect( ImplLogicToDevicePixel( rRect ) ); + if ( aRect.IsEmpty() ) + return; + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + + const Point aStart( ImplLogicToDevicePixel( rStartPt ) ); + const Point aEnd( ImplLogicToDevicePixel( rEndPt ) ); + Polygon aPiePoly( aRect, aStart, aEnd, POLY_PIE ); + + if ( aPiePoly.GetSize() >= 2 ) + { + const SalPoint* pPtAry = (const SalPoint*)aPiePoly.GetConstPointAry(); + if ( !mbFillColor ) + mpGraphics->DrawPolyLine( aPiePoly.GetSize(), pPtAry, this ); + else + { + if ( mbInitFillColor ) + ImplInitFillColor(); + mpGraphics->DrawPolygon( aPiePoly.GetSize(), pPtAry, this ); + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawPie( rRect, rStartPt, rEndPt ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawChord( const Rectangle& rRect, + const Point& rStartPt, const Point& rEndPt ) +{ + OSL_TRACE( "OutputDevice::DrawChord()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaChordAction( rRect, rStartPt, rEndPt ) ); + + if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || ImplIsRecordLayout() ) + return; + + Rectangle aRect( ImplLogicToDevicePixel( rRect ) ); + if ( aRect.IsEmpty() ) + return; + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + ImplInitLineColor(); + + const Point aStart( ImplLogicToDevicePixel( rStartPt ) ); + const Point aEnd( ImplLogicToDevicePixel( rEndPt ) ); + Polygon aChordPoly( aRect, aStart, aEnd, POLY_CHORD ); + + if ( aChordPoly.GetSize() >= 2 ) + { + const SalPoint* pPtAry = (const SalPoint*)aChordPoly.GetConstPointAry(); + if ( !mbFillColor ) + mpGraphics->DrawPolyLine( aChordPoly.GetSize(), pPtAry, this ); + else + { + if ( mbInitFillColor ) + ImplInitFillColor(); + mpGraphics->DrawPolygon( aChordPoly.GetSize(), pPtAry, this ); + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawChord( rRect, rStartPt, rEndPt ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/outdev6.cxx b/vcl/source/gdi/outdev6.cxx new file mode 100644 index 000000000000..92898ebfdf3a --- /dev/null +++ b/vcl/source/gdi/outdev6.cxx @@ -0,0 +1,1241 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <svsys.h> +#include <vcl/salgdi.hxx> +#include <tools/debug.hxx> +#include <vcl/outdev.h> +#include <vcl/outdev.hxx> +#include <vcl/virdev.hxx> +#include <vcl/bmpacc.hxx> +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/graph.hxx> +#include <vcl/wall2.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <math.h> +#include <vcl/window.h> +#include <vcl/svdata.hxx> + +// ======================================================================== + +DBG_NAMEEX( OutputDevice ) + +// ------------------------------------------------------------------------ + +void OutputDevice::DrawGrid( const Rectangle& rRect, const Size& rDist, sal_uLong nFlags ) +{ + OSL_TRACE( "OutputDevice::DrawGrid()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Rectangle aDstRect( PixelToLogic( Point() ), GetOutputSize() ); + aDstRect.Intersection( rRect ); + + if( aDstRect.IsEmpty() || ImplIsRecordLayout() ) + return; + + if( !mpGraphics && !ImplGetGraphics() ) + return; + + if( mbInitClipRegion ) + ImplInitClipRegion(); + + if( mbOutputClipped ) + return; + + const long nDistX = Max( rDist.Width(), 1L ); + const long nDistY = Max( rDist.Height(), 1L ); + long nX = ( rRect.Left() >= aDstRect.Left() ) ? rRect.Left() : ( rRect.Left() + ( ( aDstRect.Left() - rRect.Left() ) / nDistX ) * nDistX ); + long nY = ( rRect.Top() >= aDstRect.Top() ) ? rRect.Top() : ( rRect.Top() + ( ( aDstRect.Top() - rRect.Top() ) / nDistY ) * nDistY ); + const long nRight = aDstRect.Right(); + const long nBottom = aDstRect.Bottom(); + const long nStartX = ImplLogicXToDevicePixel( nX ); + const long nEndX = ImplLogicXToDevicePixel( nRight ); + const long nStartY = ImplLogicYToDevicePixel( nY ); + const long nEndY = ImplLogicYToDevicePixel( nBottom ); + long nHorzCount = 0L; + long nVertCount = 0L; + + ::com::sun::star::uno::Sequence< sal_Int32 > aVertBuf; + ::com::sun::star::uno::Sequence< sal_Int32 > aHorzBuf; + + if( ( nFlags & GRID_DOTS ) || ( nFlags & GRID_HORZLINES ) ) + { + aVertBuf.realloc( aDstRect.GetHeight() / nDistY + 2L ); + aVertBuf[ nVertCount++ ] = nStartY; + while( ( nY += nDistY ) <= nBottom ) + aVertBuf[ nVertCount++ ] = ImplLogicYToDevicePixel( nY ); + } + + if( ( nFlags & GRID_DOTS ) || ( nFlags & GRID_VERTLINES ) ) + { + aHorzBuf.realloc( aDstRect.GetWidth() / nDistX + 2L ); + aHorzBuf[ nHorzCount++ ] = nStartX; + while( ( nX += nDistX ) <= nRight ) + aHorzBuf[ nHorzCount++ ] = ImplLogicXToDevicePixel( nX ); + } + + if( mbInitLineColor ) + ImplInitLineColor(); + + if( mbInitFillColor ) + ImplInitFillColor(); + + const sal_Bool bOldMap = mbMap; + EnableMapMode( sal_False ); + + if( nFlags & GRID_DOTS ) + { + for( long i = 0L; i < nVertCount; i++ ) + for( long j = 0L, Y = aVertBuf[ i ]; j < nHorzCount; j++ ) + mpGraphics->DrawPixel( aHorzBuf[ j ], Y, this ); + } + else + { + if( nFlags & GRID_HORZLINES ) + { + for( long i = 0L; i < nVertCount; i++ ) + { + nY = aVertBuf[ i ]; + mpGraphics->DrawLine( nStartX, nY, nEndX, nY, this ); + } + } + + if( nFlags & GRID_VERTLINES ) + { + for( long i = 0L; i < nHorzCount; i++ ) + { + nX = aHorzBuf[ i ]; + mpGraphics->DrawLine( nX, nStartY, nX, nEndY, this ); + } + } + } + + EnableMapMode( bOldMap ); + + if( mpAlphaVDev ) + mpAlphaVDev->DrawGrid( rRect, rDist, nFlags ); +} + +// ------------------------------------------------------------------------ +// Caution: This method is nearly the same as +// void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly ) +// so when changes are made here do not forget to make change sthere, too + +void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, double fTransparency) +{ + OSL_TRACE( "OutputDevice::DrawTransparent(B2D&,transparency)" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // AW: Do NOT paint empty PolyPolygons + if(!rB2DPolyPoly.count()) + return; + + // we need a graphics + if( !mpGraphics ) + if( !ImplGetGraphics() ) + return; + + if( mbInitClipRegion ) + ImplInitClipRegion(); + if( mbOutputClipped ) + return; + + if( mbInitLineColor ) + ImplInitLineColor(); + if( mbInitFillColor ) + ImplInitFillColor(); + + if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) + && mpGraphics->supportsOperation(OutDevSupport_B2DDraw) + && ROP_OVERPAINT == GetRasterOp() ) + { + // b2dpolygon support not implemented yet on non-UNX platforms + const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation(); + basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly); + + // transform the polygon into device space and ensure it is closed + aB2DPolyPolygon.transform( aTransform ); + aB2DPolyPolygon.setClosed( true ); + + bool bDrawnOk = true; + if( IsFillColor() ) + bDrawnOk = mpGraphics->DrawPolyPolygon( aB2DPolyPolygon, fTransparency, this ); + if( bDrawnOk && IsLineColor() ) + { + const basegfx::B2DVector aHairlineWidth(1,1); + const int nPolyCount = aB2DPolyPolygon.count(); + for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) + { + const ::basegfx::B2DPolygon aOnePoly = aB2DPolyPolygon.getB2DPolygon( nPolyIdx ); + mpGraphics->DrawPolyLine( aOnePoly, fTransparency, aHairlineWidth, ::basegfx::B2DLINEJOIN_NONE, this ); + } + } + + if( bDrawnOk ) + { + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaTransparentAction( PolyPolygon( rB2DPolyPoly ), static_cast< sal_uInt16 >(fTransparency * 100.0))); + return; + } + } + + // fallback to old polygon drawing if needed + const PolyPolygon aToolsPolyPolygon( rB2DPolyPoly ); + DrawTransparent(PolyPolygon(rB2DPolyPoly), static_cast< sal_uInt16 >(fTransparency * 100.0)); +} + +// ------------------------------------------------------------------------ + +void OutputDevice::DrawTransparent( const PolyPolygon& rPolyPoly, + sal_uInt16 nTransparencePercent ) +{ + OSL_TRACE( "OutputDevice::DrawTransparent()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // short circuit for drawing an opaque polygon + if( (nTransparencePercent < 1) || ((mnDrawMode & DRAWMODE_NOTRANSPARENCY) != 0) ) + { + DrawPolyPolygon( rPolyPoly ); + return; + } + + // short circut for drawing an invisible polygon + if( !mbFillColor || (nTransparencePercent >= 100) ) + { + // short circuit if the polygon border is invisible too + if( !mbLineColor ) + return; + + // DrawTransparent() assumes that the border is NOT to be drawn transparently??? + Push( PUSH_FILLCOLOR ); + SetFillColor(); + DrawPolyPolygon( rPolyPoly ); + Pop(); + return; + } + + // handle metafile recording + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaTransparentAction( rPolyPoly, nTransparencePercent ) ); + + bool bDrawn = !IsDeviceOutputNecessary() || ImplIsRecordLayout(); + if( bDrawn ) + return; + + // get the device graphics as drawing target + if( !mpGraphics ) + if( !ImplGetGraphics() ) + return; + + // debug helper: + static const char* pDisableNative = getenv( "SAL_DISABLE_NATIVE_ALPHA"); + + // try hard to draw it directly, because the emulation layers are slower + if( !pDisableNative + && mpGraphics->supportsOperation( OutDevSupport_B2DDraw ) +#if defined UNX && ! defined QUARTZ + && GetBitCount() > 8 +#endif +#ifdef WIN32 + // workaround bad dithering on remote displaying when using GDI+ with toolbar buttoin hilighting + && !rPolyPoly.IsRect() +#endif + ) + { + // prepare the graphics device + if( mbInitClipRegion ) + ImplInitClipRegion(); + if( mbOutputClipped ) + return; + if( mbInitLineColor ) + ImplInitLineColor(); + if( mbInitFillColor ) + ImplInitFillColor(); + + // get the polygon in device coordinates + basegfx::B2DPolyPolygon aB2DPolyPolygon( rPolyPoly.getB2DPolyPolygon() ); + const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation(); + aB2DPolyPolygon.transform( aTransform ); + + const double fTransparency = 0.01 * nTransparencePercent; + if( mbFillColor ) + { + // draw the transparent polygon + // NOTE: filled polygons are assumed to be drawn as if they were always closed + bDrawn = mpGraphics->DrawPolyPolygon( aB2DPolyPolygon, fTransparency, this ); + } + + if( mbLineColor ) + { + // disable the fill color for now + mpGraphics->SetFillColor(); + // draw the border line + const basegfx::B2DVector aLineWidths( 1, 1 ); + const int nPolyCount = aB2DPolyPolygon.count(); + for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) + { + const ::basegfx::B2DPolygon& rPolygon = aB2DPolyPolygon.getB2DPolygon( nPolyIdx ); + bDrawn = mpGraphics->DrawPolyLine( rPolygon, fTransparency, aLineWidths, ::basegfx::B2DLINEJOIN_NONE, this ); + } + // prepare to restore the fill color + mbInitFillColor = mbFillColor; + } + } + + if( bDrawn ) + return; + + if( 1 ) + { + VirtualDevice* pOldAlphaVDev = mpAlphaVDev; + + // #110958# Disable alpha VDev, we perform the necessary + // operation explicitely further below. + if( mpAlphaVDev ) + mpAlphaVDev = NULL; + + GDIMetaFile* pOldMetaFile = mpMetaFile; + mpMetaFile = NULL; + + if( OUTDEV_PRINTER == meOutDevType ) + { + if(100 <= nTransparencePercent) + { + // #i112959# 100% transparent, draw nothing + return; + } + + Rectangle aPolyRect( LogicToPixel( rPolyPoly ).GetBoundRect() ); + const Size aDPISize( LogicToPixel( Size( 1, 1 ), MAP_INCH ) ); + const long nBaseExtent = Max( FRound( aDPISize.Width() / 300. ), 1L ); + long nMove; + const sal_uInt16 nTrans = ( nTransparencePercent < 13 ) ? 0 : + ( nTransparencePercent < 38 ) ? 25 : + ( nTransparencePercent < 63 ) ? 50 : + ( nTransparencePercent < 88 ) ? 75 : 100; + + switch( nTrans ) + { + case( 25 ): nMove = nBaseExtent * 3; break; + case( 50 ): nMove = nBaseExtent * 4; break; + case( 75 ): nMove = nBaseExtent * 6; break; + + // #i112959# very transparent (88 < nTransparencePercent <= 99) + case( 100 ): nMove = nBaseExtent * 8; break; + + // #i112959# not transparent (nTransparencePercent < 13) + default: nMove = 0; break; + } + + Push( PUSH_CLIPREGION | PUSH_LINECOLOR ); + IntersectClipRegion( rPolyPoly ); + SetLineColor( GetFillColor() ); + const sal_Bool bOldMap = mbMap; + EnableMapMode( sal_False ); + + if(nMove) + { + Rectangle aRect( aPolyRect.TopLeft(), Size( aPolyRect.GetWidth(), nBaseExtent ) ); + while( aRect.Top() <= aPolyRect.Bottom() ) + { + DrawRect( aRect ); + aRect.Move( 0, nMove ); + } + + aRect = Rectangle( aPolyRect.TopLeft(), Size( nBaseExtent, aPolyRect.GetHeight() ) ); + while( aRect.Left() <= aPolyRect.Right() ) + { + DrawRect( aRect ); + aRect.Move( nMove, 0 ); + } + } + else + { + // #i112959# if not transparent, draw full rectangle in clip region + DrawRect( aPolyRect ); + } + + EnableMapMode( bOldMap ); + Pop(); + } + else + { + PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) ); + Rectangle aPolyRect( aPolyPoly.GetBoundRect() ); + Point aPoint; + Rectangle aDstRect( aPoint, GetOutputSizePixel() ); + + aDstRect.Intersection( aPolyRect ); + + if( OUTDEV_WINDOW == meOutDevType ) + { + const Region aPaintRgn( ( (Window*) this )->GetPaintRegion() ); + + if( !aPaintRgn.IsNull() ) + aDstRect.Intersection( LogicToPixel( aPaintRgn ).GetBoundRect() ); + } + + if( !aDstRect.IsEmpty() ) + { + // #i66849# Added fast path for exactly rectangular + // polygons + // #i83087# Naturally, system alpha blending cannot + // work with separate alpha VDev + if( !mpAlphaVDev && !pDisableNative && aPolyPoly.IsRect() ) + { + // setup Graphics only here (other cases delegate + // to basic OutDev methods) + if( 1 ) + { + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); + + Rectangle aLogicPolyRect( rPolyPoly.GetBoundRect() ); + Rectangle aPixelRect( ImplLogicToDevicePixel( aLogicPolyRect ) ); + + if( !mbOutputClipped ) + { + bDrawn = mpGraphics->DrawAlphaRect( + aPixelRect.Left(), aPixelRect.Top(), + // #i98405# use methods with small g, else one pixel too much will be painted. + // This is because the source is a polygon which when painted would not paint + // the rightmost and lowest pixel line(s), so use one pixel less for the + // rectangle, too. + aPixelRect.getWidth(), aPixelRect.getHeight(), + sal::static_int_cast<sal_uInt8>(nTransparencePercent), + this ); + } + else + bDrawn = true; + } + } + + if( !bDrawn ) + { + VirtualDevice aVDev( *this, 1 ); + const Size aDstSz( aDstRect.GetSize() ); + const sal_uInt8 cTrans = (sal_uInt8) MinMax( FRound( nTransparencePercent * 2.55 ), 0, 255 ); + + if( aDstRect.Left() || aDstRect.Top() ) + aPolyPoly.Move( -aDstRect.Left(), -aDstRect.Top() ); + + if( aVDev.SetOutputSizePixel( aDstSz ) ) + { + const sal_Bool bOldMap = mbMap; + + EnableMapMode( sal_False ); + + aVDev.SetLineColor( COL_BLACK ); + aVDev.SetFillColor( COL_BLACK ); + aVDev.DrawPolyPolygon( aPolyPoly ); + + Bitmap aPaint( GetBitmap( aDstRect.TopLeft(), aDstSz ) ); + Bitmap aPolyMask( aVDev.GetBitmap( Point(), aDstSz ) ); + + // #107766# check for non-empty bitmaps before accessing them + if( !!aPaint && !!aPolyMask ) + { + BitmapWriteAccess* pW = aPaint.AcquireWriteAccess(); + BitmapReadAccess* pR = aPolyMask.AcquireReadAccess(); + + if( pW && pR ) + { + BitmapColor aPixCol; + const BitmapColor aFillCol( GetFillColor() ); + const BitmapColor aWhite( pR->GetBestMatchingColor( Color( COL_WHITE ) ) ); + const BitmapColor aBlack( pR->GetBestMatchingColor( Color( COL_BLACK ) ) ); + const long nWidth = pW->Width(), nHeight = pW->Height(); + const long nR = aFillCol.GetRed(), nG = aFillCol.GetGreen(), nB = aFillCol.GetBlue(); + long nX, nY; + + if( aPaint.GetBitCount() <= 8 ) + { + const BitmapPalette& rPal = pW->GetPalette(); + const sal_uInt16 nCount = rPal.GetEntryCount(); + BitmapColor* pMap = (BitmapColor*) new sal_uInt8[ nCount * sizeof( BitmapColor ) ]; + + for( sal_uInt16 i = 0; i < nCount; i++ ) + { + BitmapColor aCol( rPal[ i ] ); + pMap[ i ] = BitmapColor( (sal_uInt8) rPal.GetBestIndex( aCol.Merge( aFillCol, cTrans ) ) ); + } + + if( pR->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL && + pW->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ) + { + const sal_uInt8 cBlack = aBlack.GetIndex(); + + for( nY = 0; nY < nHeight; nY++ ) + { + Scanline pWScan = pW->GetScanline( nY ); + Scanline pRScan = pR->GetScanline( nY ); + sal_uInt8 cBit = 128; + + for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan++ ) + { + if( !cBit ) + cBit = 128, pRScan++; + + if( ( *pRScan & cBit ) == cBlack ) + *pWScan = (sal_uInt8) pMap[ *pWScan ].GetIndex(); + } + } + } + else + { + for( nY = 0; nY < nHeight; nY++ ) + for( nX = 0; nX < nWidth; nX++ ) + if( pR->GetPixel( nY, nX ) == aBlack ) + pW->SetPixel( nY, nX, pMap[ pW->GetPixel( nY, nX ).GetIndex() ] ); + } + + delete[] (sal_uInt8*) pMap; + } + else + { + if( pR->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL && + pW->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR ) + { + const sal_uInt8 cBlack = aBlack.GetIndex(); + + for( nY = 0; nY < nHeight; nY++ ) + { + Scanline pWScan = pW->GetScanline( nY ); + Scanline pRScan = pR->GetScanline( nY ); + sal_uInt8 cBit = 128; + + for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan += 3 ) + { + if( !cBit ) + cBit = 128, pRScan++; + + if( ( *pRScan & cBit ) == cBlack ) + { + pWScan[ 0 ] = COLOR_CHANNEL_MERGE( pWScan[ 0 ], nB, cTrans ); + pWScan[ 1 ] = COLOR_CHANNEL_MERGE( pWScan[ 1 ], nG, cTrans ); + pWScan[ 2 ] = COLOR_CHANNEL_MERGE( pWScan[ 2 ], nR, cTrans ); + } + } + } + } + else + { + for( nY = 0; nY < nHeight; nY++ ) + { + for( nX = 0; nX < nWidth; nX++ ) + { + if( pR->GetPixel( nY, nX ) == aBlack ) + { + aPixCol = pW->GetColor( nY, nX ); + pW->SetPixel( nY, nX, aPixCol.Merge( aFillCol, cTrans ) ); + } + } + } + } + } + } + + aPolyMask.ReleaseAccess( pR ); + aPaint.ReleaseAccess( pW ); + + DrawBitmap( aDstRect.TopLeft(), aPaint ); + + EnableMapMode( bOldMap ); + + if( mbLineColor ) + { + Push( PUSH_FILLCOLOR ); + SetFillColor(); + DrawPolyPolygon( rPolyPoly ); + Pop(); + } + } + } + else + DrawPolyPolygon( rPolyPoly ); + } + } + } + + mpMetaFile = pOldMetaFile; + + // #110958# Restore disabled alpha VDev + mpAlphaVDev = pOldAlphaVDev; + + // #110958# Apply alpha value also to VDev alpha channel + if( mpAlphaVDev ) + { + const Color aFillCol( mpAlphaVDev->GetFillColor() ); + mpAlphaVDev->SetFillColor( Color(sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100), + sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100), + sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100)) ); + + mpAlphaVDev->DrawTransparent( rPolyPoly, nTransparencePercent ); + + mpAlphaVDev->SetFillColor( aFillCol ); + } + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawTransparent( const GDIMetaFile& rMtf, const Point& rPos, + const Size& rSize, const Gradient& rTransparenceGradient ) +{ + OSL_TRACE( "OutputDevice::DrawTransparent()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + const Color aBlack( COL_BLACK ); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaFloatTransparentAction( rMtf, rPos, rSize, rTransparenceGradient ) ); + + if( ( rTransparenceGradient.GetStartColor() == aBlack && rTransparenceGradient.GetEndColor() == aBlack ) || + ( mnDrawMode & ( DRAWMODE_NOTRANSPARENCY ) ) ) + { + ( (GDIMetaFile&) rMtf ).WindStart(); + ( (GDIMetaFile&) rMtf ).Play( this, rPos, rSize ); + ( (GDIMetaFile&) rMtf ).WindStart(); + } + else + { + GDIMetaFile* pOldMetaFile = mpMetaFile; + Rectangle aOutRect( LogicToPixel( rPos ), LogicToPixel( rSize ) ); + Point aPoint; + Rectangle aDstRect( aPoint, GetOutputSizePixel() ); + + mpMetaFile = NULL; + aDstRect.Intersection( aOutRect ); + + if( OUTDEV_WINDOW == meOutDevType ) + { + const Region aPaintRgn( ( (Window*) this )->GetPaintRegion() ); + + if( !aPaintRgn.IsNull() ) + aDstRect.Intersection( LogicToPixel( aPaintRgn.GetBoundRect() ) ); + } + + if( !aDstRect.IsEmpty() ) + { + VirtualDevice* pVDev = new VirtualDevice; + + ((OutputDevice*)pVDev)->mnDPIX = mnDPIX; + ((OutputDevice*)pVDev)->mnDPIY = mnDPIY; + + if( pVDev->SetOutputSizePixel( aDstRect.GetSize() ) ) + { + if(GetAntialiasing()) + { + // #i102109# + // For MetaFile replay (see task) it may now be neccessary to take + // into account that the content is AntiAlialised and needs to be masked + // like that. Instead of masking, i will use a copy-modify-paste cycle + // here (as i already use in the VclPrimiziveRenderer with successs) + pVDev->SetAntialiasing(GetAntialiasing()); + + // create MapMode for buffer (offset needed) and set + MapMode aMap(GetMapMode()); + const Point aOutPos(PixelToLogic(aDstRect.TopLeft())); + aMap.SetOrigin(Point(-aOutPos.X(), -aOutPos.Y())); + pVDev->SetMapMode(aMap); + + // copy MapMode state and disable for target + const bool bOrigMapModeEnabled(IsMapModeEnabled()); + EnableMapMode(false); + + // copy MapMode state and disable for buffer + const bool bBufferMapModeEnabled(pVDev->IsMapModeEnabled()); + pVDev->EnableMapMode(false); + + // copy content from original to buffer + pVDev->DrawOutDev( + aPoint, pVDev->GetOutputSizePixel(), // dest + aDstRect.TopLeft(), pVDev->GetOutputSizePixel(), // source + *this); + + // draw MetaFile to buffer + pVDev->EnableMapMode(bBufferMapModeEnabled); + ((GDIMetaFile&)rMtf).WindStart(); + ((GDIMetaFile&)rMtf).Play(pVDev, rPos, rSize); + ((GDIMetaFile&)rMtf).WindStart(); + + // get content bitmap from buffer + pVDev->EnableMapMode(false); + const Bitmap aPaint(pVDev->GetBitmap(aPoint, pVDev->GetOutputSizePixel())); + + // create alpha mask from gradient and get as Bitmap + pVDev->EnableMapMode(bBufferMapModeEnabled); + pVDev->SetDrawMode(DRAWMODE_GRAYGRADIENT); + pVDev->DrawGradient(Rectangle(rPos, rSize), rTransparenceGradient); + pVDev->SetDrawMode(DRAWMODE_DEFAULT); + pVDev->EnableMapMode(false); + const AlphaMask aAlpha(pVDev->GetBitmap(aPoint, pVDev->GetOutputSizePixel())); + + delete pVDev; + + // draw masked content to target and restore MapMode + DrawBitmapEx(aDstRect.TopLeft(), BitmapEx(aPaint, aAlpha)); + EnableMapMode(bOrigMapModeEnabled); + } + else + { + Bitmap aPaint, aMask; + AlphaMask aAlpha; + MapMode aMap( GetMapMode() ); + Point aOutPos( PixelToLogic( aDstRect.TopLeft() ) ); + const sal_Bool bOldMap = mbMap; + + aMap.SetOrigin( Point( -aOutPos.X(), -aOutPos.Y() ) ); + pVDev->SetMapMode( aMap ); + const sal_Bool bVDevOldMap = pVDev->IsMapModeEnabled(); + + // create paint bitmap + ( (GDIMetaFile&) rMtf ).WindStart(); + ( (GDIMetaFile&) rMtf ).Play( pVDev, rPos, rSize ); + ( (GDIMetaFile&) rMtf ).WindStart(); + pVDev->EnableMapMode( sal_False ); + aPaint = pVDev->GetBitmap( Point(), pVDev->GetOutputSizePixel() ); + pVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here! + + // create mask bitmap + pVDev->SetLineColor( COL_BLACK ); + pVDev->SetFillColor( COL_BLACK ); + pVDev->DrawRect( Rectangle( pVDev->PixelToLogic( Point() ), pVDev->GetOutputSize() ) ); + pVDev->SetDrawMode( DRAWMODE_WHITELINE | DRAWMODE_WHITEFILL | DRAWMODE_WHITETEXT | + DRAWMODE_WHITEBITMAP | DRAWMODE_WHITEGRADIENT ); + ( (GDIMetaFile&) rMtf ).WindStart(); + ( (GDIMetaFile&) rMtf ).Play( pVDev, rPos, rSize ); + ( (GDIMetaFile&) rMtf ).WindStart(); + pVDev->EnableMapMode( sal_False ); + aMask = pVDev->GetBitmap( Point(), pVDev->GetOutputSizePixel() ); + pVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here! + + // create alpha mask from gradient + pVDev->SetDrawMode( DRAWMODE_GRAYGRADIENT ); + pVDev->DrawGradient( Rectangle( rPos, rSize ), rTransparenceGradient ); + pVDev->SetDrawMode( DRAWMODE_DEFAULT ); + pVDev->EnableMapMode( sal_False ); + pVDev->DrawMask( Point(), pVDev->GetOutputSizePixel(), aMask, Color( COL_WHITE ) ); + + aAlpha = pVDev->GetBitmap( Point(), pVDev->GetOutputSizePixel() ); + + delete pVDev; + + EnableMapMode( sal_False ); + DrawBitmapEx( aDstRect.TopLeft(), BitmapEx( aPaint, aAlpha ) ); + EnableMapMode( bOldMap ); + } + } + else + delete pVDev; + } + + mpMetaFile = pOldMetaFile; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawColorWallpaper( long nX, long nY, + long nWidth, long nHeight, + const Wallpaper& rWallpaper ) +{ + // Wallpaper ohne Umrandung zeichnen + Color aOldLineColor = GetLineColor(); + Color aOldFillColor = GetFillColor(); + SetLineColor(); + SetFillColor( rWallpaper.GetColor() ); + sal_Bool bMap = mbMap; + EnableMapMode( sal_False ); + DrawRect( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ) ); + SetLineColor( aOldLineColor ); + SetFillColor( aOldFillColor ); + EnableMapMode( bMap ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawBitmapWallpaper( long nX, long nY, + long nWidth, long nHeight, + const Wallpaper& rWallpaper ) +{ + BitmapEx aBmpEx; + const BitmapEx* pCached = rWallpaper.ImplGetImpWallpaper()->ImplGetCachedBitmap(); + Point aPos; + Size aSize; + GDIMetaFile* pOldMetaFile = mpMetaFile; + const WallpaperStyle eStyle = rWallpaper.GetStyle(); + const sal_Bool bOldMap = mbMap; + sal_Bool bDrawn = sal_False; + sal_Bool bDrawGradientBackground = sal_False; + sal_Bool bDrawColorBackground = sal_False; + + if( pCached ) + aBmpEx = *pCached; + else + aBmpEx = rWallpaper.GetBitmap(); + + const long nBmpWidth = aBmpEx.GetSizePixel().Width(); + const long nBmpHeight = aBmpEx.GetSizePixel().Height(); + const sal_Bool bTransparent = aBmpEx.IsTransparent(); + + // draw background + if( bTransparent ) + { + if( rWallpaper.IsGradient() ) + bDrawGradientBackground = sal_True; + else + { + if( !pCached && !rWallpaper.GetColor().GetTransparency() ) + { + VirtualDevice aVDev( *this ); + aVDev.SetBackground( rWallpaper.GetColor() ); + aVDev.SetOutputSizePixel( Size( nBmpWidth, nBmpHeight ) ); + aVDev.DrawBitmapEx( Point(), aBmpEx ); + aBmpEx = aVDev.GetBitmap( Point(), aVDev.GetOutputSizePixel() ); + } + + bDrawColorBackground = sal_True; + } + } + else if( eStyle != WALLPAPER_TILE && eStyle != WALLPAPER_SCALE ) + { + if( rWallpaper.IsGradient() ) + bDrawGradientBackground = sal_True; + else + bDrawColorBackground = sal_True; + } + + // background of bitmap? + if( bDrawGradientBackground ) + ImplDrawGradientWallpaper( nX, nY, nWidth, nHeight, rWallpaper ); + else if( bDrawColorBackground && bTransparent ) + { + ImplDrawColorWallpaper( nX, nY, nWidth, nHeight, rWallpaper ); + bDrawColorBackground = sal_False; + } + + // calc pos and size + if( rWallpaper.IsRect() ) + { + const Rectangle aBound( LogicToPixel( rWallpaper.GetRect() ) ); + aPos = aBound.TopLeft(); + aSize = aBound.GetSize(); + } + else + { + aPos = Point( nX, nY ); + aSize = Size( nWidth, nHeight ); + } + + mpMetaFile = NULL; + EnableMapMode( sal_False ); + Push( PUSH_CLIPREGION ); + IntersectClipRegion( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ) ); + + switch( eStyle ) + { + case( WALLPAPER_SCALE ): + { + if( !pCached || ( pCached->GetSizePixel() != aSize ) ) + { + if( pCached ) + rWallpaper.ImplGetImpWallpaper()->ImplReleaseCachedBitmap(); + + aBmpEx = rWallpaper.GetBitmap(); + aBmpEx.Scale( aSize ); + aBmpEx = BitmapEx( aBmpEx.GetBitmap().CreateDisplayBitmap( this ), aBmpEx.GetMask() ); + } + } + break; + + case( WALLPAPER_TOPLEFT ): + break; + + case( WALLPAPER_TOP ): + aPos.X() += ( aSize.Width() - nBmpWidth ) >> 1; + break; + + case( WALLPAPER_TOPRIGHT ): + aPos.X() += ( aSize.Width() - nBmpWidth ); + break; + + case( WALLPAPER_LEFT ): + aPos.Y() += ( aSize.Height() - nBmpHeight ) >> 1; + break; + + case( WALLPAPER_CENTER ): + { + aPos.X() += ( aSize.Width() - nBmpWidth ) >> 1; + aPos.Y() += ( aSize.Height() - nBmpHeight ) >> 1; + } + break; + + case( WALLPAPER_RIGHT ): + { + aPos.X() += ( aSize.Width() - nBmpWidth ); + aPos.Y() += ( aSize.Height() - nBmpHeight ) >> 1; + } + break; + + case( WALLPAPER_BOTTOMLEFT ): + aPos.Y() += ( aSize.Height() - nBmpHeight ); + break; + + case( WALLPAPER_BOTTOM ): + { + aPos.X() += ( aSize.Width() - nBmpWidth ) >> 1; + aPos.Y() += ( aSize.Height() - nBmpHeight ); + } + break; + + case( WALLPAPER_BOTTOMRIGHT ): + { + aPos.X() += ( aSize.Width() - nBmpWidth ); + aPos.Y() += ( aSize.Height() - nBmpHeight ); + } + break; + + default: + { + const long nRight = nX + nWidth - 1L; + const long nBottom = nY + nHeight - 1L; + long nFirstX; + long nFirstY; + + if( eStyle == WALLPAPER_TILE ) + { + nFirstX = aPos.X(); + nFirstY = aPos.Y(); + } + else + { + nFirstX = aPos.X() + ( ( aSize.Width() - nBmpWidth ) >> 1 ); + nFirstY = aPos.Y() + ( ( aSize.Height() - nBmpHeight ) >> 1 ); + } + + const long nOffX = ( nFirstX - nX ) % nBmpWidth; + const long nOffY = ( nFirstY - nY ) % nBmpHeight; + long nStartX = nX + nOffX; + long nStartY = nY + nOffY; + + if( nOffX > 0L ) + nStartX -= nBmpWidth; + + if( nOffY > 0L ) + nStartY -= nBmpHeight; + + for( long nBmpY = nStartY; nBmpY <= nBottom; nBmpY += nBmpHeight ) + for( long nBmpX = nStartX; nBmpX <= nRight; nBmpX += nBmpWidth ) + DrawBitmapEx( Point( nBmpX, nBmpY ), aBmpEx ); + + bDrawn = sal_True; + } + break; + } + + if( !bDrawn ) + { + // optimized for non-transparent bitmaps + if( bDrawColorBackground ) + { + const Size aBmpSize( aBmpEx.GetSizePixel() ); + const Point aTmpPoint; + const Rectangle aOutRect( aTmpPoint, GetOutputSizePixel() ); + const Rectangle aColRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + Rectangle aWorkRect; + + aWorkRect = Rectangle( 0, 0, aOutRect.Right(), aPos.Y() - 1L ); + aWorkRect.Justify(); + aWorkRect.Intersection( aColRect ); + if( !aWorkRect.IsEmpty() ) + { + ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(), + aWorkRect.GetWidth(), aWorkRect.GetHeight(), + rWallpaper ); + } + + aWorkRect = Rectangle( 0, aPos.Y(), aPos.X() - 1L, aPos.Y() + aBmpSize.Height() - 1L ); + aWorkRect.Justify(); + aWorkRect.Intersection( aColRect ); + if( !aWorkRect.IsEmpty() ) + { + ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(), + aWorkRect.GetWidth(), aWorkRect.GetHeight(), + rWallpaper ); + } + + aWorkRect = Rectangle( aPos.X() + aBmpSize.Width(), aPos.Y(), aOutRect.Right(), aPos.Y() + aBmpSize.Height() - 1L ); + aWorkRect.Justify(); + aWorkRect.Intersection( aColRect ); + if( !aWorkRect.IsEmpty() ) + { + ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(), + aWorkRect.GetWidth(), aWorkRect.GetHeight(), + rWallpaper ); + } + + aWorkRect = Rectangle( 0, aPos.Y() + aBmpSize.Height(), aOutRect.Right(), aOutRect.Bottom() ); + aWorkRect.Justify(); + aWorkRect.Intersection( aColRect ); + if( !aWorkRect.IsEmpty() ) + { + ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(), + aWorkRect.GetWidth(), aWorkRect.GetHeight(), + rWallpaper ); + } + } + + DrawBitmapEx( aPos, aBmpEx ); + } + + rWallpaper.ImplGetImpWallpaper()->ImplSetCachedBitmap( aBmpEx ); + + Pop(); + EnableMapMode( bOldMap ); + mpMetaFile = pOldMetaFile; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawGradientWallpaper( long nX, long nY, + long nWidth, long nHeight, + const Wallpaper& rWallpaper ) +{ + Rectangle aBound; + GDIMetaFile* pOldMetaFile = mpMetaFile; + const sal_Bool bOldMap = mbMap; + sal_Bool bNeedGradient = sal_True; + +/* + if ( rWallpaper.IsRect() ) + aBound = LogicToPixel( rWallpaper.GetRect() ); + else +*/ + aBound = Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ); + + mpMetaFile = NULL; + EnableMapMode( sal_False ); + Push( PUSH_CLIPREGION ); + IntersectClipRegion( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ) ); + + if( OUTDEV_WINDOW == meOutDevType && rWallpaper.GetStyle() == WALLPAPER_APPLICATIONGRADIENT ) + { + Window *pWin = dynamic_cast< Window* >( this ); + if( pWin ) + { + // limit gradient to useful size, so that it still can be noticed + // in maximized windows + long gradientWidth = pWin->GetDesktopRectPixel().GetSize().Width(); + if( gradientWidth > 1024 ) + gradientWidth = 1024; + if( mnOutOffX+nWidth > gradientWidth ) + ImplDrawColorWallpaper( nX, nY, nWidth, nHeight, rWallpaper.GetGradient().GetEndColor() ); + if( mnOutOffX > gradientWidth ) + bNeedGradient = sal_False; + else + aBound = Rectangle( Point( -mnOutOffX, nY ), Size( gradientWidth, nHeight ) ); + } + } + + if( bNeedGradient ) + DrawGradient( aBound, rWallpaper.GetGradient() ); + + Pop(); + EnableMapMode( bOldMap ); + mpMetaFile = pOldMetaFile; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawWallpaper( long nX, long nY, + long nWidth, long nHeight, + const Wallpaper& rWallpaper ) +{ + if( rWallpaper.IsBitmap() ) + ImplDrawBitmapWallpaper( nX, nY, nWidth, nHeight, rWallpaper ); + else if( rWallpaper.IsGradient() ) + ImplDrawGradientWallpaper( nX, nY, nWidth, nHeight, rWallpaper ); + else + ImplDrawColorWallpaper( nX, nY, nWidth, nHeight, rWallpaper ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawWallpaper( const Rectangle& rRect, + const Wallpaper& rWallpaper ) +{ + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaWallpaperAction( rRect, rWallpaper ) ); + + if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + if ( rWallpaper.GetStyle() != WALLPAPER_NULL ) + { + Rectangle aRect = LogicToPixel( rRect ); + aRect.Justify(); + + if ( !aRect.IsEmpty() ) + { + ImplDrawWallpaper( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), + rWallpaper ); + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawWallpaper( rRect, rWallpaper ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::Erase() +{ + if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + sal_Bool bNativeOK = sal_False; + if( meOutDevType == OUTDEV_WINDOW ) + { + Window* pWindow = static_cast<Window*>(this); + ControlPart aCtrlPart = pWindow->ImplGetWindowImpl()->mnNativeBackground; + if( aCtrlPart != 0 && ! pWindow->IsControlBackground() ) + { + ImplControlValue aControlValue; + Point aGcc3WorkaroundTemporary; + Rectangle aCtrlRegion( aGcc3WorkaroundTemporary, GetOutputSizePixel() ); + ControlState nState = 0; + + if( pWindow->IsEnabled() ) nState |= CTRL_STATE_ENABLED; + bNativeOK = pWindow->DrawNativeControl( CTRL_WINDOW_BACKGROUND, aCtrlPart, aCtrlRegion, + nState, aControlValue, rtl::OUString() ); + } + } + + if ( mbBackground && ! bNativeOK ) + { + RasterOp eRasterOp = GetRasterOp(); + if ( eRasterOp != ROP_OVERPAINT ) + SetRasterOp( ROP_OVERPAINT ); + ImplDrawWallpaper( 0, 0, mnOutWidth, mnOutHeight, maBackground ); + if ( eRasterOp != ROP_OVERPAINT ) + SetRasterOp( eRasterOp ); + } + + if( mpAlphaVDev ) + mpAlphaVDev->Erase(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDraw2ColorFrame( const Rectangle& rRect, + const Color& rLeftTopColor, + const Color& rRightBottomColor ) +{ + SetFillColor( rLeftTopColor ); + DrawRect( Rectangle( rRect.TopLeft(), Point( rRect.Left(), rRect.Bottom()-1 ) ) ); + DrawRect( Rectangle( rRect.TopLeft(), Point( rRect.Right()-1, rRect.Top() ) ) ); + SetFillColor( rRightBottomColor ); + DrawRect( Rectangle( rRect.BottomLeft(), rRect.BottomRight() ) ); + DrawRect( Rectangle( rRect.TopRight(), rRect.BottomRight() ) ); +} + +// ----------------------------------------------------------------------- + +bool OutputDevice::DrawEPS( const Point& rPoint, const Size& rSize, + const GfxLink& rGfxLink, GDIMetaFile* pSubst ) +{ + bool bDrawn(true); + + if ( mpMetaFile ) + { + GDIMetaFile aSubst; + + if( pSubst ) + aSubst = *pSubst; + + mpMetaFile->AddAction( new MetaEPSAction( rPoint, rSize, rGfxLink, aSubst ) ); + } + + if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return bDrawn; + + if( mbOutputClipped ) + return bDrawn; + + Rectangle aRect( ImplLogicToDevicePixel( Rectangle( rPoint, rSize ) ) ); + + if( !aRect.IsEmpty() ) + { + // draw the real EPS graphics + if( rGfxLink.GetData() && rGfxLink.GetDataSize() ) + { + if( !mpGraphics && !ImplGetGraphics() ) + return bDrawn; + + if( mbInitClipRegion ) + ImplInitClipRegion(); + + aRect.Justify(); + bDrawn = mpGraphics->DrawEPS( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), + (sal_uInt8*) rGfxLink.GetData(), rGfxLink.GetDataSize(), this ); + } + + // else draw the substitution graphics + if( !bDrawn && pSubst ) + { + GDIMetaFile* pOldMetaFile = mpMetaFile; + + mpMetaFile = NULL; + Graphic( *pSubst ).Draw( this, rPoint, rSize ); + mpMetaFile = pOldMetaFile; + } + } + + if( mpAlphaVDev ) + mpAlphaVDev->DrawEPS( rPoint, rSize, rGfxLink, pSubst ); + + return bDrawn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/outdevnative.cxx b/vcl/source/gdi/outdevnative.cxx new file mode 100644 index 000000000000..5ae7baaa9d89 --- /dev/null +++ b/vcl/source/gdi/outdevnative.cxx @@ -0,0 +1,345 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "vcl/outdev.hxx" +#include "vcl/window.hxx" +#include "vcl/salgdi.hxx" +#include "vcl/salnativewidgets.hxx" +#include "vcl/pdfextoutdevdata.hxx" + +// ----------------------------------------------------------------------- + +static bool lcl_enableNativeWidget( const OutputDevice& i_rDevice ) +{ + const OutDevType eType( i_rDevice.GetOutDevType() ); + switch ( eType ) + { + + case OUTDEV_WINDOW: + return dynamic_cast< const Window* >( &i_rDevice )->IsNativeWidgetEnabled(); + + case OUTDEV_VIRDEV: + { + const ::vcl::ExtOutDevData* pOutDevData( i_rDevice.GetExtOutDevData() ); + const ::vcl::PDFExtOutDevData* pPDFData( dynamic_cast< const ::vcl::PDFExtOutDevData* >( pOutDevData ) ); + if ( pPDFData != NULL ) + return false; + return true; + } + + default: + return false; + } +} + +ImplControlValue::~ImplControlValue() +{ +} + +ScrollbarValue::~ScrollbarValue() +{ +} + +SliderValue::~SliderValue() +{ +} + +TabitemValue::~TabitemValue() +{ +} + +SpinbuttonValue::~SpinbuttonValue() +{ +} + +ToolbarValue::~ToolbarValue() +{ +} + +MenubarValue::~MenubarValue() +{ +} + +MenupopupValue::~MenupopupValue() +{ +} + +PushButtonValue::~PushButtonValue() +{ +} + +// ----------------------------------------------------------------------- +// These functions are mainly passthrough functions that allow access to +// the SalFrame behind a Window object for native widget rendering purposes. +// ----------------------------------------------------------------------- + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::IsNativeControlSupported( ControlType nType, ControlPart nPart ) +{ + if( !lcl_enableNativeWidget( *this ) ) + return sal_False; + + if ( !mpGraphics ) + if ( !ImplGetGraphics() ) + return sal_False; + + return( mpGraphics->IsNativeControlSupported(nType, nPart) ); +} + + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::HitTestNativeControl( ControlType nType, + ControlPart nPart, + const Rectangle& rControlRegion, + const Point& aPos, + sal_Bool& rIsInside ) +{ + if( !lcl_enableNativeWidget( *this ) ) + return sal_False; + + if ( !mpGraphics ) + if ( !ImplGetGraphics() ) + return sal_False; + + Point aWinOffs( mnOutOffX, mnOutOffY ); + Rectangle screenRegion( rControlRegion ); + screenRegion.Move( aWinOffs.X(), aWinOffs.Y()); + + return( mpGraphics->HitTestNativeControl(nType, nPart, screenRegion, Point( aPos.X() + mnOutOffX, aPos.Y() + mnOutOffY ), + rIsInside, this ) ); +} + +// ----------------------------------------------------------------------- + +static boost::shared_ptr< ImplControlValue > lcl_transformControlValue( const ImplControlValue& rVal, OutputDevice& rDev ) +{ + boost::shared_ptr< ImplControlValue > aResult; + switch( rVal.getType() ) + { + case CTRL_SLIDER: + { + const SliderValue* pSlVal = static_cast<const SliderValue*>(&rVal); + SliderValue* pNew = new SliderValue( *pSlVal ); + aResult.reset( pNew ); + pNew->maThumbRect = rDev.ImplLogicToDevicePixel( pSlVal->maThumbRect ); + } + break; + case CTRL_SCROLLBAR: + { + const ScrollbarValue* pScVal = static_cast<const ScrollbarValue*>(&rVal); + ScrollbarValue* pNew = new ScrollbarValue( *pScVal ); + aResult.reset( pNew ); + pNew->maThumbRect = rDev.ImplLogicToDevicePixel( pScVal->maThumbRect ); + pNew->maButton1Rect = rDev.ImplLogicToDevicePixel( pScVal->maButton1Rect ); + pNew->maButton2Rect = rDev.ImplLogicToDevicePixel( pScVal->maButton2Rect ); + } + break; + case CTRL_SPINBUTTONS: + { + const SpinbuttonValue* pSpVal = static_cast<const SpinbuttonValue*>(&rVal); + SpinbuttonValue* pNew = new SpinbuttonValue( *pSpVal ); + aResult.reset( pNew ); + pNew->maUpperRect = rDev.ImplLogicToDevicePixel( pSpVal->maUpperRect ); + pNew->maLowerRect = rDev.ImplLogicToDevicePixel( pSpVal->maLowerRect ); + } + break; + case CTRL_TOOLBAR: + { + const ToolbarValue* pTVal = static_cast<const ToolbarValue*>(&rVal); + ToolbarValue* pNew = new ToolbarValue( *pTVal ); + aResult.reset( pNew ); + pNew->maGripRect = rDev.ImplLogicToDevicePixel( pTVal->maGripRect ); + } + break; + case CTRL_TAB_ITEM: + { + const TabitemValue* pTIVal = static_cast<const TabitemValue*>(&rVal); + TabitemValue* pNew = new TabitemValue( *pTIVal ); + aResult.reset( pNew ); + } + break; + case CTRL_MENUBAR: + { + const MenubarValue* pMVal = static_cast<const MenubarValue*>(&rVal); + MenubarValue* pNew = new MenubarValue( *pMVal ); + aResult.reset( pNew ); + } + break; + case CTRL_PUSHBUTTON: + { + const PushButtonValue* pBVal = static_cast<const PushButtonValue*>(&rVal); + PushButtonValue* pNew = new PushButtonValue( *pBVal ); + aResult.reset( pNew ); + } + break; + case CTRL_GENERIC: + aResult.reset( new ImplControlValue( rVal ) ); + break; + case CTRL_MENU_POPUP: + { + const MenupopupValue* pMVal = static_cast<const MenupopupValue*>(&rVal); + MenupopupValue* pNew = new MenupopupValue( *pMVal ); + pNew->maItemRect = rDev.ImplLogicToDevicePixel( pMVal->maItemRect ); + aResult.reset( pNew ); + } + break; + default: + OSL_FAIL( "unknown ImplControlValue type !" ); + break; + } + return aResult; +} +sal_Bool OutputDevice::DrawNativeControl( ControlType nType, + ControlPart nPart, + const Rectangle& rControlRegion, + ControlState nState, + const ImplControlValue& aValue, + ::rtl::OUString aCaption ) +{ + if( !lcl_enableNativeWidget( *this ) ) + return sal_False; + + // make sure the current clip region is initialized correctly + if ( !mpGraphics ) + if ( !ImplGetGraphics() ) + return sal_False; + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped +#ifndef WNT + // This check causes spin buttons to not be drawn on Windows, + // see fdo#36481 . Presumably it is useful on other plaforms, + // though. + || rControlRegion.IsEmpty() +#endif + ) + return sal_True; + + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); + + // Convert the coordinates from relative to Window-absolute, so we draw + // in the correct place in platform code + boost::shared_ptr< ImplControlValue > aScreenCtrlValue( lcl_transformControlValue( aValue, *this ) ); + Rectangle screenRegion( ImplLogicToDevicePixel( rControlRegion ) ); + + Region aTestRegion( GetActiveClipRegion() ); + aTestRegion.Intersect( rControlRegion ); + if( aTestRegion == rControlRegion ) + nState |= CTRL_CACHING_ALLOWED; // control is not clipped, caching allowed + + sal_Bool bRet = mpGraphics->DrawNativeControl(nType, nPart, screenRegion, nState, *aScreenCtrlValue, aCaption, this ); + + return bRet; +} + + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::DrawNativeControlText(ControlType nType, + ControlPart nPart, + const Rectangle& rControlRegion, + ControlState nState, + const ImplControlValue& aValue, + ::rtl::OUString aCaption ) +{ + if( !lcl_enableNativeWidget( *this ) ) + return sal_False; + + // make sure the current clip region is initialized correctly + if ( !mpGraphics ) + if ( !ImplGetGraphics() ) + return false; + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return true; + + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); + + // Convert the coordinates from relative to Window-absolute, so we draw + // in the correct place in platform code + boost::shared_ptr< ImplControlValue > aScreenCtrlValue( lcl_transformControlValue( aValue, *this ) ); + Rectangle screenRegion( ImplLogicToDevicePixel( rControlRegion ) ); + + sal_Bool bRet = mpGraphics->DrawNativeControlText(nType, nPart, screenRegion, nState, *aScreenCtrlValue, aCaption, this ); + + return bRet; +} + + +// ----------------------------------------------------------------------- + +sal_Bool OutputDevice::GetNativeControlRegion( ControlType nType, + ControlPart nPart, + const Rectangle& rControlRegion, + ControlState nState, + const ImplControlValue& aValue, + ::rtl::OUString aCaption, + Rectangle &rNativeBoundingRegion, + Rectangle &rNativeContentRegion ) +{ + if( !lcl_enableNativeWidget( *this ) ) + return sal_False; + + if ( !mpGraphics ) + if ( !ImplGetGraphics() ) + return sal_False; + + // Convert the coordinates from relative to Window-absolute, so we draw + // in the correct place in platform code + boost::shared_ptr< ImplControlValue > aScreenCtrlValue( lcl_transformControlValue( aValue, *this ) ); + Rectangle screenRegion( ImplLogicToDevicePixel( rControlRegion ) ); + + sal_Bool bRet = mpGraphics->GetNativeControlRegion(nType, nPart, screenRegion, nState, *aScreenCtrlValue, + aCaption, rNativeBoundingRegion, + rNativeContentRegion, this ); + if( bRet ) + { + // transform back native regions + rNativeBoundingRegion = ImplDevicePixelToLogic( rNativeBoundingRegion ); + rNativeContentRegion = ImplDevicePixelToLogic( rNativeContentRegion ); + } + + return bRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/outmap.cxx b/vcl/source/gdi/outmap.cxx new file mode 100644 index 000000000000..a0b2c6521aac --- /dev/null +++ b/vcl/source/gdi/outmap.cxx @@ -0,0 +1,2421 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <limits.h> + +#include <svsys.h> +#include <tools/bigint.hxx> +#include <tools/debug.hxx> +#include <vcl/virdev.hxx> +#include <vcl/svdata.hxx> +#include <tools/poly.hxx> +#include <vcl/region.hxx> +#include <vcl/region.h> +#include <vcl/window.h> +#include <vcl/wrkwin.hxx> +#include <vcl/cursor.hxx> +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/lineinfo.hxx> +#include <vcl/outdev.hxx> +#include <vcl/outdev.h> +#include <vcl/salgdi.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> + +// ======================================================================= + +DBG_NAMEEX( OutputDevice ) +DBG_NAMEEX( Polygon ) +DBG_NAMEEX( PolyPolygon ) +DBG_NAMEEX( Region ) + +// ======================================================================= + +static long aImplNumeratorAry[MAP_PIXEL+1] = + { 1, 1, 5, 50, 1, 1, 1, 1, 1, 1, 1 }; +static long aImplDenominatorAry[MAP_PIXEL+1] = + { 2540, 254, 127, 127, 1000, 100, 10, 1, 72, 1440, 1 }; + +// ----------------------------------------------------------------------- + +/* +Reduziert die Genauigkeit bis eine Fraction draus wird (sollte mal +ein Fraction ctor werden) koennte man dann auch mit BigInts machen +*/ + +static Fraction ImplMakeFraction( long nN1, long nN2, long nD1, long nD2 ) +{ + long i = 1; + + if ( nN1 < 0 ) { i = -i; nN1 = -nN1; } + if ( nN2 < 0 ) { i = -i; nN2 = -nN2; } + if ( nD1 < 0 ) { i = -i; nD1 = -nD1; } + if ( nD2 < 0 ) { i = -i; nD2 = -nD2; } + // alle positiv; i Vorzeichen + + Fraction aF( i*nN1, nD1 ); + aF *= Fraction( nN2, nD2 ); + + if( nD1 == 0 || nD2 == 0 ) //under these bad circumstances the following while loop will be endless + { + DBG_ASSERT(false,"Invalid parameter for ImplMakeFraction"); + return Fraction( 1, 1 ); + } + + while ( aF.GetDenominator() == -1 ) + { + if ( nN1 > nN2 ) + nN1 = (nN1 + 1) / 2; + else + nN2 = (nN2 + 1) / 2; + if ( nD1 > nD2 ) + nD1 = (nD1 + 1) / 2; + else + nD2 = (nD2 + 1) / 2; + + aF = Fraction( i*nN1, nD1 ); + aF *= Fraction( nN2, nD2 ); + } + + return aF; +} + +// ----------------------------------------------------------------------- + +// Fraction.GetNumerator() +// Fraction.GetDenominator() > 0 +// rOutRes.nPixPerInch? > 0 +// rMapRes.nMapScNum? +// rMapRes.nMapScDenom? > 0 + +static void ImplCalcBigIntThreshold( long nDPIX, long nDPIY, + const ImplMapRes& rMapRes, + ImplThresholdRes& rThresRes ) +{ + if ( nDPIX && (LONG_MAX / nDPIX < Abs( rMapRes.mnMapScNumX ) ) ) // #111139# avoid div by zero + { + rThresRes.mnThresLogToPixX = 0; + rThresRes.mnThresPixToLogX = 0; + } + else + { + // Schwellenwerte fuer BigInt Arithmetik berechnen + long nDenomHalfX = rMapRes.mnMapScDenomX / 2; + sal_uLong nDenomX = rMapRes.mnMapScDenomX; + long nProductX = nDPIX * rMapRes.mnMapScNumX; + + if ( !nProductX ) + rThresRes.mnThresLogToPixX = LONG_MAX; + else + rThresRes.mnThresLogToPixX = Abs( (LONG_MAX - nDenomHalfX) / nProductX ); + + if ( !nDenomX ) + rThresRes.mnThresPixToLogX = LONG_MAX; + else if ( nProductX >= 0 ) + rThresRes.mnThresPixToLogX = (long)(((sal_uLong)LONG_MAX - (sal_uLong)( nProductX/2)) / nDenomX); + else + rThresRes.mnThresPixToLogX = (long)(((sal_uLong)LONG_MAX + (sal_uLong)(-nProductX/2)) / nDenomX); + } + + if ( nDPIY && (LONG_MAX / nDPIY < Abs( rMapRes.mnMapScNumY ) ) ) // #111139# avoid div by zero + { + rThresRes.mnThresLogToPixY = 0; + rThresRes.mnThresPixToLogY = 0; + } + else + { + // Schwellenwerte fuer BigInt Arithmetik berechnen + long nDenomHalfY = rMapRes.mnMapScDenomY / 2; + sal_uLong nDenomY = rMapRes.mnMapScDenomY; + long nProductY = nDPIY * rMapRes.mnMapScNumY; + + if ( !nProductY ) + rThresRes.mnThresLogToPixY = LONG_MAX; + else + rThresRes.mnThresLogToPixY = Abs( (LONG_MAX - nDenomHalfY) / nProductY ); + + if ( !nDenomY ) + rThresRes.mnThresPixToLogY = LONG_MAX; + else if ( nProductY >= 0 ) + rThresRes.mnThresPixToLogY = (long)(((sal_uLong)LONG_MAX - (sal_uLong)( nProductY/2)) / nDenomY); + else + rThresRes.mnThresPixToLogY = (long)(((sal_uLong)LONG_MAX + (sal_uLong)(-nProductY/2)) / nDenomY); + } + + rThresRes.mnThresLogToPixX /= 2; + rThresRes.mnThresLogToPixY /= 2; + rThresRes.mnThresPixToLogX /= 2; + rThresRes.mnThresPixToLogY /= 2; +} + +// ----------------------------------------------------------------------- + +static void ImplCalcMapResolution( const MapMode& rMapMode, + long nDPIX, long nDPIY, ImplMapRes& rMapRes ) +{ + switch ( rMapMode.GetMapUnit() ) + { + case MAP_RELATIVE: + break; + case MAP_100TH_MM: + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = 2540; + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = 2540; + break; + case MAP_10TH_MM: + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = 254; + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = 254; + break; + case MAP_MM: + rMapRes.mnMapScNumX = 5; // 10 + rMapRes.mnMapScDenomX = 127; // 254 + rMapRes.mnMapScNumY = 5; // 10 + rMapRes.mnMapScDenomY = 127; // 254 + break; + case MAP_CM: + rMapRes.mnMapScNumX = 50; // 100 + rMapRes.mnMapScDenomX = 127; // 254 + rMapRes.mnMapScNumY = 50; // 100 + rMapRes.mnMapScDenomY = 127; // 254 + break; + case MAP_1000TH_INCH: + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = 1000; + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = 1000; + break; + case MAP_100TH_INCH: + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = 100; + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = 100; + break; + case MAP_10TH_INCH: + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = 10; + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = 10; + break; + case MAP_INCH: + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = 1; + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = 1; + break; + case MAP_POINT: + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = 72; + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = 72; + break; + case MAP_TWIP: + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = 1440; + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = 1440; + break; + case MAP_PIXEL: + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = nDPIX; + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = nDPIY; + break; + case MAP_SYSFONT: + case MAP_APPFONT: + case MAP_REALAPPFONT: + { + ImplSVData* pSVData = ImplGetSVData(); + if ( !pSVData->maGDIData.mnAppFontX ) + { + if( pSVData->maWinData.mpFirstFrame ) + Window::ImplInitAppFontData( pSVData->maWinData.mpFirstFrame ); + else + { + WorkWindow* pWin = new WorkWindow( NULL, 0 ); + Window::ImplInitAppFontData( pWin ); + delete pWin; + } + } + if ( rMapMode.GetMapUnit() == MAP_REALAPPFONT ) + rMapRes.mnMapScNumX = pSVData->maGDIData.mnRealAppFontX; + else + rMapRes.mnMapScNumX = pSVData->maGDIData.mnAppFontX; + rMapRes.mnMapScDenomX = nDPIX * 40; + rMapRes.mnMapScNumY = pSVData->maGDIData.mnAppFontY;; + rMapRes.mnMapScDenomY = nDPIY * 80; + } + break; + default: + OSL_FAIL( "unhandled MapUnit" ); + break; + } + + Fraction aScaleX = rMapMode.GetScaleX(); + Fraction aScaleY = rMapMode.GetScaleY(); + + // Offset laut MapMode setzen + Point aOrigin = rMapMode.GetOrigin(); + if ( rMapMode.GetMapUnit() != MAP_RELATIVE ) + { + rMapRes.mnMapOfsX = aOrigin.X(); + rMapRes.mnMapOfsY = aOrigin.Y(); + } + else + { + BigInt aX( rMapRes.mnMapOfsX ); + aX *= BigInt( aScaleX.GetDenominator() ); + if ( rMapRes.mnMapOfsX >= 0 ) + { + if ( aScaleX.GetNumerator() >= 0 ) + aX += BigInt( aScaleX.GetNumerator()/2 ); + else + aX -= BigInt( (aScaleX.GetNumerator()+1)/2 ); + } + else + { + if ( aScaleX.GetNumerator() >= 0 ) + aX -= BigInt( (aScaleX.GetNumerator()-1)/2 ); + else + aX += BigInt( aScaleX.GetNumerator()/2 ); + } + aX /= BigInt( aScaleX.GetNumerator() ); + rMapRes.mnMapOfsX = (long)aX + aOrigin.X(); + BigInt aY( rMapRes.mnMapOfsY ); + aY *= BigInt( aScaleY.GetDenominator() ); + if( rMapRes.mnMapOfsY >= 0 ) + { + if ( aScaleY.GetNumerator() >= 0 ) + aY += BigInt( aScaleY.GetNumerator()/2 ); + else + aY -= BigInt( (aScaleY.GetNumerator()+1)/2 ); + } + else + { + if ( aScaleY.GetNumerator() >= 0 ) + aY -= BigInt( (aScaleY.GetNumerator()-1)/2 ); + else + aY += BigInt( aScaleY.GetNumerator()/2 ); + } + aY /= BigInt( aScaleY.GetNumerator() ); + rMapRes.mnMapOfsY = (long)aY + aOrigin.Y(); + } + + // Scaling Faktor laut MapMode einberechnen + // aTemp? = rMapRes.mnMapSc? * aScale? + Fraction aTempX = ImplMakeFraction( rMapRes.mnMapScNumX, + aScaleX.GetNumerator(), + rMapRes.mnMapScDenomX, + aScaleX.GetDenominator() ); + Fraction aTempY = ImplMakeFraction( rMapRes.mnMapScNumY, + aScaleY.GetNumerator(), + rMapRes.mnMapScDenomY, + aScaleY.GetDenominator() ); + rMapRes.mnMapScNumX = aTempX.GetNumerator(); + rMapRes.mnMapScDenomX = aTempX.GetDenominator(); + rMapRes.mnMapScNumY = aTempY.GetNumerator(); + rMapRes.mnMapScDenomY = aTempY.GetDenominator(); + + // hack: 0/n ungef"ahr 1/max + if ( !rMapRes.mnMapScNumX ) + { + rMapRes.mnMapScNumX = 1; + rMapRes.mnMapScDenomX = LONG_MAX; + } + if ( !rMapRes.mnMapScNumY ) + { + rMapRes.mnMapScNumY = 1; + rMapRes.mnMapScDenomY = LONG_MAX; + } +} + +// ----------------------------------------------------------------------- + +inline void ImplCalcMapResolution( const MapMode& rMapMode, + long nDPIX, long nDPIY, + ImplMapRes& rMapRes, + ImplThresholdRes& rThresRes ) +{ + ImplCalcMapResolution( rMapMode, nDPIX, nDPIY, rMapRes ); + ImplCalcBigIntThreshold( nDPIX, nDPIY, rMapRes, rThresRes ); +} + +// ----------------------------------------------------------------------- + +static long ImplLogicToPixel( long n, long nDPI, long nMapNum, long nMapDenom, + long nThres ) +{ + // To "use" it... + (void) nThres; +#if (SAL_TYPES_SIZEOFLONG < 8) + if( (+n < nThres) && (-n < nThres) ) + { + n *= nMapNum * nDPI; + if( nMapDenom != 1 ) + { + n = (2 * n) / nMapDenom; + if( n < 0 ) --n; else ++n; + n /= 2; + } + } + else +#endif + { + sal_Int64 n64 = n; + n64 *= nMapNum; + n64 *= nDPI; + if( nMapDenom == 1 ) + n = (long)n64; + else + { + n = (long)(2 * n64 / nMapDenom); + if( n < 0 ) --n; else ++n; + n /= 2; + } + } + return n; +} + +// ----------------------------------------------------------------------- + +static long ImplPixelToLogic( long n, long nDPI, long nMapNum, long nMapDenom, + long nThres ) +{ + // To "use" it... + (void) nThres; +#if (SAL_TYPES_SIZEOFLONG < 8) + if( (+n < nThres) && (-n < nThres) ) + n = (2 * n * nMapDenom) / (nDPI * nMapNum); + else +#endif + { + sal_Int64 n64 = n; + n64 *= nMapDenom; + long nDenom = nDPI * nMapNum; + n = (long)(2 * n64 / nDenom); + } + if( n < 0 ) --n; else ++n; + return (n / 2); +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplLogicXToDevicePixel( long nX ) const +{ + if ( !mbMap ) + return nX+mnOutOffX; + + return ImplLogicToPixel( nX + maMapRes.mnMapOfsX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX )+mnOutOffX+mnOutOffOrigX; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplLogicYToDevicePixel( long nY ) const +{ + if ( !mbMap ) + return nY+mnOutOffY; + + return ImplLogicToPixel( nY + maMapRes.mnMapOfsY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY )+mnOutOffY+mnOutOffOrigY; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplLogicWidthToDevicePixel( long nWidth ) const +{ + if ( !mbMap ) + return nWidth; + + return ImplLogicToPixel( nWidth, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX ); +} + +float OutputDevice::ImplFloatLogicWidthToDevicePixel( float fLogicWidth) const +{ + if( !mbMap) + return fLogicWidth; + // TODO: consolidate the calculation into one multiplication + float fPixelWidth = (fLogicWidth * mnDPIX * maMapRes.mnMapScNumX) / maMapRes.mnMapScDenomX; + return fPixelWidth; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplLogicHeightToDevicePixel( long nHeight ) const +{ + if ( !mbMap ) + return nHeight; + + return ImplLogicToPixel( nHeight, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY ); +} + +float OutputDevice::ImplFloatLogicHeightToDevicePixel( float fLogicHeight) const +{ + if( !mbMap) + return fLogicHeight; + float fPixelHeight = (fLogicHeight * mnDPIY * maMapRes.mnMapScNumY) / maMapRes.mnMapScDenomY; + return fPixelHeight; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplDevicePixelToLogicWidth( long nWidth ) const +{ + if ( !mbMap ) + return nWidth; + + return ImplPixelToLogic( nWidth, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX ); +} + +float OutputDevice::ImplFloatDevicePixelToLogicWidth( float fPixelWidth) const +{ + if( !mbMap) + return fPixelWidth; + float fLogicHeight = (fPixelWidth * maMapRes.mnMapScDenomX) / (mnDPIX * maMapRes.mnMapScNumX); + return fLogicHeight; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplDevicePixelToLogicHeight( long nHeight ) const +{ + if ( !mbMap ) + return nHeight; + + return ImplPixelToLogic( nHeight, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY ); +} + +float OutputDevice::ImplFloatDevicePixelToLogicHeight( float fPixelHeight) const +{ + if( !mbMap) + return fPixelHeight; + float fLogicHeight = (fPixelHeight * maMapRes.mnMapScDenomY) / (mnDPIY * maMapRes.mnMapScNumY); + return fLogicHeight; +} + + +// ----------------------------------------------------------------------- + +Point OutputDevice::ImplLogicToDevicePixel( const Point& rLogicPt ) const +{ + if ( !mbMap ) + return Point( rLogicPt.X()+mnOutOffX, rLogicPt.Y()+mnOutOffY ); + + return Point( ImplLogicToPixel( rLogicPt.X() + maMapRes.mnMapOfsX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX )+mnOutOffX+mnOutOffOrigX, + ImplLogicToPixel( rLogicPt.Y() + maMapRes.mnMapOfsY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY )+mnOutOffY+mnOutOffOrigY ); +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::ImplLogicToDevicePixel( const Size& rLogicSize ) const +{ + if ( !mbMap ) + return rLogicSize; + + return Size( ImplLogicToPixel( rLogicSize.Width(), mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX ), + ImplLogicToPixel( rLogicSize.Height(), mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY ) ); +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::ImplLogicToDevicePixel( const Rectangle& rLogicRect ) const +{ + if ( rLogicRect.IsEmpty() ) + return rLogicRect; + + if ( !mbMap ) + { + return Rectangle( rLogicRect.Left()+mnOutOffX, rLogicRect.Top()+mnOutOffY, + rLogicRect.Right()+mnOutOffX, rLogicRect.Bottom()+mnOutOffY ); + } + + return Rectangle( ImplLogicToPixel( rLogicRect.Left()+maMapRes.mnMapOfsX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX )+mnOutOffX+mnOutOffOrigX, + ImplLogicToPixel( rLogicRect.Top()+maMapRes.mnMapOfsY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY )+mnOutOffY+mnOutOffOrigY, + ImplLogicToPixel( rLogicRect.Right()+maMapRes.mnMapOfsX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX )+mnOutOffX+mnOutOffOrigX, + ImplLogicToPixel( rLogicRect.Bottom()+maMapRes.mnMapOfsY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY )+mnOutOffY+mnOutOffOrigY ); +} + +// ----------------------------------------------------------------------- + +Polygon OutputDevice::ImplLogicToDevicePixel( const Polygon& rLogicPoly ) const +{ + if ( !mbMap && !mnOutOffX && !mnOutOffY ) + return rLogicPoly; + + sal_uInt16 i; + sal_uInt16 nPoints = rLogicPoly.GetSize(); + Polygon aPoly( rLogicPoly ); + + // Pointer auf das Point-Array holen (Daten werden kopiert) + const Point* pPointAry = aPoly.GetConstPointAry(); + + if ( mbMap ) + { + for ( i = 0; i < nPoints; i++ ) + { + const Point* pPt = &(pPointAry[i]); + Point aPt; + aPt.X() = ImplLogicToPixel( pPt->X()+maMapRes.mnMapOfsX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX )+mnOutOffX+mnOutOffOrigX; + aPt.Y() = ImplLogicToPixel( pPt->Y()+maMapRes.mnMapOfsY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY )+mnOutOffY+mnOutOffOrigY; + aPoly[i] = aPt; + } + } + else + { + for ( i = 0; i < nPoints; i++ ) + { + Point aPt = pPointAry[i]; + aPt.X() += mnOutOffX; + aPt.Y() += mnOutOffY; + aPoly[i] = aPt; + } + } + + return aPoly; +} + +// ----------------------------------------------------------------------- + +PolyPolygon OutputDevice::ImplLogicToDevicePixel( const PolyPolygon& rLogicPolyPoly ) const +{ + if ( !mbMap && !mnOutOffX && !mnOutOffY ) + return rLogicPolyPoly; + + PolyPolygon aPolyPoly( rLogicPolyPoly ); + sal_uInt16 nPoly = aPolyPoly.Count(); + for( sal_uInt16 i = 0; i < nPoly; i++ ) + { + Polygon& rPoly = aPolyPoly[i]; + rPoly = ImplLogicToDevicePixel( rPoly ); + } + return aPolyPoly; +} + +// ----------------------------------------------------------------------- + +LineInfo OutputDevice::ImplLogicToDevicePixel( const LineInfo& rLineInfo ) const +{ + LineInfo aInfo( rLineInfo ); + + if( aInfo.GetStyle() == LINE_DASH ) + { + if( aInfo.GetDotCount() && aInfo.GetDotLen() ) + aInfo.SetDotLen( Max( ImplLogicWidthToDevicePixel( aInfo.GetDotLen() ), 1L ) ); + else + aInfo.SetDotCount( 0 ); + + if( aInfo.GetDashCount() && aInfo.GetDashLen() ) + aInfo.SetDashLen( Max( ImplLogicWidthToDevicePixel( aInfo.GetDashLen() ), 1L ) ); + else + aInfo.SetDashCount( 0 ); + + aInfo.SetDistance( ImplLogicWidthToDevicePixel( aInfo.GetDistance() ) ); + + if( ( !aInfo.GetDashCount() && !aInfo.GetDotCount() ) || !aInfo.GetDistance() ) + aInfo.SetStyle( LINE_SOLID ); + } + + aInfo.SetWidth( ImplLogicWidthToDevicePixel( aInfo.GetWidth() ) ); + + return aInfo; +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::ImplDevicePixelToLogic( const Rectangle& rPixelRect ) const +{ + if ( rPixelRect.IsEmpty() ) + return rPixelRect; + + if ( !mbMap ) + { + return Rectangle( rPixelRect.Left()-mnOutOffX, rPixelRect.Top()-mnOutOffY, + rPixelRect.Right()-mnOutOffX, rPixelRect.Bottom()-mnOutOffY ); + } + + return Rectangle( ImplPixelToLogic( rPixelRect.Left()-mnOutOffX-mnOutOffOrigX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX )-maMapRes.mnMapOfsX, + ImplPixelToLogic( rPixelRect.Top()-mnOutOffY-mnOutOffOrigY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY )-maMapRes.mnMapOfsY, + ImplPixelToLogic( rPixelRect.Right()-mnOutOffX-mnOutOffOrigX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX )-maMapRes.mnMapOfsX, + ImplPixelToLogic( rPixelRect.Bottom()-mnOutOffY-mnOutOffOrigY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY )-maMapRes.mnMapOfsY ); +} + +// ----------------------------------------------------------------------- + +Region OutputDevice::ImplPixelToDevicePixel( const Region& rRegion ) const +{ + DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); + + if ( !mnOutOffX && !mnOutOffY ) + return rRegion; + + Region aRegion( rRegion ); + aRegion.Move( mnOutOffX+mnOutOffOrigX, mnOutOffY+mnOutOffOrigY ); + return aRegion; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::EnableMapMode( sal_Bool bEnable ) +{ + mbMap = (bEnable != 0); + + if( mpAlphaVDev ) + mpAlphaVDev->EnableMapMode( bEnable ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetMapMode() +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaMapModeAction( MapMode() ) ); + + if ( mbMap || !maMapMode.IsDefault() ) + { + mbMap = sal_False; + maMapMode = MapMode(); + + // create new objects (clip region werden nicht neu skaliert) + mbNewFont = sal_True; + mbInitFont = sal_True; + if ( GetOutDevType() == OUTDEV_WINDOW ) + { + if ( ((Window*)this)->mpWindowImpl->mpCursor ) + ((Window*)this)->mpWindowImpl->mpCursor->ImplNew(); + } + + // #106426# Adapt logical offset when changing mapmode + mnOutOffLogicX = mnOutOffOrigX; // no mapping -> equal offsets + mnOutOffLogicY = mnOutOffOrigY; + + // #i75163# + ImplInvalidateViewTransform(); + } + + if( mpAlphaVDev ) + mpAlphaVDev->SetMapMode(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetMapMode( const MapMode& rNewMapMode ) +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + sal_Bool bRelMap = (rNewMapMode.GetMapUnit() == MAP_RELATIVE); + + if ( mpMetaFile ) + { + mpMetaFile->AddAction( new MetaMapModeAction( rNewMapMode ) ); +#ifdef DBG_UTIL + if ( GetOutDevType() != OUTDEV_PRINTER ) + DBG_ASSERTWARNING( bRelMap, "Please record only relative MapModes!" ); +#endif + } + + // Ist der MapMode der gleiche wie vorher, dann mache nichts + if ( maMapMode == rNewMapMode ) + return; + + if( mpAlphaVDev ) + mpAlphaVDev->SetMapMode( rNewMapMode ); + + // Ist Default-MapMode, dann bereche nichts + sal_Bool bOldMap = mbMap; + mbMap = !rNewMapMode.IsDefault(); + if ( mbMap ) + { + // Falls nur der Orign umgesetzt wird, dann scaliere nichts neu + if ( (rNewMapMode.GetMapUnit() == maMapMode.GetMapUnit()) && + (rNewMapMode.GetScaleX() == maMapMode.GetScaleX()) && + (rNewMapMode.GetScaleY() == maMapMode.GetScaleY()) && + (bOldMap == mbMap) ) + { + // Offset setzen + Point aOrigin = rNewMapMode.GetOrigin(); + maMapRes.mnMapOfsX = aOrigin.X(); + maMapRes.mnMapOfsY = aOrigin.Y(); + maMapMode = rNewMapMode; + + // #i75163# + ImplInvalidateViewTransform(); + + return; + } + if ( !bOldMap && bRelMap ) + { + maMapRes.mnMapScNumX = 1; + maMapRes.mnMapScNumY = 1; + maMapRes.mnMapScDenomX = mnDPIX; + maMapRes.mnMapScDenomY = mnDPIY; + maMapRes.mnMapOfsX = 0; + maMapRes.mnMapOfsY = 0; + } + + // Neue MapMode-Aufloesung berechnen + ImplCalcMapResolution( rNewMapMode, mnDPIX, mnDPIY, maMapRes, maThresRes ); + } + + // Neuen MapMode setzen + if ( bRelMap ) + { + Point aOrigin( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY ); + // aScale? = maMapMode.GetScale?() * rNewMapMode.GetScale?() + Fraction aScaleX = ImplMakeFraction( maMapMode.GetScaleX().GetNumerator(), + rNewMapMode.GetScaleX().GetNumerator(), + maMapMode.GetScaleX().GetDenominator(), + rNewMapMode.GetScaleX().GetDenominator() ); + Fraction aScaleY = ImplMakeFraction( maMapMode.GetScaleY().GetNumerator(), + rNewMapMode.GetScaleY().GetNumerator(), + maMapMode.GetScaleY().GetDenominator(), + rNewMapMode.GetScaleY().GetDenominator() ); + maMapMode.SetOrigin( aOrigin ); + maMapMode.SetScaleX( aScaleX ); + maMapMode.SetScaleY( aScaleY ); + } + else + maMapMode = rNewMapMode; + + // create new objects (clip region werden nicht neu skaliert) + mbNewFont = sal_True; + mbInitFont = sal_True; + if ( GetOutDevType() == OUTDEV_WINDOW ) + { + if ( ((Window*)this)->mpWindowImpl->mpCursor ) + ((Window*)this)->mpWindowImpl->mpCursor->ImplNew(); + } + + // #106426# Adapt logical offset when changing mapmode + mnOutOffLogicX = ImplPixelToLogic( mnOutOffOrigX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX ); + mnOutOffLogicY = ImplPixelToLogic( mnOutOffOrigY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY ); + + // #i75163# + ImplInvalidateViewTransform(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetRelativeMapMode( const MapMode& rNewMapMode ) +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // Ist der MapMode der gleiche wie vorher, dann mache nichts + if ( maMapMode == rNewMapMode ) + return; + + MapUnit eOld = maMapMode.GetMapUnit(); + MapUnit eNew = rNewMapMode.GetMapUnit(); + + // a?F = rNewMapMode.GetScale?() / maMapMode.GetScale?() + Fraction aXF = ImplMakeFraction( rNewMapMode.GetScaleX().GetNumerator(), + maMapMode.GetScaleX().GetDenominator(), + rNewMapMode.GetScaleX().GetDenominator(), + maMapMode.GetScaleX().GetNumerator() ); + Fraction aYF = ImplMakeFraction( rNewMapMode.GetScaleY().GetNumerator(), + maMapMode.GetScaleY().GetDenominator(), + rNewMapMode.GetScaleY().GetDenominator(), + maMapMode.GetScaleY().GetNumerator() ); + + Point aPt( LogicToLogic( Point(), NULL, &rNewMapMode ) ); + if ( eNew != eOld ) + { + if ( eOld > MAP_PIXEL ) + { + DBG_ERRORFILE( "Not implemented MapUnit" ); + } + else if ( eNew > MAP_PIXEL ) + { + DBG_ERRORFILE( "Not implemented MapUnit" ); + } + else + { + Fraction aF( aImplNumeratorAry[eNew] * aImplDenominatorAry[eOld], + aImplNumeratorAry[eOld] * aImplDenominatorAry[eNew] ); + + // a?F = a?F * aF + aXF = ImplMakeFraction( aXF.GetNumerator(), aF.GetNumerator(), + aXF.GetDenominator(), aF.GetDenominator() ); + aYF = ImplMakeFraction( aYF.GetNumerator(), aF.GetNumerator(), + aYF.GetDenominator(), aF.GetDenominator() ); + if ( eOld == MAP_PIXEL ) + { + aXF *= Fraction( mnDPIX, 1 ); + aYF *= Fraction( mnDPIY, 1 ); + } + else if ( eNew == MAP_PIXEL ) + { + aXF *= Fraction( 1, mnDPIX ); + aYF *= Fraction( 1, mnDPIY ); + } + } + } + + MapMode aNewMapMode( MAP_RELATIVE, Point( -aPt.X(), -aPt.Y() ), aXF, aYF ); + SetMapMode( aNewMapMode ); + + if ( eNew != eOld ) + maMapMode = rNewMapMode; + + // #106426# Adapt logical offset when changing mapmode + mnOutOffLogicX = ImplPixelToLogic( mnOutOffOrigX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX ); + mnOutOffLogicY = ImplPixelToLogic( mnOutOffOrigY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY ); + + if( mpAlphaVDev ) + mpAlphaVDev->SetRelativeMapMode( rNewMapMode ); +} + +// ----------------------------------------------------------------------- + +// #i75163# +basegfx::B2DHomMatrix OutputDevice::GetViewTransformation() const +{ + if(mbMap) + { + // #i82615# + if(!mpOutDevData) + { + const_cast< OutputDevice* >(this)->ImplInitOutDevData(); + } + + if(!mpOutDevData->mpViewTransform) + { + mpOutDevData->mpViewTransform = new basegfx::B2DHomMatrix; + + const double fScaleFactorX((double)mnDPIX * (double)maMapRes.mnMapScNumX / (double)maMapRes.mnMapScDenomX); + const double fScaleFactorY((double)mnDPIY * (double)maMapRes.mnMapScNumY / (double)maMapRes.mnMapScDenomY); + const double fZeroPointX(((double)maMapRes.mnMapOfsX * fScaleFactorX) + (double)mnOutOffOrigX); + const double fZeroPointY(((double)maMapRes.mnMapOfsY * fScaleFactorY) + (double)mnOutOffOrigY); + + mpOutDevData->mpViewTransform->set(0, 0, fScaleFactorX); + mpOutDevData->mpViewTransform->set(1, 1, fScaleFactorY); + mpOutDevData->mpViewTransform->set(0, 2, fZeroPointX); + mpOutDevData->mpViewTransform->set(1, 2, fZeroPointY); + } + + return *mpOutDevData->mpViewTransform; + } + else + { + return basegfx::B2DHomMatrix(); + } +} + +// ----------------------------------------------------------------------- + +// #i75163# +basegfx::B2DHomMatrix OutputDevice::GetInverseViewTransformation() const +{ + if(mbMap) + { + // #i82615# + if(!mpOutDevData) + { + const_cast< OutputDevice* >(this)->ImplInitOutDevData(); + } + + if(!mpOutDevData->mpInverseViewTransform) + { + GetViewTransformation(); + mpOutDevData->mpInverseViewTransform = new basegfx::B2DHomMatrix(*mpOutDevData->mpViewTransform); + mpOutDevData->mpInverseViewTransform->invert(); + } + + return *mpOutDevData->mpInverseViewTransform; + } + else + { + return basegfx::B2DHomMatrix(); + } +} + +// ----------------------------------------------------------------------- + +// #i75163# +basegfx::B2DHomMatrix OutputDevice::GetViewTransformation( const MapMode& rMapMode ) const +{ + // #i82615# + ImplMapRes aMapRes; + ImplThresholdRes aThresRes; + ImplCalcMapResolution( rMapMode, mnDPIX, mnDPIY, aMapRes, aThresRes ); + + basegfx::B2DHomMatrix aTransform; + + const double fScaleFactorX((double)mnDPIX * (double)aMapRes.mnMapScNumX / (double)aMapRes.mnMapScDenomX); + const double fScaleFactorY((double)mnDPIY * (double)aMapRes.mnMapScNumY / (double)aMapRes.mnMapScDenomY); + const double fZeroPointX(((double)aMapRes.mnMapOfsX * fScaleFactorX) + (double)mnOutOffOrigX); + const double fZeroPointY(((double)aMapRes.mnMapOfsY * fScaleFactorY) + (double)mnOutOffOrigY); + + aTransform.set(0, 0, fScaleFactorX); + aTransform.set(1, 1, fScaleFactorY); + aTransform.set(0, 2, fZeroPointX); + aTransform.set(1, 2, fZeroPointY); + + return aTransform; +} + +// ----------------------------------------------------------------------- + +// #i75163# +basegfx::B2DHomMatrix OutputDevice::GetInverseViewTransformation( const MapMode& rMapMode ) const +{ + basegfx::B2DHomMatrix aMatrix( GetViewTransformation( rMapMode ) ); + aMatrix.invert(); + return aMatrix; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DHomMatrix OutputDevice::ImplGetDeviceTransformation() const +{ + basegfx::B2DHomMatrix aTransformation = GetViewTransformation(); + // TODO: is it worth to cache the transformed result? + if( mnOutOffX || mnOutOffY ) + aTransformation.translate( mnOutOffX, mnOutOffY ); + return aTransformation; +} + +// ----------------------------------------------------------------------- + +Point OutputDevice::LogicToPixel( const Point& rLogicPt ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !mbMap ) + return rLogicPt; + + return Point( ImplLogicToPixel( rLogicPt.X() + maMapRes.mnMapOfsX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX )+mnOutOffOrigX, + ImplLogicToPixel( rLogicPt.Y() + maMapRes.mnMapOfsY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY )+mnOutOffOrigY ); +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::LogicToPixel( const Size& rLogicSize ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !mbMap ) + return rLogicSize; + + return Size( ImplLogicToPixel( rLogicSize.Width(), mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX ), + ImplLogicToPixel( rLogicSize.Height(), mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY ) ); +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::LogicToPixel( const Rectangle& rLogicRect ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !mbMap || rLogicRect.IsEmpty() ) + return rLogicRect; + + return Rectangle( ImplLogicToPixel( rLogicRect.Left() + maMapRes.mnMapOfsX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX )+mnOutOffOrigX, + ImplLogicToPixel( rLogicRect.Top() + maMapRes.mnMapOfsY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY )+mnOutOffOrigY, + ImplLogicToPixel( rLogicRect.Right() + maMapRes.mnMapOfsX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX )+mnOutOffOrigX, + ImplLogicToPixel( rLogicRect.Bottom() + maMapRes.mnMapOfsY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY )+mnOutOffOrigY ); +} + +// ----------------------------------------------------------------------- + +Polygon OutputDevice::LogicToPixel( const Polygon& rLogicPoly ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rLogicPoly, Polygon, NULL ); + + if ( !mbMap ) + return rLogicPoly; + + sal_uInt16 i; + sal_uInt16 nPoints = rLogicPoly.GetSize(); + Polygon aPoly( rLogicPoly ); + + // Pointer auf das Point-Array holen (Daten werden kopiert) + const Point* pPointAry = aPoly.GetConstPointAry(); + + for ( i = 0; i < nPoints; i++ ) + { + const Point* pPt = &(pPointAry[i]); + Point aPt; + aPt.X() = ImplLogicToPixel( pPt->X() + maMapRes.mnMapOfsX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresLogToPixX )+mnOutOffOrigX; + aPt.Y() = ImplLogicToPixel( pPt->Y() + maMapRes.mnMapOfsY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresLogToPixY )+mnOutOffOrigY; + aPoly[i] = aPt; + } + + return aPoly; +} + +// ----------------------------------------------------------------------- + +PolyPolygon OutputDevice::LogicToPixel( const PolyPolygon& rLogicPolyPoly ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rLogicPolyPoly, PolyPolygon, NULL ); + + if ( !mbMap ) + return rLogicPolyPoly; + + PolyPolygon aPolyPoly( rLogicPolyPoly ); + sal_uInt16 nPoly = aPolyPoly.Count(); + for( sal_uInt16 i = 0; i < nPoly; i++ ) + { + Polygon& rPoly = aPolyPoly[i]; + rPoly = LogicToPixel( rPoly ); + } + return aPolyPoly; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolygon OutputDevice::LogicToPixel( const basegfx::B2DPolygon& rLogicPoly ) const +{ + basegfx::B2DPolygon aTransformedPoly = rLogicPoly; + const ::basegfx::B2DHomMatrix& rTransformationMatrix = GetViewTransformation(); + aTransformedPoly.transform( rTransformationMatrix ); + return aTransformedPoly; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolyPolygon OutputDevice::LogicToPixel( const basegfx::B2DPolyPolygon& rLogicPolyPoly ) const +{ + basegfx::B2DPolyPolygon aTransformedPoly = rLogicPolyPoly; + const ::basegfx::B2DHomMatrix& rTransformationMatrix = GetViewTransformation(); + aTransformedPoly.transform( rTransformationMatrix ); + return aTransformedPoly; +} + +// ----------------------------------------------------------------------- + +Region OutputDevice::LogicToPixel( const Region& rLogicRegion ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rLogicRegion, Region, ImplDbgTestRegion ); + + RegionType eType = rLogicRegion.GetType(); + + if ( !mbMap || (eType == REGION_EMPTY) || (eType == REGION_NULL) ) + return rLogicRegion; + + Region aRegion; + const ImplRegion& rImplRegion = *rLogicRegion.ImplGetImplRegion(); + const PolyPolygon* pPolyPoly = rImplRegion.mpPolyPoly; + const basegfx::B2DPolyPolygon* pB2DPolyPoly = rImplRegion.mpB2DPolyPoly; + + if ( pPolyPoly ) + aRegion = Region( LogicToPixel( *pPolyPoly ) ); + else if( pB2DPolyPoly ) + { + basegfx::B2DPolyPolygon aTransformedPoly = *pB2DPolyPoly; + const ::basegfx::B2DHomMatrix& rTransformationMatrix = GetViewTransformation(); + aTransformedPoly.transform( rTransformationMatrix ); + aRegion = Region( aTransformedPoly ); + } + else + { + long nX; + long nY; + long nWidth; + long nHeight; + ImplRegionInfo aInfo; + sal_Bool bRegionRect; + + aRegion.ImplBeginAddRect(); + bRegionRect = rLogicRegion.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight ); + while ( bRegionRect ) + { + Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + aRegion.ImplAddRect( LogicToPixel( aRect ) ); + bRegionRect = rLogicRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight ); + } + aRegion.ImplEndAddRect(); + } + + return aRegion; +} + +// ----------------------------------------------------------------------- + +Point OutputDevice::LogicToPixel( const Point& rLogicPt, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( rMapMode.IsDefault() ) + return rLogicPt; + + // MapMode-Aufloesung berechnen und Umrechnen + ImplMapRes aMapRes; + ImplThresholdRes aThresRes; + ImplCalcMapResolution( rMapMode, mnDPIX, mnDPIY, aMapRes, aThresRes ); + + return Point( ImplLogicToPixel( rLogicPt.X() + aMapRes.mnMapOfsX, mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresLogToPixX )+mnOutOffOrigX, + ImplLogicToPixel( rLogicPt.Y() + aMapRes.mnMapOfsY, mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresLogToPixY )+mnOutOffOrigY ); +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::LogicToPixel( const Size& rLogicSize, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( rMapMode.IsDefault() ) + return rLogicSize; + + // MapMode-Aufloesung berechnen und Umrechnen + ImplMapRes aMapRes; + ImplThresholdRes aThresRes; + ImplCalcMapResolution( rMapMode, mnDPIX, mnDPIY, aMapRes, aThresRes ); + + return Size( ImplLogicToPixel( rLogicSize.Width(), mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresLogToPixX ), + ImplLogicToPixel( rLogicSize.Height(), mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresLogToPixY ) ); +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::LogicToPixel( const Rectangle& rLogicRect, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( rMapMode.IsDefault() || rLogicRect.IsEmpty() ) + return rLogicRect; + + // MapMode-Aufloesung berechnen und Umrechnen + ImplMapRes aMapRes; + ImplThresholdRes aThresRes; + ImplCalcMapResolution( rMapMode, mnDPIX, mnDPIY, aMapRes, aThresRes ); + + return Rectangle( ImplLogicToPixel( rLogicRect.Left() + aMapRes.mnMapOfsX, mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresLogToPixX )+mnOutOffOrigX, + ImplLogicToPixel( rLogicRect.Top() + aMapRes.mnMapOfsY, mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresLogToPixY )+mnOutOffOrigY, + ImplLogicToPixel( rLogicRect.Right() + aMapRes.mnMapOfsX, mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresLogToPixX )+mnOutOffOrigX, + ImplLogicToPixel( rLogicRect.Bottom() + aMapRes.mnMapOfsY, mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresLogToPixY )+mnOutOffOrigY ); +} + +// ----------------------------------------------------------------------- + +Polygon OutputDevice::LogicToPixel( const Polygon& rLogicPoly, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rLogicPoly, Polygon, NULL ); + + if ( rMapMode.IsDefault() ) + return rLogicPoly; + + // MapMode-Aufloesung berechnen und Umrechnen + ImplMapRes aMapRes; + ImplThresholdRes aThresRes; + ImplCalcMapResolution( rMapMode, mnDPIX, mnDPIY, aMapRes, aThresRes ); + + sal_uInt16 i; + sal_uInt16 nPoints = rLogicPoly.GetSize(); + Polygon aPoly( rLogicPoly ); + + // Pointer auf das Point-Array holen (Daten werden kopiert) + const Point* pPointAry = aPoly.GetConstPointAry(); + + for ( i = 0; i < nPoints; i++ ) + { + const Point* pPt = &(pPointAry[i]); + Point aPt; + aPt.X() = ImplLogicToPixel( pPt->X() + aMapRes.mnMapOfsX, mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresLogToPixX )+mnOutOffOrigX; + aPt.Y() = ImplLogicToPixel( pPt->Y() + aMapRes.mnMapOfsY, mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresLogToPixY )+mnOutOffOrigY; + aPoly[i] = aPt; + } + + return aPoly; +} + +// ----------------------------------------------------------------------- + +PolyPolygon OutputDevice::LogicToPixel( const PolyPolygon& rLogicPolyPoly, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rLogicPolyPoly, PolyPolygon, NULL ); + + if ( rMapMode.IsDefault() ) + return rLogicPolyPoly; + + PolyPolygon aPolyPoly( rLogicPolyPoly ); + sal_uInt16 nPoly = aPolyPoly.Count(); + for( sal_uInt16 i = 0; i < nPoly; i++ ) + { + Polygon& rPoly = aPolyPoly[i]; + rPoly = LogicToPixel( rPoly, rMapMode ); + } + return aPolyPoly; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolyPolygon OutputDevice::LogicToPixel( const basegfx::B2DPolyPolygon& rLogicPolyPoly, + const MapMode& rMapMode ) const +{ + basegfx::B2DPolyPolygon aTransformedPoly = rLogicPolyPoly; + const ::basegfx::B2DHomMatrix& rTransformationMatrix = GetViewTransformation( rMapMode ); + aTransformedPoly.transform( rTransformationMatrix ); + return aTransformedPoly; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolygon OutputDevice::LogicToPixel( const basegfx::B2DPolygon& rLogicPoly, + const MapMode& rMapMode ) const +{ + basegfx::B2DPolygon aTransformedPoly = rLogicPoly; + const ::basegfx::B2DHomMatrix& rTransformationMatrix = GetViewTransformation( rMapMode ); + aTransformedPoly.transform( rTransformationMatrix ); + return aTransformedPoly; +} + +// ----------------------------------------------------------------------- + +Region OutputDevice::LogicToPixel( const Region& rLogicRegion, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rLogicRegion, Region, ImplDbgTestRegion ); + + RegionType eType = rLogicRegion.GetType(); + + if ( rMapMode.IsDefault() || (eType == REGION_EMPTY) || (eType == REGION_NULL) ) + return rLogicRegion; + + Region aRegion; + PolyPolygon* pPolyPoly = rLogicRegion.ImplGetImplRegion()->mpPolyPoly; + + if( pPolyPoly ) + aRegion = Region( LogicToPixel( *pPolyPoly, rMapMode ) ); + else + { + long nX; + long nY; + long nWidth; + long nHeight; + ImplRegionInfo aInfo; + sal_Bool bRegionRect; + + aRegion.ImplBeginAddRect(); + bRegionRect = rLogicRegion.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight ); + while ( bRegionRect ) + { + Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + aRegion.ImplAddRect( LogicToPixel( aRect, rMapMode ) ); + bRegionRect = rLogicRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight ); + } + aRegion.ImplEndAddRect(); + } + + return aRegion; +} + +// ----------------------------------------------------------------------- + +Point OutputDevice::PixelToLogic( const Point& rDevicePt ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !mbMap ) + return rDevicePt; + + return Point( ImplPixelToLogic( rDevicePt.X(), mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX ) - maMapRes.mnMapOfsX - mnOutOffLogicX, + ImplPixelToLogic( rDevicePt.Y(), mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY ) - maMapRes.mnMapOfsY - mnOutOffLogicY ); +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::PixelToLogic( const Size& rDeviceSize ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !mbMap ) + return rDeviceSize; + + return Size( ImplPixelToLogic( rDeviceSize.Width(), mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX ), + ImplPixelToLogic( rDeviceSize.Height(), mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY ) ); +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::PixelToLogic( const Rectangle& rDeviceRect ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !mbMap || rDeviceRect.IsEmpty() ) + return rDeviceRect; + + return Rectangle( ImplPixelToLogic( rDeviceRect.Left(), mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX ) - maMapRes.mnMapOfsX - mnOutOffLogicX, + ImplPixelToLogic( rDeviceRect.Top(), mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY ) - maMapRes.mnMapOfsY - mnOutOffLogicY, + ImplPixelToLogic( rDeviceRect.Right(), mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX ) - maMapRes.mnMapOfsX - mnOutOffLogicX, + ImplPixelToLogic( rDeviceRect.Bottom(), mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY ) - maMapRes.mnMapOfsY - mnOutOffLogicY ); +} + +// ----------------------------------------------------------------------- + +Polygon OutputDevice::PixelToLogic( const Polygon& rDevicePoly ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rDevicePoly, Polygon, NULL ); + + if ( !mbMap ) + return rDevicePoly; + + sal_uInt16 i; + sal_uInt16 nPoints = rDevicePoly.GetSize(); + Polygon aPoly( rDevicePoly ); + + // Pointer auf das Point-Array holen (Daten werden kopiert) + const Point* pPointAry = aPoly.GetConstPointAry(); + + for ( i = 0; i < nPoints; i++ ) + { + const Point* pPt = &(pPointAry[i]); + Point aPt; + aPt.X() = ImplPixelToLogic( pPt->X(), mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX ) - maMapRes.mnMapOfsX - mnOutOffLogicX; + aPt.Y() = ImplPixelToLogic( pPt->Y(), mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY ) - maMapRes.mnMapOfsY - mnOutOffLogicY; + aPoly[i] = aPt; + } + + return aPoly; +} + +// ----------------------------------------------------------------------- + +PolyPolygon OutputDevice::PixelToLogic( const PolyPolygon& rDevicePolyPoly ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rDevicePolyPoly, PolyPolygon, NULL ); + + if ( !mbMap ) + return rDevicePolyPoly; + + PolyPolygon aPolyPoly( rDevicePolyPoly ); + sal_uInt16 nPoly = aPolyPoly.Count(); + for( sal_uInt16 i = 0; i < nPoly; i++ ) + { + Polygon& rPoly = aPolyPoly[i]; + rPoly = PixelToLogic( rPoly ); + } + return aPolyPoly; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolygon OutputDevice::PixelToLogic( const basegfx::B2DPolygon& rPixelPoly ) const +{ + basegfx::B2DPolygon aTransformedPoly = rPixelPoly; + const ::basegfx::B2DHomMatrix& rTransformationMatrix = GetInverseViewTransformation(); + aTransformedPoly.transform( rTransformationMatrix ); + return aTransformedPoly; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolyPolygon OutputDevice::PixelToLogic( const basegfx::B2DPolyPolygon& rPixelPolyPoly ) const +{ + basegfx::B2DPolyPolygon aTransformedPoly = rPixelPolyPoly; + const ::basegfx::B2DHomMatrix& rTransformationMatrix = GetInverseViewTransformation(); + aTransformedPoly.transform( rTransformationMatrix ); + return aTransformedPoly; +} + +// ----------------------------------------------------------------------- + +Region OutputDevice::PixelToLogic( const Region& rDeviceRegion ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rDeviceRegion, Region, ImplDbgTestRegion ); + + RegionType eType = rDeviceRegion.GetType(); + + if ( !mbMap || (eType == REGION_EMPTY) || (eType == REGION_NULL) ) + return rDeviceRegion; + + Region aRegion; + PolyPolygon* pPolyPoly = rDeviceRegion.ImplGetImplRegion()->mpPolyPoly; + + if ( pPolyPoly ) + aRegion = Region( PixelToLogic( *pPolyPoly ) ); + else + { + long nX; + long nY; + long nWidth; + long nHeight; + ImplRegionInfo aInfo; + sal_Bool bRegionRect; + + aRegion.ImplBeginAddRect(); + bRegionRect = rDeviceRegion.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight ); + while ( bRegionRect ) + { + Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + aRegion.ImplAddRect( PixelToLogic( aRect ) ); + bRegionRect = rDeviceRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight ); + } + aRegion.ImplEndAddRect(); + } + + return aRegion; +} + +// ----------------------------------------------------------------------- + +Point OutputDevice::PixelToLogic( const Point& rDevicePt, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // Ist Default-MapMode, dann bereche nichts + if ( rMapMode.IsDefault() ) + return rDevicePt; + + // MapMode-Aufloesung berechnen und Umrechnen + ImplMapRes aMapRes; + ImplThresholdRes aThresRes; + ImplCalcMapResolution( rMapMode, mnDPIX, mnDPIY, aMapRes, aThresRes ); + + return Point( ImplPixelToLogic( rDevicePt.X(), mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresPixToLogX ) - aMapRes.mnMapOfsX - mnOutOffLogicX, + ImplPixelToLogic( rDevicePt.Y(), mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresPixToLogY ) - aMapRes.mnMapOfsY - mnOutOffLogicY ); +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::PixelToLogic( const Size& rDeviceSize, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // Ist Default-MapMode, dann bereche nichts + if ( rMapMode.IsDefault() ) + return rDeviceSize; + + // MapMode-Aufloesung berechnen und Umrechnen + ImplMapRes aMapRes; + ImplThresholdRes aThresRes; + ImplCalcMapResolution( rMapMode, mnDPIX, mnDPIY, aMapRes, aThresRes ); + + return Size( ImplPixelToLogic( rDeviceSize.Width(), mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresPixToLogX ), + ImplPixelToLogic( rDeviceSize.Height(), mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresPixToLogY ) ); +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::PixelToLogic( const Rectangle& rDeviceRect, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // Ist Default-MapMode, dann bereche nichts + if ( rMapMode.IsDefault() || rDeviceRect.IsEmpty() ) + return rDeviceRect; + + // MapMode-Aufloesung berechnen und Umrechnen + ImplMapRes aMapRes; + ImplThresholdRes aThresRes; + ImplCalcMapResolution( rMapMode, mnDPIX, mnDPIY, aMapRes, aThresRes ); + + return Rectangle( ImplPixelToLogic( rDeviceRect.Left(), mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresPixToLogX ) - aMapRes.mnMapOfsX - mnOutOffLogicX, + ImplPixelToLogic( rDeviceRect.Top(), mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresPixToLogY ) - aMapRes.mnMapOfsY - mnOutOffLogicY, + ImplPixelToLogic( rDeviceRect.Right(), mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresPixToLogX ) - aMapRes.mnMapOfsX - mnOutOffLogicX, + ImplPixelToLogic( rDeviceRect.Bottom(), mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresPixToLogY ) - aMapRes.mnMapOfsY - mnOutOffLogicY ); +} + +// ----------------------------------------------------------------------- + +Polygon OutputDevice::PixelToLogic( const Polygon& rDevicePoly, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rDevicePoly, Polygon, NULL ); + + // Ist Default-MapMode, dann bereche nichts + if ( rMapMode.IsDefault() ) + return rDevicePoly; + + // MapMode-Aufloesung berechnen und Umrechnen + ImplMapRes aMapRes; + ImplThresholdRes aThresRes; + ImplCalcMapResolution( rMapMode, mnDPIX, mnDPIY, aMapRes, aThresRes ); + + sal_uInt16 i; + sal_uInt16 nPoints = rDevicePoly.GetSize(); + Polygon aPoly( rDevicePoly ); + + // Pointer auf das Point-Array holen (Daten werden kopiert) + const Point* pPointAry = aPoly.GetConstPointAry(); + + for ( i = 0; i < nPoints; i++ ) + { + const Point* pPt = &(pPointAry[i]); + Point aPt; + aPt.X() = ImplPixelToLogic( pPt->X(), mnDPIX, + aMapRes.mnMapScNumX, aMapRes.mnMapScDenomX, + aThresRes.mnThresPixToLogX ) - aMapRes.mnMapOfsX - mnOutOffLogicX; + aPt.Y() = ImplPixelToLogic( pPt->Y(), mnDPIY, + aMapRes.mnMapScNumY, aMapRes.mnMapScDenomY, + aThresRes.mnThresPixToLogY ) - aMapRes.mnMapOfsY - mnOutOffLogicY; + aPoly[i] = aPt; + } + + return aPoly; +} + +// ----------------------------------------------------------------------- + +PolyPolygon OutputDevice::PixelToLogic( const PolyPolygon& rDevicePolyPoly, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rDevicePolyPoly, PolyPolygon, NULL ); + + if ( rMapMode.IsDefault() ) + return rDevicePolyPoly; + + PolyPolygon aPolyPoly( rDevicePolyPoly ); + sal_uInt16 nPoly = aPolyPoly.Count(); + for( sal_uInt16 i = 0; i < nPoly; i++ ) + { + Polygon& rPoly = aPolyPoly[i]; + rPoly = PixelToLogic( rPoly, rMapMode ); + } + return aPolyPoly; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolygon OutputDevice::PixelToLogic( const basegfx::B2DPolygon& rPixelPoly, + const MapMode& rMapMode ) const +{ + basegfx::B2DPolygon aTransformedPoly = rPixelPoly; + const ::basegfx::B2DHomMatrix& rTransformationMatrix = GetInverseViewTransformation( rMapMode ); + aTransformedPoly.transform( rTransformationMatrix ); + return aTransformedPoly; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolyPolygon OutputDevice::PixelToLogic( const basegfx::B2DPolyPolygon& rPixelPolyPoly, + const MapMode& rMapMode ) const +{ + basegfx::B2DPolyPolygon aTransformedPoly = rPixelPolyPoly; + const ::basegfx::B2DHomMatrix& rTransformationMatrix = GetInverseViewTransformation( rMapMode ); + aTransformedPoly.transform( rTransformationMatrix ); + return aTransformedPoly; +} + +// ----------------------------------------------------------------------- + +Region OutputDevice::PixelToLogic( const Region& rDeviceRegion, + const MapMode& rMapMode ) const +{ + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rDeviceRegion, Region, ImplDbgTestRegion ); + + RegionType eType = rDeviceRegion.GetType(); + + if ( rMapMode.IsDefault() || (eType == REGION_EMPTY) || (eType == REGION_NULL) ) + return rDeviceRegion; + + Region aRegion; + PolyPolygon* pPolyPoly = rDeviceRegion.ImplGetImplRegion()->mpPolyPoly; + + if ( pPolyPoly ) + aRegion = Region( PixelToLogic( *pPolyPoly, rMapMode ) ); + else + { + long nX; + long nY; + long nWidth; + long nHeight; + ImplRegionInfo aInfo; + sal_Bool bRegionRect; + + aRegion.ImplBeginAddRect(); + bRegionRect = rDeviceRegion.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight ); + while ( bRegionRect ) + { + Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + aRegion.ImplAddRect( PixelToLogic( aRect, rMapMode ) ); + bRegionRect = rDeviceRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight ); + } + aRegion.ImplEndAddRect(); + } + + return aRegion; +} + +// ----------------------------------------------------------------------- + +#define ENTER0( rSource, pMapModeSource, pMapModeDest ) \ + if ( !pMapModeSource ) \ + pMapModeSource = &maMapMode; \ + if ( !pMapModeDest ) \ + pMapModeDest = &maMapMode; \ + if ( *pMapModeSource == *pMapModeDest ) \ + return rSource + +// ----------------------------------------------------------------------- + +#define ENTER1( rSource, pMapModeSource, pMapModeDest ) \ + ENTER0( rSource, pMapModeSource, pMapModeDest ); \ + \ + ImplMapRes aMapResSource; \ + ImplMapRes aMapResDest; \ + \ + if ( !mbMap || pMapModeSource != &maMapMode ) \ + { \ + if ( pMapModeSource->GetMapUnit() == MAP_RELATIVE ) \ + aMapResSource = maMapRes; \ + ImplCalcMapResolution( *pMapModeSource, \ + mnDPIX, mnDPIY, aMapResSource ); \ + } \ + else \ + aMapResSource = maMapRes; \ + if ( !mbMap || pMapModeDest != &maMapMode ) \ + { \ + if ( pMapModeDest->GetMapUnit() == MAP_RELATIVE ) \ + aMapResDest = maMapRes; \ + ImplCalcMapResolution( *pMapModeDest, \ + mnDPIX, mnDPIY, aMapResDest ); \ + } \ + else \ + aMapResDest = maMapRes + +// ----------------------------------------------------------------------- + +#define ENTER2( eUnitSource, eUnitDest ) \ + DBG_ASSERT( eUnitSource != MAP_SYSFONT \ + && eUnitSource != MAP_APPFONT \ + && eUnitSource != MAP_RELATIVE, \ + "Source MapUnit nicht erlaubt" ); \ + DBG_ASSERT( eUnitDest != MAP_SYSFONT \ + && eUnitDest != MAP_APPFONT \ + && eUnitDest != MAP_RELATIVE, \ + "Destination MapUnit nicht erlaubt" ); \ + DBG_ASSERTWARNING( eUnitSource != MAP_PIXEL, \ + "MAP_PIXEL mit 72dpi angenaehert" ); \ + DBG_ASSERTWARNING( eUnitDest != MAP_PIXEL, \ + "MAP_PIXEL mit 72dpi angenaehert" ) + +// ----------------------------------------------------------------------- + +#define ENTER3( eUnitSource, eUnitDest ) \ + long nNumerator = 1; \ + long nDenominator = 1; \ + DBG_ASSERT( eUnitSource < MAP_LASTENUMDUMMY, "Invalid source map unit"); \ + DBG_ASSERT( eUnitDest < MAP_LASTENUMDUMMY, "Invalid destination map unit"); \ + if( (eUnitSource < MAP_LASTENUMDUMMY) && (eUnitDest < MAP_LASTENUMDUMMY) ) \ + { \ + nNumerator = aImplNumeratorAry[eUnitSource] * \ + aImplDenominatorAry[eUnitDest]; \ + nDenominator = aImplNumeratorAry[eUnitDest] * \ + aImplDenominatorAry[eUnitSource]; \ + } \ + if ( eUnitSource == MAP_PIXEL ) \ + nDenominator *= 72; \ + else if( eUnitDest == MAP_PIXEL ) \ + nNumerator *= 72 + +// ----------------------------------------------------------------------- + +#define ENTER4( rMapModeSource, rMapModeDest ) \ + ImplMapRes aMapResSource; \ + ImplMapRes aMapResDest; \ + \ + ImplCalcMapResolution( rMapModeSource, 72, 72, aMapResSource ); \ + ImplCalcMapResolution( rMapModeDest, 72, 72, aMapResDest ) + +// ----------------------------------------------------------------------- + +// return (n1 * n2 * n3) / (n4 * n5) +static long fn5( const long n1, + const long n2, + const long n3, + const long n4, + const long n5 ) +{ + if ( n1 == 0 || n2 == 0 || n3 == 0 || n4 == 0 || n5 == 0 ) + return 0; + if ( LONG_MAX / Abs(n2) < Abs(n3) ) + { + // a6 wird "ubersprungen + BigInt a7 = n2; + a7 *= n3; + a7 *= n1; + + if ( LONG_MAX / Abs(n4) < Abs(n5) ) + { + BigInt a8 = n4; + a8 *= n5; + + BigInt a9 = a8; + a9 /= 2; + if ( a7.IsNeg() ) + a7 -= a9; + else + a7 += a9; + + a7 /= a8; + } // of if + else + { + long n8 = n4 * n5; + + if ( a7.IsNeg() ) + a7 -= n8 / 2; + else + a7 += n8 / 2; + + a7 /= n8; + } // of else + return (long)a7; + } // of if + else + { + long n6 = n2 * n3; + + if ( LONG_MAX / Abs(n1) < Abs(n6) ) + { + BigInt a7 = n1; + a7 *= n6; + + if ( LONG_MAX / Abs(n4) < Abs(n5) ) + { + BigInt a8 = n4; + a8 *= n5; + + BigInt a9 = a8; + a9 /= 2; + if ( a7.IsNeg() ) + a7 -= a9; + else + a7 += a9; + + a7 /= a8; + } // of if + else + { + long n8 = n4 * n5; + + if ( a7.IsNeg() ) + a7 -= n8 / 2; + else + a7 += n8 / 2; + + a7 /= n8; + } // of else + return (long)a7; + } // of if + else + { + long n7 = n1 * n6; + + if ( LONG_MAX / Abs(n4) < Abs(n5) ) + { + BigInt a7 = n7; + BigInt a8 = n4; + a8 *= n5; + + BigInt a9 = a8; + a9 /= 2; + if ( a7.IsNeg() ) + a7 -= a9; + else + a7 += a9; + + a7 /= a8; + return (long)a7; + } // of if + else + { + const long n8 = n4 * n5; + const long n8_2 = n8 / 2; + + if( n7 < 0 ) + { + if( ( n7 - LONG_MIN ) >= n8_2 ) + n7 -= n8_2; + } + else if( ( LONG_MAX - n7 ) >= n8_2 ) + n7 += n8_2; + + return n7 / n8; + } // of else + } // of else + } // of else +} + +// ----------------------------------------------------------------------- + +// return (n1 * n2) / n3 +static long fn3( const long n1, const long n2, const long n3 ) +{ + if ( n1 == 0 || n2 == 0 || n3 == 0 ) + return 0; + if ( LONG_MAX / Abs(n1) < Abs(n2) ) + { + BigInt a4 = n1; + a4 *= n2; + + if ( a4.IsNeg() ) + a4 -= n3 / 2; + else + a4 += n3 / 2; + + a4 /= n3; + return (long)a4; + } // of if + else + { + long n4 = n1 * n2; + const long n3_2 = n3 / 2; + + if( n4 < 0 ) + { + if( ( n4 - LONG_MIN ) >= n3_2 ) + n4 -= n3_2; + } + else if( ( LONG_MAX - n4 ) >= n3_2 ) + n4 += n3_2; + + return n4 / n3; + } // of else +} + +// ----------------------------------------------------------------------- + +Point OutputDevice::LogicToLogic( const Point& rPtSource, + const MapMode* pMapModeSource, + const MapMode* pMapModeDest ) const +{ + ENTER1( rPtSource, pMapModeSource, pMapModeDest ); + + return Point( fn5( rPtSource.X() + aMapResSource.mnMapOfsX, + aMapResSource.mnMapScNumX, aMapResDest.mnMapScDenomX, + aMapResSource.mnMapScDenomX, aMapResDest.mnMapScNumX ) - + aMapResDest.mnMapOfsX, + fn5( rPtSource.Y() + aMapResSource.mnMapOfsY, + aMapResSource.mnMapScNumY, aMapResDest.mnMapScDenomY, + aMapResSource.mnMapScDenomY, aMapResDest.mnMapScNumY ) - + aMapResDest.mnMapOfsY ); +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::LogicToLogic( const Size& rSzSource, + const MapMode* pMapModeSource, + const MapMode* pMapModeDest ) const +{ + ENTER1( rSzSource, pMapModeSource, pMapModeDest ); + + return Size( fn5( rSzSource.Width(), + aMapResSource.mnMapScNumX, aMapResDest.mnMapScDenomX, + aMapResSource.mnMapScDenomX, aMapResDest.mnMapScNumX ), + fn5( rSzSource.Height(), + aMapResSource.mnMapScNumY, aMapResDest.mnMapScDenomY, + aMapResSource.mnMapScDenomY, aMapResDest.mnMapScNumY ) ); +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::LogicToLogic( const Rectangle& rRectSource, + const MapMode* pMapModeSource, + const MapMode* pMapModeDest ) const +{ + ENTER1( rRectSource, pMapModeSource, pMapModeDest ); + + return Rectangle( fn5( rRectSource.Left() + aMapResSource.mnMapOfsX, + aMapResSource.mnMapScNumX, aMapResDest.mnMapScDenomX, + aMapResSource.mnMapScDenomX, aMapResDest.mnMapScNumX ) - + aMapResDest.mnMapOfsX, + fn5( rRectSource.Top() + aMapResSource.mnMapOfsY, + aMapResSource.mnMapScNumY, aMapResDest.mnMapScDenomY, + aMapResSource.mnMapScDenomY, aMapResDest.mnMapScNumY ) - + aMapResDest.mnMapOfsY, + fn5( rRectSource.Right() + aMapResSource.mnMapOfsX, + aMapResSource.mnMapScNumX, aMapResDest.mnMapScDenomX, + aMapResSource.mnMapScDenomX, aMapResDest.mnMapScNumX ) - + aMapResDest.mnMapOfsX, + fn5( rRectSource.Bottom() + aMapResSource.mnMapOfsY, + aMapResSource.mnMapScNumY, aMapResDest.mnMapScDenomY, + aMapResSource.mnMapScDenomY, aMapResDest.mnMapScNumY ) - + aMapResDest.mnMapOfsY ); +} + +// ----------------------------------------------------------------------- + +long* OutputDevice::LogicToLogic( long* pX, sal_uInt16 nCount, + const MapMode* pMapModeSource, + const MapMode* pMapModeDest ) const +{ + ENTER1( pX, pMapModeSource, pMapModeDest ); + + for( ; nCount; nCount--, pX++ ) + { + *pX = fn5( *pX, + aMapResSource.mnMapScNumX, aMapResDest.mnMapScDenomX, + aMapResSource.mnMapScDenomX, aMapResDest.mnMapScNumX ); + } + + return NULL; +} + +// ----------------------------------------------------------------------- + +Point OutputDevice::LogicToLogic( const Point& rPtSource, + const MapMode& rMapModeSource, + const MapMode& rMapModeDest ) +{ + if ( rMapModeSource == rMapModeDest ) + return rPtSource; + + MapUnit eUnitSource = rMapModeSource.GetMapUnit(); + MapUnit eUnitDest = rMapModeDest.GetMapUnit(); + ENTER2( eUnitSource, eUnitDest ); + + if ( rMapModeSource.mpImplMapMode->mbSimple && + rMapModeDest.mpImplMapMode->mbSimple ) + { + ENTER3( eUnitSource, eUnitDest ); + + return Point( fn3( rPtSource.X(), nNumerator, nDenominator ), + fn3( rPtSource.Y(), nNumerator, nDenominator ) ); + } + else + { + ENTER4( rMapModeSource, rMapModeDest ); + + return Point( fn5( rPtSource.X() + aMapResSource.mnMapOfsX, + aMapResSource.mnMapScNumX, aMapResDest.mnMapScDenomX, + aMapResSource.mnMapScDenomX, aMapResDest.mnMapScNumX ) - + aMapResDest.mnMapOfsX, + fn5( rPtSource.Y() + aMapResSource.mnMapOfsY, + aMapResSource.mnMapScNumY, aMapResDest.mnMapScDenomY, + aMapResSource.mnMapScDenomY, aMapResDest.mnMapScNumY ) - + aMapResDest.mnMapOfsY ); + } +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::LogicToLogic( const Size& rSzSource, + const MapMode& rMapModeSource, + const MapMode& rMapModeDest ) +{ + if ( rMapModeSource == rMapModeDest ) + return rSzSource; + + MapUnit eUnitSource = rMapModeSource.GetMapUnit(); + MapUnit eUnitDest = rMapModeDest.GetMapUnit(); + ENTER2( eUnitSource, eUnitDest ); + + if ( rMapModeSource.mpImplMapMode->mbSimple && + rMapModeDest.mpImplMapMode->mbSimple ) + { + ENTER3( eUnitSource, eUnitDest ); + + return Size( fn3( rSzSource.Width(), nNumerator, nDenominator ), + fn3( rSzSource.Height(), nNumerator, nDenominator ) ); + } + else + { + ENTER4( rMapModeSource, rMapModeDest ); + + return Size( fn5( rSzSource.Width(), + aMapResSource.mnMapScNumX, aMapResDest.mnMapScDenomX, + aMapResSource.mnMapScDenomX, aMapResDest.mnMapScNumX ), + fn5( rSzSource.Height(), + aMapResSource.mnMapScNumY, aMapResDest.mnMapScDenomY, + aMapResSource.mnMapScDenomY, aMapResDest.mnMapScNumY ) ); + } +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolygon OutputDevice::LogicToLogic( const basegfx::B2DPolygon& rPolySource, + const MapMode& rMapModeSource, + const MapMode& rMapModeDest ) +{ + if ( rMapModeSource == rMapModeDest ) + return rPolySource; + + MapUnit eUnitSource = rMapModeSource.GetMapUnit(); + MapUnit eUnitDest = rMapModeDest.GetMapUnit(); + ENTER2( eUnitSource, eUnitDest ); + + basegfx::B2DHomMatrix aTransform; + + if ( rMapModeSource.mpImplMapMode->mbSimple && + rMapModeDest.mpImplMapMode->mbSimple ) + { + ENTER3( eUnitSource, eUnitDest ); + + const double fScaleFactor((double)nNumerator / (double)nDenominator); + aTransform.set(0, 0, fScaleFactor); + aTransform.set(1, 1, fScaleFactor); + } + else + { + ENTER4( rMapModeSource, rMapModeDest ); + + const double fScaleFactorX( (double(aMapResSource.mnMapScNumX) * double(aMapResDest.mnMapScDenomX)) + / (double(aMapResSource.mnMapScDenomX) * double(aMapResDest.mnMapScNumX)) ); + const double fScaleFactorY( (double(aMapResSource.mnMapScNumY) * double(aMapResDest.mnMapScDenomY)) + / (double(aMapResSource.mnMapScDenomY) * double(aMapResDest.mnMapScNumY)) ); + const double fZeroPointX(double(aMapResSource.mnMapOfsX) * fScaleFactorX - double(aMapResDest.mnMapOfsX)); + const double fZeroPointY(double(aMapResSource.mnMapOfsY) * fScaleFactorY - double(aMapResDest.mnMapOfsY)); + + aTransform.set(0, 0, fScaleFactorX); + aTransform.set(1, 1, fScaleFactorY); + aTransform.set(0, 2, fZeroPointX); + aTransform.set(1, 2, fZeroPointY); + } + basegfx::B2DPolygon aPoly( rPolySource ); + aPoly.transform( aTransform ); + return aPoly; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolyPolygon OutputDevice::LogicToLogic( const basegfx::B2DPolyPolygon& rPolySource, + const MapMode& rMapModeSource, + const MapMode& rMapModeDest ) +{ + if ( rMapModeSource == rMapModeDest ) + return rPolySource; + + MapUnit eUnitSource = rMapModeSource.GetMapUnit(); + MapUnit eUnitDest = rMapModeDest.GetMapUnit(); + ENTER2( eUnitSource, eUnitDest ); + + basegfx::B2DHomMatrix aTransform; + + if ( rMapModeSource.mpImplMapMode->mbSimple && + rMapModeDest.mpImplMapMode->mbSimple ) + { + ENTER3( eUnitSource, eUnitDest ); + + const double fScaleFactor((double)nNumerator / (double)nDenominator); + aTransform.set(0, 0, fScaleFactor); + aTransform.set(1, 1, fScaleFactor); + } + else + { + ENTER4( rMapModeSource, rMapModeDest ); + + const double fScaleFactorX( (double(aMapResSource.mnMapScNumX) * double(aMapResDest.mnMapScDenomX)) + / (double(aMapResSource.mnMapScDenomX) * double(aMapResDest.mnMapScNumX)) ); + const double fScaleFactorY( (double(aMapResSource.mnMapScNumY) * double(aMapResDest.mnMapScDenomY)) + / (double(aMapResSource.mnMapScDenomY) * double(aMapResDest.mnMapScNumY)) ); + const double fZeroPointX(double(aMapResSource.mnMapOfsX) * fScaleFactorX - double(aMapResDest.mnMapOfsX)); + const double fZeroPointY(double(aMapResSource.mnMapOfsY) * fScaleFactorY - double(aMapResDest.mnMapOfsY)); + + aTransform.set(0, 0, fScaleFactorX); + aTransform.set(1, 1, fScaleFactorY); + aTransform.set(0, 2, fZeroPointX); + aTransform.set(1, 2, fZeroPointY); + } + basegfx::B2DPolyPolygon aPoly( rPolySource ); + aPoly.transform( aTransform ); + return aPoly; +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::LogicToLogic( const Rectangle& rRectSource, + const MapMode& rMapModeSource, + const MapMode& rMapModeDest ) +{ + if ( rMapModeSource == rMapModeDest ) + return rRectSource; + + MapUnit eUnitSource = rMapModeSource.GetMapUnit(); + MapUnit eUnitDest = rMapModeDest.GetMapUnit(); + ENTER2( eUnitSource, eUnitDest ); + + if ( rMapModeSource.mpImplMapMode->mbSimple && + rMapModeDest.mpImplMapMode->mbSimple ) + { + ENTER3( eUnitSource, eUnitDest ); + + return Rectangle( fn3( rRectSource.Left(), nNumerator, nDenominator ), + fn3( rRectSource.Top(), nNumerator, nDenominator ), + fn3( rRectSource.Right(), nNumerator, nDenominator ), + fn3( rRectSource.Bottom(), nNumerator, nDenominator ) ); + } + else + { + ENTER4( rMapModeSource, rMapModeDest ); + + return Rectangle( fn5( rRectSource.Left() + aMapResSource.mnMapOfsX, + aMapResSource.mnMapScNumX, aMapResDest.mnMapScDenomX, + aMapResSource.mnMapScDenomX, aMapResDest.mnMapScNumX ) - + aMapResDest.mnMapOfsX, + fn5( rRectSource.Top() + aMapResSource.mnMapOfsY, + aMapResSource.mnMapScNumY, aMapResDest.mnMapScDenomY, + aMapResSource.mnMapScDenomY, aMapResDest.mnMapScNumY ) - + aMapResDest.mnMapOfsY, + fn5( rRectSource.Right() + aMapResSource.mnMapOfsX, + aMapResSource.mnMapScNumX, aMapResDest.mnMapScDenomX, + aMapResSource.mnMapScDenomX, aMapResDest.mnMapScNumX ) - + aMapResDest.mnMapOfsX, + fn5( rRectSource.Bottom() + aMapResSource.mnMapOfsY, + aMapResSource.mnMapScNumY, aMapResDest.mnMapScDenomY, + aMapResSource.mnMapScDenomY, aMapResDest.mnMapScNumY ) - + aMapResDest.mnMapOfsY ); + } +} + +// ----------------------------------------------------------------------- + +long OutputDevice::LogicToLogic( long nLongSource, + MapUnit eUnitSource, MapUnit eUnitDest ) +{ + if ( eUnitSource == eUnitDest ) + return nLongSource; + + ENTER2( eUnitSource, eUnitDest ); + ENTER3( eUnitSource, eUnitDest ); + + return fn3( nLongSource, nNumerator, nDenominator ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetPixelOffset( const Size& rOffset ) +{ + mnOutOffOrigX = rOffset.Width(); + mnOutOffOrigY = rOffset.Height(); + + mnOutOffLogicX = ImplPixelToLogic( mnOutOffOrigX, mnDPIX, + maMapRes.mnMapScNumX, maMapRes.mnMapScDenomX, + maThresRes.mnThresPixToLogX ); + mnOutOffLogicY = ImplPixelToLogic( mnOutOffOrigY, mnDPIY, + maMapRes.mnMapScNumY, maMapRes.mnMapScDenomY, + maThresRes.mnThresPixToLogY ); + + if( mpAlphaVDev ) + mpAlphaVDev->SetPixelOffset( rOffset ); +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::GetPixelOffset() const +{ + return Size(mnOutOffOrigX, mnOutOffOrigY); +} + +// ----------------------------------------------------------------------- + +long Window::ImplLogicUnitToPixelX( long nX, MapUnit eUnit ) +{ + if ( eUnit != MAP_PIXEL ) + { + ImplFrameData* pFrameData = mpWindowImpl->mpFrameData; + + // Map-Einheit verschieden, dann neu berechnen + if ( pFrameData->meMapUnit != eUnit ) + { + pFrameData->meMapUnit = eUnit; + ImplCalcMapResolution( MapMode( eUnit ), mnDPIX, mnDPIY, + pFrameData->maMapUnitRes ); + } + + // Es wird kein BigInt gebraucht, da diese Funktion nur zur Umrechnung + // von Fensterposition benutzt wird + nX = nX * mnDPIX * pFrameData->maMapUnitRes.mnMapScNumX; + nX += nX >= 0 ? (pFrameData->maMapUnitRes.mnMapScDenomX/2) : + -((pFrameData->maMapUnitRes.mnMapScDenomX-1)/2); + nX /= pFrameData->maMapUnitRes.mnMapScDenomX; + } + + return nX; +} + +// ----------------------------------------------------------------------- + +long Window::ImplLogicUnitToPixelY( long nY, MapUnit eUnit ) +{ + if ( eUnit != MAP_PIXEL ) + { + ImplFrameData* pFrameData = mpWindowImpl->mpFrameData; + + // Map-Einheit verschieden, dann neu berechnen + if ( pFrameData->meMapUnit != eUnit ) + { + pFrameData->meMapUnit = eUnit; + ImplCalcMapResolution( MapMode( eUnit ), mnDPIX, mnDPIY, + pFrameData->maMapUnitRes ); + } + + // Es wird kein BigInt gebraucht, da diese Funktion nur zur Umrechnung + // von Fensterposition benutzt wird + nY = nY * mnDPIY * pFrameData->maMapUnitRes.mnMapScNumY; + nY += nY >= 0 ? (pFrameData->maMapUnitRes.mnMapScDenomY/2) : + -((pFrameData->maMapUnitRes.mnMapScDenomY-1)/2); + nY /= pFrameData->maMapUnitRes.mnMapScDenomY; + } + + return nY; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/pdfextoutdevdata.cxx b/vcl/source/gdi/pdfextoutdevdata.cxx new file mode 100644 index 000000000000..2b3173c4b128 --- /dev/null +++ b/vcl/source/gdi/pdfextoutdevdata.cxx @@ -0,0 +1,799 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include "vcl/pdfextoutdevdata.hxx" +#include "vcl/graph.hxx" +#include "vcl/outdev.hxx" +#include "vcl/gfxlink.hxx" +#include "basegfx/polygon/b2dpolygon.hxx" +#include "basegfx/polygon/b2dpolygontools.hxx" + + +#include <boost/shared_ptr.hpp> +#include <set> + +namespace vcl +{ +struct PDFExtOutDevDataSync +{ + enum Action{ CreateNamedDest, + CreateDest, + CreateLink, + SetLinkDest, + SetLinkURL, + CreateOutlineItem, + SetOutlineItemParent, + SetOutlineItemText, + SetOutlineItemDest, + CreateNote, + SetAutoAdvanceTime, + SetPageTransition, + + BeginStructureElement, + EndStructureElement, + SetCurrentStructureElement, + SetStructureAttribute, + SetStructureAttributeNumerical, + SetStructureBoundingBox, + SetActualText, + SetAlternateText, + CreateControl, + BeginGroup, + EndGroup, + EndGroupGfxLink + }; + + sal_uInt32 nIdx; + Action eAct; +}; + +struct GlobalSyncData +{ + std::deque< PDFExtOutDevDataSync::Action > mActions; + std::deque< MapMode > mParaMapModes; + std::deque< Rectangle > mParaRects; + std::deque< sal_Int32 > mParaInts; + std::deque< sal_uInt32 > mParauInts; + std::deque< rtl::OUString > mParaOUStrings; + std::deque< PDFWriter::DestAreaType > mParaDestAreaTypes; + std::deque< PDFNote > mParaPDFNotes; + std::deque< PDFWriter::PageTransition > mParaPageTransitions; + + sal_Int32 GetMappedId(); + sal_Int32 GetMappedStructId( sal_Int32 ); + + sal_Int32 mCurId; + std::vector< sal_Int32 > mParaIds; + std::vector< sal_Int32 > mStructIdMap; + + sal_Int32 mCurrentStructElement; + std::vector< sal_Int32 > mStructParents; + GlobalSyncData() : + mCurId ( 0 ), + mCurrentStructElement( 0 ) + { + mStructParents.push_back( 0 ); + mStructIdMap.push_back( 0 ); + } + void PlayGlobalActions( PDFWriter& rWriter ); +}; + +sal_Int32 GlobalSyncData::GetMappedId() +{ + sal_Int32 nLinkId = mParaInts.front(); + mParaInts.pop_front(); + + /* negative values are intentionally passed as invalid IDs + * e.g. to create a new top level outline item + */ + if( nLinkId >= 0 ) + { + if ( (sal_uInt32)nLinkId < mParaIds.size() ) + nLinkId = mParaIds[ nLinkId ]; + else + nLinkId = -1; + + DBG_ASSERT( nLinkId >= 0, "unmapped id in GlobalSyncData" ); + } + + return nLinkId; +} + +sal_Int32 GlobalSyncData::GetMappedStructId( sal_Int32 nStructId ) +{ + if ( (sal_uInt32)nStructId < mStructIdMap.size() ) + nStructId = mStructIdMap[ nStructId ]; + else + nStructId = -1; + + DBG_ASSERT( nStructId >= 0, "unmapped structure id in GlobalSyncData" ); + + return nStructId; +} + +void GlobalSyncData::PlayGlobalActions( PDFWriter& rWriter ) +{ + std::deque< PDFExtOutDevDataSync::Action >::iterator aIter( mActions.begin() ); + std::deque< PDFExtOutDevDataSync::Action >::iterator aEnd( mActions.end() ); + while( aIter != aEnd ) + { + switch( *aIter ) + { + case PDFExtOutDevDataSync::CreateNamedDest : //i56629 + { + rWriter.Push( PUSH_MAPMODE ); + rWriter.SetMapMode( mParaMapModes.front() ); + mParaMapModes.pop_front(); + mParaIds.push_back( rWriter.CreateNamedDest( mParaOUStrings.front(), mParaRects.front(), mParaInts.front(), mParaDestAreaTypes.front() ) ); + mParaOUStrings.pop_front(); + mParaRects.pop_front(); + mParaInts.pop_front(); + mParaDestAreaTypes.pop_front(); + rWriter.Pop(); + } + break; + case PDFExtOutDevDataSync::CreateDest : + { + rWriter.Push( PUSH_MAPMODE ); + rWriter.SetMapMode( mParaMapModes.front() ); + mParaMapModes.pop_front(); + mParaIds.push_back( rWriter.CreateDest( mParaRects.front(), mParaInts.front(), mParaDestAreaTypes.front() ) ); + mParaRects.pop_front(); + mParaInts.pop_front(); + mParaDestAreaTypes.pop_front(); + rWriter.Pop(); + } + break; + case PDFExtOutDevDataSync::CreateLink : + { + rWriter.Push( PUSH_MAPMODE ); + rWriter.SetMapMode( mParaMapModes.front() ); + mParaMapModes.pop_front(); + mParaIds.push_back( rWriter.CreateLink( mParaRects.front(), mParaInts.front() ) ); + // resolve LinkAnnotation structural attribute + rWriter.SetLinkPropertyID( mParaIds.back(), sal_Int32(mParaIds.size()-1) ); + mParaRects.pop_front(); + mParaInts.pop_front(); + rWriter.Pop(); + } + break; + case PDFExtOutDevDataSync::SetLinkDest : + { + sal_Int32 nLinkId = GetMappedId(); + sal_Int32 nDestId = GetMappedId(); + rWriter.SetLinkDest( nLinkId, nDestId ); + } + break; + case PDFExtOutDevDataSync::SetLinkURL : + { + sal_Int32 nLinkId = GetMappedId(); + rWriter.SetLinkURL( nLinkId, mParaOUStrings.front() ); + mParaOUStrings.pop_front(); + } + break; + case PDFExtOutDevDataSync::CreateOutlineItem : + { + sal_Int32 nParent = GetMappedId(); + sal_Int32 nLinkId = GetMappedId(); + mParaIds.push_back( rWriter.CreateOutlineItem( nParent, mParaOUStrings.front(), nLinkId ) ); + mParaOUStrings.pop_front(); + } + break; + case PDFExtOutDevDataSync::SetOutlineItemParent : + { + sal_Int32 nItem = GetMappedId(); + sal_Int32 nNewParent = GetMappedId(); + rWriter.SetOutlineItemParent( nItem, nNewParent ); + } + break; + case PDFExtOutDevDataSync::SetOutlineItemText : + { + sal_Int32 nItem = GetMappedId(); + rWriter.SetOutlineItemText( nItem, mParaOUStrings.front() ); + mParaOUStrings.pop_front(); + } + break; + case PDFExtOutDevDataSync::SetOutlineItemDest : + { + sal_Int32 nItem = GetMappedId(); + sal_Int32 nDestId = GetMappedId(); + rWriter.SetOutlineItemDest( nItem, nDestId ); + } + break; + case PDFExtOutDevDataSync::CreateNote : + { + rWriter.Push( PUSH_MAPMODE ); + rWriter.SetMapMode( mParaMapModes.front() ); + rWriter.CreateNote( mParaRects.front(), mParaPDFNotes.front(), mParaInts.front() ); + mParaMapModes.pop_front(); + mParaRects.pop_front(); + mParaPDFNotes.pop_front(); + mParaInts.pop_front(); + } + break; + case PDFExtOutDevDataSync::SetAutoAdvanceTime : + { + rWriter.SetAutoAdvanceTime( mParauInts.front(), mParaInts.front() ); + mParauInts.pop_front(); + mParaInts.pop_front(); + } + break; + case PDFExtOutDevDataSync::SetPageTransition : + { + rWriter.SetPageTransition( mParaPageTransitions.front(), mParauInts.front(), mParaInts.front() ); + mParaPageTransitions.pop_front(); + mParauInts.pop_front(); + mParaInts.pop_front(); + } + break; + case PDFExtOutDevDataSync::BeginStructureElement: + case PDFExtOutDevDataSync::EndStructureElement: + case PDFExtOutDevDataSync::SetCurrentStructureElement: + case PDFExtOutDevDataSync::SetStructureAttribute: + case PDFExtOutDevDataSync::SetStructureAttributeNumerical: + case PDFExtOutDevDataSync::SetStructureBoundingBox: + case PDFExtOutDevDataSync::SetActualText: + case PDFExtOutDevDataSync::SetAlternateText: + case PDFExtOutDevDataSync::CreateControl: + case PDFExtOutDevDataSync::BeginGroup: + case PDFExtOutDevDataSync::EndGroup: + case PDFExtOutDevDataSync::EndGroupGfxLink: + break; + } + aIter++; + } +} + +struct PageSyncData +{ + std::deque< PDFExtOutDevDataSync > mActions; + std::deque< Rectangle > mParaRects; + std::deque< sal_Int32 > mParaInts; + std::deque< rtl::OUString > mParaOUStrings; + std::deque< PDFWriter::StructElement > mParaStructElements; + std::deque< PDFWriter::StructAttribute > mParaStructAttributes; + std::deque< PDFWriter::StructAttributeValue > mParaStructAttributeValues; + std::deque< Graphic > mGraphics; + std::deque< ::boost::shared_ptr< PDFWriter::AnyWidget > > + mControls; + GlobalSyncData* mpGlobalData; + + sal_Bool mbGroupIgnoreGDIMtfActions; + + PageSyncData( GlobalSyncData* pGlobal ) : mbGroupIgnoreGDIMtfActions ( sal_False ) { mpGlobalData = pGlobal; } + + void PushAction( const OutputDevice& rOutDev, const PDFExtOutDevDataSync::Action eAct ); + sal_Bool PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rCurGDIMtfAction, const PDFExtOutDevData& rOutDevData ); +}; +void PageSyncData::PushAction( const OutputDevice& rOutDev, const PDFExtOutDevDataSync::Action eAct ) +{ + GDIMetaFile* pMtf = rOutDev.GetConnectMetaFile(); + DBG_ASSERT( pMtf, "PageSyncData::PushAction -> no ConnectMetaFile !!!" ); + + PDFExtOutDevDataSync aSync; + aSync.eAct = eAct; + if ( pMtf ) + aSync.nIdx = pMtf->GetActionCount(); + else + aSync.nIdx = 0x7fffffff; // sync not possible + mActions.push_back( aSync ); +} +sal_Bool PageSyncData::PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rCurGDIMtfAction, const PDFExtOutDevData& rOutDevData ) +{ + sal_Bool bRet = sal_False; + if ( mActions.size() && ( mActions.front().nIdx == rCurGDIMtfAction ) ) + { + bRet = sal_True; + PDFExtOutDevDataSync aDataSync = mActions.front(); + mActions.pop_front(); + switch( aDataSync.eAct ) + { + case PDFExtOutDevDataSync::BeginStructureElement : + { + sal_Int32 nNewEl = rWriter.BeginStructureElement( mParaStructElements.front(), mParaOUStrings.front() ) ; + mParaStructElements.pop_front(); + mParaOUStrings.pop_front(); + mpGlobalData->mStructIdMap.push_back( nNewEl ); + } + break; + case PDFExtOutDevDataSync::EndStructureElement : + { + rWriter.EndStructureElement(); + } + break; + case PDFExtOutDevDataSync::SetCurrentStructureElement: + { + rWriter.SetCurrentStructureElement( mpGlobalData->GetMappedStructId( mParaInts.front() ) ); + mParaInts.pop_front(); + } + break; + case PDFExtOutDevDataSync::SetStructureAttribute : + { + rWriter.SetStructureAttribute( mParaStructAttributes.front(), mParaStructAttributeValues.front() ); + mParaStructAttributeValues.pop_front(); + mParaStructAttributes.pop_front(); + } + break; + case PDFExtOutDevDataSync::SetStructureAttributeNumerical : + { + rWriter.SetStructureAttributeNumerical( mParaStructAttributes.front(), mParaInts.front() ); + mParaStructAttributes.pop_front(); + mParaInts.pop_front(); + } + break; + case PDFExtOutDevDataSync::SetStructureBoundingBox : + { + rWriter.SetStructureBoundingBox( mParaRects.front() ); + mParaRects.pop_front(); + } + break; + case PDFExtOutDevDataSync::SetActualText : + { + rWriter.SetActualText( mParaOUStrings.front() ); + mParaOUStrings.pop_front(); + } + break; + case PDFExtOutDevDataSync::SetAlternateText : + { + rWriter.SetAlternateText( mParaOUStrings.front() ); + mParaOUStrings.pop_front(); + } + break; + case PDFExtOutDevDataSync::CreateControl: + { + ::boost::shared_ptr< PDFWriter::AnyWidget > pControl( mControls.front() ); + DBG_ASSERT( pControl.get(), "PageSyncData::PlaySyncPageAct: invalid widget!" ); + if ( pControl.get() ) + rWriter.CreateControl( *pControl ); + mControls.pop_front(); + } + break; + case PDFExtOutDevDataSync::BeginGroup : + { + /* first determining if this BeginGroup is starting a GfxLink, + by searching for a EndGroup or a EndGroupGfxLink */ + mbGroupIgnoreGDIMtfActions = sal_False; + std::deque< PDFExtOutDevDataSync >::iterator aBeg = mActions.begin(); + std::deque< PDFExtOutDevDataSync >::iterator aEnd = mActions.end(); + while ( aBeg != aEnd ) + { + if ( aBeg->eAct == PDFExtOutDevDataSync::EndGroup ) + { + break; + } + else if ( aBeg->eAct == PDFExtOutDevDataSync::EndGroupGfxLink ) + { + if ( rOutDevData.GetIsLosslessCompression() && !rOutDevData.GetIsReduceImageResolution() ) + { + Graphic& rGraphic = mGraphics.front(); + if ( rGraphic.IsLink() && rGraphic.GetLink().GetType() == GFX_LINK_TYPE_NATIVE_JPG ) + { + mbGroupIgnoreGDIMtfActions = sal_True; + } + } + break; + } + ++aBeg; + } + } + break; + case PDFExtOutDevDataSync::EndGroup : + { + mbGroupIgnoreGDIMtfActions = sal_False; + } + break; + case PDFExtOutDevDataSync::EndGroupGfxLink : + { + Rectangle aOutputRect, aVisibleOutputRect; + Graphic aGraphic( mGraphics.front() ); + + mGraphics.pop_front(); + mParaInts.pop_front(); //Transparency + aOutputRect = mParaRects.front(); + mParaRects.pop_front(); + aVisibleOutputRect = mParaRects.front(); + mParaRects.pop_front(); + + if ( mbGroupIgnoreGDIMtfActions ) + { + sal_Bool bClippingNeeded = ( aOutputRect != aVisibleOutputRect ) && !aVisibleOutputRect.IsEmpty(); + + GfxLink aGfxLink( aGraphic.GetLink() ); + if ( aGfxLink.GetType() == GFX_LINK_TYPE_NATIVE_JPG ) + { + if ( bClippingNeeded ) + { + rWriter.Push(); + basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect( + basegfx::B2DRectangle( aVisibleOutputRect.Left(), aVisibleOutputRect.Top(), + aVisibleOutputRect.Right(), aVisibleOutputRect.Bottom() ) ) ); + rWriter.SetClipRegion( aRect); + } + Bitmap aMask; + SvMemoryStream aTmp; + const sal_uInt8* pData = aGfxLink.GetData(); + sal_uInt32 nBytes = aGfxLink.GetDataSize(); + if( pData && nBytes ) + { + aTmp.Write( pData, nBytes ); + rWriter.DrawJPGBitmap( aTmp, aGraphic.GetBitmap().GetBitCount() > 8, aGraphic.GetSizePixel(), aOutputRect, aMask ); + } + + if ( bClippingNeeded ) + rWriter.Pop(); + } + mbGroupIgnoreGDIMtfActions = sal_False; + } + } + break; + case PDFExtOutDevDataSync::CreateNamedDest: + case PDFExtOutDevDataSync::CreateDest: + case PDFExtOutDevDataSync::CreateLink: + case PDFExtOutDevDataSync::SetLinkDest: + case PDFExtOutDevDataSync::SetLinkURL: + case PDFExtOutDevDataSync::CreateOutlineItem: + case PDFExtOutDevDataSync::SetOutlineItemParent: + case PDFExtOutDevDataSync::SetOutlineItemText: + case PDFExtOutDevDataSync::SetOutlineItemDest: + case PDFExtOutDevDataSync::CreateNote: + case PDFExtOutDevDataSync::SetAutoAdvanceTime: + case PDFExtOutDevDataSync::SetPageTransition: + break; + } + } + else if ( mbGroupIgnoreGDIMtfActions ) + { + rCurGDIMtfAction++; + bRet = sal_True; + } + return bRet; +} + +TYPEINIT1(PDFExtOutDevData,ExtOutDevData); +PDFExtOutDevData::PDFExtOutDevData( const OutputDevice& rOutDev ) : + mrOutDev ( rOutDev ), + mbTaggedPDF ( sal_False ), + mbExportNotes ( sal_True ), + mbTransitionEffects ( sal_True ), + mbUseLosslessCompression( sal_True ), + mbReduceImageResolution ( sal_False ), + mbExportNDests ( sal_False ), + mnFormsFormat ( 0 ), + mnPage ( -1 ), + mpPageSyncData ( NULL ), + mpGlobalSyncData ( new GlobalSyncData() ) +{ + mpPageSyncData = new PageSyncData( mpGlobalSyncData ); +} + +PDFExtOutDevData::~PDFExtOutDevData() +{ + delete mpPageSyncData; + delete mpGlobalSyncData; +} + +const com::sun::star::lang::Locale& PDFExtOutDevData::GetDocumentLocale() const +{ + return maDocLocale; +} +void PDFExtOutDevData::SetDocumentLocale( const com::sun::star::lang::Locale& rLoc ) +{ + maDocLocale = rLoc; +} +sal_Int32 PDFExtOutDevData::GetCurrentPageNumber() const +{ + return mnPage; +} +void PDFExtOutDevData::SetCurrentPageNumber( const sal_Int32 nPage ) +{ + mnPage = nPage; +} +sal_Bool PDFExtOutDevData::GetIsLosslessCompression() const +{ + return mbUseLosslessCompression; +} +void PDFExtOutDevData::SetIsLosslessCompression( const sal_Bool bUseLosslessCompression ) +{ + mbUseLosslessCompression = bUseLosslessCompression; +} +sal_Bool PDFExtOutDevData::GetIsReduceImageResolution() const +{ + return mbReduceImageResolution; +} +void PDFExtOutDevData::SetIsReduceImageResolution( const sal_Bool bReduceImageResolution ) +{ + mbReduceImageResolution = bReduceImageResolution; +} +sal_Bool PDFExtOutDevData::GetIsExportNotes() const +{ + return mbExportNotes; +} +void PDFExtOutDevData::SetIsExportNotes( const sal_Bool bExportNotes ) +{ + mbExportNotes = bExportNotes; +} +sal_Bool PDFExtOutDevData::GetIsExportTaggedPDF() const +{ + return mbTaggedPDF; +} +void PDFExtOutDevData::SetIsExportTaggedPDF( const sal_Bool bTaggedPDF ) +{ + mbTaggedPDF = bTaggedPDF; +} +sal_Bool PDFExtOutDevData::GetIsExportTransitionEffects() const +{ + return mbTransitionEffects; +} +void PDFExtOutDevData::SetIsExportTransitionEffects( const sal_Bool bTransitionEffects ) +{ + mbTransitionEffects = bTransitionEffects; +} +sal_Bool PDFExtOutDevData::GetIsExportFormFields() const +{ + return mbExportFormFields; +} +void PDFExtOutDevData::SetIsExportFormFields( const sal_Bool bExportFomtFields ) +{ + mbExportFormFields = bExportFomtFields; +} +sal_Int32 PDFExtOutDevData::GetFormsFormat() const +{ + return mnFormsFormat; +} +void PDFExtOutDevData::SetFormsFormat( const sal_Int32 nFormsFormat ) +{ + mnFormsFormat = nFormsFormat; +} +sal_Bool PDFExtOutDevData::GetIsExportBookmarks() const +{ + return mbExportBookmarks; +} +void PDFExtOutDevData::SetIsExportBookmarks( const sal_Bool bExportBookmarks ) +{ + mbExportBookmarks = bExportBookmarks; +} +std::vector< PDFExtOutDevBookmarkEntry >& PDFExtOutDevData::GetBookmarks() +{ + return maBookmarks; +} +sal_Bool PDFExtOutDevData::GetIsExportNamedDestinations() const +{ + return mbExportNDests; +} +void PDFExtOutDevData::SetIsExportNamedDestinations( const sal_Bool bExportNDests ) +{ + mbExportNDests = bExportNDests; +} +void PDFExtOutDevData::ResetSyncData() +{ + *mpPageSyncData = PageSyncData( mpGlobalSyncData ); +} +sal_Bool PDFExtOutDevData::PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rIdx ) +{ + return mpPageSyncData->PlaySyncPageAct( rWriter, rIdx, *this ); +} +void PDFExtOutDevData::PlayGlobalActions( PDFWriter& rWriter ) +{ + mpGlobalSyncData->PlayGlobalActions( rWriter ); +} + +/* global actions, syncronisation to the recorded metafile isn't needed, + all actions will be played after the last page was recorded +*/ +//--->i56629 +sal_Int32 PDFExtOutDevData::CreateNamedDest(const String& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateNamedDest ); + mpGlobalSyncData->mParaOUStrings.push_back( sDestName ); + mpGlobalSyncData->mParaRects.push_back( rRect ); + mpGlobalSyncData->mParaMapModes.push_back( mrOutDev.GetMapMode() ); + mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); + mpGlobalSyncData->mParaDestAreaTypes.push_back( eType ); + return mpGlobalSyncData->mCurId++; +} +//<---i56629 +sal_Int32 PDFExtOutDevData::CreateDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateDest ); + mpGlobalSyncData->mParaRects.push_back( rRect ); + mpGlobalSyncData->mParaMapModes.push_back( mrOutDev.GetMapMode() ); + mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); + mpGlobalSyncData->mParaDestAreaTypes.push_back( eType ); + return mpGlobalSyncData->mCurId++; +} +sal_Int32 PDFExtOutDevData::CreateLink( const Rectangle& rRect, sal_Int32 nPageNr ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateLink ); + mpGlobalSyncData->mParaRects.push_back( rRect ); + mpGlobalSyncData->mParaMapModes.push_back( mrOutDev.GetMapMode() ); + mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); + return mpGlobalSyncData->mCurId++; +} +sal_Int32 PDFExtOutDevData::SetLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetLinkDest ); + mpGlobalSyncData->mParaInts.push_back( nLinkId ); + mpGlobalSyncData->mParaInts.push_back( nDestId ); + return 0; +} +sal_Int32 PDFExtOutDevData::SetLinkURL( sal_Int32 nLinkId, const rtl::OUString& rURL ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetLinkURL ); + mpGlobalSyncData->mParaInts.push_back( nLinkId ); + mpGlobalSyncData->mParaOUStrings.push_back( rURL ); + return 0; +} +sal_Int32 PDFExtOutDevData::CreateOutlineItem( sal_Int32 nParent, const rtl::OUString& rText, sal_Int32 nDestID ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateOutlineItem ); + mpGlobalSyncData->mParaInts.push_back( nParent ); + mpGlobalSyncData->mParaOUStrings.push_back( rText ); + mpGlobalSyncData->mParaInts.push_back( nDestID ); + return mpGlobalSyncData->mCurId++; +} +sal_Int32 PDFExtOutDevData::SetOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetOutlineItemParent ); + mpGlobalSyncData->mParaInts.push_back( nItem ); + mpGlobalSyncData->mParaInts.push_back( nNewParent ); + return 0; +} +sal_Int32 PDFExtOutDevData::SetOutlineItemText( sal_Int32 nItem, const rtl::OUString& rText ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetOutlineItemText ); + mpGlobalSyncData->mParaInts.push_back( nItem ); + mpGlobalSyncData->mParaOUStrings.push_back( rText ); + return 0; +} +sal_Int32 PDFExtOutDevData::SetOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetOutlineItemDest ); + mpGlobalSyncData->mParaInts.push_back( nItem ); + mpGlobalSyncData->mParaInts.push_back( nDestID ); + return 0; +} +void PDFExtOutDevData::CreateNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateNote ); + mpGlobalSyncData->mParaRects.push_back( rRect ); + mpGlobalSyncData->mParaMapModes.push_back( mrOutDev.GetMapMode() ); + mpGlobalSyncData->mParaPDFNotes.push_back( rNote ); + mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); +} +void PDFExtOutDevData::SetAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetAutoAdvanceTime ); + mpGlobalSyncData->mParauInts.push_back( nSeconds ); + mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); +} +void PDFExtOutDevData::SetPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr ) +{ + mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetPageTransition ); + mpGlobalSyncData->mParaPageTransitions.push_back( eType ); + mpGlobalSyncData->mParauInts.push_back( nMilliSec ); + mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); +} + +/* local (page), actions have to be played synchroniously to the actions of + of the recorded metafile (created by each xRenderable->render()) */ + sal_Int32 PDFExtOutDevData::BeginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias ) +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::BeginStructureElement ); + mpPageSyncData->mParaStructElements.push_back( eType ); + mpPageSyncData->mParaOUStrings.push_back( rAlias ); + // need a global id + sal_Int32 nNewId = mpGlobalSyncData->mStructParents.size(); + mpGlobalSyncData->mStructParents.push_back( mpGlobalSyncData->mCurrentStructElement ); + mpGlobalSyncData->mCurrentStructElement = nNewId; + return nNewId; +} +void PDFExtOutDevData::EndStructureElement() +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::EndStructureElement ); + mpGlobalSyncData->mCurrentStructElement = mpGlobalSyncData->mStructParents[ mpGlobalSyncData->mCurrentStructElement ]; +} +bool PDFExtOutDevData::SetCurrentStructureElement( sal_Int32 nStructId ) +{ + bool bSuccess = false; + if( sal_uInt32(nStructId) < mpGlobalSyncData->mStructParents.size() ) + { + mpGlobalSyncData->mCurrentStructElement = nStructId; + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetCurrentStructureElement ); + mpPageSyncData->mParaInts.push_back( nStructId ); + bSuccess = true; + } + return bSuccess; +} +sal_Int32 PDFExtOutDevData::GetCurrentStructureElement() +{ + return mpGlobalSyncData->mCurrentStructElement; +} +bool PDFExtOutDevData::SetStructureAttribute( PDFWriter::StructAttribute eAttr, PDFWriter::StructAttributeValue eVal ) +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetStructureAttribute ); + mpPageSyncData->mParaStructAttributes.push_back( eAttr ); + mpPageSyncData->mParaStructAttributeValues.push_back( eVal ); + return true; +} +bool PDFExtOutDevData::SetStructureAttributeNumerical( PDFWriter::StructAttribute eAttr, sal_Int32 nValue ) +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetStructureAttributeNumerical ); + mpPageSyncData->mParaStructAttributes.push_back( eAttr ); + mpPageSyncData->mParaInts.push_back( nValue ); + return true; +} +void PDFExtOutDevData::SetStructureBoundingBox( const Rectangle& rRect ) +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetStructureBoundingBox ); + mpPageSyncData->mParaRects.push_back( rRect ); +} +void PDFExtOutDevData::SetActualText( const String& rText ) +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetActualText ); + mpPageSyncData->mParaOUStrings.push_back( rText ); +} +void PDFExtOutDevData::SetAlternateText( const String& rText ) +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetAlternateText ); + mpPageSyncData->mParaOUStrings.push_back( rText ); +} + +void PDFExtOutDevData::CreateControl( const PDFWriter::AnyWidget& rControlType, sal_Int32 /*nPageNr*/ ) +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::CreateControl ); + + ::boost::shared_ptr< PDFWriter::AnyWidget > pClone( rControlType.Clone() ); + mpPageSyncData->mControls.push_back( pClone ); +} + +void PDFExtOutDevData::BeginGroup() +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::BeginGroup ); +} + +void PDFExtOutDevData::EndGroup() +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::EndGroup ); +} +void PDFExtOutDevData::EndGroup( const Graphic& rGraphic, + sal_uInt8 nTransparency, + const Rectangle& rOutputRect, + const Rectangle& rVisibleOutputRect ) +{ + mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::EndGroupGfxLink ); + mpPageSyncData->mGraphics.push_back( rGraphic ); + mpPageSyncData->mParaInts.push_back( nTransparency ); + mpPageSyncData->mParaRects.push_back( rOutputRect ); + mpPageSyncData->mParaRects.push_back( rVisibleOutputRect ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/pdffontcache.cxx b/vcl/source/gdi/pdffontcache.cxx new file mode 100644 index 000000000000..2b5944377eac --- /dev/null +++ b/vcl/source/gdi/pdffontcache.cxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_vcl.hxx" + +#include "pdffontcache.hxx" +#include <vcl/salgdi.hxx> +#include <vcl/outfont.hxx> +#include <vcl/sallayout.hxx> + +using namespace vcl; + +PDFFontCache::FontIdentifier::FontIdentifier( const ImplFontData* pFont, bool bVertical ) : + m_nFontId( pFont->GetFontId() ), + m_nMagic( pFont->GetFontMagic() ), + m_bVertical( bVertical ) +{ +} + +PDFFontCache::FontData& PDFFontCache::getFont( const ImplFontData* pFont, bool bVertical ) +{ + FontIdentifier aId( pFont, bVertical ); + FontToIndexMap::iterator it = m_aFontToIndex.find( aId ); + if( it != m_aFontToIndex.end() ) + return m_aFonts[ it->second ]; + m_aFontToIndex[ aId ] = sal_uInt32(m_aFonts.size()); + m_aFonts.push_back( FontData() ); + return m_aFonts.back(); +} + +sal_Int32 PDFFontCache::getGlyphWidth( const ImplFontData* pFont, sal_GlyphId nGlyph, bool bVertical, SalGraphics* pGraphics ) +{ + sal_Int32 nWidth = 0; + FontData& rFontData( getFont( pFont, bVertical ) ); + if( rFontData.m_nWidths.empty() ) + { + pGraphics->GetGlyphWidths( pFont, bVertical, rFontData.m_nWidths, rFontData.m_aGlyphIdToIndex ); + } + if( ! rFontData.m_nWidths.empty() ) + { + sal_GlyphId nIndex = nGlyph; + if( (nGlyph & GF_ISCHAR) != 0 ) + { + const sal_Ucs cCode = static_cast<sal_Ucs>(nGlyph & GF_IDXMASK); + Ucs2UIntMap::const_iterator it = rFontData.m_aGlyphIdToIndex.find( cCode ); + + // allow symbol aliasing U+00xx -> U+F0xx if there is no direct match + if( it == rFontData.m_aGlyphIdToIndex.end() + && pFont->IsSymbolFont() + && (cCode < 0x0100) ) + it = rFontData.m_aGlyphIdToIndex.find( cCode+0xF000 ); + + nIndex = (it != rFontData.m_aGlyphIdToIndex.end()) ? it->second : 0; + } + nIndex &= GF_IDXMASK; + if( nIndex < rFontData.m_nWidths.size() ) + nWidth = rFontData.m_nWidths[ nIndex ]; + } + return nWidth; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/pdffontcache.hxx b/vcl/source/gdi/pdffontcache.hxx new file mode 100644 index 000000000000..30d075d23e42 --- /dev/null +++ b/vcl/source/gdi/pdffontcache.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef VCL_PDFFONTCACHE_HXX +#define VCL_PDFFONTCACHE_HXX + +#include <sal/types.h> + +#include <vcl/sallayout.hxx> +#include <vcl/salgdi.hxx> + +namespace vcl +{ + class PDFFontCache + { + struct FontIdentifier + { + sal_IntPtr m_nFontId; + int m_nMagic; + bool m_bVertical; + + FontIdentifier( const ImplFontData*, bool bVertical ); + FontIdentifier() : m_nFontId(0), m_nMagic(0), m_bVertical( false ) {} + + bool operator==( const FontIdentifier& rRight ) const + { + return m_nFontId == rRight.m_nFontId && + m_nMagic == rRight.m_nMagic && + m_bVertical == rRight.m_bVertical; + } + bool operator<( const FontIdentifier& rRight ) const + { + return m_nFontId < rRight.m_nFontId || + m_nMagic < rRight.m_nMagic || + m_bVertical < rRight.m_bVertical; + } + }; + struct FontData + { + Int32Vector m_nWidths; + Ucs2UIntMap m_aGlyphIdToIndex; + }; + typedef std::map< FontIdentifier, sal_uInt32 > FontToIndexMap; + + std::vector< FontData > m_aFonts; + FontToIndexMap m_aFontToIndex; + + FontData& getFont( const ImplFontData*, bool bVertical ); + public: + PDFFontCache() {} + ~PDFFontCache() {} + + sal_Int32 getGlyphWidth( const ImplFontData*, sal_GlyphId, bool bVertical, SalGraphics* ); + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/pdfwriter.cxx b/vcl/source/gdi/pdfwriter.cxx new file mode 100644 index 000000000000..881205e0af77 --- /dev/null +++ b/vcl/source/gdi/pdfwriter.cxx @@ -0,0 +1,578 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <pdfwriter_impl.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/image.hxx> + +using namespace vcl; + +PDFWriter::AnyWidget::~AnyWidget() +{ +} + +PDFWriter::PDFWriter( const PDFWriter::PDFWriterContext& rContext, const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc ) + : + pImplementation( new PDFWriterImpl( rContext, xEnc, *this ) ) +{ +} + +PDFWriter::~PDFWriter() +{ + delete (PDFWriterImpl*)pImplementation; +} + +OutputDevice* PDFWriter::GetReferenceDevice() +{ + return ((PDFWriterImpl*)pImplementation)->getReferenceDevice(); +} + +sal_Int32 PDFWriter::NewPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, Orientation eOrientation ) +{ + return ((PDFWriterImpl*)pImplementation)->newPage( nPageWidth, nPageHeight, eOrientation ); +} + +bool PDFWriter::Emit() +{ + return ((PDFWriterImpl*)pImplementation)->emit(); +} + +PDFWriter::PDFVersion PDFWriter::GetVersion() const +{ + return ((PDFWriterImpl*)pImplementation)->getVersion(); +} + +void PDFWriter::SetDocumentLocale( const com::sun::star::lang::Locale& rLoc ) +{ + ((PDFWriterImpl*)pImplementation)->setDocumentLocale( rLoc ); +} + +void PDFWriter::SetFont( const Font& rFont ) +{ + ((PDFWriterImpl*)pImplementation)->setFont( rFont ); +} + +void PDFWriter::DrawText( const Point& rPos, const String& rText ) +{ + ((PDFWriterImpl*)pImplementation)->drawText( rPos, rText ); +} + +void PDFWriter::DrawTextLine( + const Point& rPos, + long nWidth, + FontStrikeout eStrikeout, + FontUnderline eUnderline, + FontUnderline eOverline, + sal_Bool bUnderlineAbove ) +{ + ((PDFWriterImpl*)pImplementation)->drawTextLine( rPos, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove ); +} + +void PDFWriter::DrawTextArray( + const Point& rStartPt, + const XubString& rStr, + const sal_Int32* pDXAry, + xub_StrLen nIndex, + xub_StrLen nLen ) +{ + ((PDFWriterImpl*)pImplementation)->drawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen ); +} + +void PDFWriter::DrawStretchText( + const Point& rStartPt, + sal_uLong nWidth, + const XubString& rStr, + xub_StrLen nIndex, + xub_StrLen nLen ) +{ + ((PDFWriterImpl*)pImplementation)->drawStretchText( rStartPt, nWidth, rStr, nIndex, nLen ); +} + +void PDFWriter::DrawText( + const Rectangle& rRect, + const XubString& rStr, + sal_uInt16 nStyle ) +{ + ((PDFWriterImpl*)pImplementation)->drawText( rRect, rStr, nStyle ); +} + +void PDFWriter::DrawLine( const Point& rStart, const Point& rStop ) +{ + ((PDFWriterImpl*)pImplementation)->drawLine( rStart, rStop ); +} + +void PDFWriter::DrawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo ) +{ + ((PDFWriterImpl*)pImplementation)->drawLine( rStart, rStop, rInfo ); +} + +void PDFWriter::DrawPolygon( const Polygon& rPoly ) +{ + ((PDFWriterImpl*)pImplementation)->drawPolygon( rPoly ); +} + +void PDFWriter::DrawPolyLine( const Polygon& rPoly ) +{ + ((PDFWriterImpl*)pImplementation)->drawPolyLine( rPoly ); +} + +void PDFWriter::DrawRect( const Rectangle& rRect ) +{ + ((PDFWriterImpl*)pImplementation)->drawRectangle( rRect ); +} + +void PDFWriter::DrawRect( const Rectangle& rRect, sal_uLong nHorzRound, sal_uLong nVertRound ) +{ + ((PDFWriterImpl*)pImplementation)->drawRectangle( rRect, nHorzRound, nVertRound ); +} + +void PDFWriter::DrawEllipse( const Rectangle& rRect ) +{ + ((PDFWriterImpl*)pImplementation)->drawEllipse( rRect ); +} + +void PDFWriter::DrawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop ) +{ + ((PDFWriterImpl*)pImplementation)->drawArc( rRect, rStart, rStop, false, false ); +} + +void PDFWriter::DrawPie( const Rectangle& rRect, const Point& rStart, const Point& rStop ) +{ + ((PDFWriterImpl*)pImplementation)->drawArc( rRect, rStart, rStop, true, false ); +} + +void PDFWriter::DrawChord( const Rectangle& rRect, const Point& rStart, const Point& rStop ) +{ + ((PDFWriterImpl*)pImplementation)->drawArc( rRect, rStart, rStop, false, true ); +} + +void PDFWriter::DrawPolyLine( const Polygon& rPoly, const LineInfo& rInfo ) +{ + ((PDFWriterImpl*)pImplementation)->drawPolyLine( rPoly, rInfo ); +} + +void PDFWriter::DrawPolyLine( const Polygon& rPoly, const ExtLineInfo& rInfo ) +{ + ((PDFWriterImpl*)pImplementation)->drawPolyLine( rPoly, rInfo ); +} + +void PDFWriter::DrawPolyPolygon( const PolyPolygon& rPolyPoly ) +{ + ((PDFWriterImpl*)pImplementation)->drawPolyPolygon( rPolyPoly ); +} + +void PDFWriter::DrawPixel( const Point& rPos, const Color& rColor ) +{ + ((PDFWriterImpl*)pImplementation)->drawPixel( rPos, rColor ); +} + +void PDFWriter::DrawPixel( const Polygon& rPts, const Color* pColors ) +{ + ((PDFWriterImpl*)pImplementation)->drawPixel( rPts, pColors ); +} + +void PDFWriter::DrawBitmap( const Point& rDestPt, const Bitmap& rBitmap ) +{ + Size aSize = OutputDevice::LogicToLogic( rBitmap.GetPrefSize(), + rBitmap.GetPrefMapMode(), + ((PDFWriterImpl*)pImplementation)->getMapMode() ); + ((PDFWriterImpl*)pImplementation)->drawBitmap( rDestPt, aSize, rBitmap ); +} + +void PDFWriter::DrawBitmap( const Point& rDestPt, const Size& rDestSize, const Bitmap& rBitmap ) +{ + ((PDFWriterImpl*)pImplementation)->drawBitmap( rDestPt, rDestSize, rBitmap ); +} + +void PDFWriter::DrawBitmap( const Point& rDestPt, const Size& rDestSize, const Point& rSrcPtPixel, const Size& rSrcSizePixel, const Bitmap& rBitmap ) +{ + Bitmap aBitmap( rBitmap ); + aBitmap.Crop( Rectangle( rSrcPtPixel, rSrcSizePixel ) ); + ((PDFWriterImpl*)pImplementation)->drawBitmap( rDestPt, rDestSize, aBitmap ); +} + +void PDFWriter::DrawBitmapEx( const Point& rDestPt, const BitmapEx& rBitmap ) +{ + Size aSize = OutputDevice::LogicToLogic( rBitmap.GetPrefSize(), + rBitmap.GetPrefMapMode(), + ((PDFWriterImpl*)pImplementation)->getMapMode() ); + ((PDFWriterImpl*)pImplementation)->drawBitmap( rDestPt, aSize, rBitmap ); +} + +void PDFWriter::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize, const BitmapEx& rBitmap ) +{ + ((PDFWriterImpl*)pImplementation)->drawBitmap( rDestPt, rDestSize, rBitmap ); +} + +void PDFWriter::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize, const Point& rSrcPtPixel, const Size& rSrcSizePixel, const BitmapEx& rBitmap ) +{ + if ( !!rBitmap ) + { + BitmapEx aBitmap( rBitmap ); + aBitmap.Crop( Rectangle( rSrcPtPixel, rSrcSizePixel ) ); + ((PDFWriterImpl*)pImplementation)->drawBitmap( rDestPt, rDestSize, aBitmap ); + } +} + +void PDFWriter::DrawMask( const Point& rDestPt, const Bitmap& rBitmap, const Color& rMaskColor ) +{ + Size aSize = OutputDevice::LogicToLogic( rBitmap.GetPrefSize(), + rBitmap.GetPrefMapMode(), + ((PDFWriterImpl*)pImplementation)->getMapMode() ); + ((PDFWriterImpl*)pImplementation)->drawMask( rDestPt, aSize, rBitmap, rMaskColor ); +} + +void PDFWriter::DrawMask( const Point& rDestPt, const Size& rDestSize, const Bitmap& rBitmap, const Color& rMaskColor ) +{ + ((PDFWriterImpl*)pImplementation)->drawMask( rDestPt, rDestSize, rBitmap, rMaskColor ); +} + +void PDFWriter::DrawMask( const Point& rDestPt, const Size& rDestSize, const Point& rSrcPtPixel, const Size& rSrcSizePixel, const Bitmap& rBitmap, const Color& rMaskColor ) +{ + Bitmap aBitmap( rBitmap ); + aBitmap.Crop( Rectangle( rSrcPtPixel, rSrcSizePixel ) ); + ((PDFWriterImpl*)pImplementation)->drawMask( rDestPt, rDestSize, aBitmap, rMaskColor ); +} + +void PDFWriter::DrawGradient( const Rectangle& rRect, const Gradient& rGradient ) +{ + ((PDFWriterImpl*)pImplementation)->drawGradient( rRect, rGradient ); +} + +void PDFWriter::DrawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient ) +{ + ((PDFWriterImpl*)pImplementation)->drawGradient( rPolyPoly, rGradient ); +} + +void PDFWriter::DrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch ) +{ + ((PDFWriterImpl*)pImplementation)->drawHatch( rPolyPoly, rHatch ); +} + +void PDFWriter::DrawWallpaper( const Rectangle& rRect, const Wallpaper& rWallpaper ) +{ + ((PDFWriterImpl*)pImplementation)->drawWallpaper( rRect, rWallpaper ); +} + +void PDFWriter::DrawTransparent( const PolyPolygon& rPolyPoly, sal_uInt16 nTransparencePercent ) +{ + ((PDFWriterImpl*)pImplementation)->drawTransparent( rPolyPoly, nTransparencePercent ); +} + +void PDFWriter::BeginTransparencyGroup() +{ + ((PDFWriterImpl*)pImplementation)->beginTransparencyGroup(); +} + +void PDFWriter::EndTransparencyGroup( const Rectangle& rRect, sal_uInt16 nTransparentPercent ) +{ + ((PDFWriterImpl*)pImplementation)->endTransparencyGroup( rRect, nTransparentPercent ); +} + +void PDFWriter::EndTransparencyGroup( const Rectangle& rRect, const Bitmap& rAlphaMask ) +{ + ((PDFWriterImpl*)pImplementation)->endTransparencyGroup( rRect, rAlphaMask ); +} + +void PDFWriter::Push( sal_uInt16 nFlags ) +{ + ((PDFWriterImpl*)pImplementation)->push( nFlags ); +} + +void PDFWriter::Pop() +{ + ((PDFWriterImpl*)pImplementation)->pop(); +} + +void PDFWriter::SetMapMode( const MapMode& rMapMode ) +{ + ((PDFWriterImpl*)pImplementation)->setMapMode( rMapMode ); +} + +void PDFWriter::SetMapMode() +{ + ((PDFWriterImpl*)pImplementation)->setMapMode(); +} + +void PDFWriter::SetLineColor( const Color& rColor ) +{ + ((PDFWriterImpl*)pImplementation)->setLineColor( rColor ); +} + +void PDFWriter::SetFillColor( const Color& rColor ) +{ + ((PDFWriterImpl*)pImplementation)->setFillColor( rColor ); +} + +void PDFWriter::SetClipRegion() +{ + ((PDFWriterImpl*)pImplementation)->clearClipRegion(); +} + +void PDFWriter::SetClipRegion( const basegfx::B2DPolyPolygon& rRegion ) +{ + ((PDFWriterImpl*)pImplementation)->setClipRegion( rRegion ); +} + +void PDFWriter::MoveClipRegion( long nHorzMove, long nVertMove ) +{ + ((PDFWriterImpl*)pImplementation)->moveClipRegion( nHorzMove, nVertMove ); +} + +void PDFWriter::IntersectClipRegion( const basegfx::B2DPolyPolygon& rRegion ) +{ + ((PDFWriterImpl*)pImplementation)->intersectClipRegion( rRegion ); +} + +void PDFWriter::IntersectClipRegion( const Rectangle& rRect ) +{ + ((PDFWriterImpl*)pImplementation)->intersectClipRegion( rRect ); +} + +void PDFWriter::SetAntialiasing( sal_uInt16 nMode ) +{ + ((PDFWriterImpl*)pImplementation)->setAntiAlias( (sal_Int32)nMode ); +} + +void PDFWriter::SetLayoutMode( sal_uLong nMode ) +{ + ((PDFWriterImpl*)pImplementation)->setLayoutMode( (sal_Int32)nMode ); +} + +void PDFWriter::SetDigitLanguage( LanguageType eLang ) +{ + ((PDFWriterImpl*)pImplementation)->setDigitLanguage( eLang ); +} + +void PDFWriter::SetTextColor( const Color& rColor ) +{ + ((PDFWriterImpl*)pImplementation)->setTextColor( rColor ); +} + +void PDFWriter::SetTextFillColor() +{ + ((PDFWriterImpl*)pImplementation)->setTextFillColor(); +} + +void PDFWriter::SetTextFillColor( const Color& rColor ) +{ + ((PDFWriterImpl*)pImplementation)->setTextFillColor( rColor ); +} + +void PDFWriter::SetTextLineColor() +{ + ((PDFWriterImpl*)pImplementation)->setTextLineColor(); +} + +void PDFWriter::SetTextLineColor( const Color& rColor ) +{ + ((PDFWriterImpl*)pImplementation)->setTextLineColor( rColor ); +} + +void PDFWriter::SetOverlineColor() +{ + ((PDFWriterImpl*)pImplementation)->setOverlineColor(); +} + +void PDFWriter::SetOverlineColor( const Color& rColor ) +{ + ((PDFWriterImpl*)pImplementation)->setOverlineColor( rColor ); +} + +void PDFWriter::SetTextAlign( ::TextAlign eAlign ) +{ + ((PDFWriterImpl*)pImplementation)->setTextAlign( eAlign ); +} + +void PDFWriter::DrawJPGBitmap( SvStream& rStreamData, bool bIsTrueColor, const Size& rSrcSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask ) +{ + ((PDFWriterImpl*)pImplementation)->drawJPGBitmap( rStreamData, bIsTrueColor, rSrcSizePixel, rTargetArea, rMask ); +} + +sal_Int32 PDFWriter::CreateLink( const Rectangle& rRect, sal_Int32 nPageNr ) +{ + return ((PDFWriterImpl*)pImplementation)->createLink( rRect, nPageNr ); +} +//--->i56629 +sal_Int32 PDFWriter::CreateNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) +{ + return ((PDFWriterImpl*)pImplementation)->createNamedDest( sDestName, rRect, nPageNr, eType ); +} +//<--- +sal_Int32 PDFWriter::CreateDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) +{ + return ((PDFWriterImpl*)pImplementation)->createDest( rRect, nPageNr, eType ); +} + +sal_Int32 PDFWriter::SetLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ) +{ + return ((PDFWriterImpl*)pImplementation)->setLinkDest( nLinkId, nDestId ); +} + +sal_Int32 PDFWriter::SetLinkURL( sal_Int32 nLinkId, const rtl::OUString& rURL ) +{ + return ((PDFWriterImpl*)pImplementation)->setLinkURL( nLinkId, rURL ); +} + +void PDFWriter::SetLinkPropertyID( sal_Int32 nLinkId, sal_Int32 nPropertyId ) +{ + ((PDFWriterImpl*)pImplementation)->setLinkPropertyId( nLinkId, nPropertyId ); +} + +sal_Int32 PDFWriter::CreateOutlineItem( sal_Int32 nParent, const rtl::OUString& rText, sal_Int32 nDestID ) +{ + return ((PDFWriterImpl*)pImplementation)->createOutlineItem( nParent, rText, nDestID ); +} + +sal_Int32 PDFWriter::SetOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent ) +{ + return ((PDFWriterImpl*)pImplementation)->setOutlineItemParent( nItem, nNewParent ); +} + +sal_Int32 PDFWriter::SetOutlineItemText( sal_Int32 nItem, const rtl::OUString& rText ) +{ + return ((PDFWriterImpl*)pImplementation)->setOutlineItemText( nItem, rText ); +} + +sal_Int32 PDFWriter::SetOutlineItemDest( sal_Int32 nItem, sal_Int32 nDest ) +{ + return ((PDFWriterImpl*)pImplementation)->setOutlineItemDest( nItem, nDest ); +} + +void PDFWriter::CreateNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr ) +{ + ((PDFWriterImpl*)pImplementation)->createNote( rRect, rNote, nPageNr ); +} + +sal_Int32 PDFWriter::BeginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias ) +{ + return ((PDFWriterImpl*)pImplementation)->beginStructureElement( eType, rAlias ); +} + +void PDFWriter::EndStructureElement() +{ + ((PDFWriterImpl*)pImplementation)->endStructureElement(); +} + +bool PDFWriter::SetCurrentStructureElement( sal_Int32 nID ) +{ + return ((PDFWriterImpl*)pImplementation)->setCurrentStructureElement( nID ); +} + +sal_Int32 PDFWriter::GetCurrentStructureElement() +{ + return ((PDFWriterImpl*)pImplementation)->getCurrentStructureElement(); +} + +bool PDFWriter::SetStructureAttribute( enum StructAttribute eAttr, enum StructAttributeValue eVal ) +{ + return ((PDFWriterImpl*)pImplementation)->setStructureAttribute( eAttr, eVal ); +} + +bool PDFWriter::SetStructureAttributeNumerical( enum StructAttribute eAttr, sal_Int32 nValue ) +{ + return ((PDFWriterImpl*)pImplementation)->setStructureAttributeNumerical( eAttr, nValue ); +} + +void PDFWriter::SetStructureBoundingBox( const Rectangle& rRect ) +{ + ((PDFWriterImpl*)pImplementation)->setStructureBoundingBox( rRect ); +} + +void PDFWriter::SetActualText( const String& rText ) +{ + ((PDFWriterImpl*)pImplementation)->setActualText( rText ); +} + +void PDFWriter::SetAlternateText( const String& rText ) +{ + ((PDFWriterImpl*)pImplementation)->setAlternateText( rText ); +} + +void PDFWriter::SetAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr ) +{ + ((PDFWriterImpl*)pImplementation)->setAutoAdvanceTime( nSeconds, nPageNr ); +} + +void PDFWriter::SetPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr ) +{ + ((PDFWriterImpl*)pImplementation)->setPageTransition( eType, nMilliSec, nPageNr ); +} + +sal_Int32 PDFWriter::CreateControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr ) +{ + return ((PDFWriterImpl*)pImplementation)->createControl( rControl, nPageNr ); +} + +PDFOutputStream::~PDFOutputStream() +{ +} + +void PDFWriter::AddStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress ) +{ + ((PDFWriterImpl*)pImplementation)->addStream( rMimeType, pStream, bCompress ); +} + +void PDFWriter::BeginPattern( const Rectangle& rCellRect ) +{ + ((PDFWriterImpl*)pImplementation)->beginPattern( rCellRect ); +} + +sal_Int32 PDFWriter::EndPattern( const SvtGraphicFill::Transform& rTransform ) +{ + return ((PDFWriterImpl*)pImplementation)->endPattern( rTransform ); +} + +void PDFWriter::DrawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill ) +{ + ((PDFWriterImpl*)pImplementation)->drawPolyPolygon( rPolyPoly, nPattern, bEOFill ); +} + +std::set< PDFWriter::ErrorCode > PDFWriter::GetErrors() +{ + return ((PDFWriterImpl*)pImplementation)->getErrors(); +} + +com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder > +PDFWriter::InitEncryption( const rtl::OUString& i_rOwnerPassword, + const rtl::OUString& i_rUserPassword, + bool b128Bit + ) +{ + return PDFWriterImpl::initEncryption( i_rOwnerPassword, i_rUserPassword, b128Bit ); +} + +void PDFWriter::PlayMetafile( const GDIMetaFile& i_rMTF, const vcl::PDFWriter::PlayMetafileContext& i_rPlayContext, PDFExtOutDevData* i_pData ) +{ + ((PDFWriterImpl*)pImplementation)->playMetafile( i_rMTF, i_pData, i_rPlayContext, NULL); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx new file mode 100644 index 000000000000..b9d76cd4faa5 --- /dev/null +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -0,0 +1,12124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#define _USE_MATH_DEFINES +#include <math.h> +#include <algorithm> + +#include <tools/urlobj.hxx> +#include <pdfwriter_impl.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <rtl/ustrbuf.hxx> +#include <tools/debug.hxx> +#include <tools/zcodec.hxx> +#include <tools/stream.hxx> +#include <i18npool/mslangid.hxx> +#include <vcl/virdev.hxx> +#include <vcl/bmpacc.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/image.hxx> +#include <vcl/outdev.h> +#include <vcl/sallayout.hxx> +#include <vcl/metric.hxx> +#include <vcl/fontsubset.hxx> +#include <vcl/textlayout.hxx> +#include <vcl/cvtgrf.hxx> +#include <svsys.h> +#include <vcl/salgdi.hxx> +#include <vcl/svapp.hxx> +#include <osl/thread.h> +#include <osl/file.h> +#include <rtl/crc.h> +#include <rtl/digest.h> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/util/URL.hpp> +#include "cppuhelper/implbase1.hxx" +#include <icc/sRGB-IEC61966-2.1.hxx> +#include <vcl/lineinfo.hxx> +#include "vcl/strhelper.hxx" + +using namespace vcl; + +using ::rtl::OUString; +using ::rtl::OUStringToOString; +using ::rtl::OString; +using ::rtl::OStringHash; +using ::rtl::OUStringHash; +using ::rtl::OStringBuffer; +using ::rtl::OUStringBuffer; + +#if (OSL_DEBUG_LEVEL < 2) +#define COMPRESS_PAGES +#else +#define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams +#endif + +#ifdef DO_TEST_PDF +class PDFTestOutputStream : public PDFOutputStream +{ + public: + virtual ~PDFTestOutputStream(); + virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream ); +}; + +PDFTestOutputStream::~PDFTestOutputStream() +{ +} + +void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream ) +{ + OString aStr( "lalala\ntest\ntest\ntest" ); + com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() ); + rtl_copyMemory( aData.getArray(), aStr.getStr(), aStr.getLength() ); + xStream->writeBytes( aData ); +} + +// this test code cannot be used to test PDF/A-1 because it forces +// control item (widgets) to bypass the structure controlling +// the embedding of such elements in actual run +void doTestCode() +{ + static const char* pHome = getenv( "HOME" ); + rtl::OUString aTestFile( RTL_CONSTASCII_USTRINGPARAM( "file://" ) ); + aTestFile += rtl::OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 ); + aTestFile += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/pdf_export_test.pdf" ) ); + + PDFWriter::PDFWriterContext aContext; + aContext.URL = aTestFile; + aContext.Version = PDFWriter::PDF_1_4; + aContext.Tagged = true; + aContext.InitialPage = 2; + aContext.DocumentInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) ); + aContext.DocumentInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) ); + + PDFWriter aWriter( aContext ); + aWriter.NewPage( 595, 842 ); + aWriter.BeginStructureElement( PDFWriter::Document ); + // set duration of 3 sec for first page + aWriter.SetAutoAdvanceTime( 3 ); + aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); + + aWriter.SetFillColor( Color( COL_LIGHTRED ) ); + aWriter.SetLineColor( Color( COL_LIGHTGREEN ) ); + aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 ); + + aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) ); + aWriter.SetTextColor( Color( COL_BLACK ) ); + aWriter.SetLineColor( Color( COL_BLACK ) ); + aWriter.SetFillColor( Color( COL_LIGHTBLUE ) ); + + Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) ); + aWriter.DrawRect( aRect ); + aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 1" ) ) ); + sal_Int32 nFirstLink = aWriter.CreateLink( aRect ); + PDFNote aNote; + aNote.Title = String( RTL_CONSTASCII_USTRINGPARAM( "A small test note" ) ); + aNote.Contents = String( RTL_CONSTASCII_USTRINGPARAM( "There is no business like show business like no business i know. Everything about it is appealing." ) ); + aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote ); + + Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) ); + aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); + aWriter.DrawRect( aTargetRect ); + aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest second link" ) ) ); + sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect ); + + aWriter.BeginStructureElement( PDFWriter::Section ); + aWriter.BeginStructureElement( PDFWriter::Heading ); + aWriter.DrawText( Point(4500, 9000), String( RTL_CONSTASCII_USTRINGPARAM( "A small structure test" ) ) ); + aWriter.EndStructureElement(); + aWriter.BeginStructureElement( PDFWriter::Paragraph ); + aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb ); + aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline ); + aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "It was the best of PDF, it was the worst of PDF ... or so. This is a pretty nonsensical text to denote a paragraph. I suggest you stop reading it. Because if you read on you might get bored. So continue on your on risk. Hey, you're still here ? Why do you continue to read this as it is of no use at all ? OK, it's your time, but still... . Woah, i even get bored writing this, so let's end this here and now." ) ), + TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK + ); + aWriter.SetActualText( String( RTL_CONSTASCII_USTRINGPARAM( "It was the best of PDF, it was the worst of PDF ... or so. This is a pretty nonsensical text to denote a paragraph. I suggest you stop reading it. Because if you read on you might get bored. So continue on your on risk. Hey, you're still here ? Why do you continue to read this as it is of no use at all ? OK, it's your time, but still... . Woah, i even get bored writing this, so let's end this here and now." ) ) ); + aWriter.SetAlternateText( String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) ) ); + aWriter.EndStructureElement(); + sal_Int32 nLongPara = aWriter.BeginStructureElement( PDFWriter::Paragraph ); + aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb ); + aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph is nothing special either but ends on the next page structurewise" ) ), + TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK + ); + + aWriter.NewPage( 595, 842 ); + // test AddStream interface + aWriter.AddStream( String( RTL_CONSTASCII_USTRINGPARAM( "text/plain" ) ), new PDFTestOutputStream(), true ); + // set transitional mode + aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 ); + aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); + aWriter.SetTextColor( Color( COL_BLACK ) ); + aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) ); + aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "Here's where all things come to an end ... well at least the paragaph from the last page." ) ), + TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK + ); + aWriter.EndStructureElement(); + + aWriter.SetFillColor( Color( COL_LIGHTBLUE ) ); + // disable structure + aWriter.BeginStructureElement( PDFWriter::NonStructElement ); + aWriter.DrawRect( aRect ); + aWriter.BeginStructureElement( PDFWriter::Paragraph ); + aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 2" ) ) ); + sal_Int32 nSecondLink = aWriter.CreateLink( aRect ); + + aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); + aWriter.BeginStructureElement( PDFWriter::ListItem ); + aWriter.DrawRect( aTargetRect ); + aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest first link" ) ) ); + sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect ); + // enable structure + aWriter.EndStructureElement(); + // add something to the long paragraph as an afterthought + sal_Int32 nSaveStruct = aWriter.GetCurrentStructureElement(); + aWriter.SetCurrentStructureElement( nLongPara ); + aWriter.DrawText( Rectangle( Point( 4500,4500 ), Size( 12000, 1000 ) ), + String( RTL_CONSTASCII_USTRINGPARAM( "Add something to the longish paragraph above." ) ), + TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); + aWriter.SetCurrentStructureElement( nSaveStruct ); + aWriter.EndStructureElement(); + aWriter.EndStructureElement(); + aWriter.BeginStructureElement( PDFWriter::Figure ); + aWriter.BeginStructureElement( PDFWriter::Caption ); + aWriter.DrawText( Point( 4500, 9000 ), String( RTL_CONSTASCII_USTRINGPARAM( "Some drawing stuff inside the structure" ) ) ); + aWriter.EndStructureElement(); + + // test clipping + basegfx::B2DPolyPolygon aClip; + basegfx::B2DPolygon aClipPoly; + aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) ); + aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) ); + aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) ); + aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) ); + aClipPoly.setClosed( true ); + //aClipPoly.flip(); + aClip.append( aClipPoly ); + + aWriter.Push( PUSH_CLIPREGION | PUSH_FILLCOLOR ); + aWriter.SetClipRegion( aClip ); + aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) ); + aWriter.MoveClipRegion( 1000, 500 ); + aWriter.SetFillColor( Color( COL_RED ) ); + aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) ); + aWriter.Pop(); + // test transparency + // draw background + Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) ); + aWriter.SetFillColor( Color( COL_LIGHTRED ) ); + aWriter.DrawRect( aTranspRect ); + aWriter.BeginTransparencyGroup(); + + aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); + aWriter.DrawEllipse( aTranspRect ); + aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); + aWriter.DrawText( aTranspRect, + String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ), + TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); + + aWriter.EndTransparencyGroup( aTranspRect, 50 ); + + // prepare an alpha mask + Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) ); + BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess(); + for( int nX = 0; nX < 256; nX++ ) + for( int nY = 0; nY < 256; nY++ ) + pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) ); + aTransMask.ReleaseAccess( pAcc ); + aTransMask.SetPrefMapMode( MAP_MM ); + aTransMask.SetPrefSize( Size( 10, 10 ) ); + + aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask ); + + aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) ); + aWriter.SetFillColor( Color( COL_LIGHTRED ) ); + aWriter.DrawRect( aTranspRect ); + aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); + aWriter.DrawEllipse( aTranspRect ); + aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); + aWriter.DrawText( aTranspRect, + String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ), + TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); + aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) ); + aWriter.SetFillColor( Color( COL_LIGHTRED ) ); + aWriter.DrawRect( aTranspRect ); + aWriter.BeginTransparencyGroup(); + aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); + aWriter.DrawEllipse( aTranspRect ); + aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); + aWriter.DrawText( aTranspRect, + String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ), + TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); + aWriter.EndTransparencyGroup( aTranspRect, aTransMask ); + + Bitmap aImageBmp( Size( 256, 256 ), 24 ); + pAcc = aImageBmp.AcquireWriteAccess(); + pAcc->SetFillColor( Color( 0xff, 0, 0xff ) ); + pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) ); + aImageBmp.ReleaseAccess( pAcc ); + BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) ); + aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx ); + + + aWriter.EndStructureElement(); + aWriter.EndStructureElement(); + + LineInfo aLI( LINE_DASH, 3 ); + aLI.SetDashCount( 2 ); + aLI.SetDashLen( 50 ); + aLI.SetDotCount( 2 ); + aLI.SetDotLen( 25 ); + aLI.SetDistance( 15 ); + Point aLIPoints[] = { Point( 4000, 10000 ), + Point( 8000, 12000 ), + Point( 3000, 19000 ) }; + Polygon aLIPoly( 3, aLIPoints ); + aWriter.SetLineColor( Color( COL_BLUE ) ); + aWriter.SetFillColor(); + aWriter.DrawPolyLine( aLIPoly, aLI ); + + aLI.SetDashCount( 4 ); + aLIPoly.Move( 1000, 1000 ); + aWriter.DrawPolyLine( aLIPoly, aLI ); + + aWriter.NewPage( 595, 842 ); + aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); + Wallpaper aWall( aTransMask ); + aWall.SetStyle( WALLPAPER_TILE ); + aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall ); + + aWriter.Push( PUSH_ALL ); + aWriter.BeginPattern(Rectangle(Point(0,0),Size(2000,1000))); + aWriter.SetFillColor( Color( COL_RED ) ); + aWriter.SetLineColor( Color( COL_LIGHTBLUE ) ); + Point aFillPoints[] = { Point( 1000, 0 ), + Point( 0, 1000 ), + Point( 2000, 1000 ) }; + aWriter.DrawPolygon( Polygon( 3, aFillPoints ) ); + aWriter.DrawBitmap( Point( 200, 200 ), Size( 1600, 600 ), aTransMask ); + aWriter.DrawText( Rectangle( Point( 200, 200 ), Size( 1600, 600 ) ), String( RTL_CONSTASCII_USTRINGPARAM( "Pattern" ) ) ); + sal_Int32 nPattern = aWriter.EndPattern( SvtGraphicFill::Transform() ); + aWriter.Pop(); + Rectangle aPolyRect( Point( 3800, 11200 ), Size( 10200, 6300 ) ); + aWriter.DrawPolyPolygon( PolyPolygon( Polygon( aPolyRect ) ), nPattern, true ); + aWriter.SetFillColor(); + aWriter.SetLineColor( Color( COL_LIGHTBLUE ) ); + aWriter.DrawRect( aPolyRect ); + + aWriter.NewPage( 595, 842 ); + aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); + aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) ); + aWriter.SetTextColor( Color( COL_BLACK ) ); + aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) ); + aWriter.DrawRect( aRect ); + aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "www.heise.de" ) ) ); + sal_Int32 nURILink = aWriter.CreateLink( aRect ); + aWriter.SetLinkURL( nURILink, OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ) ); + + aWriter.SetLinkDest( nFirstLink, nFirstDest ); + aWriter.SetLinkDest( nSecondLink, nSecondDest ); + + // include a button + PDFWriter::PushButtonWidget aBtn; + aBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testButton" ) ); + aBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test button" ) ); + aBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "hit me" ) ); + aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) ); + aBtn.Border = aBtn.Background = true; + aWriter.CreateControl( aBtn ); + + // include a uri button + PDFWriter::PushButtonWidget aUriBtn; + aUriBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "wwwButton" ) ); + aUriBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A URI button" ) ); + aUriBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to www" ) ); + aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) ); + aUriBtn.Border = aUriBtn.Background = true; + aUriBtn.URL = OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ); + aWriter.CreateControl( aUriBtn ); + + // include a dest button + PDFWriter::PushButtonWidget aDstBtn; + aDstBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "destButton" ) ); + aDstBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A Dest button" ) ); + aDstBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to paragraph" ) ); + aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) ); + aDstBtn.Border = aDstBtn.Background = true; + aDstBtn.Dest = nFirstDest; + aWriter.CreateControl( aDstBtn ); + + PDFWriter::CheckBoxWidget aCBox; + aCBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox" ) ); + aCBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test check box" ) ); + aCBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me" ) ); + aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) ); + aCBox.Checked = true; + aCBox.Border = aCBox.Background = false; + aWriter.CreateControl( aCBox ); + + PDFWriter::CheckBoxWidget aCBox2; + aCBox2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox2" ) ); + aCBox2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "Another test check box" ) ); + aCBox2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me right" ) ); + aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) ); + aCBox2.Checked = true; + aCBox2.Border = aCBox2.Background = false; + aCBox2.ButtonIsLeft = false; + aWriter.CreateControl( aCBox2 ); + + PDFWriter::RadioButtonWidget aRB1; + aRB1.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_1" ) ); + aRB1.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 1" ) ); + aRB1.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Despair" ) ); + aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) ); + aRB1.Selected = true; + aRB1.RadioGroup = 1; + aRB1.Border = aRB1.Background = true; + aRB1.ButtonIsLeft = false; + aRB1.BorderColor = Color( COL_LIGHTGREEN ); + aRB1.BackgroundColor = Color( COL_LIGHTBLUE ); + aRB1.TextColor = Color( COL_LIGHTRED ); + aRB1.TextFont = Font( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), Size( 0, 800 ) ); + aWriter.CreateControl( aRB1 ); + + PDFWriter::RadioButtonWidget aRB2; + aRB2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb2_1" ) ); + aRB2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 2 button 1" ) ); + aRB2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Joy" ) ); + aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) ); + aRB2.Selected = true; + aRB2.RadioGroup = 2; + aWriter.CreateControl( aRB2 ); + + PDFWriter::RadioButtonWidget aRB3; + aRB3.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_2" ) ); + aRB3.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 2" ) ); + aRB3.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Desperation" ) ); + aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) ); + aRB3.Selected = true; + aRB3.RadioGroup = 1; + aWriter.CreateControl( aRB3 ); + + PDFWriter::EditWidget aEditBox; + aEditBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testEdit" ) ); + aEditBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test edit field" ) ); + aEditBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "A little test text" ) ); + aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER; + aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) ); + aEditBox.MaxLen = 100; + aEditBox.Border = aEditBox.Background = true; + aEditBox.BorderColor = Color( COL_BLACK ); + aWriter.CreateControl( aEditBox ); + + // normal list box + PDFWriter::ListBoxWidget aLstBox; + aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testListBox" ) ); + aLstBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ); + aLstBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "select me" ) ); + aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) ); + aLstBox.Sort = true; + aLstBox.MultiSelect = true; + aLstBox.Border = aLstBox.Background = true; + aLstBox.BorderColor = Color( COL_BLACK ); + aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ) ); + aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Two" ) ) ); + aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Three" ) ) ); + aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Four" ) ) ); + aLstBox.SelectedEntries.push_back( 1 ); + aLstBox.SelectedEntries.push_back( 2 ); + aWriter.CreateControl( aLstBox ); + + // dropdown list box + aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testDropDownListBox" ) ); + aLstBox.DropDown = true; + aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) ); + aWriter.CreateControl( aLstBox ); + + // combo box + PDFWriter::ComboBoxWidget aComboBox; + aComboBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testComboBox" ) ); + aComboBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "test a combobox" ) ); + aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Larry" ) ) ); + aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Curly" ) ) ); + aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Moe" ) ) ); + aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) ); + aWriter.CreateControl( aComboBox ); + + // test outlines + sal_Int32 nPage1OL = aWriter.CreateOutlineItem(); + aWriter.SetOutlineItemText( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 1" ) ) ); + aWriter.SetOutlineItemDest( nPage1OL, nSecondDest ); + aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2" ) ), nSecondDest ); + aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 revisited" ) ), nSecondDest ); + aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 again" ) ), nSecondDest ); + sal_Int32 nPage2OL = aWriter.CreateOutlineItem(); + aWriter.SetOutlineItemText( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 2" ) ) ); + aWriter.CreateOutlineItem( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 1" ) ), nFirstDest ); + + aWriter.EndStructureElement(); // close document + aWriter.Emit(); +} +#endif + +static const sal_Int32 nLog10Divisor = 1; +static const double fDivisor = 10.0; + +static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; } +static inline double pixelToPoint( double px ) { return px/fDivisor; } +static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); } + +const sal_uInt8 PDFWriterImpl::s_nPadString[32] = +{ + 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, + 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A +}; + +static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer ) +{ + static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] ); + rBuffer.append( pHexDigits[ nInt & 15 ] ); +} + +static void appendName( const OUString& rStr, OStringBuffer& rBuffer ) +{ +// FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1 +// I guess than when reading the #xx sequence it will count for a single character. + OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) ); + const sal_Char* pStr = aStr.getStr(); + int nLen = aStr.getLength(); + for( int i = 0; i < nLen; i++ ) + { + /* #i16920# PDF recommendation: output UTF8, any byte + * outside the interval [33(=ASCII'!');126(=ASCII'~')] + * should be escaped hexadecimal + * for the sake of ghostscript which also reads PDF + * but has a narrower acceptance rate we only pass + * alphanumerics and '-' literally. + */ + if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) || + (pStr[i] >= 'a' && pStr[i] <= 'z' ) || + (pStr[i] >= '0' && pStr[i] <= '9' ) || + pStr[i] == '-' ) + { + rBuffer.append( pStr[i] ); + } + else + { + rBuffer.append( '#' ); + appendHex( (sal_Int8)pStr[i], rBuffer ); + } + } +} + +static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer ) +{ +//FIXME i59651 see above + while( pStr && *pStr ) + { + if( (*pStr >= 'A' && *pStr <= 'Z' ) || + (*pStr >= 'a' && *pStr <= 'z' ) || + (*pStr >= '0' && *pStr <= '9' ) || + *pStr == '-' ) + { + rBuffer.append( *pStr ); + } + else + { + rBuffer.append( '#' ); + appendHex( (sal_Int8)*pStr, rBuffer ); + } + pStr++; + } +} + +//used only to emit encoded passwords +static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer ) +{ + while( nLength ) + { + switch( *pStr ) + { + case '\n' : + rBuffer.append( "\\n" ); + break; + case '\r' : + rBuffer.append( "\\r" ); + break; + case '\t' : + rBuffer.append( "\\t" ); + break; + case '\b' : + rBuffer.append( "\\b" ); + break; + case '\f' : + rBuffer.append( "\\f" ); + break; + case '(' : + case ')' : + case '\\' : + rBuffer.append( "\\" ); + rBuffer.append( (sal_Char) *pStr ); + break; + default: + rBuffer.append( (sal_Char) *pStr ); + break; + } + pStr++; + nLength--; + } +} + +/**--->i56629 + * Convert a string before using it. + * + * This string conversion function is needed because the destination name + * in a PDF file seen through an Internet browser should be + * specially crafted, in order to be used directly by the browser. + * In this way the fragment part of a hyperlink to a PDF file (e.g. something + * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the + * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called + * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf + * and go to named destination thefragment using default zoom'. + * The conversion is needed because in case of a fragment in the form: Slide%201 + * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201 + * using this conversion, in both the generated named destinations, fragment and GoToR + * destination. + * + * The names for destinations are name objects and so they don't need to be encrypted + * even though they expose the content of PDF file (e.g. guessing the PDF content from the + * destination name). + * + * Fhurter limitation: it is advisable to use standard ASCII characters for + * OOo bookmarks. +*/ +static void appendDestinationName( const rtl::OUString& rString, OStringBuffer& rBuffer ) +{ + const sal_Unicode* pStr = rString.getStr(); + sal_Int32 nLen = rString.getLength(); + for( int i = 0; i < nLen; i++ ) + { + sal_Unicode aChar = pStr[i]; + if( (aChar >= '0' && aChar <= '9' ) || + (aChar >= 'a' && aChar <= 'z' ) || + (aChar >= 'A' && aChar <= 'Z' ) || + aChar == '-' ) + { + rBuffer.append((sal_Char)aChar); + } + else + { + sal_Int8 aValueHigh = sal_Int8(aChar >> 8); + if(aValueHigh > 0) + appendHex( aValueHigh, rBuffer ); + appendHex( (sal_Int8)(aChar & 255 ), rBuffer ); + } + } +} +//<--- i56629 + +static void appendUnicodeTextString( const rtl::OUString& rString, OStringBuffer& rBuffer ) +{ + rBuffer.append( "FEFF" ); + const sal_Unicode* pStr = rString.getStr(); + sal_Int32 nLen = rString.getLength(); + for( int i = 0; i < nLen; i++ ) + { + sal_Unicode aChar = pStr[i]; + appendHex( (sal_Int8)(aChar >> 8), rBuffer ); + appendHex( (sal_Int8)(aChar & 255 ), rBuffer ); + } +} + +void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl ) +{ + /* #i80258# previously we use appendName here + however we need a slightly different coding scheme than the normal + name encoding for field names + */ + const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text; + OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) ); + const sal_Char* pStr = aStr.getStr(); + int nLen = aStr.getLength(); + + OStringBuffer aBuffer( rName.getLength()+64 ); + for( int i = 0; i < nLen; i++ ) + { + /* #i16920# PDF recommendation: output UTF8, any byte + * outside the interval [32(=ASCII' ');126(=ASCII'~')] + * should be escaped hexadecimal + */ + if( (pStr[i] >= 32 && pStr[i] <= 126 ) ) + aBuffer.append( pStr[i] ); + else + { + aBuffer.append( '#' ); + appendHex( (sal_Int8)pStr[i], aBuffer ); + } + } + + OString aFullName( aBuffer.makeStringAndClear() ); + + /* #i82785# create hierarchical fields down to the for each dot in i_rName */ + sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0; + OString aPartialName; + OString aDomain; + do + { + nLastTokenIndex = nTokenIndex; + aPartialName = aFullName.getToken( 0, '.', nTokenIndex ); + if( nTokenIndex != -1 ) + { + // find or create a hierarchical field + // first find the fully qualified name up to this field + aDomain = aFullName.copy( 0, nTokenIndex-1 ); + boost::unordered_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain ); + if( it == m_aFieldNameMap.end() ) + { + // create new hierarchy field + sal_Int32 nNewWidget = m_aWidgets.size(); + m_aWidgets.push_back( PDFWidget() ); + m_aWidgets[nNewWidget].m_nObject = createObject(); + m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy; + m_aWidgets[nNewWidget].m_aName = aPartialName; + m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject; + m_aFieldNameMap[aDomain] = nNewWidget; + m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject; + if( nLastTokenIndex > 0 ) + { + // this field is not a root field and + // needs to be inserted to its parent + OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) ); + it = m_aFieldNameMap.find( aParentDomain ); + OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" ); + if( it != m_aFieldNameMap.end() ) + { + OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" ); + if( it->second < sal_Int32(m_aWidgets.size()) ) + { + PDFWidget& rParentField( m_aWidgets[it->second] ); + rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject ); + rParentField.m_aKidsIndex.push_back( nNewWidget ); + m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject; + } + } + } + } + else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy ) + { + // this is invalid, someone tries to have a terminal field as parent + // example: a button with the name foo.bar exists and + // another button is named foo.bar.no + // workaround: put the second terminal field as much up in the hierarchy as + // necessary to have a non-terminal field as parent (or none at all) + // since it->second already is terminal, we just need to use its parent + aDomain = OString(); + aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 ); + if( nLastTokenIndex > 0 ) + { + aDomain = aFullName.copy( 0, nLastTokenIndex-1 ); + OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() ); + aBuf.append( aDomain ); + aBuf.append( '.' ); + aBuf.append( aPartialName ); + aFullName = aBuf.makeStringAndClear(); + } + else + aFullName = aPartialName; + break; + } + } + } while( nTokenIndex != -1 ); + + // insert widget into its hierarchy field + if( aDomain.getLength() ) + { + boost::unordered_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain ); + if( it != m_aFieldNameMap.end() ) + { + OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" ); + if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) ) + { + m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject; + m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject); + m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex ); + } + } + } + + if( aPartialName.getLength() == 0 ) + { + // how funny, an empty field name + if( i_rControl.getType() == PDFWriter::RadioButton ) + { + aPartialName = "RadioGroup"; + aPartialName += OString::valueOf( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup ); + } + else + aPartialName = OString( "Widget" ); + } + + if( ! m_aContext.AllowDuplicateFieldNames ) + { + boost::unordered_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName ); + + if( it != m_aFieldNameMap.end() ) // not unique + { + boost::unordered_map< OString, sal_Int32, OStringHash >::const_iterator check_it; + OString aTry; + sal_Int32 nTry = 2; + do + { + OStringBuffer aUnique( aFullName.getLength() + 16 ); + aUnique.append( aFullName ); + aUnique.append( '_' ); + aUnique.append( nTry++ ); + aTry = aUnique.makeStringAndClear(); + check_it = m_aFieldNameMap.find( aTry ); + } while( check_it != m_aFieldNameMap.end() ); + aFullName = aTry; + m_aFieldNameMap[ aFullName ] = i_nWidgetIndex; + aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 ); + } + else + m_aFieldNameMap[ aFullName ] = i_nWidgetIndex; + } + + // finally + m_aWidgets[i_nWidgetIndex].m_aName = aPartialName; +} + +static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor ) +{ + if( nValue < 0 ) + { + rBuffer.append( '-' ); + nValue = -nValue; + } + sal_Int32 nFactor = 1, nDiv = nPrecision; + while( nDiv-- ) + nFactor *= 10; + + sal_Int32 nInt = nValue / nFactor; + rBuffer.append( nInt ); + if( nFactor > 1 ) + { + sal_Int32 nDecimal = nValue % nFactor; + if( nDecimal ) + { + rBuffer.append( '.' ); + // omit trailing zeros + while( (nDecimal % 10) == 0 ) + nDecimal /= 10; + rBuffer.append( nDecimal ); + } + } +} + + +// appends a double. PDF does not accept exponential format, only fixed point +static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 ) +{ + bool bNeg = false; + if( fValue < 0.0 ) + { + bNeg = true; + fValue=-fValue; + } + + sal_Int64 nInt = (sal_Int64)fValue; + fValue -= (double)nInt; + // optimizing hardware may lead to a value of 1.0 after the subtraction + if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision ) + { + nInt++; + fValue = 0.0; + } + sal_Int64 nFrac = 0; + if( fValue ) + { + fValue *= pow( 10.0, (double)nPrecision ); + nFrac = (sal_Int64)fValue; + } + if( bNeg && ( nInt || nFrac ) ) + rBuffer.append( '-' ); + rBuffer.append( nInt ); + if( nFrac ) + { + int i; + rBuffer.append( '.' ); + sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5); + for ( i = 0; ( i < nPrecision ) && nFrac; i++ ) + { + sal_Int64 nNumb = nFrac / nBound; + nFrac -= nNumb * nBound; + rBuffer.append( nNumb ); + nBound /= 10; + } + } +} + + +static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false ) +{ + + if( rColor != Color( COL_TRANSPARENT ) ) + { + if( bConvertToGrey ) + { + sal_uInt8 cByte = rColor.GetLuminance(); + appendDouble( (double)cByte / 255.0, rBuffer ); + } + else + { + appendDouble( (double)rColor.GetRed() / 255.0, rBuffer ); + rBuffer.append( ' ' ); + appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer ); + rBuffer.append( ' ' ); + appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer ); + } + } +} + +void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) +{ + if( rColor != Color( COL_TRANSPARENT ) ) + { + bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale; + appendColor( rColor, rBuffer, bGrey ); + rBuffer.append( bGrey ? " G" : " RG" ); + } +} + +void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) +{ + if( rColor != Color( COL_TRANSPARENT ) ) + { + bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale; + appendColor( rColor, rBuffer, bGrey ); + rBuffer.append( bGrey ? " g" : " rg" ); + } +} + +// matrix helper class +// TODO: use basegfx matrix class instead or derive from it +namespace vcl // TODO: use anonymous namespace to keep this class local +{ +/* for sparse matrices of the form (2D linear transformations) + * f[0] f[1] 0 + * f[2] f[3] 0 + * f[4] f[5] 1 + */ +class Matrix3 +{ + double f[6]; + + void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; } +public: + Matrix3(); + ~Matrix3() {} + + void skew( double alpha, double beta ); + void scale( double sx, double sy ); + void rotate( double angle ); + void translate( double tx, double ty ); + bool invert(); + + void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL ); + + Point transform( const Point& rPoint ) const; +}; +} + +Matrix3::Matrix3() +{ + // initialize to unity + f[0] = 1.0; + f[1] = 0.0; + f[2] = 0.0; + f[3] = 1.0; + f[4] = 0.0; + f[5] = 0.0; +} + +Point Matrix3::transform( const Point& rOrig ) const +{ + double x = (double)rOrig.X(), y = (double)rOrig.Y(); + return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) ); +} + +void Matrix3::skew( double alpha, double beta ) +{ + double fn[6]; + double tb = tan( beta ); + fn[0] = f[0] + f[2]*tb; + fn[1] = f[1]; + fn[2] = f[2] + f[3]*tb; + fn[3] = f[3]; + fn[4] = f[4] + f[5]*tb; + fn[5] = f[5]; + if( alpha != 0.0 ) + { + double ta = tan( alpha ); + fn[1] += f[0]*ta; + fn[3] += f[2]*ta; + fn[5] += f[4]*ta; + } + set( fn ); +} + +void Matrix3::scale( double sx, double sy ) +{ + double fn[6]; + fn[0] = sx*f[0]; + fn[1] = sy*f[1]; + fn[2] = sx*f[2]; + fn[3] = sy*f[3]; + fn[4] = sx*f[4]; + fn[5] = sy*f[5]; + set( fn ); +} + +void Matrix3::rotate( double angle ) +{ + double fn[6]; + double fSin = sin(angle); + double fCos = cos(angle); + fn[0] = f[0]*fCos - f[1]*fSin; + fn[1] = f[0]*fSin + f[1]*fCos; + fn[2] = f[2]*fCos - f[3]*fSin; + fn[3] = f[2]*fSin + f[3]*fCos; + fn[4] = f[4]*fCos - f[5]*fSin; + fn[5] = f[4]*fSin + f[5]*fCos; + set( fn ); +} + +void Matrix3::translate( double tx, double ty ) +{ + f[4] += tx; + f[5] += ty; +} + +bool Matrix3::invert() +{ + // short circuit trivial cases + if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 ) + { + f[4] = -f[4]; + f[5] = -f[5]; + return true; + } + + // check determinant + const double fDet = f[0]*f[3]-f[1]*f[2]; + if( fDet == 0.0 ) + return false; + + // invert the matrix + double fn[6]; + fn[0] = +f[3] / fDet; + fn[1] = -f[1] / fDet; + fn[2] = -f[2] / fDet; + fn[3] = +f[0] / fDet; + + // apply inversion to translation + fn[4] = -(f[4]*fn[0] + f[5]*fn[2]); + fn[5] = -(f[4]*fn[1] + f[5]*fn[3]); + + set( fn ); + return true; +} + +void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack ) +{ + appendDouble( f[0], rBuffer ); + rBuffer.append( ' ' ); + appendDouble( f[1], rBuffer ); + rBuffer.append( ' ' ); + appendDouble( f[2], rBuffer ); + rBuffer.append( ' ' ); + appendDouble( f[3], rBuffer ); + rBuffer.append( ' ' ); + rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack ); +} + +static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList ) +{ + if( rList.empty() ) + return; + rBuf.append( '/' ); + rBuf.append( pPrefix ); + rBuf.append( "<<" ); + int ni = 0; + for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it ) + { + if( it->first.getLength() && it->second > 0 ) + { + rBuf.append( '/' ); + rBuf.append( it->first ); + rBuf.append( ' ' ); + rBuf.append( it->second ); + rBuf.append( " 0 R" ); + if( ((++ni) & 7) == 0 ) + rBuf.append( '\n' ); + } + } + rBuf.append( ">>\n" ); +} + +void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject ) +{ + rBuf.append( "<</Font " ); + rBuf.append( nFontDictObject ); + rBuf.append( " 0 R\n" ); + appendResourceMap( rBuf, "XObject", m_aXObjects ); + appendResourceMap( rBuf, "ExtGState", m_aExtGStates ); + appendResourceMap( rBuf, "Shading", m_aShadings ); + appendResourceMap( rBuf, "Pattern", m_aPatterns ); + rBuf.append( "/ProcSet[/PDF/Text" ); + if( !m_aXObjects.empty() ) + rBuf.append( "/ImageC/ImageI/ImageB" ); + rBuf.append( "]\n>>\n" ); +}; + +PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ) + : + m_pWriter( pWriter ), + m_nPageWidth( nPageWidth ), + m_nPageHeight( nPageHeight ), + m_eOrientation( eOrientation ), + m_nPageObject( 0 ), // invalid object number + m_nPageIndex( -1 ), // invalid index + m_nStreamLengthObject( 0 ), + m_nBeginStreamPos( 0 ), + m_eTransition( PDFWriter::Regular ), + m_nTransTime( 0 ), + m_nDuration( 0 ), + m_bHasWidgets( false ) +{ + // object ref must be only ever updated in emit() + m_nPageObject = m_pWriter->createObject(); +} + +PDFWriterImpl::PDFPage::~PDFPage() +{ +} + +void PDFWriterImpl::PDFPage::beginStream() +{ +#if OSL_DEBUG_LEVEL > 1 + { + OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" ); + m_pWriter->emitComment( aLine.getStr() ); + } +#endif + m_aStreamObjects.push_back(m_pWriter->createObject()); + if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) ) + return; + + m_nStreamLengthObject = m_pWriter->createObject(); + // write content stream header + OStringBuffer aLine; + aLine.append( m_aStreamObjects.back() ); + aLine.append( " 0 obj\n<</Length " ); + aLine.append( m_nStreamLengthObject ); + aLine.append( " 0 R" ); +#if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION ) + aLine.append( "/Filter/FlateDecode" ); +#endif + aLine.append( ">>\nstream\n" ); + if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) ) + return; + if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) ) + { + osl_closeFile( m_pWriter->m_aFile ); + m_pWriter->m_bOpen = false; + } +#if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION ) + m_pWriter->beginCompression(); +#endif + m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() ); +} + +void PDFWriterImpl::PDFPage::endStream() +{ +#if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION ) + m_pWriter->endCompression(); +#endif + sal_uInt64 nEndStreamPos; + if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) ) + { + osl_closeFile( m_pWriter->m_aFile ); + m_pWriter->m_bOpen = false; + return; + } + m_pWriter->disableStreamEncryption(); + if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) + return; + // emit stream length object + if( ! m_pWriter->updateObject( m_nStreamLengthObject ) ) + return; + OStringBuffer aLine; + aLine.append( m_nStreamLengthObject ); + aLine.append( " 0 obj\n" ); + aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) ); + aLine.append( "\nendobj\n\n" ); + m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject ) +{ + // emit page object + if( ! m_pWriter->updateObject( m_nPageObject ) ) + return false; + OStringBuffer aLine; + + aLine.append( m_nPageObject ); + aLine.append( " 0 obj\n" + "<</Type/Page/Parent " ); + aLine.append( nParentObject ); + aLine.append( " 0 R" ); + aLine.append( "/Resources " ); + aLine.append( m_pWriter->getResourceDictObj() ); + aLine.append( " 0 R" ); + if( m_nPageWidth && m_nPageHeight ) + { + aLine.append( "/MediaBox[0 0 " ); + aLine.append( m_nPageWidth ); + aLine.append( ' ' ); + aLine.append( m_nPageHeight ); + aLine.append( "]" ); + } + switch( m_eOrientation ) + { + case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break; + case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break; + case PDFWriter::Portrait: aLine.append( "/Rotate 0\n" );break; + + case PDFWriter::Inherit: + default: + break; + } + int nAnnots = m_aAnnotations.size(); + if( nAnnots > 0 ) + { + aLine.append( "/Annots[\n" ); + for( int i = 0; i < nAnnots; i++ ) + { + aLine.append( m_aAnnotations[i] ); + aLine.append( " 0 R" ); + aLine.append( ((i+1)%15) ? " " : "\n" ); + } + aLine.append( "]\n" ); + } + if( m_aMCIDParents.size() > 0 ) + { + OStringBuffer aStructParents( 1024 ); + aStructParents.append( "[ " ); + int nParents = m_aMCIDParents.size(); + for( int i = 0; i < nParents; i++ ) + { + aStructParents.append( m_aMCIDParents[i] ); + aStructParents.append( " 0 R" ); + aStructParents.append( ((i%10) == 9) ? "\n" : " " ); + } + aStructParents.append( "]" ); + m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() ); + + aLine.append( "/StructParents " ); + aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) ); + aLine.append( "\n" ); + } + if( m_nDuration > 0 ) + { + aLine.append( "/Dur " ); + aLine.append( (sal_Int32)m_nDuration ); + aLine.append( "\n" ); + } + if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 ) + { + // transition duration + aLine.append( "/Trans<</D " ); + appendDouble( (double)m_nTransTime/1000.0, aLine, 3 ); + aLine.append( "\n" ); + const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL; + switch( m_eTransition ) + { + case PDFWriter::SplitHorizontalInward: + pStyle = "Split"; pDm = "H"; pM = "I"; break; + case PDFWriter::SplitHorizontalOutward: + pStyle = "Split"; pDm = "H"; pM = "O"; break; + case PDFWriter::SplitVerticalInward: + pStyle = "Split"; pDm = "V"; pM = "I"; break; + case PDFWriter::SplitVerticalOutward: + pStyle = "Split"; pDm = "V"; pM = "O"; break; + case PDFWriter::BlindsHorizontal: + pStyle = "Blinds"; pDm = "H"; break; + case PDFWriter::BlindsVertical: + pStyle = "Blinds"; pDm = "V"; break; + case PDFWriter::BoxInward: + pStyle = "Box"; pM = "I"; break; + case PDFWriter::BoxOutward: + pStyle = "Box"; pM = "O"; break; + case PDFWriter::WipeLeftToRight: + pStyle = "Wipe"; pDi = "0"; break; + case PDFWriter::WipeBottomToTop: + pStyle = "Wipe"; pDi = "90"; break; + case PDFWriter::WipeRightToLeft: + pStyle = "Wipe"; pDi = "180"; break; + case PDFWriter::WipeTopToBottom: + pStyle = "Wipe"; pDi = "270"; break; + case PDFWriter::Dissolve: + pStyle = "Dissolve"; break; + case PDFWriter::GlitterLeftToRight: + pStyle = "Glitter"; pDi = "0"; break; + case PDFWriter::GlitterTopToBottom: + pStyle = "Glitter"; pDi = "270"; break; + case PDFWriter::GlitterTopLeftToBottomRight: + pStyle = "Glitter"; pDi = "315"; break; + case PDFWriter::Regular: + break; + } + // transition style + if( pStyle ) + { + aLine.append( "/S/" ); + aLine.append( pStyle ); + aLine.append( "\n" ); + } + if( pDm ) + { + aLine.append( "/Dm/" ); + aLine.append( pDm ); + aLine.append( "\n" ); + } + if( pM ) + { + aLine.append( "/M/" ); + aLine.append( pM ); + aLine.append( "\n" ); + } + if( pDi ) + { + aLine.append( "/Di " ); + aLine.append( pDi ); + aLine.append( "\n" ); + } + aLine.append( ">>\n" ); + } + if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 ) + { + aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" ); + } + aLine.append( "/Contents" ); + unsigned int nStreamObjects = m_aStreamObjects.size(); + if( nStreamObjects > 1 ) + aLine.append( '[' ); + for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ ) + { + aLine.append( ' ' ); + aLine.append( m_aStreamObjects[i] ); + aLine.append( " 0 R" ); + } + if( nStreamObjects > 1 ) + aLine.append( ']' ); + aLine.append( ">>\nendobj\n\n" ); + return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +namespace vcl +{ +template < class GEOMETRY > +GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject ) +{ + GEOMETRY aPoint; + if ( MAP_PIXEL == _rSource.GetMapUnit() ) + { + aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest ); + } + else + { + aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest ); + } + return aPoint; +} +} + +void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const +{ + if( pOutPoint ) + { + Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, + m_pWriter->m_aMapMode, + m_pWriter->getReferenceDevice(), + rPoint ) ); + *pOutPoint = aPoint; + } + + Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, + m_pWriter->m_aMapMode, + m_pWriter->getReferenceDevice(), + rPoint ) ); + + sal_Int32 nValue = aPoint.X(); + if( bNeg ) + nValue = -nValue; + + appendFixedInt( nValue, rBuffer ); + + rBuffer.append( ' ' ); + + nValue = pointToPixel(getHeight()) - aPoint.Y(); + if( bNeg ) + nValue = -nValue; + + appendFixedInt( nValue, rBuffer ); +} + +void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const +{ + double fValue = pixelToPoint(rPoint.getX()); + + appendDouble( fValue, rBuffer, nLog10Divisor ); + + rBuffer.append( ' ' ); + + fValue = double(getHeight()) - pixelToPoint(rPoint.getY()); + + appendDouble( fValue, rBuffer, nLog10Divisor ); +} + +void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const +{ + appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer ); + rBuffer.append( ' ' ); + appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false ); + rBuffer.append( ' ' ); + appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true ); + rBuffer.append( " re" ); +} + +void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const +{ + Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, + m_pWriter->m_aMapMode, + m_pWriter->getReferenceDevice(), + rRect.BottomLeft() + Point( 0, 1 ) + ); + Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, + m_pWriter->m_aMapMode, + m_pWriter->getReferenceDevice(), + rRect.GetSize() ); + rRect.Left() = aLL.X(); + rRect.Right() = aLL.X() + aSize.Width(); + rRect.Top() = pointToPixel(getHeight()) - aLL.Y(); + rRect.Bottom() = rRect.Top() + aSize.Height(); +} + +void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const +{ + sal_uInt16 nPoints = rPoly.GetSize(); + /* + * #108582# applications do weird things + */ + sal_uInt32 nBufLen = rBuffer.getLength(); + if( nPoints > 0 ) + { + const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry(); + appendPoint( rPoly[0], rBuffer ); + rBuffer.append( " m\n" ); + for( sal_uInt16 i = 1; i < nPoints; i++ ) + { + if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 ) + { + // bezier + DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" ); + appendPoint( rPoly[i], rBuffer ); + rBuffer.append( " " ); + appendPoint( rPoly[i+1], rBuffer ); + rBuffer.append( " " ); + appendPoint( rPoly[i+2], rBuffer ); + rBuffer.append( " c" ); + i += 2; // add additionally consumed points + } + else + { + // line + appendPoint( rPoly[i], rBuffer ); + rBuffer.append( " l" ); + } + if( (rBuffer.getLength() - nBufLen) > 65 ) + { + rBuffer.append( "\n" ); + nBufLen = rBuffer.getLength(); + } + else + rBuffer.append( " " ); + } + if( bClose ) + rBuffer.append( "h\n" ); + } +} + +void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const +{ + basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, + m_pWriter->m_aMapMode, + m_pWriter->getReferenceDevice(), + rPoly ) ); + + if( basegfx::tools::isRectangle( aPoly ) ) + { + basegfx::B2DRange aRange( aPoly.getB2DRange() ); + basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() ); + appendPixelPoint( aBL, rBuffer ); + rBuffer.append( ' ' ); + appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor ); + rBuffer.append( ' ' ); + appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor ); + rBuffer.append( " re\n" ); + return; + } + sal_uInt32 nPoints = aPoly.count(); + if( nPoints > 0 ) + { + sal_uInt32 nBufLen = rBuffer.getLength(); + basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) ); + appendPixelPoint( aLastPoint, rBuffer ); + rBuffer.append( " m\n" ); + for( sal_uInt32 i = 1; i <= nPoints; i++ ) + { + if( i != nPoints || aPoly.isClosed() ) + { + sal_uInt32 nCurPoint = i % nPoints; + sal_uInt32 nLastPoint = i-1; + basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) ); + if( aPoly.isNextControlPointUsed( nLastPoint ) && + aPoly.isPrevControlPointUsed( nCurPoint ) ) + { + appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); + rBuffer.append( ' ' ); + appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); + rBuffer.append( ' ' ); + appendPixelPoint( aPoint, rBuffer ); + rBuffer.append( " c" ); + } + else if( aPoly.isNextControlPointUsed( nLastPoint ) ) + { + appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); + rBuffer.append( ' ' ); + appendPixelPoint( aPoint, rBuffer ); + rBuffer.append( " y" ); + } + else if( aPoly.isPrevControlPointUsed( nCurPoint ) ) + { + appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); + rBuffer.append( ' ' ); + appendPixelPoint( aPoint, rBuffer ); + rBuffer.append( " v" ); + } + else + { + appendPixelPoint( aPoint, rBuffer ); + rBuffer.append( " l" ); + } + if( (rBuffer.getLength() - nBufLen) > 65 ) + { + rBuffer.append( "\n" ); + nBufLen = rBuffer.getLength(); + } + else + rBuffer.append( " " ); + } + } + if( bClose ) + rBuffer.append( "h\n" ); + } +} + +void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const +{ + sal_uInt16 nPolygons = rPolyPoly.Count(); + for( sal_uInt16 n = 0; n < nPolygons; n++ ) + appendPolygon( rPolyPoly[n], rBuffer, bClose ); +} + +void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const +{ + sal_uInt32 nPolygons = rPolyPoly.count(); + for( sal_uInt32 n = 0; n < nPolygons; n++ ) + appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose ); +} + +void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const +{ + sal_Int32 nValue = nLength; + if ( nLength < 0 ) + { + rBuffer.append( '-' ); + nValue = -nLength; + } + Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, + m_pWriter->m_aMapMode, + m_pWriter->getReferenceDevice(), + Size( nValue, nValue ) ) ); + nValue = bVertical ? aSize.Height() : aSize.Width(); + if( pOutLength ) + *pOutLength = ((nLength < 0 ) ? -nValue : nValue); + + appendFixedInt( nValue, rBuffer, 1 ); +} + +void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const +{ + Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, + m_pWriter->m_aMapMode, + m_pWriter->getReferenceDevice(), + Size( 1000, 1000 ) ) ); + if( pOutLength ) + *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0); + fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0); + appendDouble( fLength, rBuffer, nPrecision ); +} + +bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const +{ + bool bRet = true; + if( rInfo.GetStyle() == LINE_DASH ) + { + rBuffer.append( "[ " ); + if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case + { + appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer ); + rBuffer.append( ' ' ); + appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); + rBuffer.append( ' ' ); + } + else + { + // check for implementation limits of dash array + // in PDF reader apps (e.g. acroread) + if( 2*(rInfo.GetDashCount() + rInfo.GetDotCount()) > 10 ) + bRet = false; + for( int n = 0; n < rInfo.GetDashCount(); n++ ) + { + appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer ); + rBuffer.append( ' ' ); + appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); + rBuffer.append( ' ' ); + } + for( int m = 0; m < rInfo.GetDotCount(); m++ ) + { + appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer ); + rBuffer.append( ' ' ); + appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); + rBuffer.append( ' ' ); + } + } + rBuffer.append( "] 0 d\n" ); + } + if( rInfo.GetWidth() > 1 ) + { + appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer ); + rBuffer.append( " w\n" ); + } + else if( rInfo.GetWidth() == 0 ) + { + // "pixel" line + appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer ); + rBuffer.append( " w\n" ); + } + return bRet; +} + +void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const +{ + if( nWidth <= 0 ) + return; + if( nDelta < 1 ) + nDelta = 1; + + rBuffer.append( "0 " ); + appendMappedLength( nY, rBuffer, true ); + rBuffer.append( " m\n" ); + for( sal_Int32 n = 0; n < nWidth; ) + { + n += nDelta; + appendMappedLength( n, rBuffer, false ); + rBuffer.append( ' ' ); + appendMappedLength( nDelta+nY, rBuffer, true ); + rBuffer.append( ' ' ); + n += nDelta; + appendMappedLength( n, rBuffer, false ); + rBuffer.append( ' ' ); + appendMappedLength( nY, rBuffer, true ); + rBuffer.append( " v " ); + if( n < nWidth ) + { + n += nDelta; + appendMappedLength( n, rBuffer, false ); + rBuffer.append( ' ' ); + appendMappedLength( nY-nDelta, rBuffer, true ); + rBuffer.append( ' ' ); + n += nDelta; + appendMappedLength( n, rBuffer, false ); + rBuffer.append( ' ' ); + appendMappedLength( nY, rBuffer, true ); + rBuffer.append( " v\n" ); + } + } + rBuffer.append( "S\n" ); +} + +/* + * class PDFWriterImpl + */ + + PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, + const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc, + PDFWriter& i_rOuterFace) + : + m_pReferenceDevice( NULL ), + m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ), + m_nCurrentStructElement( 0 ), + m_bEmitStructure( true ), + m_bNewMCID( false ), + m_nCurrentControl( -1 ), + m_bEmbedStandardFonts( false ), + m_nNextFID( 1 ), + m_nInheritedPageWidth( 595 ), // default A4 + m_nInheritedPageHeight( 842 ), // default A4 + m_eInheritedOrientation( PDFWriter::Portrait ), + m_nCurrentPage( -1 ), + m_nResourceDict( -1 ), + m_nFontDictObject( -1 ), + m_pCodec( NULL ), + m_aDocDigest( rtl_digest_createMD5() ), + m_aCipher( (rtlCipher)NULL ), + m_aDigest( NULL ), + m_bEncryptThisStream( false ), + m_pEncryptionBuffer( NULL ), + m_nEncryptionBufferSize( 0 ), + m_bIsPDF_A1( false ), + m_rOuterFace( i_rOuterFace ) +{ +#ifdef DO_TEST_PDF + static bool bOnce = true; + if( bOnce ) + { + bOnce = false; + doTestCode(); + } +#endif + m_aContext = rContext; + m_aStructure.push_back( PDFStructureElement() ); + m_aStructure[0].m_nOwnElement = 0; + m_aStructure[0].m_nParentElement = 0; + + Font aFont; + aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) ); + aFont.SetSize( Size( 0, 12 ) ); + + GraphicsState aState; + aState.m_aMapMode = m_aMapMode; + aState.m_aFont = aFont; + m_aGraphicsStack.push_front( aState ); + + oslFileError aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ); + if( aError != osl_File_E_None ) + { + if( aError == osl_File_E_EXIST ) + { + aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write ); + if( aError == osl_File_E_None ) + aError = osl_setFileSize( m_aFile, 0 ); + } + } + if( aError != osl_File_E_None ) + return; + + m_bOpen = true; + + // setup DocInfo + setupDocInfo(); + + /* prepare the cypher engine, can be done in CTOR, free in DTOR */ + m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); + m_aDigest = rtl_digest_createMD5(); + + /* the size of the Codec default maximum */ + checkEncryptionBufferSize( 0x4000 ); + + if( xEnc.is() ) + prepareEncryption( xEnc ); + + if( m_aContext.Encryption.Encrypt() ) + { + // sanity check + if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE || + m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE || + m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH + ) + { + // the field lengths are invalid ? This was not setup by initEncryption. + // do not encrypt after all + m_aContext.Encryption.OValue.clear(); + m_aContext.Encryption.UValue.clear(); + OSL_ENSURE( 0, "encryption data failed sanity check, encryption disabled" ); + } + else // setup key lengths + m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength ); + } + + // write header + OStringBuffer aBuffer( 20 ); + aBuffer.append( "%PDF-" ); + switch( m_aContext.Version ) + { + case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break; + case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break; + case PDFWriter::PDF_A_1: + default: + case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break; + case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break; + } + // append something binary as comment (suggested in PDF Reference) + aBuffer.append( "\n%äüöß\n" ); + if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) ) + { + osl_closeFile( m_aFile ); + m_bOpen = false; + return; + } + + // insert outline root + m_aOutline.push_back( PDFOutlineEntry() ); + + m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1); + if( m_bIsPDF_A1 ) + m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour + + m_bEmbedStandardFonts = m_aContext.EmbedStandardFonts; +} + +PDFWriterImpl::~PDFWriterImpl() +{ + if( m_aDocDigest ) + rtl_digest_destroyMD5( m_aDocDigest ); + delete static_cast<VirtualDevice*>(m_pReferenceDevice); + + if( m_aCipher ) + rtl_cipher_destroyARCFOUR( m_aCipher ); + if( m_aDigest ) + rtl_digest_destroyMD5( m_aDigest ); + + rtl_freeMemory( m_pEncryptionBuffer ); +} + +void PDFWriterImpl::setupDocInfo() +{ + std::vector< sal_uInt8 > aId; + computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString ); + if( m_aContext.Encryption.DocumentIdentifier.empty() ) + m_aContext.Encryption.DocumentIdentifier = aId; +} + +void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier, + const vcl::PDFWriter::PDFDocInfo& i_rDocInfo, + rtl::OString& o_rCString1, + rtl::OString& o_rCString2 + ) +{ + o_rIdentifier.clear(); + + //build the document id + rtl::OString aInfoValuesOut; + OStringBuffer aID( 1024 ); + if( i_rDocInfo.Title.Len() ) + appendUnicodeTextString( i_rDocInfo.Title, aID ); + if( i_rDocInfo.Author.Len() ) + appendUnicodeTextString( i_rDocInfo.Author, aID ); + if( i_rDocInfo.Subject.Len() ) + appendUnicodeTextString( i_rDocInfo.Subject, aID ); + if( i_rDocInfo.Keywords.Len() ) + appendUnicodeTextString( i_rDocInfo.Keywords, aID ); + if( i_rDocInfo.Creator.Len() ) + appendUnicodeTextString( i_rDocInfo.Creator, aID ); + if( i_rDocInfo.Producer.Len() ) + appendUnicodeTextString( i_rDocInfo.Producer, aID ); + + TimeValue aTVal, aGMT; + oslDateTime aDT; + osl_getSystemTime( &aGMT ); + osl_getLocalTimeFromSystemTime( &aGMT, &aTVal ); + osl_getDateTimeFromTimeValue( &aTVal, &aDT ); + rtl::OStringBuffer aCreationDateString(64), aCreationMetaDateString(64); + aCreationDateString.append( "D:" ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); + + //--> i59651, we fill the Metadata date string as well, if PDF/A is requested + // according to ISO 19005-1:2005 6.7.3 the date is corrected for + // local time zone offset UTC only, whereas Acrobat 8 seems + // to use the localtime notation only + // according to a raccomandation in XMP Specification (Jan 2004, page 75) + // the Acrobat way seems the right approach + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); + aCreationMetaDateString.append( "-" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); + aCreationMetaDateString.append( "-" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); + aCreationMetaDateString.append( "T" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); + aCreationMetaDateString.append( ":" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); + aCreationMetaDateString.append( ":" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); + + sal_uInt32 nDelta = 0; + if( aGMT.Seconds > aTVal.Seconds ) + { + aCreationDateString.append( "-" ); + nDelta = aGMT.Seconds-aTVal.Seconds; + aCreationMetaDateString.append( "-" ); + } + else if( aGMT.Seconds < aTVal.Seconds ) + { + aCreationDateString.append( "+" ); + nDelta = aTVal.Seconds-aGMT.Seconds; + aCreationMetaDateString.append( "+" ); + } + else + { + aCreationDateString.append( "Z" ); + aCreationMetaDateString.append( "Z" ); + + } + if( nDelta ) + { + aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); + aCreationDateString.append( "'" ); + aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); + aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); + + aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); + aCreationMetaDateString.append( ":" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); + } + aCreationDateString.append( "'" ); + aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() ); + + aInfoValuesOut = aID.makeStringAndClear(); + o_rCString1 = aCreationDateString.makeStringAndClear(); + o_rCString2 = aCreationMetaDateString.makeStringAndClear(); + + rtlDigest aDigest = rtl_digest_createMD5(); + OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" ); + if( aDigest ) + { + rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) ); + if( nError == rtl_Digest_E_None ) + nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() ); + if( nError == rtl_Digest_E_None ) + { + o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 ); + //the binary form of the doc id is needed for encryption stuff + rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 ); + } + } +} + +/* i12626 methods */ +/* +check if the Unicode string must be encrypted or not, perform the requested task, +append the string as unicode hex, encrypted if needed + */ +inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer ) +{ + rOutBuffer.append( "<" ); + if( m_aContext.Encryption.Encrypt() ) + { + const sal_Unicode* pStr = rInString.getStr(); + sal_Int32 nLen = rInString.getLength(); +//prepare a unicode string, encrypt it + if( checkEncryptionBufferSize( nLen*2 ) ) + { + enableStringEncryption( nInObjectNumber ); + register sal_uInt8 *pCopy = m_pEncryptionBuffer; + sal_Int32 nChars = 2; + *pCopy++ = 0xFE; + *pCopy++ = 0xFF; +// we need to prepare a byte stream from the unicode string buffer + for( register int i = 0; i < nLen; i++ ) + { + register sal_Unicode aUnChar = pStr[i]; + *pCopy++ = (sal_uInt8)( aUnChar >> 8 ); + *pCopy++ = (sal_uInt8)( aUnChar & 255 ); + nChars += 2; + } +//encrypt in place + rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars ); +//now append, hexadecimal (appendHex), the encrypted result + for(register int i = 0; i < nChars; i++) + appendHex( m_pEncryptionBuffer[i], rOutBuffer ); + } + } + else + appendUnicodeTextString( rInString, rOutBuffer ); + rOutBuffer.append( ">" ); +} + +inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ) +{ + rOutBuffer.append( "(" ); + sal_Int32 nChars = rInString.getLength(); +//check for encryption, if ok, encrypt the string, then convert with appndLiteralString + if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) ) + { +//encrypt the string in a buffer, then append it + enableStringEncryption( nInObjectNumber ); + rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars ); + appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer ); + } + else + appendLiteralString( rInString.getStr(), nChars , rOutBuffer ); + rOutBuffer.append( ")" ); +} + +inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ) +{ + rtl::OStringBuffer aBufferString( rInString ); + appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer); +} + +void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc ) +{ + rtl::OString aBufferString( rtl::OUStringToOString( rInString, nEnc ) ); + sal_Int32 nLen = aBufferString.getLength(); + rtl::OStringBuffer aBuf( nLen ); + const sal_Char* pT = aBufferString.getStr(); + + for( sal_Int32 i = 0; i < nLen; i++, pT++ ) + { + if( (*pT & 0x80) == 0 ) + aBuf.append( *pT ); + else + { + aBuf.append( '<' ); + appendHex( *pT, aBuf ); + aBuf.append( '>' ); + } + } + aBufferString = aBuf.makeStringAndClear(); + appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer); +} + +/* end i12626 methods */ + +void PDFWriterImpl::emitComment( const char* pComment ) +{ + OStringBuffer aLine( 64 ); + aLine.append( "% " ); + aLine.append( (const sal_Char*)pComment ); + aLine.append( "\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +bool PDFWriterImpl::compressStream( SvMemoryStream* pStream ) +{ +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + pStream->Seek( STREAM_SEEK_TO_END ); + sal_uLong nEndPos = pStream->Tell(); + pStream->Seek( STREAM_SEEK_TO_BEGIN ); + ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 ); + SvMemoryStream aStream; + pCodec->BeginCompression(); + pCodec->Write( aStream, (const sal_uInt8*)pStream->GetData(), nEndPos ); + pCodec->EndCompression(); + delete pCodec; + nEndPos = aStream.Tell(); + pStream->Seek( STREAM_SEEK_TO_BEGIN ); + aStream.Seek( STREAM_SEEK_TO_BEGIN ); + pStream->SetStreamSize( nEndPos ); + pStream->Write( aStream.GetData(), nEndPos ); + return true; +#else + (void)pStream; + return false; +#endif +} + +void PDFWriterImpl::beginCompression() +{ +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + m_pCodec = new ZCodec( 0x4000, 0x4000 ); + m_pMemStream = new SvMemoryStream(); + m_pCodec->BeginCompression(); +#endif +} + +void PDFWriterImpl::endCompression() +{ +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + if( m_pCodec ) + { + m_pCodec->EndCompression(); + delete m_pCodec; + m_pCodec = NULL; + sal_uInt64 nLen = m_pMemStream->Tell(); + m_pMemStream->Seek( 0 ); + writeBuffer( m_pMemStream->GetData(), nLen ); + delete m_pMemStream; + m_pMemStream = NULL; + } +#endif +} + +bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes ) +{ + if( ! m_bOpen ) // we are already down the drain + return false; + + if( ! nBytes ) // huh ? + return true; + + if( m_aOutputStreams.begin() != m_aOutputStreams.end() ) + { + m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END ); + m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) ); + return true; + } + + sal_uInt64 nWritten; + if( m_pCodec ) + { + m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes ); + nWritten = nBytes; + } + else + { + sal_Bool buffOK = sal_True; + if( m_bEncryptThisStream ) + { +/* implement the encryption part of the PDF spec encryption algorithm 3.1 */ + if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) != sal_False ) + rtl_cipher_encodeARCFOUR( m_aCipher, + (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes), + m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) ); + } + + const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer; + if( m_aDocDigest ) + rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) ); + + if( osl_writeFile( m_aFile, + pWriteBuffer, + nBytes, &nWritten ) != osl_File_E_None ) + nWritten = 0; + + if( nWritten != nBytes ) + { + osl_closeFile( m_aFile ); + m_bOpen = false; + } + } + + return nWritten == nBytes; +} + +OutputDevice* PDFWriterImpl::getReferenceDevice() +{ + if( ! m_pReferenceDevice ) + { + VirtualDevice* pVDev = new VirtualDevice( 0 ); + + m_pReferenceDevice = pVDev; + + if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 ) + pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 ); + else + pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy ); + + pVDev->SetOutputSizePixel( Size( 640, 480 ) ); + pVDev->SetMapMode( MAP_MM ); + + m_pReferenceDevice->mpPDFWriter = this; + m_pReferenceDevice->ImplUpdateFontData( sal_True ); + } + return m_pReferenceDevice; +} + +class ImplPdfBuiltinFontData : public ImplFontData +{ +private: + const PDFWriterImpl::BuiltinFont& mrBuiltin; + +public: + enum {PDF_FONT_MAGIC = 0xBDFF0A1C }; + ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& ); + const PDFWriterImpl::BuiltinFont* GetBuiltinFont() const { return &mrBuiltin; } + + virtual ImplFontData* Clone() const { return new ImplPdfBuiltinFontData(*this); } + virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const; + virtual sal_IntPtr GetFontId() const { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); } +}; + +inline const ImplPdfBuiltinFontData* GetPdfFontData( const ImplFontData* pFontData ) +{ + const ImplPdfBuiltinFontData* pFD = NULL; + if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) ) + pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData ); + return pFD; +} + +static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin ) +{ + ImplDevFontAttributes aDFA; + aDFA.maName = String::CreateFromAscii( rBuiltin.m_pName ); + aDFA.maStyleName = String::CreateFromAscii( rBuiltin.m_pStyleName ); + aDFA.meFamily = rBuiltin.m_eFamily; + aDFA.mbSymbolFlag = (rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 ); + aDFA.mePitch = rBuiltin.m_ePitch; + aDFA.meWeight = rBuiltin.m_eWeight; + aDFA.meItalic = rBuiltin.m_eItalic; + aDFA.meWidthType = rBuiltin.m_eWidthType; + + aDFA.mbOrientation = true; + aDFA.mbDevice = true; + aDFA.mnQuality = 50000; + aDFA.mbSubsettable = false; + aDFA.mbEmbeddable = false; + return aDFA; +} + +ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin ) +: ImplFontData( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ), + mrBuiltin( rBuiltin ) +{} + +ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const +{ + ImplFontEntry* pEntry = new ImplFontEntry( rFSD ); + return pEntry; +} + +ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList ) +{ + DBG_ASSERT( m_aSubsets.size() == 0, "Fonts changing during PDF generation, document will be invalid" ); + ImplDevFontList* pFiltered = pFontList->Clone( true, true ); + + // append the PDF builtin fonts + if( !m_bIsPDF_A1 && !m_bEmbedStandardFonts) + for( unsigned int i = 0; i < SAL_N_ELEMENTS(m_aBuiltinFonts); i++ ) + { + ImplFontData* pNewData = new ImplPdfBuiltinFontData( m_aBuiltinFonts[i] ); + pFiltered->Add( pNewData ); + } + return pFiltered; +} + +bool PDFWriterImpl::isBuiltinFont( const ImplFontData* pFont ) const +{ + const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont ); + return (pFD != NULL); +} + +void PDFWriterImpl::getFontMetric( ImplFontSelectData* pSelect, ImplFontMetricData* pMetric ) const +{ + const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData ); + if( !pFD ) + return; + const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); + + pMetric->mnOrientation = sal::static_int_cast<short>(pSelect->mnOrientation); + pMetric->meFamily = pBuiltinFont->m_eFamily; + pMetric->mePitch = pBuiltinFont->m_ePitch; + pMetric->meWeight = pBuiltinFont->m_eWeight; + pMetric->meItalic = pBuiltinFont->m_eItalic; + pMetric->mbSymbolFlag = pFD->IsSymbolFont(); + pMetric->mnWidth = pSelect->mnHeight; + pMetric->mnAscent = ( pSelect->mnHeight * +pBuiltinFont->m_nAscent + 500 ) / 1000; + pMetric->mnDescent = ( pSelect->mnHeight * -pBuiltinFont->m_nDescent + 500 ) / 1000; + pMetric->mnIntLeading = 0; + pMetric->mnExtLeading = 0; + pMetric->mnSlant = 0; + pMetric->mbScalableFont = true; + pMetric->mbDevice = true; +} + +// ----------------------------------------------------------------------- + +namespace vcl { + +class PDFSalLayout : public GenericSalLayout +{ + PDFWriterImpl& mrPDFWriterImpl; + const PDFWriterImpl::BuiltinFont& mrBuiltinFont; + bool mbIsSymbolFont; + long mnPixelPerEM; + String maOrigText; + +public: + PDFSalLayout( PDFWriterImpl&, + const PDFWriterImpl::BuiltinFont&, + long nPixelPerEM, int nOrientation ); + + void SetText( const String& rText ) { maOrigText = rText; } + virtual bool LayoutText( ImplLayoutArgs& ); + virtual void InitFont() const; + virtual void DrawText( SalGraphics& ) const; +}; + +} + +// ----------------------------------------------------------------------- + +PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl, + const PDFWriterImpl::BuiltinFont& rBuiltinFont, + long nPixelPerEM, int nOrientation ) +: mrPDFWriterImpl( rPDFWriterImpl ), + mrBuiltinFont( rBuiltinFont ), + mnPixelPerEM( nPixelPerEM ) +{ + mbIsSymbolFont = (rBuiltinFont.m_eCharSet != RTL_TEXTENCODING_MS_1252); + SetOrientation( nOrientation ); +} + +// ----------------------------------------------------------------------- + +bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs ) +{ + const String aText( rArgs.mpStr+rArgs.mnMinCharPos, sal::static_int_cast<xub_StrLen>(rArgs.mnEndCharPos-rArgs.mnMinCharPos) ); + SetText( aText ); + SetUnitsPerPixel( 1000 ); + + rtl_UnicodeToTextConverter aConv = rtl_createTextToUnicodeConverter( mrBuiltinFont.m_eCharSet ); + + Point aNewPos( 0, 0 ); + bool bRightToLeft; + for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); ) + { + // TODO: handle unicode surrogates + // on the other hand the PDF builtin fonts don't support them anyway + sal_Unicode cChar = rArgs.mpStr[ nCharPos ]; + if( bRightToLeft ) + cChar = static_cast<sal_Unicode>(GetMirroredChar( cChar )); + + if( 1 ) // TODO: shortcut for ASCII? + { + sal_Char aBuf[4]; + sal_uInt32 nInfo; + sal_Size nSrcCvtChars; + + sal_Size nConv = rtl_convertUnicodeToText( aConv, + NULL, + &cChar, 1, + aBuf, sizeof(aBuf)/sizeof(*aBuf), + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR, + &nInfo, &nSrcCvtChars ); + // check whether conversion was possible + // else fallback font is needed as the standard fonts + // are handled via WinAnsi encoding + if( nConv > 0 ) + cChar = ((sal_Unicode)aBuf[0]) & 0x00ff; + } + if( cChar & 0xff00 ) + { + cChar = 0; // NotDef glyph + rArgs.NeedFallback( nCharPos, bRightToLeft ); + } + + long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM; + long nGlyphFlags = 0; // builtin fonts don't have diacritic glyphs + if( bRightToLeft ) + nGlyphFlags |= GlyphItem::IS_RTL_GLYPH; + // TODO: get kerning from builtin fonts + GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth ); + AppendGlyph( aGI ); + + aNewPos.X() += nGlyphWidth; + } + + rtl_destroyUnicodeToTextConverter( aConv ); + + return true; +} + +// ----------------------------------------------------------------------- + +void PDFSalLayout::InitFont() const +{ + // TODO: recreate font with all its attributes +} + +// ----------------------------------------------------------------------- + +void PDFSalLayout::DrawText( SalGraphics& ) const +{ + mrPDFWriterImpl.drawLayout( *const_cast<PDFSalLayout*>(this), maOrigText, true ); +} + +// ----------------------------------------------------------------------- + +SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pSelect ) +{ + DBG_ASSERT( (pSelect->mpFontData != NULL), + "PDFWriterImpl::GetTextLayout mpFontData is NULL" ); + + const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData ); + if( !pFD ) + return NULL; + const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); + + long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight; + int nOrientation = pSelect->mnOrientation; + PDFSalLayout* pLayout = new PDFSalLayout( *this, *pBuiltinFont, nPixelPerEM, nOrientation ); + pLayout->SetText( rArgs.mpStr ); + return pLayout; +} + +sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ) +{ + endPage(); + m_nCurrentPage = m_aPages.size(); + m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) ); + m_aPages.back().m_nPageIndex = m_nCurrentPage; + m_aPages.back().beginStream(); + + // setup global graphics state + // linewidth is "1 pixel" by default + OStringBuffer aBuf( 16 ); + appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf ); + aBuf.append( " w\n" ); + writeBuffer( aBuf.getStr(), aBuf.getLength() ); + + return m_nCurrentPage; +} + +void PDFWriterImpl::endPage() +{ + if( m_aPages.begin() != m_aPages.end() ) + { + // close eventual MC sequence + endStructureElementMCSeq(); + + // sanity check + if( m_aOutputStreams.begin() != m_aOutputStreams.end() ) + { + OSL_FAIL( "redirection across pages !!!" ); + m_aOutputStreams.clear(); // leak ! + m_aMapMode.SetOrigin( Point() ); + } + + m_aGraphicsStack.clear(); + m_aGraphicsStack.push_back( GraphicsState() ); + + // this should pop the PDF graphics stack if necessary + updateGraphicsState(); + + m_aPages.back().endStream(); + + // reset the default font + Font aFont; + aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) ); + aFont.SetSize( Size( 0, 12 ) ); + + m_aCurrentPDFState = m_aGraphicsStack.front(); + m_aGraphicsStack.front().m_aFont = aFont; + + for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin(); + it != m_aBitmaps.end(); ++it ) + { + if( ! it->m_aBitmap.IsEmpty() ) + { + writeBitmapObject( *it ); + it->m_aBitmap = BitmapEx(); + } + } + for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg ) + { + if( jpeg->m_pStream ) + { + writeJPG( *jpeg ); + delete jpeg->m_pStream; + jpeg->m_pStream = NULL; + jpeg->m_aMask = Bitmap(); + } + } + for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin(); + t != m_aTransparentObjects.end(); ++t ) + { + if( t->m_pContentStream ) + { + writeTransparentObject( *t ); + delete t->m_pContentStream; + t->m_pContentStream = NULL; + } + } + } +} + +sal_Int32 PDFWriterImpl::createObject() +{ + m_aObjects.push_back( ~0U ); + return m_aObjects.size(); +} + +bool PDFWriterImpl::updateObject( sal_Int32 n ) +{ + if( ! m_bOpen ) + return false; + + sal_uInt64 nOffset = ~0U; + oslFileError aError = osl_getFilePos( m_aFile, &nOffset ); + DBG_ASSERT( aError == osl_File_E_None, "could not register object" ); + if( aError != osl_File_E_None ) + { + osl_closeFile( m_aFile ); + m_bOpen = false; + } + m_aObjects[ n-1 ] = nOffset; + return aError == osl_File_E_None; +} + +#define CHECK_RETURN( x ) if( !(x) ) return 0 + +sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject ) +{ + if( nObject > 0 ) + { + OStringBuffer aLine( 1024 ); + + aLine.append( nObject ); + aLine.append( " 0 obj\n" + "<</Nums[\n" ); + sal_Int32 nTreeItems = m_aStructParentTree.size(); + for( sal_Int32 n = 0; n < nTreeItems; n++ ) + { + aLine.append( n ); + aLine.append( ' ' ); + aLine.append( m_aStructParentTree[n] ); + aLine.append( "\n" ); + } + aLine.append( "]>>\nendobj\n\n" ); + CHECK_RETURN( updateObject( nObject ) ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + } + return nObject; +} + +const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr ) +{ + static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings; + // fill maps once + if( aAttributeStrings.empty() ) + { + aAttributeStrings[ PDFWriter::Placement ] = "Placement"; + aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode"; + aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore"; + aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter"; + aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent"; + aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent"; + aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent"; + aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign"; + aAttributeStrings[ PDFWriter::Width ] = "Width"; + aAttributeStrings[ PDFWriter::Height ] = "Height"; + aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign"; + aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign"; + aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight"; + aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift"; + aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType"; + aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering"; + aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan"; + aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan"; + aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation"; + } + + std::map< PDFWriter::StructAttribute, const char* >::const_iterator it = + aAttributeStrings.find( eAttr ); + +#if OSL_DEBUG_LEVEL > 1 + if( it == aAttributeStrings.end() ) + fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr ); +#endif + + return it != aAttributeStrings.end() ? it->second : ""; +} + +const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal ) +{ + static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings; + + if( aValueStrings.empty() ) + { + aValueStrings[ PDFWriter::NONE ] = "None"; + aValueStrings[ PDFWriter::Block ] = "Block"; + aValueStrings[ PDFWriter::Inline ] = "Inline"; + aValueStrings[ PDFWriter::Before ] = "Before"; + aValueStrings[ PDFWriter::After ] = "After"; + aValueStrings[ PDFWriter::Start ] = "Start"; + aValueStrings[ PDFWriter::End ] = "End"; + aValueStrings[ PDFWriter::LrTb ] = "LrTb"; + aValueStrings[ PDFWriter::RlTb ] = "RlTb"; + aValueStrings[ PDFWriter::TbRl ] = "TbRl"; + aValueStrings[ PDFWriter::Center ] = "Center"; + aValueStrings[ PDFWriter::Justify ] = "Justify"; + aValueStrings[ PDFWriter::Auto ] = "Auto"; + aValueStrings[ PDFWriter::Middle ] = "Middle"; + aValueStrings[ PDFWriter::Normal ] = "Normal"; + aValueStrings[ PDFWriter::Underline ] = "Underline"; + aValueStrings[ PDFWriter::Overline ] = "Overline"; + aValueStrings[ PDFWriter::LineThrough ] = "LineThrough"; + aValueStrings[ PDFWriter::Disc ] = "Disc"; + aValueStrings[ PDFWriter::Circle ] = "Circle"; + aValueStrings[ PDFWriter::Square ] = "Square"; + aValueStrings[ PDFWriter::Decimal ] = "Decimal"; + aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman"; + aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman"; + aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha"; + aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha"; + } + + std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it = + aValueStrings.find( eVal ); + +#if OSL_DEBUG_LEVEL > 1 + if( it == aValueStrings.end() ) + fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal ); +#endif + + return it != aValueStrings.end() ? it->second : ""; +} + +static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt ) +{ + o_rLine.append( "/" ); + o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) ); + + if( i_rVal.eValue != PDFWriter::Invalid ) + { + o_rLine.append( "/" ); + o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) ); + } + else + { + // numerical value + o_rLine.append( " " ); + if( i_bIsFixedInt ) + appendFixedInt( i_rVal.nValue, o_rLine ); + else + o_rLine.append( i_rVal.nValue ); + } + o_rLine.append( "\n" ); +} + +OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle ) +{ + // create layout, list and table attribute sets + OStringBuffer aLayout(256), aList(64), aTable(64); + for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin(); + it != i_rEle.m_aAttributes.end(); ++it ) + { + if( it->first == PDFWriter::ListNumbering ) + appendStructureAttributeLine( it->first, it->second, aList, true ); + else if( it->first == PDFWriter::RowSpan || + it->first == PDFWriter::ColSpan ) + appendStructureAttributeLine( it->first, it->second, aTable, false ); + else if( it->first == PDFWriter::LinkAnnotation ) + { + sal_Int32 nLink = it->second.nValue; + std::map< sal_Int32, sal_Int32 >::const_iterator link_it = + m_aLinkPropertyMap.find( nLink ); + if( link_it != m_aLinkPropertyMap.end() ) + nLink = link_it->second; + if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() ) + { + // update struct parent of link + OStringBuffer aStructParentEntry( 32 ); + aStructParentEntry.append( i_rEle.m_nObject ); + aStructParentEntry.append( " 0 R" ); + m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() ); + m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1; + + sal_Int32 nRefObject = createObject(); + OStringBuffer aRef( 256 ); + aRef.append( nRefObject ); + aRef.append( " 0 obj\n" + "<</Type/OBJR/Obj " ); + aRef.append( m_aLinks[ nLink ].m_nObject ); + aRef.append( " 0 R>>\n" + "endobj\n\n" + ); + updateObject( nRefObject ); + writeBuffer( aRef.getStr(), aRef.getLength() ); + + i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) ); + } + else + { + OSL_FAIL( "unresolved link id for Link structure" ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink ); + { + OStringBuffer aLine( "unresolved link id " ); + aLine.append( nLink ); + aLine.append( " for Link structure" ); + emitComment( aLine.getStr() ); + } +#endif + } + } + else + appendStructureAttributeLine( it->first, it->second, aLayout, true ); + } + if( ! i_rEle.m_aBBox.IsEmpty() ) + { + aLayout.append( "/BBox[" ); + appendFixedInt( i_rEle.m_aBBox.Left(), aLayout ); + aLayout.append( " " ); + appendFixedInt( i_rEle.m_aBBox.Top(), aLayout ); + aLayout.append( " " ); + appendFixedInt( i_rEle.m_aBBox.Right(), aLayout ); + aLayout.append( " " ); + appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout ); + aLayout.append( "]\n" ); + } + + std::vector< sal_Int32 > aAttribObjects; + if( aLayout.getLength() ) + { + aAttribObjects.push_back( createObject() ); + updateObject( aAttribObjects.back() ); + OStringBuffer aObj( 64 ); + aObj.append( aAttribObjects.back() ); + aObj.append( " 0 obj\n" + "<</O/Layout\n" ); + aLayout.append( ">>\nendobj\n\n" ); + writeBuffer( aObj.getStr(), aObj.getLength() ); + writeBuffer( aLayout.getStr(), aLayout.getLength() ); + } + if( aList.getLength() ) + { + aAttribObjects.push_back( createObject() ); + updateObject( aAttribObjects.back() ); + OStringBuffer aObj( 64 ); + aObj.append( aAttribObjects.back() ); + aObj.append( " 0 obj\n" + "<</O/List\n" ); + aList.append( ">>\nendobj\n\n" ); + writeBuffer( aObj.getStr(), aObj.getLength() ); + writeBuffer( aList.getStr(), aList.getLength() ); + } + if( aTable.getLength() ) + { + aAttribObjects.push_back( createObject() ); + updateObject( aAttribObjects.back() ); + OStringBuffer aObj( 64 ); + aObj.append( aAttribObjects.back() ); + aObj.append( " 0 obj\n" + "<</O/Table\n" ); + aTable.append( ">>\nendobj\n\n" ); + writeBuffer( aObj.getStr(), aObj.getLength() ); + writeBuffer( aTable.getStr(), aTable.getLength() ); + } + + OStringBuffer aRet( 64 ); + if( aAttribObjects.size() > 1 ) + aRet.append( " [" ); + for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin(); + at_it != aAttribObjects.end(); ++at_it ) + { + aRet.append( " " ); + aRet.append( *at_it ); + aRet.append( " 0 R" ); + } + if( aAttribObjects.size() > 1 ) + aRet.append( " ]" ); + return aRet.makeStringAndClear(); +} + +sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle ) +{ + if( + // do not emit NonStruct and its children + rEle.m_eType == PDFWriter::NonStructElement && + rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root + ) + return 0; + + for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) + { + if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) + { + PDFStructureElement& rChild = m_aStructure[ *it ]; + if( rChild.m_eType != PDFWriter::NonStructElement ) + { + if( rChild.m_nParentElement == rEle.m_nOwnElement ) + emitStructure( rChild ); + else + { + OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it ); +#endif + } + } + } + else + { + OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it ); +#endif + } + } + + OStringBuffer aLine( 512 ); + aLine.append( rEle.m_nObject ); + aLine.append( " 0 obj\n" + "<</Type" ); + sal_Int32 nParentTree = -1; + if( rEle.m_nOwnElement == rEle.m_nParentElement ) + { + nParentTree = createObject(); + CHECK_RETURN( nParentTree ); + aLine.append( "/StructTreeRoot\n" ); + aLine.append( "/ParentTree " ); + aLine.append( nParentTree ); + aLine.append( " 0 R\n" ); + if( ! m_aRoleMap.empty() ) + { + aLine.append( "/RoleMap<<" ); + for( boost::unordered_map<OString,OString,OStringHash>::const_iterator + it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it ) + { + aLine.append( '/' ); + aLine.append(it->first); + aLine.append( '/' ); + aLine.append( it->second ); + aLine.append( '\n' ); + } + aLine.append( ">>\n" ); + } + } + else + { + aLine.append( "/StructElem\n" + "/S/" ); + if( rEle.m_aAlias.getLength() > 0 ) + aLine.append( rEle.m_aAlias ); + else + aLine.append( getStructureTag( rEle.m_eType ) ); + aLine.append( "\n" + "/P " ); + aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject ); + aLine.append( " 0 R\n" + "/Pg " ); + aLine.append( rEle.m_nFirstPageObject ); + aLine.append( " 0 R\n" ); + if( rEle.m_aActualText.getLength() ) + { + aLine.append( "/ActualText" ); + appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine ); + aLine.append( "\n" ); + } + if( rEle.m_aAltText.getLength() ) + { + aLine.append( "/Alt" ); + appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine ); + aLine.append( "\n" ); + } + } + if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() ) + { + OString aAttribs = emitStructureAttributes( rEle ); + if( aAttribs.getLength() ) + { + aLine.append( "/A" ); + aLine.append( aAttribs ); + aLine.append( "\n" ); + } + } + if( rEle.m_aLocale.Language.getLength() > 0 ) + { + OUStringBuffer aLocBuf( 16 ); + aLocBuf.append( rEle.m_aLocale.Language.toAsciiLowerCase() ); + if( rEle.m_aLocale.Country.getLength() > 0 ) + { + aLocBuf.append( sal_Unicode('-') ); + aLocBuf.append( rEle.m_aLocale.Country ); + } + aLine.append( "/Lang" ); + appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine ); + aLine.append( "\n" ); + } + if( ! rEle.m_aKids.empty() ) + { + unsigned int i = 0; + aLine.append( "/K[" ); + for( std::list< PDFStructureElementKid >::const_iterator it = + rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ ) + { + if( it->nMCID == -1 ) + { + aLine.append( it->nObject ); + aLine.append( " 0 R" ); + aLine.append( ( (i & 15) == 15 ) ? "\n" : " " ); + } + else + { + if( it->nObject == rEle.m_nFirstPageObject ) + { + aLine.append( it->nMCID ); + aLine.append( " " ); + } + else + { + aLine.append( "<</Type/MCR/Pg " ); + aLine.append( it->nObject ); + aLine.append( " 0 R /MCID " ); + aLine.append( it->nMCID ); + aLine.append( ">>\n" ); + } + } + } + aLine.append( "]\n" ); + } + aLine.append( ">>\nendobj\n\n" ); + + CHECK_RETURN( updateObject( rEle.m_nObject ) ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + CHECK_RETURN( emitStructParentTree( nParentTree ) ); + + return rEle.m_nObject; +} + +bool PDFWriterImpl::emitGradients() +{ + for( std::list<GradientEmit>::iterator it = m_aGradients.begin(); + it != m_aGradients.end(); ++it ) + { + CHECK_RETURN( writeGradientFunction( *it ) ); + } + return true; +} + +bool PDFWriterImpl::emitTilings() +{ + OStringBuffer aTilingObj( 1024 ); + + for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it ) + { + DBG_ASSERT( it->m_pTilingStream, "tiling without stream" ); + if( ! it->m_pTilingStream ) + continue; + + aTilingObj.setLength( 0 ); + + #if OSL_DEBUG_LEVEL > 1 + emitComment( "PDFWriterImpl::emitTilings" ); + #endif + + sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left(); + sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top(); + sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth(); + sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight(); + if( it->m_aCellSize.Width() == 0 ) + it->m_aCellSize.Width() = nW; + if( it->m_aCellSize.Height() == 0 ) + it->m_aCellSize.Height() = nH; + + bool bDeflate = compressStream( it->m_pTilingStream ); + it->m_pTilingStream->Seek( STREAM_SEEK_TO_END ); + sal_Size nTilingStreamSize = it->m_pTilingStream->Tell(); + it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN ); + + // write pattern object + aTilingObj.append( it->m_nObject ); + aTilingObj.append( " 0 obj\n" ); + aTilingObj.append( "<</Type/Pattern/PatternType 1\n" + "/PaintType 1\n" + "/TilingType 2\n" + "/BBox[" ); + appendFixedInt( nX, aTilingObj ); + aTilingObj.append( ' ' ); + appendFixedInt( nY, aTilingObj ); + aTilingObj.append( ' ' ); + appendFixedInt( nX+nW, aTilingObj ); + aTilingObj.append( ' ' ); + appendFixedInt( nY+nH, aTilingObj ); + aTilingObj.append( "]\n" + "/XStep " ); + appendFixedInt( it->m_aCellSize.Width(), aTilingObj ); + aTilingObj.append( "\n" + "/YStep " ); + appendFixedInt( it->m_aCellSize.Height(), aTilingObj ); + aTilingObj.append( "\n" ); + if( it->m_aTransform.matrix[0] != 1.0 || + it->m_aTransform.matrix[1] != 0.0 || + it->m_aTransform.matrix[3] != 0.0 || + it->m_aTransform.matrix[4] != 1.0 || + it->m_aTransform.matrix[2] != 0.0 || + it->m_aTransform.matrix[5] != 0.0 ) + { + aTilingObj.append( "/Matrix [" ); + // TODO: scaling, mirroring on y, etc + appendDouble( it->m_aTransform.matrix[0], aTilingObj ); + aTilingObj.append( ' ' ); + appendDouble( it->m_aTransform.matrix[1], aTilingObj ); + aTilingObj.append( ' ' ); + appendDouble( it->m_aTransform.matrix[3], aTilingObj ); + aTilingObj.append( ' ' ); + appendDouble( it->m_aTransform.matrix[4], aTilingObj ); + aTilingObj.append( ' ' ); + appendDouble( it->m_aTransform.matrix[2], aTilingObj ); + aTilingObj.append( ' ' ); + appendDouble( it->m_aTransform.matrix[5], aTilingObj ); + aTilingObj.append( "]\n" ); + } + aTilingObj.append( "/Resources" ); + it->m_aResources.append( aTilingObj, getFontDictObject() ); + if( bDeflate ) + aTilingObj.append( "/Filter/FlateDecode" ); + aTilingObj.append( "/Length " ); + aTilingObj.append( (sal_Int32)nTilingStreamSize ); + aTilingObj.append( ">>\nstream\n" ); + CHECK_RETURN( updateObject( it->m_nObject ) ); + CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ); + checkAndEnableStreamEncryption( it->m_nObject ); + nTilingStreamSize = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize ); + delete it->m_pTilingStream; + it->m_pTilingStream = NULL; + if( nTilingStreamSize == 0 ) + return false; + disableStreamEncryption(); + aTilingObj.setLength( 0 ); + aTilingObj.append( "\nendstream\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ); + } + return true; +} + +sal_Int32 PDFWriterImpl::emitBuiltinFont( const ImplFontData* pFont, sal_Int32 nFontObject ) +{ + const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont ); + if( !pFD ) + return 0; + const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); + + OStringBuffer aLine( 1024 ); + + if( nFontObject <= 0 ) + nFontObject = createObject(); + CHECK_RETURN( updateObject( nFontObject ) ); + aLine.append( nFontObject ); + aLine.append( " 0 obj\n" + "<</Type/Font/Subtype/Type1/BaseFont/" ); + appendName( pBuiltinFont->m_pPSName, aLine ); + aLine.append( "\n" ); + if( pBuiltinFont->m_eCharSet == RTL_TEXTENCODING_MS_1252 ) + aLine.append( "/Encoding/WinAnsiEncoding\n" ); + aLine.append( ">>\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + return nFontObject; +} + +std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const ImplFontData* pFont, EmbedFont& rEmbed ) +{ + std::map< sal_Int32, sal_Int32 > aRet; + if( isBuiltinFont( pFont ) ) + { + aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont ); + return aRet; + } + + sal_Int32 nFontDescriptor = 0; + rtl::OString aSubType( "/Type1" ); + FontSubsetInfo aInfo; + // fill in dummy values + aInfo.m_nAscent = 1000; + aInfo.m_nDescent = 200; + aInfo.m_nCapHeight = 1000; + aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) ); + aInfo.m_aPSName = pFont->maName; + sal_Int32 pWidths[256]; + rtl_zeroMemory( pWidths, sizeof(pWidths) ); + if( pFont->IsEmbeddable() ) + { + const unsigned char* pFontData = NULL; + long nFontLen = 0; + sal_Ucs nEncodedCodes[256]; + sal_Int32 pEncWidths[256]; + if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, aInfo, &nFontLen )) != NULL ) + { + m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen ); + for( int i = 0; i < 256; i++ ) + { + if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 ) + { + pWidths[i] = pEncWidths[ i ]; + } + } + } + } + else if( pFont->mbSubsettable ) + { + aSubType = rtl::OString( "/TrueType" ); + Int32Vector aGlyphWidths; + Ucs2UIntMap aUnicodeMap; + m_pReferenceDevice->mpGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap ); + + OUString aTmpName; + osl_createTempFile( NULL, NULL, &aTmpName.pData ); + sal_Int32 pGlyphIDs[ 256 ]; + sal_uInt8 pEncoding[ 256 ]; + sal_Ucs pUnicodes[ 256 ]; + sal_Int32 pDuWidths[ 256 ]; + + memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) ); + memset( pEncoding, 0, sizeof( pEncoding ) ); + memset( pUnicodes, 0, sizeof( pUnicodes ) ); + memset( pDuWidths, 0, sizeof( pDuWidths ) ); + + for( sal_Ucs c = 32; c < 256; c++ ) + { + pUnicodes[c] = c; + pEncoding[c] = c; + pGlyphIDs[c] = 0; + if( aUnicodeMap.find( c ) != aUnicodeMap.end() ) + pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ]; + } + + m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, pFont, pGlyphIDs, pEncoding, pDuWidths, 256, aInfo ); + osl_removeFile( aTmpName.pData ); + } + else + { + OSL_FAIL( "system font neither embeddable nor subsettable" ); + } + + // write font descriptor + nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 ); + if( nFontDescriptor ) + { + // write font object + sal_Int32 nObject = createObject(); + if( updateObject( nObject ) ) + { + OStringBuffer aLine( 1024 ); + aLine.append( nObject ); + aLine.append( " 0 obj\n" + "<</Type/Font/Subtype" ); + aLine.append( aSubType ); + aLine.append( "/BaseFont/" ); + appendName( aInfo.m_aPSName, aLine ); + aLine.append( "\n" ); + if( !pFont->mbSymbolFlag ) + aLine.append( "/Encoding/WinAnsiEncoding\n" ); + aLine.append( "/FirstChar 32 /LastChar 255\n" + "/Widths[" ); + for( int i = 32; i < 256; i++ ) + { + aLine.append( pWidths[i] ); + aLine.append( ((i&15) == 15) ? "\n" : " " ); + } + aLine.append( "]\n" + "/FontDescriptor " ); + aLine.append( nFontDescriptor ); + aLine.append( " 0 R>>\n" + "endobj\n\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + + aRet[ rEmbed.m_nNormalFontID ] = nObject; + } + } + + return aRet; +} + +typedef int ThreeInts[3]; +static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen, + ThreeInts& rSegmentLengths ) +{ + if( !pFontBytes || (nByteLen < 0) ) + return false; + const unsigned char* pPtr = pFontBytes; + const unsigned char* pEnd = pFontBytes + nByteLen; + + for( int i = 0; i < 3; ++i) { + // read segment1 header + if( pPtr+6 >= pEnd ) + return false; + if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) ) + return false; + const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2]; + if( nLen <= 0) + return false; + rSegmentLengths[i] = nLen; + pPtr += nLen + 6; + } + + // read segment-end header + if( pPtr+2 >= pEnd ) + return false; + if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) ) + return false; + + return true; +} + +struct FontException : public std::exception +{ +}; + +// TODO: always subset instead of embedding the full font => this method becomes obsolete then +std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFontData* pFont, EmbedFont& rEmbed ) +{ + std::map< sal_Int32, sal_Int32 > aRet; + if( isBuiltinFont( pFont ) ) + { + aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont ); + return aRet; + } + + sal_Int32 nStreamObject = 0; + sal_Int32 nFontDescriptor = 0; + + // prepare font encoding + const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL ); + sal_Int32 nToUnicodeStream = 0; + sal_uInt8 nEncoding[256]; + sal_Ucs nEncodedCodes[256]; + std::vector<sal_Ucs> aUnicodes; + aUnicodes.reserve( 256 ); + sal_Int32 pUnicodesPerGlyph[256]; + sal_Int32 pEncToUnicodeIndex[256]; + if( pEncoding ) + { + rtl_zeroMemory( nEncoding, sizeof(nEncoding) ); + rtl_zeroMemory( nEncodedCodes, sizeof(nEncodedCodes) ); + rtl_zeroMemory( pUnicodesPerGlyph, sizeof(pUnicodesPerGlyph) ); + rtl_zeroMemory( pEncToUnicodeIndex, sizeof(pEncToUnicodeIndex) ); + for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it ) + { + if( it->second != -1 ) + { + sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff); + nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode ); + nEncodedCodes[ nCode ] = it->first; + pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size()); + aUnicodes.push_back( it->first ); + pUnicodesPerGlyph[ nCode ] = 1; + } + } + } + + FontSubsetInfo aInfo; + sal_Int32 pWidths[256]; + const unsigned char* pFontData = NULL; + long nFontLen = 0; + sal_Int32 nLength1, nLength2; + try + { + if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL ) + { + if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 ) + throw FontException(); + // see whether it is pfb or pfa; if it is a pfb, fill ranges + // of 6 bytes that are not part of the font program + std::list< int > aSections; + std::list< int >::const_iterator it; + int nIndex = 0; + while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 ) + { + aSections.push_back( nIndex ); + if( pFontData[nIndex+1] == 0x03 ) + break; + sal_Int32 nBytes = + ((sal_Int32)pFontData[nIndex+2]) | + ((sal_Int32)pFontData[nIndex+3]) << 8 | + ((sal_Int32)pFontData[nIndex+4]) << 16 | + ((sal_Int32)pFontData[nIndex+5]) << 24; + nIndex += nBytes+6; + } + + // search for eexec + // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below + nIndex = 0; + int nEndAsciiIndex; + int nBeginBinaryIndex; + int nEndBinaryIndex; + do + { + while( nIndex < nFontLen-4 && + ( pFontData[nIndex] != 'e' || + pFontData[nIndex+1] != 'e' || + pFontData[nIndex+2] != 'x' || + pFontData[nIndex+3] != 'e' || + pFontData[nIndex+4] != 'c' + ) + ) + nIndex++; + // check whether we are in a excluded section + for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) + ; + } while( it != aSections.end() && nIndex < nFontLen-4 ); + // this should end the ascii part + if( nIndex > nFontLen-5 ) + throw FontException(); + + nEndAsciiIndex = nIndex+4; + // now count backwards until we can account for 512 '0' + // which is the endmarker of the (hopefully) binary data + // do not count the pfb header sections + int nFound = 0; + nIndex = nFontLen-1; + while( nIndex > 0 && nFound < 512 ) + { + for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) + ; + if( it == aSections.end() ) + { + // inside the 512 '0' block there may only be whitespace + // according to T1 spec; probably it would be to simple + // if all fonts complied + if( pFontData[nIndex] == '0' ) + nFound++; + else if( nFound > 0 && + pFontData[nIndex] != '\r' && + pFontData[nIndex] != '\t' && + pFontData[nIndex] != '\n' && + pFontData[nIndex] != ' ' ) + break; + } + nIndex--; + } + + if( nIndex < 1 || nIndex <= nEndAsciiIndex ) + throw FontException(); + + // nLength3 is the rest of the file - excluding any section headers + // nIndex now points before the first of the 512 '0' characters marking the + // fixed content portion + sal_Int32 nLength3 = nFontLen - nIndex - 1; + for( it = aSections.begin(); it != aSections.end(); ++it ) + { + if( *it >= nIndex ) + { + // special case: nIndex inside a section marker + if( nIndex >= (*it) && (*it)+5 > nIndex ) + nLength3 -= (*it)+5 - nIndex; + else + { + if( *it < nFontLen - 6 ) + nLength3 -= 6; + else // the last section 0x8003 is only 2 bytes after all + nLength3 -= (nFontLen - *it); + } + } + } + + // there may be whitespace to ignore before the 512 '0' + while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' ) + { + nIndex--; + for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) + ; + if( it != aSections.end() ) + { + nIndex = (*it)-1; + break; // this is surely a binary boundary, in ascii case it wouldn't matter + } + } + nEndBinaryIndex = nIndex; + + // search for beginning of binary section + nBeginBinaryIndex = nEndAsciiIndex; + do + { + nBeginBinaryIndex++; + for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it ) + ; + } while( nBeginBinaryIndex < nEndBinaryIndex && + ( pFontData[nBeginBinaryIndex] == '\r' || + pFontData[nBeginBinaryIndex] == '\n' || + it != aSections.end() ) ); + + // it seems to be vital to copy the exact whitespace between binary data + // and eexec, else a invalid font results. so make nEndAsciiIndex + // always immediate in front of nBeginBinaryIndex + nEndAsciiIndex = nBeginBinaryIndex-1; + for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it ) + ; + if( it != aSections.end() ) + nEndAsciiIndex = (*it)-1; + + nLength1 = nEndAsciiIndex+1; // including the last character + for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it ) + nLength1 -= 6; // decrease by pfb section size + + // if the first four bytes are all ascii hex characters, then binary data + // has to be converted to real binary data + for( nIndex = 0; nIndex < 4 && + ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) || + ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) || + ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' ) + ); ++nIndex ) + ; + bool bConvertHexData = true; + if( nIndex < 4 ) + { + bConvertHexData = false; + nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte + for( it = aSections.begin(); it != aSections.end(); ++it ) + if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex ) + nLength2 -= 6; + } + else + { + // count the hex ascii characters to get nLength2 + nLength2 = 0; + int nNextSectionIndex = 0; + for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) + ; + if( it != aSections.end() ) + nNextSectionIndex = *it; + for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) + { + if( nIndex == nNextSectionIndex ) + { + nIndex += 6; + ++it; + nNextSectionIndex = (it == aSections.end() ? 0 : *it ); + } + if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) || + ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) || + ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) ) + nLength2++; + } + DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" ); + nLength2 /= 2; + } + + // now we can actually write the font stream ! + #if OSL_DEBUG_LEVEL > 1 + emitComment( " PDFWriterImpl::emitEmbeddedFont" ); + #endif + OStringBuffer aLine( 512 ); + nStreamObject = createObject(); + if( !updateObject(nStreamObject)) + throw FontException(); + sal_Int32 nStreamLengthObject = createObject(); + aLine.append( nStreamObject ); + aLine.append( " 0 obj\n" + "<</Length " ); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 R" + #ifndef DEBUG_DISABLE_PDFCOMPRESSION + "/Filter/FlateDecode" + #endif + "/Length1 " ); + aLine.append( nLength1 ); + aLine.append( " /Length2 " ); + aLine.append( nLength2 ); + aLine.append( " /Length3 "); + aLine.append( nLength3 ); + aLine.append( ">>\n" + "stream\n" ); + if( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); + + sal_uInt64 nBeginStreamPos = 0; + osl_getFilePos( m_aFile, &nBeginStreamPos ); + + beginCompression(); + checkAndEnableStreamEncryption( nStreamObject ); + + // write ascii section + if( aSections.begin() == aSections.end() ) + { + if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) ) + throw FontException(); + } + else + { + // first section always starts at 0 + it = aSections.begin(); + nIndex = (*it)+6; + ++it; + while( *it < nEndAsciiIndex ) + { + if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) ) + throw FontException(); + nIndex = (*it)+6; + ++it; + } + // write partial last section + if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) ) + throw FontException(); + } + + // write binary section + if( ! bConvertHexData ) + { + if( aSections.begin() == aSections.end() ) + { + if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) ) + throw FontException(); + } + else + { + for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it ) + ; + // write first partial section + if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) ) + throw FontException(); + // write following sections + while( it != aSections.end() ) + { + nIndex = (*it)+6; + ++it; + if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes + { + sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; + if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) + throw FontException(); + } + } + } + } + else + { + boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] ); + rtl_zeroMemory( pWriteBuffer.get(), nLength2 ); + int nWriteIndex = 0; + + int nNextSectionIndex = 0; + for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) + ; + if( it != aSections.end() ) + nNextSectionIndex = *it; + for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) + { + if( nIndex == nNextSectionIndex ) + { + nIndex += 6; + ++it; + nNextSectionIndex = (it == aSections.end() ? nFontLen : *it ); + } + unsigned char cNibble = 0x80; + if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) + cNibble = pFontData[nIndex] - '0'; + else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) + cNibble = pFontData[nIndex] - 'a' + 10; + else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) + cNibble = pFontData[nIndex] - 'A' + 10; + if( cNibble != 0x80 ) + { + if( !(nWriteIndex & 1 ) ) + cNibble <<= 4; + pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble; + nWriteIndex++; + } + } + if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) ) + throw FontException(); + if( aSections.empty() ) + { + if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) ) + throw FontException(); + } + else + { + // write rest of this section + if( nIndex < nNextSectionIndex ) + { + if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) ) + throw FontException(); + } + // write following sections + while( it != aSections.end() ) + { + nIndex = (*it)+6; + ++it; + if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes + { + sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; + if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) + throw FontException(); + } + } + } + } + endCompression(); + disableStreamEncryption(); + + + sal_uInt64 nEndStreamPos = 0; + osl_getFilePos( m_aFile, &nEndStreamPos ); + + // and finally close the stream + aLine.setLength( 0 ); + aLine.append( "\nendstream\nendobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); + + // write stream length object + aLine.setLength( 0 ); + if( ! updateObject( nStreamLengthObject ) ) + throw FontException(); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 obj\n" ); + aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) ); + aLine.append( "\nendobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); + } + else + { + rtl::OStringBuffer aErrorComment( 256 ); + aErrorComment.append( "GetEmbedFontData failed for font \"" ); + aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); + aErrorComment.append( '\"' ); + if( pFont->GetSlant() == ITALIC_NORMAL ) + aErrorComment.append( " italic" ); + else if( pFont->GetSlant() == ITALIC_OBLIQUE ) + aErrorComment.append( " oblique" ); + aErrorComment.append( " weight=" ); + aErrorComment.append( sal_Int32(pFont->GetWeight()) ); + emitComment( aErrorComment.getStr() ); + } + + if( nStreamObject ) + // write font descriptor + nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject ); + + if( nFontDescriptor ) + { + if( pEncoding ) + nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, SAL_N_ELEMENTS(nEncoding) ); + + // write font object + sal_Int32 nObject = createObject(); + if( ! updateObject( nObject ) ) + throw FontException(); + + OStringBuffer aLine( 1024 ); + aLine.append( nObject ); + aLine.append( " 0 obj\n" + "<</Type/Font/Subtype/Type1/BaseFont/" ); + appendName( aInfo.m_aPSName, aLine ); + aLine.append( "\n" ); + if( !pFont->mbSymbolFlag && pEncoding == 0 ) + aLine.append( "/Encoding/WinAnsiEncoding\n" ); + if( nToUnicodeStream ) + { + aLine.append( "/ToUnicode " ); + aLine.append( nToUnicodeStream ); + aLine.append( " 0 R\n" ); + } + aLine.append( "/FirstChar 0 /LastChar 255\n" + "/Widths[" ); + for( int i = 0; i < 256; i++ ) + { + aLine.append( pWidths[i] ); + aLine.append( ((i&15) == 15) ? "\n" : " " ); + } + aLine.append( "]\n" + "/FontDescriptor " ); + aLine.append( nFontDescriptor ); + aLine.append( " 0 R>>\n" + "endobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); + + aRet[ rEmbed.m_nNormalFontID ] = nObject; + + // write additional encodings + for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it ) + { + sal_Int32 aEncWidths[ 256 ]; + // emit encoding dict + sal_Int32 nEncObject = createObject(); + if( ! updateObject( nEncObject ) ) + throw FontException(); + + OutputDevice* pRef = getReferenceDevice(); + pRef->Push( PUSH_FONT | PUSH_MAPMODE ); + pRef->SetMapMode( MapMode( MAP_PIXEL ) ); + Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) ); + aFont.SetWeight( pFont->GetWeight() ); + aFont.SetItalic( pFont->GetSlant() ); + aFont.SetPitch( pFont->GetPitch() ); + pRef->SetFont( aFont ); + pRef->ImplNewFont(); + + aLine.setLength( 0 ); + aLine.append( nEncObject ); + aLine.append( " 0 obj\n" + "<</Type/Encoding/Differences[ 0\n" ); + int nEncoded = 0; + aUnicodes.clear(); + for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it ) + { + String aStr( str_it->m_aUnicode ); + aEncWidths[nEncoded] = pRef->GetTextWidth( aStr ); + nEncodedCodes[nEncoded] = str_it->m_aUnicode; + nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded); + pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size()); + aUnicodes.push_back( nEncodedCodes[nEncoded] ); + pUnicodesPerGlyph[nEncoded] = 1; + + aLine.append( " /" ); + aLine.append( str_it->m_aName ); + if( !((++nEncoded) & 15) ) + aLine.append( "\n" ); + } + aLine.append( "]>>\n" + "endobj\n\n" ); + + pRef->Pop(); + + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); + + nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded ); + + nObject = createObject(); + if( ! updateObject( nObject ) ) + throw FontException(); + + aLine.setLength( 0 ); + aLine.append( nObject ); + aLine.append( " 0 obj\n" + "<</Type/Font/Subtype/Type1/BaseFont/" ); + appendName( aInfo.m_aPSName, aLine ); + aLine.append( "\n" ); + aLine.append( "/Encoding " ); + aLine.append( nEncObject ); + aLine.append( " 0 R\n" ); + if( nToUnicodeStream ) + { + aLine.append( "/ToUnicode " ); + aLine.append( nToUnicodeStream ); + aLine.append( " 0 R\n" ); + } + aLine.append( "/FirstChar 0\n" + "/LastChar " ); + aLine.append( (sal_Int32)(nEncoded-1) ); + aLine.append( "\n" + "/Widths[" ); + for( int i = 0; i < nEncoded; i++ ) + { + aLine.append( aEncWidths[i] ); + aLine.append( ((i&15) == 15) ? "\n" : " " ); + } + aLine.append( " ]\n" + "/FontDescriptor " ); + aLine.append( nFontDescriptor ); + aLine.append( " 0 R>>\n" + "endobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); + + aRet[ enc_it->m_nFontID ] = nObject; + } + } + } + catch( FontException& ) + { + // these do nothing in case there was no compression or encryption ongoing + endCompression(); + disableStreamEncryption(); + } + + if( pFontData ) + m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen ); + + return aRet; +} + +static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer ) +{ + if( nSubsetID ) + { + for( int i = 0; i < 6; i++ ) + { + int nOffset = (nSubsetID % 26); + nSubsetID /= 26; + rBuffer.append( (sal_Char)('A'+nOffset) ); + } + rBuffer.append( '+' ); + } + appendName( rPSName, rBuffer ); +} + +sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, + sal_Ucs* pUnicodes, + sal_Int32* pUnicodesPerGlyph, + sal_Int32* pEncToUnicodeIndex, + int nGlyphs ) +{ + int nMapped = 0, n = 0; + for( n = 0; n < nGlyphs; n++ ) + if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) + nMapped++; + + if( nMapped == 0 ) + return 0; + + sal_Int32 nStream = createObject(); + CHECK_RETURN( updateObject( nStream ) ); + + OStringBuffer aContents( 1024 ); + aContents.append( + "/CIDInit/ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo<<\n" + "/Registry (Adobe)\n" + "/Ordering (UCS)\n" + "/Supplement 0\n" + ">> def\n" + "/CMapName/Adobe-Identity-UCS def\n" + "/CMapType 2 def\n" + "1 begincodespacerange\n" + "<00> <FF>\n" + "endcodespacerange\n" + ); + int nCount = 0; + for( n = 0; n < nGlyphs; n++ ) + { + if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) + { + if( (nCount % 100) == 0 ) + { + if( nCount ) + aContents.append( "endbfchar\n" ); + aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) ); + aContents.append( " beginbfchar\n" ); + } + aContents.append( '<' ); + appendHex( (sal_Int8)pEncoding[n], aContents ); + aContents.append( "> <" ); + // TODO: handle unicodes>U+FFFF + sal_Int32 nIndex = pEncToUnicodeIndex[n]; + for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ ) + { + appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents ); + appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents ); + } + aContents.append( ">\n" ); + nCount++; + } + } + aContents.append( "endbfchar\n" + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n" ); +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 ); + SvMemoryStream aStream; + pCodec->BeginCompression(); + pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() ); + pCodec->EndCompression(); + delete pCodec; +#endif + + #if OSL_DEBUG_LEVEL > 1 + emitComment( "PDFWriterImpl::createToUnicodeCMap" ); + #endif + OStringBuffer aLine( 40 ); + + aLine.append( nStream ); + aLine.append( " 0 obj\n<</Length " ); +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + sal_Int32 nLen = (sal_Int32)aStream.Tell(); + aStream.Seek( 0 ); + aLine.append( nLen ); + aLine.append( "/Filter/FlateDecode" ); +#else + aLine.append( aContents.getLength() ); +#endif + aLine.append( ">>\nstream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + checkAndEnableStreamEncryption( nStream ); +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) ); +#else + CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) ); +#endif + disableStreamEncryption(); + aLine.setLength( 0 ); + aLine.append( "\nendstream\n" + "endobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + return nStream; +} + +sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream ) +{ + OStringBuffer aLine( 1024 ); + // get font flags, see PDF reference 1.4 p. 358 + // possibly characters outside Adobe standard encoding + // so set Symbolic flag + sal_Int32 nFontFlags = (1<<2); + if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE ) + nFontFlags |= (1 << 6); + if( pFont->GetPitch() == PITCH_FIXED ) + nFontFlags |= 1; + if( pFont->GetFamilyType() == FAMILY_SCRIPT ) + nFontFlags |= (1 << 3); + else if( pFont->GetFamilyType() == FAMILY_ROMAN ) + nFontFlags |= (1 << 1); + + sal_Int32 nFontDescriptor = createObject(); + CHECK_RETURN( updateObject( nFontDescriptor ) ); + aLine.setLength( 0 ); + aLine.append( nFontDescriptor ); + aLine.append( " 0 obj\n" + "<</Type/FontDescriptor/FontName/" ); + appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine ); + aLine.append( "\n" + "/Flags " ); + aLine.append( nFontFlags ); + aLine.append( "\n" + "/FontBBox[" ); + // note: Top and Bottom are reversed in VCL and PDF rectangles + aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() ); + aLine.append( ' ' ); + aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() ); + aLine.append( ' ' ); + aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() ); + aLine.append( ' ' ); + aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) ); + aLine.append( "]/ItalicAngle " ); + if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL ) + aLine.append( "-30" ); + else + aLine.append( "0" ); + aLine.append( "\n" + "/Ascent " ); + aLine.append( (sal_Int32)rInfo.m_nAscent ); + aLine.append( "\n" + "/Descent " ); + aLine.append( (sal_Int32)-rInfo.m_nDescent ); + aLine.append( "\n" + "/CapHeight " ); + aLine.append( (sal_Int32)rInfo.m_nCapHeight ); + // According to PDF reference 1.4 StemV is required + // seems a tad strange to me, but well ... + aLine.append( "\n" + "/StemV 80\n" ); + if( nFontStream ) + { + aLine.append( "/FontFile" ); + switch( rInfo.m_nFontType ) + { + case FontSubsetInfo::SFNT_TTF: + aLine.append( '2' ); + break; + case FontSubsetInfo::TYPE1_PFA: + case FontSubsetInfo::TYPE1_PFB: + case FontSubsetInfo::ANY_TYPE1: + break; + default: + OSL_FAIL( "unknown fonttype in PDF font descriptor" ); + return 0; + } + aLine.append( ' ' ); + aLine.append( nFontStream ); + aLine.append( " 0 R\n" ); + } + aLine.append( ">>\n" + "endobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + return nFontDescriptor; +} + +void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const +{ + for( std::map< sal_Int32, sal_Int32 >::const_iterator it = + m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it ) + { + rDict.append( m_aBuiltinFonts[it->first].getNameObject() ); + rDict.append( ' ' ); + rDict.append( it->second ); + rDict.append( " 0 R" ); + } +} + +bool PDFWriterImpl::emitFonts() +{ + if( ! m_pReferenceDevice->ImplGetGraphics() ) + return false; + + OStringBuffer aLine( 1024 ); + + std::map< sal_Int32, sal_Int32 > aFontIDToObject; + + OUString aTmpName; + osl_createTempFile( NULL, NULL, &aTmpName.pData ); + for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it ) + { + for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit ) + { + sal_Int32 pGlyphIDs[ 256 ]; + sal_Int32 pWidths[ 256 ]; + sal_uInt8 pEncoding[ 256 ]; + sal_Int32 pEncToUnicodeIndex[ 256 ]; + sal_Int32 pUnicodesPerGlyph[ 256 ]; + std::vector<sal_Ucs> aUnicodes; + aUnicodes.reserve( 256 ); + int nGlyphs = 1; + // fill arrays and prepare encoding index map + sal_Int32 nToUnicodeStream = 0; + + rtl_zeroMemory( pGlyphIDs, sizeof( pGlyphIDs ) ); + rtl_zeroMemory( pEncoding, sizeof( pEncoding ) ); + rtl_zeroMemory( pUnicodesPerGlyph, sizeof( pUnicodesPerGlyph ) ); + rtl_zeroMemory( pEncToUnicodeIndex, sizeof( pEncToUnicodeIndex ) ); + for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit ) + { + sal_uInt8 nEnc = fit->second.getGlyphId(); + + DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" ); + DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" ); + + pGlyphIDs[ nEnc ] = fit->first; + pEncoding[ nEnc ] = nEnc; + pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size()); + pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes(); + for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ ) + aUnicodes.push_back( fit->second.getCode( n ) ); + if( fit->second.getCode(0) ) + nToUnicodeStream = 1; + if( nGlyphs < 256 ) + nGlyphs++; + else + { + OSL_FAIL( "too many glyphs for subset" ); + } + } + FontSubsetInfo aSubsetInfo; + if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) ) + { + // create font stream + oslFileHandle aFontFile; + CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) ); + // get file size + sal_uInt64 nLength1; + CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) ); + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) ); + CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); + + #if OSL_DEBUG_LEVEL > 1 + emitComment( "PDFWriterImpl::emitFonts" ); + #endif + sal_Int32 nFontStream = createObject(); + sal_Int32 nStreamLengthObject = createObject(); + CHECK_RETURN( updateObject( nFontStream ) ); + aLine.setLength( 0 ); + aLine.append( nFontStream ); + aLine.append( " 0 obj\n" + "<</Length " ); + aLine.append( (sal_Int32)nStreamLengthObject ); + aLine.append( " 0 R" + #ifndef DEBUG_DISABLE_PDFCOMPRESSION + "/Filter/FlateDecode" + #endif + "/Length1 " ); + + sal_uInt64 nStartPos = 0; + if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF ) + { + aLine.append( (sal_Int32)nLength1 ); + + aLine.append( ">>\n" + "stream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); + + // copy font file + beginCompression(); + checkAndEnableStreamEncryption( nFontStream ); + sal_Bool bEOF = sal_False; + do + { + char buf[8192]; + sal_uInt64 nRead; + CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) ); + CHECK_RETURN( writeBuffer( buf, nRead ) ); + CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) ); + } while( ! bEOF ); + } + else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 ) + { + // TODO: implement + OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" ); + } + else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA? + { + boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] ); + + sal_uInt64 nBytesRead = 0; + CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) ); + DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" ); + CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); + // get the PFB-segment lengths + ThreeInts aSegmentLengths = {0,0,0}; + getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths ); + // the lengths below are mandatory for PDF-exported Type1 fonts + // because the PFB segment headers get stripped! WhyOhWhy. + aLine.append( (sal_Int32)aSegmentLengths[0] ); + aLine.append( "/Length2 " ); + aLine.append( (sal_Int32)aSegmentLengths[1] ); + aLine.append( "/Length3 " ); + aLine.append( (sal_Int32)aSegmentLengths[2] ); + + aLine.append( ">>\n" + "stream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); + + // emit PFB-sections without section headers + beginCompression(); + checkAndEnableStreamEncryption( nFontStream ); + CHECK_RETURN( writeBuffer( &pBuffer[6], aSegmentLengths[0] ) ); + CHECK_RETURN( writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ); + CHECK_RETURN( writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ); + } + else + { + fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType); + aLine.append( "0 >>\nstream\n" ); + } + + endCompression(); + disableStreamEncryption(); + // close the file + osl_closeFile( aFontFile ); + + sal_uInt64 nEndPos = 0; + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) ); + // end the stream + aLine.setLength( 0 ); + aLine.append( "\nendstream\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + // emit stream length object + CHECK_RETURN( updateObject( nStreamLengthObject ) ); + aLine.setLength( 0 ); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 obj\n" ); + aLine.append( (sal_Int64)(nEndPos-nStartPos) ); + aLine.append( "\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + // write font descriptor + sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream ); + + if( nToUnicodeStream ) + nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs ); + + sal_Int32 nFontObject = createObject(); + CHECK_RETURN( updateObject( nFontObject ) ); + aLine.setLength( 0 ); + aLine.append( nFontObject ); + + aLine.append( " 0 obj\n" ); + aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ? + "<</Type/Font/Subtype/Type1/BaseFont/" : + "<</Type/Font/Subtype/TrueType/BaseFont/" ); + appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine ); + aLine.append( "\n" + "/FirstChar 0\n" + "/LastChar " ); + aLine.append( (sal_Int32)(nGlyphs-1) ); + aLine.append( "\n" + "/Widths[" ); + for( int i = 0; i < nGlyphs; i++ ) + { + aLine.append( pWidths[ i ] ); + aLine.append( ((i & 15) == 15) ? "\n" : " " ); + } + aLine.append( "]\n" + "/FontDescriptor " ); + aLine.append( nFontDescriptor ); + aLine.append( " 0 R\n" ); + if( nToUnicodeStream ) + { + aLine.append( "/ToUnicode " ); + aLine.append( nToUnicodeStream ); + aLine.append( " 0 R\n" ); + } + aLine.append( ">>\n" + "endobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + aFontIDToObject[ lit->m_nFontID ] = nFontObject; + } + else + { + const ImplFontData* pFont = it->first; + rtl::OStringBuffer aErrorComment( 256 ); + aErrorComment.append( "CreateFontSubset failed for font \"" ); + aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); + aErrorComment.append( '\"' ); + if( pFont->GetSlant() == ITALIC_NORMAL ) + aErrorComment.append( " italic" ); + else if( pFont->GetSlant() == ITALIC_OBLIQUE ) + aErrorComment.append( " oblique" ); + aErrorComment.append( " weight=" ); + aErrorComment.append( sal_Int32(pFont->GetWeight()) ); + emitComment( aErrorComment.getStr() ); + } + } + } + osl_removeFile( aTmpName.pData ); + + // emit embedded fonts + for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit ) + { + std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second ); + for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) + { + CHECK_RETURN( fit->second ); + aFontIDToObject[ fit->first ] = fit->second; + } + } + + // emit system fonts + for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit ) + { + std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second ); + for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) + { + CHECK_RETURN( fit->second ); + aFontIDToObject[ fit->first ] = fit->second; + } + } + + OStringBuffer aFontDict( 1024 ); + aFontDict.append( getFontDictObject() ); + aFontDict.append( " 0 obj\n" + "<<" ); + int ni = 0; + for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit ) + { + aFontDict.append( "/F" ); + aFontDict.append( mit->first ); + aFontDict.append( ' ' ); + aFontDict.append( mit->second ); + aFontDict.append( " 0 R" ); + if( ((++ni) & 7) == 0 ) + aFontDict.append( '\n' ); + } + // emit builtin font for widget apperances / variable text + for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin(); + it != m_aBuiltinFontToObjectMap.end(); ++it ) + { + ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]); + it->second = emitBuiltinFont( &aData, it->second ); + } + appendBuiltinFontsToDict( aFontDict ); + aFontDict.append( "\n>>\nendobj\n\n" ); + + CHECK_RETURN( updateObject( getFontDictObject() ) ); + CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ); + return true; +} + +sal_Int32 PDFWriterImpl::emitResources() +{ + // emit shadings + if( ! m_aGradients.empty() ) + CHECK_RETURN( emitGradients() ); + // emit tilings + if( ! m_aTilings.empty() ) + CHECK_RETURN( emitTilings() ); + + // emit font dict + CHECK_RETURN( emitFonts() ); + + // emit Resource dict + OStringBuffer aLine( 512 ); + sal_Int32 nResourceDict = getResourceDictObj(); + CHECK_RETURN( updateObject( nResourceDict ) ); + aLine.setLength( 0 ); + aLine.append( nResourceDict ); + aLine.append( " 0 obj\n" ); + m_aGlobalResourceDict.append( aLine, getFontDictObject() ); + aLine.append( "endobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + return nResourceDict; +} + +sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts, + sal_Int32 nItemLevel, + sal_Int32 nCurrentItemId ) +{ + /* The /Count number of an item is + positive: the number of visible subitems + negative: the negative number of subitems that will become visible if + the item gets opened + see PDF ref 1.4 p 478 + */ + + sal_Int32 nCount = 0; + + if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible + m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible + ) + { + PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; + sal_Int32 nChildren = rItem.m_aChildren.size(); + for( sal_Int32 i = 0; i < nChildren; i++ ) + nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); + rCounts[nCurrentItemId] = nCount; + // return 1 (this item) + visible sub items + if( nCount < 0 ) + nCount = 0; + nCount++; + } + else + { + // this bookmark level is invisible + PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; + sal_Int32 nChildren = rItem.m_aChildren.size(); + rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size()); + for( sal_Int32 i = 0; i < nChildren; i++ ) + updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); + nCount = -1; + } + + return nCount; +} + +sal_Int32 PDFWriterImpl::emitOutline() +{ + int i, nItems = m_aOutline.size(); + + // do we have an outline at all ? + if( nItems < 2 ) + return 0; + + // reserve object numbers for all outline items + for( i = 0; i < nItems; ++i ) + m_aOutline[i].m_nObject = createObject(); + + // update all parent, next and prev object ids + for( i = 0; i < nItems; ++i ) + { + PDFOutlineEntry& rItem = m_aOutline[i]; + int nChildren = rItem.m_aChildren.size(); + + if( nChildren ) + { + for( int n = 0; n < nChildren; ++n ) + { + PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ]; + + rChild.m_nParentObject = rItem.m_nObject; + rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0; + rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0; + } + + } + } + + // calculate Count entries for all items + std::vector< sal_Int32 > aCounts( nItems ); + updateOutlineItemCount( aCounts, 0, 0 ); + + // emit hierarchy + for( i = 0; i < nItems; ++i ) + { + PDFOutlineEntry& rItem = m_aOutline[i]; + OStringBuffer aLine( 1024 ); + + CHECK_RETURN( updateObject( rItem.m_nObject ) ); + aLine.append( rItem.m_nObject ); + aLine.append( " 0 obj\n" ); + aLine.append( "<<" ); + // number of visible children (all levels) + if( i > 0 || aCounts[0] > 0 ) + { + aLine.append( "/Count " ); + aLine.append( aCounts[i] ); + } + if( ! rItem.m_aChildren.empty() ) + { + // children list: First, Last + aLine.append( "/First " ); + aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject ); + aLine.append( " 0 R/Last " ); + aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject ); + aLine.append( " 0 R\n" ); + } + if( i > 0 ) + { + // Title, Dest, Parent, Prev, Next + aLine.append( "/Title" ); + appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine ); + aLine.append( "\n" ); + // Dest is not required + if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() ) + { + aLine.append( "/Dest" ); + appendDest( rItem.m_nDestID, aLine ); + } + aLine.append( "/Parent " ); + aLine.append( rItem.m_nParentObject ); + aLine.append( " 0 R" ); + if( rItem.m_nPrevObject ) + { + aLine.append( "/Prev " ); + aLine.append( rItem.m_nPrevObject ); + aLine.append( " 0 R" ); + } + if( rItem.m_nNextObject ) + { + aLine.append( "/Next " ); + aLine.append( rItem.m_nNextObject ); + aLine.append( " 0 R" ); + } + } + aLine.append( ">>\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + } + + return m_aOutline[0].m_nObject; +} + +#undef CHECK_RETURN +#define CHECK_RETURN( x ) if( !x ) return false + +bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer ) +{ + if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID ); +#endif + return false; + } + + + const PDFDest& rDest = m_aDests[ nDestID ]; + const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; + + rBuffer.append( '[' ); + rBuffer.append( rDestPage.m_nPageObject ); + rBuffer.append( " 0 R" ); + + switch( rDest.m_eType ) + { + case PDFWriter::XYZ: + default: + rBuffer.append( "/XYZ " ); + appendFixedInt( rDest.m_aRect.Left(), rBuffer ); + rBuffer.append( ' ' ); + appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); + rBuffer.append( " 0" ); + break; + case PDFWriter::Fit: + rBuffer.append( "/Fit" ); + break; + case PDFWriter::FitRectangle: + rBuffer.append( "/FitR " ); + appendFixedInt( rDest.m_aRect.Left(), rBuffer ); + rBuffer.append( ' ' ); + appendFixedInt( rDest.m_aRect.Top(), rBuffer ); + rBuffer.append( ' ' ); + appendFixedInt( rDest.m_aRect.Right(), rBuffer ); + rBuffer.append( ' ' ); + appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); + break; + case PDFWriter::FitHorizontal: + rBuffer.append( "/FitH " ); + appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); + break; + case PDFWriter::FitVertical: + rBuffer.append( "/FitV " ); + appendFixedInt( rDest.m_aRect.Left(), rBuffer ); + break; + case PDFWriter::FitPageBoundingBox: + rBuffer.append( "/FitB" ); + break; + case PDFWriter::FitPageBoundingBoxHorizontal: + rBuffer.append( "/FitBH " ); + appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); + break; + case PDFWriter::FitPageBoundingBoxVertical: + rBuffer.append( "/FitBV " ); + appendFixedInt( rDest.m_aRect.Left(), rBuffer ); + break; + } + rBuffer.append( ']' ); + + return true; +} + +bool PDFWriterImpl::emitLinkAnnotations() +{ + int nAnnots = m_aLinks.size(); + for( int i = 0; i < nAnnots; i++ ) + { + const PDFLink& rLink = m_aLinks[i]; + if( ! updateObject( rLink.m_nObject ) ) + continue; + + OStringBuffer aLine( 1024 ); + aLine.append( rLink.m_nObject ); + aLine.append( " 0 obj\n" ); +//i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' +// see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 + aLine.append( "<</Type/Annot" ); + if( m_bIsPDF_A1 ) + aLine.append( "/F 4" ); + aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" ); + + appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle + aLine.append( ' ' ); + appendFixedInt( rLink.m_aRect.Top(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle + aLine.append( ' ' ); + appendFixedInt( rLink.m_aRect.Bottom(), aLine ); + aLine.append( "]" ); + if( rLink.m_nDest >= 0 ) + { + aLine.append( "/Dest" ); + appendDest( rLink.m_nDest, aLine ); + } + else + { +/*--->i56629 +destination is external to the document, so +we check in the following sequence: + + if target type is neither .pdf, nor .od[tpgs], then + check if relative or absolute and act accordingly (use URI or 'launch application' as requested) + end processing + else if target is .od[tpgs]: then + if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file + processing continue + + if (new)target is .pdf : then + if GotToR is requested, then + convert the target in GoToR where the fragment of the URI is + considered the named destination in the target file, set relative or absolute as requested + else strip the fragment from URL and then set URI or 'launch application' as requested +*/ +// +// FIXME: check if the decode mechanisms for URL processing throughout this implementation +// are the correct one!! +// +// extract target file type + INetURLObject aDocumentURL( m_aContext.BaseURL ); + INetURLObject aTargetURL( rLink.m_aURL ); + sal_Int32 nSetGoToRMode = 0; + sal_Bool bTargetHasPDFExtension = sal_False; + INetProtocol eTargetProtocol = aTargetURL.GetProtocol(); + sal_Bool bIsUNCPath = sal_False; +// check if the protocol is a known one, or if there is no protocol at all (on target only) +// if there is no protocol, make the target relative to the current document directory +// getting the needed URL information from the current document path + if( eTargetProtocol == INET_PROT_NOT_VALID ) + { + if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0) + { + bIsUNCPath = sal_True; + } + else + { + INetURLObject aNewBase( aDocumentURL );//duplicate document URL + aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the + //target document + aNewBase.insertName( rLink.m_aURL ); + aTargetURL = aNewBase;//reassign the new target URL +//recompute the target protocol, with the new URL +//normal URL processing resumes + eTargetProtocol = aTargetURL.GetProtocol(); + } + } + + rtl::OUString aFileExtension = aTargetURL.GetFileExtension(); + +// Check if the URL ends in '/': if yes it's a directory, +// it will be forced to a URI link. +// possibly a malformed URI, leave it as it is, force as URI + if( aTargetURL.hasFinalSlash() ) + m_aContext.DefaultLinkAction = PDFWriter::URIAction; + + if( aFileExtension.getLength() > 0 ) + { + if( m_aContext.ConvertOOoTargetToPDFTarget ) + { + sal_Int32 bChangeFileExtensionToPDF = false; + //examine the file type (.odm .odt. .odp, odg, ods) + if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) ) + bChangeFileExtensionToPDF = true; + if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) ) + bChangeFileExtensionToPDF = true; + else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) ) + bChangeFileExtensionToPDF = true; + else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) ) + bChangeFileExtensionToPDF = true; + else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) ) + bChangeFileExtensionToPDF = true; + if( bChangeFileExtensionToPDF ) + aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) ); + } +//check if extension is pdf, see if GoToR should be forced + bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) ); + if( m_aContext.ForcePDFAction && bTargetHasPDFExtension ) + nSetGoToRMode++; + } +//prepare the URL, if relative or not + INetProtocol eBaseProtocol = aDocumentURL.GetProtocol(); +//queue the string common to all types of actions + aLine.append( "/A<</Type/Action/S"); + if( bIsUNCPath ) // handle Win UNC paths + { + aLine.append( "/Launch/Win<</F" ); + // INetURLObject is not good with UNC paths, use original path + appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); + aLine.append( ">>" ); + } + else + { + bool bSetRelative = false; + bool bFileSpec = false; +//check if relative file link is requested and if the protocol is 'file://' + if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE ) + bSetRelative = true; + + rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is, + if( nSetGoToRMode == 0 ) + { + switch( m_aContext.DefaultLinkAction ) + { + default: + case PDFWriter::URIAction : + case PDFWriter::URIActionDestination : + aLine.append( "/URI/URI" ); + break; + case PDFWriter::LaunchAction: +// now: +// if a launch action is requested and the hyperlink target has a fragment +// and the target file does not have a pdf extension, or it's not a 'file:://' protocol +// then force the uri action on it +// This code will permit the correct opening of application on web pages, the one that +// normally have fragments (but I may be wrong...) +// and will force the use of URI when the protocol is not file:// + if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) || + eTargetProtocol != INET_PROT_FILE ) + { + aLine.append( "/URI/URI" ); + } + else + { + aLine.append( "/Launch/F" ); + bFileSpec = true; + } + break; + } + } +//fragment are encoded in the same way as in the named destination processing + if( nSetGoToRMode ) + { + //add the fragment + rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET ); + aLine.append("/GoToR"); + aLine.append("/F"); + bFileSpec = true; + appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark, + INetURLObject::WAS_ENCODED, + INetURLObject::DECODE_WITH_CHARSET ) : + aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); + if( aFragment.getLength() > 0 ) + { + aLine.append("/D/"); + appendDestinationName( aFragment , aLine ); + } + } + else + { +// change the fragment to accomodate the bookmark (only if the file extension is PDF and +// the requested action is of the correct type) + if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination && + bTargetHasPDFExtension && aFragment.getLength() > 0 ) + { + OStringBuffer aLineLoc( 1024 ); + appendDestinationName( aFragment , aLineLoc ); +//substitute the fragment + aTargetURL.SetMark( aLineLoc.getStr() ); + } + rtl::OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE ); + appendLiteralStringEncrypt(bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL, + INetURLObject::WAS_ENCODED, + bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE + ) : + aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); + } +//<--- i56629 + } + aLine.append( ">>\n" ); + } + if( rLink.m_nStructParent > 0 ) + { + aLine.append( "/StructParent " ); + aLine.append( rLink.m_nStructParent ); + } + aLine.append( ">>\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + } + + return true; +} + +bool PDFWriterImpl::emitNoteAnnotations() +{ + // emit note annotations + int nAnnots = m_aNotes.size(); + for( int i = 0; i < nAnnots; i++ ) + { + const PDFNoteEntry& rNote = m_aNotes[i]; + if( ! updateObject( rNote.m_nObject ) ) + return false; + + OStringBuffer aLine( 1024 ); + aLine.append( rNote.m_nObject ); + aLine.append( " 0 obj\n" ); +//i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' +// see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 + aLine.append( "<</Type/Annot" ); + if( m_bIsPDF_A1 ) + aLine.append( "/F 4" ); + aLine.append( "/Subtype/Text/Rect[" ); + + appendFixedInt( rNote.m_aRect.Left(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rNote.m_aRect.Top(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rNote.m_aRect.Right(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rNote.m_aRect.Bottom(), aLine ); + aLine.append( "]" ); + + // contents of the note (type text string) + aLine.append( "/Contents\n" ); + appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine ); + aLine.append( "\n" ); + + // optional title + if( rNote.m_aContents.Title.Len() ) + { + aLine.append( "/T" ); + appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine ); + aLine.append( "\n" ); + } + + aLine.append( ">>\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + } + return true; +} + +Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font& rAppSetFont ) +{ + bool bAdjustSize = false; + + Font aFont( rControlFont ); + if( ! aFont.GetName().Len() ) + { + aFont = rAppSetFont; + if( rControlFont.GetHeight() ) + aFont.SetSize( Size( 0, rControlFont.GetHeight() ) ); + else + bAdjustSize = true; + if( rControlFont.GetItalic() != ITALIC_DONTKNOW ) + aFont.SetItalic( rControlFont.GetItalic() ); + if( rControlFont.GetWeight() != WEIGHT_DONTKNOW ) + aFont.SetWeight( rControlFont.GetWeight() ); + } + else if( ! aFont.GetHeight() ) + { + aFont.SetSize( rAppSetFont.GetSize() ); + bAdjustSize = true; + } + if( bAdjustSize ) + { + Size aFontSize = aFont.GetSize(); + OutputDevice* pDefDev = Application::GetDefaultDevice(); + aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() ); + aFont.SetSize( aFontSize ); + } + return aFont; +} + +sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont ) +{ + sal_Int32 nBest = 4; // default to Helvetica + OUString aFontName( rFont.GetName() ); + aFontName = aFontName.toAsciiLowerCase(); + + if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 ) + nBest = 8; + else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 ) + nBest = 0; + else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 ) + nBest = 13; + else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 ) + nBest = 12; + if( nBest < 12 ) + { + if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL ) + nBest += 1; + if( rFont.GetWeight() > WEIGHT_MEDIUM ) + nBest += 2; + } + + if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() ) + m_aBuiltinFontToObjectMap[ nBest ] = createObject(); + + return nBest; +} + +static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 ) +{ + return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1; +} + +void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget ) +{ + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + + // save graphics state + push( sal::static_int_cast<sal_uInt16>(~0U) ); + + // transform relative to control's coordinates since an + // appearance stream is a form XObject + // this relies on the m_aRect member of rButton NOT already being transformed + // to default user space + if( rWidget.Background || rWidget.Border ) + { + setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) ); + setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) ); + drawRectangle( rWidget.Location ); + } + // prepare font to use + Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() ); + setFont( aFont ); + setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) ); + + drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle ); + + // create DA string while local mapmode is still in place + // (that is before endRedirect()) + OStringBuffer aDA( 256 ); + appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA ); + Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() ); + sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont ); + aDA.append( ' ' ); + aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() ); + aDA.append( ' ' ); + m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); + aDA.append( " Tf" ); + rButton.m_aDAString = aDA.makeStringAndClear(); + + pop(); + + rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream(); + + /* seems like a bad hack but at least works in both AR5 and 6: + we draw the button ourselves and tell AR + the button would be totally transparent with no text + + One would expect that simply setting a normal appearance + should suffice, but no, as soon as the user actually presses + the button and an action is tied to it (gasp! a button that + does something) the appearance gets replaced by some crap that AR + creates on the fly even if no DA or MK is given. On AR6 at least + the DA and MK work as expected, but on AR5 this creates a region + filled with the background color but nor text. Urgh. + */ + rButton.m_aMKDict = "/BC [] /BG [] /CA"; + rButton.m_aMKDictCAString = ""; +} + +Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern, + const PDFWriter::AnyWidget& rWidget, + const StyleSettings& rSettings ) +{ + Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() ); + + if( rWidget.Background || rWidget.Border ) + { + if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) ) + { + sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500; + if( nDelta < 1 ) + nDelta = 1; + setLineColor( Color( COL_TRANSPARENT ) ); + Rectangle aRect = rIntern.m_aRect; + setFillColor( rSettings.GetLightBorderColor() ); + drawRectangle( aRect ); + aRect.Left() += nDelta; aRect.Top() += nDelta; + aRect.Right() -= nDelta; aRect.Bottom() -= nDelta; + setFillColor( rSettings.GetFieldColor() ); + drawRectangle( aRect ); + setFillColor( rSettings.GetLightColor() ); + drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) ); + drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) ); + setFillColor( rSettings.GetDarkShadowColor() ); + drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) ); + drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) ); + } + else + { + setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) ); + setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); + drawRectangle( rIntern.m_aRect ); + } + + if( rWidget.Border ) + { + // adjust edit area accounting for border + sal_Int32 nDelta = aFont.GetHeight()/4; + if( nDelta < 1 ) + nDelta = 1; + rIntern.m_aRect.Left() += nDelta; + rIntern.m_aRect.Top() += nDelta; + rIntern.m_aRect.Right() -= nDelta; + rIntern.m_aRect.Bottom()-= nDelta; + } + } + return aFont; +} + +void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget ) +{ + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 ); + + push( sal::static_int_cast<sal_uInt16>(~0U) ); + + // prepare font to use, draw field border + Font aFont = drawFieldBorder( rEdit, rWidget, rSettings ); + sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont ); + + // prepare DA string + OStringBuffer aDA( 32 ); + appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); + aDA.append( ' ' ); + if( m_aContext.FieldsUseSystemFonts ) + { + aDA.append( "/F" ); + aDA.append( nBest ); + + OStringBuffer aDR( 32 ); + aDR.append( "/Font " ); + aDR.append( getFontDictObject() ); + aDR.append( " 0 R" ); + rEdit.m_aDRDict = aDR.makeStringAndClear(); + } + else + aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); + aDA.append( ' ' ); + m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); + aDA.append( " Tf" ); + + /* create an empty appearance stream, let the viewer create + the appearance at runtime. This is because AR5 seems to + paint the widget appearance always, and a dynamically created + appearance on top of it. AR6 is well behaved in that regard, so + that behaviour seems to be a bug. Anyway this empty appearance + relies on /NeedAppearances in the AcroForm dictionary set to "true" + */ + beginRedirect( pEditStream, rEdit.m_aRect ); + OStringBuffer aAppearance( 32 ); + aAppearance.append( "/Tx BMC\nEMC\n" ); + writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); + + endRedirect(); + pop(); + + rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream; + + rEdit.m_aDAString = aDA.makeStringAndClear(); +} + +void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget ) +{ + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 ); + + push( sal::static_int_cast<sal_uInt16>(~0U) ); + + // prepare font to use, draw field border + Font aFont = drawFieldBorder( rBox, rWidget, rSettings ); + sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont ); + + beginRedirect( pListBoxStream, rBox.m_aRect ); + OStringBuffer aAppearance( 64 ); + + setLineColor( Color( COL_TRANSPARENT ) ); + setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) ); + drawRectangle( rBox.m_aRect ); + + // empty appearance, see createDefaultEditAppearance for reference + aAppearance.append( "/Tx BMC\nEMC\n" ); + writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); + + endRedirect(); + pop(); + + rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream; + + // prepare DA string + OStringBuffer aDA( 256 ); + // prepare DA string + appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); + aDA.append( ' ' ); + if( m_aContext.FieldsUseSystemFonts ) + { + aDA.append( "/F" ); + aDA.append( nBest ); + + OStringBuffer aDR( 32 ); + aDR.append( "/Font " ); + aDR.append( getFontDictObject() ); + aDR.append( " 0 R" ); + rBox.m_aDRDict = aDR.makeStringAndClear(); + } + else + aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); + aDA.append( ' ' ); + m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); + aDA.append( " Tf" ); + rBox.m_aDAString = aDA.makeStringAndClear(); +} + +void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget ) +{ + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + + // save graphics state + push( sal::static_int_cast<sal_uInt16>(~0U) ); + + if( rWidget.Background || rWidget.Border ) + { + setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); + setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); + drawRectangle( rBox.m_aRect ); + } + + Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); + setFont( aFont ); + Size aFontSize = aFont.GetSize(); + if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) + aFontSize.Height() = rBox.m_aRect.GetHeight(); + sal_Int32 nDelta = aFontSize.Height()/10; + if( nDelta < 1 ) + nDelta = 1; + + Rectangle aCheckRect, aTextRect; + if( rWidget.ButtonIsLeft ) + { + aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; + aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; + aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); + aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); + + // #i74206# handle small controls without text area + while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) + { + aCheckRect.Right() -= nDelta; + aCheckRect.Top() += nDelta/2; + aCheckRect.Bottom() -= nDelta - (nDelta/2); + } + + aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; + aTextRect.Top() = rBox.m_aRect.Top(); + aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; + aTextRect.Bottom() = rBox.m_aRect.Bottom(); + } + else + { + aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height(); + aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; + aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); + aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); + + // #i74206# handle small controls without text area + while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) + { + aCheckRect.Left() += nDelta; + aCheckRect.Top() += nDelta/2; + aCheckRect.Bottom() -= nDelta - (nDelta/2); + } + + aTextRect.Left() = rBox.m_aRect.Left(); + aTextRect.Top() = rBox.m_aRect.Top(); + aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; + aTextRect.Bottom() = rBox.m_aRect.Bottom(); + } + setLineColor( Color( COL_BLACK ) ); + setFillColor( Color( COL_TRANSPARENT ) ); + OStringBuffer aLW( 32 ); + aLW.append( "q " ); + m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW ); + aLW.append( " w " ); + writeBuffer( aLW.getStr(), aLW.getLength() ); + drawRectangle( aCheckRect ); + writeBuffer( " Q\n", 3 ); + setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); + drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); + + pop(); + + OStringBuffer aDA( 256 ); + appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); + sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) ); + aDA.append( ' ' ); + aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); + aDA.append( " 0 Tf" ); + rBox.m_aDAString = aDA.makeStringAndClear(); + rBox.m_aMKDict = "/CA"; + rBox.m_aMKDictCAString = "8"; + rBox.m_aRect = aCheckRect; + + // create appearance streams + sal_Char cMark = '8'; + sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)]; + nCharXOffset *= aCheckRect.GetHeight(); + nCharXOffset /= 2000; + sal_Int32 nCharYOffset = 1000- + (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative + nCharYOffset *= aCheckRect.GetHeight(); + nCharYOffset /= 2000; + + SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); + beginRedirect( pCheckStream, aCheckRect ); + aDA.append( "/Tx BMC\nq BT\n" ); + appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); + aDA.append( ' ' ); + aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); + aDA.append( ' ' ); + m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); + aDA.append( " Tf\n" ); + m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA ); + aDA.append( " " ); + m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA ); + aDA.append( " Td (" ); + aDA.append( cMark ); + aDA.append( ") Tj\nET\nQ\nEMC\n" ); + writeBuffer( aDA.getStr(), aDA.getLength() ); + endRedirect(); + rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; + + SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); + beginRedirect( pUncheckStream, aCheckRect ); + writeBuffer( "/Tx BMC\nEMC\n", 12 ); + endRedirect(); + rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; +} + +void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget ) +{ + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + + // save graphics state + push( sal::static_int_cast<sal_uInt16>(~0U) ); + + if( rWidget.Background || rWidget.Border ) + { + setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); + setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); + drawRectangle( rBox.m_aRect ); + } + + Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); + setFont( aFont ); + Size aFontSize = aFont.GetSize(); + if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) + aFontSize.Height() = rBox.m_aRect.GetHeight(); + sal_Int32 nDelta = aFontSize.Height()/10; + if( nDelta < 1 ) + nDelta = 1; + + Rectangle aCheckRect, aTextRect; + if( rWidget.ButtonIsLeft ) + { + aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; + aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; + aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); + aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); + + // #i74206# handle small controls without text area + while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) + { + aCheckRect.Right() -= nDelta; + aCheckRect.Top() += nDelta/2; + aCheckRect.Bottom() -= nDelta - (nDelta/2); + } + + aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; + aTextRect.Top() = rBox.m_aRect.Top(); + aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; + aTextRect.Bottom() = rBox.m_aRect.Bottom(); + } + else + { + aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height(); + aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; + aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); + aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); + + // #i74206# handle small controls without text area + while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) + { + aCheckRect.Left() += nDelta; + aCheckRect.Top() += nDelta/2; + aCheckRect.Bottom() -= nDelta - (nDelta/2); + } + + aTextRect.Left() = rBox.m_aRect.Left(); + aTextRect.Top() = rBox.m_aRect.Top(); + aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; + aTextRect.Bottom() = rBox.m_aRect.Bottom(); + } + setLineColor( Color( COL_BLACK ) ); + setFillColor( Color( COL_TRANSPARENT ) ); + OStringBuffer aLW( 32 ); + aLW.append( "q " ); + m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW ); + aLW.append( " w " ); + writeBuffer( aLW.getStr(), aLW.getLength() ); + drawEllipse( aCheckRect ); + writeBuffer( " Q\n", 3 ); + setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); + drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); + + pop(); + + OStringBuffer aDA( 256 ); + appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); + sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) ); + aDA.append( ' ' ); + aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); + aDA.append( " 0 Tf" ); + rBox.m_aDAString = aDA.makeStringAndClear(); +//to encrypt this (el) + rBox.m_aMKDict = "/CA"; +//after this assignement, to m_aMKDic cannot be added anything + rBox.m_aMKDictCAString = "l"; + + rBox.m_aRect = aCheckRect; + + // create appearance streams + push( sal::static_int_cast<sal_uInt16>(~0U) ); + SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); + + beginRedirect( pCheckStream, aCheckRect ); + aDA.append( "/Tx BMC\nq BT\n" ); + appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); + aDA.append( ' ' ); + aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); + aDA.append( ' ' ); + m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); + aDA.append( " Tf\n0 0 Td\nET\nQ\n" ); + writeBuffer( aDA.getStr(), aDA.getLength() ); + setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); + setLineColor( Color( COL_TRANSPARENT ) ); + aCheckRect.Left() += 3*nDelta; + aCheckRect.Top() += 3*nDelta; + aCheckRect.Bottom() -= 3*nDelta; + aCheckRect.Right() -= 3*nDelta; + drawEllipse( aCheckRect ); + writeBuffer( "\nEMC\n", 5 ); + endRedirect(); + + pop(); + rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; + + SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); + beginRedirect( pUncheckStream, aCheckRect ); + writeBuffer( "/Tx BMC\nEMC\n", 12 ); + endRedirect(); + rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; +} + +bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict ) +{ + + // TODO: check and insert default streams + rtl::OString aStandardAppearance; + switch( rWidget.m_eType ) + { + case PDFWriter::CheckBox: + aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US ); + break; + default: + break; + } + + if( rWidget.m_aAppearances.size() ) + { + rAnnotDict.append( "/AP<<\n" ); + for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it ) + { + rAnnotDict.append( "/" ); + rAnnotDict.append( dict_it->first ); + bool bUseSubDict = (dict_it->second.size() > 1); + rAnnotDict.append( bUseSubDict ? "<<" : " " ); + + for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin(); + stream_it != dict_it->second.end(); ++stream_it ) + { + SvMemoryStream* pApppearanceStream = stream_it->second; + dict_it->second[ stream_it->first ] = NULL; + + bool bDeflate = compressStream( pApppearanceStream ); + + pApppearanceStream->Seek( STREAM_SEEK_TO_END ); + sal_Int64 nStreamLen = pApppearanceStream->Tell(); + pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN ); + sal_Int32 nObject = createObject(); + CHECK_RETURN( updateObject( nObject ) ); + #if OSL_DEBUG_LEVEL > 1 + emitComment( "PDFWriterImpl::emitAppearances" ); + #endif + OStringBuffer aLine; + aLine.append( nObject ); + + aLine.append( " 0 obj\n" + "<</Type/XObject\n" + "/Subtype/Form\n" + "/BBox[0 0 " ); + appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine ); + aLine.append( " " ); + appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine ); + aLine.append( "]\n" + "/Resources " ); + aLine.append( getResourceDictObj() ); + aLine.append( " 0 R\n" + "/Length " ); + aLine.append( nStreamLen ); + aLine.append( "\n" ); + if( bDeflate ) + aLine.append( "/Filter/FlateDecode\n" ); + aLine.append( ">>\nstream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + checkAndEnableStreamEncryption( nObject ); + CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) ); + disableStreamEncryption(); + CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) ); + + if( bUseSubDict ) + { + rAnnotDict.append( " /" ); + rAnnotDict.append( stream_it->first ); + rAnnotDict.append( " " ); + } + rAnnotDict.append( nObject ); + rAnnotDict.append( " 0 R" ); + + delete pApppearanceStream; + } + + rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" ); + } + rAnnotDict.append( ">>\n" ); + if( aStandardAppearance.getLength() ) + { + rAnnotDict.append( "/AS /" ); + rAnnotDict.append( aStandardAppearance ); + rAnnotDict.append( "\n" ); + } + } + + return true; +} + +bool PDFWriterImpl::emitWidgetAnnotations() +{ + ensureUniqueRadioOnValues(); + + int nAnnots = m_aWidgets.size(); + for( int a = 0; a < nAnnots; a++ ) + { + PDFWidget& rWidget = m_aWidgets[a]; + + OStringBuffer aLine( 1024 ); + OStringBuffer aValue( 256 ); + aLine.append( rWidget.m_nObject ); + aLine.append( " 0 obj\n" + "<<" ); + if( rWidget.m_eType != PDFWriter::Hierarchy ) + { + // emit widget annotation only for terminal fields + if( rWidget.m_aKids.empty() ) + { + aLine.append( "/Type/Annot/Subtype/Widget/F 4\n" + "/Rect[" ); + appendFixedInt( rWidget.m_aRect.Left()-1, aLine ); + aLine.append( ' ' ); + appendFixedInt( rWidget.m_aRect.Top()+1, aLine ); + aLine.append( ' ' ); + appendFixedInt( rWidget.m_aRect.Right()+1, aLine ); + aLine.append( ' ' ); + appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine ); + aLine.append( "]\n" ); + } + aLine.append( "/FT/" ); + switch( rWidget.m_eType ) + { + case PDFWriter::RadioButton: + case PDFWriter::CheckBox: + // for radio buttons only the RadioButton field, not the + // CheckBox children should have a value, else acrobat reader + // does not always check the right button + // of course real check boxes (not belonging to a readio group) + // need their values, too + if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 ) + { + aValue.append( "/" ); + // check for radio group with all buttons unpressed + if( rWidget.m_aValue.getLength() == 0 ) + aValue.append( "Off" ); + else + appendName( rWidget.m_aValue, aValue ); + } + case PDFWriter::PushButton: + aLine.append( "Btn" ); + break; + case PDFWriter::ListBox: + if( rWidget.m_nFlags & 0x200000 ) // multiselect + { + aValue.append( "[" ); + for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ ) + { + sal_Int32 nEntry = rWidget.m_aSelectedEntries[i]; + if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) ) + appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue ); + } + aValue.append( "]" ); + } + else if( rWidget.m_aSelectedEntries.size() > 0 && + rWidget.m_aSelectedEntries[0] >= 0 && + rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) ) + { + appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue ); + } + else + appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue ); + aLine.append( "Ch" ); + break; + case PDFWriter::ComboBox: + appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); + aLine.append( "Ch" ); + break; + case PDFWriter::Edit: + aLine.append( "Tx" ); + appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); + break; + case PDFWriter::Hierarchy: // make the compiler happy + break; + } + aLine.append( "\n" ); + aLine.append( "/P " ); + aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject ); + aLine.append( " 0 R\n" ); + } + if( rWidget.m_nParent ) + { + aLine.append( "/Parent " ); + aLine.append( rWidget.m_nParent ); + aLine.append( " 0 R\n" ); + } + if( rWidget.m_aKids.size() ) + { + aLine.append( "/Kids[" ); + for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ ) + { + aLine.append( rWidget.m_aKids[i] ); + aLine.append( " 0 R" ); + aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); + } + aLine.append( "]\n" ); + } + if( rWidget.m_aName.getLength() ) + { + aLine.append( "/T" ); + appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine ); + aLine.append( "\n" ); + } + if( m_aContext.Version > PDFWriter::PDF_1_2 && rWidget.m_aDescription.getLength() ) + { + // the alternate field name should be unicode able since it is + // supposed to be used in UI + aLine.append( "/TU" ); + appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine ); + aLine.append( "\n" ); + } + + if( rWidget.m_nFlags ) + { + aLine.append( "/Ff " ); + aLine.append( rWidget.m_nFlags ); + aLine.append( "\n" ); + } + if( aValue.getLength() ) + { + OString aVal = aValue.makeStringAndClear(); + aLine.append( "/V " ); + aLine.append( aVal ); + aLine.append( "\n" + "/DV " ); + aLine.append( aVal ); + aLine.append( "\n" ); + } + if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox ) + { + sal_Int32 nTI = -1; + aLine.append( "/Opt[\n" ); + sal_Int32 i = 0; + for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i ) + { + appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine ); + aLine.append( "\n" ); + if( *it == rWidget.m_aValue ) + nTI = i; + } + aLine.append( "]\n" ); + if( nTI > 0 ) + { + aLine.append( "/TI " ); + aLine.append( nTI ); + aLine.append( "\n" ); + if( rWidget.m_nFlags & 0x200000 ) // Multiselect + { + aLine.append( "/I [" ); + aLine.append( nTI ); + aLine.append( "]\n" ); + } + } + } + if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 ) + { + aLine.append( "/MaxLen " ); + aLine.append( rWidget.m_nMaxLen ); + aLine.append( "\n" ); + } + if( rWidget.m_eType == PDFWriter::PushButton ) + { + if(!m_bIsPDF_A1) + { + OStringBuffer aDest; + if( rWidget.m_nDest != -1 && appendDest( rWidget.m_nDest, aDest ) ) + { + aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " ); + aLine.append( aDest.makeStringAndClear() ); + aLine.append( ">>>>\n" ); + } + else if( rWidget.m_aListEntries.empty() ) + { + // create a reset form action + aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" ); + } + else if( rWidget.m_bSubmit ) + { + // create a submit form action + aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" ); + appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() ); + aLine.append( "/Flags " ); + + sal_Int32 nFlags = 0; + switch( m_aContext.SubmitFormat ) + { + case PDFWriter::HTML: + nFlags |= 4; + break; + case PDFWriter::XML: + if( m_aContext.Version > PDFWriter::PDF_1_3 ) + nFlags |= 32; + break; + case PDFWriter::PDF: + if( m_aContext.Version > PDFWriter::PDF_1_3 ) + nFlags |= 256; + break; + case PDFWriter::FDF: + default: + break; + } + if( rWidget.m_bSubmitGet ) + nFlags |= 8; + aLine.append( nFlags ); + aLine.append( ">>>>\n" ); + } + else + { + // create a URI action + aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" ); + aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) ); + aLine.append( ")>>>>\n" ); + } + } + else + m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA ); + } + if( rWidget.m_aDAString.getLength() ) + { + if( rWidget.m_aDRDict.getLength() ) + { + aLine.append( "/DR<<" ); + aLine.append( rWidget.m_aDRDict ); + aLine.append( ">>\n" ); + } + else + { + aLine.append( "/DR<</Font<<" ); + appendBuiltinFontsToDict( aLine ); + aLine.append( ">>>>\n" ); + } + aLine.append( "/DA" ); + appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine ); + aLine.append( "\n" ); + if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER ) + aLine.append( "/Q 1\n" ); + else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT ) + aLine.append( "/Q 2\n" ); + } + // appearance charactristics for terminal fields + // which are supposed to have an appearance constructed + // by the viewer application + if( rWidget.m_aMKDict.getLength() ) + { + aLine.append( "/MK<<" ); + aLine.append( rWidget.m_aMKDict ); +//add the CA string, encrypting it + appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine); + aLine.append( ">>\n" ); + } + + CHECK_RETURN( emitAppearances( rWidget, aLine ) ); + + aLine.append( ">>\n" + "endobj\n\n" ); + CHECK_RETURN( updateObject( rWidget.m_nObject ) ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + } + return true; +} + +bool PDFWriterImpl::emitAnnotations() +{ + if( m_aPages.size() < 1 ) + return false; + + CHECK_RETURN( emitLinkAnnotations() ); + + CHECK_RETURN( emitNoteAnnotations() ); + + CHECK_RETURN( emitWidgetAnnotations() ); + + return true; +} + +#undef CHECK_RETURN +#define CHECK_RETURN( x ) if( !x ) return false + +bool PDFWriterImpl::emitCatalog() +{ + // build page tree + // currently there is only one node that contains all leaves + + // first create a page tree node id + sal_Int32 nTreeNode = createObject(); + + // emit global resource dictionary (page emit needs it) + CHECK_RETURN( emitResources() ); + + // emit all pages + for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it ) + if( ! it->emit( nTreeNode ) ) + return false; + + sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations(); + + sal_Int32 nOutlineDict = emitOutline(); + + //emit Output intent i59651 + sal_Int32 nOutputIntentObject = emitOutputIntent(); + + //emit metadata + sal_Int32 nMetadataObject = emitDocumentMetadata(); + + sal_Int32 nStructureDict = 0; + if(m_aStructure.size() > 1) + { +///check if dummy structure containers are needed + addInternalStructureContainer(m_aStructure[0]); + nStructureDict = m_aStructure[0].m_nObject = createObject(); + emitStructure( m_aStructure[ 0 ] ); + } + + // adjust tree node file offset + if( ! updateObject( nTreeNode ) ) + return false; + + // emit tree node + OStringBuffer aLine( 2048 ); + aLine.append( nTreeNode ); + aLine.append( " 0 obj\n" ); + aLine.append( "<</Type/Pages\n" ); + aLine.append( "/Resources " ); + aLine.append( getResourceDictObj() ); + aLine.append( " 0 R\n" ); + + switch( m_eInheritedOrientation ) + { + case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break; + case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break; + + case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant + case PDFWriter::Portrait: + default: + break; + } + sal_Int32 nMediaBoxWidth = 0; + sal_Int32 nMediaBoxHeight = 0; + if( m_aPages.empty() ) // sanity check, this should not happen + { + nMediaBoxWidth = m_nInheritedPageWidth; + nMediaBoxHeight = m_nInheritedPageHeight; + } + else + { + for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter ) + { + if( iter->m_nPageWidth > nMediaBoxWidth ) + nMediaBoxWidth = iter->m_nPageWidth; + if( iter->m_nPageHeight > nMediaBoxHeight ) + nMediaBoxHeight = iter->m_nPageHeight; + } + } + aLine.append( "/MediaBox[ 0 0 " ); + aLine.append( nMediaBoxWidth ); + aLine.append( ' ' ); + aLine.append( nMediaBoxHeight ); + aLine.append( " ]\n" + "/Kids[ " ); + unsigned int i = 0; + for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ ) + { + aLine.append( iter->m_nPageObject ); + aLine.append( " 0 R" ); + aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); + } + aLine.append( "]\n" + "/Count " ); + aLine.append( (sal_Int32)m_aPages.size() ); + aLine.append( ">>\n" + "endobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + // emit annotation objects + CHECK_RETURN( emitAnnotations() ); + + // emit Catalog + m_nCatalogObject = createObject(); + if( ! updateObject( m_nCatalogObject ) ) + return false; + aLine.setLength( 0 ); + aLine.append( m_nCatalogObject ); + aLine.append( " 0 obj\n" + "<</Type/Catalog/Pages " ); + aLine.append( nTreeNode ); + aLine.append( " 0 R\n" ); +//--->i56629 +//check if there are named destinations to emit (root must be inside the catalog) + if( nNamedDestinationsDictionary ) + { + aLine.append("/Dests "); + aLine.append( nNamedDestinationsDictionary ); + aLine.append( " 0 R\n" ); + } +//<---- + if( m_aContext.PageLayout != PDFWriter::DefaultLayout ) + switch( m_aContext.PageLayout ) + { + default : + case PDFWriter::SinglePage : + aLine.append( "/PageLayout/SinglePage\n" ); + break; + case PDFWriter::Continuous : + aLine.append( "/PageLayout/OneColumn\n" ); + break; + case PDFWriter::ContinuousFacing : +//the flag m_aContext.FirstPageLeft below is used to set the page on the left side + aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side + break; + } + if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode ) + switch( m_aContext.PDFDocumentMode ) + { + default : + aLine.append( "/PageMode/UseNone\n" ); + break; + case PDFWriter::UseOutlines : + aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open + break; + case PDFWriter::UseThumbs : + aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open + break; + } + else if( m_aContext.OpenInFullScreenMode ) + aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen + + OStringBuffer aInitPageRef; + if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() ) + { + aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject ); + aInitPageRef.append( " 0 R" ); + } + else + aInitPageRef.append( "0" ); + switch( m_aContext.PDFDocumentAction ) + { + case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default + default: + if( aInitPageRef.getLength() > 1 ) + { + aLine.append( "/OpenAction[" ); + aLine.append( aInitPageRef ); + aLine.append( " /XYZ null null 0]\n" ); + } + break; + case PDFWriter::FitInWindow : + aLine.append( "/OpenAction[" ); + aLine.append( aInitPageRef ); + aLine.append( " /Fit]\n" ); //Open fit page + break; + case PDFWriter::FitWidth : + aLine.append( "/OpenAction[" ); + aLine.append( aInitPageRef ); + aLine.append( " /FitH " ); + aLine.append( m_nInheritedPageHeight );//Open fit width + aLine.append( "]\n" ); + break; + case PDFWriter::FitVisible : + aLine.append( "/OpenAction[" ); + aLine.append( aInitPageRef ); + aLine.append( " /FitBH " ); + aLine.append( m_nInheritedPageHeight );//Open fit visible + aLine.append( "]\n" ); + break; + case PDFWriter::ActionZoom : + aLine.append( "/OpenAction[" ); + aLine.append( aInitPageRef ); + aLine.append( " /XYZ null null " ); + if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 ) + aLine.append( (double)m_aContext.Zoom/100.0 ); + else + aLine.append( "0" ); + aLine.append( "]\n" ); + break; + } +// viewer preferences, if we had some, then emit + if( m_aContext.HideViewerToolbar || + ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) || + m_aContext.HideViewerMenubar || + m_aContext.HideViewerWindowControls || m_aContext.FitWindow || + m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) || + m_aContext.OpenInFullScreenMode ) + { + aLine.append( "/ViewerPreferences<<" ); + if( m_aContext.HideViewerToolbar ) + aLine.append( "/HideToolbar true\n" ); + if( m_aContext.HideViewerMenubar ) + aLine.append( "/HideMenubar true\n" ); + if( m_aContext.HideViewerWindowControls ) + aLine.append( "/HideWindowUI true\n" ); + if( m_aContext.FitWindow ) + aLine.append( "/FitWindow true\n" ); + if( m_aContext.CenterWindow ) + aLine.append( "/CenterWindow true\n" ); + if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) + aLine.append( "/DisplayDocTitle true\n" ); + if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) + aLine.append( "/Direction/R2L\n" ); + if( m_aContext.OpenInFullScreenMode ) + switch( m_aContext.PDFDocumentMode ) + { + default : + case PDFWriter::ModeDefault : + aLine.append( "/NonFullScreenPageMode/UseNone\n" ); + break; + case PDFWriter::UseOutlines : + aLine.append( "/NonFullScreenPageMode/UseOutlines\n" ); + break; + case PDFWriter::UseThumbs : + aLine.append( "/NonFullScreenPageMode/UseThumbs\n" ); + break; + } + aLine.append( ">>\n" ); + } + + if( nOutlineDict ) + { + aLine.append( "/Outlines " ); + aLine.append( nOutlineDict ); + aLine.append( " 0 R\n" ); + } + if( nStructureDict ) + { + aLine.append( "/StructTreeRoot " ); + aLine.append( nStructureDict ); + aLine.append( " 0 R\n" ); + } + if( m_aContext.DocumentLocale.Language.getLength() > 0 ) + { + OUStringBuffer aLocBuf( 16 ); + aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() ); + if( m_aContext.DocumentLocale.Country.getLength() > 0 ) + { + aLocBuf.append( sal_Unicode('-') ); + aLocBuf.append( m_aContext.DocumentLocale.Country ); + } + aLine.append( "/Lang" ); + appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine ); + aLine.append( "\n" ); + } + if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 ) + { + aLine.append( "/MarkInfo<</Marked true>>\n" ); + } + if( m_aWidgets.size() > 0 ) + { + aLine.append( "/AcroForm<</Fields[\n" ); + int nWidgets = m_aWidgets.size(); + int nOut = 0; + for( int j = 0; j < nWidgets; j++ ) + { + // output only root fields + if( m_aWidgets[j].m_nParent < 1 ) + { + aLine.append( m_aWidgets[j].m_nObject ); + aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " ); + } + } + aLine.append( "\n]/DR " ); + aLine.append( getResourceDictObj() ); + aLine.append( " 0 R" ); + if( m_bIsPDF_A1 ) + aLine.append( ">>\n" ); + else + aLine.append( "/NeedAppearances true>>\n" ); + } +//--->i59651 +//check if there is a Metadata object + if( nOutputIntentObject ) + { + aLine.append("/OutputIntents["); + aLine.append( nOutputIntentObject ); + aLine.append( " 0 R]" ); + } + if( nMetadataObject ) + { + aLine.append("/Metadata "); + aLine.append( nMetadataObject ); + aLine.append( " 0 R" ); + } +//<---- + aLine.append( ">>\n" + "endobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + return true; +} + +sal_Int32 PDFWriterImpl::emitInfoDict( ) +{ + sal_Int32 nObject = createObject(); + + if( updateObject( nObject ) ) + { + OStringBuffer aLine( 1024 ); + aLine.append( nObject ); + aLine.append( " 0 obj\n" + "<<" ); + if( m_aContext.DocumentInfo.Title.Len() ) + { + aLine.append( "/Title" ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine ); + aLine.append( "\n" ); + } + if( m_aContext.DocumentInfo.Author.Len() ) + { + aLine.append( "/Author" ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine ); + aLine.append( "\n" ); + } + if( m_aContext.DocumentInfo.Subject.Len() ) + { + aLine.append( "/Subject" ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine ); + aLine.append( "\n" ); + } + if( m_aContext.DocumentInfo.Keywords.Len() ) + { + aLine.append( "/Keywords" ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine ); + aLine.append( "\n" ); + } + if( m_aContext.DocumentInfo.Creator.Len() ) + { + aLine.append( "/Creator" ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine ); + aLine.append( "\n" ); + } + if( m_aContext.DocumentInfo.Producer.Len() ) + { + aLine.append( "/Producer" ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine ); + aLine.append( "\n" ); + } + + aLine.append( "/CreationDate" ); + appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine ); + aLine.append( ">>\nendobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + nObject = 0; + } + else + nObject = 0; + + return nObject; +} + +//--->i56629 +// Part of this function may be shared with method appendDest. +// +sal_Int32 PDFWriterImpl::emitNamedDestinations() +{ + sal_Int32 nCount = m_aNamedDests.size(); + if( nCount <= 0 ) + return 0;//define internal error + +//get the object number for all the destinations + sal_Int32 nObject = createObject(); + + if( updateObject( nObject ) ) + { +//emit the dictionary + OStringBuffer aLine( 1024 ); + aLine.append( nObject ); + aLine.append( " 0 obj\n" + "<<" ); + + sal_Int32 nDestID; + for( nDestID = 0; nDestID < nCount; nDestID++ ) + { + const PDFNamedDest& rDest = m_aNamedDests[ nDestID ]; +// In order to correctly function both under an Internet browser and +// directly with a reader (provided the reader has the feature) we +// need to set the name of the destination the same way it will be encoded +// in an Internet link + INetURLObject aLocalURL( + OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used + aLocalURL.SetMark( rDest.m_aDestName ); + + const rtl::OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as + // in link creation ( see PDFWriterImpl::emitLinkAnnotations ) + const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; + + aLine.append( '/' ); + appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog ) + aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function + //maps the preceeding character properly + aLine.append( rDestPage.m_nPageObject ); + aLine.append( " 0 R" ); + + switch( rDest.m_eType ) + { + case PDFWriter::XYZ: + default: + aLine.append( "/XYZ " ); + appendFixedInt( rDest.m_aRect.Left(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rDest.m_aRect.Bottom(), aLine ); + aLine.append( " 0" ); + break; + case PDFWriter::Fit: + aLine.append( "/Fit" ); + break; + case PDFWriter::FitRectangle: + aLine.append( "/FitR " ); + appendFixedInt( rDest.m_aRect.Left(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rDest.m_aRect.Top(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rDest.m_aRect.Right(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rDest.m_aRect.Bottom(), aLine ); + break; + case PDFWriter::FitHorizontal: + aLine.append( "/FitH " ); + appendFixedInt( rDest.m_aRect.Bottom(), aLine ); + break; + case PDFWriter::FitVertical: + aLine.append( "/FitV " ); + appendFixedInt( rDest.m_aRect.Left(), aLine ); + break; + case PDFWriter::FitPageBoundingBox: + aLine.append( "/FitB" ); + break; + case PDFWriter::FitPageBoundingBoxHorizontal: + aLine.append( "/FitBH " ); + appendFixedInt( rDest.m_aRect.Bottom(), aLine ); + break; + case PDFWriter::FitPageBoundingBoxVertical: + aLine.append( "/FitBV " ); + appendFixedInt( rDest.m_aRect.Left(), aLine ); + break; + } + aLine.append( "]\n" ); + } +//close + + aLine.append( ">>\nendobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + nObject = 0; + } + else + nObject = 0; + + return nObject; +} +//<--- i56629 + +//--->i59651 +// emits the output intent dictionary + +sal_Int32 PDFWriterImpl::emitOutputIntent() +{ + if( !m_bIsPDF_A1 ) + return 0; + +//emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1 + + OStringBuffer aLine( 1024 ); + sal_Int32 nICCObject = createObject(); + sal_Int32 nStreamLengthObject = createObject(); + + aLine.append( nICCObject ); +// sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16) + aLine.append( " 0 obj\n<</N 3/Length " ); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 R" ); +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + aLine.append( "/Filter/FlateDecode" ); +#endif + aLine.append( ">>\nstream\n" ); + CHECK_RETURN( updateObject( nICCObject ) ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); +//get file position + sal_uInt64 nBeginStreamPos = 0; + osl_getFilePos( m_aFile, &nBeginStreamPos ); + beginCompression(); + checkAndEnableStreamEncryption( nICCObject ); + sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) ); + disableStreamEncryption(); + endCompression(); + sal_uInt64 nEndStreamPos = 0; + osl_getFilePos( m_aFile, &nEndStreamPos ); + + if( nStreamSize == 0 ) + return 0; + if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) + return 0 ; + aLine.setLength( 0 ); + +//emit the stream length object + CHECK_RETURN( updateObject( nStreamLengthObject ) ); + aLine.setLength( 0 ); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 obj\n" ); + aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); + aLine.append( "\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + aLine.setLength( 0 ); + +//emit the OutputIntent dictionary + sal_Int32 nOIObject = createObject(); + CHECK_RETURN( updateObject( nOIObject ) ); + aLine.append( nOIObject ); + aLine.append( " 0 obj\n" + "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier"); + + rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) ); + appendLiteralStringEncrypt( aComment ,nOIObject, aLine ); + aLine.append("/DestOutputProfile "); + aLine.append( nICCObject ); + aLine.append( " 0 R>>\nendobj\n\n" );; + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + return nOIObject; +} + +// formats the string for the XML stream +static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue) +{ + const sal_Unicode* pUni = rStr.getStr(); + int nLen = rStr.getLength(); + for( ; nLen; nLen--, pUni++ ) + { + switch( *pUni ) + { + case sal_Unicode('&'): + rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&" ) ); + break; + case sal_Unicode('<'): + rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "<" ) ); + break; + case sal_Unicode('>'): + rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ">" ) ); + break; + case sal_Unicode('\''): + rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "'" ) ); + break; + case sal_Unicode('"'): + rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( """ ) ); + break; + default: + rValue += rtl::OUString( *pUni ); + break; + } + } +} + +// emits the document metadata +// +sal_Int32 PDFWriterImpl::emitDocumentMetadata() +{ + if( !m_bIsPDF_A1 ) + return 0; + + //get the object number for all the destinations + sal_Int32 nObject = createObject(); + + if( updateObject( nObject ) ) + { +// the following string are written in UTF-8 unicode + OStringBuffer aMetadataStream( 8192 ); + + aMetadataStream.append( "<?xpacket begin=\"" ); +// this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used +// as a byte-order marker. + aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) ); + aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" ); + aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" ); + aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" ); +//PDF/A part ( ISO 19005-1:2005 - 6.7.11 ) + aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); + aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" ); + aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" ); + aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" ); + aMetadataStream.append( " </rdf:Description>\n" ); +//... Dublin Core properties go here + if( m_aContext.DocumentInfo.Title.Len() || + m_aContext.DocumentInfo.Author.Len() || + m_aContext.DocumentInfo.Subject.Len() ) + { + aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); + aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" ); + if( m_aContext.DocumentInfo.Title.Len() ) + { +// this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) + aMetadataStream.append( " <dc:title>\n" ); + aMetadataStream.append( " <rdf:Alt>\n" ); + aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); + rtl::OUString aTitle; + escapeStringXML( m_aContext.DocumentInfo.Title, aTitle ); + aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) ); + aMetadataStream.append( "</rdf:li>\n" ); + aMetadataStream.append( " </rdf:Alt>\n" ); + aMetadataStream.append( " </dc:title>\n" ); + } + if( m_aContext.DocumentInfo.Author.Len() ) + { + aMetadataStream.append( " <dc:creator>\n" ); + aMetadataStream.append( " <rdf:Seq>\n" ); + aMetadataStream.append( " <rdf:li>" ); + rtl::OUString aAuthor; + escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor ); + aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) ); + aMetadataStream.append( "</rdf:li>\n" ); + aMetadataStream.append( " </rdf:Seq>\n" ); + aMetadataStream.append( " </dc:creator>\n" ); + } + if( m_aContext.DocumentInfo.Subject.Len() ) + { +// this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) + aMetadataStream.append( " <dc:description>\n" ); + aMetadataStream.append( " <rdf:Alt>\n" ); + aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); + rtl::OUString aSubject; + escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject ); + aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) ); + aMetadataStream.append( "</rdf:li>\n" ); + aMetadataStream.append( " </rdf:Alt>\n" ); + aMetadataStream.append( " </dc:description>\n" ); + } + aMetadataStream.append( " </rdf:Description>\n" ); + } + +//... PDF properties go here + if( m_aContext.DocumentInfo.Producer.Len() || + m_aContext.DocumentInfo.Keywords.Len() ) + { + aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); + aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" ); + if( m_aContext.DocumentInfo.Producer.Len() ) + { + aMetadataStream.append( " <pdf:Producer>" ); + rtl::OUString aProducer; + escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer ); + aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) ); + aMetadataStream.append( "</pdf:Producer>\n" ); + } + if( m_aContext.DocumentInfo.Keywords.Len() ) + { + aMetadataStream.append( " <pdf:Keywords>" ); + rtl::OUString aKeywords; + escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords ); + aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) ); + aMetadataStream.append( "</pdf:Keywords>\n" ); + } + aMetadataStream.append( " </rdf:Description>\n" ); + } + + aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); + aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" ); + if( m_aContext.DocumentInfo.Creator.Len() ) + { + aMetadataStream.append( " <xmp:CreatorTool>" ); + rtl::OUString aCreator; + escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator ); + aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) ); + aMetadataStream.append( "</xmp:CreatorTool>\n" ); + } +//creation date + aMetadataStream.append( " <xmp:CreateDate>" ); + aMetadataStream.append( m_aCreationMetaDateString ); + aMetadataStream.append( "</xmp:CreateDate>\n" ); + + aMetadataStream.append( " </rdf:Description>\n" ); + aMetadataStream.append( " </rdf:RDF>\n" ); + aMetadataStream.append( "</x:xmpmeta>\n" ); + +//add the padding + for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ ) + { + aMetadataStream.append( " " ); + if( nSpaces % 100 == 0 ) + aMetadataStream.append( "\n" ); + } + + aMetadataStream.append( "<?xpacket end=\"w\"?>\n" ); + + OStringBuffer aMetadataObj( 1024 ); + + aMetadataObj.append( nObject ); + aMetadataObj.append( " 0 obj\n" ); + + aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " ); + + aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() ); + aMetadataObj.append( ">>\nstream\n" ); + CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ); +//emit the stream + CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) ); + + aMetadataObj.setLength( 0 ); + aMetadataObj.append( "\nendstream\nendobj\n\n" ); + if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ) + nObject = 0; + } + else + nObject = 0; + + return nObject; +} +//<---i59651 + +bool PDFWriterImpl::emitTrailer() +{ + // emit doc info + OString aInfoValuesOut; + sal_Int32 nDocInfoObject = emitInfoDict( ); + + sal_Int32 nSecObject = 0; + + if( m_aContext.Encryption.Encrypt() ) + { +//emit the security information +//must be emitted as indirect dictionary object, since +//Acrobat Reader 5 works only with this kind of implementation + nSecObject = createObject(); + + if( updateObject( nSecObject ) ) + { + OStringBuffer aLineS( 1024 ); + aLineS.append( nSecObject ); + aLineS.append( " 0 obj\n" + "<</Filter/Standard/V " ); + // check the version + if( m_aContext.Encryption.Security128bit ) + aLineS.append( "2/Length 128/R 3" ); + else + aLineS.append( "1/R 2" ); + + // emit the owner password, must not be encrypted + aLineS.append( "/O(" ); + appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS ); + aLineS.append( ")/U(" ); + appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS ); + aLineS.append( ")/P " );// the permission set + aLineS.append( m_nAccessPermissions ); + aLineS.append( ">>\nendobj\n\n" ); + if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) ) + nSecObject = 0; + } + else + nSecObject = 0; + } + // emit xref table + // remember start + sal_uInt64 nXRefOffset = 0; + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) ); + CHECK_RETURN( writeBuffer( "xref\n", 5 ) ); + + sal_Int32 nObjects = m_aObjects.size(); + OStringBuffer aLine; + aLine.append( "0 " ); + aLine.append( (sal_Int32)(nObjects+1) ); + aLine.append( "\n" ); + aLine.append( "0000000000 65535 f \n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + for( sal_Int32 i = 0; i < nObjects; i++ ) + { + aLine.setLength( 0 ); + OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] ); + for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ ) + aLine.append( '0' ); + aLine.append( aOffset ); + aLine.append( " 00000 n \n" ); + DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + } + + // prepare document checksum + OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 ); + if( m_aDocDigest ) + { + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) ); + for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ ) + appendHex( nMD5Sum[i], aDocChecksum ); + } + // document id set in setDocInfo method + // emit trailer + aLine.setLength( 0 ); + aLine.append( "trailer\n" + "<</Size " ); + aLine.append( (sal_Int32)(nObjects+1) ); + aLine.append( "/Root " ); + aLine.append( m_nCatalogObject ); + aLine.append( " 0 R\n" ); + if( nSecObject |= 0 ) + { + aLine.append( "/Encrypt "); + aLine.append( nSecObject ); + aLine.append( " 0 R\n" ); + } + if( nDocInfoObject ) + { + aLine.append( "/Info " ); + aLine.append( nDocInfoObject ); + aLine.append( " 0 R\n" ); + } + if( ! m_aContext.Encryption.DocumentIdentifier.empty() ) + { + aLine.append( "/ID [ <" ); + for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); + it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) + { + appendHex( sal_Int8(*it), aLine ); + } + aLine.append( ">\n" + "<" ); + for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); + it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) + { + appendHex( sal_Int8(*it), aLine ); + } + aLine.append( "> ]\n" ); + } + if( aDocChecksum.getLength() ) + { + aLine.append( "/DocChecksum /" ); + aLine.append( aDocChecksum ); + aLine.append( "\n" ); + } + if( m_aAdditionalStreams.size() > 0 ) + { + aLine.append( "/AdditionalStreams [" ); + for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ ) + { + aLine.append( "/" ); + appendName( m_aAdditionalStreams[i].m_aMimeType, aLine ); + aLine.append( " " ); + aLine.append( m_aAdditionalStreams[i].m_nStreamObject ); + aLine.append( " 0 R\n" ); + } + aLine.append( "]\n" ); + } + aLine.append( ">>\n" + "startxref\n" ); + aLine.append( (sal_Int64)nXRefOffset ); + aLine.append( "\n" + "%%EOF\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + return true; +} + +struct AnnotationSortEntry +{ + sal_Int32 nTabOrder; + sal_Int32 nObject; + sal_Int32 nWidgetIndex; + + AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) : + nTabOrder( nTab ), + nObject( nObj ), + nWidgetIndex( nI ) + {} +}; + +struct AnnotSortContainer +{ + std::set< sal_Int32 > aObjects; + std::vector< AnnotationSortEntry > aSortedAnnots; +}; + +struct AnnotSorterLess +{ + std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets; + + AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {} + + bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight ) + { + if( rLeft.nTabOrder < rRight.nTabOrder ) + return true; + if( rRight.nTabOrder < rLeft.nTabOrder ) + return false; + if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 ) + return false; + if( rRight.nWidgetIndex < 0 ) + return true; + if( rLeft.nWidgetIndex < 0 ) + return false; + // remember: widget rects are in PDF coordinates, so they are ordered down up + if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() > + m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() ) + return true; + if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() > + m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() ) + return false; + if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() < + m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() ) + return true; + return false; + } +}; + +void PDFWriterImpl::sortWidgets() +{ + // sort widget annotations on each page as per their + // TabOrder attribute + boost::unordered_map< sal_Int32, AnnotSortContainer > sorted; + int nWidgets = m_aWidgets.size(); + for( int nW = 0; nW < nWidgets; nW++ ) + { + const PDFWidget& rWidget = m_aWidgets[nW]; + if( rWidget.m_nPage >= 0 ) + { + AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ]; + // optimize vector allocation + if( rCont.aSortedAnnots.empty() ) + rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() ); + // insert widget to tab sorter + // RadioButtons are not page annotations, only their individual check boxes are + if( rWidget.m_eType != PDFWriter::RadioButton ) + { + rCont.aObjects.insert( rWidget.m_nObject ); + rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) ); + } + } + } + for( boost::unordered_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it ) + { + // append entries for non widget annotations + PDFPage& rPage = m_aPages[ it->first ]; + unsigned int nAnnots = rPage.m_aAnnotations.size(); + for( unsigned int nA = 0; nA < nAnnots; nA++ ) + if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end()) + it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) ); + + AnnotSorterLess aLess( m_aWidgets ); + std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess ); + // sanity check + if( it->second.aSortedAnnots.size() == nAnnots) + { + for( unsigned int nA = 0; nA < nAnnots; nA++ ) + rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject; + } + else + { + DBG_ASSERT( 0, "wrong number of sorted annotations" ); + #if OSL_DEBUG_LEVEL > 0 + fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n" + " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots ); + #endif + } + } + + // FIXME: implement tab order in structure tree for PDF 1.5 +} + +namespace vcl { +class PDFStreamIf : + public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream > +{ + PDFWriterImpl* m_pWriter; + bool m_bWrite; + public: + PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {} + virtual ~PDFStreamIf(); + + virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(); + virtual void SAL_CALL flush() throw(); + virtual void SAL_CALL closeOutput() throw(); +}; +} + +PDFStreamIf::~PDFStreamIf() +{ +} + +void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw() +{ + if( m_bWrite ) + { + sal_Int32 nBytes = aData.getLength(); + if( nBytes > 0 ) + m_pWriter->writeBuffer( aData.getConstArray(), nBytes ); + } +} + +void SAL_CALL PDFStreamIf::flush() throw() +{ +} + +void SAL_CALL PDFStreamIf::closeOutput() throw() +{ + m_bWrite = false; +} + +bool PDFWriterImpl::emitAdditionalStreams() +{ + unsigned int nStreams = m_aAdditionalStreams.size(); + for( unsigned int i = 0; i < nStreams; i++ ) + { + PDFAddStream& rStream = m_aAdditionalStreams[i]; + rStream.m_nStreamObject = createObject(); + sal_Int32 nSizeObject = createObject(); + + if( ! updateObject( rStream.m_nStreamObject ) ) + return false; + + OStringBuffer aLine; + aLine.append( rStream.m_nStreamObject ); + aLine.append( " 0 obj\n<</Length " ); + aLine.append( nSizeObject ); + aLine.append( " 0 R" ); + if( rStream.m_bCompress ) + aLine.append( "/Filter/FlateDecode" ); + aLine.append( ">>\nstream\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + return false; + sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0; + if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) ) + { + osl_closeFile( m_aFile ); + m_bOpen = false; + } + if( rStream.m_bCompress ) + beginCompression(); + + checkAndEnableStreamEncryption( rStream.m_nStreamObject ); + com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) ); + rStream.m_pStream->write( xStream ); + xStream.clear(); + delete rStream.m_pStream; + rStream.m_pStream = NULL; + disableStreamEncryption(); + + if( rStream.m_bCompress ) + endCompression(); + + if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) ) + { + osl_closeFile( m_aFile ); + m_bOpen = false; + return false; + } + if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) + return false ; + // emit stream length object + if( ! updateObject( nSizeObject ) ) + return false; + aLine.setLength( 0 ); + aLine.append( nSizeObject ); + aLine.append( " 0 obj\n" ); + aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); + aLine.append( "\nendobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + return false; + } + return true; +} + +bool PDFWriterImpl::emit() +{ + endPage(); + + // resort structure tree and annotations if necessary + // needed for widget tab order + sortWidgets(); + + // emit additional streams + CHECK_RETURN( emitAdditionalStreams() ); + + // emit catalog + CHECK_RETURN( emitCatalog() ); + + // emit trailer + CHECK_RETURN( emitTrailer() ); + + osl_closeFile( m_aFile ); + m_bOpen = false; + + return true; +} + +std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors() +{ + return m_aErrors; +} + +sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont ) +{ + getReferenceDevice()->Push(); + getReferenceDevice()->SetFont( i_rFont ); + getReferenceDevice()->ImplNewFont(); + + const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; + sal_Int32 nFontID = 0; + FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont ); + if( it != m_aSystemFonts.end() ) + nFontID = it->second.m_nNormalFontID; + else + { + nFontID = m_nNextFID++; + m_aSystemFonts[ pDevFont ] = EmbedFont(); + m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID; + } + + getReferenceDevice()->Pop(); + getReferenceDevice()->ImplNewFont(); + + return nFontID; +} + +void PDFWriterImpl::registerGlyphs( int nGlyphs, + sal_GlyphId* pGlyphs, + sal_Int32* pGlyphWidths, + sal_Ucs* pUnicodes, + sal_Int32* pUnicodesPerGlyph, + sal_uInt8* pMappedGlyphs, + sal_Int32* pMappedFontObjects, + const ImplFontData* pFallbackFonts[] ) +{ + const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; + sal_Ucs* pCurUnicode = pUnicodes; + for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ ) + { + const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB); + const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont; + + if( isBuiltinFont( pCurrentFont ) ) + { + sal_Int32 nFontID = 0; + FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont ); + if( it != m_aEmbeddedFonts.end() ) + nFontID = it->second.m_nNormalFontID; + else + { + nFontID = m_nNextFID++; + m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont(); + m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID; + } + + pGlyphWidths[ i ] = 0; + pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId ); + pMappedFontObjects[ i ] = nFontID; + const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont ); + if( pFD ) + { + const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); + pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ]; + } + } + else if( pCurrentFont->mbSubsettable ) + { + FontSubset& rSubset = m_aSubsets[ pCurrentFont ]; + // search for font specific glyphID + FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId ); + if( it != rSubset.m_aMapping.end() ) + { + pMappedFontObjects[i] = it->second.m_nFontID; + pMappedGlyphs[i] = it->second.m_nSubsetGlyphID; + } + else + { + // create new subset if necessary + if( rSubset.m_aSubsets.empty() + || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) ) + { + rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) ); + } + + // copy font id + pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID; + // create new glyph in subset + sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1); + pMappedGlyphs[i] = nNewId; + + // add new glyph to emitted font subset + GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ]; + rNewGlyphEmit.setGlyphId( nNewId ); + for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ ) + rNewGlyphEmit.addCode( pCurUnicode[n] ); + + // add new glyph to font mapping + Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ]; + rNewGlyph.m_nFontID = pMappedFontObjects[i]; + rNewGlyph.m_nSubsetGlyphID = nNewId; + } + getReferenceDevice()->ImplGetGraphics(); + const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0); + pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont, + nFontGlyphId, + bVertical, + m_pReferenceDevice->mpGraphics ); + } + else if( pCurrentFont->IsEmbeddable() ) + { + sal_Int32 nFontID = 0; + FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont ); + if( it != m_aEmbeddedFonts.end() ) + nFontID = it->second.m_nNormalFontID; + else + { + nFontID = m_nNextFID++; + m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont(); + m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID; + } + EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont]; + + const Ucs2SIntMap* pEncoding = NULL; + const Ucs2OStrMap* pNonEncoded = NULL; + getReferenceDevice()->ImplGetGraphics(); + pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded ); + + Ucs2SIntMap::const_iterator enc_it; + Ucs2OStrMap::const_iterator nonenc_it; + + sal_Int32 nCurFontID = nFontID; + sal_Ucs cChar = *pCurUnicode; + if( pEncoding ) + { + enc_it = pEncoding->find( cChar ); + if( enc_it != pEncoding->end() && enc_it->second > 0 ) + { + DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" ); + cChar = (sal_Ucs)enc_it->second; + } + else if( (enc_it == pEncoding->end() || enc_it->second == -1) && + pNonEncoded && + (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() ) + { + nCurFontID = 0; + // find non encoded glyph + for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it ) + { + if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() ) + { + nCurFontID = nec_it->m_nFontID; + cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ]; + break; + } + } + if( nCurFontID == 0 ) // new nonencoded glyph + { + if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 ) + { + rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() ); + rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++; + } + EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back(); + rEncoding.m_aEncVector.push_back( EmbedCode() ); + rEncoding.m_aEncVector.back().m_aUnicode = cChar; + rEncoding.m_aEncVector.back().m_aName = nonenc_it->second; + rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1); + nCurFontID = rEncoding.m_nFontID; + cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ]; + } + } + else + pEncoding = NULL; + } + if( ! pEncoding ) + { + if( cChar & 0xff00 ) + { + // some characters can be used by conversion + if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area + cChar -= 0xf000; + else + { + String aString(cChar); + ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 ); + cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff; + } + } + } + + pMappedGlyphs[ i ] = (sal_Int8)cChar; + pMappedFontObjects[ i ] = nCurFontID; + pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont, + (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR, + false, + m_pReferenceDevice->mpGraphics ); + } + } +} + +void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines ) +{ + push( PUSH_ALL ); + + FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief(); + + Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor(); + Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor; + Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; + Color aReliefColor( COL_LIGHTGRAY ); + if( aTextColor == COL_BLACK ) + aTextColor = Color( COL_WHITE ); + if( aTextLineColor == COL_BLACK ) + aTextLineColor = Color( COL_WHITE ); + if( aOverlineColor == COL_BLACK ) + aOverlineColor = Color( COL_WHITE ); + if( aTextColor == COL_WHITE ) + aReliefColor = Color( COL_BLACK ); + + Font aSetFont = m_aCurrentPDFState.m_aFont; + aSetFont.SetRelief( RELIEF_NONE ); + aSetFont.SetShadow( sal_False ); + + aSetFont.SetColor( aReliefColor ); + setTextLineColor( aReliefColor ); + setOverlineColor( aReliefColor ); + setFont( aSetFont ); + long nOff = 1 + getReferenceDevice()->mnDPIX/300; + if( eRelief == RELIEF_ENGRAVED ) + nOff = -nOff; + + rLayout.DrawOffset() += Point( nOff, nOff ); + updateGraphicsState(); + drawLayout( rLayout, rText, bTextLines ); + + rLayout.DrawOffset() -= Point( nOff, nOff ); + setTextLineColor( aTextLineColor ); + setOverlineColor( aOverlineColor ); + aSetFont.SetColor( aTextColor ); + setFont( aSetFont ); + updateGraphicsState(); + drawLayout( rLayout, rText, bTextLines ); + + // clean up the mess + pop(); +} + +void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines ) +{ + Font aSaveFont = m_aCurrentPDFState.m_aFont; + Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor; + Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor; + + Font& rFont = m_aCurrentPDFState.m_aFont; + if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 ) + rFont.SetColor( Color( COL_LIGHTGRAY ) ); + else + rFont.SetColor( Color( COL_BLACK ) ); + rFont.SetShadow( sal_False ); + rFont.SetOutline( sal_False ); + setFont( rFont ); + setTextLineColor( rFont.GetColor() ); + setOverlineColor( rFont.GetColor() ); + updateGraphicsState(); + + long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24); + if( rFont.IsOutline() ) + nOff++; + rLayout.DrawBase() += Point( nOff, nOff ); + drawLayout( rLayout, rText, bTextLines ); + rLayout.DrawBase() -= Point( nOff, nOff ); + + setFont( aSaveFont ); + setTextLineColor( aSaveTextLineColor ); + setOverlineColor( aSaveOverlineColor ); + updateGraphicsState(); +} + +void PDFWriterImpl::drawVerticalGlyphs( + const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, + OStringBuffer& rLine, + const Point& rAlignOffset, + const Matrix3& rRotScale, + double fAngle, + double fXScale, + double fSkew, + sal_Int32 nFontHeight ) +{ + long nXOffset = 0; + Point aCurPos( rGlyphs[0].m_aPos ); + aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); + aCurPos += rAlignOffset; + for( size_t i = 0; i < rGlyphs.size(); i++ ) + { + // have to emit each glyph on its own + double fDeltaAngle = 0.0; + double fYScale = 1.0; + double fTempXScale = fXScale; + double fSkewB = fSkew; + double fSkewA = 0.0; + + Point aDeltaPos; + if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL ) + { + fDeltaAngle = M_PI/2.0; + aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent(); + aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale); + fYScale = fXScale; + fTempXScale = 1.0; + fSkewA = -fSkewB; + fSkewB = 0.0; + } + else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR ) + { + fDeltaAngle = -M_PI/2.0; + aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale); + aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent(); + fYScale = fXScale; + fTempXScale = 1.0; + fSkewA = fSkewB; + fSkewB = 0.0; + } + aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) ); + if( i < rGlyphs.size()-1 ) + nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y(); + if( ! rGlyphs[i].m_nGlyphId ) + continue; + + aDeltaPos = rRotScale.transform( aDeltaPos ); + + Matrix3 aMat; + if( fSkewB != 0.0 || fSkewA != 0.0 ) + aMat.skew( fSkewA, fSkewB ); + aMat.scale( fTempXScale, fYScale ); + aMat.rotate( fAngle+fDeltaAngle ); + aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() ); + aMat.append( m_aPages.back(), rLine ); + rLine.append( " Tm" ); + if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId ) + { + rLine.append( " /F" ); + rLine.append( rGlyphs[i].m_nMappedFontId ); + rLine.append( ' ' ); + m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); + rLine.append( " Tf" ); + } + rLine.append( "<" ); + appendHex( rGlyphs[i].m_nMappedGlyphId, rLine ); + rLine.append( ">Tj\n" ); + } +} + +void PDFWriterImpl::drawHorizontalGlyphs( + const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, + OStringBuffer& rLine, + const Point& rAlignOffset, + double fAngle, + double fXScale, + double fSkew, + sal_Int32 nFontHeight, + sal_Int32 nPixelFontHeight + ) +{ + // horizontal (= normal) case + + // fill in run end indices + // end is marked by index of the first glyph of the next run + // a run is marked by same mapped font id and same Y position + std::vector< sal_uInt32 > aRunEnds; + aRunEnds.reserve( rGlyphs.size() ); + for( size_t i = 1; i < rGlyphs.size(); i++ ) + { + if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId || + rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() ) + { + aRunEnds.push_back(i); + } + } + // last run ends at last glyph + aRunEnds.push_back( rGlyphs.size() ); + + // loop over runs of the same font + sal_uInt32 nBeginRun = 0; + for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ ) + { + // setup text matrix + Point aCurPos = rGlyphs[nBeginRun].m_aPos; + // back transformation to current coordinate system + aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); + aCurPos += rAlignOffset; + // the first run can be set with "Td" operator + // subsequent use of that operator would move + // the texline matrix relative to what was set before + // making use of that would drive us into rounding issues + Matrix3 aMat; + if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 ) + { + m_aPages.back().appendPoint( aCurPos, rLine, false ); + rLine.append( " Td " ); + } + else + { + if( fSkew != 0.0 ) + aMat.skew( 0.0, fSkew ); + aMat.scale( fXScale, 1.0 ); + aMat.rotate( fAngle ); + aMat.translate( aCurPos.X(), aCurPos.Y() ); + aMat.append( m_aPages.back(), rLine ); + rLine.append( " Tm\n" ); + } + // set up correct font + rLine.append( "/F" ); + rLine.append( rGlyphs[nBeginRun].m_nMappedFontId ); + rLine.append( ' ' ); + m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); + rLine.append( " Tf" ); + + // output glyphs using Tj or TJ + OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 ); + aKernedLine.append( "[<" ); + aUnkernedLine.append( '<' ); + appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine ); + appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine ); + + aMat.invert(); + bool bNeedKern = false; + for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ ) + { + appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine ); + // check if default glyph positioning is sufficient + const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos ); + const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos ); + double fAdvance = aThisPos.X() - aPrevPos.X(); + fAdvance *= 1000.0 / nPixelFontHeight; + const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5); + if( nAdjustment != 0 ) + { + // apply individual glyph positioning + bNeedKern = true; + aKernedLine.append( ">" ); + aKernedLine.append( nAdjustment ); + aKernedLine.append( "<" ); + } + appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine ); + } + aKernedLine.append( ">]TJ\n" ); + aUnkernedLine.append( ">Tj\n" ); + rLine.append( bNeedKern ? aKernedLine : aUnkernedLine ); + + // set beginning of next run + nBeginRun = aRunEnds[nRun]; + } +} + +void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines ) +{ + // relief takes precedence over shadow (see outdev3.cxx) + if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE ) + { + drawRelief( rLayout, rText, bTextLines ); + return; + } + else if( m_aCurrentPDFState.m_aFont.IsShadow() ) + drawShadow( rLayout, rText, bTextLines ); + + OStringBuffer aLine( 512 ); + + const int nMaxGlyphs = 256; + + sal_GlyphId pGlyphs[nMaxGlyphs]; + sal_Int32 pGlyphWidths[nMaxGlyphs]; + sal_uInt8 pMappedGlyphs[nMaxGlyphs]; + sal_Int32 pMappedFontObjects[nMaxGlyphs]; + std::vector<sal_Ucs> aUnicodes; + aUnicodes.reserve( nMaxGlyphs ); + sal_Int32 pUnicodesPerGlyph[nMaxGlyphs]; + int pCharPosAry[nMaxGlyphs]; + sal_Int32 nAdvanceWidths[nMaxGlyphs]; + const ImplFontData* pFallbackFonts[nMaxGlyphs]; + bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical(); + int nGlyphs; + int nIndex = 0; + int nMinCharPos = 0, nMaxCharPos = rText.Len()-1; + double fXScale = 1.0; + double fSkew = 0.0; + sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight; + TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); + + // transform font height back to current units + // note: the layout calculates in outdevs device pixel !! + sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight ); + if( m_aCurrentPDFState.m_aFont.GetWidth() ) + { + Font aFont( m_aCurrentPDFState.m_aFont ); + aFont.SetWidth( 0 ); + FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont ); + if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() ) + { + fXScale = + (double)m_aCurrentPDFState.m_aFont.GetWidth() / + (double)aMetric.GetWidth(); + } + // force state before GetFontMetric + m_pReferenceDevice->ImplNewFont(); + } + + // perform artificial italics if necessary + if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL || + m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) && + !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL || + m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE ) + ) + { + fSkew = M_PI/12.0; + } + + // if the mapmode is distorted we need to adjust for that also + if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() ) + { + fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY()); + } + + int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation(); + // normalize angles + while( nAngle < 0 ) + nAngle += 3600; + nAngle = nAngle % 3600; + double fAngle = (double)nAngle * M_PI / 1800.0; + + Matrix3 aRotScale; + aRotScale.scale( fXScale, 1.0 ); + if( fAngle != 0.0 ) + aRotScale.rotate( -fAngle ); + + bool bPop = false; + bool bABold = false; + // artificial bold necessary ? + if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM && + m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM ) + { + if( ! bPop ) + aLine.append( "q " ); + bPop = true; + bABold = true; + } + // setup text colors (if necessary) + Color aStrokeColor( COL_TRANSPARENT ); + Color aNonStrokeColor( COL_TRANSPARENT ); + + if( m_aCurrentPDFState.m_aFont.IsOutline() ) + { + aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); + aNonStrokeColor = Color( COL_WHITE ); + } + else + aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); + if( bABold ) + aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); + + if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor ) + { + if( ! bPop ) + aLine.append( "q " ); + bPop = true; + appendStrokingColor( aStrokeColor, aLine ); + aLine.append( "\n" ); + } + if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor ) + { + if( ! bPop ) + aLine.append( "q " ); + bPop = true; + appendNonStrokingColor( aNonStrokeColor, aLine ); + aLine.append( "\n" ); + } + + // begin text object + aLine.append( "BT\n" ); + // outline attribute ? + if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold ) + { + // set correct text mode, set stroke width + aLine.append( "2 Tr " ); // fill, then stroke + + if( m_aCurrentPDFState.m_aFont.IsOutline() ) + { + // unclear what to do in case of outline and artificial bold + // for the time being outline wins + aLine.append( "0.25 w \n" ); + } + else + { + double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0; + m_aPages.back().appendMappedLength( fW, aLine ); + aLine.append ( " w\n" ); + } + } + + FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric(); + + // collect the glyphs into a single array + const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686# + std::vector< PDFGlyph > aGlyphs; + aGlyphs.reserve( nTmpMaxGlyphs ); + // first get all the glyphs and register them; coordinates still in Pixel + Point aGNGlyphPos; + while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 ) + { + aUnicodes.clear(); + for( int i = 0; i < nGlyphs; i++ ) + { + pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] ); + + // default case: 1 glyph is one unicode + pUnicodesPerGlyph[i] = 1; + if( (pGlyphs[i] & GF_ISCHAR) ) + { + aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) ); + } + else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos ) + { + int nChars = 1; + aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) ); + pUnicodesPerGlyph[i] = 1; + // try to handle ligatures and such + if( i < nGlyphs-1 ) + { + nChars = pCharPosAry[i+1] - pCharPosAry[i]; + // #i115618# fix for simple RTL+CTL cases + // TODO: sanitize for RTL ligatures, more complex CTL, etc. + if( nChars < 0 ) + nChars = -nChars; + else if( nChars == 0 ) + nChars = 1; + pUnicodesPerGlyph[i] = nChars; + for( int n = 1; n < nChars; n++ ) + aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) ); + } + // #i36691# hack that is needed because currently the pGlyphs[] + // argument is ignored for embeddable fonts and so the layout + // engine's glyph work is ignored (i.e. char mirroring) + // TODO: a real solution would be to map the layout engine's + // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font) + // back to unicode and then to embeddable font's encoding + if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL ) + { + size_t nI = aUnicodes.size()-1; + for( int n = 0; n < nChars; n++, nI-- ) + aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI])); + } + } + else + aUnicodes.push_back( 0 ); + // note: in case of ctl one character may result + // in multiple glyphs. The current SalLayout + // implementations set -1 then to indicate that no direct + // mapping is possible + } + + registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts ); + + for( int i = 0; i < nGlyphs; i++ ) + { + aGlyphs.push_back( PDFGlyph( aGNGlyphPos, + pGlyphWidths[i], + pGlyphs[i], + pMappedFontObjects[i], + pMappedGlyphs[i] ) ); + if( bVertical ) + aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); + else + aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); + } + } + + Point aAlignOffset; + if ( eAlign == ALIGN_BOTTOM ) + aAlignOffset.Y() -= aRefDevFontMetric.GetDescent(); + else if ( eAlign == ALIGN_TOP ) + aAlignOffset.Y() += aRefDevFontMetric.GetAscent(); + if( aAlignOffset.X() || aAlignOffset.Y() ) + aAlignOffset = aRotScale.transform( aAlignOffset ); + + /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original + string contained only on of the UTF16 BOMs + */ + if( ! aGlyphs.empty() ) + { + if( bVertical ) + drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight ); + else + drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight ); + } + + // end textobject + aLine.append( "ET\n" ); + if( bPop ) + aLine.append( "Q\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); + + // draw eventual textlines + FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout(); + FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline(); + FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline(); + if( bTextLines && + ( + ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) || + ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) || + ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW ) + ) + ) + { + sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont ); + if( m_aCurrentPDFState.m_aFont.IsWordLineMode() ) + { + Point aPos, aStartPt; + sal_Int32 nWidth = 0, nAdvance=0; + for( int nStart = 0;;) + { + sal_GlyphId nGlyphIndex; + if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) + break; + + if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) + { + if( !nWidth ) + aStartPt = aPos; + + nWidth += nAdvance; + } + else if( nWidth > 0 ) + { + drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), + m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + nWidth = 0; + } + } + + if( nWidth > 0 ) + { + drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), + m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + } + } + else + { + Point aStartPt = rLayout.GetDrawPosition(); + int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel(); + drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), + m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + } + } + + // write eventual emphasis marks + if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) + { + PolyPolygon aEmphPoly; + Rectangle aEmphRect1; + Rectangle aEmphRect2; + long nEmphYOff; + long nEmphWidth; + long nEmphHeight; + sal_Bool bEmphPolyLine; + FontEmphasisMark nEmphMark; + + push( PUSH_ALL ); + + aLine.setLength( 0 ); + aLine.append( "q\n" ); + + nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont ); + if ( nEmphMark & EMPHASISMARK_POS_BELOW ) + nEmphHeight = m_pReferenceDevice->mnEmphasisDescent; + else + nEmphHeight = m_pReferenceDevice->mnEmphasisAscent; + m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly, + bEmphPolyLine, + aEmphRect1, + aEmphRect2, + nEmphYOff, + nEmphWidth, + nEmphMark, + m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight), + m_pReferenceDevice->mpFontEntry->mnOrientation ); + if ( bEmphPolyLine ) + { + setLineColor( m_aCurrentPDFState.m_aFont.GetColor() ); + setFillColor( Color( COL_TRANSPARENT ) ); + } + else + { + setFillColor( m_aCurrentPDFState.m_aFont.GetColor() ); + setLineColor( Color( COL_TRANSPARENT ) ); + } + writeBuffer( aLine.getStr(), aLine.getLength() ); + + Point aOffset = Point(0,0); + + if ( nEmphMark & EMPHASISMARK_POS_BELOW ) + aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff; + else + aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff; + + long nEmphWidth2 = nEmphWidth / 2; + long nEmphHeight2 = nEmphHeight / 2; + aOffset += Point( nEmphWidth2, nEmphHeight2 ); + + if ( eAlign == ALIGN_BOTTOM ) + aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent; + else if ( eAlign == ALIGN_TOP ) + aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent; + + for( int nStart = 0;;) + { + Point aPos; + sal_GlyphId nGlyphIndex; + sal_Int32 nAdvance; + if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) + break; + + if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) + { + Point aAdjOffset = aOffset; + aAdjOffset.X() += (nAdvance - nEmphWidth) / 2; + aAdjOffset = aRotScale.transform( aAdjOffset ); + + aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 ); + + aPos += aAdjOffset; + aPos = m_pReferenceDevice->PixelToLogic( aPos ); + drawEmphasisMark( aPos.X(), aPos.Y(), + aEmphPoly, bEmphPolyLine, + aEmphRect1, aEmphRect2 ); + } + } + + writeBuffer( "Q\n", 2 ); + pop(); + } + +} + +void PDFWriterImpl::drawEmphasisMark( long nX, long nY, + const PolyPolygon& rPolyPoly, sal_Bool bPolyLine, + const Rectangle& rRect1, const Rectangle& rRect2 ) +{ + // TODO: pass nWidth as width of this mark + // long nWidth = 0; + + if ( rPolyPoly.Count() ) + { + if ( bPolyLine ) + { + Polygon aPoly = rPolyPoly.GetObject( 0 ); + aPoly.Move( nX, nY ); + drawPolyLine( aPoly ); + } + else + { + PolyPolygon aPolyPoly = rPolyPoly; + aPolyPoly.Move( nX, nY ); + drawPolyPolygon( aPolyPoly ); + } + } + + if ( !rRect1.IsEmpty() ) + { + Rectangle aRect( Point( nX+rRect1.Left(), + nY+rRect1.Top() ), rRect1.GetSize() ); + drawRectangle( aRect ); + } + + if ( !rRect2.IsEmpty() ) + { + Rectangle aRect( Point( nX+rRect2.Left(), + nY+rRect2.Top() ), rRect2.GetSize() ); + + drawRectangle( aRect ); + } +} + +void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) +{ + MARK( "drawText" ); + + updateGraphicsState(); + + // get a layout from the OuputDevice's SalGraphics + // this also enforces font substitution and sets the font on SalGraphics + SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos ); + if( pLayout ) + { + drawLayout( *pLayout, rText, bTextLines ); + pLayout->Release(); + } +} + +void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) +{ + MARK( "drawText with array" ); + + updateGraphicsState(); + + // get a layout from the OuputDevice's SalGraphics + // this also enforces font substitution and sets the font on SalGraphics + SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray ); + if( pLayout ) + { + drawLayout( *pLayout, rText, bTextLines ); + pLayout->Release(); + } +} + +void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) +{ + MARK( "drawStretchText" ); + + updateGraphicsState(); + + // get a layout from the OuputDevice's SalGraphics + // this also enforces font substitution and sets the font on SalGraphics + SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth ); + if( pLayout ) + { + drawLayout( *pLayout, rText, bTextLines ); + pLayout->Release(); + } +} + +void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines ) +{ + long nWidth = rRect.GetWidth(); + long nHeight = rRect.GetHeight(); + + if ( nWidth <= 0 || nHeight <= 0 ) + return; + + MARK( "drawText with rectangle" ); + + updateGraphicsState(); + + // clip with rectangle + OStringBuffer aLine; + aLine.append( "q " ); + m_aPages.back().appendRect( rRect, aLine ); + aLine.append( " W* n\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + + // if disabled text is needed, put in here + + Point aPos = rRect.TopLeft(); + + long nTextHeight = m_pReferenceDevice->GetTextHeight(); + xub_StrLen nMnemonicPos = STRING_NOTFOUND; + + String aStr = rOrigStr; + if ( nStyle & TEXT_DRAW_MNEMONIC ) + aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos ); + + // multiline text + if ( nStyle & TEXT_DRAW_MULTILINE ) + { + XubString aLastLine; + ImplMultiTextLineInfo aMultiLineInfo; + ImplTextLineInfo* pLineInfo; + xub_StrLen i; + xub_StrLen nLines; + xub_StrLen nFormatLines; + + if ( nTextHeight ) + { + ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice ); + OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout ); + nLines = (xub_StrLen)(nHeight/nTextHeight); + nFormatLines = aMultiLineInfo.Count(); + if ( !nLines ) + nLines = 1; + if ( nFormatLines > nLines ) + { + if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) + { + // handle last line + nFormatLines = nLines-1; + + pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); + aLastLine = aStr.Copy( pLineInfo->GetIndex() ); + aLastLine.ConvertLineEnd( LINEEND_LF ); + // replace line feed by space + xub_StrLen nLastLineLen = aLastLine.Len(); + for ( i = 0; i < nLastLineLen; i++ ) + { + if ( aLastLine.GetChar( i ) == _LF ) + aLastLine.SetChar( i, ' ' ); + } + aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle ); + nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM); + nStyle |= TEXT_DRAW_TOP; + } + } + + // vertical alignment + if ( nStyle & TEXT_DRAW_BOTTOM ) + aPos.Y() += nHeight-(nFormatLines*nTextHeight); + else if ( nStyle & TEXT_DRAW_VCENTER ) + aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; + + // draw all lines excluding the last + for ( i = 0; i < nFormatLines; i++ ) + { + pLineInfo = aMultiLineInfo.GetLine( i ); + if ( nStyle & TEXT_DRAW_RIGHT ) + aPos.X() += nWidth-pLineInfo->GetWidth(); + else if ( nStyle & TEXT_DRAW_CENTER ) + aPos.X() += (nWidth-pLineInfo->GetWidth())/2; + xub_StrLen nIndex = pLineInfo->GetIndex(); + xub_StrLen nLineLen = pLineInfo->GetLen(); + drawText( aPos, aStr, nIndex, nLineLen, bTextLines ); + // mnemonics should not appear in documents, + // if the need arises, put them in here + aPos.Y() += nTextHeight; + aPos.X() = rRect.Left(); + } + + + // output last line left adjusted since it was shortened + if ( aLastLine.Len() ) + drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines ); + } + } + else + { + long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); + + // Evt. Text kuerzen + if ( nTextWidth > nWidth ) + { + if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) ) + { + aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle ); + nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT); + nStyle |= TEXT_DRAW_LEFT; + nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); + } + } + + // vertical alignment + if ( nStyle & TEXT_DRAW_RIGHT ) + aPos.X() += nWidth-nTextWidth; + else if ( nStyle & TEXT_DRAW_CENTER ) + aPos.X() += (nWidth-nTextWidth)/2; + + if ( nStyle & TEXT_DRAW_BOTTOM ) + aPos.Y() += nHeight-nTextHeight; + else if ( nStyle & TEXT_DRAW_VCENTER ) + aPos.Y() += (nHeight-nTextHeight)/2; + + // mnemonics should be inserted here if the need arises + + // draw the actual text + drawText( aPos, aStr, 0, STRING_LEN, bTextLines ); + } + + // reset clip region to original value + aLine.setLength( 0 ); + aLine.append( "Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop ) +{ + MARK( "drawLine" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) + return; + + OStringBuffer aLine; + m_aPages.back().appendPoint( rStart, aLine ); + aLine.append( " m " ); + m_aPages.back().appendPoint( rStop, aLine ); + aLine.append( " l S\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo ) +{ + MARK( "drawLine with LineInfo" ); + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) + return; + + if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 ) + { + drawLine( rStart, rStop ); + return; + } + + OStringBuffer aLine; + + aLine.append( "q " ); + if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) + { + m_aPages.back().appendPoint( rStart, aLine ); + aLine.append( " m " ); + m_aPages.back().appendPoint( rStop, aLine ); + aLine.append( " l S Q\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); + } + else + { + PDFWriter::ExtLineInfo aInfo; + convertLineInfoToExtLineInfo( rInfo, aInfo ); + Point aPolyPoints[2] = { rStart, rStop }; + Polygon aPoly( 2, aPolyPoints ); + drawPolyLine( aPoly, aInfo ); + } +} + +void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth ) +{ + Point aDiff( rStop-rStart ); + double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) ); + if( fLen < 1.0 ) + return; + + MARK( "drawWaveLine" ); + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) + return; + + OStringBuffer aLine( 512 ); + aLine.append( "q " ); + m_aPages.back().appendMappedLength( nLineWidth, aLine, true ); + aLine.append( " w " ); + + appendDouble( (double)aDiff.X()/fLen, aLine ); + aLine.append( ' ' ); + appendDouble( -(double)aDiff.Y()/fLen, aLine ); + aLine.append( ' ' ); + appendDouble( (double)aDiff.Y()/fLen, aLine ); + aLine.append( ' ' ); + appendDouble( (double)aDiff.X()/fLen, aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( rStart, aLine ); + aLine.append( " cm " ); + m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine ); + aLine.append( "Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +#define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x ) +#define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x ) + +void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) +{ + // note: units in pFontEntry are ref device pixel + ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; + long nLineHeight = 0; + long nLinePos = 0; + + appendStrokingColor( aColor, aLine ); + aLine.append( "\n" ); + + if ( bIsAbove ) + { + if ( !pFontEntry->maMetric.mnAboveWUnderlineSize ) + m_pReferenceDevice->ImplInitAboveTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset ); + } + else + { + if ( !pFontEntry->maMetric.mnWUnderlineSize ) + m_pReferenceDevice->ImplInitTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset ); + } + if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) ) + nLineHeight = 3; + + long nLineWidth = getReferenceDevice()->mnDPIX/450; + if ( ! nLineWidth ) + nLineWidth = 1; + + if ( eTextLine == UNDERLINE_BOLDWAVE ) + nLineWidth = 3*nLineWidth; + + m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine ); + aLine.append( " w " ); + + if ( eTextLine == UNDERLINE_DOUBLEWAVE ) + { + long nOrgLineHeight = nLineHeight; + nLineHeight /= 3; + if ( nLineHeight < 2 ) + { + if ( nOrgLineHeight > 1 ) + nLineHeight = 2; + else + nLineHeight = 1; + } + long nLineDY = nOrgLineHeight-(nLineHeight*2); + if ( nLineDY < nLineWidth ) + nLineDY = nLineWidth; + long nLineDY2 = nLineDY/2; + if ( !nLineDY2 ) + nLineDY2 = 1; + + nLinePos -= nLineWidth-nLineDY2; + + m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); + + nLinePos += nLineWidth+nLineDY; + m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); + } + else + { + if ( eTextLine != UNDERLINE_BOLDWAVE ) + nLinePos -= nLineWidth/2; + m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine ); + } +} + +void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) +{ + // note: units in pFontEntry are ref device pixel + ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; + long nLineHeight = 0; + long nLinePos = 0; + long nLinePos2 = 0; + + if ( eTextLine > UNDERLINE_BOLDWAVE ) + eTextLine = UNDERLINE_SINGLE; + + switch ( eTextLine ) + { + case UNDERLINE_SINGLE: + case UNDERLINE_DOTTED: + case UNDERLINE_DASH: + case UNDERLINE_LONGDASH: + case UNDERLINE_DASHDOT: + case UNDERLINE_DASHDOTDOT: + if ( bIsAbove ) + { + if ( !pFontEntry->maMetric.mnAboveUnderlineSize ) + m_pReferenceDevice->ImplInitAboveTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset ); + } + else + { + if ( !pFontEntry->maMetric.mnUnderlineSize ) + m_pReferenceDevice->ImplInitTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset ); + } + break; + case UNDERLINE_BOLD: + case UNDERLINE_BOLDDOTTED: + case UNDERLINE_BOLDDASH: + case UNDERLINE_BOLDLONGDASH: + case UNDERLINE_BOLDDASHDOT: + case UNDERLINE_BOLDDASHDOTDOT: + if ( bIsAbove ) + { + if ( !pFontEntry->maMetric.mnAboveBUnderlineSize ) + m_pReferenceDevice->ImplInitAboveTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset ); + } + else + { + if ( !pFontEntry->maMetric.mnBUnderlineSize ) + m_pReferenceDevice->ImplInitTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset ); + nLinePos += nLineHeight/2; + } + break; + case UNDERLINE_DOUBLE: + if ( bIsAbove ) + { + if ( !pFontEntry->maMetric.mnAboveDUnderlineSize ) + m_pReferenceDevice->ImplInitAboveTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 ); + nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 ); + } + else + { + if ( !pFontEntry->maMetric.mnDUnderlineSize ) + m_pReferenceDevice->ImplInitTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 ); + nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 ); + } + default: + break; + } + + if ( nLineHeight ) + { + m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); + aLine.append( " w " ); + appendStrokingColor( aColor, aLine ); + aLine.append( "\n" ); + + switch ( eTextLine ) + { + case UNDERLINE_DOTTED: + case UNDERLINE_BOLDDOTTED: + aLine.append( "[ " ); + m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); + aLine.append( " ] 0 d\n" ); + break; + case UNDERLINE_DASH: + case UNDERLINE_LONGDASH: + case UNDERLINE_BOLDDASH: + case UNDERLINE_BOLDLONGDASH: + { + sal_Int32 nDashLength = 4*nLineHeight; + sal_Int32 nVoidLength = 2*nLineHeight; + if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) ) + nDashLength = 8*nLineHeight; + + aLine.append( "[ " ); + m_aPages.back().appendMappedLength( nDashLength, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); + aLine.append( " ] 0 d\n" ); + } + break; + case UNDERLINE_DASHDOT: + case UNDERLINE_BOLDDASHDOT: + { + sal_Int32 nDashLength = 4*nLineHeight; + sal_Int32 nVoidLength = 2*nLineHeight; + aLine.append( "[ " ); + m_aPages.back().appendMappedLength( nDashLength, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); + aLine.append( " ] 0 d\n" ); + } + break; + case UNDERLINE_DASHDOTDOT: + case UNDERLINE_BOLDDASHDOTDOT: + { + sal_Int32 nDashLength = 4*nLineHeight; + sal_Int32 nVoidLength = 2*nLineHeight; + aLine.append( "[ " ); + m_aPages.back().appendMappedLength( nDashLength, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); + aLine.append( " ] 0 d\n" ); + } + break; + default: + break; + } + + aLine.append( "0 " ); + m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); + aLine.append( " m " ); + m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); + aLine.append( " l S\n" ); + if ( eTextLine == UNDERLINE_DOUBLE ) + { + aLine.append( "0 " ); + m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); + aLine.append( " m " ); + m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); + aLine.append( " l S\n" ); + } + } +} + +void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor ) +{ + // note: units in pFontEntry are ref device pixel + ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; + long nLineHeight = 0; + long nLinePos = 0; + long nLinePos2 = 0; + + if ( eStrikeout > STRIKEOUT_X ) + eStrikeout = STRIKEOUT_SINGLE; + + switch ( eStrikeout ) + { + case STRIKEOUT_SINGLE: + if ( !pFontEntry->maMetric.mnStrikeoutSize ) + m_pReferenceDevice->ImplInitTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset ); + break; + case STRIKEOUT_BOLD: + if ( !pFontEntry->maMetric.mnBStrikeoutSize ) + m_pReferenceDevice->ImplInitTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset ); + break; + case STRIKEOUT_DOUBLE: + if ( !pFontEntry->maMetric.mnDStrikeoutSize ) + m_pReferenceDevice->ImplInitTextLineSize(); + nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize ); + nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 ); + nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 ); + break; + default: + break; + } + + if ( nLineHeight ) + { + m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); + aLine.append( " w " ); + appendStrokingColor( aColor, aLine ); + aLine.append( "\n" ); + + aLine.append( "0 " ); + m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); + aLine.append( " m " ); + m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); + aLine.append( " l S\n" ); + + if ( eStrikeout == STRIKEOUT_DOUBLE ) + { + aLine.append( "0 " ); + m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); + aLine.append( " m " ); + m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); + aLine.append( " l S\n" ); + } + } +} + +void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout ) +{ + //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need + //to tweak this + + String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" ); + String aStrikeout = aStrikeoutChar; + while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth ) + aStrikeout.Append( aStrikeout ); + + // do not get broader than nWidth modulo 1 character + while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth ) + aStrikeout.Erase( 0, 1 ); + aStrikeout.Append( aStrikeoutChar ); + sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow(); + if ( bShadow ) + { + Font aFont = m_aCurrentPDFState.m_aFont; + aFont.SetShadow( sal_False ); + setFont( aFont ); + updateGraphicsState(); + } + + // strikeout string is left aligned non-CTL text + sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode(); + m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED ); + + push( PUSH_CLIPREGION ); + FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric(); + Rectangle aRect; + aRect.nLeft = rPos.X(); + aRect.nRight = aRect.nLeft+nWidth; + aRect.nBottom = rPos.Y()+aRefDevFontMetric.GetDescent(); + aRect.nTop = rPos.Y()-aRefDevFontMetric.GetAscent(); + + ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; + if (pFontEntry->mnOrientation) + { + Polygon aPoly( aRect ); + aPoly.Rotate( rPos, pFontEntry->mnOrientation); + aRect = aPoly.GetBoundRect(); + } + + intersectClipRegion( aRect ); + drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false ); + pop(); + + m_pReferenceDevice->SetLayoutMode( nOrigTLM ); + + if ( bShadow ) + { + Font aFont = m_aCurrentPDFState.m_aFont; + aFont.SetShadow( sal_True ); + setFont( aFont ); + updateGraphicsState(); + } +} + +void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove ) +{ + if ( !nWidth || + ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) && + ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) && + ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) ) + return; + + MARK( "drawTextLine" ); + updateGraphicsState(); + + // note: units in pFontEntry are ref device pixel + ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; + Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor; + Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; + Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor(); + bool bStrikeoutDone = false; + bool bUnderlineDone = false; + bool bOverlineDone = false; + + if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) ) + { + drawStrikeoutChar( rPos, nWidth, eStrikeout ); + bStrikeoutDone = true; + } + + Point aPos( rPos ); + TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); + if( eAlign == ALIGN_TOP ) + aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent ); + else if( eAlign == ALIGN_BOTTOM ) + aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent ); + + OStringBuffer aLine( 512 ); + // save GS + aLine.append( "q " ); + + // rotate and translate matrix + double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0; + Matrix3 aMat; + aMat.rotate( fAngle ); + aMat.translate( aPos.X(), aPos.Y() ); + aMat.append( m_aPages.back(), aLine ); + aLine.append( " cm\n" ); + + if ( aUnderlineColor.GetTransparency() != 0 ) + aUnderlineColor = aStrikeoutColor; + + if ( (eUnderline == UNDERLINE_SMALLWAVE) || + (eUnderline == UNDERLINE_WAVE) || + (eUnderline == UNDERLINE_DOUBLEWAVE) || + (eUnderline == UNDERLINE_BOLDWAVE) ) + { + drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); + bUnderlineDone = true; + } + + if ( (eOverline == UNDERLINE_SMALLWAVE) || + (eOverline == UNDERLINE_WAVE) || + (eOverline == UNDERLINE_DOUBLEWAVE) || + (eOverline == UNDERLINE_BOLDWAVE) ) + { + drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); + bOverlineDone = true; + } + + if ( !bUnderlineDone ) + { + drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); + } + + if ( !bOverlineDone ) + { + drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); + } + + if ( !bStrikeoutDone ) + { + drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor ); + } + + aLine.append( "Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawPolygon( const Polygon& rPoly ) +{ + MARK( "drawPolygon" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) + return; + + int nPoints = rPoly.GetSize(); + OStringBuffer aLine( 20 * nPoints ); + m_aPages.back().appendPolygon( rPoly, aLine ); + if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) + aLine.append( "B*\n" ); + else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) + aLine.append( "S\n" ); + else + aLine.append( "f*\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly ) +{ + MARK( "drawPolyPolygon" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) + return; + + int nPolygons = rPolyPoly.Count(); + + OStringBuffer aLine( 40 * nPolygons ); + m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); + if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) + aLine.append( "B*\n" ); + else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) + aLine.append( "S\n" ); + else + aLine.append( "f*\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent ) +{ + DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); + nTransparentPercent = nTransparentPercent % 100; + + MARK( "drawTransparent" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) + return; + + if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 ) + { + m_aErrors.insert( m_bIsPDF_A1 ? + PDFWriter::Warning_Transparency_Omitted_PDFA : + PDFWriter::Warning_Transparency_Omitted_PDF13 ); + + drawPolyPolygon( rPolyPoly ); + return; + } + + // create XObject + m_aTransparentObjects.push_back( TransparencyEmit() ); + // FIXME: polygons with beziers may yield incorrect bound rect + m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect(); + // convert rectangle to default user space + m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); + m_aTransparentObjects.back().m_nObject = createObject(); + m_aTransparentObjects.back().m_nExtGStateObject = createObject(); + m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; + m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 ); + // create XObject's content stream + OStringBuffer aContent( 256 ); + m_aPages.back().appendPolyPolygon( rPolyPoly, aContent ); + if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) && + m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) ) + aContent.append( " B*\n" ); + else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) ) + aContent.append( " S\n" ); + else + aContent.append( " f*\n" ); + m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() ); + + OStringBuffer aObjName( 16 ); + aObjName.append( "Tr" ); + aObjName.append( m_aTransparentObjects.back().m_nObject ); + OString aTrName( aObjName.makeStringAndClear() ); + aObjName.append( "EGS" ); + aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); + OString aExtName( aObjName.makeStringAndClear() ); + + OStringBuffer aLine( 80 ); + // insert XObject + aLine.append( "q /" ); + aLine.append( aExtName ); + aLine.append( " gs /" ); + aLine.append( aTrName ); + aLine.append( " Do Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + + pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); + pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); +} + +void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject ) +{ + if( nObject >= 0 ) + { + switch( eKind ) + { + case ResXObject: + m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject; + if( ! m_aOutputStreams.empty() ) + m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject; + break; + case ResExtGState: + m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject; + if( ! m_aOutputStreams.empty() ) + m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject; + break; + case ResShading: + m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject; + if( ! m_aOutputStreams.empty() ) + m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject; + break; + case ResPattern: + m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject; + if( ! m_aOutputStreams.empty() ) + m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject; + break; + } + } +} + +void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect ) +{ + push( PUSH_ALL ); + + // force reemitting clip region + clearClipRegion(); + updateGraphicsState(); + + m_aOutputStreams.push_front( StreamRedirect() ); + m_aOutputStreams.front().m_pStream = pStream; + m_aOutputStreams.front().m_aMapMode = m_aMapMode; + + if( !rTargetRect.IsEmpty() ) + { + m_aOutputStreams.front().m_aTargetRect = + lcl_convert( m_aGraphicsStack.front().m_aMapMode, + m_aMapMode, + getReferenceDevice(), + rTargetRect ); + Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft(); + long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight()); + aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom()); + m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta ); + } + + // setup graphics state for independent object stream + + // force reemitting colors + m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); + m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); +} + +Rectangle PDFWriterImpl::getRedirectTargetRect() const +{ + return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect; +} + +SvStream* PDFWriterImpl::endRedirect() +{ + SvStream* pStream = NULL; + if( ! m_aOutputStreams.empty() ) + { + pStream = m_aOutputStreams.front().m_pStream; + m_aMapMode = m_aOutputStreams.front().m_aMapMode; + m_aOutputStreams.pop_front(); + } + + pop(); + // force reemitting colors and clip region + clearClipRegion(); + m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion; + m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion; + m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); + m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); + + updateGraphicsState(); + + return pStream; +} + +void PDFWriterImpl::beginTransparencyGroup() +{ + updateGraphicsState(); + if( m_aContext.Version >= PDFWriter::PDF_1_4 ) + beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); +} + +void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent ) +{ + DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); + nTransparentPercent = nTransparentPercent % 100; + + if( m_aContext.Version >= PDFWriter::PDF_1_4 ) + { + // create XObject + m_aTransparentObjects.push_back( TransparencyEmit() ); + m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; + // convert rectangle to default user space + m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); + m_aTransparentObjects.back().m_nObject = createObject(); + m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; + // get XObject's content stream + m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); + m_aTransparentObjects.back().m_nExtGStateObject = createObject(); + + OStringBuffer aObjName( 16 ); + aObjName.append( "Tr" ); + aObjName.append( m_aTransparentObjects.back().m_nObject ); + OString aTrName( aObjName.makeStringAndClear() ); + aObjName.append( "EGS" ); + aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); + OString aExtName( aObjName.makeStringAndClear() ); + + OStringBuffer aLine( 80 ); + // insert XObject + aLine.append( "q /" ); + aLine.append( aExtName ); + aLine.append( " gs /" ); + aLine.append( aTrName ); + aLine.append( " Do Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + + pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); + pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); + } +} + +void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask ) +{ + if( m_aContext.Version >= PDFWriter::PDF_1_4 ) + { + // create XObject + m_aTransparentObjects.push_back( TransparencyEmit() ); + m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; + // convert rectangle to default user space + m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); + m_aTransparentObjects.back().m_nObject = createObject(); + m_aTransparentObjects.back().m_fAlpha = 0.0; + // get XObject's content stream + m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); + m_aTransparentObjects.back().m_nExtGStateObject = createObject(); + + // draw soft mask + beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); + drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask ); + m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect()); + + OStringBuffer aObjName( 16 ); + aObjName.append( "Tr" ); + aObjName.append( m_aTransparentObjects.back().m_nObject ); + OString aTrName( aObjName.makeStringAndClear() ); + aObjName.append( "EGS" ); + aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); + OString aExtName( aObjName.makeStringAndClear() ); + + OStringBuffer aLine( 80 ); + // insert XObject + aLine.append( "q /" ); + aLine.append( aExtName ); + aLine.append( " gs /" ); + aLine.append( aTrName ); + aLine.append( " Do Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + + pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); + pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); + } +} + +void PDFWriterImpl::drawRectangle( const Rectangle& rRect ) +{ + MARK( "drawRectangle" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) + return; + + OStringBuffer aLine( 40 ); + m_aPages.back().appendRect( rRect, aLine ); + + if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) + aLine.append( " B*\n" ); + else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) + aLine.append( " S\n" ); + else + aLine.append( " f*\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound ) +{ + MARK( "drawRectangle with rounded edges" ); + + if( !nHorzRound && !nVertRound ) + drawRectangle( rRect ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) + return; + + if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 ) + nHorzRound = rRect.GetWidth()/2; + if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 ) + nVertRound = rRect.GetHeight()/2; + + Point aPoints[16]; + const double kappa = 0.5522847498; + const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5); + const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5); + + aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() ); + aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); + aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() ); + aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() ); + + aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound ); + aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky ); + aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound ); + aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky ); + + aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 ); + aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() ); + aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() ); + aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() ); + + aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound ); + aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky ); + aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound ); + aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky ); + + + OStringBuffer aLine( 80 ); + m_aPages.back().appendPoint( aPoints[1], aLine ); + aLine.append( " m " ); + m_aPages.back().appendPoint( aPoints[2], aLine ); + aLine.append( " l " ); + m_aPages.back().appendPoint( aPoints[3], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[4], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[5], aLine ); + aLine.append( " c\n" ); + m_aPages.back().appendPoint( aPoints[6], aLine ); + aLine.append( " l " ); + m_aPages.back().appendPoint( aPoints[7], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[8], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[9], aLine ); + aLine.append( " c\n" ); + m_aPages.back().appendPoint( aPoints[10], aLine ); + aLine.append( " l " ); + m_aPages.back().appendPoint( aPoints[11], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[12], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[13], aLine ); + aLine.append( " c\n" ); + m_aPages.back().appendPoint( aPoints[14], aLine ); + aLine.append( " l " ); + m_aPages.back().appendPoint( aPoints[15], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[0], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[1], aLine ); + aLine.append( " c " ); + + if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) + aLine.append( "b*\n" ); + else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) + aLine.append( "s\n" ); + else + aLine.append( "f*\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawEllipse( const Rectangle& rRect ) +{ + MARK( "drawEllipse" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) + return; + + Point aPoints[12]; + const double kappa = 0.5522847498; + const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5); + const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5); + + aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() ); + aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); + aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() ); + + aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 ); + aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky ); + aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky ); + + aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 ); + aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() ); + aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() ); + + aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 ); + aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky ); + aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky ); + + OStringBuffer aLine( 80 ); + m_aPages.back().appendPoint( aPoints[1], aLine ); + aLine.append( " m " ); + m_aPages.back().appendPoint( aPoints[2], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[3], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[4], aLine ); + aLine.append( " c\n" ); + m_aPages.back().appendPoint( aPoints[5], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[6], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[7], aLine ); + aLine.append( " c\n" ); + m_aPages.back().appendPoint( aPoints[8], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[9], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[10], aLine ); + aLine.append( " c\n" ); + m_aPages.back().appendPoint( aPoints[11], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[0], aLine ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( aPoints[1], aLine ); + aLine.append( " c " ); + + if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) + aLine.append( "b*\n" ); + else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) + aLine.append( "s\n" ); + else + aLine.append( "f*\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +static double calcAngle( const Rectangle& rRect, const Point& rPoint ) +{ + Point aOrigin((rRect.Left()+rRect.Right()+1)/2, + (rRect.Top()+rRect.Bottom()+1)/2); + Point aPoint = rPoint - aOrigin; + + double fX = (double)aPoint.X(); + double fY = (double)-aPoint.Y(); + + if( rRect.GetWidth() > rRect.GetHeight() ) + fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight()); + else if( rRect.GetHeight() > rRect.GetWidth() ) + fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth()); + return atan2( fY, fX ); +} + +void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord ) +{ + MARK( "drawArc" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) + return; + + // calculate start and stop angles + const double fStartAngle = calcAngle( rRect, rStart ); + double fStopAngle = calcAngle( rRect, rStop ); + while( fStopAngle < fStartAngle ) + fStopAngle += 2.0*M_PI; + const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1; + const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments; + const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0); + const double halfWidth = (double)rRect.GetWidth()/2.0; + const double halfHeight = (double)rRect.GetHeight()/2.0; + + const Point aCenter( (rRect.Left()+rRect.Right()+1)/2, + (rRect.Top()+rRect.Bottom()+1)/2 ); + + OStringBuffer aLine( 30*nFragments ); + Point aPoint( (int)(halfWidth * cos(fStartAngle) ), + -(int)(halfHeight * sin(fStartAngle) ) ); + aPoint += aCenter; + m_aPages.back().appendPoint( aPoint, aLine ); + aLine.append( " m " ); + if( !basegfx::fTools::equal(fStartAngle, fStopAngle) ) + { + for( int i = 0; i < nFragments; i++ ) + { + const double fStartFragment = fStartAngle + (double)i*fFragmentDelta; + const double fStopFragment = fStartFragment + fFragmentDelta; + aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ), + -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) ); + aPoint += aCenter; + m_aPages.back().appendPoint( aPoint, aLine ); + aLine.append( ' ' ); + + aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ), + -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) ); + aPoint += aCenter; + m_aPages.back().appendPoint( aPoint, aLine ); + aLine.append( ' ' ); + + aPoint = Point( (int)(halfWidth * cos(fStopFragment) ), + -(int)(halfHeight * sin(fStopFragment) ) ); + aPoint += aCenter; + m_aPages.back().appendPoint( aPoint, aLine ); + aLine.append( " c\n" ); + } + } + if( bWithChord || bWithPie ) + { + if( bWithPie ) + { + m_aPages.back().appendPoint( aCenter, aLine ); + aLine.append( " l " ); + } + aLine.append( "h " ); + } + if( ! bWithChord && ! bWithPie ) + aLine.append( "S\n" ); + else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) + aLine.append( "B*\n" ); + else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) + aLine.append( "S\n" ); + else + aLine.append( "f*\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawPolyLine( const Polygon& rPoly ) +{ + MARK( "drawPolyLine" ); + + sal_uInt16 nPoints = rPoly.GetSize(); + if( nPoints < 2 ) + return; + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) + return; + + OStringBuffer aLine( 20 * nPoints ); + m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] ); + aLine.append( "S\n" ); + + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo ) +{ + MARK( "drawPolyLine with LineInfo" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) + return; + + OStringBuffer aLine; + aLine.append( "q " ); + if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) + { + writeBuffer( aLine.getStr(), aLine.getLength() ); + drawPolyLine( rPoly ); + writeBuffer( "Q\n", 2 ); + } + else + { + PDFWriter::ExtLineInfo aInfo; + convertLineInfoToExtLineInfo( rInfo, aInfo ); + drawPolyLine( rPoly, aInfo ); + } +} + +void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut ) +{ + DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" ); + rOut.m_fLineWidth = rIn.GetWidth(); + rOut.m_fTransparency = 0.0; + rOut.m_eCap = PDFWriter::capButt; + rOut.m_eJoin = PDFWriter::joinMiter; + rOut.m_fMiterLimit = 10; + rOut.m_aDashArray.clear(); + + int nDashes = rIn.GetDashCount(); + int nDashLen = rIn.GetDashLen(); + int nDistance = rIn.GetDistance(); + for( int n = 0; n < nDashes; n++ ) + { + rOut.m_aDashArray.push_back( nDashLen ); + rOut.m_aDashArray.push_back( nDistance ); + } + int nDots = rIn.GetDotCount(); + int nDotLen = rIn.GetDotLen(); + for( int n = 0; n < nDots; n++ ) + { + rOut.m_aDashArray.push_back( nDotLen ); + rOut.m_aDashArray.push_back( nDistance ); + } +} + +void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo ) +{ + MARK( "drawPolyLine with ExtLineInfo" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) + return; + + if( rInfo.m_fTransparency >= 1.0 ) + return; + + if( rInfo.m_fTransparency != 0.0 ) + beginTransparencyGroup(); + + OStringBuffer aLine; + aLine.append( "q " ); + m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine ); + aLine.append( " w" ); + if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader + { + switch( rInfo.m_eCap ) + { + default: + case PDFWriter::capButt: aLine.append( " 0 J" );break; + case PDFWriter::capRound: aLine.append( " 1 J" );break; + case PDFWriter::capSquare: aLine.append( " 2 J" );break; + } + switch( rInfo.m_eJoin ) + { + default: + case PDFWriter::joinMiter: + { + double fLimit = rInfo.m_fMiterLimit; + if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit ) + fLimit = fLimit / rInfo.m_fLineWidth; + if( fLimit < 1.0 ) + fLimit = 1.0; + aLine.append( " 0 j " ); + appendDouble( fLimit, aLine ); + aLine.append( " M" ); + } + break; + case PDFWriter::joinRound: aLine.append( " 1 j" );break; + case PDFWriter::joinBevel: aLine.append( " 2 j" );break; + } + if( rInfo.m_aDashArray.size() > 0 ) + { + aLine.append( " [ " ); + for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin(); + it != rInfo.m_aDashArray.end(); ++it ) + { + m_aPages.back().appendMappedLength( *it, aLine ); + aLine.append( ' ' ); + } + aLine.append( "] 0 d" ); + } + aLine.append( "\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + drawPolyLine( rPoly ); + } + else + { + basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon()); + basegfx::B2DPolyPolygon aPolyPoly; + + basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly); + + // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments. + // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality) + // this line needs to be removed and the loop below adapted accordingly + aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly); + + const sal_uInt32 nPolygonCount(aPolyPoly.count()); + + for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ ) + { + aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " ); + aPoly = aPolyPoly.getB2DPolygon( nPoly ); + const sal_uInt32 nPointCount(aPoly.count()); + + if(nPointCount) + { + const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1); + basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0)); + + for(sal_uInt32 a(0); a < nEdgeCount; a++) + { + if( a > 0 ) + aLine.append( " " ); + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex)); + + m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()), + FRound(aCurrent.getY()) ), + aLine ); + aLine.append( " m " ); + m_aPages.back().appendPoint( Point( FRound(aNext.getX()), + FRound(aNext.getY()) ), + aLine ); + aLine.append( " l" ); + + // prepare next edge + aCurrent = aNext; + } + } + } + aLine.append( " S " ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + } + writeBuffer( "Q\n", 2 ); + + if( rInfo.m_fTransparency != 0.0 ) + { + // FIXME: actually this may be incorrect with bezier polygons + Rectangle aBoundRect( rPoly.GetBoundRect() ); + // avoid clipping with thick lines + if( rInfo.m_fLineWidth > 0.0 ) + { + sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth); + aBoundRect.Top() -= nLW; + aBoundRect.Left() -= nLW; + aBoundRect.Right() += nLW; + aBoundRect.Bottom() += nLW; + } + endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) ); + } +} + +void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor ) +{ + MARK( "drawPixel" ); + + Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor ); + + if( aColor == Color( COL_TRANSPARENT ) ) + return; + + // pixels are drawn in line color, so have to set + // the nonstroking color to line color + Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; + setFillColor( aColor ); + + updateGraphicsState(); + + OStringBuffer aLine( 20 ); + m_aPages.back().appendPoint( rPoint, aLine ); + aLine.append( ' ' ); + appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine ); + aLine.append( ' ' ); + appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine ); + aLine.append( " re f\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + + setFillColor( aOldFillColor ); +} + +void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors ) +{ + MARK( "drawPixel with Polygon" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors ) + return; + + sal_uInt16 nPoints = rPoints.GetSize(); + OStringBuffer aLine( nPoints*40 ); + aLine.append( "q " ); + if( ! pColors ) + { + appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine ); + aLine.append( ' ' ); + } + + OStringBuffer aPixel(32); + aPixel.append( ' ' ); + appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel ); + aPixel.append( ' ' ); + appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel ); + OString aPixelStr = aPixel.makeStringAndClear(); + for( sal_uInt16 i = 0; i < nPoints; i++ ) + { + if( pColors ) + { + if( pColors[i] == Color( COL_TRANSPARENT ) ) + continue; + + appendNonStrokingColor( pColors[i], aLine ); + aLine.append( ' ' ); + } + m_aPages.back().appendPoint( rPoints[i], aLine ); + aLine.append( aPixelStr ); + aLine.append( " re f\n" ); + } + aLine.append( "Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +class AccessReleaser +{ + BitmapReadAccess* m_pAccess; +public: + AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){} + ~AccessReleaser() { delete m_pAccess; } +}; + +bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject ) +{ + CHECK_RETURN( updateObject( rObject.m_nObject ) ); + + bool bFlateFilter = compressStream( rObject.m_pContentStream ); + rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END ); + sal_uLong nSize = rObject.m_pContentStream->Tell(); + rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN ); + #if OSL_DEBUG_LEVEL > 1 + emitComment( "PDFWriterImpl::writeTransparentObject" ); + #endif + OStringBuffer aLine( 512 ); + CHECK_RETURN( updateObject( rObject.m_nObject ) ); + aLine.append( rObject.m_nObject ); + aLine.append( " 0 obj\n" + "<</Type/XObject\n" + "/Subtype/Form\n" + "/BBox[ " ); + appendFixedInt( rObject.m_aBoundRect.Left(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rObject.m_aBoundRect.Top(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rObject.m_aBoundRect.Right(), aLine ); + aLine.append( ' ' ); + appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine ); + aLine.append( " ]\n" ); + if( ! rObject.m_pSoftMaskStream ) + { + if( ! m_bIsPDF_A1 ) + { + aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" ); + } + } + /* #i42884# the PDF reference recommends that each Form XObject + * should have a resource dict; alas if that is the same object + * as the one of the page it triggers an endless recursion in + * acroread 5 (6 and up have that fixed). Since we have only one + * resource dict anyway, let's use the one from the page by NOT + * emitting a Resources entry. + */ + + aLine.append( "/Length " ); + aLine.append( (sal_Int32)(nSize) ); + aLine.append( "\n" ); + if( bFlateFilter ) + aLine.append( "/Filter/FlateDecode\n" ); + aLine.append( ">>\n" + "stream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + checkAndEnableStreamEncryption( rObject.m_nObject ); + CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) ); + disableStreamEncryption(); + aLine.setLength( 0 ); + aLine.append( "\n" + "endstream\n" + "endobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + // write ExtGState dict for this XObject + aLine.setLength( 0 ); + aLine.append( rObject.m_nExtGStateObject ); + aLine.append( " 0 obj\n" + "<<" ); + if( ! rObject.m_pSoftMaskStream ) + { +//i59651 + if( m_bIsPDF_A1 ) + { + aLine.append( "/CA 1.0/ca 1.0" ); + m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); + } + else + { + aLine.append( "/CA " ); + appendDouble( rObject.m_fAlpha, aLine ); + aLine.append( "\n" + " /ca " ); + appendDouble( rObject.m_fAlpha, aLine ); + } + aLine.append( "\n" ); + } + else + { + if( m_bIsPDF_A1 ) + { + aLine.append( "/SMask/None" ); + m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); + } + else + { + rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END ); + sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell(); + rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN ); + sal_Int32 nMaskObject = createObject(); + aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " ); + aLine.append( nMaskObject ); + aLine.append( " 0 R>>\n" ); + + OStringBuffer aMask; + aMask.append( nMaskObject ); + aMask.append( " 0 obj\n" + "<</Type/XObject\n" + "/Subtype/Form\n" + "/BBox[" ); + appendFixedInt( rObject.m_aBoundRect.Left(), aMask ); + aMask.append( ' ' ); + appendFixedInt( rObject.m_aBoundRect.Top(), aMask ); + aMask.append( ' ' ); + appendFixedInt( rObject.m_aBoundRect.Right(), aMask ); + aMask.append( ' ' ); + appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask ); + aMask.append( "]\n" ); + + /* #i42884# see above */ + aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" ); + aMask.append( "/Length " ); + aMask.append( nMaskSize ); + aMask.append( ">>\n" + "stream\n" ); + CHECK_RETURN( updateObject( nMaskObject ) ); + checkAndEnableStreamEncryption( nMaskObject ); + CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); + CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) ); + disableStreamEncryption(); + aMask.setLength( 0 ); + aMask.append( "\nendstream\n" + "endobj\n\n" ); + CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); + } + } + aLine.append( ">>\n" + "endobj\n\n" ); + CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + return true; +} + +bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject ) +{ + sal_Int32 nFunctionObject = createObject(); + CHECK_RETURN( updateObject( nFunctionObject ) ); + + VirtualDevice aDev; + aDev.SetOutputSizePixel( rObject.m_aSize ); + aDev.SetMapMode( MapMode( MAP_PIXEL ) ); + if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) + aDev.SetDrawMode( aDev.GetDrawMode() | + ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT | + DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) ); + aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient ); + + Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize ); + BitmapReadAccess* pAccess = aSample.AcquireReadAccess(); + AccessReleaser aReleaser( pAccess ); + + Size aSize = aSample.GetSizePixel(); + + sal_Int32 nStreamLengthObject = createObject(); + #if OSL_DEBUG_LEVEL > 1 + emitComment( "PDFWriterImpl::writeGradientFunction" ); + #endif + OStringBuffer aLine( 120 ); + aLine.append( nFunctionObject ); + aLine.append( " 0 obj\n" + "<</FunctionType 0\n" + "/Domain[ 0 1 0 1 ]\n" + "/Size[ " ); + aLine.append( (sal_Int32)aSize.Width() ); + aLine.append( ' ' ); + aLine.append( (sal_Int32)aSize.Height() ); + aLine.append( " ]\n" + "/BitsPerSample 8\n" + "/Range[ 0 1 0 1 0 1 ]\n" + "/Order 3\n" + "/Length " ); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 R\n" +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + "/Filter/FlateDecode" +#endif + ">>\n" + "stream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + sal_uInt64 nStartStreamPos = 0; + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) ); + + checkAndEnableStreamEncryption( nFunctionObject ); + beginCompression(); + for( int y = aSize.Height()-1; y >= 0; y-- ) + { + for( int x = 0; x < aSize.Width(); x++ ) + { + sal_uInt8 aCol[3]; + BitmapColor aColor = pAccess->GetColor( y, x ); + aCol[0] = aColor.GetRed(); + aCol[1] = aColor.GetGreen(); + aCol[2] = aColor.GetBlue(); + CHECK_RETURN( writeBuffer( aCol, 3 ) ); + } + } + endCompression(); + disableStreamEncryption(); + + sal_uInt64 nEndStreamPos = 0; + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) ); + + aLine.setLength( 0 ); + aLine.append( "\nendstream\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + // write stream length + CHECK_RETURN( updateObject( nStreamLengthObject ) ); + aLine.setLength( 0 ); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 obj\n" ); + aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) ); + aLine.append( "\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + CHECK_RETURN( updateObject( rObject.m_nObject ) ); + aLine.setLength( 0 ); + aLine.append( rObject.m_nObject ); + aLine.append( " 0 obj\n" + "<</ShadingType 1\n" + "/ColorSpace/DeviceRGB\n" + "/AntiAlias true\n" + "/Domain[ 0 1 0 1 ]\n" + "/Matrix[ " ); + aLine.append( (sal_Int32)aSize.Width() ); + aLine.append( " 0 0 " ); + aLine.append( (sal_Int32)aSize.Height() ); + aLine.append( " 0 0 ]\n" + "/Function " ); + aLine.append( nFunctionObject ); + aLine.append( " 0 R\n" + ">>\n" + "endobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + return true; +} + +bool PDFWriterImpl::writeJPG( JPGEmit& rObject ) +{ + CHECK_RETURN( rObject.m_pStream ); + CHECK_RETURN( updateObject( rObject.m_nObject ) ); + + sal_Int32 nLength = 0; + rObject.m_pStream->Seek( STREAM_SEEK_TO_END ); + nLength = rObject.m_pStream->Tell(); + rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN ); + + sal_Int32 nMaskObject = 0; + if( !!rObject.m_aMask ) + { + if( rObject.m_aMask.GetBitCount() == 1 || + ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651 + ) + { + nMaskObject = createObject(); + } + else if( m_bIsPDF_A1 ) + m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); + else if( m_aContext.Version < PDFWriter::PDF_1_4 ) + m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 ); + + } + #if OSL_DEBUG_LEVEL > 1 + emitComment( "PDFWriterImpl::writeJPG" ); + #endif + + OStringBuffer aLine(200); + aLine.append( rObject.m_nObject ); + aLine.append( " 0 obj\n" + "<</Type/XObject/Subtype/Image/Width " ); + aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() ); + aLine.append( " /Height " ); + aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() ); + aLine.append( " /BitsPerComponent 8 " ); + if( rObject.m_bTrueColor ) + aLine.append( "/ColorSpace/DeviceRGB" ); + else + aLine.append( "/ColorSpace/DeviceGray" ); + aLine.append( "/Filter/DCTDecode/Length " ); + aLine.append( nLength ); + if( nMaskObject ) + { + aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " ); + aLine.append( nMaskObject ); + aLine.append( " 0 R " ); + } + aLine.append( ">>\nstream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + checkAndEnableStreamEncryption( rObject.m_nObject ); + CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) ); + disableStreamEncryption(); + + aLine.setLength( 0 ); + aLine.append( "\nendstream\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + if( nMaskObject ) + { + BitmapEmit aEmit; + aEmit.m_nObject = nMaskObject; + if( rObject.m_aMask.GetBitCount() == 1 ) + aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask ); + else if( rObject.m_aMask.GetBitCount() == 8 ) + aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) ); + writeBitmapObject( aEmit, true ); + } + + return true; +} + +bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) +{ + CHECK_RETURN( updateObject( rObject.m_nObject ) ); + + Bitmap aBitmap; + Color aTransparentColor( COL_TRANSPARENT ); + bool bWriteMask = false; + if( ! bMask ) + { + aBitmap = rObject.m_aBitmap.GetBitmap(); + if( rObject.m_aBitmap.IsAlpha() ) + { + if( m_aContext.Version >= PDFWriter::PDF_1_4 ) + bWriteMask = true; + // else draw without alpha channel + } + else + { + switch( rObject.m_aBitmap.GetTransparentType() ) + { + case TRANSPARENT_NONE: + // comes from drawMask function + if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask ) + bMask = true; + break; + case TRANSPARENT_COLOR: + aTransparentColor = rObject.m_aBitmap.GetTransparentColor(); + break; + case TRANSPARENT_BITMAP: + bWriteMask = true; + break; + } + } + } + else + { + if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() ) + { + aBitmap = rObject.m_aBitmap.GetMask(); + aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); + DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); + } + else if( aBitmap.GetBitCount() != 8 ) + { + aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap(); + aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS ); + DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" ); + } + } + + BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess(); + AccessReleaser aReleaser( pAccess ); + + bool bTrueColor; + sal_Int32 nBitsPerComponent; + switch( aBitmap.GetBitCount() ) + { + case 1: + case 2: + case 4: + case 8: + bTrueColor = false; + nBitsPerComponent = aBitmap.GetBitCount(); + break; + default: + bTrueColor = true; + nBitsPerComponent = 8; + break; + } + + sal_Int32 nStreamLengthObject = createObject(); + sal_Int32 nMaskObject = 0; + + #if OSL_DEBUG_LEVEL > 1 + emitComment( "PDFWriterImpl::writeBitmapObject" ); + #endif + OStringBuffer aLine(1024); + aLine.append( rObject.m_nObject ); + aLine.append( " 0 obj\n" + "<</Type/XObject/Subtype/Image/Width " ); + aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); + aLine.append( "/Height " ); + aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() ); + aLine.append( "/BitsPerComponent " ); + aLine.append( nBitsPerComponent ); + aLine.append( "/Length " ); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 R\n" ); +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + if( nBitsPerComponent != 1 ) + { + aLine.append( "/Filter/FlateDecode" ); + } + else + { + aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " ); + aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); + aLine.append( ">>\n" ); + } +#endif + if( ! bMask ) + { + aLine.append( "/ColorSpace" ); + if( bTrueColor ) + aLine.append( "/DeviceRGB\n" ); + else if( aBitmap.HasGreyPalette() ) + { + aLine.append( "/DeviceGray\n" ); + if( aBitmap.GetBitCount() == 1 ) + { + // #i47395# 1 bit bitmaps occasionally have an inverted grey palette + sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); + DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); + if( nBlackIndex == 1 ) + aLine.append( "/Decode[1 0]\n" ); + } + } + else + { + aLine.append( "[ /Indexed/DeviceRGB " ); + aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) ); + aLine.append( "\n<" ); + if( m_aContext.Encryption.Encrypt() ) + { + enableStringEncryption( rObject.m_nObject ); + //check encryption buffer size + if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) ) + { + int nChar = 0; + //fill the encryption buffer + for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) + { + const BitmapColor& rColor = pAccess->GetPaletteColor( i ); + m_pEncryptionBuffer[nChar++] = rColor.GetRed(); + m_pEncryptionBuffer[nChar++] = rColor.GetGreen(); + m_pEncryptionBuffer[nChar++] = rColor.GetBlue(); + } + //encrypt the colorspace lookup table + rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar ); + //now queue the data for output + nChar = 0; + for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) + { + appendHex(m_pEncryptionBuffer[nChar++], aLine ); + appendHex(m_pEncryptionBuffer[nChar++], aLine ); + appendHex(m_pEncryptionBuffer[nChar++], aLine ); + } + } + } + else //no encryption requested (PDF/A-1a program flow drops here) + { + for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) + { + const BitmapColor& rColor = pAccess->GetPaletteColor( i ); + appendHex( rColor.GetRed(), aLine ); + appendHex( rColor.GetGreen(), aLine ); + appendHex( rColor.GetBlue(), aLine ); + } + } + aLine.append( ">\n]\n" ); + } + } + else + { + if( aBitmap.GetBitCount() == 1 ) + { + aLine.append( "/ImageMask true\n" ); + sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); + DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); + if( nBlackIndex ) + aLine.append( "/Decode[ 1 0 ]\n" ); + else + aLine.append( "/Decode[ 0 1 ]\n" ); + } + else if( aBitmap.GetBitCount() == 8 ) + { + aLine.append( "/ColorSpace/DeviceGray\n" + "/Decode [ 1 0 ]\n" ); + } + } + + if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651 + { + if( bWriteMask ) + { + nMaskObject = createObject(); + if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 ) + aLine.append( "/SMask " ); + else + aLine.append( "/Mask " ); + aLine.append( nMaskObject ); + aLine.append( " 0 R\n" ); + } + else if( aTransparentColor != Color( COL_TRANSPARENT ) ) + { + aLine.append( "/Mask[ " ); + if( bTrueColor ) + { + aLine.append( (sal_Int32)aTransparentColor.GetRed() ); + aLine.append( ' ' ); + aLine.append( (sal_Int32)aTransparentColor.GetRed() ); + aLine.append( ' ' ); + aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); + aLine.append( ' ' ); + aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); + aLine.append( ' ' ); + aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); + aLine.append( ' ' ); + aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); + } + else + { + sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) ); + aLine.append( nIndex ); + } + aLine.append( " ]\n" ); + } + } + else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) ) + m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); + + aLine.append( ">>\n" + "stream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + sal_uInt64 nStartPos = 0; + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) ); + + checkAndEnableStreamEncryption( rObject.m_nObject ); +#ifndef DEBUG_DISABLE_PDFCOMPRESSION + if( nBitsPerComponent == 1 ) + { + writeG4Stream( pAccess ); + } + else +#endif + { + beginCompression(); + if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ) + { + const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U ); + + for( int i = 0; i < pAccess->Height(); i++ ) + { + CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) ); + } + } + else + { + const int nScanLineBytes = pAccess->Width()*3; + boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] ); + for( int y = 0; y < pAccess->Height(); y++ ) + { + for( int x = 0; x < pAccess->Width(); x++ ) + { + BitmapColor aColor = pAccess->GetColor( y, x ); + pCol[3*x+0] = aColor.GetRed(); + pCol[3*x+1] = aColor.GetGreen(); + pCol[3*x+2] = aColor.GetBlue(); + } + CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) ); + } + } + endCompression(); + } + disableStreamEncryption(); + + sal_uInt64 nEndPos = 0; + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) ); + aLine.setLength( 0 ); + aLine.append( "\nendstream\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + CHECK_RETURN( updateObject( nStreamLengthObject ) ); + aLine.setLength( 0 ); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 obj\n" ); + aLine.append( (sal_Int64)(nEndPos-nStartPos) ); + aLine.append( "\nendobj\n\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + + if( nMaskObject ) + { + BitmapEmit aEmit; + aEmit.m_nObject = nMaskObject; + aEmit.m_aBitmap = rObject.m_aBitmap; + return writeBitmapObject( aEmit, true ); + } + + return true; +} + +void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask ) +{ + MARK( "drawJPGBitmap" ); + + OStringBuffer aLine( 80 ); + updateGraphicsState(); + + // #i40055# sanity check + if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) ) + return; + if( ! (rSizePixel.Width() && rSizePixel.Height()) ) + return; + + rDCTData.Seek( 0 ); + if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale ) + { + // need to convert to grayscale; + // load stream to bitmap and draw the bitmap instead + Graphic aGraphic; + GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG ); + Bitmap aBmp( aGraphic.GetBitmap() ); + if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() ) + { + BitmapEx aBmpEx( aBmp, rMask ); + drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx ); + } + else + drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp ); + return; + } + + SvMemoryStream* pStream = new SvMemoryStream; + *pStream << rDCTData; + pStream->Seek( STREAM_SEEK_TO_END ); + + BitmapID aID; + aID.m_aPixelSize = rSizePixel; + aID.m_nSize = pStream->Tell(); + pStream->Seek( STREAM_SEEK_TO_BEGIN ); + aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize ); + if( ! rMask.IsEmpty() ) + aID.m_nMaskChecksum = rMask.GetChecksum(); + + std::list< JPGEmit >::const_iterator it; + for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it ) + ; + if( it == m_aJPGs.end() ) + { + m_aJPGs.push_front( JPGEmit() ); + JPGEmit& rEmit = m_aJPGs.front(); + rEmit.m_nObject = createObject(); + rEmit.m_aID = aID; + rEmit.m_pStream = pStream; + rEmit.m_bTrueColor = bIsTrueColor; + if( !! rMask && rMask.GetSizePixel() == rSizePixel ) + rEmit.m_aMask = rMask; + + it = m_aJPGs.begin(); + } + else + delete pStream; + + aLine.append( "q " ); + sal_Int32 nCheckWidth = 0; + m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth ); + aLine.append( " 0 0 " ); + sal_Int32 nCheckHeight = 0; + m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine ); + aLine.append( " cm\n/Im" ); + aLine.append( it->m_nObject ); + aLine.append( " Do Q\n" ); + if( nCheckWidth == 0 || nCheckHeight == 0 ) + { + // #i97512# avoid invalid current matrix + aLine.setLength( 0 ); + aLine.append( "\n%jpeg image /Im" ); + aLine.append( it->m_nObject ); + aLine.append( " scaled to zero size, omitted\n" ); + } + writeBuffer( aLine.getStr(), aLine.getLength() ); + + OStringBuffer aObjName( 16 ); + aObjName.append( "Im" ); + aObjName.append( it->m_nObject ); + pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); + +} + +void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor ) +{ + OStringBuffer aLine( 80 ); + updateGraphicsState(); + + aLine.append( "q " ); + if( rFillColor != Color( COL_TRANSPARENT ) ) + { + appendNonStrokingColor( rFillColor, aLine ); + aLine.append( ' ' ); + } + sal_Int32 nCheckWidth = 0; + m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth ); + aLine.append( " 0 0 " ); + sal_Int32 nCheckHeight = 0; + m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight ); + aLine.append( ' ' ); + m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine ); + aLine.append( " cm\n/Im" ); + aLine.append( rBitmap.m_nObject ); + aLine.append( " Do Q\n" ); + if( nCheckWidth == 0 || nCheckHeight == 0 ) + { + // #i97512# avoid invalid current matrix + aLine.setLength( 0 ); + aLine.append( "\n%bitmap image /Im" ); + aLine.append( rBitmap.m_nObject ); + aLine.append( " scaled to zero size, omitted\n" ); + } + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask ) +{ + BitmapEx aBitmap( i_rBitmap ); + if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) + { + BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS; + int nDepth = aBitmap.GetBitmap().GetBitCount(); + if( nDepth <= 4 ) + eConv = BMP_CONVERSION_4BIT_GREYS; + if( nDepth > 1 ) + aBitmap.Convert( eConv ); + } + BitmapID aID; + aID.m_aPixelSize = aBitmap.GetSizePixel(); + aID.m_nSize = aBitmap.GetBitCount(); + aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum(); + aID.m_nMaskChecksum = 0; + if( aBitmap.IsAlpha() ) + aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum(); + else + { + Bitmap aMask = aBitmap.GetMask(); + if( ! aMask.IsEmpty() ) + aID.m_nMaskChecksum = aMask.GetChecksum(); + } + std::list< BitmapEmit >::const_iterator it; + for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it ) + { + if( aID == it->m_aID ) + break; + } + if( it == m_aBitmaps.end() ) + { + m_aBitmaps.push_front( BitmapEmit() ); + m_aBitmaps.front().m_aID = aID; + m_aBitmaps.front().m_aBitmap = aBitmap; + m_aBitmaps.front().m_nObject = createObject(); + m_aBitmaps.front().m_bDrawMask = bDrawMask; + it = m_aBitmaps.begin(); + } + + OStringBuffer aObjName( 16 ); + aObjName.append( "Im" ); + aObjName.append( it->m_nObject ); + pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); + + return *it; +} + +void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap ) +{ + MARK( "drawBitmap (Bitmap)" ); + + // #i40055# sanity check + if( ! (rDestSize.Width() && rDestSize.Height()) ) + return; + + const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) ); + drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); +} + +void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap ) +{ + MARK( "drawBitmap (BitmapEx)" ); + + // #i40055# sanity check + if( ! (rDestSize.Width() && rDestSize.Height()) ) + return; + + const BitmapEmit& rEmit = createBitmapEmit( rBitmap ); + drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); +} + +void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor ) +{ + MARK( "drawMask" ); + + // #i40055# sanity check + if( ! (rDestSize.Width() && rDestSize.Height()) ) + return; + + Bitmap aBitmap( rBitmap ); + if( aBitmap.GetBitCount() > 1 ) + aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); + DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); + + const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true ); + drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor ); +} + +sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize ) +{ + Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode, + MapMode( MAP_POINT ), + getReferenceDevice(), + rSize ) ); + // check if we already have this gradient + std::list<GradientEmit>::iterator it; + // rounding to point will generally lose some pixels + // round up to point boundary + aPtSize.Width()++; + aPtSize.Height()++; + for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it ) + { + if( it->m_aGradient == rGradient ) + { + if( it->m_aSize == aPtSize ) + break; + } + } + if( it == m_aGradients.end() ) + { + m_aGradients.push_front( GradientEmit() ); + m_aGradients.front().m_aGradient = rGradient; + m_aGradients.front().m_nObject = createObject(); + m_aGradients.front().m_aSize = aPtSize; + it = m_aGradients.begin(); + } + + OStringBuffer aObjName( 16 ); + aObjName.append( 'P' ); + aObjName.append( it->m_nObject ); + pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject ); + + return it->m_nObject; +} + +void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient ) +{ + MARK( "drawGradient (Rectangle)" ); + + if( m_aContext.Version == PDFWriter::PDF_1_2 ) + { + drawRectangle( rRect ); + return; + } + + sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() ); + + Point aTranslate( rRect.BottomLeft() ); + aTranslate += Point( 0, 1 ); + + updateGraphicsState(); + + OStringBuffer aLine( 80 ); + aLine.append( "q 1 0 0 1 " ); + m_aPages.back().appendPoint( aTranslate, aLine ); + aLine.append( " cm " ); + // if a stroke is appended reset the clip region before stroke + if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) + aLine.append( "q " ); + aLine.append( "0 0 " ); + m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); + aLine.append( " re W n\n" ); + + aLine.append( "/P" ); + aLine.append( nGradient ); + aLine.append( " sh " ); + if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) + { + aLine.append( "Q 0 0 " ); + m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); + aLine.append( ' ' ); + m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); + aLine.append( " re S " ); + } + aLine.append( "Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient ) +{ + MARK( "drawGradient (PolyPolygon)" ); + + if( m_aContext.Version == PDFWriter::PDF_1_2 ) + { + drawPolyPolygon( rPolyPoly ); + return; + } + + Rectangle aBoundRect = rPolyPoly.GetBoundRect(); + sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() ); + + updateGraphicsState(); + + Point aTranslate = aBoundRect.BottomLeft(); + int nPolygons = rPolyPoly.Count(); + + OStringBuffer aLine( 80*nPolygons ); + aLine.append( "q " ); + // set PolyPolygon as clip path + m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); + aLine.append( "W* n\n" ); + aLine.append( "1 0 0 1 " ); + m_aPages.back().appendPoint( aTranslate, aLine ); + aLine.append( " cm\n" ); + aLine.append( "/P" ); + aLine.append( nGradient ); + aLine.append( " sh Q\n" ); + if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) + { + // and draw the surrounding path + m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); + aLine.append( "S\n" ); + } + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch ) +{ + MARK( "drawHatch" ); + + updateGraphicsState(); + + if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && + m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) + return; + if( rPolyPoly.Count() ) + { + PolyPolygon aPolyPoly( rPolyPoly ); + + aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME ); + push( PUSH_LINECOLOR ); + setLineColor( rHatch.GetColor() ); + getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False ); + pop(); + } +} + +void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall ) +{ + MARK( "drawWallpaper" ); + + bool bDrawColor = false; + bool bDrawGradient = false; + bool bDrawBitmap = false; + + BitmapEx aBitmap; + Point aBmpPos = rRect.TopLeft(); + Size aBmpSize; + if( rWall.IsBitmap() ) + { + aBitmap = rWall.GetBitmap(); + aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(), + getMapMode(), + getReferenceDevice(), + aBitmap.GetPrefSize() ); + Rectangle aRect( rRect ); + if( rWall.IsRect() ) + { + aRect = rWall.GetRect(); + aBmpPos = aRect.TopLeft(); + aBmpSize = aRect.GetSize(); + } + if( rWall.GetStyle() != WALLPAPER_SCALE ) + { + if( rWall.GetStyle() != WALLPAPER_TILE ) + { + bDrawBitmap = true; + if( rWall.IsGradient() ) + bDrawGradient = true; + else + bDrawColor = true; + switch( rWall.GetStyle() ) + { + case WALLPAPER_TOPLEFT: + break; + case WALLPAPER_TOP: + aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; + break; + case WALLPAPER_LEFT: + aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; + break; + case WALLPAPER_TOPRIGHT: + aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); + break; + case WALLPAPER_CENTER: + aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; + aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; + break; + case WALLPAPER_RIGHT: + aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); + aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; + break; + case WALLPAPER_BOTTOMLEFT: + aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); + break; + case WALLPAPER_BOTTOM: + aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; + aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); + break; + case WALLPAPER_BOTTOMRIGHT: + aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); + aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); + break; + default: ; + } + } + else + { + // push the bitmap + const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) ); + + // convert to page coordinates; this needs to be done here + // since the emit does not know the page anymore + Rectangle aConvertRect( aBmpPos, aBmpSize ); + m_aPages.back().convertRect( aConvertRect ); + + OStringBuffer aNameBuf(16); + aNameBuf.append( "Im" ); + aNameBuf.append( rEmit.m_nObject ); + OString aImageName( aNameBuf.makeStringAndClear() ); + + // push the pattern + OStringBuffer aTilingStream( 32 ); + appendFixedInt( aConvertRect.GetWidth(), aTilingStream ); + aTilingStream.append( " 0 0 " ); + appendFixedInt( aConvertRect.GetHeight(), aTilingStream ); + aTilingStream.append( " 0 0 cm\n/" ); + aTilingStream.append( aImageName ); + aTilingStream.append( " Do\n" ); + + m_aTilings.push_back( TilingEmit() ); + m_aTilings.back().m_nObject = createObject(); + m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() ); + m_aTilings.back().m_pTilingStream = new SvMemoryStream(); + m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() ); + // phase the tiling so wallpaper begins on upper left + m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor; + m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor; + m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject; + + updateGraphicsState(); + + OStringBuffer aObjName( 16 ); + aObjName.append( 'P' ); + aObjName.append( m_aTilings.back().m_nObject ); + OString aPatternName( aObjName.makeStringAndClear() ); + pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject ); + + // fill a rRect with the pattern + OStringBuffer aLine( 100 ); + aLine.append( "q /Pattern cs /" ); + aLine.append( aPatternName ); + aLine.append( " scn " ); + m_aPages.back().appendRect( rRect, aLine ); + aLine.append( " f Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + } + } + else + { + aBmpPos = aRect.TopLeft(); + aBmpSize = aRect.GetSize(); + bDrawBitmap = true; + } + + if( aBitmap.IsTransparent() ) + { + if( rWall.IsGradient() ) + bDrawGradient = true; + else + bDrawColor = true; + } + } + else if( rWall.IsGradient() ) + bDrawGradient = true; + else + bDrawColor = true; + + if( bDrawGradient ) + { + drawGradient( rRect, rWall.GetGradient() ); + } + if( bDrawColor ) + { + Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor; + Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; + setLineColor( Color( COL_TRANSPARENT ) ); + setFillColor( rWall.GetColor() ); + drawRectangle( rRect ); + setLineColor( aOldLineColor ); + setFillColor( aOldFillColor ); + } + if( bDrawBitmap ) + { + // set temporary clip region since aBmpPos and aBmpSize + // may be outside rRect + OStringBuffer aLine( 20 ); + aLine.append( "q " ); + m_aPages.back().appendRect( rRect, aLine ); + aLine.append( " W n\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + drawBitmap( aBmpPos, aBmpSize, aBitmap ); + writeBuffer( "Q\n", 2 ); + } +} + +void PDFWriterImpl::beginPattern( const Rectangle& rCellRect ) +{ + beginRedirect( new SvMemoryStream(), rCellRect ); +} + +sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform ) +{ + Rectangle aConvertRect( getRedirectTargetRect() ); + DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" ); + + // get scaling between current mapmode and PDF output + Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) ); + double fSX = (double(aScaling.Width()) / 10000.0); + double fSY = (double(aScaling.Height()) / 10000.0); + + // transform translation part of matrix + Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] ); + aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation ); + + sal_Int32 nTilingId = m_aTilings.size(); + m_aTilings.push_back( TilingEmit() ); + TilingEmit& rTile = m_aTilings.back(); + rTile.m_nObject = createObject(); + rTile.m_aResources = m_aOutputStreams.front().m_aResourceDict; + rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX; + rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY; + rTile.m_aTransform.matrix[2] = aTranslation.Width(); + rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX; + rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY; + rTile.m_aTransform.matrix[5] = -aTranslation.Height(); + // caution: endRedirect pops the stream, so do this last + rTile.m_pTilingStream = dynamic_cast<SvMemoryStream*>(endRedirect()); + // FIXME: bound rect will not work with rotated matrix + rTile.m_aRectangle = Rectangle( Point(0,0), aConvertRect.GetSize() ); + rTile.m_aCellSize = aConvertRect.GetSize(); + + OStringBuffer aObjName( 16 ); + aObjName.append( 'P' ); + aObjName.append( rTile.m_nObject ); + pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject ); + return nTilingId; +} + +void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill ) +{ + if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() ) + return; + + m_aPages.back().endStream(); + sal_Int32 nXObject = createObject(); + OStringBuffer aNameBuf( 16 ); + aNameBuf.append( "Pol" ); + aNameBuf.append( nXObject ); + OString aObjName( aNameBuf.makeStringAndClear() ); + Rectangle aObjRect; + if( updateObject( nXObject ) ) + { + // get bounding rect of object + PolyPolygon aSubDiv; + rPolyPoly.AdaptiveSubdivide( aSubDiv ); + aObjRect = aSubDiv.GetBoundRect(); + Rectangle aConvObjRect( aObjRect ); + m_aPages.back().convertRect( aConvObjRect ); + + // move polypolygon to bottom left of page + PolyPolygon aLocalPath( rPolyPoly ); + sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72; + sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72; + Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode ); + sal_Int32 nXOff = aObjRect.Left(); + sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom(); + aLocalPath.Move( -nXOff, nYOff ); + + // prepare XObject's content stream + OStringBuffer aStream( 512 ); + aStream.append( "/Pattern cs /P" ); + aStream.append( m_aTilings[ nPattern ].m_nObject ); + aStream.append( " scn\n" ); + m_aPages.back().appendPolyPolygon( aLocalPath, aStream ); + aStream.append( bEOFill ? "f*" : "f" ); + SvMemoryStream aMemStream( aStream.getLength() ); + aMemStream.Write( aStream.getStr(), aStream.getLength() ); + bool bDeflate = compressStream( &aMemStream ); + aMemStream.Seek( STREAM_SEEK_TO_END ); + sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell(); + aMemStream.Seek( STREAM_SEEK_TO_BEGIN ); + + // add new XObject to global resource dict + m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject; + + // write XObject + OStringBuffer aLine( 512 ); + aLine.append( nXObject ); + aLine.append( " 0 obj\n" + "<</Type/XObject/Subtype/Form/BBox[0 0 " ); + appendFixedInt( aConvObjRect.GetWidth(), aLine ); + aLine.append( ' ' ); + appendFixedInt( aConvObjRect.GetHeight(), aLine ); + aLine.append( "]/Length " ); + aLine.append( nStreamLen ); + if( bDeflate ) + aLine.append( "/Filter/FlateDecode" ); + aLine.append( ">>\n" + "stream\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + checkAndEnableStreamEncryption( nXObject ); + writeBuffer( aMemStream.GetData(), nStreamLen ); + disableStreamEncryption(); + writeBuffer( "\nendstream\nendobj\n\n", 19 ); + } + m_aPages.back().beginStream(); + OStringBuffer aLine( 80 ); + aLine.append( "q 1 0 0 1 " ); + m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine ); + aLine.append( " cm/" ); + aLine.append( aObjName ); + aLine.append( " Do Q\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +void PDFWriterImpl::updateGraphicsState() +{ + OStringBuffer aLine( 256 ); + GraphicsState& rNewState = m_aGraphicsStack.front(); + // first set clip region since it might invalidate everything else + + if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) ) + { + rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion; + + if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion || + ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) ) + { + if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() ) + { + aLine.append( "Q " ); + // invalidate everything but the clip region + m_aCurrentPDFState = GraphicsState(); + rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion); + } + if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() ) + { + // clip region is always stored in private PDF mapmode + MapMode aNewMapMode = rNewState.m_aMapMode; + rNewState.m_aMapMode = m_aMapMode; + getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); + m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; + + aLine.append( "q " ); + m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine ); + aLine.append( "W* n\n" ); + rNewState.m_aMapMode = aNewMapMode; + getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); + m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; + } + } + } + + if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) ) + { + rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode; + getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); + } + + if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) ) + { + rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont; + getReferenceDevice()->SetFont( rNewState.m_aFont ); + getReferenceDevice()->ImplNewFont(); + } + + if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) ) + { + rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode; + getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode ); + } + + if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) ) + { + rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage; + getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage ); + } + + if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) ) + { + rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor; + if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor && + rNewState.m_aLineColor != Color( COL_TRANSPARENT ) ) + { + appendStrokingColor( rNewState.m_aLineColor, aLine ); + aLine.append( "\n" ); + } + } + + if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) ) + { + rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor; + if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor && + rNewState.m_aFillColor != Color( COL_TRANSPARENT ) ) + { + appendNonStrokingColor( rNewState.m_aFillColor, aLine ); + aLine.append( "\n" ); + } + } + + if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) ) + { + rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent; + if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent ) + { + // TODO: switch extended graphicsstate + } + } + + // everything is up to date now + m_aCurrentPDFState = m_aGraphicsStack.front(); + if( aLine.getLength() ) + writeBuffer( aLine.getStr(), aLine.getLength() ); +} + +/* #i47544# imitate OutputDevice behaviour: +* if a font with a nontransparent color is set, it overwrites the current +* text color. OTOH setting the text color will overwrite the color of the font. +*/ +void PDFWriterImpl::setFont( const Font& rFont ) +{ + Color aColor = rFont.GetColor(); + if( aColor == Color( COL_TRANSPARENT ) ) + aColor = m_aGraphicsStack.front().m_aFont.GetColor(); + m_aGraphicsStack.front().m_aFont = rFont; + m_aGraphicsStack.front().m_aFont.SetColor( aColor ); + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont; +} + +void PDFWriterImpl::push( sal_uInt16 nFlags ) +{ + OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" ); + m_aGraphicsStack.push_front( m_aGraphicsStack.front() ); + m_aGraphicsStack.front().m_nFlags = nFlags; +} + +void PDFWriterImpl::pop() +{ + OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" ); + if( m_aGraphicsStack.size() < 2 ) + return; + + GraphicsState aState = m_aGraphicsStack.front(); + m_aGraphicsStack.pop_front(); + GraphicsState& rOld = m_aGraphicsStack.front(); + + // move those parameters back that were not pushed + // in the first place + if( ! (aState.m_nFlags & PUSH_LINECOLOR) ) + setLineColor( aState.m_aLineColor ); + if( ! (aState.m_nFlags & PUSH_FILLCOLOR) ) + setFillColor( aState.m_aFillColor ); + if( ! (aState.m_nFlags & PUSH_FONT) ) + setFont( aState.m_aFont ); + if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) ) + setTextColor( aState.m_aFont.GetColor() ); + if( ! (aState.m_nFlags & PUSH_MAPMODE) ) + setMapMode( aState.m_aMapMode ); + if( ! (aState.m_nFlags & PUSH_CLIPREGION) ) + { + // do not use setClipRegion here + // it would convert again assuming the current mapmode + rOld.m_aClipRegion = aState.m_aClipRegion; + rOld.m_bClipRegion = aState.m_bClipRegion; + } + if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) ) + setTextLineColor( aState.m_aTextLineColor ); + if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) ) + setOverlineColor( aState.m_aOverlineColor ); + if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) ) + setTextAlign( aState.m_aFont.GetAlign() ); + if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) ) + setTextFillColor( aState.m_aFont.GetFillColor() ); + if( ! (aState.m_nFlags & PUSH_REFPOINT) ) + { + // what ? + } + // invalidate graphics state + m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U); +} + +void PDFWriterImpl::setMapMode( const MapMode& rMapMode ) +{ + m_aGraphicsStack.front().m_aMapMode = rMapMode; + getReferenceDevice()->SetMapMode( rMapMode ); + m_aCurrentPDFState.m_aMapMode = rMapMode; +} + +void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion ) +{ + basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ); + aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); + m_aGraphicsStack.front().m_aClipRegion = aRegion; + m_aGraphicsStack.front().m_bClipRegion = true; + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; +} + +void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY ) +{ + if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() ) + { + Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode, + m_aMapMode, + getReferenceDevice(), + Point( nX, nY ) ) ); + aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode, + m_aMapMode, + getReferenceDevice(), + Point() ); + basegfx::B2DHomMatrix aMat; + aMat.translate( aPoint.X(), aPoint.Y() ); + m_aGraphicsStack.front().m_aClipRegion.transform( aMat ); + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; + } +} + +bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect ) +{ + basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect( + basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); + return intersectClipRegion( aRect ); +} + + +bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion ) +{ + basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) ); + aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; + if( m_aGraphicsStack.front().m_bClipRegion ) + { + basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) ); + aRegion = basegfx::tools::prepareForPolygonOperation( aRegion ); + m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion ); + } + else + { + m_aGraphicsStack.front().m_aClipRegion = aRegion; + m_aGraphicsStack.front().m_bClipRegion = true; + } + return true; +} + +void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr ) +{ + if( nPageNr < 0 ) + nPageNr = m_nCurrentPage; + + if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) + return; + + m_aNotes.push_back( PDFNoteEntry() ); + m_aNotes.back().m_nObject = createObject(); + m_aNotes.back().m_aContents = rNote; + m_aNotes.back().m_aRect = rRect; + // convert to default user space now, since the mapmode may change + m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect ); + + // insert note to page's annotation list + m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject ); +} + +sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr ) +{ + if( nPageNr < 0 ) + nPageNr = m_nCurrentPage; + + if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) + return -1; + + sal_Int32 nRet = m_aLinks.size(); + + m_aLinks.push_back( PDFLink() ); + m_aLinks.back().m_nObject = createObject(); + m_aLinks.back().m_nPage = nPageNr; + m_aLinks.back().m_aRect = rRect; + // convert to default user space now, since the mapmode may change + m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect ); + + // insert link to page's annotation list + m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject ); + + return nRet; +} + +//--->i56629 +sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) +{ + if( nPageNr < 0 ) + nPageNr = m_nCurrentPage; + + if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) + return -1; + + sal_Int32 nRet = m_aNamedDests.size(); + + m_aNamedDests.push_back( PDFNamedDest() ); + m_aNamedDests.back().m_aDestName = sDestName; + m_aNamedDests.back().m_nPage = nPageNr; + m_aNamedDests.back().m_eType = eType; + m_aNamedDests.back().m_aRect = rRect; + // convert to default user space now, since the mapmode may change + m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect ); + + return nRet; +} +//<---i56629 + +sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) +{ + if( nPageNr < 0 ) + nPageNr = m_nCurrentPage; + + if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) + return -1; + + sal_Int32 nRet = m_aDests.size(); + + m_aDests.push_back( PDFDest() ); + m_aDests.back().m_nPage = nPageNr; + m_aDests.back().m_eType = eType; + m_aDests.back().m_aRect = rRect; + // convert to default user space now, since the mapmode may change + m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect ); + + return nRet; +} + +sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ) +{ + if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) + return -1; + if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() ) + return -2; + + m_aLinks[ nLinkId ].m_nDest = nDestId; + + return 0; +} + +sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL ) +{ + if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) + return -1; + + m_aLinks[ nLinkId ].m_nDest = -1; + + using namespace ::com::sun::star; + + if (!m_xTrans.is()) + { + uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() ); + if( xFact.is() ) + { + m_xTrans = uno::Reference < util::XURLTransformer >( + xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY ); + } + } + + util::URL aURL; + aURL.Complete = rURL; + + if (m_xTrans.is()) + m_xTrans->parseStrict( aURL ); + + m_aLinks[ nLinkId ].m_aURL = aURL.Complete; + + return 0; +} + +void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId ) +{ + m_aLinkPropertyMap[ nPropertyId ] = nLinkId; +} + +sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID ) +{ + // create new item + sal_Int32 nNewItem = m_aOutline.size(); + m_aOutline.push_back( PDFOutlineEntry() ); + + // set item attributes + setOutlineItemParent( nNewItem, nParent ); + setOutlineItemText( nNewItem, rText ); + setOutlineItemDest( nNewItem, nDestID ); + + return nNewItem; +} + +sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent ) +{ + if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) + return -1; + + int nRet = 0; + + if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem ) + { + nNewParent = 0; + nRet = -2; + } + // remove item from previous parent + sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID; + if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() ) + { + PDFOutlineEntry& rParent = m_aOutline[ nParentID ]; + + for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin(); + it != rParent.m_aChildren.end(); ++it ) + { + if( *it == nItem ) + { + rParent.m_aChildren.erase( it ); + break; + } + } + } + + // insert item to new parent's list of children + m_aOutline[ nNewParent ].m_aChildren.push_back( nItem ); + + return nRet; +} + +sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText ) +{ + if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) + return -1; + + m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText ); + return 0; +} + +sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID ) +{ + if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist + return -1; + if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist + return -2; + m_aOutline[nItem].m_nDestID = nDestID; + return 0; +} + +const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType ) +{ + static std::map< PDFWriter::StructElement, const char* > aTagStrings; + if( aTagStrings.empty() ) + { + aTagStrings[ PDFWriter::NonStructElement] = "NonStruct"; + aTagStrings[ PDFWriter::Document ] = "Document"; + aTagStrings[ PDFWriter::Part ] = "Part"; + aTagStrings[ PDFWriter::Article ] = "Art"; + aTagStrings[ PDFWriter::Section ] = "Sect"; + aTagStrings[ PDFWriter::Division ] = "Div"; + aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote"; + aTagStrings[ PDFWriter::Caption ] = "Caption"; + aTagStrings[ PDFWriter::TOC ] = "TOC"; + aTagStrings[ PDFWriter::TOCI ] = "TOCI"; + aTagStrings[ PDFWriter::Index ] = "Index"; + aTagStrings[ PDFWriter::Paragraph ] = "P"; + aTagStrings[ PDFWriter::Heading ] = "H"; + aTagStrings[ PDFWriter::H1 ] = "H1"; + aTagStrings[ PDFWriter::H2 ] = "H2"; + aTagStrings[ PDFWriter::H3 ] = "H3"; + aTagStrings[ PDFWriter::H4 ] = "H4"; + aTagStrings[ PDFWriter::H5 ] = "H5"; + aTagStrings[ PDFWriter::H6 ] = "H6"; + aTagStrings[ PDFWriter::List ] = "L"; + aTagStrings[ PDFWriter::ListItem ] = "LI"; + aTagStrings[ PDFWriter::LILabel ] = "Lbl"; + aTagStrings[ PDFWriter::LIBody ] = "LBody"; + aTagStrings[ PDFWriter::Table ] = "Table"; + aTagStrings[ PDFWriter::TableRow ] = "TR"; + aTagStrings[ PDFWriter::TableHeader ] = "TH"; + aTagStrings[ PDFWriter::TableData ] = "TD"; + aTagStrings[ PDFWriter::Span ] = "Span"; + aTagStrings[ PDFWriter::Quote ] = "Quote"; + aTagStrings[ PDFWriter::Note ] = "Note"; + aTagStrings[ PDFWriter::Reference ] = "Reference"; + aTagStrings[ PDFWriter::BibEntry ] = "BibEntry"; + aTagStrings[ PDFWriter::Code ] = "Code"; + aTagStrings[ PDFWriter::Link ] = "Link"; + aTagStrings[ PDFWriter::Figure ] = "Figure"; + aTagStrings[ PDFWriter::Formula ] = "Formula"; + aTagStrings[ PDFWriter::Form ] = "Form"; + } + + std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType ); + + return it != aTagStrings.end() ? it->second : "Div"; +} + +void PDFWriterImpl::beginStructureElementMCSeq() +{ + if( m_bEmitStructure && + m_nCurrentStructElement > 0 && // StructTreeRoot + ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence + ) + { + PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ]; + OStringBuffer aLine( 128 ); + sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size(); + aLine.append( "/" ); + if( rEle.m_aAlias.getLength() > 0 ) + aLine.append( rEle.m_aAlias ); + else + aLine.append( getStructureTag( rEle.m_eType ) ); + aLine.append( "<</MCID " ); + aLine.append( nMCID ); + aLine.append( ">>BDC\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + + // update the element's content list +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n", + nMCID, + m_aPages[ m_nCurrentPage ].m_nPageObject, + rEle.m_nFirstPageObject ); +#endif + rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) ); + // update the page's mcid parent list + m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject ); + // mark element MC sequence as open + rEle.m_bOpenMCSeq = true; + } + // handle artifacts + else if( ! m_bEmitStructure && m_aContext.Tagged && + m_nCurrentStructElement > 0 && + m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement && + ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence + ) + { + OStringBuffer aLine( 128 ); + aLine.append( "/Artifact BMC\n" ); + writeBuffer( aLine.getStr(), aLine.getLength() ); + // mark element MC sequence as open + m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true; + } +} + +void PDFWriterImpl::endStructureElementMCSeq() +{ + if( m_nCurrentStructElement > 0 && // StructTreeRoot + ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) && + m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence + ) + { + writeBuffer( "EMC\n", 4 ); + m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false; + } +} + +bool PDFWriterImpl::checkEmitStructure() +{ + bool bEmit = false; + if( m_aContext.Tagged ) + { + bEmit = true; + sal_Int32 nEle = m_nCurrentStructElement; + while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) ) + { + if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement ) + { + bEmit = false; + break; + } + nEle = m_aStructure[ nEle ].m_nParentElement; + } + } + return bEmit; +} + +sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias ) +{ + if( m_nCurrentPage < 0 ) + return -1; + + if( ! m_aContext.Tagged ) + return -1; + + // close eventual current MC sequence + endStructureElementMCSeq(); + + if( m_nCurrentStructElement == 0 && + eType != PDFWriter::Document && eType != PDFWriter::NonStructElement ) + { + // struct tree root hit, but not beginning document + // this might happen with setCurrentStructureElement + // silently insert structure into document again if one properly exists + if( ! m_aStructure[ 0 ].m_aChildren.empty() ) + { + PDFWriter::StructElement childType = PDFWriter::NonStructElement; + sal_Int32 nNewCurElement = 0; + const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren; + for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin(); + childType != PDFWriter::Document && it != rRootChildren.end(); ++it ) + { + nNewCurElement = *it; + childType = m_aStructure[ nNewCurElement ].m_eType; + } + if( childType == PDFWriter::Document ) + { + m_nCurrentStructElement = nNewCurElement; + DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" ); + } + else { + OSL_FAIL( "document structure in disorder !" ); + } + } + else { + OSL_FAIL( "PDF document structure MUST be contained in a Document element" ); + } + } + + sal_Int32 nNewId = sal_Int32(m_aStructure.size()); + m_aStructure.push_back( PDFStructureElement() ); + PDFStructureElement& rEle = m_aStructure.back(); + rEle.m_eType = eType; + rEle.m_nOwnElement = nNewId; + rEle.m_nParentElement = m_nCurrentStructElement; + rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject; + m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId ); + m_nCurrentStructElement = nNewId; + + // handle alias names + if( rAlias.getLength() && eType != PDFWriter::NonStructElement ) + { + OStringBuffer aNameBuf( rAlias.getLength() ); + appendName( rAlias, aNameBuf ); + OString aAliasName( aNameBuf.makeStringAndClear() ); + rEle.m_aAlias = aAliasName; + m_aRoleMap[ aAliasName ] = getStructureTag( eType ); + } + +#if OSL_DEBUG_LEVEL > 1 + OStringBuffer aLine( "beginStructureElement " ); + aLine.append( m_nCurrentStructElement ); + aLine.append( ": " ); + aLine.append( getStructureTag( eType ) ); + if( rEle.m_aAlias.getLength() ) + { + aLine.append( " aliased as \"" ); + aLine.append( rEle.m_aAlias ); + aLine.append( '\"' ); + } + emitComment( aLine.getStr() ); +#endif + + // check whether to emit structure henceforth + m_bEmitStructure = checkEmitStructure(); + + if( m_bEmitStructure ) // don't create nonexistant objects + { + rEle.m_nObject = createObject(); + // update parent's kids list + m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject ); + } + return nNewId; +} + +void PDFWriterImpl::endStructureElement() +{ + if( m_nCurrentPage < 0 ) + return; + + if( ! m_aContext.Tagged ) + return; + + if( m_nCurrentStructElement == 0 ) + { + // hit the struct tree root, that means there is an endStructureElement + // without corresponding beginStructureElement + return; + } + + // end the marked content sequence + endStructureElementMCSeq(); + +#if OSL_DEBUG_LEVEL > 1 + OStringBuffer aLine( "endStructureElement " ); + aLine.append( m_nCurrentStructElement ); + aLine.append( ": " ); + aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); + if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) + { + aLine.append( " aliased as \"" ); + aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); + aLine.append( '\"' ); + } +#endif + + // "end" the structure element, the parent becomes current element + m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement; + + // check whether to emit structure henceforth + m_bEmitStructure = checkEmitStructure(); + +#if OSL_DEBUG_LEVEL > 1 + if( m_bEmitStructure ) + emitComment( aLine.getStr() ); +#endif +} + +//---> i94258 +/* + * This function adds an internal structure list container to overcome the 8191 elements array limitation + * in kids element emission. + * Recursive function + * + */ +void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle ) +{ + if( rEle.m_eType == PDFWriter::NonStructElement && + rEle.m_nOwnElement != rEle.m_nParentElement ) + return; + + for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) + { + if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) + { + PDFStructureElement& rChild = m_aStructure[ *it ]; + if( rChild.m_eType != PDFWriter::NonStructElement ) + { + //triggered when a child of the rEle element is found + if( rChild.m_nParentElement == rEle.m_nOwnElement ) + addInternalStructureContainer( rChild );//examine the child + else + { + OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it ); +#endif + } + } + } + else + { + OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it ); +#endif + } + } + + if( rEle.m_nOwnElement != rEle.m_nParentElement ) + { + if( !rEle.m_aKids.empty() ) + { + if( rEle.m_aKids.size() > ncMaxPDFArraySize ) { + //then we need to add the containers for the kids elements + // a list to be used for the new kid element + std::list< PDFStructureElementKid > aNewKids; + std::list< sal_Int32 > aNewChildren; + + // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?) + OStringBuffer aNameBuf( "Div" ); + OString aAliasName( aNameBuf.makeStringAndClear() ); + m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division ); + + while( rEle.m_aKids.size() > ncMaxPDFArraySize ) + { + sal_Int32 nCurrentStructElement = rEle.m_nOwnElement; + sal_Int32 nNewId = sal_Int32(m_aStructure.size()); + m_aStructure.push_back( PDFStructureElement() ); + PDFStructureElement& rEleNew = m_aStructure.back(); + rEleNew.m_aAlias = aAliasName; + rEleNew.m_eType = PDFWriter::Division; // a new Div type container + rEleNew.m_nOwnElement = nNewId; + rEleNew.m_nParentElement = nCurrentStructElement; + //inherit the same page as the first child to be reparented + rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject; + rEleNew.m_nObject = createObject();//assign a PDF object number + //add the object to the kid list of the parent + aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) ); + aNewChildren.push_back( nNewId ); + + std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() ); + std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() ); + advance( aChildEndIt, ncMaxPDFArraySize ); + advance( aKidEndIt, ncMaxPDFArraySize ); + + rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(), + rEle.m_aKids, + rEle.m_aKids.begin(), + aKidEndIt ); + rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(), + rEle.m_aChildren, + rEle.m_aChildren.begin(), + aChildEndIt ); + // set the kid's new parent + for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin(); + it != rEleNew.m_aChildren.end(); ++it ) + { + m_aStructure[ *it ].m_nParentElement = nNewId; + } + } + //finally add the new kids resulting from the container added + rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() ); + rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() ); + } + } + } +} +//<--- i94258 + +bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle ) +{ + bool bSuccess = false; + + if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) ) + { + // end eventual previous marked content sequence + endStructureElementMCSeq(); + + m_nCurrentStructElement = nEle; + m_bEmitStructure = checkEmitStructure(); +#if OSL_DEBUG_LEVEL > 1 + OStringBuffer aLine( "setCurrentStructureElement " ); + aLine.append( m_nCurrentStructElement ); + aLine.append( ": " ); + aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); + if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) + { + aLine.append( " aliased as \"" ); + aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); + aLine.append( '\"' ); + } + if( ! m_bEmitStructure ) + aLine.append( " (inside NonStruct)" ); + emitComment( aLine.getStr() ); +#endif + bSuccess = true; + } + + return bSuccess; +} + +sal_Int32 PDFWriterImpl::getCurrentStructureElement() +{ + return m_nCurrentStructElement; +} + +bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal ) +{ + if( !m_aContext.Tagged ) + return false; + + bool bInsert = false; + if( m_nCurrentStructElement > 0 && m_bEmitStructure ) + { + PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; + switch( eAttr ) + { + case PDFWriter::Placement: + if( eVal == PDFWriter::Block || + eVal == PDFWriter::Inline || + eVal == PDFWriter::Before || + eVal == PDFWriter::Start || + eVal == PDFWriter::End ) + bInsert = true; + break; + case PDFWriter::WritingMode: + if( eVal == PDFWriter::LrTb || + eVal == PDFWriter::RlTb || + eVal == PDFWriter::TbRl ) + { + bInsert = true; + } + break; + case PDFWriter::TextAlign: + if( eVal == PDFWriter::Start || + eVal == PDFWriter::Center || + eVal == PDFWriter::End || + eVal == PDFWriter::Justify ) + { + if( eType == PDFWriter::Paragraph || + eType == PDFWriter::Heading || + eType == PDFWriter::H1 || + eType == PDFWriter::H2 || + eType == PDFWriter::H3 || + eType == PDFWriter::H4 || + eType == PDFWriter::H5 || + eType == PDFWriter::H6 || + eType == PDFWriter::List || + eType == PDFWriter::ListItem || + eType == PDFWriter::LILabel || + eType == PDFWriter::LIBody || + eType == PDFWriter::Table || + eType == PDFWriter::TableRow || + eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData ) + { + bInsert = true; + } + } + break; + case PDFWriter::Width: + case PDFWriter::Height: + if( eVal == PDFWriter::Auto ) + { + if( eType == PDFWriter::Figure || + eType == PDFWriter::Formula || + eType == PDFWriter::Form || + eType == PDFWriter::Table || + eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData ) + { + bInsert = true; + } + } + break; + case PDFWriter::BlockAlign: + if( eVal == PDFWriter::Before || + eVal == PDFWriter::Middle || + eVal == PDFWriter::After || + eVal == PDFWriter::Justify ) + { + if( eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData ) + { + bInsert = true; + } + } + break; + case PDFWriter::InlineAlign: + if( eVal == PDFWriter::Start || + eVal == PDFWriter::Center || + eVal == PDFWriter::End ) + { + if( eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData ) + { + bInsert = true; + } + } + break; + case PDFWriter::LineHeight: + if( eVal == PDFWriter::Normal || + eVal == PDFWriter::Auto ) + { + // only for ILSE and BLSE + if( eType == PDFWriter::Paragraph || + eType == PDFWriter::Heading || + eType == PDFWriter::H1 || + eType == PDFWriter::H2 || + eType == PDFWriter::H3 || + eType == PDFWriter::H4 || + eType == PDFWriter::H5 || + eType == PDFWriter::H6 || + eType == PDFWriter::List || + eType == PDFWriter::ListItem || + eType == PDFWriter::LILabel || + eType == PDFWriter::LIBody || + eType == PDFWriter::Table || + eType == PDFWriter::TableRow || + eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData || + eType == PDFWriter::Span || + eType == PDFWriter::Quote || + eType == PDFWriter::Note || + eType == PDFWriter::Reference || + eType == PDFWriter::BibEntry || + eType == PDFWriter::Code || + eType == PDFWriter::Link ) + { + bInsert = true; + } + } + break; + case PDFWriter::TextDecorationType: + if( eVal == PDFWriter::NONE || + eVal == PDFWriter::Underline || + eVal == PDFWriter::Overline || + eVal == PDFWriter::LineThrough ) + { + // only for ILSE and BLSE + if( eType == PDFWriter::Paragraph || + eType == PDFWriter::Heading || + eType == PDFWriter::H1 || + eType == PDFWriter::H2 || + eType == PDFWriter::H3 || + eType == PDFWriter::H4 || + eType == PDFWriter::H5 || + eType == PDFWriter::H6 || + eType == PDFWriter::List || + eType == PDFWriter::ListItem || + eType == PDFWriter::LILabel || + eType == PDFWriter::LIBody || + eType == PDFWriter::Table || + eType == PDFWriter::TableRow || + eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData || + eType == PDFWriter::Span || + eType == PDFWriter::Quote || + eType == PDFWriter::Note || + eType == PDFWriter::Reference || + eType == PDFWriter::BibEntry || + eType == PDFWriter::Code || + eType == PDFWriter::Link ) + { + bInsert = true; + } + } + break; + case PDFWriter::ListNumbering: + if( eVal == PDFWriter::NONE || + eVal == PDFWriter::Disc || + eVal == PDFWriter::Circle || + eVal == PDFWriter::Square || + eVal == PDFWriter::Decimal || + eVal == PDFWriter::UpperRoman || + eVal == PDFWriter::LowerRoman || + eVal == PDFWriter::UpperAlpha || + eVal == PDFWriter::LowerAlpha ) + { + if( eType == PDFWriter::List ) + bInsert = true; + } + break; + default: break; + } + } + + if( bInsert ) + m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal ); +#if OSL_DEBUG_LEVEL > 1 + else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) + fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n", + getAttributeTag( eAttr ), + getAttributeValueTag( eVal ), + getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), + m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() + ); +#endif + + return bInsert; +} + +bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue ) +{ + if( ! m_aContext.Tagged ) + return false; + + bool bInsert = false; + if( m_nCurrentStructElement > 0 && m_bEmitStructure ) + { + if( eAttr == PDFWriter::Language ) + { + m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue ); + return true; + } + + PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; + switch( eAttr ) + { + case PDFWriter::SpaceBefore: + case PDFWriter::SpaceAfter: + case PDFWriter::StartIndent: + case PDFWriter::EndIndent: + // just for BLSE + if( eType == PDFWriter::Paragraph || + eType == PDFWriter::Heading || + eType == PDFWriter::H1 || + eType == PDFWriter::H2 || + eType == PDFWriter::H3 || + eType == PDFWriter::H4 || + eType == PDFWriter::H5 || + eType == PDFWriter::H6 || + eType == PDFWriter::List || + eType == PDFWriter::ListItem || + eType == PDFWriter::LILabel || + eType == PDFWriter::LIBody || + eType == PDFWriter::Table || + eType == PDFWriter::TableRow || + eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData ) + { + bInsert = true; + } + break; + case PDFWriter::TextIndent: + // paragraph like BLSE and additional elements + if( eType == PDFWriter::Paragraph || + eType == PDFWriter::Heading || + eType == PDFWriter::H1 || + eType == PDFWriter::H2 || + eType == PDFWriter::H3 || + eType == PDFWriter::H4 || + eType == PDFWriter::H5 || + eType == PDFWriter::H6 || + eType == PDFWriter::LILabel || + eType == PDFWriter::LIBody || + eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData ) + { + bInsert = true; + } + break; + case PDFWriter::Width: + case PDFWriter::Height: + if( eType == PDFWriter::Figure || + eType == PDFWriter::Formula || + eType == PDFWriter::Form || + eType == PDFWriter::Table || + eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData ) + { + bInsert = true; + } + break; + case PDFWriter::LineHeight: + case PDFWriter::BaselineShift: + // only for ILSE and BLSE + if( eType == PDFWriter::Paragraph || + eType == PDFWriter::Heading || + eType == PDFWriter::H1 || + eType == PDFWriter::H2 || + eType == PDFWriter::H3 || + eType == PDFWriter::H4 || + eType == PDFWriter::H5 || + eType == PDFWriter::H6 || + eType == PDFWriter::List || + eType == PDFWriter::ListItem || + eType == PDFWriter::LILabel || + eType == PDFWriter::LIBody || + eType == PDFWriter::Table || + eType == PDFWriter::TableRow || + eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData || + eType == PDFWriter::Span || + eType == PDFWriter::Quote || + eType == PDFWriter::Note || + eType == PDFWriter::Reference || + eType == PDFWriter::BibEntry || + eType == PDFWriter::Code || + eType == PDFWriter::Link ) + { + bInsert = true; + } + break; + case PDFWriter::RowSpan: + case PDFWriter::ColSpan: + // only for table cells + if( eType == PDFWriter::TableHeader || + eType == PDFWriter::TableData ) + { + bInsert = true; + } + break; + case PDFWriter::LinkAnnotation: + if( eType == PDFWriter::Link ) + bInsert = true; + break; + default: break; + } + } + + if( bInsert ) + m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue ); +#if OSL_DEBUG_LEVEL > 1 + else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) + fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n", + getAttributeTag( eAttr ), + (int)nValue, + getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), + m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() ); +#endif + + return bInsert; +} + +void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect ) +{ + sal_Int32 nPageNr = m_nCurrentPage; + if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged ) + return; + + + if( m_nCurrentStructElement > 0 && m_bEmitStructure ) + { + PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; + if( eType == PDFWriter::Figure || + eType == PDFWriter::Formula || + eType == PDFWriter::Form || + eType == PDFWriter::Table ) + { + m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect; + // convert to default user space now, since the mapmode may change + m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox ); + } + } +} + +void PDFWriterImpl::setActualText( const String& rText ) +{ + if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) + { + m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText; + } +} + +void PDFWriterImpl::setAlternateText( const String& rText ) +{ + if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) + { + m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText; + } +} + +void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr ) +{ + if( nPageNr < 0 ) + nPageNr = m_nCurrentPage; + + if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) + return; + + m_aPages[ nPageNr ].m_nDuration = nSeconds; +} + +void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr ) +{ + if( nPageNr < 0 ) + nPageNr = m_nCurrentPage; + + if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) + return; + + m_aPages[ nPageNr ].m_eTransition = eType; + m_aPages[ nPageNr ].m_nTransTime = nMilliSec; +} + +void PDFWriterImpl::ensureUniqueRadioOnValues() +{ + // loop over radio groups + for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin(); + group != m_aRadioGroupWidgets.end(); ++group ) + { + PDFWidget& rGroupWidget = m_aWidgets[ group->second ]; + // check whether all kids have a unique OnValue + boost::unordered_map< OUString, sal_Int32, OUStringHash > aOnValues; + int nChildren = rGroupWidget.m_aKidsIndex.size(); + bool bIsUnique = true; + for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ ) + { + int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; + const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue; + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() ); + #endif + if( aOnValues.find( rVal ) == aOnValues.end() ) + { + aOnValues[ rVal ] = 1; + } + else + { + bIsUnique = false; + } + } + if( ! bIsUnique ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "enforcing unique OnValues\n" ); + #endif + // make unique by using ascending OnValues + for( int nKid = 0; nKid < nChildren; nKid++ ) + { + int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; + PDFWidget& rKid = m_aWidgets[nKidIndex]; + rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) ); + if( ! rKid.m_aValue.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Off" ) ) ) + rKid.m_aValue = rKid.m_aOnValue; + } + } + // finally move the "Yes" appearance to the OnValue appearance + for( int nKid = 0; nKid < nChildren; nKid++ ) + { + int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; + PDFWidget& rKid = m_aWidgets[nKidIndex]; + PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" ); + if( app_it != rKid.m_aAppearances.end() ) + { + PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" ); + if( stream_it != app_it->second.end() ) + { + SvMemoryStream* pStream = stream_it->second; + app_it->second.erase( stream_it ); + OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 ); + appendName( rKid.m_aOnValue, aBuf ); + (app_it->second)[ aBuf.makeStringAndClear() ] = pStream; + } + #if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" ); + #endif + } + // update selected radio button + if( ! rKid.m_aValue.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Off" ) ) ) + { + rGroupWidget.m_aValue = rKid.m_aValue; + } + } + } +} + +sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn ) +{ + sal_Int32 nRadioGroupWidget = -1; + + std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup ); + + if( it == m_aRadioGroupWidgets.end() ) + { + m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget = + sal_Int32(m_aWidgets.size()); + + // new group, insert the radiobutton + m_aWidgets.push_back( PDFWidget() ); + m_aWidgets.back().m_nObject = createObject(); + m_aWidgets.back().m_nPage = m_nCurrentPage; + m_aWidgets.back().m_eType = PDFWriter::RadioButton; + m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup; + m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits + + createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn ); + } + else + nRadioGroupWidget = it->second; + + return nRadioGroupWidget; +} + +sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr ) +{ + if( nPageNr < 0 ) + nPageNr = m_nCurrentPage; + + if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) + return -1; + + sal_Int32 nNewWidget = m_aWidgets.size(); + m_aWidgets.push_back( PDFWidget() ); + + m_aWidgets.back().m_nObject = createObject(); + m_aWidgets.back().m_aRect = rControl.Location; + m_aWidgets.back().m_nPage = nPageNr; + m_aWidgets.back().m_eType = rControl.getType(); + + sal_Int32 nRadioGroupWidget = -1; + // for unknown reasons the radio buttons of a radio group must not have a + // field name, else the buttons are in fact check boxes - + // that is multiple buttons of the radio group can be selected + if( rControl.getType() == PDFWriter::RadioButton ) + nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) ); + else + { + createWidgetFieldName( nNewWidget, rControl ); + } + + // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid + PDFWidget& rNewWidget = m_aWidgets[nNewWidget]; + rNewWidget.m_aDescription = rControl.Description; + rNewWidget.m_aText = rControl.Text; + rNewWidget.m_nTextStyle = rControl.TextStyle & + ( TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP | + TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM | + TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); + rNewWidget.m_nTabOrder = rControl.TabOrder; + + // various properties are set via the flags (/Ff) property of the field dict + if( rControl.ReadOnly ) + rNewWidget.m_nFlags |= 1; + if( rControl.getType() == PDFWriter::PushButton ) + { + const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl); + if( rNewWidget.m_nTextStyle == 0 ) + rNewWidget.m_nTextStyle = + TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | + TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; + + rNewWidget.m_nFlags |= 0x00010000; + if( rBtn.URL.getLength() ) + rNewWidget.m_aListEntries.push_back( rBtn.URL ); + rNewWidget.m_bSubmit = rBtn.Submit; + rNewWidget.m_bSubmitGet = rBtn.SubmitGet; + rNewWidget.m_nDest = rBtn.Dest; + createDefaultPushButtonAppearance( rNewWidget, rBtn ); + } + else if( rControl.getType() == PDFWriter::RadioButton ) + { + const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl); + if( rNewWidget.m_nTextStyle == 0 ) + rNewWidget.m_nTextStyle = + TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; + /* PDF sees a RadioButton group as one radio button with + * children which are in turn check boxes + * + * so we need to create a radio button on demand for a new group + * and insert a checkbox for each RadioButtonWidget as its child + */ + rNewWidget.m_eType = PDFWriter::CheckBox; + rNewWidget.m_nRadioGroup = rBtn.RadioGroup; + + DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" ); + + PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget]; + rRadioButton.m_aKids.push_back( rNewWidget.m_nObject ); + rRadioButton.m_aKidsIndex.push_back( nNewWidget ); + rNewWidget.m_nParent = rRadioButton.m_nObject; + + rNewWidget.m_aValue = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) ); + rNewWidget.m_aOnValue = rBtn.OnValue; + if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected ) + { + rNewWidget.m_aValue = rNewWidget.m_aOnValue; + rRadioButton.m_aValue = rNewWidget.m_aOnValue; + } + createDefaultRadioButtonAppearance( rNewWidget, rBtn ); + + // union rect of radio group + Rectangle aRect = rNewWidget.m_aRect; + m_aPages[ nPageNr ].convertRect( aRect ); + rRadioButton.m_aRect.Union( aRect ); + } + else if( rControl.getType() == PDFWriter::CheckBox ) + { + const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl); + if( rNewWidget.m_nTextStyle == 0 ) + rNewWidget.m_nTextStyle = + TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; + + rNewWidget.m_aValue = rBox.Checked ? OUString(RTL_CONSTASCII_USTRINGPARAM("Yes")) : OUString(RTL_CONSTASCII_USTRINGPARAM("Off" )); + // create default appearance before m_aRect gets transformed + createDefaultCheckBoxAppearance( rNewWidget, rBox ); + } + else if( rControl.getType() == PDFWriter::ListBox ) + { + if( rNewWidget.m_nTextStyle == 0 ) + rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; + + const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl); + rNewWidget.m_aListEntries = rLstBox.Entries; + rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries; + rNewWidget.m_aValue = rLstBox.Text; + if( rLstBox.DropDown ) + rNewWidget.m_nFlags |= 0x00020000; + if( rLstBox.Sort ) + rNewWidget.m_nFlags |= 0x00080000; + if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 ) + rNewWidget.m_nFlags |= 0x00200000; + + createDefaultListBoxAppearance( rNewWidget, rLstBox ); + } + else if( rControl.getType() == PDFWriter::ComboBox ) + { + if( rNewWidget.m_nTextStyle == 0 ) + rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; + + const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl); + rNewWidget.m_aValue = rBox.Text; + rNewWidget.m_aListEntries = rBox.Entries; + rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag + if( rBox.Sort ) + rNewWidget.m_nFlags |= 0x00080000; + + PDFWriter::ListBoxWidget aLBox; + aLBox.Name = rBox.Name; + aLBox.Description = rBox.Description; + aLBox.Text = rBox.Text; + aLBox.TextStyle = rBox.TextStyle; + aLBox.ReadOnly = rBox.ReadOnly; + aLBox.Border = rBox.Border; + aLBox.BorderColor = rBox.BorderColor; + aLBox.Background = rBox.Background; + aLBox.BackgroundColor = rBox.BackgroundColor; + aLBox.TextFont = rBox.TextFont; + aLBox.TextColor = rBox.TextColor; + aLBox.DropDown = true; + aLBox.Sort = rBox.Sort; + aLBox.MultiSelect = false; + aLBox.Entries = rBox.Entries; + + createDefaultListBoxAppearance( rNewWidget, aLBox ); + } + else if( rControl.getType() == PDFWriter::Edit ) + { + if( rNewWidget.m_nTextStyle == 0 ) + rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER; + + const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl); + if( rEdit.MultiLine ) + { + rNewWidget.m_nFlags |= 0x00001000; + rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; + } + if( rEdit.Password ) + rNewWidget.m_nFlags |= 0x00002000; + if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 ) + rNewWidget.m_nFlags |= 0x00100000; + rNewWidget.m_nMaxLen = rEdit.MaxLen; + rNewWidget.m_aValue = rEdit.Text; + + createDefaultEditAppearance( rNewWidget, rEdit ); + } + + // convert to default user space now, since the mapmode may change + // note: create default appearances before m_aRect gets transformed + m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect ); + + // insert widget to page's annotation list + m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject ); + + // mark page as having widgets + m_aPages[ nPageNr ].m_bHasWidgets = true; + + return nNewWidget; +} + +void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl ) +{ + if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() ) + return; + + PDFWidget& rWidget = m_aWidgets[ nControl ]; + m_nCurrentControl = nControl; + + SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 ); + // back conversion of control rect to current MapMode; necessary because + // MapMode between createControl and beginControlAppearance + // could have changed; therefore the widget rectangle is + // already converted + Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ), + rWidget.m_aRect.GetSize() ); + aBack = lcl_convert( m_aMapMode, + m_aGraphicsStack.front().m_aMapMode, + getReferenceDevice(), + aBack ); + beginRedirect( pControlStream, aBack ); + writeBuffer( "/Tx BMC\n", 8 ); +} + +bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState ) +{ + bool bRet = false; + if( ! m_aOutputStreams.empty() ) + writeBuffer( "\nEMC\n", 5 ); + SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect()); + if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() ) + { + PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ]; + OString aState, aStyle; + switch( rWidget.m_eType ) + { + case PDFWriter::PushButton: + if( eState == PDFWriter::Up || eState == PDFWriter::Down ) + { + aState = (eState == PDFWriter::Up) ? "N" : "D"; + aStyle = "Standard"; + } + break; + case PDFWriter::CheckBox: + if( eState == PDFWriter::Up || eState == PDFWriter::Down ) + { + aState = "N"; + aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes"; + /* cf PDFReference 3rd ed. V1.4 p539: + recommended name for on state is "Yes", + recommended name for off state is "Off" + */ + } + break; + case PDFWriter::RadioButton: + if( eState == PDFWriter::Up || eState == PDFWriter::Down ) + { + aState = "N"; + if( eState == PDFWriter::Up ) + aStyle = "Off"; + else + { + OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 ); + appendName( rWidget.m_aOnValue, aBuf ); + aStyle = aBuf.makeStringAndClear(); + } + } + break; + case PDFWriter::Edit: + aState = "N"; + aStyle = "Standard"; + break; + case PDFWriter::ListBox: + case PDFWriter::ComboBox: + case PDFWriter::Hierarchy: + break; + } + if( aState.getLength() && aStyle.getLength() ) + { + // delete eventual existing stream + PDFAppearanceStreams::iterator it = + rWidget.m_aAppearances[ aState ].find( aStyle ); + if( it != rWidget.m_aAppearances[ aState ].end() ) + delete it->second; + rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance; + bRet = true; + } + } + + if( ! bRet ) + delete pAppearance; + + m_nCurrentControl = -1; + + return bRet; +} + +void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress ) +{ + if( pStream ) + { + m_aAdditionalStreams.push_back( PDFAddStream() ); + PDFAddStream& rStream = m_aAdditionalStreams.back(); + rStream.m_aMimeType = rMimeType.Len() + ? OUString( rMimeType ) + : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) ); + rStream.m_pStream = pStream; + rStream.m_bCompress = bCompress; + } +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx new file mode 100644 index 000000000000..09459e40a896 --- /dev/null +++ b/vcl/source/gdi/pdfwriter_impl.hxx @@ -0,0 +1,1339 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _VCL_PDFWRITER_IMPL_HXX +#define _VCL_PDFWRITER_IMPL_HXX + +#include "vcl/pdfwriter.hxx" +#include "rtl/ustring.hxx" +#include "osl/file.h" +#include "tools/gen.hxx" +#include "tools/stream.hxx" +#include "vcl/outdev.hxx" +#include "vcl/bitmapex.hxx" +#include "vcl/gradient.hxx" +#include "vcl/hatch.hxx" +#include "vcl/wall.hxx" +#include "vcl/outdata.hxx" +#include "rtl/strbuf.hxx" +#include "rtl/cipher.h" +#include "rtl/digest.h" +#include "com/sun/star/util/XURLTransformer.hpp" +#include "com/sun/star/lang/Locale.hpp" +#include <sal/macros.h> + +#include <vcl/sallayout.hxx> +#include "pdffontcache.hxx" + +#include <vector> +#include <map> +#include <boost/unordered_map.hpp> +#include <list> + +#include <boost/shared_array.hpp> + +class ImplFontSelectData; +class ImplFontMetricData; +class FontSubsetInfo; +class ZCodec; +class EncHashTransporter; +struct BitStreamState; + +// the maximum password length +#define ENCRYPTED_PWD_SIZE 32 +#define MD5_DIGEST_SIZE 16 +#define SECUR_40BIT_KEY 5 +// security 128 bit +#define SECUR_128BIT_KEY 16 +// maximum length of MD5 digest input, in step 2 of algorithm 3.1 +// PDF spec ver. 1.4: see there for details +#define MAXIMUM_RC4_KEY_LENGTH (SECUR_128BIT_KEY+3+2) + +namespace vcl +{ + +class PDFSalLayout; +class PDFStreamIf; +class Matrix3; + +class PDFWriterImpl +{ + friend class PDFSalLayout; + friend class PDFStreamIf; +public: + // definition of structs + struct BuiltinFont + { + const char * m_pName; // Name + const char * m_pStyleName; // StyleName + const char * m_pPSName; // PSName + int m_nAscent; + int m_nDescent; + FontFamily m_eFamily; // Family + CharSet m_eCharSet; // CharSet + FontPitch m_ePitch; // Pitch + FontWidth m_eWidthType; // WidthType + FontWeight m_eWeight; // Weight + FontItalic m_eItalic; // Italic + int m_aWidths[256]; // character metrics + + rtl::OString getNameObject() const; + }; + + + enum ResourceKind { ResXObject, ResExtGState, ResShading, ResPattern }; + typedef std::map< rtl::OString, sal_Int32 > ResourceMap; + struct ResourceDict + { + // note: handle fonts globally for performance + ResourceMap m_aXObjects; + ResourceMap m_aExtGStates; + ResourceMap m_aShadings; + ResourceMap m_aPatterns; + + void append( rtl::OStringBuffer&, sal_Int32 nFontDictObject ); + }; + + struct PDFPage + { + PDFWriterImpl* m_pWriter; + sal_Int32 m_nPageWidth; // in inch/72 + sal_Int32 m_nPageHeight; // in inch/72 + PDFWriter::Orientation m_eOrientation; + sal_Int32 m_nPageObject; + sal_Int32 m_nPageIndex; + std::vector<sal_Int32> m_aStreamObjects; + sal_Int32 m_nStreamLengthObject; + sal_uInt64 m_nBeginStreamPos; + std::vector<sal_Int32> m_aAnnotations; + std::vector<sal_Int32> m_aMCIDParents; + PDFWriter::PageTransition m_eTransition; + sal_uInt32 m_nTransTime; + sal_uInt32 m_nDuration; + bool m_bHasWidgets; + + PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ); + ~PDFPage(); + + void beginStream(); + void endStream(); + bool emit( sal_Int32 nParentPage ); + + // converts point from ref device coordinates to + // page coordinates and appends the point to the buffer + // if bNeg is true, the coordinates are inverted AFTER transformation + // to page (useful for transformation matrices + // if pOutPoint is set it will be updated to the emitted point + // (in PDF map mode, that is 10th of point) + void appendPoint( const Point& rPoint, rtl::OStringBuffer& rBuffer, bool bNeg = false, Point* pOutPoint = NULL ) const; + // appends a B2DPoint without further transformation + void appendPixelPoint( const basegfx::B2DPoint& rPoint, rtl::OStringBuffer& rBuffer ) const; + // appends a rectangle + void appendRect( const Rectangle& rRect, rtl::OStringBuffer& rBuffer ) const; + // converts a rectangle to 10th points page space + void convertRect( Rectangle& rRect ) const; + // appends a polygon optionally closing it + void appendPolygon( const Polygon& rPoly, rtl::OStringBuffer& rBuffer, bool bClose = true ) const; + // appends a polygon optionally closing it + void appendPolygon( const basegfx::B2DPolygon& rPoly, rtl::OStringBuffer& rBuffer, bool bClose = true ) const; + // appends a polypolygon optionally closing the subpaths + void appendPolyPolygon( const PolyPolygon& rPolyPoly, rtl::OStringBuffer& rBuffer, bool bClose = true ) const; + // appends a polypolygon optionally closing the subpaths + void appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, rtl::OStringBuffer& rBuffer, bool bClose = true ) const; + // converts a length (either vertical or horizontal; this + // can be important if the source MapMode is not + // symmetrical) to page length and appends it to the buffer + // if pOutLength is set it will be updated to the emitted length + // (in PDF map mode, that is 10th of point) + void appendMappedLength( sal_Int32 nLength, rtl::OStringBuffer& rBuffer, bool bVertical = true, sal_Int32* pOutLength = NULL ) const; + // the same for double values + void appendMappedLength( double fLength, rtl::OStringBuffer& rBuffer, bool bVertical = true, sal_Int32* pOutLength = NULL, sal_Int32 nPrecision = 5 ) const; + // appends LineInfo + // returns false if too many dash array entry were created for + // the implementation limits of some PDF readers + bool appendLineInfo( const LineInfo& rInfo, rtl::OStringBuffer& rBuffer ) const; + // appends a horizontal waveline with vertical offset (helper for drawWaveLine) + void appendWaveLine( sal_Int32 nLength, sal_Int32 nYOffset, sal_Int32 nDelta, rtl::OStringBuffer& rBuffer ) const; + + sal_Int32 getWidth() const { return m_nPageWidth ? m_nPageWidth : m_pWriter->m_nInheritedPageWidth; } + sal_Int32 getHeight() const { return m_nPageHeight ? m_nPageHeight : m_pWriter->m_nInheritedPageHeight; } + }; + + friend struct PDFPage; + + struct BitmapID + { + Size m_aPixelSize; + sal_Int32 m_nSize; + sal_Int32 m_nChecksum; + sal_Int32 m_nMaskChecksum; + + BitmapID() : m_nSize( 0 ), m_nChecksum( 0 ), m_nMaskChecksum( 0 ) {} + + BitmapID& operator=( const BitmapID& rCopy ) + { + m_aPixelSize = rCopy.m_aPixelSize; + m_nSize = rCopy.m_nSize; + m_nChecksum = rCopy.m_nChecksum; + m_nMaskChecksum = rCopy.m_nMaskChecksum; + return *this; + } + + bool operator==( const BitmapID& rComp ) const + { + return (m_aPixelSize == rComp.m_aPixelSize && + m_nSize == rComp.m_nSize && + m_nChecksum == rComp.m_nChecksum && + m_nMaskChecksum == rComp.m_nMaskChecksum ); + } + }; + + struct BitmapEmit + { + BitmapID m_aID; + BitmapEx m_aBitmap; + sal_Int32 m_nObject; + bool m_bDrawMask; + + BitmapEmit() : m_bDrawMask( false ) {} + }; + + struct JPGEmit + { + BitmapID m_aID; + SvMemoryStream* m_pStream; + Bitmap m_aMask; + sal_Int32 m_nObject; + bool m_bTrueColor; + + JPGEmit() : m_pStream( NULL ), m_bTrueColor( false ) {} + ~JPGEmit() { delete m_pStream; } + }; + + struct GradientEmit + { + Gradient m_aGradient; + Size m_aSize; + sal_Int32 m_nObject; + }; + + // for tilings (drawWallpaper, begin/endPattern) + struct TilingEmit + { + sal_Int32 m_nObject; + Rectangle m_aRectangle; + Size m_aCellSize; + SvtGraphicFill::Transform m_aTransform; + ResourceDict m_aResources; + SvMemoryStream* m_pTilingStream; + + TilingEmit() + : m_nObject( 0 ), + m_pTilingStream( NULL ) + {} + }; + + // for transparency group XObjects + struct TransparencyEmit + { + sal_Int32 m_nObject; + sal_Int32 m_nExtGStateObject; + double m_fAlpha; + Rectangle m_aBoundRect; + SvMemoryStream* m_pContentStream; + SvMemoryStream* m_pSoftMaskStream; + + TransparencyEmit() + : m_nObject( 0 ), + m_nExtGStateObject( -1 ), + m_fAlpha( 0.0 ), + m_pContentStream( NULL ), + m_pSoftMaskStream( NULL ) + {} + ~TransparencyEmit() + { + delete m_pContentStream; + delete m_pSoftMaskStream; + } + }; + + // font subsets + class GlyphEmit + { + // performance: actually this should probably a vector; + sal_Ucs m_aBufferedUnicodes[3]; + sal_Int32 m_nUnicodes; + sal_Int32 m_nMaxUnicodes; + boost::shared_array<sal_Ucs> m_pUnicodes; + sal_uInt8 m_nSubsetGlyphID; + + public: + GlyphEmit() : m_nUnicodes(0), m_nSubsetGlyphID(0) + { + rtl_zeroMemory( m_aBufferedUnicodes, sizeof( m_aBufferedUnicodes ) ); + m_nMaxUnicodes = SAL_N_ELEMENTS(m_aBufferedUnicodes); + } + ~GlyphEmit() + { + } + + void setGlyphId( sal_uInt8 i_nId ) { m_nSubsetGlyphID = i_nId; } + sal_uInt8 getGlyphId() const { return m_nSubsetGlyphID; } + + void addCode( sal_Ucs i_cCode ) + { + if( m_nUnicodes == m_nMaxUnicodes ) + { + sal_Ucs* pNew = new sal_Ucs[ 2 * m_nMaxUnicodes]; + if( m_pUnicodes.get() ) + rtl_copyMemory( pNew, m_pUnicodes.get(), m_nMaxUnicodes * sizeof(sal_Ucs) ); + else + rtl_copyMemory( pNew, m_aBufferedUnicodes, m_nMaxUnicodes * sizeof(sal_Ucs) ); + m_pUnicodes.reset( pNew ); + m_nMaxUnicodes *= 2; + } + if( m_pUnicodes.get() ) + m_pUnicodes[ m_nUnicodes++ ] = i_cCode; + else + m_aBufferedUnicodes[ m_nUnicodes++ ] = i_cCode; + } + sal_Int32 countCodes() const { return m_nUnicodes; } + sal_Ucs getCode( sal_Int32 i_nIndex ) const + { + sal_Ucs nRet = 0; + if( i_nIndex < m_nUnicodes ) + nRet = m_pUnicodes.get() ? m_pUnicodes[ i_nIndex ] : m_aBufferedUnicodes[ i_nIndex ]; + return nRet; + } + }; + typedef std::map< sal_GlyphId, GlyphEmit > FontEmitMapping; + struct FontEmit + { + sal_Int32 m_nFontID; + FontEmitMapping m_aMapping; + + FontEmit( sal_Int32 nID ) : m_nFontID( nID ) {} + }; + typedef std::list< FontEmit > FontEmitList; + struct Glyph + { + sal_Int32 m_nFontID; + sal_uInt8 m_nSubsetGlyphID; + }; + typedef std::map< sal_GlyphId, Glyph > FontMapping; + struct FontSubset + { + FontEmitList m_aSubsets; + FontMapping m_aMapping; + }; + typedef std::map< const ImplFontData*, FontSubset > FontSubsetData; + struct EmbedCode + { + sal_Ucs m_aUnicode; + rtl::OString m_aName; + }; + struct EmbedEncoding + { + sal_Int32 m_nFontID; + std::vector< EmbedCode > m_aEncVector; + std::map< sal_Ucs, sal_Int8 > m_aCMap; + }; + struct EmbedFont + { + sal_Int32 m_nNormalFontID; + std::list< EmbedEncoding > m_aExtendedEncodings; + + EmbedFont() : m_nNormalFontID( 0 ) {} + }; + typedef std::map< const ImplFontData*, EmbedFont > FontEmbedData; + + struct PDFDest + { + sal_Int32 m_nPage; + PDFWriter::DestAreaType m_eType; + Rectangle m_aRect; + }; + +//--->i56629 + struct PDFNamedDest + { + rtl::OUString m_aDestName; + sal_Int32 m_nPage; + PDFWriter::DestAreaType m_eType; + Rectangle m_aRect; + }; +//<--- + + struct PDFOutlineEntry + { + sal_Int32 m_nParentID; + sal_Int32 m_nObject; + sal_Int32 m_nParentObject; + sal_Int32 m_nNextObject; + sal_Int32 m_nPrevObject; + std::vector< sal_Int32 > m_aChildren; + rtl::OUString m_aTitle; + sal_Int32 m_nDestID; + + PDFOutlineEntry() + : m_nParentID( -1 ), + m_nObject( 0 ), + m_nParentObject( 0 ), + m_nNextObject( 0 ), + m_nPrevObject( 0 ), + m_nDestID( -1 ) + {} + }; + + struct PDFAnnotation + { + sal_Int32 m_nObject; + Rectangle m_aRect; + sal_Int32 m_nPage; + + PDFAnnotation() + : m_nObject( -1 ), + m_nPage( -1 ) + {} + }; + + struct PDFLink : public PDFAnnotation + { + sal_Int32 m_nDest; // set to -1 for URL, to a dest else + rtl::OUString m_aURL; + sal_Int32 m_nStructParent; // struct parent entry + + PDFLink() + : m_nDest( -1 ), + m_nStructParent( -1 ) + {} + }; + + struct PDFNoteEntry : public PDFAnnotation + { + PDFNote m_aContents; + + PDFNoteEntry() + {} + }; + + typedef boost::unordered_map< rtl::OString, SvMemoryStream*, rtl::OStringHash > PDFAppearanceStreams; + typedef boost::unordered_map< rtl::OString, PDFAppearanceStreams, rtl::OStringHash > PDFAppearanceMap; + + struct PDFWidget : public PDFAnnotation + { + PDFWriter::WidgetType m_eType; + rtl::OString m_aName; + rtl::OUString m_aDescription; + rtl::OUString m_aText; + sal_uInt16 m_nTextStyle; + rtl::OUString m_aValue; + rtl::OString m_aDAString; + rtl::OString m_aDRDict; + rtl::OString m_aMKDict; + rtl::OString m_aMKDictCAString; // i12626, added to be able to encrypt the /CA text string + // since the object number is not known at the moment + // of filling m_aMKDict, the string will be encrypted when emitted. + // the /CA string MUST BE the last added to m_aMKDict + // see code for details + sal_Int32 m_nFlags; + sal_Int32 m_nParent; // if not 0, parent's object number + std::vector<sal_Int32> m_aKids; // widget children, contains object numbers + std::vector<sal_Int32> m_aKidsIndex; // widget children, contains index to m_aWidgets + rtl::OUString m_aOnValue; + sal_Int32 m_nTabOrder; // lowest number gets first in tab order + sal_Int32 m_nRadioGroup; + sal_Int32 m_nMaxLen; + bool m_bSubmit; + bool m_bSubmitGet; + sal_Int32 m_nDest; + std::vector<rtl::OUString> m_aListEntries; + std::vector<sal_Int32> m_aSelectedEntries; + PDFAppearanceMap m_aAppearances; + PDFWidget() + : m_eType( PDFWriter::PushButton ), + m_nTextStyle( 0 ), + m_nFlags( 0 ), + m_nParent( 0 ), + m_nRadioGroup( -1 ), + m_nMaxLen( 0 ), + m_bSubmit( false ), + m_bSubmitGet( false ), + m_nDest( -1 ) + {} + }; + + struct PDFStructureAttribute + { + PDFWriter::StructAttributeValue eValue; + sal_Int32 nValue; + + PDFStructureAttribute() + : eValue( PDFWriter::Invalid ), + nValue( 0 ) + {} + + PDFStructureAttribute( PDFWriter::StructAttributeValue eVal ) + : eValue( eVal ), + nValue( 0 ) + {} + + PDFStructureAttribute( sal_Int32 nVal ) + : eValue( PDFWriter::Invalid ), + nValue( nVal ) + {} + }; + + typedef std::map<PDFWriter::StructAttribute, PDFStructureAttribute > PDFStructAttributes; + + struct PDFStructureElementKid // for Kids entries + { + sal_Int32 nObject; // an object number if nMCID is -1, + // else the page object relevant to MCID + sal_Int32 nMCID; // an MCID if >= 0 + + PDFStructureElementKid( sal_Int32 nObj ) : nObject( nObj ), nMCID( -1 ) {} + PDFStructureElementKid( sal_Int32 MCID, sal_Int32 nPage ) : nObject( nPage ), nMCID( MCID ) {} + }; + + struct PDFStructureElement + { + sal_Int32 m_nObject; + PDFWriter::StructElement m_eType; + rtl::OString m_aAlias; + sal_Int32 m_nOwnElement; // index into structure vector + sal_Int32 m_nParentElement; // index into structure vector + sal_Int32 m_nFirstPageObject; + bool m_bOpenMCSeq; + std::list< sal_Int32 > m_aChildren; // indexes into structure vector + std::list< PDFStructureElementKid > m_aKids; + PDFStructAttributes m_aAttributes; + Rectangle m_aBBox; + rtl::OUString m_aActualText; + rtl::OUString m_aAltText; + com::sun::star::lang::Locale m_aLocale; + + // m_aContents contains the element's marked content sequence + // as pairs of (page nr, MCID) + + PDFStructureElement() + : m_nObject( 0 ), + m_eType( PDFWriter::NonStructElement ), + m_nOwnElement( -1 ), + m_nParentElement( -1 ), + m_nFirstPageObject( 0 ), + m_bOpenMCSeq( false ) + { + } + + }; + + struct PDFAddStream + { + rtl::OUString m_aMimeType; + PDFOutputStream* m_pStream; + sal_Int32 m_nStreamObject; + bool m_bCompress; + + PDFAddStream() : m_pStream( NULL ), m_nStreamObject( 0 ), m_bCompress( true ) {} + }; + + + // helper structure for drawLayout and friends + struct PDFGlyph + { + Point m_aPos; + sal_Int32 m_nNativeWidth; + sal_Int32 m_nGlyphId; + sal_Int32 m_nMappedFontId; + sal_uInt8 m_nMappedGlyphId; + + PDFGlyph( const Point& rPos, + sal_Int32 nNativeWidth, + sal_Int32 nGlyphId, + sal_Int32 nFontId, + sal_uInt8 nMappedGlyphId ) + : m_aPos( rPos ), m_nNativeWidth( nNativeWidth ), m_nGlyphId( nGlyphId ), + m_nMappedFontId( nFontId ), m_nMappedGlyphId( nMappedGlyphId ) + {} + }; + + + static const sal_Char* getStructureTag( PDFWriter::StructElement ); + static const sal_Char* getAttributeTag( PDFWriter::StructAttribute eAtr ); + static const sal_Char* getAttributeValueTag( PDFWriter::StructAttributeValue eVal ); + + // returns true if compression was done + // else false + static bool compressStream( SvMemoryStream* ); + + static void convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut ); +private: + static const BuiltinFont m_aBuiltinFonts[14]; + + OutputDevice* m_pReferenceDevice; + + MapMode m_aMapMode; // PDFWriterImpl scaled units + std::vector< PDFPage > m_aPages; + /* maps object numbers to file offsets (needed for xref) */ + std::vector< sal_uInt64 > m_aObjects; + /* contains Bitmaps until they are written to the + * file stream as XObjects*/ + std::list< BitmapEmit > m_aBitmaps; + /* contains JPG streams until written to file */ + std::list<JPGEmit> m_aJPGs; + /*--->i56629 contains all named destinations ever set during the PDF creation, + destination id is always the destination's position in this vector + */ + std::vector<PDFNamedDest> m_aNamedDests; + //<--- + /* contains all dests ever set during the PDF creation, + dest id is always the dest's position in this vector + */ + std::vector<PDFDest> m_aDests; + /* contains all links ever set during PDF creation, + link id is always the link's position in this vector + */ + std::vector<PDFLink> m_aLinks; + /* makes correctly encoded for export to PDF URLS + */ + com::sun::star::uno::Reference< com::sun::star::util::XURLTransformer > m_xTrans; + /* maps arbitrary link ids for structure attributes to real link ids + (for setLinkPropertyId) + */ + std::map<sal_Int32, sal_Int32> m_aLinkPropertyMap; + /* contains all outline items, + object 0 is the outline root + */ + std::vector<PDFOutlineEntry> m_aOutline; + /* contains all notes set during PDF creation + */ + std::vector<PDFNoteEntry> m_aNotes; + /* the root of the structure tree + */ + std::vector<PDFStructureElement> m_aStructure; + /* current object in the structure hierarchy + */ + sal_Int32 m_nCurrentStructElement; + /* structure parent tree */ + std::vector< rtl::OString > m_aStructParentTree; + /* emit strucure marks currently (aka. NonStructElement or not) + */ + bool m_bEmitStructure; + bool m_bNewMCID; + /* role map of struct tree root */ + boost::unordered_map< rtl::OString, rtl::OString, rtl::OStringHash > + m_aRoleMap; + + /* contains all widgets used in the PDF + */ + std::vector<PDFWidget> m_aWidgets; + /* maps radio group id to index of radio group control in m_aWidgets */ + std::map< sal_Int32, sal_Int32 > m_aRadioGroupWidgets; + /* used to store control id during beginControlAppearance/endControlAppearance */ + sal_Int32 m_nCurrentControl; + /* boost::unordered_map for field names, used to ensure unique field names */ + boost::unordered_map< rtl::OString, sal_Int32, rtl::OStringHash > m_aFieldNameMap; + + /* contains Bitmaps for gradient functions until they are written + * to the file stream */ + std::list< GradientEmit > m_aGradients; + /* contains bitmap tiling patterns */ + std::vector< TilingEmit > m_aTilings; + std::list< TransparencyEmit > m_aTransparentObjects; + /* contains all font subsets in use */ + FontSubsetData m_aSubsets; + bool m_bEmbedStandardFonts; + FontEmbedData m_aEmbeddedFonts; + FontEmbedData m_aSystemFonts; + sal_Int32 m_nNextFID; + PDFFontCache m_aFontCache; + + sal_Int32 m_nInheritedPageWidth; // in inch/72 + sal_Int32 m_nInheritedPageHeight; // in inch/72 + PDFWriter::Orientation m_eInheritedOrientation; + sal_Int32 m_nCurrentPage; + + sal_Int32 m_nCatalogObject; + sal_Int32 m_nResourceDict; + ResourceDict m_aGlobalResourceDict; + sal_Int32 m_nFontDictObject; + std::map< sal_Int32, sal_Int32 > m_aBuiltinFontToObjectMap; + + PDFWriter::PDFWriterContext m_aContext; + oslFileHandle m_aFile; + bool m_bOpen; + + + /* output redirection; e.g. to accumulate content streams for + XObjects + */ + struct StreamRedirect + { + SvStream* m_pStream; + MapMode m_aMapMode; + Rectangle m_aTargetRect; + ResourceDict m_aResourceDict; + }; + std::list< StreamRedirect > m_aOutputStreams; + + // graphics state + struct GraphicsState + { + Font m_aFont; + MapMode m_aMapMode; + Color m_aLineColor; + Color m_aFillColor; + Color m_aTextLineColor; + Color m_aOverlineColor; + basegfx::B2DPolyPolygon m_aClipRegion; + bool m_bClipRegion; + sal_Int32 m_nAntiAlias; + sal_Int32 m_nLayoutMode; + LanguageType m_aDigitLanguage; + sal_Int32 m_nTransparentPercent; + sal_uInt16 m_nFlags; + sal_uInt16 m_nUpdateFlags; + + static const sal_uInt16 updateFont = 0x0001; + static const sal_uInt16 updateMapMode = 0x0002; + static const sal_uInt16 updateLineColor = 0x0004; + static const sal_uInt16 updateFillColor = 0x0008; + static const sal_uInt16 updateTextLineColor = 0x0010; + static const sal_uInt16 updateOverlineColor = 0x0020; + static const sal_uInt16 updateClipRegion = 0x0040; + static const sal_uInt16 updateAntiAlias = 0x0080; + static const sal_uInt16 updateLayoutMode = 0x0100; + static const sal_uInt16 updateTransparentPercent = 0x0200; + static const sal_uInt16 updateDigitLanguage = 0x0400; + + GraphicsState() : + m_aLineColor( COL_TRANSPARENT ), + m_aFillColor( COL_TRANSPARENT ), + m_aTextLineColor( COL_TRANSPARENT ), + m_aOverlineColor( COL_TRANSPARENT ), + m_bClipRegion( false ), + m_nAntiAlias( 1 ), + m_nLayoutMode( 0 ), + m_aDigitLanguage( 0 ), + m_nTransparentPercent( 0 ), + m_nFlags( 0xffff ), + m_nUpdateFlags( 0xffff ) + {} + GraphicsState( const GraphicsState& rState ) : + m_aFont( rState.m_aFont ), + m_aMapMode( rState.m_aMapMode ), + m_aLineColor( rState.m_aLineColor ), + m_aFillColor( rState.m_aFillColor ), + m_aTextLineColor( rState.m_aTextLineColor ), + m_aOverlineColor( rState.m_aOverlineColor ), + m_aClipRegion( rState.m_aClipRegion ), + m_bClipRegion( rState.m_bClipRegion ), + m_nAntiAlias( rState.m_nAntiAlias ), + m_nLayoutMode( rState.m_nLayoutMode ), + m_aDigitLanguage( rState.m_aDigitLanguage ), + m_nTransparentPercent( rState.m_nTransparentPercent ), + m_nFlags( rState.m_nFlags ), + m_nUpdateFlags( rState.m_nUpdateFlags ) + { + } + + GraphicsState& operator=(const GraphicsState& rState ) + { + m_aFont = rState.m_aFont; + m_aMapMode = rState.m_aMapMode; + m_aLineColor = rState.m_aLineColor; + m_aFillColor = rState.m_aFillColor; + m_aTextLineColor = rState.m_aTextLineColor; + m_aOverlineColor = rState.m_aOverlineColor; + m_aClipRegion = rState.m_aClipRegion; + m_bClipRegion = rState.m_bClipRegion; + m_nAntiAlias = rState.m_nAntiAlias; + m_nLayoutMode = rState.m_nLayoutMode; + m_aDigitLanguage = rState.m_aDigitLanguage; + m_nTransparentPercent = rState.m_nTransparentPercent; + m_nFlags = rState.m_nFlags; + m_nUpdateFlags = rState.m_nUpdateFlags; + return *this; + } + }; + std::list< GraphicsState > m_aGraphicsStack; + GraphicsState m_aCurrentPDFState; + + ZCodec* m_pCodec; + SvMemoryStream* m_pMemStream; + + std::vector< PDFAddStream > m_aAdditionalStreams; + std::set< PDFWriter::ErrorCode > m_aErrors; + + rtlDigest m_aDocDigest; + +/* +variables for PDF security +i12626 +*/ +/* used to cipher the stream data and for password management */ + rtlCipher m_aCipher; + rtlDigest m_aDigest; + /* pad string used for password in Standard security handler */ + static const sal_uInt8 s_nPadString[ENCRYPTED_PWD_SIZE]; + + /* the encryption key, formed with the user password according to algorithm 3.2, maximum length is 16 bytes + 3 + 2 + for 128 bit security */ + sal_Int32 m_nKeyLength; // key length, 16 or 5 + sal_Int32 m_nRC4KeyLength; // key length, 16 or 10, to be input to the algorith 3.1 + + /* set to true if the following stream must be encrypted, used inside writeBuffer() */ + sal_Bool m_bEncryptThisStream; + + /* the numerical value of the access permissions, according to PDF spec, must be signed */ + sal_Int32 m_nAccessPermissions; + /* string to hold the PDF creation date */ + rtl::OString m_aCreationDateString; + /* string to hold the PDF creation date, for PDF/A metadata */ + rtl::OString m_aCreationMetaDateString; + /* the buffer where the data are encrypted, dynamically allocated */ + sal_uInt8 *m_pEncryptionBuffer; + /* size of the buffer */ + sal_Int32 m_nEncryptionBufferSize; + + /* check and reallocate the buffer for encryption */ + sal_Bool checkEncryptionBufferSize( register sal_Int32 newSize ); + /* this function implements part of the PDF spec algorithm 3.1 in encryption, the rest (the actual encryption) is in PDFWriterImpl::writeBuffer */ + void checkAndEnableStreamEncryption( register sal_Int32 nObject ); + + void disableStreamEncryption() { m_bEncryptThisStream = false; }; + + /* */ + void enableStringEncryption( register sal_Int32 nObject ); + +// test if the encryption is active, if yes than encrypt the unicode string and add to the OStringBuffer parameter + void appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ); + + void appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc = RTL_TEXTENCODING_ASCII_US ); + void appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ); + void appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ); + + /* creates fonts and subsets that will be emitted later */ + void registerGlyphs( int nGlyphs, sal_GlyphId* pGlyphs, sal_Int32* pGlpyhWidths, sal_Ucs* pUnicodes, sal_Int32* pUnicodesPerGlyph, sal_uInt8* pMappedGlyphs, sal_Int32* pMappedFontObjects, const ImplFontData* pFallbackFonts[] ); + + /* emits a text object according to the passed layout */ + /* TODO: remove rText as soon as SalLayout will change so that rText is not necessary anymore */ + void drawVerticalGlyphs( const std::vector<PDFGlyph>& rGlyphs, rtl::OStringBuffer& rLine, const Point& rAlignOffset, const Matrix3& rRotScale, double fAngle, double fXScale, double fSkew, sal_Int32 nFontHeight ); + void drawHorizontalGlyphs( const std::vector<PDFGlyph>& rGlyphs, rtl::OStringBuffer& rLine, const Point& rAlignOffset, double fAngle, double fXScale, double fSkew, sal_Int32 nFontHeight, sal_Int32 nPixelFontHeight ); + void drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines ); + void drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines ); + void drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines ); + + /* writes differences between graphics stack and current real PDF + * state to the file + */ + void updateGraphicsState(); + + /* writes a transparency group object */ + bool writeTransparentObject( TransparencyEmit& rObject ); + + /* writes an XObject of type image, may create + a second for the mask + */ + bool writeBitmapObject( BitmapEmit& rObject, bool bMask = false ); + + bool writeJPG( JPGEmit& rEmit ); + + /* tries to find the bitmap by its id and returns its emit data if exists, + else creates a new emit data block */ + const BitmapEmit& createBitmapEmit( const BitmapEx& rBitmapEx, bool bDrawMask = false ); + + /* writes the Do operation inside the content stream */ + void drawBitmap( const Point& rDestPt, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor ); + /* write the function object for a Gradient */ + bool writeGradientFunction( GradientEmit& rObject ); + /* creates a GradientEmit and returns its object number */ + sal_Int32 createGradient( const Gradient& rGradient, const Size& rSize ); + + /* writes all tilings */ + bool emitTilings(); + /* writes all gradient patterns */ + bool emitGradients(); + /* writes a builtin font object and returns its objectid (or 0 in case of failure ) */ + sal_Int32 emitBuiltinFont( const ImplFontData*, sal_Int32 nObject = -1 ); + /* writes a type1 embedded font object and returns its mapping from font ids to object ids (or 0 in case of failure ) */ + std::map< sal_Int32, sal_Int32 > emitEmbeddedFont( const ImplFontData*, EmbedFont& ); + /* writes a type1 system font object and returns its mapping from font ids to object ids (or 0 in case of failure ) */ + std::map< sal_Int32, sal_Int32 > emitSystemFont( const ImplFontData*, EmbedFont& ); + /* writes a font descriptor and returns its object id (or 0) */ + sal_Int32 emitFontDescriptor( const ImplFontData*, FontSubsetInfo&, sal_Int32 nSubsetID, sal_Int32 nStream ); + /* writes a ToUnicode cmap, returns the corresponding stream object */ + sal_Int32 createToUnicodeCMap( sal_uInt8* pEncoding, sal_Ucs* pUnicodes, sal_Int32* pUnicodesPerGlyph, sal_Int32* pEncToUnicodeIndex, int nGlyphs ); + + /* get resource dict object number */ + sal_Int32 getResourceDictObj() + { + if( m_nResourceDict <= 0 ) + m_nResourceDict = createObject(); + return m_nResourceDict; + } + /* get the font dict object */ + sal_Int32 getFontDictObject() + { + if( m_nFontDictObject <= 0 ) + m_nFontDictObject = createObject(); + return m_nFontDictObject; + } + /* push resource into current (redirected) resource dict */ + void pushResource( ResourceKind eKind, const rtl::OString& rResource, sal_Int32 nObject ); + + void appendBuiltinFontsToDict( rtl::OStringBuffer& rDict ) const; + /* writes a the font dictionary and emits all font objects + * returns object id of font directory (or 0 on error) + */ + bool emitFonts(); + /* writes the Resource dictionary; + * returns dict object id (or 0 on error) + */ + sal_Int32 emitResources(); + // appends a dest + bool appendDest( sal_Int32 nDestID, rtl::OStringBuffer& rBuffer ); + // write all links + bool emitLinkAnnotations(); + // write all notes + bool emitNoteAnnotations(); + // write the appearance streams of a widget + bool emitAppearances( PDFWidget& rWidget, rtl::OStringBuffer& rAnnotDict ); + // clean up radio button "On" values + void ensureUniqueRadioOnValues(); + // write all widgets + bool emitWidgetAnnotations(); + // writes all annotation objects + bool emitAnnotations(); + // writes the dest dict for the catalog + sal_Int32 emitDestDict(); + //write the named destination stuff + sal_Int32 emitNamedDestinations();//i56629 + // writes outline dict and tree + sal_Int32 emitOutline(); + // puts the attribute objects of a structure element into the returned string, + // helper for emitStructure + rtl::OString emitStructureAttributes( PDFStructureElement& rEle ); + //--->i94258 + // the maximum array elements allowed for PDF array object + static const sal_uInt32 ncMaxPDFArraySize = 8191; + //check if internal dummy container are needed in the structure elements + void addInternalStructureContainer( PDFStructureElement& rEle ); + //<---i94258 + // writes document structure + sal_Int32 emitStructure( PDFStructureElement& rEle ); + // writes structure parent tree + sal_Int32 emitStructParentTree( sal_Int32 nTreeObject ); + // writes page tree and catalog + bool emitCatalog(); + // writes xref and trailer + bool emitTrailer(); + // emit additional streams collected; also create there object numbers + bool emitAdditionalStreams(); + // emits info dict (if applicable) + sal_Int32 emitInfoDict( ); + + // acrobat reader 5 and 6 use the order of the annotations + // as their tab order; since PDF1.5 one can make the + // tab order explicit by using the structure tree + void sortWidgets(); + + // updates the count numbers of outline items + sal_Int32 updateOutlineItemCount( std::vector< sal_Int32 >& rCounts, + sal_Int32 nItemLevel, + sal_Int32 nCurrentItemId ); + // default appearences for widgets + sal_Int32 findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rRadio ); + Font replaceFont( const Font& rControlFont, const Font& rAppSetFont ); + sal_Int32 getBestBuiltinFont( const Font& rFont ); + sal_Int32 getSystemFont( const Font& i_rFont ); + + // used for edit and listbox + Font drawFieldBorder( PDFWidget&, const PDFWriter::AnyWidget&, const StyleSettings& ); + + void createDefaultPushButtonAppearance( PDFWidget&, const PDFWriter::PushButtonWidget& rWidget ); + void createDefaultCheckBoxAppearance( PDFWidget&, const PDFWriter::CheckBoxWidget& rWidget ); + void createDefaultRadioButtonAppearance( PDFWidget&, const PDFWriter::RadioButtonWidget& rWidget ); + void createDefaultEditAppearance( PDFWidget&, const PDFWriter::EditWidget& rWidget ); + void createDefaultListBoxAppearance( PDFWidget&, const PDFWriter::ListBoxWidget& rWidget ); + + /* ensure proper escapement and uniqueness of field names */ + void createWidgetFieldName( sal_Int32 i_nWidgetsIndex, const PDFWriter::AnyWidget& i_rInWidget ); + /* adds an entry to m_aObjects and returns its index+1, + * sets the offset to ~0 + */ + sal_Int32 createObject(); + /* sets the offset of object n to the current position of output file+1 + */ + bool updateObject( sal_Int32 n ); + + bool writeBuffer( const void* pBuffer, sal_uInt64 nBytes ); + void beginCompression(); + void endCompression(); + void beginRedirect( SvStream* pStream, const Rectangle& ); + // returns an empty rect if no redirection is happending + Rectangle getRedirectTargetRect() const; + SvStream* endRedirect(); + + void endPage(); + + void beginStructureElementMCSeq(); + void endStructureElementMCSeq(); + /** checks whether a non struct element lies in the ancestor hierarchy + of the current structure element + + @returns + <true/> if no NonStructElement was found in ancestor path and tagged + PDF output is enabled + <false/> else + */ + bool checkEmitStructure(); + + /* draws an emphasis mark */ + void drawEmphasisMark( long nX, long nY, const PolyPolygon& rPolyPoly, sal_Bool bPolyLine, const Rectangle& rRect1, const Rectangle& rRect2 ); + + /* true if PDF/A-1a or PDF/A-1b is output */ + sal_Bool m_bIsPDF_A1; + PDFWriter& m_rOuterFace; + + /* + i12626 + methods for PDF security + + pad a password according algorithm 3.2, step 1 */ + static void padPassword( const rtl::OUString& i_rPassword, sal_uInt8* o_pPaddedPW ); + /* algorithm 3.2: compute an encryption key */ + static bool computeEncryptionKey( EncHashTransporter*, + vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, + sal_Int32 i_nAccessPermissions + ); + /* algorithm 3.3: computing the encryption dictionary'ss owner password value ( /O ) */ + static bool computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword, const sal_uInt8* i_pPaddedUserPassword, + std::vector< sal_uInt8 >& io_rOValue, + sal_Int32 i_nKeyLength + ); + /* algorithm 3.4 or 3.5: computing the encryption dictionary's user password value ( /U ) revision 2 or 3 of the standard security handler */ + static bool computeUDictionaryValue( EncHashTransporter* i_pTransporter, + vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, + sal_Int32 i_nKeyLength, + sal_Int32 i_nAccessPermissions + ); + + static void computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier, + const vcl::PDFWriter::PDFDocInfo& i_rDocInfo, + rtl::OString& o_rCString1, + rtl::OString& o_rCString2 + ); + static sal_Int32 computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties, + sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength ); + void setupDocInfo(); + bool prepareEncryption( const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& ); + + // helper for playMetafile + void implWriteGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient, + VirtualDevice* pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& ); + void implWriteBitmapEx( const Point& rPoint, const Size& rSize, const BitmapEx& rBitmapEx, + VirtualDevice* pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& ); + + // helpers for CCITT 1bit bitmap stream + void putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState ); + void putG4Span( long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState ); + void writeG4Stream( BitmapReadAccess* i_pBitmap ); + + // color helper functions + void appendStrokingColor( const Color& rColor, rtl::OStringBuffer& rBuffer ); + void appendNonStrokingColor( const Color& rColor, rtl::OStringBuffer& rBuffer ); +public: + PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >&, PDFWriter& ); + ~PDFWriterImpl(); + + static com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder > + initEncryption( const rtl::OUString& i_rOwnerPassword, + const rtl::OUString& i_rUserPassword, + bool b128Bit ); + + /* for OutputDevice so the reference device can have a list + * that contains only suitable fonts (subsettable or builtin) + * produces a new font list + */ + ImplDevFontList* filterDevFontList( ImplDevFontList* pFontList ); + /* for OutputDevice: get layout for builtin fonts + */ + bool isBuiltinFont( const ImplFontData* ) const; + SalLayout* GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pFont ); + void getFontMetric( ImplFontSelectData* pFont, ImplFontMetricData* pMetric ) const; + + + /* for documentation of public functions please see pdfwriter.hxx */ + + OutputDevice* getReferenceDevice(); + + /* document structure */ + sal_Int32 newPage( sal_Int32 nPageWidth , sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ); + bool emit(); + std::set< PDFWriter::ErrorCode > getErrors(); + void insertError( PDFWriter::ErrorCode eErr ) { m_aErrors.insert( eErr ); } + void playMetafile( const GDIMetaFile&, vcl::PDFExtOutDevData*, const vcl::PDFWriter::PlayMetafileContext&, VirtualDevice* pDummyDev = NULL ); + + Size getCurPageSize() const + { + Size aSize; + if( m_nCurrentPage >= 0 && m_nCurrentPage < (sal_Int32)m_aPages.size() ) + aSize = Size( m_aPages[ m_nCurrentPage ].m_nPageWidth, m_aPages[ m_nCurrentPage ].m_nPageHeight ); + return aSize; + } + + PDFWriter::PDFVersion getVersion() const { return m_aContext.Version; } + + void setDocumentLocale( const com::sun::star::lang::Locale& rLoc ) + { m_aContext.DocumentLocale = rLoc; } + + + /* graphics state */ + void push( sal_uInt16 nFlags ); + void pop(); + + void setFont( const Font& rFont ); + + void setMapMode( const MapMode& rMapMode ); + void setMapMode() { setMapMode( m_aMapMode ); } + + + const MapMode& getMapMode() { return m_aGraphicsStack.front().m_aMapMode; } + + void setLineColor( const Color& rColor ) + { + m_aGraphicsStack.front().m_aLineColor = ImplIsColorTransparent(rColor) ? Color( COL_TRANSPARENT ) : rColor; + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateLineColor; + } + + void setFillColor( const Color& rColor ) + { + m_aGraphicsStack.front().m_aFillColor = ImplIsColorTransparent(rColor) ? Color( COL_TRANSPARENT ) : rColor; + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFillColor; + } + + void setTextLineColor() + { + m_aGraphicsStack.front().m_aTextLineColor = Color( COL_TRANSPARENT ); + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateTextLineColor; + } + + void setTextLineColor( const Color& rColor ) + { + m_aGraphicsStack.front().m_aTextLineColor = rColor; + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateTextLineColor; + } + + void setOverlineColor() + { + m_aGraphicsStack.front().m_aOverlineColor = Color( COL_TRANSPARENT ); + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateOverlineColor; + } + + void setOverlineColor( const Color& rColor ) + { + m_aGraphicsStack.front().m_aOverlineColor = rColor; + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateOverlineColor; + } + + void setTextFillColor( const Color& rColor ) + { + m_aGraphicsStack.front().m_aFont.SetFillColor( rColor ); + m_aGraphicsStack.front().m_aFont.SetTransparent( ImplIsColorTransparent( rColor ) ? sal_True : sal_False ); + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont; + } + void setTextFillColor() + { + m_aGraphicsStack.front().m_aFont.SetFillColor( Color( COL_TRANSPARENT ) ); + m_aGraphicsStack.front().m_aFont.SetTransparent( sal_True ); + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont; + } + void setTextColor( const Color& rColor ) + { + m_aGraphicsStack.front().m_aFont.SetColor( rColor ); + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont; + } + + void clearClipRegion() + { + m_aGraphicsStack.front().m_aClipRegion.clear(); + m_aGraphicsStack.front().m_bClipRegion = false; + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; + } + + void setClipRegion( const basegfx::B2DPolyPolygon& rRegion ); + + void moveClipRegion( sal_Int32 nX, sal_Int32 nY ); + + bool intersectClipRegion( const Rectangle& rRect ); + + bool intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion ); + + void setLayoutMode( sal_Int32 nLayoutMode ) + { + m_aGraphicsStack.front().m_nLayoutMode = nLayoutMode; + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateLayoutMode; + } + + void setDigitLanguage( LanguageType eLang ) + { + m_aGraphicsStack.front().m_aDigitLanguage = eLang; + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateDigitLanguage; + } + + void setTextAlign( TextAlign eAlign ) + { + m_aGraphicsStack.front().m_aFont.SetAlign( eAlign ); + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont; + } + + void setAntiAlias( sal_Int32 nAntiAlias ) + { + m_aGraphicsStack.front().m_nAntiAlias = nAntiAlias; + m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateAntiAlias; + } + + /* actual drawing functions */ + void drawText( const Point& rPos, const String& rText, xub_StrLen nIndex = 0, xub_StrLen nLen = STRING_LEN, bool bTextLines = true ); + void drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray = NULL, xub_StrLen nIndex = 0, xub_StrLen nLen = STRING_LEN, bool bTextLines = true ); + void drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, + xub_StrLen nIndex = 0, xub_StrLen nLen = STRING_LEN, + bool bTextLines = true ); + void drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines = true ); + void drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove ); + void drawWaveTextLine( rtl::OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ); + void drawStraightTextLine( rtl::OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ); + void drawStrikeoutLine( rtl::OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor ); + void drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout ); + + void drawLine( const Point& rStart, const Point& rStop ); + void drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo ); + void drawPolygon( const Polygon& rPoly ); + void drawPolyPolygon( const PolyPolygon& rPolyPoly ); + void drawPolyLine( const Polygon& rPoly ); + void drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo ); + void drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo ); + void drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth ); + + void drawPixel( const Point& rPt, const Color& rColor ); + void drawPixel( const Polygon& rPts, const Color* pColors = NULL ); + + void drawRectangle( const Rectangle& rRect ); + void drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound ); + void drawEllipse( const Rectangle& rRect ); + void drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWidthChord ); + + void drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap ); + void drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap ); + void drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor ); + void drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask ); + + void drawGradient( const Rectangle& rRect, const Gradient& rGradient ); + void drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient ); + void drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch ); + void drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall ); + void drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent ); + void beginTransparencyGroup(); + void endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent ); + void endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask ); + void beginPattern( const Rectangle& rCell ); + sal_Int32 endPattern( const SvtGraphicFill::Transform& rTransform ); + void drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill ); + + void emitComment( const char* pComment ); + + //--->i56629 named destinations + sal_Int32 createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr = -1, PDFWriter::DestAreaType eType = PDFWriter::XYZ ); + + //--->i59651 + //emits output intent + sal_Int32 emitOutputIntent(); + + //emits the document metadata + sal_Int32 emitDocumentMetadata(); + + // links + sal_Int32 createLink( const Rectangle& rRect, sal_Int32 nPageNr = -1 ); + sal_Int32 createDest( const Rectangle& rRect, sal_Int32 nPageNr = -1, PDFWriter::DestAreaType eType = PDFWriter::XYZ ); + sal_Int32 setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ); + sal_Int32 setLinkURL( sal_Int32 nLinkId, const rtl::OUString& rURL ); + void setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId ); + + // outline + sal_Int32 createOutlineItem( sal_Int32 nParent = 0, const rtl::OUString& rText = rtl::OUString(), sal_Int32 nDestID = -1 ); + sal_Int32 setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent ); + sal_Int32 setOutlineItemText( sal_Int32 nItem, const rtl::OUString& rText ); + sal_Int32 setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID ); + + // notes + void createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr = -1 ); + // structure elements + sal_Int32 beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias ); + void endStructureElement(); + bool setCurrentStructureElement( sal_Int32 nElement ); + sal_Int32 getCurrentStructureElement(); + bool setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal ); + bool setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue ); + void setStructureBoundingBox( const Rectangle& rRect ); + void setActualText( const String& rText ); + void setAlternateText( const String& rText ); + + // transitional effects + void setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr = -1 ); + void setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr = -1 ); + + // controls + sal_Int32 createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr = -1 ); + void beginControlAppearance( sal_Int32 nControl ); + bool endControlAppearance( PDFWriter::WidgetState eState ); + + // additional streams + void addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress ); + + // helper: eventually begin marked content sequence and + // emit a comment in debug case + void MARK( const char* +#if OSL_DEBUG_LEVEL > 1 + pString +#endif + ) + { + beginStructureElementMCSeq(); +#if OSL_DEBUG_LEVEL > 1 + emitComment( pString ); +#endif + } +}; + +} + +#endif //_VCL_PDFEXPORT_HXX + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/pdfwriter_impl2.cxx b/vcl/source/gdi/pdfwriter_impl2.cxx new file mode 100644 index 000000000000..3e957d175897 --- /dev/null +++ b/vcl/source/gdi/pdfwriter_impl2.cxx @@ -0,0 +1,2037 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_vcl.hxx" + +#include "pdfwriter_impl.hxx" + +#include "vcl/pdfextoutdevdata.hxx" +#include "vcl/virdev.hxx" +#include "vcl/gdimtf.hxx" +#include "vcl/metaact.hxx" +#include "vcl/bmpacc.hxx" +#include "vcl/graph.hxx" +#include "vcl/svdata.hxx" +#include "unotools/streamwrap.hxx" +#include "unotools/processfactory.hxx" +#include "comphelper/processfactory.hxx" + +#include "com/sun/star/beans/PropertyValue.hpp" +#include "com/sun/star/io/XSeekable.hpp" +#include "com/sun/star/graphic/XGraphicProvider.hpp" + +#include "cppuhelper/implbase1.hxx" + +#include <rtl/digest.h> + +#undef USE_PDFGRADIENTS + +using namespace vcl; +using namespace rtl; +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; + +// ----------------------------------------------------------------------------- + +void PDFWriterImpl::implWriteGradient( const PolyPolygon& i_rPolyPoly, const Gradient& i_rGradient, + VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext ) +{ + GDIMetaFile aTmpMtf; + + i_pDummyVDev->AddGradientActions( i_rPolyPoly.GetBoundRect(), i_rGradient, aTmpMtf ); + + m_rOuterFace.Push(); + m_rOuterFace.IntersectClipRegion( i_rPolyPoly.getB2DPolyPolygon() ); + playMetafile( aTmpMtf, NULL, i_rContext, i_pDummyVDev ); + m_rOuterFace.Pop(); +} + +// ----------------------------------------------------------------------------- + +void PDFWriterImpl::implWriteBitmapEx( const Point& i_rPoint, const Size& i_rSize, const BitmapEx& i_rBitmapEx, + VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext ) +{ + if ( !i_rBitmapEx.IsEmpty() && i_rSize.Width() && i_rSize.Height() ) + { + BitmapEx aBitmapEx( i_rBitmapEx ); + Point aPoint( i_rPoint ); + Size aSize( i_rSize ); + + // #i19065# Negative sizes have mirror semantics on + // OutputDevice. BitmapEx and co. have no idea about that, so + // perform that _before_ doing anything with aBitmapEx. + sal_uLong nMirrorFlags(BMP_MIRROR_NONE); + if( aSize.Width() < 0 ) + { + aSize.Width() *= -1; + aPoint.X() -= aSize.Width(); + nMirrorFlags |= BMP_MIRROR_HORZ; + } + if( aSize.Height() < 0 ) + { + aSize.Height() *= -1; + aPoint.Y() -= aSize.Height(); + nMirrorFlags |= BMP_MIRROR_VERT; + } + + if( nMirrorFlags != BMP_MIRROR_NONE ) + { + aBitmapEx.Mirror( nMirrorFlags ); + } + if( i_rContext.m_nMaxImageResolution > 50 ) + { + // do downsampling if neccessary + const Size aDstSizeTwip( i_pDummyVDev->PixelToLogic( i_pDummyVDev->LogicToPixel( aSize ), MAP_TWIP ) ); + const Size aBmpSize( aBitmapEx.GetSizePixel() ); + const double fBmpPixelX = aBmpSize.Width(); + const double fBmpPixelY = aBmpSize.Height(); + const double fMaxPixelX = aDstSizeTwip.Width() * i_rContext.m_nMaxImageResolution / 1440.0; + const double fMaxPixelY = aDstSizeTwip.Height() * i_rContext.m_nMaxImageResolution / 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() ) + aBitmapEx.Scale( aNewBmpSize ); + else + aBitmapEx.SetEmpty(); + } + } + + const Size aSizePixel( aBitmapEx.GetSizePixel() ); + if ( aSizePixel.Width() && aSizePixel.Height() ) + { + if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) + { + BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS; + int nDepth = aBitmapEx.GetBitmap().GetBitCount(); + if( nDepth <= 4 ) + eConv = BMP_CONVERSION_4BIT_GREYS; + if( nDepth > 1 ) + aBitmapEx.Convert( eConv ); + } + sal_Bool bUseJPGCompression = !i_rContext.m_bOnlyLosslessCompression; + if ( ( aSizePixel.Width() < 32 ) || ( aSizePixel.Height() < 32 ) ) + bUseJPGCompression = sal_False; + + SvMemoryStream aStrm; + Bitmap aMask; + + bool bTrueColorJPG = true; + if ( bUseJPGCompression ) + { + sal_uInt32 nZippedFileSize; // sj: we will calculate the filesize of a zipped bitmap + { // to determine if jpeg compression is usefull + SvMemoryStream aTemp; + aTemp.SetCompressMode( aTemp.GetCompressMode() | COMPRESSMODE_ZBITMAP ); + aTemp.SetVersion( SOFFICE_FILEFORMAT_40 ); // sj: up from version 40 our bitmap stream operator + aTemp << aBitmapEx; // is capable of zlib stream compression + aTemp.Seek( STREAM_SEEK_TO_END ); + nZippedFileSize = aTemp.Tell(); + } + if ( aBitmapEx.IsTransparent() ) + { + if ( aBitmapEx.IsAlpha() ) + aMask = aBitmapEx.GetAlpha().GetBitmap(); + else + aMask = aBitmapEx.GetMask(); + } + Graphic aGraphic( aBitmapEx.GetBitmap() ); + sal_Int32 nColorMode = 0; + + Sequence< PropertyValue > aFilterData( 2 ); + aFilterData[ 0 ].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "Quality" ) ); + aFilterData[ 0 ].Value <<= sal_Int32(i_rContext.m_nJPEGQuality); + aFilterData[ 1 ].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "ColorMode" ) ); + aFilterData[ 1 ].Value <<= nColorMode; + + try + { + uno::Reference < io::XStream > xStream = new utl::OStreamWrapper( aStrm ); + uno::Reference< io::XSeekable > xSeekable( xStream, UNO_QUERY_THROW ); + uno::Reference< graphic::XGraphicProvider > xGraphicProvider( ImplGetSVData()->maAppData.mxMSF->createInstance( + OUString::createFromAscii( "com.sun.star.graphic.GraphicProvider" ) ), UNO_QUERY ); + if ( xGraphicProvider.is() ) + { + uno::Reference< graphic::XGraphic > xGraphic( aGraphic.GetXGraphic() ); + uno::Reference < io::XOutputStream > xOut( xStream->getOutputStream() ); + rtl::OUString aMimeType( ::rtl::OUString::createFromAscii( "image/jpeg" ) ); + uno::Sequence< beans::PropertyValue > aOutMediaProperties( 3 ); + aOutMediaProperties[0].Name = ::rtl::OUString::createFromAscii( "OutputStream" ); + aOutMediaProperties[0].Value <<= xOut; + aOutMediaProperties[1].Name = ::rtl::OUString::createFromAscii( "MimeType" ); + aOutMediaProperties[1].Value <<= aMimeType; + aOutMediaProperties[2].Name = ::rtl::OUString::createFromAscii( "FilterData" ); + aOutMediaProperties[2].Value <<= aFilterData; + xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties ); + xOut->flush(); + if ( xSeekable->getLength() > nZippedFileSize ) + { + bUseJPGCompression = sal_False; + } + else + { + aStrm.Seek( STREAM_SEEK_TO_END ); + + xSeekable->seek( 0 ); + Sequence< PropertyValue > aArgs( 1 ); + aArgs[ 0 ].Name = ::rtl::OUString::createFromAscii( "InputStream" ); + aArgs[ 0 ].Value <<= xStream; + uno::Reference< XPropertySet > xPropSet( xGraphicProvider->queryGraphicDescriptor( aArgs ) ); + if ( xPropSet.is() ) + { + sal_Int16 nBitsPerPixel = 24; + if ( xPropSet->getPropertyValue( ::rtl::OUString::createFromAscii( "BitsPerPixel" ) ) >>= nBitsPerPixel ) + { + bTrueColorJPG = nBitsPerPixel != 8; + } + } + } + } + else + bUseJPGCompression = sal_False; + } + catch( uno::Exception& ) + { + bUseJPGCompression = sal_False; + } + } + if ( bUseJPGCompression ) + m_rOuterFace.DrawJPGBitmap( aStrm, bTrueColorJPG, aSizePixel, Rectangle( aPoint, aSize ), aMask ); + else if ( aBitmapEx.IsTransparent() ) + m_rOuterFace.DrawBitmapEx( aPoint, aSize, aBitmapEx ); + else + m_rOuterFace.DrawBitmap( aPoint, aSize, aBitmapEx.GetBitmap() ); + } + } +} + + +// ----------------------------------------------------------------------------- + +void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevData* i_pOutDevData, const vcl::PDFWriter::PlayMetafileContext& i_rContext, VirtualDevice* pDummyVDev ) +{ + bool bAssertionFired( false ); + + VirtualDevice* pPrivateDevice = NULL; + if( ! pDummyVDev ) + { + pPrivateDevice = pDummyVDev = new VirtualDevice(); + pDummyVDev->EnableOutput( sal_False ); + pDummyVDev->SetMapMode( i_rMtf.GetPrefMapMode() ); + } + GDIMetaFile aMtf( i_rMtf ); + + for( sal_uInt32 i = 0, nCount = aMtf.GetActionCount(); i < nCount; ) + { + if ( !i_pOutDevData || !i_pOutDevData->PlaySyncPageAct( m_rOuterFace, i ) ) + { + const MetaAction* pAction = aMtf.GetAction( i ); + const sal_uInt16 nType = pAction->GetType(); + + switch( nType ) + { + case( META_PIXEL_ACTION ): + { + const MetaPixelAction* pA = (const MetaPixelAction*) pAction; + m_rOuterFace.DrawPixel( pA->GetPoint(), pA->GetColor() ); + } + break; + + case( META_POINT_ACTION ): + { + const MetaPointAction* pA = (const MetaPointAction*) pAction; + m_rOuterFace.DrawPixel( pA->GetPoint() ); + } + break; + + case( META_LINE_ACTION ): + { + const MetaLineAction* pA = (const MetaLineAction*) pAction; + if ( pA->GetLineInfo().IsDefault() ) + m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint() ); + else + m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint(), pA->GetLineInfo() ); + } + break; + + case( META_RECT_ACTION ): + { + const MetaRectAction* pA = (const MetaRectAction*) pAction; + m_rOuterFace.DrawRect( pA->GetRect() ); + } + break; + + case( META_ROUNDRECT_ACTION ): + { + const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pAction; + m_rOuterFace.DrawRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() ); + } + break; + + case( META_ELLIPSE_ACTION ): + { + const MetaEllipseAction* pA = (const MetaEllipseAction*) pAction; + m_rOuterFace.DrawEllipse( pA->GetRect() ); + } + break; + + case( META_ARC_ACTION ): + { + const MetaArcAction* pA = (const MetaArcAction*) pAction; + m_rOuterFace.DrawArc( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() ); + } + break; + + case( META_PIE_ACTION ): + { + const MetaArcAction* pA = (const MetaArcAction*) pAction; + m_rOuterFace.DrawPie( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() ); + } + break; + + case( META_CHORD_ACTION ): + { + const MetaChordAction* pA = (const MetaChordAction*) pAction; + m_rOuterFace.DrawChord( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() ); + } + break; + + case( META_POLYGON_ACTION ): + { + const MetaPolygonAction* pA = (const MetaPolygonAction*) pAction; + m_rOuterFace.DrawPolygon( pA->GetPolygon() ); + } + break; + + case( META_POLYLINE_ACTION ): + { + const MetaPolyLineAction* pA = (const MetaPolyLineAction*) pAction; + if ( pA->GetLineInfo().IsDefault() ) + m_rOuterFace.DrawPolyLine( pA->GetPolygon() ); + else + m_rOuterFace.DrawPolyLine( pA->GetPolygon(), pA->GetLineInfo() ); + } + break; + + case( META_POLYPOLYGON_ACTION ): + { + const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*) pAction; + m_rOuterFace.DrawPolyPolygon( pA->GetPolyPolygon() ); + } + break; + + case( META_GRADIENT_ACTION ): + { + const MetaGradientAction* pA = (const MetaGradientAction*) pAction; + #ifdef USE_PDFGRADIENTS + m_rOuterFace.DrawGradient( pA->GetRect(), pA->GetGradient() ); + #else + const PolyPolygon aPolyPoly( pA->GetRect() ); + implWriteGradient( aPolyPoly, pA->GetGradient(), pDummyVDev, i_rContext ); + #endif + } + break; + + case( META_GRADIENTEX_ACTION ): + { + const MetaGradientExAction* pA = (const MetaGradientExAction*) pAction; + #ifdef USE_PDFGRADIENTS + m_rOuterFace.DrawGradient( pA->GetPolyPolygon(), pA->GetGradient() ); + #else + implWriteGradient( pA->GetPolyPolygon(), pA->GetGradient(), pDummyVDev, i_rContext ); + #endif + } + break; + + case META_HATCH_ACTION: + { + const MetaHatchAction* pA = (const MetaHatchAction*) pAction; + m_rOuterFace.DrawHatch( pA->GetPolyPolygon(), pA->GetHatch() ); + } + break; + + case( META_TRANSPARENT_ACTION ): + { + const MetaTransparentAction* pA = (const MetaTransparentAction*) pAction; + m_rOuterFace.DrawTransparent( pA->GetPolyPolygon(), pA->GetTransparence() ); + } + break; + + case( META_FLOATTRANSPARENT_ACTION ): + { + const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*) pAction; + + GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() ); + const Point& rPos = pA->GetPoint(); + const Size& rSize= pA->GetSize(); + const Gradient& rTransparenceGradient = pA->GetGradient(); + + // special case constant alpha value + if( rTransparenceGradient.GetStartColor() == rTransparenceGradient.GetEndColor() ) + { + const Color aTransCol( rTransparenceGradient.GetStartColor() ); + const sal_uInt16 nTransPercent = aTransCol.GetLuminance() * 100 / 255; + m_rOuterFace.BeginTransparencyGroup(); + playMetafile( aTmpMtf, NULL, i_rContext, pDummyVDev ); + m_rOuterFace.EndTransparencyGroup( Rectangle( rPos, rSize ), nTransPercent ); + } + else + { + const Size aDstSizeTwip( pDummyVDev->PixelToLogic( pDummyVDev->LogicToPixel( rSize ), MAP_TWIP ) ); + sal_Int32 nMaxBmpDPI = i_rContext.m_bOnlyLosslessCompression ? 300 : 72; + if( i_rContext.m_nMaxImageResolution > 50 ) + { + if ( nMaxBmpDPI > i_rContext.m_nMaxImageResolution ) + nMaxBmpDPI = i_rContext.m_nMaxImageResolution; + } + const sal_Int32 nPixelX = (sal_Int32)((double)aDstSizeTwip.Width() * (double)nMaxBmpDPI / 1440.0); + const sal_Int32 nPixelY = (sal_Int32)((double)aDstSizeTwip.Height() * (double)nMaxBmpDPI / 1440.0); + if ( nPixelX && nPixelY ) + { + Size aDstSizePixel( nPixelX, nPixelY ); + VirtualDevice* pVDev = new VirtualDevice; + if( pVDev->SetOutputSizePixel( aDstSizePixel ) ) + { + Bitmap aPaint, aMask; + AlphaMask aAlpha; + Point aPoint; + + MapMode aMapMode( pDummyVDev->GetMapMode() ); + aMapMode.SetOrigin( aPoint ); + pVDev->SetMapMode( aMapMode ); + Size aDstSize( pVDev->PixelToLogic( aDstSizePixel ) ); + + Point aMtfOrigin( aTmpMtf.GetPrefMapMode().GetOrigin() ); + if ( aMtfOrigin.X() || aMtfOrigin.Y() ) + aTmpMtf.Move( -aMtfOrigin.X(), -aMtfOrigin.Y() ); + double fScaleX = (double)aDstSize.Width() / (double)aTmpMtf.GetPrefSize().Width(); + double fScaleY = (double)aDstSize.Height() / (double)aTmpMtf.GetPrefSize().Height(); + if( fScaleX != 1.0 || fScaleY != 1.0 ) + aTmpMtf.Scale( fScaleX, fScaleY ); + aTmpMtf.SetPrefMapMode( aMapMode ); + + // create paint bitmap + aTmpMtf.WindStart(); + aTmpMtf.Play( pVDev, aPoint, aDstSize ); + aTmpMtf.WindStart(); + + pVDev->EnableMapMode( sal_False ); + aPaint = pVDev->GetBitmap( aPoint, aDstSizePixel ); + pVDev->EnableMapMode( sal_True ); + + // create mask bitmap + pVDev->SetLineColor( COL_BLACK ); + pVDev->SetFillColor( COL_BLACK ); + pVDev->DrawRect( Rectangle( aPoint, aDstSize ) ); + pVDev->SetDrawMode( DRAWMODE_WHITELINE | DRAWMODE_WHITEFILL | DRAWMODE_WHITETEXT | + DRAWMODE_WHITEBITMAP | DRAWMODE_WHITEGRADIENT ); + aTmpMtf.WindStart(); + aTmpMtf.Play( pVDev, aPoint, aDstSize ); + aTmpMtf.WindStart(); + pVDev->EnableMapMode( sal_False ); + aMask = pVDev->GetBitmap( aPoint, aDstSizePixel ); + pVDev->EnableMapMode( sal_True ); + + // create alpha mask from gradient + pVDev->SetDrawMode( DRAWMODE_GRAYGRADIENT ); + pVDev->DrawGradient( Rectangle( aPoint, aDstSize ), rTransparenceGradient ); + pVDev->SetDrawMode( DRAWMODE_DEFAULT ); + pVDev->EnableMapMode( sal_False ); + pVDev->DrawMask( aPoint, aDstSizePixel, aMask, Color( COL_WHITE ) ); + aAlpha = pVDev->GetBitmap( aPoint, aDstSizePixel ); + implWriteBitmapEx( rPos, rSize, BitmapEx( aPaint, aAlpha ), pDummyVDev, i_rContext ); + } + delete pVDev; + } + } + } + break; + + case( META_EPS_ACTION ): + { + const MetaEPSAction* pA = (const MetaEPSAction*) pAction; + const GDIMetaFile aSubstitute( pA->GetSubstitute() ); + + m_rOuterFace.Push(); + pDummyVDev->Push(); + + MapMode aMapMode( aSubstitute.GetPrefMapMode() ); + Size aOutSize( pDummyVDev->LogicToLogic( pA->GetSize(), pDummyVDev->GetMapMode(), aMapMode ) ); + aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) ); + aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) ); + aMapMode.SetOrigin( pDummyVDev->LogicToLogic( pA->GetPoint(), pDummyVDev->GetMapMode(), aMapMode ) ); + + m_rOuterFace.SetMapMode( aMapMode ); + pDummyVDev->SetMapMode( aMapMode ); + playMetafile( aSubstitute, NULL, i_rContext, pDummyVDev ); + pDummyVDev->Pop(); + m_rOuterFace.Pop(); + } + break; + + case( META_COMMENT_ACTION ): + if( ! i_rContext.m_bTransparenciesWereRemoved ) + { + const MetaCommentAction* pA = (const MetaCommentAction*) pAction; + String aSkipComment; + + if( pA->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL ) + { + const MetaGradientExAction* pGradAction = NULL; + sal_Bool bDone = sal_False; + + while( !bDone && ( ++i < nCount ) ) + { + pAction = aMtf.GetAction( i ); + + if( pAction->GetType() == META_GRADIENTEX_ACTION ) + pGradAction = (const MetaGradientExAction*) pAction; + else if( ( pAction->GetType() == META_COMMENT_ACTION ) && + ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) ) + { + bDone = sal_True; + } + } + + if( pGradAction ) + { + #if USE_PDFGRADIENTS + m_rOuterFace.DrawGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient() ); + #else + implWriteGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), pDummyVDev, i_rContext ); + #endif + } + } + else + { + const sal_uInt8* pData = pA->GetData(); + if ( pData ) + { + SvMemoryStream aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ ); + sal_Bool bSkipSequence = sal_False; + ByteString sSeqEnd; + + if( pA->GetComment().Equals( "XPATHSTROKE_SEQ_BEGIN" ) ) + { + sSeqEnd = ByteString( "XPATHSTROKE_SEQ_END" ); + SvtGraphicStroke aStroke; + aMemStm >> aStroke; + + Polygon aPath; + aStroke.getPath( aPath ); + + PolyPolygon aStartArrow; + PolyPolygon aEndArrow; + double fTransparency( aStroke.getTransparency() ); + double fStrokeWidth( aStroke.getStrokeWidth() ); + SvtGraphicStroke::DashArray aDashArray; + + aStroke.getStartArrow( aStartArrow ); + aStroke.getEndArrow( aEndArrow ); + aStroke.getDashArray( aDashArray ); + + bSkipSequence = sal_True; + if ( aStartArrow.Count() || aEndArrow.Count() ) + bSkipSequence = sal_False; + if ( aDashArray.size() && ( fStrokeWidth != 0.0 ) && ( fTransparency == 0.0 ) ) + bSkipSequence = sal_False; + if ( bSkipSequence ) + { + PDFWriter::ExtLineInfo aInfo; + aInfo.m_fLineWidth = fStrokeWidth; + aInfo.m_fTransparency = fTransparency; + aInfo.m_fMiterLimit = aStroke.getMiterLimit(); + switch( aStroke.getCapType() ) + { + default: + case SvtGraphicStroke::capButt: aInfo.m_eCap = PDFWriter::capButt;break; + case SvtGraphicStroke::capRound: aInfo.m_eCap = PDFWriter::capRound;break; + case SvtGraphicStroke::capSquare: aInfo.m_eCap = PDFWriter::capSquare;break; + } + switch( aStroke.getJoinType() ) + { + default: + case SvtGraphicStroke::joinMiter: aInfo.m_eJoin = PDFWriter::joinMiter;break; + case SvtGraphicStroke::joinRound: aInfo.m_eJoin = PDFWriter::joinRound;break; + case SvtGraphicStroke::joinBevel: aInfo.m_eJoin = PDFWriter::joinBevel;break; + case SvtGraphicStroke::joinNone: + aInfo.m_eJoin = PDFWriter::joinMiter; + aInfo.m_fMiterLimit = 0.0; + break; + } + aInfo.m_aDashArray = aDashArray; + + if(SvtGraphicStroke::joinNone == aStroke.getJoinType() + && fStrokeWidth > 0.0) + { + // emulate no edge rounding by handling single edges + const sal_uInt16 nPoints(aPath.GetSize()); + const bool bCurve(aPath.HasFlags()); + + for(sal_uInt16 a(0); a + 1 < nPoints; a++) + { + if(bCurve + && POLY_NORMAL != aPath.GetFlags(a + 1) + && a + 2 < nPoints + && POLY_NORMAL != aPath.GetFlags(a + 2) + && a + 3 < nPoints) + { + const Polygon aSnippet(4, + aPath.GetConstPointAry() + a, + aPath.GetConstFlagAry() + a); + m_rOuterFace.DrawPolyLine( aSnippet, aInfo ); + a += 2; + } + else + { + const Polygon aSnippet(2, + aPath.GetConstPointAry() + a); + m_rOuterFace.DrawPolyLine( aSnippet, aInfo ); + } + } + } + else + { + m_rOuterFace.DrawPolyLine( aPath, aInfo ); + } + } + } + else if ( pA->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) ) + { + sSeqEnd = ByteString( "XPATHFILL_SEQ_END" ); + SvtGraphicFill aFill; + aMemStm >> aFill; + + if ( ( aFill.getFillType() == SvtGraphicFill::fillSolid ) && ( aFill.getFillRule() == SvtGraphicFill::fillEvenOdd ) ) + { + double fTransparency = aFill.getTransparency(); + if ( fTransparency == 0.0 ) + { + PolyPolygon aPath; + aFill.getPath( aPath ); + + bSkipSequence = sal_True; + m_rOuterFace.DrawPolyPolygon( aPath ); + } + else if ( fTransparency == 1.0 ) + bSkipSequence = sal_True; + } +/* #i81548# removing optimization for fill textures, because most of the texture settings are not + exported properly. In OpenOffice 3.1 the drawing layer will support graphic primitives, then it + will not be a problem to optimize the filltexture export. But for wysiwyg is more important than + filesize. + else if( aFill.getFillType() == SvtGraphicFill::fillTexture && aFill.isTiling() ) + { + sal_Int32 nPattern = mnCachePatternId; + Graphic aPatternGraphic; + aFill.getGraphic( aPatternGraphic ); + bool bUseCache = false; + SvtGraphicFill::Transform aPatTransform; + aFill.getTransform( aPatTransform ); + + if( mnCachePatternId >= 0 ) + { + SvtGraphicFill::Transform aCacheTransform; + maCacheFill.getTransform( aCacheTransform ); + if( aCacheTransform.matrix[0] == aPatTransform.matrix[0] && + aCacheTransform.matrix[1] == aPatTransform.matrix[1] && + aCacheTransform.matrix[2] == aPatTransform.matrix[2] && + aCacheTransform.matrix[3] == aPatTransform.matrix[3] && + aCacheTransform.matrix[4] == aPatTransform.matrix[4] && + aCacheTransform.matrix[5] == aPatTransform.matrix[5] + ) + { + Graphic aCacheGraphic; + maCacheFill.getGraphic( aCacheGraphic ); + if( aCacheGraphic == aPatternGraphic ) + bUseCache = true; + } + } + + if( ! bUseCache ) + { + + // paint graphic to metafile + GDIMetaFile aPattern; + pDummyVDev->SetConnectMetaFile( &aPattern ); + pDummyVDev->Push(); + pDummyVDev->SetMapMode( aPatternGraphic.GetPrefMapMode() ); + + aPatternGraphic.Draw( &rDummyVDev, Point( 0, 0 ) ); + pDummyVDev->Pop(); + pDummyVDev->SetConnectMetaFile( NULL ); + aPattern.WindStart(); + + MapMode aPatternMapMode( aPatternGraphic.GetPrefMapMode() ); + // prepare pattern from metafile + Size aPrefSize( aPatternGraphic.GetPrefSize() ); + // FIXME: this magic -1 shouldn't be necessary + aPrefSize.Width() -= 1; + aPrefSize.Height() -= 1; + aPrefSize = m_rOuterFace.GetReferenceDevice()-> + LogicToLogic( aPrefSize, + &aPatternMapMode, + &m_rOuterFace.GetReferenceDevice()->GetMapMode() ); + // build bounding rectangle of pattern + Rectangle aBound( Point( 0, 0 ), aPrefSize ); + m_rOuterFace.BeginPattern( aBound ); + m_rOuterFace.Push(); + pDummyVDev->Push(); + m_rOuterFace.SetMapMode( aPatternMapMode ); + pDummyVDev->SetMapMode( aPatternMapMode ); + ImplWriteActions( m_rOuterFace, NULL, aPattern, rDummyVDev ); + pDummyVDev->Pop(); + m_rOuterFace.Pop(); + + nPattern = m_rOuterFace.EndPattern( aPatTransform ); + + // try some caching and reuse pattern + mnCachePatternId = nPattern; + maCacheFill = aFill; + } + + // draw polypolygon with pattern fill + PolyPolygon aPath; + aFill.getPath( aPath ); + m_rOuterFace.DrawPolyPolygon( aPath, nPattern, aFill.getFillRule() == SvtGraphicFill::fillEvenOdd ); + + bSkipSequence = sal_True; + } +*/ + } + if ( bSkipSequence ) + { + while( ++i < nCount ) + { + pAction = aMtf.GetAction( i ); + if ( pAction->GetType() == META_COMMENT_ACTION ) + { + ByteString sComment( ((MetaCommentAction*)pAction)->GetComment() ); + if ( sComment.Equals( sSeqEnd ) ) + break; + } + // #i44496# + // the replacement action for stroke is a filled rectangle + // the set fillcolor of the replacement is part of the graphics + // state and must not be skipped + else if( pAction->GetType() == META_FILLCOLOR_ACTION ) + { + const MetaFillColorAction* pMA = (const MetaFillColorAction*) pAction; + if( pMA->IsSetting() ) + m_rOuterFace.SetFillColor( pMA->GetColor() ); + else + m_rOuterFace.SetFillColor(); + } + } + } + } + } + } + break; + + case( META_BMP_ACTION ): + { + const MetaBmpAction* pA = (const MetaBmpAction*) pAction; + BitmapEx aBitmapEx( pA->GetBitmap() ); + Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(), + aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) ); + if( ! ( aSize.Width() && aSize.Height() ) ) + aSize = pDummyVDev->PixelToLogic( aBitmapEx.GetSizePixel() ); + implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, pDummyVDev, i_rContext ); + } + break; + + case( META_BMPSCALE_ACTION ): + { + const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*) pAction; + implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), BitmapEx( pA->GetBitmap() ), pDummyVDev, i_rContext ); + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*) pAction; + BitmapEx aBitmapEx( pA->GetBitmap() ); + aBitmapEx.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) ); + implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, pDummyVDev, i_rContext ); + } + break; + + case( META_BMPEX_ACTION ): + { + const MetaBmpExAction* pA = (const MetaBmpExAction*) pAction; + BitmapEx aBitmapEx( pA->GetBitmapEx() ); + Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(), + aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) ); + implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, pDummyVDev, i_rContext ); + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pAction; + implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), pA->GetBitmapEx(), pDummyVDev, i_rContext ); + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*) pAction; + BitmapEx aBitmapEx( pA->GetBitmapEx() ); + aBitmapEx.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) ); + implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, pDummyVDev, i_rContext ); + } + break; + + case( META_MASK_ACTION ): + case( META_MASKSCALE_ACTION ): + case( META_MASKSCALEPART_ACTION ): + { + OSL_TRACE( "MetaMask...Action not supported yet" ); + } + break; + + case( META_TEXT_ACTION ): + { + const MetaTextAction* pA = (const MetaTextAction*) pAction; + m_rOuterFace.DrawText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ) ); + } + break; + + case( META_TEXTRECT_ACTION ): + { + const MetaTextRectAction* pA = (const MetaTextRectAction*) pAction; + m_rOuterFace.DrawText( pA->GetRect(), String( pA->GetText() ), pA->GetStyle() ); + } + break; + + case( META_TEXTARRAY_ACTION ): + { + const MetaTextArrayAction* pA = (const MetaTextArrayAction*) pAction; + m_rOuterFace.DrawTextArray( pA->GetPoint(), pA->GetText(), pA->GetDXArray(), pA->GetIndex(), pA->GetLen() ); + } + break; + + case( META_STRETCHTEXT_ACTION ): + { + const MetaStretchTextAction* pA = (const MetaStretchTextAction*) pAction; + m_rOuterFace.DrawStretchText( pA->GetPoint(), pA->GetWidth(), pA->GetText(), pA->GetIndex(), pA->GetLen() ); + } + break; + + + case( META_TEXTLINE_ACTION ): + { + const MetaTextLineAction* pA = (const MetaTextLineAction*) pAction; + m_rOuterFace.DrawTextLine( pA->GetStartPoint(), pA->GetWidth(), pA->GetStrikeout(), pA->GetUnderline(), pA->GetOverline() ); + + } + break; + + case( META_CLIPREGION_ACTION ): + { + const MetaClipRegionAction* pA = (const MetaClipRegionAction*) pAction; + + if( pA->IsClipping() ) + { + if( pA->GetRegion().IsEmpty() ) + m_rOuterFace.SetClipRegion( basegfx::B2DPolyPolygon() ); + else + { + Region aReg( pA->GetRegion() ); + m_rOuterFace.SetClipRegion( aReg.ConvertToB2DPolyPolygon() ); + } + } + else + m_rOuterFace.SetClipRegion(); + } + break; + + case( META_ISECTRECTCLIPREGION_ACTION ): + { + const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*) pAction; + m_rOuterFace.IntersectClipRegion( pA->GetRect() ); + } + break; + + case( META_ISECTREGIONCLIPREGION_ACTION ): + { + const MetaISectRegionClipRegionAction* pA = (const MetaISectRegionClipRegionAction*) pAction; + Region aReg( pA->GetRegion() ); + m_rOuterFace.IntersectClipRegion( aReg.ConvertToB2DPolyPolygon() ); + } + break; + + case( META_MOVECLIPREGION_ACTION ): + { + const MetaMoveClipRegionAction* pA = (const MetaMoveClipRegionAction*) pAction; + m_rOuterFace.MoveClipRegion( pA->GetHorzMove(), pA->GetVertMove() ); + } + break; + + case( META_MAPMODE_ACTION ): + { + const_cast< MetaAction* >( pAction )->Execute( pDummyVDev ); + m_rOuterFace.SetMapMode( pDummyVDev->GetMapMode() ); + } + break; + + case( META_LINECOLOR_ACTION ): + { + const MetaLineColorAction* pA = (const MetaLineColorAction*) pAction; + + if( pA->IsSetting() ) + m_rOuterFace.SetLineColor( pA->GetColor() ); + else + m_rOuterFace.SetLineColor(); + } + break; + + case( META_FILLCOLOR_ACTION ): + { + const MetaFillColorAction* pA = (const MetaFillColorAction*) pAction; + + if( pA->IsSetting() ) + m_rOuterFace.SetFillColor( pA->GetColor() ); + else + m_rOuterFace.SetFillColor(); + } + break; + + case( META_TEXTLINECOLOR_ACTION ): + { + const MetaTextLineColorAction* pA = (const MetaTextLineColorAction*) pAction; + + if( pA->IsSetting() ) + m_rOuterFace.SetTextLineColor( pA->GetColor() ); + else + m_rOuterFace.SetTextLineColor(); + } + break; + + case( META_OVERLINECOLOR_ACTION ): + { + const MetaOverlineColorAction* pA = (const MetaOverlineColorAction*) pAction; + + if( pA->IsSetting() ) + m_rOuterFace.SetOverlineColor( pA->GetColor() ); + else + m_rOuterFace.SetOverlineColor(); + } + break; + + case( META_TEXTFILLCOLOR_ACTION ): + { + const MetaTextFillColorAction* pA = (const MetaTextFillColorAction*) pAction; + + if( pA->IsSetting() ) + m_rOuterFace.SetTextFillColor( pA->GetColor() ); + else + m_rOuterFace.SetTextFillColor(); + } + break; + + case( META_TEXTCOLOR_ACTION ): + { + const MetaTextColorAction* pA = (const MetaTextColorAction*) pAction; + m_rOuterFace.SetTextColor( pA->GetColor() ); + } + break; + + case( META_TEXTALIGN_ACTION ): + { + const MetaTextAlignAction* pA = (const MetaTextAlignAction*) pAction; + m_rOuterFace.SetTextAlign( pA->GetTextAlign() ); + } + break; + + case( META_FONT_ACTION ): + { + const MetaFontAction* pA = (const MetaFontAction*) pAction; + m_rOuterFace.SetFont( pA->GetFont() ); + } + break; + + case( META_PUSH_ACTION ): + { + const MetaPushAction* pA = (const MetaPushAction*) pAction; + + pDummyVDev->Push( pA->GetFlags() ); + m_rOuterFace.Push( pA->GetFlags() ); + } + break; + + case( META_POP_ACTION ): + { + pDummyVDev->Pop(); + m_rOuterFace.Pop(); + } + break; + + case( META_LAYOUTMODE_ACTION ): + { + const MetaLayoutModeAction* pA = (const MetaLayoutModeAction*) pAction; + m_rOuterFace.SetLayoutMode( pA->GetLayoutMode() ); + } + break; + + case META_TEXTLANGUAGE_ACTION: + { + const MetaTextLanguageAction* pA = (const MetaTextLanguageAction*) pAction; + m_rOuterFace.SetDigitLanguage( pA->GetTextLanguage() ); + } + break; + + case( META_WALLPAPER_ACTION ): + { + const MetaWallpaperAction* pA = (const MetaWallpaperAction*) pAction; + m_rOuterFace.DrawWallpaper( pA->GetRect(), pA->GetWallpaper() ); + } + break; + + case( META_RASTEROP_ACTION ): + { + // !!! >>> we don't want to support this actions + } + break; + + case( META_REFPOINT_ACTION ): + { + // !!! >>> we don't want to support this actions + } + break; + + default: + // #i24604# Made assertion fire only once per + // metafile. The asserted actions here are all + // deprecated + if( !bAssertionFired ) + { + bAssertionFired = true; + OSL_TRACE( "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered" ); + } + break; + } + i++; + } + } + + delete pPrivateDevice; +} + +// Encryption methods + +/* a crutch to transport an rtlDigest safely though UNO API + this is needed for the PDF export dialog, which otherwise would have to pass + clear text passwords down till they can be used in PDFWriter. Unfortunately + the MD5 sum of the password (which is needed to create the PDF encryption key) + is not sufficient, since an rtl MD5 digest cannot be created in an arbitrary state + which would be needed in PDFWriterImpl::computeEncryptionKey. +*/ +class EncHashTransporter : public cppu::WeakImplHelper1 < com::sun::star::beans::XMaterialHolder > +{ + rtlDigest maUDigest; + sal_IntPtr maID; + std::vector< sal_uInt8 > maOValue; + + static std::map< sal_IntPtr, EncHashTransporter* > sTransporters; +public: + EncHashTransporter() + : maUDigest( rtl_digest_createMD5() ) + { + maID = reinterpret_cast< sal_IntPtr >(this); + while( sTransporters.find( maID ) != sTransporters.end() ) // paranoia mode + maID++; + sTransporters[ maID ] = this; + } + + virtual ~EncHashTransporter() + { + sTransporters.erase( maID ); + if( maUDigest ) + rtl_digest_destroyMD5( maUDigest ); + OSL_TRACE( "EncHashTransporter freed\n" ); + } + + rtlDigest getUDigest() const { return maUDigest; }; + std::vector< sal_uInt8 >& getOValue() { return maOValue; } + void invalidate() + { + if( maUDigest ) + { + rtl_digest_destroyMD5( maUDigest ); + maUDigest = NULL; + } + } + + // XMaterialHolder + virtual uno::Any SAL_CALL getMaterial() throw() + { + return uno::makeAny( sal_Int64(maID) ); + } + + static EncHashTransporter* getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& ); + +}; + +std::map< sal_IntPtr, EncHashTransporter* > EncHashTransporter::sTransporters; + +EncHashTransporter* EncHashTransporter::getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& xRef ) +{ + EncHashTransporter* pResult = NULL; + if( xRef.is() ) + { + uno::Any aMat( xRef->getMaterial() ); + sal_Int64 nMat = 0; + if( aMat >>= nMat ) + { + std::map< sal_IntPtr, EncHashTransporter* >::iterator it = sTransporters.find( static_cast<sal_IntPtr>(nMat) ); + if( it != sTransporters.end() ) + pResult = it->second; + } + } + return pResult; +} + +sal_Bool PDFWriterImpl::checkEncryptionBufferSize( register sal_Int32 newSize ) +{ + if( m_nEncryptionBufferSize < newSize ) + { + /* reallocate the buffer, the used function allocate as rtl_allocateMemory + if the pointer parameter is NULL */ + m_pEncryptionBuffer = (sal_uInt8*)rtl_reallocateMemory( m_pEncryptionBuffer, newSize ); + if( m_pEncryptionBuffer ) + m_nEncryptionBufferSize = newSize; + else + m_nEncryptionBufferSize = 0; + } + return ( m_nEncryptionBufferSize != 0 ); +} + +void PDFWriterImpl::checkAndEnableStreamEncryption( register sal_Int32 nObject ) +{ + if( m_aContext.Encryption.Encrypt() ) + { + m_bEncryptThisStream = true; + sal_Int32 i = m_nKeyLength; + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject; + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 ); + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 ); + //the other location of m_nEncryptionKey are already set to 0, our fixed generation number + // do the MD5 hash + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + // the i+2 to take into account the generation number, always zero + rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) ); + // initialize the RC4 with the key + // key legth: see algoritm 3.1, step 4: (N+5) max 16 + rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 ); + } +} + +void PDFWriterImpl::enableStringEncryption( register sal_Int32 nObject ) +{ + if( m_aContext.Encryption.Encrypt() ) + { + sal_Int32 i = m_nKeyLength; + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject; + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 ); + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 ); + //the other location of m_nEncryptionKey are already set to 0, our fixed generation number + // do the MD5 hash + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + // the i+2 to take into account the generation number, always zero + rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) ); + // initialize the RC4 with the key + // key legth: see algoritm 3.1, step 4: (N+5) max 16 + rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 ); + } +} + +/* init the encryption engine +1. init the document id, used both for building the document id and for building the encryption key(s) +2. build the encryption key following algorithms described in the PDF specification + */ +uno::Reference< beans::XMaterialHolder > PDFWriterImpl::initEncryption( const rtl::OUString& i_rOwnerPassword, + const rtl::OUString& i_rUserPassword, + bool b128Bit + ) +{ + uno::Reference< beans::XMaterialHolder > xResult; + if( i_rOwnerPassword.getLength() || i_rUserPassword.getLength() ) + { + EncHashTransporter* pTransporter = new EncHashTransporter; + xResult = pTransporter; + + // get padded passwords + sal_uInt8 aPadUPW[ENCRYPTED_PWD_SIZE], aPadOPW[ENCRYPTED_PWD_SIZE]; + padPassword( i_rOwnerPassword.getLength() ? i_rOwnerPassword : i_rUserPassword, aPadOPW ); + padPassword( i_rUserPassword, aPadUPW ); + sal_Int32 nKeyLength = SECUR_40BIT_KEY; + if( b128Bit ) + nKeyLength = SECUR_128BIT_KEY; + + if( computeODictionaryValue( aPadOPW, aPadUPW, pTransporter->getOValue(), nKeyLength ) ) + { + rtlDigest aDig = pTransporter->getUDigest(); + if( rtl_digest_updateMD5( aDig, aPadUPW, ENCRYPTED_PWD_SIZE ) != rtl_Digest_E_None ) + xResult.clear(); + } + else + xResult.clear(); + + // trash temporary padded cleartext PWDs + rtl_zeroMemory( aPadOPW, sizeof(aPadOPW) ); + rtl_zeroMemory( aPadUPW, sizeof(aPadUPW) ); + + } + return xResult; +} + +bool PDFWriterImpl::prepareEncryption( const uno::Reference< beans::XMaterialHolder >& xEnc ) +{ + bool bSuccess = false; + EncHashTransporter* pTransporter = EncHashTransporter::getEncHashTransporter( xEnc ); + if( pTransporter ) + { + sal_Int32 nKeyLength = 0, nRC4KeyLength = 0; + sal_Int32 nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, nKeyLength, nRC4KeyLength ); + m_aContext.Encryption.OValue = pTransporter->getOValue(); + bSuccess = computeUDictionaryValue( pTransporter, m_aContext.Encryption, nKeyLength, nAccessPermissions ); + } + if( ! bSuccess ) + { + m_aContext.Encryption.OValue.clear(); + m_aContext.Encryption.UValue.clear(); + m_aContext.Encryption.EncryptionKey.clear(); + } + return bSuccess; +} + +sal_Int32 PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties, + sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength ) +{ + /* + 2) compute the access permissions, in numerical form + + the default value depends on the revision 2 (40 bit) or 3 (128 bit security): + - for 40 bit security the unused bit must be set to 1, since they are not used + - for 128 bit security the same bit must be preset to 0 and set later if needed + according to the table 3.15, pdf v 1.4 */ + sal_Int32 nAccessPermissions = ( i_rProperties.Security128bit ) ? 0xfffff0c0 : 0xffffffc0 ; + + /* check permissions for 40 bit security case */ + nAccessPermissions |= ( i_rProperties.CanPrintTheDocument ) ? 1 << 2 : 0; + nAccessPermissions |= ( i_rProperties.CanModifyTheContent ) ? 1 << 3 : 0; + nAccessPermissions |= ( i_rProperties.CanCopyOrExtract ) ? 1 << 4 : 0; + nAccessPermissions |= ( i_rProperties.CanAddOrModify ) ? 1 << 5 : 0; + o_rKeyLength = SECUR_40BIT_KEY; + o_rRC4KeyLength = SECUR_40BIT_KEY+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5 + + if( i_rProperties.Security128bit ) + { + o_rKeyLength = SECUR_128BIT_KEY; + o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum + // permitted value is 16 + nAccessPermissions |= ( i_rProperties.CanFillInteractive ) ? 1 << 8 : 0; + nAccessPermissions |= ( i_rProperties.CanExtractForAccessibility ) ? 1 << 9 : 0; + nAccessPermissions |= ( i_rProperties.CanAssemble ) ? 1 << 10 : 0; + nAccessPermissions |= ( i_rProperties.CanPrintFull ) ? 1 << 11 : 0; + } + return nAccessPermissions; +} + +/************************************************************* +begin i12626 methods + +Implements Algorithm 3.2, step 1 only +*/ +void PDFWriterImpl::padPassword( const rtl::OUString& i_rPassword, sal_uInt8* o_pPaddedPW ) +{ + // get ansi-1252 version of the password string CHECKIT ! i12626 + rtl::OString aString( rtl::OUStringToOString( i_rPassword, RTL_TEXTENCODING_MS_1252 ) ); + + //copy the string to the target + sal_Int32 nToCopy = ( aString.getLength() < ENCRYPTED_PWD_SIZE ) ? aString.getLength() : ENCRYPTED_PWD_SIZE; + sal_Int32 nCurrentChar; + + for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ ) + o_pPaddedPW[nCurrentChar] = (sal_uInt8)( aString.getStr()[nCurrentChar] ); + + //pad it with standard byte string + sal_Int32 i,y; + for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ ) + o_pPaddedPW[i] = s_nPadString[y]; + + // trash memory of temporary clear text password + rtl_zeroMemory( (sal_Char*)aString.getStr(), aString.getLength() ); +} + +/********************************** +Algorithm 3.2 Compute the encryption key used + +step 1 should already be done before calling, the paThePaddedPassword parameter should contain +the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter, +it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used + +TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec. + +*/ +bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter* i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, sal_Int32 i_nAccessPermissions ) +{ + bool bSuccess = true; + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + + // transporter contains an MD5 digest with the padded user password already + rtlDigest aDigest = i_pTransporter->getUDigest(); + rtlDigestError nError = rtl_Digest_E_None; + if( aDigest ) + { + //step 3 + if( ! io_rProperties.OValue.empty() ) + nError = rtl_digest_updateMD5( aDigest, &io_rProperties.OValue[0] , sal_Int32(io_rProperties.OValue.size()) ); + else + bSuccess = false; + //Step 4 + sal_uInt8 nPerm[4]; + + nPerm[0] = (sal_uInt8)i_nAccessPermissions; + nPerm[1] = (sal_uInt8)( i_nAccessPermissions >> 8 ); + nPerm[2] = (sal_uInt8)( i_nAccessPermissions >> 16 ); + nPerm[3] = (sal_uInt8)( i_nAccessPermissions >> 24 ); + + if( nError == rtl_Digest_E_None ) + nError = rtl_digest_updateMD5( aDigest, nPerm , sizeof( nPerm ) ); + + //step 5, get the document ID, binary form + if( nError == rtl_Digest_E_None ) + nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) ); + //get the digest + if( nError == rtl_Digest_E_None ) + { + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); + + //step 6, only if 128 bit + if( io_rProperties.Security128bit ) + { + for( sal_Int32 i = 0; i < 50; i++ ) + { + nError = rtl_digest_updateMD5( aDigest, &nMD5Sum, sizeof( nMD5Sum ) ); + if( nError != rtl_Digest_E_None ) + { + bSuccess = false; + break; + } + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); + } + } + } + } + else + bSuccess = false; + + i_pTransporter->invalidate(); + + //Step 7 + if( bSuccess ) + { + io_rProperties.EncryptionKey.resize( MAXIMUM_RC4_KEY_LENGTH ); + for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ ) + io_rProperties.EncryptionKey[i] = nMD5Sum[i]; + } + else + io_rProperties.EncryptionKey.clear(); + + return bSuccess; +} + +/********************************** +Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member +the step numbers down here correspond to the ones in PDF v.1.4 specfication +*/ +bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword, + const sal_uInt8* i_pPaddedUserPassword, + std::vector< sal_uInt8 >& io_rOValue, + sal_Int32 i_nKeyLength + ) +{ + bool bSuccess = true; + + io_rOValue.resize( ENCRYPTED_PWD_SIZE ); + + rtlDigest aDigest = rtl_digest_createMD5(); + rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); + if( aDigest && aCipher) + { + //step 1 already done, data is in i_pPaddedOwnerPassword + //step 2 + + rtlDigestError nError = rtl_digest_updateMD5( aDigest, i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE ); + if( nError == rtl_Digest_E_None ) + { + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) ); +//step 3, only if 128 bit + if( i_nKeyLength == SECUR_128BIT_KEY ) + { + sal_Int32 i; + for( i = 0; i < 50; i++ ) + { + nError = rtl_digest_updateMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); + if( nError != rtl_Digest_E_None ) + { + bSuccess = false; + break; + } + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); + } + } + //Step 4, the key is in nMD5Sum + //step 5 already done, data is in i_pPaddedUserPassword + //step 6 + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + nMD5Sum, i_nKeyLength , NULL, 0 ); + // encrypt the user password using the key set above + rtl_cipher_encodeARCFOUR( aCipher, i_pPaddedUserPassword, ENCRYPTED_PWD_SIZE, // the data to be encrypted + &io_rOValue[0], sal_Int32(io_rOValue.size()) ); //encrypted data + //Step 7, only if 128 bit + if( i_nKeyLength == SECUR_128BIT_KEY ) + { + sal_uInt32 i, y; + sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key + + for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1 + { + for( y = 0; y < sizeof( nLocalKey ); y++ ) + nLocalKey[y] = (sal_uInt8)( nMD5Sum[y] ^ i ); + + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + nLocalKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area, on init can be NULL + rtl_cipher_encodeARCFOUR( aCipher, &io_rOValue[0], sal_Int32(io_rOValue.size()), // the data to be encrypted + &io_rOValue[0], sal_Int32(io_rOValue.size()) ); // encrypted data, can be the same as the input, encrypt "in place" + //step 8, store in class data member + } + } + } + else + bSuccess = false; + } + else + bSuccess = false; + + if( aDigest ) + rtl_digest_destroyMD5( aDigest ); + if( aCipher ) + rtl_cipher_destroyARCFOUR( aCipher ); + + if( ! bSuccess ) + io_rOValue.clear(); + return bSuccess; +} + +/********************************** +Algorithms 3.4 and 3.5 Compute the encryption dictionary /U value, save into the class data member, revision 2 (40 bit) or 3 (128 bit) +*/ +bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter* i_pTransporter, + vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, + sal_Int32 i_nKeyLength, + sal_Int32 i_nAccessPermissions + ) +{ + bool bSuccess = true; + + io_rProperties.UValue.resize( ENCRYPTED_PWD_SIZE ); + + rtlDigest aDigest = rtl_digest_createMD5(); + rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); + if( aDigest && aCipher ) + { + //step 1, common to both 3.4 and 3.5 + if( computeEncryptionKey( i_pTransporter, io_rProperties, i_nAccessPermissions ) ) + { + // prepare encryption key for object + for( sal_Int32 i = i_nKeyLength, y = 0; y < 5 ; y++ ) + io_rProperties.EncryptionKey[i++] = 0; + + if( io_rProperties.Security128bit == false ) + { + //3.4 + //step 2 and 3 + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + &io_rProperties.EncryptionKey[0], 5 , // key and key length + NULL, 0 ); //destination data area + // encrypt the user password using the key set above, save for later use + rtl_cipher_encodeARCFOUR( aCipher, s_nPadString, sizeof( s_nPadString ), // the data to be encrypted + &io_rProperties.UValue[0], sal_Int32(io_rProperties.UValue.size()) ); //encrypted data, stored in class data member + } + else + { + //or 3.5, for 128 bit security + //step6, initilize the last 16 bytes of the encrypted user password to 0 + for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sal_uInt32(io_rProperties.UValue.size()); i++) + io_rProperties.UValue[i] = 0; + //step 2 + rtlDigestError nError = rtl_digest_updateMD5( aDigest, s_nPadString, sizeof( s_nPadString ) ); + //step 3 + if( nError == rtl_Digest_E_None ) + nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) ); + else + bSuccess = false; + + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) ); + //Step 4 + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + &io_rProperties.EncryptionKey[0], SECUR_128BIT_KEY, NULL, 0 ); //destination data area + rtl_cipher_encodeARCFOUR( aCipher, nMD5Sum, sizeof( nMD5Sum ), // the data to be encrypted + &io_rProperties.UValue[0], sizeof( nMD5Sum ) ); //encrypted data, stored in class data member + //step 5 + sal_uInt32 i, y; + sal_uInt8 nLocalKey[SECUR_128BIT_KEY]; + + for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1 + { + for( y = 0; y < sizeof( nLocalKey ) ; y++ ) + nLocalKey[y] = (sal_uInt8)( io_rProperties.EncryptionKey[y] ^ i ); + + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + nLocalKey, SECUR_128BIT_KEY, // key and key length + NULL, 0 ); //destination data area, on init can be NULL + rtl_cipher_encodeARCFOUR( aCipher, &io_rProperties.UValue[0], SECUR_128BIT_KEY, // the data to be encrypted + &io_rProperties.UValue[0], SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place" + } + } + } + else + bSuccess = false; + } + else + bSuccess = false; + + if( aDigest ) + rtl_digest_destroyMD5( aDigest ); + if( aCipher ) + rtl_cipher_destroyARCFOUR( aCipher ); + + if( ! bSuccess ) + io_rProperties.UValue.clear(); + return bSuccess; +} + +/* end i12626 methods */ + +static const long unsetRun[256] = +{ + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */ +}; + +static const long setRun[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */ + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */ +}; + +inline bool isSet( const Scanline i_pLine, long i_nIndex ) +{ + return (i_pLine[ i_nIndex/8 ] & (0x80 >> (i_nIndex&7))) != 0; +} + +long findBitRun( const Scanline i_pLine, long i_nStartIndex, long i_nW, bool i_bSet ) +{ + if( i_nStartIndex < 0 ) + return i_nW; + + long nIndex = i_nStartIndex; + if( nIndex < i_nW ) + { + const sal_uInt8 * pByte = static_cast<sal_uInt8*>(i_pLine) + (nIndex/8); + sal_uInt8 nByte = *pByte; + + // run up to byte boundary + long nBitInByte = (nIndex & 7); + if( nBitInByte ) + { + sal_uInt8 nMask = 0x80 >> nBitInByte; + while( nBitInByte != 8 ) + { + if( (nByte & nMask) != (i_bSet ? nMask : 0) ) + return nIndex < i_nW ? nIndex : i_nW; + nMask = nMask >> 1; + nBitInByte++; + nIndex++; + } + if( nIndex < i_nW ) + { + pByte++; + nByte = *pByte; + } + } + + sal_uInt8 nRunByte; + const long* pRunTable; + if( i_bSet ) + { + nRunByte = 0xff; + pRunTable = setRun; + } + else + { + nRunByte = 0; + pRunTable = unsetRun; + } + + while( nByte == nRunByte && nIndex < i_nW ) + { + nIndex += 8; + pByte++; + nByte = *pByte; + } + if( nIndex < i_nW ) + { + nIndex += pRunTable[nByte]; + } + } + return nIndex < i_nW ? nIndex : i_nW; +} + +struct BitStreamState +{ + sal_uInt8 mnBuffer; + sal_uInt32 mnNextBitPos; + + BitStreamState() + : mnBuffer( 0 ) + , mnNextBitPos( 8 ) + { + } + + const sal_uInt8* getByte() const { return &mnBuffer; } + void flush() { mnNextBitPos = 8; mnBuffer = 0; } +}; + +void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState ) +{ + while( i_nLength > io_rState.mnNextBitPos ) + { + io_rState.mnBuffer |= static_cast<sal_uInt8>( i_nCode >> (i_nLength - io_rState.mnNextBitPos) ); + i_nLength -= io_rState.mnNextBitPos; + writeBuffer( io_rState.getByte(), 1 ); + io_rState.flush(); + } + OSL_ASSERT( i_nLength < 9 ); + static const unsigned int msbmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + io_rState.mnBuffer |= static_cast<sal_uInt8>( (i_nCode & msbmask[i_nLength]) << (io_rState.mnNextBitPos - i_nLength) ); + io_rState.mnNextBitPos -= i_nLength; + if( io_rState.mnNextBitPos == 0 ) + { + writeBuffer( io_rState.getByte(), 1 ); + io_rState.flush(); + } +} + +struct PixelCode +{ + sal_uInt32 mnEncodedPixels; + sal_uInt32 mnCodeBits; + sal_uInt32 mnCode; +}; + +static const PixelCode WhitePixelCodes[] = +{ + { 0, 8, 0x35 }, // 0011 0101 + { 1, 6, 0x7 }, // 0001 11 + { 2, 4, 0x7 }, // 0111 + { 3, 4, 0x8 }, // 1000 + { 4, 4, 0xB }, // 1011 + { 5, 4, 0xC }, // 1100 + { 6, 4, 0xE }, // 1110 + { 7, 4, 0xF }, // 1111 + { 8, 5, 0x13 }, // 1001 1 + { 9, 5, 0x14 }, // 1010 0 + { 10, 5, 0x7 }, // 0011 1 + { 11, 5, 0x8 }, // 0100 0 + { 12, 6, 0x8 }, // 0010 00 + { 13, 6, 0x3 }, // 0000 11 + { 14, 6, 0x34 }, // 1101 00 + { 15, 6, 0x35 }, // 1101 01 + { 16, 6, 0x2A }, // 1010 10 + { 17, 6, 0x2B }, // 1010 11 + { 18, 7, 0x27 }, // 0100 111 + { 19, 7, 0xC }, // 0001 100 + { 20, 7, 0x8 }, // 0001 000 + { 21, 7, 0x17 }, // 0010 111 + { 22, 7, 0x3 }, // 0000 011 + { 23, 7, 0x4 }, // 0000 100 + { 24, 7, 0x28 }, // 0101 000 + { 25, 7, 0x2B }, // 0101 011 + { 26, 7, 0x13 }, // 0010 011 + { 27, 7, 0x24 }, // 0100 100 + { 28, 7, 0x18 }, // 0011 000 + { 29, 8, 0x2 }, // 0000 0010 + { 30, 8, 0x3 }, // 0000 0011 + { 31, 8, 0x1A }, // 0001 1010 + { 32, 8, 0x1B }, // 0001 1011 + { 33, 8, 0x12 }, // 0001 0010 + { 34, 8, 0x13 }, // 0001 0011 + { 35, 8, 0x14 }, // 0001 0100 + { 36, 8, 0x15 }, // 0001 0101 + { 37, 8, 0x16 }, // 0001 0110 + { 38, 8, 0x17 }, // 0001 0111 + { 39, 8, 0x28 }, // 0010 1000 + { 40, 8, 0x29 }, // 0010 1001 + { 41, 8, 0x2A }, // 0010 1010 + { 42, 8, 0x2B }, // 0010 1011 + { 43, 8, 0x2C }, // 0010 1100 + { 44, 8, 0x2D }, // 0010 1101 + { 45, 8, 0x4 }, // 0000 0100 + { 46, 8, 0x5 }, // 0000 0101 + { 47, 8, 0xA }, // 0000 1010 + { 48, 8, 0xB }, // 0000 1011 + { 49, 8, 0x52 }, // 0101 0010 + { 50, 8, 0x53 }, // 0101 0011 + { 51, 8, 0x54 }, // 0101 0100 + { 52, 8, 0x55 }, // 0101 0101 + { 53, 8, 0x24 }, // 0010 0100 + { 54, 8, 0x25 }, // 0010 0101 + { 55, 8, 0x58 }, // 0101 1000 + { 56, 8, 0x59 }, // 0101 1001 + { 57, 8, 0x5A }, // 0101 1010 + { 58, 8, 0x5B }, // 0101 1011 + { 59, 8, 0x4A }, // 0100 1010 + { 60, 8, 0x4B }, // 0100 1011 + { 61, 8, 0x32 }, // 0011 0010 + { 62, 8, 0x33 }, // 0011 0011 + { 63, 8, 0x34 }, // 0011 0100 + { 64, 5, 0x1B }, // 1101 1 + { 128, 5, 0x12 }, // 1001 0 + { 192, 6, 0x17 }, // 0101 11 + { 256, 7, 0x37 }, // 0110 111 + { 320, 8, 0x36 }, // 0011 0110 + { 384, 8, 0x37 }, // 0011 0111 + { 448, 8, 0x64 }, // 0110 0100 + { 512, 8, 0x65 }, // 0110 0101 + { 576, 8, 0x68 }, // 0110 1000 + { 640, 8, 0x67 }, // 0110 0111 + { 704, 9, 0xCC }, // 0110 0110 0 + { 768, 9, 0xCD }, // 0110 0110 1 + { 832, 9, 0xD2 }, // 0110 1001 0 + { 896, 9, 0xD3 }, // 0110 1001 1 + { 960, 9, 0xD4 }, // 0110 1010 0 + { 1024, 9, 0xD5 }, // 0110 1010 1 + { 1088, 9, 0xD6 }, // 0110 1011 0 + { 1152, 9, 0xD7 }, // 0110 1011 1 + { 1216, 9, 0xD8 }, // 0110 1100 0 + { 1280, 9, 0xD9 }, // 0110 1100 1 + { 1344, 9, 0xDA }, // 0110 1101 0 + { 1408, 9, 0xDB }, // 0110 1101 1 + { 1472, 9, 0x98 }, // 0100 1100 0 + { 1536, 9, 0x99 }, // 0100 1100 1 + { 1600, 9, 0x9A }, // 0100 1101 0 + { 1664, 6, 0x18 }, // 0110 00 + { 1728, 9, 0x9B }, // 0100 1101 1 + { 1792, 11, 0x8 }, // 0000 0001 000 + { 1856, 11, 0xC }, // 0000 0001 100 + { 1920, 11, 0xD }, // 0000 0001 101 + { 1984, 12, 0x12 }, // 0000 0001 0010 + { 2048, 12, 0x13 }, // 0000 0001 0011 + { 2112, 12, 0x14 }, // 0000 0001 0100 + { 2176, 12, 0x15 }, // 0000 0001 0101 + { 2240, 12, 0x16 }, // 0000 0001 0110 + { 2304, 12, 0x17 }, // 0000 0001 0111 + { 2368, 12, 0x1C }, // 0000 0001 1100 + { 2432, 12, 0x1D }, // 0000 0001 1101 + { 2496, 12, 0x1E }, // 0000 0001 1110 + { 2560, 12, 0x1F } // 0000 0001 1111 +}; + +static const PixelCode BlackPixelCodes[] = +{ + { 0, 10, 0x37 }, // 0000 1101 11 + { 1, 3, 0x2 }, // 010 + { 2, 2, 0x3 }, // 11 + { 3, 2, 0x2 }, // 10 + { 4, 3, 0x3 }, // 011 + { 5, 4, 0x3 }, // 0011 + { 6, 4, 0x2 }, // 0010 + { 7, 5, 0x3 }, // 0001 1 + { 8, 6, 0x5 }, // 0001 01 + { 9, 6, 0x4 }, // 0001 00 + { 10, 7, 0x4 }, // 0000 100 + { 11, 7, 0x5 }, // 0000 101 + { 12, 7, 0x7 }, // 0000 111 + { 13, 8, 0x4 }, // 0000 0100 + { 14, 8, 0x7 }, // 0000 0111 + { 15, 9, 0x18 }, // 0000 1100 0 + { 16, 10, 0x17 }, // 0000 0101 11 + { 17, 10, 0x18 }, // 0000 0110 00 + { 18, 10, 0x8 }, // 0000 0010 00 + { 19, 11, 0x67 }, // 0000 1100 111 + { 20, 11, 0x68 }, // 0000 1101 000 + { 21, 11, 0x6C }, // 0000 1101 100 + { 22, 11, 0x37 }, // 0000 0110 111 + { 23, 11, 0x28 }, // 0000 0101 000 + { 24, 11, 0x17 }, // 0000 0010 111 + { 25, 11, 0x18 }, // 0000 0011 000 + { 26, 12, 0xCA }, // 0000 1100 1010 + { 27, 12, 0xCB }, // 0000 1100 1011 + { 28, 12, 0xCC }, // 0000 1100 1100 + { 29, 12, 0xCD }, // 0000 1100 1101 + { 30, 12, 0x68 }, // 0000 0110 1000 + { 31, 12, 0x69 }, // 0000 0110 1001 + { 32, 12, 0x6A }, // 0000 0110 1010 + { 33, 12, 0x6B }, // 0000 0110 1011 + { 34, 12, 0xD2 }, // 0000 1101 0010 + { 35, 12, 0xD3 }, // 0000 1101 0011 + { 36, 12, 0xD4 }, // 0000 1101 0100 + { 37, 12, 0xD5 }, // 0000 1101 0101 + { 38, 12, 0xD6 }, // 0000 1101 0110 + { 39, 12, 0xD7 }, // 0000 1101 0111 + { 40, 12, 0x6C }, // 0000 0110 1100 + { 41, 12, 0x6D }, // 0000 0110 1101 + { 42, 12, 0xDA }, // 0000 1101 1010 + { 43, 12, 0xDB }, // 0000 1101 1011 + { 44, 12, 0x54 }, // 0000 0101 0100 + { 45, 12, 0x55 }, // 0000 0101 0101 + { 46, 12, 0x56 }, // 0000 0101 0110 + { 47, 12, 0x57 }, // 0000 0101 0111 + { 48, 12, 0x64 }, // 0000 0110 0100 + { 49, 12, 0x65 }, // 0000 0110 0101 + { 50, 12, 0x52 }, // 0000 0101 0010 + { 51, 12, 0x53 }, // 0000 0101 0011 + { 52, 12, 0x24 }, // 0000 0010 0100 + { 53, 12, 0x37 }, // 0000 0011 0111 + { 54, 12, 0x38 }, // 0000 0011 1000 + { 55, 12, 0x27 }, // 0000 0010 0111 + { 56, 12, 0x28 }, // 0000 0010 1000 + { 57, 12, 0x58 }, // 0000 0101 1000 + { 58, 12, 0x59 }, // 0000 0101 1001 + { 59, 12, 0x2B }, // 0000 0010 1011 + { 60, 12, 0x2C }, // 0000 0010 1100 + { 61, 12, 0x5A }, // 0000 0101 1010 + { 62, 12, 0x66 }, // 0000 0110 0110 + { 63, 12, 0x67 }, // 0000 0110 0111 + { 64, 10, 0xF }, // 0000 0011 11 + { 128, 12, 0xC8 }, // 0000 1100 1000 + { 192, 12, 0xC9 }, // 0000 1100 1001 + { 256, 12, 0x5B }, // 0000 0101 1011 + { 320, 12, 0x33 }, // 0000 0011 0011 + { 384, 12, 0x34 }, // 0000 0011 0100 + { 448, 12, 0x35 }, // 0000 0011 0101 + { 512, 13, 0x6C }, // 0000 0011 0110 0 + { 576, 13, 0x6D }, // 0000 0011 0110 1 + { 640, 13, 0x4A }, // 0000 0010 0101 0 + { 704, 13, 0x4B }, // 0000 0010 0101 1 + { 768, 13, 0x4C }, // 0000 0010 0110 0 + { 832, 13, 0x4D }, // 0000 0010 0110 1 + { 896, 13, 0x72 }, // 0000 0011 1001 0 + { 960, 13, 0x73 }, // 0000 0011 1001 1 + { 1024, 13, 0x74 }, // 0000 0011 1010 0 + { 1088, 13, 0x75 }, // 0000 0011 1010 1 + { 1152, 13, 0x76 }, // 0000 0011 1011 0 + { 1216, 13, 0x77 }, // 0000 0011 1011 1 + { 1280, 13, 0x52 }, // 0000 0010 1001 0 + { 1344, 13, 0x53 }, // 0000 0010 1001 1 + { 1408, 13, 0x54 }, // 0000 0010 1010 0 + { 1472, 13, 0x55 }, // 0000 0010 1010 1 + { 1536, 13, 0x5A }, // 0000 0010 1101 0 + { 1600, 13, 0x5B }, // 0000 0010 1101 1 + { 1664, 13, 0x64 }, // 0000 0011 0010 0 + { 1728, 13, 0x65 }, // 0000 0011 0010 1 + { 1792, 11, 0x8 }, // 0000 0001 000 + { 1856, 11, 0xC }, // 0000 0001 100 + { 1920, 11, 0xD }, // 0000 0001 101 + { 1984, 12, 0x12 }, // 0000 0001 0010 + { 2048, 12, 0x13 }, // 0000 0001 0011 + { 2112, 12, 0x14 }, // 0000 0001 0100 + { 2176, 12, 0x15 }, // 0000 0001 0101 + { 2240, 12, 0x16 }, // 0000 0001 0110 + { 2304, 12, 0x17 }, // 0000 0001 0111 + { 2368, 12, 0x1C }, // 0000 0001 1100 + { 2432, 12, 0x1D }, // 0000 0001 1101 + { 2496, 12, 0x1E }, // 0000 0001 1110 + { 2560, 12, 0x1F } // 0000 0001 1111 +}; + + +void PDFWriterImpl::putG4Span( long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState ) +{ + const PixelCode* pTable = i_bWhitePixel ? WhitePixelCodes : BlackPixelCodes; + // maximum encoded span is 2560 consecutive pixels + while( i_nSpan > 2623 ) + { + // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table + putG4Bits( pTable[103].mnCodeBits, pTable[103].mnCode, io_rState ); + i_nSpan -= pTable[103].mnEncodedPixels; + } + // write multiples of 64 pixels up to 2560 + if( i_nSpan > 63 ) + { + sal_uInt32 nTabIndex = 63 + (i_nSpan >> 6); + OSL_ASSERT( pTable[nTabIndex].mnEncodedPixels == static_cast<sal_uInt32>(64*(i_nSpan >> 6)) ); + putG4Bits( pTable[nTabIndex].mnCodeBits, pTable[nTabIndex].mnCode, io_rState ); + i_nSpan -= pTable[nTabIndex].mnEncodedPixels; + } + putG4Bits( pTable[i_nSpan].mnCodeBits, pTable[i_nSpan].mnCode, io_rState ); +} + +void PDFWriterImpl::writeG4Stream( BitmapReadAccess* i_pBitmap ) +{ + long nW = i_pBitmap->Width(); + long nH = i_pBitmap->Height(); + if( nW <= 0 || nH <= 0 ) + return; + if( i_pBitmap->GetBitCount() != 1 ) + return; + + BitStreamState aBitState; + + // the first reference line is virtual and completely empty + const Scanline pFirstRefLine = (Scanline)rtl_allocateZeroMemory( nW/8 + 1 ); + Scanline pRefLine = pFirstRefLine; + for( long nY = 0; nY < nH; nY++ ) + { + const Scanline pCurLine = i_pBitmap->GetScanline( nY ); + long nLineIndex = 0; + bool bRunSet = (*pCurLine & 0x80) ? true : false; + bool bRefSet = (*pRefLine & 0x80) ? true : false; + long nRunIndex1 = bRunSet ? 0 : findBitRun( pCurLine, 0, nW, bRunSet ); + long nRefIndex1 = bRefSet ? 0 : findBitRun( pRefLine, 0, nW, bRefSet ); + for( ; nLineIndex < nW; ) + { + long nRefIndex2 = findBitRun( pRefLine, nRefIndex1, nW, isSet( pRefLine, nRefIndex1 ) ); + if( nRefIndex2 >= nRunIndex1 ) + { + long nDiff = nRefIndex1 - nRunIndex1; + if( -3 <= nDiff && nDiff <= 3 ) + { // vertical coding + static const struct + { + sal_uInt32 mnCodeBits; + sal_uInt32 mnCode; + } VerticalCodes[7] = { + { 7, 0x03 }, // 0000 011 + { 6, 0x03 }, // 0000 11 + { 3, 0x03 }, // 011 + { 1, 0x1 }, // 1 + { 3, 0x2 }, // 010 + { 6, 0x02 }, // 0000 10 + { 7, 0x02 } // 0000 010 + }; + // convert to index + nDiff += 3; + + // emit diff code + putG4Bits( VerticalCodes[nDiff].mnCodeBits, VerticalCodes[nDiff].mnCode, aBitState ); + nLineIndex = nRunIndex1; + } + else + { // difference too large, horizontal coding + // emit horz code 001 + putG4Bits( 3, 0x1, aBitState ); + long nRunIndex2 = findBitRun( pCurLine, nRunIndex1, nW, isSet( pCurLine, nRunIndex1 ) ); + bool bWhiteFirst = ( nLineIndex + nRunIndex1 == 0 || ! isSet( pCurLine, nLineIndex ) ); + putG4Span( nRunIndex1 - nLineIndex, bWhiteFirst, aBitState ); + putG4Span( nRunIndex2 - nRunIndex1, ! bWhiteFirst, aBitState ); + nLineIndex = nRunIndex2; + } + } + else + { // emit pass code 0001 + putG4Bits( 4, 0x1, aBitState ); + nLineIndex = nRefIndex2; + } + if( nLineIndex < nW ) + { + bool bSet = isSet( pCurLine, nLineIndex ); + nRunIndex1 = findBitRun( pCurLine, nLineIndex, nW, bSet ); + nRefIndex1 = findBitRun( pRefLine, nLineIndex, nW, ! bSet ); + nRefIndex1 = findBitRun( pRefLine, nRefIndex1, nW, bSet ); + } + } + + // the current line is the reference for the next line + pRefLine = pCurLine; + } + // terminate strip with EOFB + putG4Bits( 12, 1, aBitState ); + putG4Bits( 12, 1, aBitState ); + if( aBitState.mnNextBitPos != 8 ) + { + writeBuffer( aBitState.getByte(), 1 ); + aBitState.flush(); + } + + rtl_freeMemory( pFirstRefLine ); +} diff --git a/vcl/source/gdi/pngread.cxx b/vcl/source/gdi/pngread.cxx new file mode 100644 index 000000000000..66e3cd1bea1a --- /dev/null +++ b/vcl/source/gdi/pngread.cxx @@ -0,0 +1,1564 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <vcl/pngread.hxx> + +#include <cmath> +#include <rtl/crc.h> +#include <rtl/memory.h> +#include <rtl/alloc.h> +#include <tools/zcodec.hxx> +#include <tools/stream.hxx> +#include <vcl/bmpacc.hxx> +#include <vcl/svapp.hxx> +#include <vcl/alpha.hxx> +#include <osl/endian.h> + +// ----------- +// - Defines - +// ----------- + +#define PNGCHUNK_IHDR 0x49484452 +#define PNGCHUNK_PLTE 0x504c5445 +#define PNGCHUNK_IDAT 0x49444154 +#define PNGCHUNK_IEND 0x49454e44 +#define PNGCHUNK_bKGD 0x624b4744 +#define PNGCHUNK_cHRM 0x6348524d +#define PNGCHUNK_gAMA 0x67414d41 +#define PNGCHUNK_hIST 0x68495354 +#define PNGCHUNK_pHYs 0x70485973 +#define PNGCHUNK_sBIT 0x73425420 +#define PNGCHUNK_tIME 0x74494d45 +#define PNGCHUNK_tEXt 0x74455874 +#define PNGCHUNK_tRNS 0x74524e53 +#define PNGCHUNK_zTXt 0x7a545874 +#define PMGCHUNG_msOG 0x6d734f47 // Microsoft Office Animated GIF + +#define VIEWING_GAMMA 2.35 +#define DISPLAY_GAMMA 1.0 + +namespace vcl +{ +// ----------- +// - statics - +// ----------- + +// ------------------------------------------------------------------------------ + +static const sal_uInt8 mpDefaultColorTable[ 256 ] = +{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +// ------------- +// - PNGReaderImpl - +// ------------- + +class PNGReaderImpl +{ +private: + SvStream& mrPNGStream; + sal_uInt16 mnOrigStreamMode; + + std::vector< vcl::PNGReader::ChunkData > maChunkSeq; + std::vector< vcl::PNGReader::ChunkData >::iterator maChunkIter; + std::vector< sal_uInt8 >::iterator maDataIter; + + Bitmap* mpBmp; + BitmapWriteAccess* mpAcc; + Bitmap* mpMaskBmp; + AlphaMask* mpAlphaMask; + BitmapWriteAccess* mpMaskAcc; + ZCodec* mpZCodec; + sal_uInt8* mpInflateInBuf; // as big as the size of a scanline + alphachannel + 1 + sal_uInt8* mpScanPrior; // pointer to the latest scanline + sal_uInt8* mpTransTab; // for transparency in images with palette colortype + sal_uInt8* mpScanCurrent; // pointer into the current scanline + sal_uInt8* mpColorTable; // + sal_Size mnStreamSize; // estimate of PNG file size + sal_uInt32 mnChunkType; // Type of current PNG chunk + sal_Int32 mnChunkLen; // Length of current PNG chunk + Size maOrigSize; // pixel size of the full image + Size maTargetSize; // pixel size of the result image + Size maPhysSize; // prefered size in MAP_100TH_MM units + sal_uInt32 mnBPP; // number of bytes per pixel + sal_uInt32 mnScansize; // max size of scanline + sal_uInt32 mnYpos; // latest y position in full image + int mnPass; // if interlaced the latest pass ( 1..7 ) else 7 + sal_uInt32 mnXStart; // the starting X for the current pass + sal_uInt32 mnXAdd; // the increment for input images X coords for the current pass + sal_uInt32 mnYAdd; // the increment for input images Y coords for the current pass + int mnPreviewShift; // shift to convert orig image coords into preview image coords + int mnPreviewMask; // == ((1 << mnPreviewShift) - 1) + sal_uInt16 mnIStmOldMode; + sal_uInt16 mnTargetDepth; // pixel depth of target bitmap + sal_uInt8 mnTransRed; + sal_uInt8 mnTransGreen; + sal_uInt8 mnTransBlue; + sal_uInt8 mnPngDepth; // pixel depth of PNG data + sal_uInt8 mnColorType; + sal_uInt8 mnCompressionType; + sal_uInt8 mnFilterType; + sal_uInt8 mnInterlaceType; + BitmapColor mcTranspColor; // transparency mask's transparency "color" + BitmapColor mcOpaqueColor; // transparency mask's opaque "color" + sal_Bool mbTransparent; // graphic includes an tRNS Chunk or an alpha Channel + sal_Bool mbAlphaChannel; // is true for ColorType 4 and 6 + sal_Bool mbRGBTriple; + sal_Bool mbPalette; // sal_False if we need a Palette + sal_Bool mbGrayScale; + sal_Bool mbzCodecInUse; + sal_Bool mbStatus; + sal_Bool mbIDAT; // sal_True if finished with enough IDAT chunks + sal_Bool mbGamma; // sal_True if Gamma Correction available + sal_Bool mbpHYs; // sal_True if pysical size of pixel available + sal_Bool mbIgnoreGammaChunk; + + bool ReadNextChunk(); + void ReadRemainingChunks(); + + void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor & ); + void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex ); + void ImplSetTranspPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor &, sal_Bool bTrans ); + void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex, sal_uInt8 nAlpha ); + void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor&, sal_uInt8 nAlpha ); + void ImplReadIDAT(); + bool ImplPreparePass(); + void ImplApplyFilter(); + void ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd ); + sal_Bool ImplReadTransparent(); + void ImplGetGamma(); + void ImplGetBackground(); + sal_uInt8 ImplScaleColor(); + sal_Bool ImplReadHeader( const Size& rPreviewSizeHint ); + sal_Bool ImplReadPalette(); + void ImplGetGrayPalette( sal_uInt16 ); + sal_uInt32 ImplReadsal_uInt32(); + +public: + + PNGReaderImpl( SvStream& ); + ~PNGReaderImpl(); + + BitmapEx GetBitmapEx( const Size& rPreviewSizeHint ); + const std::vector< PNGReader::ChunkData >& GetAllChunks(); + void SetIgnoreGammaChunk( sal_Bool bIgnore ){ mbIgnoreGammaChunk = bIgnore; }; +}; + +// ------------------------------------------------------------------------------ + +PNGReaderImpl::PNGReaderImpl( SvStream& rPNGStream ) +: mrPNGStream( rPNGStream ), + mpBmp ( NULL ), + mpAcc ( NULL ), + mpMaskBmp ( NULL ), + mpAlphaMask ( NULL ), + mpMaskAcc ( NULL ), + mpZCodec ( new ZCodec( DEFAULT_IN_BUFSIZE, DEFAULT_OUT_BUFSIZE, MAX_MEM_USAGE ) ), + mpInflateInBuf ( NULL ), + mpScanPrior ( NULL ), + mpTransTab ( NULL ), + mpScanCurrent ( NULL ), + mpColorTable ( (sal_uInt8*) mpDefaultColorTable ), + mnPass ( 0 ), + mbzCodecInUse ( sal_False ), + mbStatus( sal_True), + mbIDAT( sal_False ), + mbGamma ( sal_False ), + mbpHYs ( sal_False ), + mbIgnoreGammaChunk ( sal_False ) +{ + // prepare the PNG data stream + mnOrigStreamMode = mrPNGStream.GetNumberFormatInt(); + mrPNGStream.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN ); + + // prepare the chunk reader + maChunkSeq.reserve( 16 ); + maChunkIter = maChunkSeq.begin(); + + // estimate PNG file size (to allow sanity checks) + const sal_Size nStreamPos = mrPNGStream.Tell(); + mrPNGStream.Seek( STREAM_SEEK_TO_END ); + mnStreamSize = mrPNGStream.Tell(); + mrPNGStream.Seek( nStreamPos ); + + // check the PNG header magic + sal_uInt32 nDummy = 0; + mrPNGStream >> nDummy; + mbStatus = (nDummy == 0x89504e47); + mrPNGStream >> nDummy; + mbStatus &= (nDummy == 0x0d0a1a0a); + + mnPreviewShift = 0; + mnPreviewMask = (1 << mnPreviewShift) - 1; +} + +// ------------------------------------------------------------------------ + +PNGReaderImpl::~PNGReaderImpl() +{ + mrPNGStream.SetNumberFormatInt( mnOrigStreamMode ); + + if ( mbzCodecInUse ) + mpZCodec->EndCompression(); + + if( mpColorTable != mpDefaultColorTable ) + delete[] mpColorTable; + + delete mpBmp; + delete mpAlphaMask; + delete mpMaskBmp; + delete[] mpTransTab; + delete[] mpInflateInBuf; + delete[] mpScanPrior; + delete mpZCodec; +} + +// ------------------------------------------------------------------------ + +bool PNGReaderImpl::ReadNextChunk() +{ + if( maChunkIter == maChunkSeq.end() ) + { + // get the next chunk from the stream + + // unless we are at the end of the PNG stream + if( mrPNGStream.IsEof() || (mrPNGStream.GetError() != ERRCODE_NONE) ) + return false; + if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) ) + return false; + + PNGReader::ChunkData aDummyChunk; + maChunkIter = maChunkSeq.insert( maChunkSeq.end(), aDummyChunk ); + PNGReader::ChunkData& rChunkData = *maChunkIter; + + // read the chunk header + mrPNGStream >> mnChunkLen >> mnChunkType; + rChunkData.nType = mnChunkType; + + // #128377#/#149343# sanity check for chunk length + if( mnChunkLen < 0 ) + return false; + const sal_Size nStreamPos = mrPNGStream.Tell(); + if( nStreamPos + mnChunkLen >= mnStreamSize ) + return false; + + // calculate chunktype CRC (swap it back to original byte order) + sal_uInt32 nChunkType = mnChunkType;; + #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN) + nChunkType = SWAPLONG( nChunkType ); + #endif + sal_uInt32 nCRC32 = rtl_crc32( 0, &nChunkType, 4 ); + + // read the chunk data and check the CRC + if( mnChunkLen && !mrPNGStream.IsEof() ) + { + rChunkData.aData.resize( mnChunkLen ); + + sal_Int32 nBytesRead = 0; + do { + sal_uInt8* pPtr = &rChunkData.aData[ nBytesRead ]; + nBytesRead += mrPNGStream.Read( pPtr, mnChunkLen - nBytesRead ); + } while ( ( nBytesRead < mnChunkLen ) && ( mrPNGStream.GetError() == ERRCODE_NONE ) ); + + nCRC32 = rtl_crc32( nCRC32, &rChunkData.aData[ 0 ], mnChunkLen ); + maDataIter = rChunkData.aData.begin(); + } + sal_uInt32 nCheck; + mrPNGStream >> nCheck; + if( nCRC32 != nCheck ) + return false; + } + else + { + // the next chunk was already read + mnChunkType = (*maChunkIter).nType; + mnChunkLen = (*maChunkIter).aData.size(); + maDataIter = (*maChunkIter).aData.begin(); + } + + ++maChunkIter; + if( mnChunkType == PNGCHUNK_IEND ) + return false; + return true; +} + +// ------------------------------------------------------------------------ + +// read the remaining chunks from mrPNGStream +void PNGReaderImpl::ReadRemainingChunks() +{ + while( ReadNextChunk() ) ; +} + +// ------------------------------------------------------------------------ + +const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks() +{ + ReadRemainingChunks(); + return maChunkSeq; +} + +// ------------------------------------------------------------------------ + +BitmapEx PNGReaderImpl::GetBitmapEx( const Size& rPreviewSizeHint ) +{ + // reset to the first chunk + maChunkIter = maChunkSeq.begin(); + + // parse the chunks + while( mbStatus && !mbIDAT && ReadNextChunk() ) + { + switch( mnChunkType ) + { + case PNGCHUNK_IHDR : + { + mbStatus = ImplReadHeader( rPreviewSizeHint ); + } + break; + + case PNGCHUNK_gAMA : // the gamma chunk must precede + { // the 'IDAT' and also the 'PLTE'(if available ) + if ( !mbIgnoreGammaChunk && ( mbIDAT == sal_False ) ) + ImplGetGamma(); + } + break; + + case PNGCHUNK_PLTE : + { + if ( !mbPalette ) + mbStatus = ImplReadPalette(); + } + break; + + case PNGCHUNK_tRNS : + { + if ( !mbIDAT ) // the tRNS chunk must precede the IDAT + mbStatus = ImplReadTransparent(); + } + break; + + case PNGCHUNK_bKGD : // the background chunk must appear + { + if ( ( mbIDAT == sal_False ) && mbPalette ) // before the 'IDAT' and after the + ImplGetBackground(); // PLTE(if available ) chunk. + } + break; + + case PNGCHUNK_IDAT : + { + if ( !mpInflateInBuf ) // taking care that the header has properly been read + mbStatus = sal_False; + else if ( !mbIDAT ) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes + ImplReadIDAT(); + } + break; + + case PNGCHUNK_pHYs : + { + if ( !mbIDAT && mnChunkLen == 9 ) + { + sal_uInt32 nXPixelPerMeter = ImplReadsal_uInt32(); + sal_uInt32 nYPixelPerMeter = ImplReadsal_uInt32(); + + sal_uInt8 nUnitSpecifier = *maDataIter++; + if( (nUnitSpecifier == 1) && nXPixelPerMeter && nXPixelPerMeter ) + { + mbpHYs = sal_True; + + // convert into MAP_100TH_MM + maPhysSize.Width() = (sal_Int32)( (100000.0 * maOrigSize.Width()) / nXPixelPerMeter ); + maPhysSize.Height() = (sal_Int32)( (100000.0 * maOrigSize.Height()) / nYPixelPerMeter ); + } + } + } + break; + + case PNGCHUNK_IEND: + mbStatus = mbIDAT; // there is a problem if the image is not complete yet + break; + } + } + + // release write access of the bitmaps + if ( mpAcc ) + mpBmp->ReleaseAccess( mpAcc ), mpAcc = NULL; + + if ( mpMaskAcc ) + { + if ( mpAlphaMask ) + mpAlphaMask->ReleaseAccess( mpMaskAcc ); + else if ( mpMaskBmp ) + mpMaskBmp->ReleaseAccess( mpMaskAcc ); + + mpMaskAcc = NULL; + } + + // return the resulting BitmapEx + BitmapEx aRet; + + if( !mbStatus || !mbIDAT ) + aRet.Clear(); + else + { + if ( mpAlphaMask ) + aRet = BitmapEx( *mpBmp, *mpAlphaMask ); + else if ( mpMaskBmp ) + aRet = BitmapEx( *mpBmp, *mpMaskBmp ); + else + aRet = *mpBmp; + + if ( mbpHYs && maPhysSize.Width() && maPhysSize.Height() ) + { + aRet.SetPrefMapMode( MAP_100TH_MM ); + aRet.SetPrefSize( maPhysSize ); + } + + } + + return aRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint ) +{ + if( mnChunkLen < 13 ) + return sal_False; + + maOrigSize.Width() = ImplReadsal_uInt32(); + maOrigSize.Height() = ImplReadsal_uInt32(); + + if ( !maOrigSize.Width() || !maOrigSize.Height() ) + return sal_False; + + mnPngDepth = *(maDataIter++); + mnColorType = *(maDataIter++); + + mnCompressionType = *(maDataIter++); + if( mnCompressionType != 0 ) // unknown compression type + return sal_False; + + mnFilterType = *(maDataIter++); + if( mnFilterType != 0 ) // unknown filter type + return sal_False; + + mnInterlaceType = *(maDataIter++); + switch ( mnInterlaceType ) // filter type valid ? + { + case 0 : // progressive image + mnPass = 7; + break; + case 1 : // Adam7-interlaced image + mnPass = 0; + break; + default: + return sal_False; + } + + mbPalette = sal_True; + mbIDAT = mbAlphaChannel = mbTransparent = sal_False; + mbGrayScale = mbRGBTriple = sal_False; + mnTargetDepth = mnPngDepth; + sal_uInt64 nScansize64 = ( ( static_cast< sal_uInt64 >( maOrigSize.Width() ) * mnPngDepth ) + 7 ) >> 3; + + // valid color types are 0,2,3,4 & 6 + switch ( mnColorType ) + { + case 0 : // each pixel is a grayscale + { + switch ( mnPngDepth ) + { + case 2 : // 2bit target not available -> use four bits + mnTargetDepth = 4; // we have to expand the bitmap + mbGrayScale = sal_True; + break; + case 16 : + mnTargetDepth = 8; // we have to reduce the bitmap + // fall through + case 1 : + case 4 : + case 8 : + mbGrayScale = sal_True; + break; + default : + return sal_False; + } + } + break; + + case 2 : // each pixel is an RGB triple + { + mbRGBTriple = sal_True; + nScansize64 *= 3; + switch ( mnPngDepth ) + { + case 16 : // we have to reduce the bitmap + case 8 : + mnTargetDepth = 24; + break; + default : + return sal_False; + } + } + break; + + case 3 : // each pixel is a palette index + { + switch ( mnPngDepth ) + { + case 2 : + mnTargetDepth = 4; // we have to expand the bitmap + // fall through + case 1 : + case 4 : + case 8 : + mbPalette = sal_False; + break; + default : + return sal_False; + } + } + break; + + case 4 : // each pixel is a grayscale sample followed by an alpha sample + { + nScansize64 *= 2; + mbAlphaChannel = sal_True; + switch ( mnPngDepth ) + { + case 16 : + mnTargetDepth = 8; // we have to reduce the bitmap + case 8 : + mbGrayScale = sal_True; + break; + default : + return sal_False; + } + } + break; + + case 6 : // each pixel is an RGB triple followed by an alpha sample + { + mbRGBTriple = sal_True; + nScansize64 *= 4; + mbAlphaChannel = sal_True; + switch (mnPngDepth ) + { + case 16 : // we have to reduce the bitmap + case 8 : + mnTargetDepth = 24; + break; + default : + return sal_False; + } + } + break; + + default : + return sal_False; + } + + mnBPP = static_cast< sal_uInt32 >( nScansize64 / maOrigSize.Width() ); + if ( !mnBPP ) + mnBPP = 1; + + nScansize64++; // each scanline includes one filterbyte + + if ( nScansize64 > SAL_MAX_UINT32 ) + return sal_False; + + mnScansize = static_cast< sal_uInt32 >( nScansize64 ); + + // TODO: switch between both scanlines instead of copying + mpInflateInBuf = new (std::nothrow) sal_uInt8[ mnScansize ]; + mpScanCurrent = mpInflateInBuf; + mpScanPrior = new (std::nothrow) sal_uInt8[ mnScansize ]; + + if ( !mpInflateInBuf || !mpScanPrior ) + return sal_False; + + // calculate target size from original size and the preview hint + if( rPreviewSizeHint.Width() || rPreviewSizeHint.Height() ) + { + Size aPreviewSize( rPreviewSizeHint.Width(), rPreviewSizeHint.Height() ); + maTargetSize = maOrigSize; + + if( aPreviewSize.Width() == 0 ) { + aPreviewSize.setWidth( ( maOrigSize.Width()*aPreviewSize.Height() )/maOrigSize.Height() ); + if( aPreviewSize.Width() <= 0 ) + aPreviewSize.setWidth( 1 ); + } else if( aPreviewSize.Height() == 0 ) { + aPreviewSize.setHeight( ( maOrigSize.Height()*aPreviewSize.Width() )/maOrigSize.Width() ); + if( aPreviewSize.Height() <= 0 ) + aPreviewSize.setHeight( 1 ); + } + + if( aPreviewSize.Width() < maOrigSize.Width() && aPreviewSize.Height() < maOrigSize.Height() ) { + OSL_TRACE("preview size %dx%d", aPreviewSize.Width(), aPreviewSize.Height() ); + + for( int i = 1; i < 5; ++i ) + { + if( (maTargetSize.Width() >> i) < aPreviewSize.Width() ) + break; + if( (maTargetSize.Height() >> i) < aPreviewSize.Height() ) + break; + mnPreviewShift = i; + } + mnPreviewMask = (1 << mnPreviewShift) - 1; + } + } + + maTargetSize.Width() = (maOrigSize.Width() + mnPreviewMask) >> mnPreviewShift; + maTargetSize.Height() = (maOrigSize.Height() + mnPreviewMask) >> mnPreviewShift; + + mpBmp = new Bitmap( maTargetSize, mnTargetDepth ); + mpAcc = mpBmp->AcquireWriteAccess(); + if( !mpAcc ) + return sal_False; + + mpBmp->SetSourceSizePixel( maOrigSize ); + + if ( mbAlphaChannel ) + { + mpAlphaMask = new AlphaMask( maTargetSize ); + mpAlphaMask->Erase( 128 ); + mpMaskAcc = mpAlphaMask->AcquireWriteAccess(); + if( !mpMaskAcc ) + return sal_False; + } + + if ( mbGrayScale ) + ImplGetGrayPalette( mnPngDepth ); + + ImplPreparePass(); + + return sal_True; +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth ) +{ + if( nBitDepth > 8 ) + nBitDepth = 8; + + sal_uInt16 nPaletteEntryCount = 1 << nBitDepth; + sal_uInt32 nAdd = nBitDepth ? 256 / (nPaletteEntryCount - 1) : 0; + + // no bitdepth==2 available + // but bitdepth==4 with two unused bits is close enough + if( nBitDepth == 2 ) + nPaletteEntryCount = 16; + + mpAcc->SetPaletteEntryCount( nPaletteEntryCount ); + for ( sal_uInt32 i = 0, nStart = 0; nStart < 256; i++, nStart += nAdd ) + mpAcc->SetPaletteColor( (sal_uInt16)i, BitmapColor( mpColorTable[ nStart ], + mpColorTable[ nStart ], mpColorTable[ nStart ] ) ); +} + +// ------------------------------------------------------------------------ + +sal_Bool PNGReaderImpl::ImplReadPalette() +{ + sal_uInt16 nCount = static_cast<sal_uInt16>( mnChunkLen / 3 ); + + if ( ( ( mnChunkLen % 3 ) == 0 ) && ( ( 0 < nCount ) && ( nCount <= 256 ) ) && mpAcc ) + { + mbPalette = sal_True; + mpAcc->SetPaletteEntryCount( (sal_uInt16) nCount ); + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + sal_uInt8 nRed = mpColorTable[ *maDataIter++ ]; + sal_uInt8 nGreen = mpColorTable[ *maDataIter++ ]; + sal_uInt8 nBlue = mpColorTable[ *maDataIter++ ]; + mpAcc->SetPaletteColor( i, Color( nRed, nGreen, nBlue ) ); + } + } + else + mbStatus = sal_False; + + return mbStatus; +} + +// ------------------------------------------------------------------------ + +sal_Bool PNGReaderImpl::ImplReadTransparent() +{ + bool bNeedAlpha = false; + + if ( mpTransTab == NULL ) + { + switch ( mnColorType ) + { + case 0 : + { + if ( mnChunkLen == 2 ) + { + mpTransTab = new sal_uInt8[ 256 ]; + rtl_fillMemory( mpTransTab, 256, 0xff ); + // color type 0 and 4 is always greyscale, + // so the return value can be used as index + sal_uInt8 nIndex = ImplScaleColor(); + mpTransTab[ nIndex ] = 0; + mbTransparent = true; + } + } + break; + + case 2 : + { + if ( mnChunkLen == 6 ) + { + mnTransRed = ImplScaleColor(); + mnTransGreen = ImplScaleColor(); + mnTransBlue = ImplScaleColor(); + mbTransparent = true; + } + } + break; + + case 3 : + { + if ( mnChunkLen <= 256 ) + { + mpTransTab = new sal_uInt8 [ 256 ]; + rtl_fillMemory( mpTransTab, 256, 0xff ); + rtl_copyMemory( mpTransTab, &(*maDataIter), mnChunkLen ); + maDataIter += mnChunkLen; + mbTransparent = true; + // need alpha transparency if not on/off masking + for( int i = 0; i < mnChunkLen; ++i ) + bNeedAlpha |= (mpTransTab[i]!=0x00) && (mpTransTab[i]!=0xFF); + } + } + break; + } + } + + if( mbTransparent && !mbAlphaChannel && !mpMaskBmp ) + { + if( bNeedAlpha) + { + mpAlphaMask = new AlphaMask( maTargetSize ); + mpMaskAcc = mpAlphaMask->AcquireWriteAccess(); + } + else + { + mpMaskBmp = new Bitmap( maTargetSize, 1 ); + mpMaskAcc = mpMaskBmp->AcquireWriteAccess(); + } + mbTransparent = (mpMaskAcc != NULL); + if( !mbTransparent ) + return sal_False; + mcOpaqueColor = BitmapColor( 0x00 ); + mcTranspColor = BitmapColor( 0xFF ); + mpMaskAcc->Erase( 0x00 ); + } + + return sal_True; +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplGetGamma() +{ + if( mnChunkLen < 4 ) + return; + + sal_uInt32 nGammaValue = ImplReadsal_uInt32(); + double fGamma = ( ( VIEWING_GAMMA / DISPLAY_GAMMA ) * ( (double)nGammaValue / 100000 ) ); + double fInvGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma ); + + if ( fInvGamma != 1.0 ) + { + mbGamma = sal_True; + + if ( mpColorTable == mpDefaultColorTable ) + mpColorTable = new sal_uInt8[ 256 ]; + + for ( sal_Int32 i = 0; i < 256; i++ ) + mpColorTable[ i ] = (sal_uInt8)(pow((double)i/255.0, fInvGamma) * 255.0 + 0.5); + + if ( mbGrayScale ) + ImplGetGrayPalette( mnPngDepth ); + } +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplGetBackground() +{ + switch ( mnColorType ) + { + case 3 : + { + if ( mnChunkLen == 1 ) + { + sal_uInt16 nCol = *maDataIter++; + if ( nCol < mpAcc->GetPaletteEntryCount() ) + { + mpAcc->Erase( mpAcc->GetPaletteColor( (sal_uInt8)nCol ) ); + break; + } + } + } + break; + + case 0 : + case 4 : + { + if ( mnChunkLen == 2 ) + { + // the color type 0 and 4 is always greyscale, + // so the return value can be used as index + sal_uInt8 nIndex = ImplScaleColor(); + mpAcc->Erase( mpAcc->GetPaletteColor( nIndex ) ); + } + } + break; + + case 2 : + case 6 : + { + if ( mnChunkLen == 6 ) + { + sal_uInt8 nRed = ImplScaleColor(); + sal_uInt8 nGreen = ImplScaleColor(); + sal_uInt8 nBlue = ImplScaleColor(); + mpAcc->Erase( Color( nRed, nGreen, nBlue ) ); + } + } + break; + } +} + +// ------------------------------------------------------------------------ + +// for color type 0 and 4 (greyscale) the return value is always index to the color +// 2 and 6 (RGB) the return value is always the 8 bit color component +sal_uInt8 PNGReaderImpl::ImplScaleColor() +{ + sal_uInt32 nMask = ( ( 1 << mnPngDepth ) - 1 ); + sal_uInt16 nCol = ( *maDataIter++ << 8 ); + + nCol += *maDataIter++ & (sal_uInt16)nMask; + + if ( mnPngDepth > 8 ) // convert 16bit graphics to 8 + nCol >>= 8; + + return (sal_uInt8) nCol; +} + +// ------------------------------------------------------------------------ +// ImplReadIDAT reads as much image data as needed + +void PNGReaderImpl::ImplReadIDAT() +{ + if( mnChunkLen > 0 ) + { + if ( mbzCodecInUse == sal_False ) + { + mbzCodecInUse = sal_True; + mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT ); + } + mpZCodec->SetBreak( mnChunkLen ); + SvMemoryStream aIStrm( &(*maDataIter), mnChunkLen, STREAM_READ ); + + while ( ( mpZCodec->GetBreak() ) ) + { + // get bytes needed to fill the current scanline + sal_Int32 nToRead = mnScansize - (mpScanCurrent - mpInflateInBuf); + sal_Int32 nRead = mpZCodec->ReadAsynchron( aIStrm, mpScanCurrent, nToRead ); + if ( nRead < 0 ) + { + mbStatus = sal_False; + break; + } + if ( nRead < nToRead ) + { + mpScanCurrent += nRead; // more ZStream data in the next IDAT chunk + break; + } + else // this scanline is Finished + { + mpScanCurrent = mpInflateInBuf; + ImplApplyFilter(); + + ImplDrawScanline( mnXStart, mnXAdd ); + mnYpos += mnYAdd; + } + + if ( mnYpos >= (sal_uInt32)maOrigSize.Height() ) + { + if( (mnPass < 7) && mnInterlaceType ) + if( ImplPreparePass() ) + continue; + mbIDAT = true; + break; + } + } + } + + if( mbIDAT ) + { + mpZCodec->EndCompression(); + mbzCodecInUse = sal_False; + } +} + +// --------------------------------------------------------------------------------------------------- + +bool PNGReaderImpl::ImplPreparePass() +{ + struct InterlaceParams{ int mnXStart, mnYStart, mnXAdd, mnYAdd; }; + static const InterlaceParams aInterlaceParams[8] = + { + // non-interlaced + { 0, 0, 1, 1 }, + // Adam7-interlaced + { 0, 0, 8, 8 }, // pass 1 + { 4, 0, 8, 8 }, // pass 2 + { 0, 4, 4, 8 }, // pass 3 + { 2, 0, 4, 4 }, // pass 4 + { 0, 2, 2, 4 }, // pass 5 + { 1, 0, 2, 2 }, // pass 6 + { 0, 1, 1, 2 } // pass 7 + }; + + const InterlaceParams* pParam = &aInterlaceParams[ 0 ]; + if( mnInterlaceType ) + { + while( ++mnPass <= 7 ) + { + pParam = &aInterlaceParams[ mnPass ]; + + // skip this pass if the original image is too small for it + if( (pParam->mnXStart < maOrigSize.Width()) + && (pParam->mnYStart < maOrigSize.Height()) ) + break; + } + if( mnPass > 7 ) + return false; + + // skip the last passes if possible (for scaled down target images) + if( mnPreviewMask & (pParam->mnXStart | pParam->mnYStart) ) + return false; + } + + mnYpos = pParam->mnYStart; + mnXStart = pParam->mnXStart; + mnXAdd = pParam->mnXAdd; + mnYAdd = pParam->mnYAdd; + + // in Interlace mode the size of scanline is not constant + // so first we calculate the number of entrys + long nScanWidth = (maOrigSize.Width() - mnXStart + mnXAdd - 1) / mnXAdd; + mnScansize = nScanWidth; + + if( mbRGBTriple ) + mnScansize = 3 * nScanWidth; + + if( mbAlphaChannel ) + mnScansize += nScanWidth; + + // convert to width in bytes + mnScansize = ( mnScansize*mnPngDepth + 7 ) >> 3; + + ++mnScansize; // scan size also needs room for the filtertype byte + rtl_zeroMemory( mpScanPrior, mnScansize ); + + return true; +} + +// ---------------------------------------------------------------------------- +// ImplApplyFilter writes the complete Scanline (nY) +// in interlace mode the parameter nXStart and nXAdd are non-zero + +void PNGReaderImpl::ImplApplyFilter() +{ + OSL_ASSERT( mnScansize >= mnBPP + 1 ); + const sal_uInt8* const pScanEnd = mpInflateInBuf + mnScansize; + + sal_uInt8 nFilterType = *mpInflateInBuf; // the filter type may change each scanline + switch ( nFilterType ) + { + default: // unknown Scanline Filter Type + case 0: // Filter Type "None" + // we let the pixels pass and display the data unfiltered + break; + + case 1: // Scanline Filter Type "Sub" + { + sal_uInt8* p1 = mpInflateInBuf + 1; + const sal_uInt8* p2 = p1; + p1 += mnBPP; + + // use left pixels + do + *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) ); + while( ++p1 < pScanEnd ); + } + break; + + case 2: // Scanline Filter Type "Up" + { + sal_uInt8* p1 = mpInflateInBuf + 1; + const sal_uInt8* p2 = mpScanPrior + 1; + + // use pixels from prior line + while( p1 < pScanEnd ) + { + *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) ); + ++p1; + } + } + break; + + case 3: // Scanline Filter Type "Average" + { + sal_uInt8* p1 = mpInflateInBuf + 1; + const sal_uInt8* p2 = mpScanPrior + 1; + const sal_uInt8* p3 = p1; + + // use one pixel from prior line + for( int n = mnBPP; --n >= 0; ++p1, ++p2) + *p1 = static_cast<sal_uInt8>( *p1 + (*p2 >> 1) ); + + // predict by averaging the left and prior line pixels + while( p1 < pScanEnd ) + { + *p1 = static_cast<sal_uInt8>( *p1 + ((*(p2++) + *(p3++)) >> 1) ); + ++p1; + } + } + break; + + case 4: // Scanline Filter Type "PaethPredictor" + { + sal_uInt8* p1 = mpInflateInBuf + 1; + const sal_uInt8* p2 = mpScanPrior + 1; + const sal_uInt8* p3 = p1; + const sal_uInt8* p4 = p2; + + // use one pixel from prior line + for( int n = mnBPP; --n >= 0; ++p1) + *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) ); + + // predict by using the left and the prior line pixels + while( p1 < pScanEnd ) + { + int na = *(p2++); + int nb = *(p3++); + int nc = *(p4++); + + int npa = nb - (int)nc; + int npb = na - (int)nc; + int npc = npa + npb; + + if( npa < 0 ) + npa =-npa; + if( npb < 0 ) + npb =-npb; + if( npc < 0 ) + npc =-npc; + + if( npa > npb ) + na = nb, npa = npb; + if( npa > npc ) + na = nc; + + *p1 = static_cast<sal_uInt8>( *p1 + na ); + ++p1; + } + } + break; + } + + rtl_copyMemory( mpScanPrior, mpInflateInBuf, mnScansize ); +} + +// --------------------------------------------------------------------------------------------------- +// ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap +// In interlace mode the parameter nXStart and nXAdd append to the currently used pass + +void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd ) +{ + // optimization for downscaling + if( mnYpos & mnPreviewMask ) + return; + if( nXStart & mnPreviewMask ) + return; + + // convert nY to pixel units in the target image + // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods + const sal_uInt32 nY = mnYpos >> mnPreviewShift; + + const sal_uInt8* pTmp = mpInflateInBuf + 1; + if ( mpAcc->HasPalette() ) // alphachannel is not allowed by pictures including palette entries + { + switch ( mpAcc->GetBitCount() ) + { + case 1 : + { + if ( mbTransparent ) + { + for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd ) + { + sal_uInt8 nCol; + nShift = (nShift - 1) & 7; + if ( nShift == 0 ) + nCol = *(pTmp++); + else + nCol = static_cast<sal_uInt8>( *pTmp >> nShift ); + nCol &= 1; + + ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] ); + } + } + else + { // BMP_FORMAT_1BIT_MSB_PAL + for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd ) + { + nShift = (nShift - 1) & 7; + + sal_uInt8 nCol; + if ( nShift == 0 ) + nCol = *(pTmp++); + else + nCol = static_cast<sal_uInt8>( *pTmp >> nShift ); + nCol &= 1; + + ImplSetPixel( nY, nX, nCol ); + } + } + } + break; + + case 4 : + { + if ( mbTransparent ) + { + if ( mnPngDepth == 4 ) // check if source has a two bit pixel format + { + for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, ++nXIndex ) + { + if( nXIndex & 1 ) + { + ImplSetAlphaPixel( nY, nX, *pTmp & 0x0f, mpTransTab[ *pTmp & 0x0f ] ); + pTmp++; + } + else + { + ImplSetAlphaPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f, mpTransTab[ *pTmp >> 4 ] ); + } + } + } + else // if ( mnPngDepth == 2 ) + { + for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ ) + { + sal_uInt8 nCol; + switch( nXIndex & 3 ) + { + case 0 : + nCol = *pTmp >> 6; + break; + + case 1 : + nCol = ( *pTmp >> 4 ) & 0x03 ; + break; + + case 2 : + nCol = ( *pTmp >> 2 ) & 0x03; + break; + + case 3 : + nCol = ( *pTmp++ ) & 0x03; + break; + + default: // get rid of nCol uninitialized warning + nCol = 0; + break; + } + + ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] ); + } + } + } + else + { + if ( mnPngDepth == 4 ) // maybe the source is a two bitmap graphic + { // BMP_FORMAT_4BIT_LSN_PAL + for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ ) + { + if( nXIndex & 1 ) + ImplSetPixel( nY, nX, *pTmp++ & 0x0f ); + else + ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f ); + } + } + else // if ( mnPngDepth == 2 ) + { + for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ ) + { + switch( nXIndex & 3 ) + { + case 0 : + ImplSetPixel( nY, nX, *pTmp >> 6 ); + break; + + case 1 : + ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x03 ); + break; + + case 2 : + ImplSetPixel( nY, nX, ( *pTmp >> 2 ) & 0x03 ); + break; + + case 3 : + ImplSetPixel( nY, nX, *pTmp++ & 0x03 ); + break; + } + } + } + } + } + break; + + case 8 : + { + if ( mbAlphaChannel ) + { + if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 ) + ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 1 ] ); + } + else + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) + ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 2 ] ); + } + } + else if ( mbTransparent ) + { + if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp++ ) + ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] ); + } + else + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 ) + ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] ); + } + } + else // neither alpha nor transparency + { + if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale + { + if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible + { + int nLineBytes = maOrigSize.Width(); + mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_8BIT_PAL, nLineBytes ); + pTmp += nLineBytes; + } + else + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd ) + ImplSetPixel( nY, nX, *pTmp++ ); + } + } + else + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 ) + ImplSetPixel( nY, nX, *pTmp ); + } + } + } + break; + + default : + mbStatus = sal_False; + break; + } + } + else // no palette => truecolor + { + if( mbAlphaChannel ) // has RGB + alpha + { // BMP_FORMAT_32BIT_TC_RGBA + if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample + { + if ( mpColorTable != mpDefaultColorTable ) + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) + ImplSetAlphaPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], + mpColorTable[ pTmp[ 1 ] ], + mpColorTable[ pTmp[ 2 ] ] ), pTmp[ 3 ] ); + } + else + { +// if ( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible +// { +// int nLineBytes = 4 * maOrigSize.Width(); +// mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_32BIT_TC_RGBA, nLineBytes ); +// pTmp += nLineBytes; +// } +// else + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) + ImplSetAlphaPixel( nY, nX, BitmapColor( pTmp[0], pTmp[1], pTmp[2] ), pTmp[3] ); + } + } + } + else + { // BMP_FORMAT_64BIT_TC_RGBA + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 8 ) + ImplSetAlphaPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], + mpColorTable[ pTmp[ 2 ] ], + mpColorTable[ pTmp[ 4 ] ] ), pTmp[6] ); + } + } + else if( mbTransparent ) // has RGB + transparency + { // BMP_FORMAT_24BIT_TC_RGB + if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) + { + sal_uInt8 nRed = pTmp[ 0 ]; + sal_uInt8 nGreen = pTmp[ 1 ]; + sal_uInt8 nBlue = pTmp[ 2 ]; + sal_Bool bTransparent = ( ( nRed == mnTransRed ) + && ( nGreen == mnTransGreen ) + && ( nBlue == mnTransBlue ) ); + + ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ], + mpColorTable[ nGreen ], + mpColorTable[ nBlue ] ), bTransparent ); + } + } + else + { // BMP_FORMAT_48BIT_TC_RGB + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 ) + { + sal_uInt8 nRed = pTmp[ 0 ]; + sal_uInt8 nGreen = pTmp[ 2 ]; + sal_uInt8 nBlue = pTmp[ 4 ]; + sal_Bool bTransparent = ( ( nRed == mnTransRed ) + && ( nGreen == mnTransGreen ) + && ( nBlue == mnTransBlue ) ); + + ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ], + mpColorTable[ nGreen ], + mpColorTable[ nBlue ] ), bTransparent ); + } + } + } + else // has RGB but neither alpha nor transparency + { // BMP_FORMAT_24BIT_TC_RGB + if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample + { + if ( mpColorTable != mpDefaultColorTable ) + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) + ImplSetPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], + mpColorTable[ pTmp[ 1 ] ], + mpColorTable[ pTmp[ 2 ] ] ) ); + } + else + { + if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible + { + int nLineBytes = maOrigSize.Width() * 3; + mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_24BIT_TC_RGB, nLineBytes ); + pTmp += nLineBytes; + } + else + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) + ImplSetPixel( nY, nX, BitmapColor( pTmp[0], pTmp[1], pTmp[2] ) ); + } + } + } + else + { // BMP_FORMAT_48BIT_TC_RGB + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 ) + ImplSetPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], + mpColorTable[ pTmp[ 2 ] ], + mpColorTable[ pTmp[ 4 ] ] ) ); + } + } + } +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor ) +{ + // TODO: get preview mode checks out of inner loop + if( nX & mnPreviewMask ) + return; + nX >>= mnPreviewShift; + + mpAcc->SetPixel( nY, nX, rBitmapColor ); +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, sal_uInt8 nPalIndex ) +{ + // TODO: get preview mode checks out of inner loop + if( nX & mnPreviewMask ) + return; + nX >>= mnPreviewShift; + + mpAcc->SetPixel( nY, nX, nPalIndex ); +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor, sal_Bool bTrans ) +{ + // TODO: get preview mode checks out of inner loop + if( nX & mnPreviewMask ) + return; + nX >>= mnPreviewShift; + + mpAcc->SetPixel( nY, nX, rBitmapColor ); + + if ( bTrans ) + mpMaskAcc->SetPixel( nY, nX, mcTranspColor ); + else + mpMaskAcc->SetPixel( nY, nX, mcOpaqueColor ); +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX, + sal_uInt8 nPalIndex, sal_uInt8 nAlpha ) +{ + // TODO: get preview mode checks out of inner loop + if( nX & mnPreviewMask ) + return; + nX >>= mnPreviewShift; + + mpAcc->SetPixel( nY, nX, nPalIndex ); + mpMaskAcc->SetPixel( nY, nX, ~nAlpha ); +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX, + const BitmapColor& rBitmapColor, sal_uInt8 nAlpha ) +{ + // TODO: get preview mode checks out of inner loop + if( nX & mnPreviewMask ) + return; + nX >>= mnPreviewShift; + + mpAcc->SetPixel( nY, nX, rBitmapColor ); + mpMaskAcc->SetPixel( nY, nX, ~nAlpha ); +} + +// ------------------------------------------------------------------------ + +sal_uInt32 PNGReaderImpl::ImplReadsal_uInt32() +{ + sal_uInt32 nRet; + nRet = *maDataIter++; + nRet <<= 8; + nRet |= *maDataIter++; + nRet <<= 8; + nRet |= *maDataIter++; + nRet <<= 8; + nRet |= *maDataIter++; + return nRet; +} + +// ------------------------------------------------------------------------ + +// ------------- +// - PNGReader - +// ------------- + +PNGReader::PNGReader( SvStream& rIStm ) : + mpImpl( new ::vcl::PNGReaderImpl( rIStm ) ) +{ +} + +// ------------------------------------------------------------------------ + +PNGReader::~PNGReader() +{ + delete mpImpl; +} + +// ------------------------------------------------------------------------ + +BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint ) +{ + return mpImpl->GetBitmapEx( i_rPreviewSizeHint ); +} + +// ------------------------------------------------------------------------ + +const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const +{ + return mpImpl->GetAllChunks(); +} + +// ------------------------------------------------------------------------ + +void PNGReader::SetIgnoreGammaChunk( sal_Bool b ) +{ + mpImpl->SetIgnoreGammaChunk( b ); +} + + +} // namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/pngwrite.cxx b/vcl/source/gdi/pngwrite.cxx new file mode 100644 index 000000000000..b87457c35054 --- /dev/null +++ b/vcl/source/gdi/pngwrite.cxx @@ -0,0 +1,739 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <vcl/pngwrite.hxx> + +#include <cmath> +#include <limits> +#include <rtl/crc.h> +#include <rtl/memory.h> +#include <rtl/alloc.h> +#include <tools/zcodec.hxx> +#include <tools/stream.hxx> +#include <vcl/bmpacc.hxx> +#include <vcl/svapp.hxx> +#include <vcl/alpha.hxx> +#include <osl/endian.h> + +// ----------- +// - Defines - +// ----------- + +#define PNG_DEF_COMPRESSION 6 + +#define PNGCHUNK_IHDR 0x49484452 +#define PNGCHUNK_PLTE 0x504c5445 +#define PNGCHUNK_IDAT 0x49444154 +#define PNGCHUNK_IEND 0x49454e44 +#define PNGCHUNK_bKGD 0x624b4744 +#define PNGCHUNK_cHRM 0x6348524d +#define PNGCHUNK_gAMA 0x67414d41 +#define PNGCHUNK_hIST 0x68495354 +#define PNGCHUNK_pHYs 0x70485973 +#define PNGCHUNK_sBIT 0x73425420 +#define PNGCHUNK_tIME 0x74494d45 +#define PNGCHUNK_tEXt 0x74455874 +#define PNGCHUNK_tRNS 0x74524e53 +#define PNGCHUNK_zTXt 0x7a545874 + +namespace vcl +{ +// ----------------- +// - PNGWriterImplImpl - +// ----------------- + +class PNGWriterImpl +{ +public: + + PNGWriterImpl( const BitmapEx& BmpEx, + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData = NULL ); + ~PNGWriterImpl(); + + sal_Bool Write( SvStream& rOStm ); + + std::vector< vcl::PNGWriter::ChunkData >& GetChunks(); + +private: + + std::vector< vcl::PNGWriter::ChunkData > maChunkSeq; + + sal_Int32 mnCompLevel; + sal_Int32 mnInterlaced; + sal_uInt32 mnMaxChunkSize; + sal_Bool mbStatus; + + BitmapReadAccess* mpAccess; + BitmapReadAccess* mpMaskAccess; + ZCodec* mpZCodec; + + sal_uInt8* mpDeflateInBuf; // as big as the size of a scanline + alphachannel + 1 + sal_uInt8* mpPreviousScan; // as big as mpDeflateInBuf + sal_uInt8* mpCurrentScan; + sal_uLong mnDeflateInSize; + + sal_uLong mnWidth, mnHeight; + sal_uInt8 mnBitsPerPixel; + sal_uInt8 mnFilterType; // 0 oder 4; + sal_uLong mnBBP; // bytes per pixel ( needed for filtering ) + sal_Bool mbTrueAlpha; + sal_uLong mnCRC; + long mnChunkDatSize; + sal_uLong mnLastPercent; + + void ImplWritepHYs( const BitmapEx& rBitmapEx ); + void ImplWriteIDAT(); + sal_uLong ImplGetFilter( sal_uLong nY, sal_uLong nXStart=0, sal_uLong nXAdd=1 ); + void ImplClearFirstScanline(); + void ImplWriteTransparent(); + sal_Bool ImplWriteHeader(); + void ImplWritePalette(); + void ImplOpenChunk( sal_uLong nChunkType ); + void ImplWriteChunk( sal_uInt8 nNumb ); + void ImplWriteChunk( sal_uInt32 nNumb ); + void ImplWriteChunk( unsigned char* pSource, sal_uInt32 nDatSize ); + void ImplCloseChunk( void ) const; +}; + +// ------------------------------------------------------------------------ + +PNGWriterImpl::PNGWriterImpl( const BitmapEx& rBmpEx, + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData ) : + mnCompLevel ( PNG_DEF_COMPRESSION ), + mbStatus ( sal_True ), + mpAccess ( NULL ), + mpMaskAccess ( NULL ), + mpZCodec ( new ZCodec( DEFAULT_IN_BUFSIZE, DEFAULT_OUT_BUFSIZE, MAX_MEM_USAGE ) ), + mnCRC(0UL), + mnLastPercent ( 0UL ) +{ + if ( !rBmpEx.IsEmpty() ) + { + Bitmap aBmp( rBmpEx.GetBitmap() ); + + mnInterlaced = 0; // ( aBmp.GetSizePixel().Width() > 128 ) || ( aBmp.GetSizePixel().Height() > 128 ) ? 1 : 0; #i67236# + + // #i67234# defaulting max chunk size to 256kb when using interlace mode + mnMaxChunkSize = mnInterlaced == 0 ? std::numeric_limits< sal_uInt32 >::max() : 0x40000; + + if ( pFilterData ) + { + sal_Int32 i = 0; + for ( i = 0; i < pFilterData->getLength(); i++ ) + { + if ( (*pFilterData)[ i ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Compression" ) ) ) + (*pFilterData)[ i ].Value >>= mnCompLevel; + else if ( (*pFilterData)[ i ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Interlaced" ) ) ) + (*pFilterData)[ i ].Value >>= mnInterlaced; + else if ( (*pFilterData)[ i ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "MaxChunkSize" ) ) ) + { + sal_Int32 nVal = 0; + if ( (*pFilterData)[ i ].Value >>= nVal ) + mnMaxChunkSize = (sal_uInt32)nVal; + } + } + } + mnBitsPerPixel = (sal_uInt8)aBmp.GetBitCount(); + + if( rBmpEx.IsTransparent() ) + { + if ( mnBitsPerPixel <= 8 && rBmpEx.IsAlpha() ) + { + aBmp.Convert( BMP_CONVERSION_24BIT ); + mnBitsPerPixel = 24; + } + + if ( mnBitsPerPixel <= 8 ) // transparent palette + { + aBmp.Convert( BMP_CONVERSION_8BIT_TRANS ); + aBmp.Replace( rBmpEx.GetMask(), BMP_COL_TRANS ); + mnBitsPerPixel = 8; + mpAccess = aBmp.AcquireReadAccess(); + if ( mpAccess ) + { + if ( ImplWriteHeader() ) + { + ImplWritepHYs( rBmpEx ); + ImplWritePalette(); + ImplWriteTransparent(); + ImplWriteIDAT(); + } + aBmp.ReleaseAccess( mpAccess ); + } + else + mbStatus = sal_False; + } + else + { + mpAccess = aBmp.AcquireReadAccess(); // sal_True RGB with alphachannel + if( mpAccess ) + { + if ( ( mbTrueAlpha = rBmpEx.IsAlpha() ) != sal_False ) + { + AlphaMask aMask( rBmpEx.GetAlpha() ); + mpMaskAccess = aMask.AcquireReadAccess(); + if ( mpMaskAccess ) + { + if ( ImplWriteHeader() ) + { + ImplWritepHYs( rBmpEx ); + ImplWriteIDAT(); + } + aMask.ReleaseAccess( mpMaskAccess ); + } + else + mbStatus = sal_False; + } + else + { + Bitmap aMask( rBmpEx.GetMask() ); + mpMaskAccess = aMask.AcquireReadAccess(); + if( mpMaskAccess ) + { + if ( ImplWriteHeader() ) + { + ImplWritepHYs( rBmpEx ); + ImplWriteIDAT(); + } + aMask.ReleaseAccess( mpMaskAccess ); + } + else + mbStatus = sal_False; + } + aBmp.ReleaseAccess( mpAccess ); + } + else + mbStatus = sal_False; + } + } + else + { + mpAccess = aBmp.AcquireReadAccess(); // palette + RGB without alphachannel + if( mpAccess ) + { + if ( ImplWriteHeader() ) + { + ImplWritepHYs( rBmpEx ); + if( mpAccess->HasPalette() ) + ImplWritePalette(); + + ImplWriteIDAT(); + } + aBmp.ReleaseAccess( mpAccess ); + } + else + mbStatus = sal_False; + } + if ( mbStatus ) + { + ImplOpenChunk( PNGCHUNK_IEND ); // create an IEND chunk + ImplCloseChunk(); + } + } +} + +// ------------------------------------------------------------------------ + +PNGWriterImpl::~PNGWriterImpl() +{ + delete mpZCodec; +} + +// ------------------------------------------------------------------------ + +sal_Bool PNGWriterImpl::Write( SvStream& rOStm ) +{ + /* png signature is always an array of 8 bytes */ + sal_uInt16 nOldMode = rOStm.GetNumberFormatInt(); + rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN ); + rOStm << static_cast<sal_uInt32>(0x89504e47); + rOStm << static_cast<sal_uInt32>(0x0d0a1a0a); + + std::vector< vcl::PNGWriter::ChunkData >::iterator aBeg( maChunkSeq.begin() ); + std::vector< vcl::PNGWriter::ChunkData >::iterator aEnd( maChunkSeq.end() ); + while( aBeg != aEnd ) + { + sal_uInt32 nType = aBeg->nType; + #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN) + nType = SWAPLONG( nType ); + #endif + sal_uInt32 nCRC = rtl_crc32( 0, &nType, 4 ); + sal_uInt32 nDataSize = aBeg->aData.size(); + if ( nDataSize ) + nCRC = rtl_crc32( nCRC, &aBeg->aData[ 0 ], nDataSize ); + rOStm << nDataSize + << aBeg->nType; + if ( nDataSize ) + rOStm.Write( &aBeg->aData[ 0 ], nDataSize ); + rOStm << nCRC; + aBeg++; + } + rOStm.SetNumberFormatInt( nOldMode ); + return mbStatus; +} + +// ------------------------------------------------------------------------ + +std::vector< vcl::PNGWriter::ChunkData >& PNGWriterImpl::GetChunks() +{ + return maChunkSeq; +} + +// ------------------------------------------------------------------------ + +sal_Bool PNGWriterImpl::ImplWriteHeader() +{ + ImplOpenChunk(PNGCHUNK_IHDR); + ImplWriteChunk( sal_uInt32( mnWidth = mpAccess->Width() ) ); + ImplWriteChunk( sal_uInt32( mnHeight = mpAccess->Height() ) ); + + if ( mnWidth && mnHeight && mnBitsPerPixel && mbStatus ) + { + sal_uInt8 nBitDepth = mnBitsPerPixel; + if ( mnBitsPerPixel <= 8 ) + mnFilterType = 0; + else + mnFilterType = 4; + + sal_uInt8 nColorType = 2; // colortype: + // bit 0 -> palette is used + if ( mpAccess->HasPalette() ) // bit 1 -> color is used + nColorType |= 1; // bit 2 -> alpha channel is used + else + nBitDepth /= 3; + + if ( mpMaskAccess ) + nColorType |= 4; + + ImplWriteChunk( nBitDepth ); + ImplWriteChunk( nColorType ); // colortype + ImplWriteChunk((sal_uInt8) 0 ); // compression type + ImplWriteChunk((sal_uInt8) 0 ); // filter type - is not supported in this version + ImplWriteChunk((sal_uInt8) mnInterlaced ); // interlace type + ImplCloseChunk(); + } + else + mbStatus = sal_False; + return mbStatus; +} + +// ------------------------------------------------------------------------ + +void PNGWriterImpl::ImplWritePalette() +{ + const sal_uLong nCount = mpAccess->GetPaletteEntryCount(); + sal_uInt8* pTempBuf = new sal_uInt8[ nCount*3 ]; + sal_uInt8* pTmp = pTempBuf; + + ImplOpenChunk( PNGCHUNK_PLTE ); + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + const BitmapColor& rColor = mpAccess->GetPaletteColor( i ); + *pTmp++ = rColor.GetRed(); + *pTmp++ = rColor.GetGreen(); + *pTmp++ = rColor.GetBlue(); + } + ImplWriteChunk( pTempBuf, nCount*3 ); + ImplCloseChunk(); + delete[] pTempBuf; +} + +// ------------------------------------------------------------------------ + +void PNGWriterImpl::ImplWriteTransparent () +{ + const sal_uLong nTransIndex = mpAccess->GetBestMatchingColor( BMP_COL_TRANS ); + + ImplOpenChunk( PNGCHUNK_tRNS ); + + for ( sal_uLong n = 0UL; n <= nTransIndex; n++ ) + ImplWriteChunk( ( nTransIndex == n ) ? (sal_uInt8) 0x0 : (sal_uInt8) 0xff ); + + ImplCloseChunk(); +} + +// ------------------------------------------------------------------------ + +void PNGWriterImpl::ImplWritepHYs( const BitmapEx& rBmpEx ) +{ + if ( rBmpEx.GetPrefMapMode() == MAP_100TH_MM ) + { + Size aPrefSize( rBmpEx.GetPrefSize() ); + if ( aPrefSize.Width() && aPrefSize.Height() ) + { + ImplOpenChunk( PNGCHUNK_pHYs ); + sal_uInt8 nMapUnit = 1; + sal_uInt32 nPrefSizeX = (sal_uInt32)( (double)100000.0 / ( (double)aPrefSize.Width() / mnWidth ) + 0.5 ); + sal_uInt32 nPrefSizeY = (sal_uInt32)( (double)100000.0 / ( (double)aPrefSize.Height() / mnHeight ) + 0.5 ); + ImplWriteChunk( nPrefSizeX ); + ImplWriteChunk( nPrefSizeY ); + ImplWriteChunk( nMapUnit ); + ImplCloseChunk(); + } + } +} + +// ------------------------------------------------------------------------ + +void PNGWriterImpl::ImplWriteIDAT () +{ + mnDeflateInSize = mnBitsPerPixel; + + if( mpMaskAccess ) + mnDeflateInSize += 8; + + mnBBP = ( mnDeflateInSize + 7 ) >> 3; + + mnDeflateInSize = mnBBP * mnWidth + 1; + + mpDeflateInBuf = new sal_uInt8[ mnDeflateInSize ]; + + if ( mnFilterType ) // using filter type 4 we need memory for the scanline 3 times + { + mpPreviousScan = new sal_uInt8[ mnDeflateInSize ]; + mpCurrentScan = new sal_uInt8[ mnDeflateInSize ]; + ImplClearFirstScanline(); + } + mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT + mnCompLevel ); + mpZCodec->SetCRC( mnCRC ); + SvMemoryStream aOStm; + if ( mnInterlaced == 0 ) + { + for ( sal_uLong nY = 0; nY < mnHeight; nY++ ) + mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter( nY ) ); + } + else + { + // interlace mode + sal_uLong nY; + for ( nY = 0; nY < mnHeight; nY+=8 ) // pass 1 + mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 8 ) ); + ImplClearFirstScanline(); + + for ( nY = 0; nY < mnHeight; nY+=8 ) // pass 2 + mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 4, 8 ) ); + ImplClearFirstScanline(); + + if ( mnHeight >= 5 ) // pass 3 + { + for ( nY = 4; nY < mnHeight; nY+=8 ) + mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 4 ) ); + ImplClearFirstScanline(); + } + + for ( nY = 0; nY < mnHeight; nY+=4 ) // pass 4 + mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 2, 4 ) ); + ImplClearFirstScanline(); + + if ( mnHeight >= 3 ) // pass 5 + { + for ( nY = 2; nY < mnHeight; nY+=4 ) + mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 2 ) ); + ImplClearFirstScanline(); + } + + for ( nY = 0; nY < mnHeight; nY+=2 ) // pass 6 + mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 1, 2 ) ); + ImplClearFirstScanline(); + + if ( mnHeight >= 2 ) // pass 7 + { + for ( nY = 1; nY < mnHeight; nY+=2 ) + mpZCodec->Write( aOStm, mpDeflateInBuf, ImplGetFilter ( nY, 0, 1 ) ); + } + } + mpZCodec->EndCompression(); + mnCRC = mpZCodec->GetCRC(); + + if ( mnFilterType ) // using filter type 4 we need memory for the scanline 3 times + { + delete[] mpCurrentScan; + delete[] mpPreviousScan; + } + delete[] mpDeflateInBuf; + + sal_uInt32 nIDATSize = aOStm.Tell(); + sal_uInt32 nBytes, nBytesToWrite = nIDATSize; + while( nBytesToWrite ) + { + nBytes = nBytesToWrite <= mnMaxChunkSize ? nBytesToWrite : mnMaxChunkSize; + ImplOpenChunk( PNGCHUNK_IDAT ); + ImplWriteChunk( (unsigned char*)aOStm.GetData() + ( nIDATSize - nBytesToWrite ), nBytes ); + ImplCloseChunk(); + nBytesToWrite -= nBytes; + } +} + +// --------------------------------------------------------------------------------------------------- +// ImplGetFilter writes the complete Scanline (nY) - in interlace mode the parameter nXStart and nXAdd +// appends to the currently used pass +// the complete size of scanline will be returned - in interlace mode zero is possible! + +sal_uLong PNGWriterImpl::ImplGetFilter ( sal_uLong nY, sal_uLong nXStart, sal_uLong nXAdd ) +{ + sal_uInt8* pDest; + + if ( mnFilterType ) + pDest = mpCurrentScan; + else + pDest = mpDeflateInBuf; + + if ( nXStart < mnWidth ) + { + *pDest++ = mnFilterType; // in this version the filter type is either 0 or 4 + + if ( mpAccess->HasPalette() ) // alphachannel is not allowed by pictures including palette entries + { + switch ( mnBitsPerPixel ) + { + case( 1 ): + { + sal_uLong nX, nXIndex; + for ( nX = nXStart, nXIndex = 0; nX < mnWidth; nX+=nXAdd, nXIndex++ ) + { + sal_uLong nShift = ( nXIndex & 7 ) ^ 7; + if ( nShift == 7) + *pDest = (sal_uInt8)(mpAccess->GetPixel( nY, nX ) << nShift); + else if ( nShift == 0 ) + *pDest++ |= (sal_uInt8) mpAccess->GetPixel( nY, nX ) << nShift; + else + *pDest |= (sal_uInt8) mpAccess->GetPixel( nY, nX ) << nShift; + } + if ( ( nXIndex & 7 ) != 0 ) pDest++; // byte is not completely used, so the + } // bufferpointer is to correct + break; + + case( 4 ): + { + sal_uLong nX, nXIndex; + for ( nX = nXStart, nXIndex = 0; nX < mnWidth; nX+= nXAdd, nXIndex++ ) + { + if( nXIndex & 1 ) + *pDest++ |= (sal_uInt8) mpAccess->GetPixel( nY, nX ); + else + *pDest = (sal_uInt8) mpAccess->GetPixel( nY, nX ) << 4; + } + if ( nXIndex & 1 ) pDest++; + } + break; + + case( 8 ): + { + for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd ) + *pDest++ = mpAccess->GetPixel( nY, nX ); + } + break; + + default : + mbStatus = sal_False; + break; + } + } + else + { + if ( mpMaskAccess ) // mpMaskAccess != NULL -> alphachannel is to create + { + if ( mbTrueAlpha ) + { + for ( sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd ) + { + const BitmapColor& rColor = mpAccess->GetPixel( nY, nX ); + *pDest++ = rColor.GetRed(); + *pDest++ = rColor.GetGreen(); + *pDest++ = rColor.GetBlue(); + *pDest++ = 255 - mpMaskAccess->GetPixel( nY, nX ); + } + } + else + { + const BitmapColor aTrans( mpMaskAccess->GetBestMatchingColor( Color( COL_WHITE ) ) ); + + for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd ) + { + const BitmapColor& rColor = mpAccess->GetPixel( nY, nX ); + *pDest++ = rColor.GetRed(); + *pDest++ = rColor.GetGreen(); + *pDest++ = rColor.GetBlue(); + + if( mpMaskAccess->GetPixel( nY, nX ) == aTrans ) + *pDest++ = 0; + else + *pDest++ = 0xff; + } + } + } + else + { + for ( sal_uLong nX = nXStart; nX < mnWidth; nX+=nXAdd ) + { + const BitmapColor& rColor = mpAccess->GetPixel( nY, nX ); + *pDest++ = rColor.GetRed(); + *pDest++ = rColor.GetGreen(); + *pDest++ = rColor.GetBlue(); + } + } + } + } + // filter type4 ( PAETH ) will be used only for 24bit graphics + if ( mnFilterType ) + { + mnDeflateInSize = pDest - mpCurrentScan; + pDest = mpDeflateInBuf; + *pDest++ = 4; // filter type + + sal_uLong na, nb, nc; + long np, npa, npb, npc; + + sal_uInt8* p1 = mpCurrentScan + 1; // Current Pixel + sal_uInt8* p2 = p1 - mnBBP; // left pixel + sal_uInt8* p3 = mpPreviousScan; // upper pixel + sal_uInt8* p4 = p3 - mnBBP; // upperleft Pixel; + + while ( pDest < mpDeflateInBuf + mnDeflateInSize ) + { + nb = *p3++; + if ( p2 >= mpCurrentScan + 1 ) + { + na = *p2; + nc = *p4; + } + else + na = nc = 0; + + np = na + nb; + np -= nc; + npa = np - na; + npb = np - nb; + npc = np - nc; + if ( npa < 0 ) + npa =-npa; + if ( npb < 0 ) + npb =-npb; + if ( npc < 0 ) + npc =-npc; + if ( ( npa <= npb ) && ( npa <= npc ) ) *pDest++ = *p1++ - (sal_uInt8)na; + else if ( npb <= npc ) *pDest++ = *p1++ - (sal_uInt8)nb; + else *pDest++ = *p1++ - (sal_uInt8)nc; + p4++; + p2++; + } + for ( long i = 0; i < (long)( mnDeflateInSize - 1 ); i++ ) + mpPreviousScan[ i ] = mpCurrentScan[ i + 1 ]; + } + else + mnDeflateInSize = pDest - mpDeflateInBuf; + return ( mnDeflateInSize ); +} + +// ------------------------------------------------------------------------ + +void PNGWriterImpl::ImplClearFirstScanline() +{ + if ( mnFilterType ) + rtl_zeroMemory( mpPreviousScan, mnDeflateInSize ); +} + +// ------------------------------------------------------------------------ + +void PNGWriterImpl::ImplOpenChunk ( sal_uLong nChunkType ) +{ + maChunkSeq.resize( maChunkSeq.size() + 1 ); + maChunkSeq.back().nType = nChunkType; +} + +// ------------------------------------------------------------------------ + +void PNGWriterImpl::ImplWriteChunk ( sal_uInt8 nSource ) +{ + maChunkSeq.back().aData.push_back( nSource ); +} + +void PNGWriterImpl::ImplWriteChunk ( sal_uInt32 nSource ) +{ + vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back(); + rChunkData.aData.push_back( (sal_uInt8)( nSource >> 24 ) ); + rChunkData.aData.push_back( (sal_uInt8)( nSource >> 16 ) ); + rChunkData.aData.push_back( (sal_uInt8)( nSource >> 8 ) ); + rChunkData.aData.push_back( (sal_uInt8)( nSource ) ); +} + +void PNGWriterImpl::ImplWriteChunk ( unsigned char* pSource, sal_uInt32 nDatSize ) +{ + if ( nDatSize ) + { + vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back(); + sal_uInt32 nSize = rChunkData.aData.size(); + rChunkData.aData.resize( nSize + nDatSize ); + rtl_copyMemory( &rChunkData.aData[ nSize ], pSource, nDatSize ); + } +} + +// ------------------------------------------------------------------------ +// nothing to do +void PNGWriterImpl::ImplCloseChunk ( void ) const +{ +} + +// ------------- +// - PNGWriter - +// ------------- + +PNGWriter::PNGWriter( const BitmapEx& rBmpEx, + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData ) : + mpImpl( new ::vcl::PNGWriterImpl( rBmpEx, pFilterData ) ) +{ +} + +// ------------------------------------------------------------------------ + +PNGWriter::~PNGWriter() +{ + delete mpImpl; +} + +// ------------------------------------------------------------------------ + +sal_Bool PNGWriter::Write( SvStream& rIStm ) +{ + return mpImpl->Write( rIStm ); +} + +// ------------------------------------------------------------------------ + +std::vector< vcl::PNGWriter::ChunkData >& PNGWriter::GetChunks() +{ + return mpImpl->GetChunks(); +} + +} // namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/polyscan.cxx b/vcl/source/gdi/polyscan.cxx new file mode 100644 index 000000000000..81d05bf33354 --- /dev/null +++ b/vcl/source/gdi/polyscan.cxx @@ -0,0 +1,361 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <tools/new.hxx> +#include <vcl/salbtype.hxx> +#include "polyscan.hxx" +#include <tools/poly.hxx> + +// ---------------- +// - PolyScanline - +// ---------------- + +PolyScanline::PolyScanline() : + mpFirst ( NULL ), + mpLast ( NULL ), + mpAct ( NULL ), + mnLeft ( 0L ), + mnRight ( 0L ) +{ +} + +// ------------------------------------------------------------------------ + +PolyScanline::~PolyScanline() +{ + ImplDelete(); +} + +// ------------------------------------------------------------------------ + +void PolyScanline::ImplDelete() +{ + ScanlinePoint* pAct = mpFirst; + + while( pAct ) + { + ScanlinePoint* pNext = pAct->mpNext; + delete pAct; + pAct = pNext; + } + + mnLeft = mnRight = 0L; + mpFirst = mpAct = mpLast = NULL; +} + +// ------------------------------------------------------------------------ + +void PolyScanline::Insert( long nX ) +{ + // first point to insert? + if( !mpFirst ) + mpLast = mpFirst = new ScanlinePoint( mnLeft = mnRight = nX, NULL ); + else + { + // insert at the beginning of the scanline + if( nX <= mpFirst->mnX ) + mpFirst = new ScanlinePoint( mnLeft = nX, mpFirst ); + else if( nX >= mnRight ) + mpLast = mpLast->mpNext = new ScanlinePoint( mnRight = nX, NULL ); + else + { + ScanlinePoint* pLast = mpFirst; + ScanlinePoint* pAct = mpFirst->mpNext; + + while( pAct ) + { + // insert in the midlle of the scanline? + if( nX <= pAct->mnX ) + { + pLast->mpNext = new ScanlinePoint( nX, pAct ); + break; + } + + pLast = pAct; + pAct = pAct->mpNext; + } + } + } +} + +// ------------------------------------------------------------------------ + +void PolyScanline::Set( long nStart, long nEnd ) +{ + if( mpFirst ) + ImplDelete(); + + if( nStart <= nEnd ) + mpFirst = new ScanlinePoint( mnLeft = nStart, mpLast = new ScanlinePoint( mnRight = nEnd, NULL ) ); + else + mpFirst = new ScanlinePoint( mnLeft = nEnd, mpLast = new ScanlinePoint( mnRight = nStart, NULL ) ); +} + +// ------------------------------------------------------------------------ + +sal_Bool PolyScanline::GetFirstSegment( PolyScanSegment& rSegment ) +{ + sal_Bool bRet = GetFirstX( rSegment.mnStart ); + + if( bRet && !GetNextX( rSegment.mnEnd ) ) + rSegment.mnEnd = rSegment.mnStart; + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool PolyScanline::GetNextSegment( PolyScanSegment& rSegment ) +{ + sal_Bool bRet = GetNextX( rSegment.mnStart ); + + if( bRet && !GetNextX( rSegment.mnEnd ) ) + rSegment.mnEnd = rSegment.mnStart; + + return bRet; +} + +// --------------- +// - PolyScanner - +// --------------- + +PolyScanner::PolyScanner( const Rectangle& rRect ) +{ + if( !rRect.IsEmpty() ) + { + Rectangle aRect( rRect ); + sal_uLong nHeight; + + aRect.Justify(); + mnLeft = aRect.Left(); + mnTop = aRect.Top(); + mnRight = aRect.Right(); + mnBottom = aRect.Bottom(); + mpArray = new PolyScanline[ nHeight = Height() ]; + + for( sal_uLong i = 0UL; i < nHeight; i++ ) + mpArray[ i ].Set( mnLeft, mnRight ); + } + else + { + mnLeft = mnTop = mnRight = mnBottom = 0L; + mpArray = NULL; + } +} + +// ------------------------------------------------------------------------ + +PolyScanner::PolyScanner( const Polygon& rPoly ) +{ + const long nCount = rPoly.GetSize(); + + if( nCount ) + { + long nLast = nCount - 1; + Point aFirst( rPoly[ 0 ] ); + Point aLast( rPoly[ (sal_uInt16) nLast ] ); + + while( nLast && ( aLast == aFirst ) ) + aLast = rPoly[ (sal_uInt16) --nLast ]; + + if( !nLast ) + { + aLast = rPoly[ 0 ]; + mnLeft = mnRight = aLast.X(); + mnTop = mnBottom = aLast.Y(); + mpArray = new PolyScanline[ 1UL ]; + mpArray[ 0 ].Set( mnLeft, mnRight ); + } + else + { + const Rectangle aRect( rPoly.GetBoundRect() ); + sal_uLong nHeight; + + mnLeft = aRect.Left(); + mnTop = aRect.Top(); + mnRight = aRect.Right(); + mnBottom = aRect.Bottom(); + aLast = aFirst; + mpArray = new PolyScanline[ nHeight = Height() ]; + + for( long i = 1L; i <= nLast; i++ ) + { + const Point& rPt = rPoly[ (sal_uInt16) i ]; + + if( rPt != aLast ) + { + InsertLine( aLast, rPt ); + aLast = rPt; + } + } + + InsertLine( aLast, aFirst ); + } + } + else + mpArray = NULL; +} + +// ------------------------------------------------------------------------ + +PolyScanner::PolyScanner( const PolyPolygon& rPolyPoly ) +{ + mpArray = NULL; +} + +// ------------------------------------------------------------------------ + +PolyScanner::~PolyScanner() +{ + delete[] mpArray; +} + +// ------------------------------------------------------------------------ + +PolyScanline* PolyScanner::operator[]( sal_uLong nPos ) const +{ + DBG_ASSERT( nPos < Count(), "nPos out of range!" ); + return( mpArray ? ( mpArray + nPos ) : NULL ); +} + +// ------------------------------------------------------------------------ + +void PolyScanner::InsertLine( const Point& rStart, const Point& rEnd ) +{ + long nX, nY; + + if( rStart.Y() == rEnd.Y() ) + mpArray[ rStart.Y() - mnTop ].Insert( rStart.X() ); + else if( rStart.X() == rEnd.X() ) + { + // vertical line + const long nEndY = rEnd.Y(); + + nX = rStart.X(); + nY = rStart.Y(); + + if( nEndY > nY ) + while( nY < nEndY ) + mpArray[ nY++ - mnTop ].Insert( nX ); + else + while( nY > nEndY ) + mpArray[ nY-- - mnTop ].Insert( nX ); + } + else + { + const long nDX = labs( rEnd.X() - rStart.X() ); + const long nDY = labs( rEnd.Y() - rStart.Y() ); + const long nStartX = rStart.X(); + const long nStartY = rStart.Y(); + const long nEndX = rEnd.X(); + const long nEndY = rEnd.Y(); + const long nXInc = ( nStartX < nEndX ) ? 1L : -1L; + const long nYInc = ( nStartY < nEndY ) ? 1L : -1L; + long nLastX = nStartX; + long nLastY = nStartY; + sal_Bool bLast = sal_False; + + mpArray[ nStartY - mnTop ].Insert( nStartX ); + + if( nDX >= nDY ) + { + const long nDYX = ( nDY - nDX ) << 1; + const long nDY2 = nDY << 1; + long nD = nDY2 - nDX; + + for( nX = nStartX, nY = nLastY = nStartY; nX != nEndX; ) + { + if( nY != nLastY ) + { + if( bLast ) + mpArray[ nLastY - mnTop ].Insert( nLastX ); + + mpArray[ nY - mnTop ].Insert( nX ); + bLast = sal_False; + } + else + bLast = sal_True; + + nLastX = nX; + nLastY = nY; + + if( nD < 0L ) + nD += nDY2; + else + { + nD += nDYX; + nY += nYInc; + } + + nX += nXInc; + } + } + else + { + const long nDYX = ( nDX - nDY ) << 1; + const long nDY2 = nDX << 1; + long nD = nDY2 - nDY; + + for( nX = nStartX, nY = nStartY; nY != nEndY; ) + { + if( nY != nLastY ) + { + if( bLast ) + mpArray[ nLastY - mnTop ].Insert( nLastX ); + + mpArray[ nY - mnTop ].Insert( nX ); + bLast = sal_False; + } + else + bLast = sal_True; + + nLastX = nX; + nLastY = nY; + + if( nD < 0L ) + nD += nDY2; + else + { + nD += nDYX; + nX += nXInc; + } + + nY += nYInc; + } + } + + if( bLast ) + mpArray[ nLastY - mnTop ].Insert( nLastX ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/print.cxx b/vcl/source/gdi/print.cxx new file mode 100644 index 000000000000..15964d9da09f --- /dev/null +++ b/vcl/source/gdi/print.cxx @@ -0,0 +1,1631 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#define ENABLE_BYTESTRING_STREAM_OPERATORS +#include <list> + +#include <svsys.h> +#include <vcl/salinst.hxx> +#include <vcl/salgdi.hxx> +#include <vcl/salptype.hxx> +#include <vcl/salprn.hxx> + +#include <vcl/unohelp.hxx> +#include <tools/debug.hxx> +#include <tools/resary.hxx> +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <vcl/svdata.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/jobset.h> +#include <vcl/outdev.h> +#include <vcl/virdev.hxx> +#include <vcl/window.hxx> +#include <vcl/print.h> +#include <vcl/gdimtf.hxx> +#include <vcl/metaact.hxx> +#include <vcl/print.hxx> +#include <vcl/svids.hrc> + +#include <comphelper/processfactory.hxx> + +#include "com/sun/star/beans/XPropertySet.hpp" +#include "com/sun/star/container/XNameAccess.hpp" +#include "com/sun/star/lang/XMultiServiceFactory.hpp" + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; + +int nImplSysDialog = 0; + +// ======================================================================= + +namespace +{ + static Paper ImplGetPaperFormat( long nWidth100thMM, long nHeight100thMM ) + { + PaperInfo aInfo(nWidth100thMM, nHeight100thMM); + aInfo.doSloppyFit(); + return aInfo.getPaper(); + } + +// ----------------------------------------------------------------------- + + static const PaperInfo& ImplGetEmptyPaper() + { + static PaperInfo aInfo(PAPER_USER); + return aInfo; + } +} + +// ======================================================================= + +void ImplUpdateJobSetupPaper( JobSetup& rJobSetup ) +{ + const ImplJobSetup* pConstData = rJobSetup.ImplGetConstData(); + + if ( !pConstData->mnPaperWidth || !pConstData->mnPaperHeight ) + { + if ( pConstData->mePaperFormat != PAPER_USER ) + { + ImplJobSetup* pData = rJobSetup.ImplGetData(); + PaperInfo aInfo(pConstData->mePaperFormat); + pData->mnPaperWidth = aInfo.getWidth(); + pData->mnPaperHeight = aInfo.getHeight(); + } + } + else if ( pConstData->mePaperFormat == PAPER_USER ) + { + Paper ePaper = ImplGetPaperFormat( pConstData->mnPaperWidth, pConstData->mnPaperHeight ); + if ( ePaper != PAPER_USER ) + rJobSetup.ImplGetData()->mePaperFormat = ePaper; + } +} + +// ------------------ +// - PrinterOptions - +// ------------------ + +PrinterOptions::PrinterOptions() : + mbReduceTransparency( sal_False ), + meReducedTransparencyMode( PRINTER_TRANSPARENCY_AUTO ), + mbReduceGradients( sal_False ), + meReducedGradientsMode( PRINTER_GRADIENT_STRIPES ), + mnReducedGradientStepCount( 64 ), + mbReduceBitmaps( sal_False ), + meReducedBitmapMode( PRINTER_BITMAP_NORMAL ), + mnReducedBitmapResolution( 200 ), + mbReducedBitmapsIncludeTransparency( sal_True ), + mbConvertToGreyscales( sal_False ) +{ +} + +// ----------------------------------------------------------------------- + +PrinterOptions::~PrinterOptions() +{ +} + +#define PROPERTYNAME_REDUCETRANSPARENCY rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReduceTransparency")) +#define PROPERTYNAME_REDUCEDTRANSPARENCYMODE rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReducedTransparencyMode")) +#define PROPERTYNAME_REDUCEGRADIENTS rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReduceGradients")) +#define PROPERTYNAME_REDUCEDGRADIENTMODE rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReducedGradientMode")) +#define PROPERTYNAME_REDUCEDGRADIENTSTEPCOUNT rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReducedGradientStepCount")) +#define PROPERTYNAME_REDUCEBITMAPS rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReduceBitmaps")) +#define PROPERTYNAME_REDUCEDBITMAPMODE rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReducedBitmapMode")) +#define PROPERTYNAME_REDUCEDBITMAPRESOLUTION rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReducedBitmapResolution")) +#define PROPERTYNAME_REDUCEDBITMAPINCLUDESTRANSPARENCY rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReducedBitmapIncludesTransparency")) +#define PROPERTYNAME_CONVERTTOGREYSCALES rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ConvertToGreyscales")) + +bool PrinterOptions::ReadFromConfig( bool i_bFile ) +{ + bool bSuccess = false; + // save old state in case something goes wrong + PrinterOptions aOldValues( *this ); + + // get the configuration service + Reference< XMultiServiceFactory > xConfigProvider; + Reference< XNameAccess > xConfigAccess; + try + { + // get service provider + Reference< XMultiServiceFactory > xSMgr( comphelper::getProcessServiceFactory() ); + // create configuration hierachical access name + if( xSMgr.is() ) + { + try + { + xConfigProvider = Reference< XMultiServiceFactory >( + xSMgr->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.configuration.ConfigurationProvider" ))), + UNO_QUERY ); + if( xConfigProvider.is() ) + { + Sequence< Any > aArgs(1); + PropertyValue aVal; + aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "nodepath" ) ); + if( i_bFile ) + aVal.Value <<= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.Office.Common/Print/Option/File" ) ); + else + aVal.Value <<= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.Office.Common/Print/Option/Printer" ) ); + aArgs.getArray()[0] <<= aVal; + xConfigAccess = Reference< XNameAccess >( + xConfigProvider->createInstanceWithArguments( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationAccess" )), aArgs ), + UNO_QUERY ); + if( xConfigAccess.is() ) + { + Reference< XPropertySet > xSet( xConfigAccess, UNO_QUERY ); + if( xSet.is() ) + { + sal_Int32 nValue = 0; + sal_Bool bValue = 0; + if( xSet->getPropertyValue(PROPERTYNAME_REDUCETRANSPARENCY) >>= bValue ) + SetReduceTransparency( bValue ); + if( xSet->getPropertyValue(PROPERTYNAME_REDUCEDTRANSPARENCYMODE) >>= nValue ) + SetReducedTransparencyMode( (PrinterTransparencyMode)nValue ); + if( xSet->getPropertyValue(PROPERTYNAME_REDUCEGRADIENTS) >>= bValue ) + SetReduceGradients( bValue ); + if( xSet->getPropertyValue(PROPERTYNAME_REDUCEDGRADIENTMODE) >>= nValue ) + SetReducedGradientMode( (PrinterGradientMode)nValue ); + if( xSet->getPropertyValue(PROPERTYNAME_REDUCEDGRADIENTSTEPCOUNT) >>= nValue ) + SetReducedGradientStepCount( (sal_uInt16)nValue ); + if( xSet->getPropertyValue(PROPERTYNAME_REDUCEBITMAPS) >>= bValue ) + SetReduceBitmaps( bValue ); + if( xSet->getPropertyValue(PROPERTYNAME_REDUCEDBITMAPMODE) >>= nValue ) + SetReducedBitmapMode( (PrinterBitmapMode)nValue ); + if( xSet->getPropertyValue(PROPERTYNAME_REDUCEDBITMAPRESOLUTION) >>= nValue ) + SetReducedBitmapResolution( (sal_uInt16)nValue ); + if( xSet->getPropertyValue(PROPERTYNAME_REDUCEDBITMAPINCLUDESTRANSPARENCY) >>= bValue ) + SetReducedBitmapIncludesTransparency( bValue ); + if( xSet->getPropertyValue(PROPERTYNAME_CONVERTTOGREYSCALES) >>= bValue ) + SetConvertToGreyscales( bValue ); + + bSuccess = true; + } + } + } + } + catch( Exception& ) + { + } + } + } + catch( WrappedTargetException& ) + { + } + + if( ! bSuccess ) + *this = aOldValues; + return bSuccess; +} + +void Printer::SetPrinterOptions( const PrinterOptions& i_rOptions ) +{ + *mpPrinterOptions = i_rOptions; +} + +// ------------- +// - QueueInfo - +// ------------- + +QueueInfo::QueueInfo() +{ + mnStatus = 0; + mnJobs = 0; +} + +// ----------------------------------------------------------------------- + +QueueInfo::QueueInfo( const QueueInfo& rInfo ) : + maPrinterName( rInfo.maPrinterName ), + maDriver( rInfo.maDriver ), + maLocation( rInfo.maLocation ), + maComment( rInfo.maComment ), + mnStatus( rInfo.mnStatus ), + mnJobs( rInfo.mnJobs ) +{ +} + +// ----------------------------------------------------------------------- + +QueueInfo::~QueueInfo() +{ +} + +// ----------------------------------------------------------------------- + +bool QueueInfo::operator==( const QueueInfo& rInfo ) const +{ + return + maPrinterName == rInfo.maPrinterName && + maDriver == rInfo.maDriver && + maLocation == rInfo.maLocation && + maComment == rInfo.maComment && + mnStatus == rInfo.mnStatus && + mnJobs == rInfo.mnJobs; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const QueueInfo& rInfo ) +{ + VersionCompat aCompat( rOStream, STREAM_WRITE, 1 ); + + rOStream.WriteByteString( rInfo.maPrinterName, RTL_TEXTENCODING_UTF8 ); + rOStream.WriteByteString( rInfo.maDriver, RTL_TEXTENCODING_UTF8 ); + rOStream.WriteByteString( rInfo.maLocation, RTL_TEXTENCODING_UTF8 ); + rOStream.WriteByteString( rInfo.maComment, RTL_TEXTENCODING_UTF8 ); + rOStream << rInfo.mnStatus; + rOStream << rInfo.mnJobs; + + return rOStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStream, QueueInfo& rInfo ) +{ + VersionCompat aCompat( rIStream, STREAM_READ ); + + rIStream.ReadByteString( rInfo.maPrinterName, RTL_TEXTENCODING_UTF8 ); + rIStream.ReadByteString( rInfo.maDriver, RTL_TEXTENCODING_UTF8 ); + rIStream.ReadByteString( rInfo.maLocation, RTL_TEXTENCODING_UTF8 ); + rIStream.ReadByteString( rInfo.maComment, RTL_TEXTENCODING_UTF8 ); + rIStream >> rInfo.mnStatus; + rIStream >> rInfo.mnJobs; + + return rIStream; +} + +// ======================================================================= + +SalPrinterQueueInfo::SalPrinterQueueInfo() +{ + mnStatus = 0; + mnJobs = QUEUE_JOBS_DONTKNOW; + mpSysData = NULL; +} + +// ----------------------------------------------------------------------- + +SalPrinterQueueInfo::~SalPrinterQueueInfo() +{ +} + +// ----------------------------------------------------------------------- + +ImplPrnQueueList::~ImplPrnQueueList() +{ + ImplSVData* pSVData = ImplGetSVData(); + for( unsigned int i = 0; i < m_aQueueInfos.size(); i++ ) + { + delete m_aQueueInfos[i].mpQueueInfo; + pSVData->mpDefInst->DeletePrinterQueueInfo( m_aQueueInfos[i].mpSalQueueInfo ); + } +} + +// ----------------------------------------------------------------------- + +void ImplPrnQueueList::Add( SalPrinterQueueInfo* pData ) +{ + boost::unordered_map< rtl::OUString, sal_Int32, rtl::OUStringHash >::iterator it = + m_aNameToIndex.find( pData->maPrinterName ); + if( it == m_aNameToIndex.end() ) + { + m_aNameToIndex[ pData->maPrinterName ] = m_aQueueInfos.size(); + m_aQueueInfos.push_back( ImplPrnQueueData() ); + m_aQueueInfos.back().mpQueueInfo = NULL; + m_aQueueInfos.back().mpSalQueueInfo = pData; + m_aPrinterList.push_back( pData->maPrinterName ); + } + else // this should not happen, but ... + { + ImplPrnQueueData& rData = m_aQueueInfos[ it->second ]; + delete rData.mpQueueInfo; + rData.mpQueueInfo = NULL; + ImplGetSVData()->mpDefInst->DeletePrinterQueueInfo( rData.mpSalQueueInfo ); + rData.mpSalQueueInfo = pData; + } +} + +// ----------------------------------------------------------------------- + +ImplPrnQueueData* ImplPrnQueueList::Get( const rtl::OUString& rPrinter ) +{ + ImplPrnQueueData* pData = NULL; + boost::unordered_map<rtl::OUString,sal_Int32,rtl::OUStringHash>::iterator it = + m_aNameToIndex.find( rPrinter ); + if( it != m_aNameToIndex.end() ) + pData = &m_aQueueInfos[it->second]; + return pData; +} + +// ======================================================================= + +static void ImplInitPrnQueueList() +{ + ImplSVData* pSVData = ImplGetSVData(); + + pSVData->maGDIData.mpPrinterQueueList = new ImplPrnQueueList; + + static const char* pEnv = getenv( "SAL_DISABLE_PRINTERLIST" ); + if( !pEnv || !*pEnv ) + pSVData->mpDefInst->GetPrinterQueueInfo( pSVData->maGDIData.mpPrinterQueueList ); +} + +// ----------------------------------------------------------------------- + +void ImplDeletePrnQueueList() +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplPrnQueueList* pPrnList = pSVData->maGDIData.mpPrinterQueueList; + + if ( pPrnList ) + { + delete pPrnList; + pSVData->maGDIData.mpPrinterQueueList = NULL; + } +} + +// ----------------------------------------------------------------------- + +const std::vector<rtl::OUString>& Printer::GetPrinterQueues() +{ + ImplSVData* pSVData = ImplGetSVData(); + if ( !pSVData->maGDIData.mpPrinterQueueList ) + ImplInitPrnQueueList(); + return pSVData->maGDIData.mpPrinterQueueList->m_aPrinterList; +} + +// ----------------------------------------------------------------------- +const QueueInfo* Printer::GetQueueInfo( const String& rPrinterName, bool bStatusUpdate ) +{ + ImplSVData* pSVData = ImplGetSVData(); + + if ( !pSVData->maGDIData.mpPrinterQueueList ) + ImplInitPrnQueueList(); + + ImplPrnQueueData* pInfo = pSVData->maGDIData.mpPrinterQueueList->Get( rPrinterName ); + if( pInfo ) + { + if( !pInfo->mpQueueInfo || bStatusUpdate ) + pSVData->mpDefInst->GetPrinterQueueState( pInfo->mpSalQueueInfo ); + + if ( !pInfo->mpQueueInfo ) + pInfo->mpQueueInfo = new QueueInfo; + + pInfo->mpQueueInfo->maPrinterName = pInfo->mpSalQueueInfo->maPrinterName; + pInfo->mpQueueInfo->maDriver = pInfo->mpSalQueueInfo->maDriver; + pInfo->mpQueueInfo->maLocation = pInfo->mpSalQueueInfo->maLocation; + pInfo->mpQueueInfo->maComment = pInfo->mpSalQueueInfo->maComment; + pInfo->mpQueueInfo->mnStatus = pInfo->mpSalQueueInfo->mnStatus; + pInfo->mpQueueInfo->mnJobs = pInfo->mpSalQueueInfo->mnJobs; + return pInfo->mpQueueInfo; + } + return NULL; +} + +// ----------------------------------------------------------------------- + +XubString Printer::GetDefaultPrinterName() +{ + static const char* pEnv = getenv( "SAL_DISABLE_DEFAULTPRINTER" ); + if( !pEnv || !*pEnv ) + { + ImplSVData* pSVData = ImplGetSVData(); + + return pSVData->mpDefInst->GetDefaultPrinter(); + } + return XubString(); +} + +// ======================================================================= + +void Printer::ImplInitData() +{ + mbDevOutput = sal_False; + meOutDevType = OUTDEV_PRINTER; + mbDefPrinter = sal_False; + mnError = 0; + mnCurPage = 0; + mnCurPrintPage = 0; + mnPageQueueSize = 0; + mnCopyCount = 1; + mbCollateCopy = sal_False; + mbPrinting = sal_False; + mbJobActive = sal_False; + mbPrintFile = sal_False; + mbInPrintPage = sal_False; + mbNewJobSetup = sal_False; + mpInfoPrinter = NULL; + mpPrinter = NULL; + mpDisplayDev = NULL; + mbIsQueuePrinter = sal_False; + mpPrinterOptions = new PrinterOptions; + + // Printer in die Liste eintragen + ImplSVData* pSVData = ImplGetSVData(); + mpNext = pSVData->maGDIData.mpFirstPrinter; + mpPrev = NULL; + if ( mpNext ) + mpNext->mpPrev = this; + else + pSVData->maGDIData.mpLastPrinter = this; + pSVData->maGDIData.mpFirstPrinter = this; +} + +// ----------------------------------------------------------------------- + +void Printer::ImplInit( SalPrinterQueueInfo* pInfo ) +{ + ImplSVData* pSVData = ImplGetSVData(); + // #i74084# update info for this specific SalPrinterQueueInfo + pSVData->mpDefInst->GetPrinterQueueState( pInfo ); + + // Testen, ob Treiber ueberhaupt mit dem JobSetup uebereinstimmt + ImplJobSetup* pJobSetup = maJobSetup.ImplGetData(); + + if ( pJobSetup->mpDriverData ) + { + if ( (pJobSetup->maPrinterName != pInfo->maPrinterName) || + (pJobSetup->maDriver != pInfo->maDriver) ) + { + rtl_freeMemory( pJobSetup->mpDriverData ); + pJobSetup->mpDriverData = NULL; + pJobSetup->mnDriverDataLen = 0; + } + } + + // Printernamen merken + maPrinterName = pInfo->maPrinterName; + maDriver = pInfo->maDriver; + + // In JobSetup den Printernamen eintragen + pJobSetup->maPrinterName = maPrinterName; + pJobSetup->maDriver = maDriver; + + mpInfoPrinter = pSVData->mpDefInst->CreateInfoPrinter( pInfo, pJobSetup ); + mpPrinter = NULL; + mpJobGraphics = NULL; + ImplUpdateJobSetupPaper( maJobSetup ); + + if ( !mpInfoPrinter ) + { + ImplInitDisplay( NULL ); + return; + } + + // we need a graphics + if ( !ImplGetGraphics() ) + { + ImplInitDisplay( NULL ); + return; + } + + // Daten initialisieren + ImplUpdatePageData(); + mpFontList = new ImplDevFontList(); + mpFontCache = new ImplFontCache( sal_True ); + mpGraphics->GetDevFontList( mpFontList ); +} + +// ----------------------------------------------------------------------- + +void Printer::ImplInitDisplay( const Window* pWindow ) +{ + ImplSVData* pSVData = ImplGetSVData(); + + mpInfoPrinter = NULL; + mpPrinter = NULL; + mpJobGraphics = NULL; + + if ( pWindow ) + mpDisplayDev = new VirtualDevice( *pWindow ); + else + mpDisplayDev = new VirtualDevice(); + mpFontList = pSVData->maGDIData.mpScreenFontList; + mpFontCache = pSVData->maGDIData.mpScreenFontCache; + mnDPIX = mpDisplayDev->mnDPIX; + mnDPIY = mpDisplayDev->mnDPIY; +} + +// ----------------------------------------------------------------------- + +SalPrinterQueueInfo* Printer::ImplGetQueueInfo( const XubString& rPrinterName, + const XubString* pDriver ) +{ + ImplSVData* pSVData = ImplGetSVData(); + if ( !pSVData->maGDIData.mpPrinterQueueList ) + ImplInitPrnQueueList(); + + ImplPrnQueueList* pPrnList = pSVData->maGDIData.mpPrinterQueueList; + if ( pPrnList && pPrnList->m_aQueueInfos.size() ) + { + // first search for the printer name driectly + ImplPrnQueueData* pInfo = pPrnList->Get( rPrinterName ); + if( pInfo ) + return pInfo->mpSalQueueInfo; + + // then search case insensitive + for( unsigned int i = 0; i < pPrnList->m_aQueueInfos.size(); i++ ) + { + if( pPrnList->m_aQueueInfos[i].mpSalQueueInfo->maPrinterName.EqualsIgnoreCaseAscii( rPrinterName ) ) + return pPrnList->m_aQueueInfos[i].mpSalQueueInfo; + } + + // then search for driver name + if ( pDriver ) + { + for( unsigned int i = 0; i < pPrnList->m_aQueueInfos.size(); i++ ) + { + if( pPrnList->m_aQueueInfos[i].mpSalQueueInfo->maDriver == *pDriver ) + return pPrnList->m_aQueueInfos[i].mpSalQueueInfo; + } + } + + // then the default printer + pInfo = pPrnList->Get( GetDefaultPrinterName() ); + if( pInfo ) + return pInfo->mpSalQueueInfo; + + // last chance: the first available printer + return pPrnList->m_aQueueInfos[0].mpSalQueueInfo; + } + + return NULL; +} + +// ----------------------------------------------------------------------- + +void Printer::ImplUpdatePageData() +{ + // we need a graphics + if ( !ImplGetGraphics() ) + return; + + mpGraphics->GetResolution( mnDPIX, mnDPIY ); + mpInfoPrinter->GetPageInfo( maJobSetup.ImplGetConstData(), + mnOutWidth, mnOutHeight, + maPageOffset.X(), maPageOffset.Y(), + maPaperSize.Width(), maPaperSize.Height() ); + static const char* pDebugOffset = getenv( "SAL_DBG_PAGEOFFSET" ); + if( pDebugOffset ) + { + rtl::OString aLine( pDebugOffset ); + sal_Int32 nIndex = 0; + rtl::OString aToken( aLine.getToken( 0, ',', nIndex ) ); + sal_Int32 nLeft = aToken.toInt32(); + sal_Int32 nTop = nLeft; + if( nIndex > 0 ) + { + aToken = aLine.getToken( 0, ',', nIndex ); + nTop = aToken.toInt32(); + } + maPageOffset = LogicToPixel( Point( static_cast<long>(nLeft), + static_cast<long>(nTop) ), + MapMode( MAP_100TH_MM ) + ); + mnOutWidth = maPaperSize.Width() - 2*maPageOffset.X(); + mnOutWidth = maPaperSize.Width() - 2*maPageOffset.Y(); + } +} + +// ----------------------------------------------------------------------- + +void Printer::ImplUpdateFontList() +{ + ImplUpdateFontData( sal_True ); +} + +// ----------------------------------------------------------------------- + +Printer::Printer() +{ + ImplInitData(); + SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( GetDefaultPrinterName(), NULL ); + if ( pInfo ) + { + ImplInit( pInfo ); + if ( !IsDisplayPrinter() ) + mbDefPrinter = sal_True; + } + else + ImplInitDisplay( NULL ); +} + +// ----------------------------------------------------------------------- + +Printer::Printer( const Window* pWindow ) +{ + ImplInitData(); + ImplInitDisplay( pWindow ); +} + +// ----------------------------------------------------------------------- + +Printer::Printer( const JobSetup& rJobSetup ) : + maJobSetup( rJobSetup ) +{ + ImplInitData(); + SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( rJobSetup.mpData->maPrinterName, + &rJobSetup.mpData->maDriver ); + if ( pInfo ) + { + ImplInit( pInfo ); + SetJobSetup( rJobSetup ); + } + else + { + ImplInitDisplay( NULL ); + maJobSetup = JobSetup(); + } +} + +// ----------------------------------------------------------------------- + +Printer::Printer( const QueueInfo& rQueueInfo ) +{ + ImplInitData(); + SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( rQueueInfo.GetPrinterName(), + &rQueueInfo.GetDriver() ); + if ( pInfo ) + ImplInit( pInfo ); + else + ImplInitDisplay( NULL ); +} + +// ----------------------------------------------------------------------- + +Printer::Printer( const XubString& rPrinterName ) +{ + ImplInitData(); + SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( rPrinterName, NULL ); + if ( pInfo ) + ImplInit( pInfo ); + else + ImplInitDisplay( NULL ); +} + +// ----------------------------------------------------------------------- + +Printer::~Printer() +{ + DBG_ASSERT( !IsPrinting(), "Printer::~Printer() - Job is printing" ); + DBG_ASSERT( !IsJobActive(), "Printer::~Printer() - Job is active" ); + + delete mpPrinterOptions; + + ImplReleaseGraphics(); + if ( mpInfoPrinter ) + ImplGetSVData()->mpDefInst->DestroyInfoPrinter( mpInfoPrinter ); + if ( mpDisplayDev ) + delete mpDisplayDev; + else + { + // OutputDevice-Dtor versucht das gleiche, deshalb muss hier + // der FontEntry auch auf NULL gesetzt werden + // TODO: consolidate duplicate cleanup by Printer and OutputDevice + if ( mpFontEntry ) + { + mpFontCache->Release( mpFontEntry ); + mpFontEntry = NULL; + } + if ( mpGetDevFontList ) + { + delete mpGetDevFontList; + mpGetDevFontList = NULL; + } + if ( mpGetDevSizeList ) + { + delete mpGetDevSizeList; + mpGetDevSizeList = NULL; + } + delete mpFontCache; + mpFontCache = NULL; + // font list deleted by OutputDevice dtor + } + + // Printer aus der Liste eintragen + ImplSVData* pSVData = ImplGetSVData(); + if ( mpPrev ) + mpPrev->mpNext = mpNext; + else + pSVData->maGDIData.mpFirstPrinter = mpNext; + if ( mpNext ) + mpNext->mpPrev = mpPrev; + else + pSVData->maGDIData.mpLastPrinter = mpPrev; +} + +// ----------------------------------------------------------------------- +void Printer::Compat_OldPrinterMetrics( bool bSet ) +{ + // propagate flag + if( mpInfoPrinter ) + mpInfoPrinter->m_bCompatMetrics = bSet; + + // get new font data + ImplUpdateFontData( sal_True ); +} + +// ----------------------------------------------------------------------- + +sal_uLong Printer::GetCapabilities( sal_uInt16 nType ) const +{ + if ( IsDisplayPrinter() ) + return sal_False; + + if( mpInfoPrinter ) + return mpInfoPrinter->GetCapabilities( maJobSetup.ImplGetConstData(), nType ); + else + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::HasSupport( PrinterSupport eFeature ) const +{ + switch ( eFeature ) + { + case SUPPORT_SET_ORIENTATION: + return (sal_Bool)GetCapabilities( PRINTER_CAPABILITIES_SETORIENTATION ); + case SUPPORT_SET_PAPERBIN: + return (sal_Bool)GetCapabilities( PRINTER_CAPABILITIES_SETPAPERBIN ); + case SUPPORT_SET_PAPERSIZE: + return (sal_Bool)GetCapabilities( PRINTER_CAPABILITIES_SETPAPERSIZE ); + case SUPPORT_SET_PAPER: + return (sal_Bool)GetCapabilities( PRINTER_CAPABILITIES_SETPAPER ); + case SUPPORT_COPY: + return (GetCapabilities( PRINTER_CAPABILITIES_COPIES ) != 0); + case SUPPORT_COLLATECOPY: + return (GetCapabilities( PRINTER_CAPABILITIES_COLLATECOPIES ) != 0); + case SUPPORT_SETUPDIALOG: + return (sal_Bool)GetCapabilities( PRINTER_CAPABILITIES_SUPPORTDIALOG ); + case SUPPORT_FAX: + return (sal_Bool) GetCapabilities( PRINTER_CAPABILITIES_FAX ); + case SUPPORT_PDF: + return (sal_Bool) GetCapabilities( PRINTER_CAPABILITIES_PDF ); + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::SetJobSetup( const JobSetup& rSetup ) +{ + if ( IsDisplayPrinter() || mbInPrintPage ) + return sal_False; + + JobSetup aJobSetup = rSetup; + + ImplReleaseGraphics(); + if ( mpInfoPrinter->SetPrinterData( aJobSetup.ImplGetData() ) ) + { + ImplUpdateJobSetupPaper( aJobSetup ); + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + ImplUpdatePageData(); + ImplUpdateFontList(); + return sal_True; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + + +sal_Bool Printer::Setup( Window* pWindow ) +{ + if ( IsDisplayPrinter() ) + return sal_False; + + if ( IsJobActive() || IsPrinting() ) + return sal_False; + + JobSetup aJobSetup = maJobSetup; + SalFrame* pFrame; + if ( !pWindow ) + pWindow = ImplGetDefaultWindow(); + if( !pWindow ) + return sal_False; + + pFrame = pWindow->ImplGetFrame(); + ImplReleaseGraphics(); + ImplSVData* pSVData = ImplGetSVData(); + pSVData->maAppData.mnModalMode++; + nImplSysDialog++; + sal_Bool bSetup = mpInfoPrinter->Setup( pFrame, aJobSetup.ImplGetData() ); + pSVData->maAppData.mnModalMode--; + nImplSysDialog--; + if ( bSetup ) + { + ImplUpdateJobSetupPaper( aJobSetup ); + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + ImplUpdatePageData(); + ImplUpdateFontList(); + return sal_True; + } + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::SetPrinterProps( const Printer* pPrinter ) +{ + if ( IsJobActive() || IsPrinting() ) + return sal_False; + + ImplSVData* pSVData = ImplGetSVData(); + + mbDefPrinter = pPrinter->mbDefPrinter; + maPrintFile = pPrinter->maPrintFile; + mbPrintFile = pPrinter->mbPrintFile; + mnCopyCount = pPrinter->mnCopyCount; + mbCollateCopy = pPrinter->mbCollateCopy; + mnPageQueueSize = pPrinter->mnPageQueueSize; + *mpPrinterOptions = *pPrinter->mpPrinterOptions; + + if ( pPrinter->IsDisplayPrinter() ) + { + // Alten Printer zerstoeren + if ( !IsDisplayPrinter() ) + { + ImplReleaseGraphics(); + pSVData->mpDefInst->DestroyInfoPrinter( mpInfoPrinter ); + if ( mpFontEntry ) + { + mpFontCache->Release( mpFontEntry ); + mpFontEntry = NULL; + } + if ( mpGetDevFontList ) + { + delete mpGetDevFontList; + mpGetDevFontList = NULL; + } + if ( mpGetDevSizeList ) + { + delete mpGetDevSizeList; + mpGetDevSizeList = NULL; + } + // clean up font list + delete mpFontCache; + delete mpFontList; + mpFontCache = NULL; + mpFontList = NULL; + + mbInitFont = sal_True; + mbNewFont = sal_True; + mpInfoPrinter = NULL; + } + + // Neuen Printer bauen + ImplInitDisplay( NULL ); + return sal_True; + } + + // Alten Printer zerstoeren? + if ( GetName() != pPrinter->GetName() ) + { + ImplReleaseGraphics(); + if ( mpDisplayDev ) + { + delete mpDisplayDev; + mpDisplayDev = NULL; + } + else + { + pSVData->mpDefInst->DestroyInfoPrinter( mpInfoPrinter ); + + if ( mpFontEntry ) + { + mpFontCache->Release( mpFontEntry ); + mpFontEntry = NULL; + } + if ( mpGetDevFontList ) + { + delete mpGetDevFontList; + mpGetDevFontList = NULL; + } + if ( mpGetDevSizeList ) + { + delete mpGetDevSizeList; + mpGetDevSizeList = NULL; + } + delete mpFontCache; + delete mpFontList; + mpFontCache = NULL; + mpFontList = NULL; + mbInitFont = sal_True; + mbNewFont = sal_True; + mpInfoPrinter = NULL; + } + + // Neuen Printer bauen + XubString aDriver = pPrinter->GetDriverName(); + SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( pPrinter->GetName(), &aDriver ); + if ( pInfo ) + { + ImplInit( pInfo ); + SetJobSetup( pPrinter->GetJobSetup() ); + } + else + ImplInitDisplay( NULL ); + } + else + SetJobSetup( pPrinter->GetJobSetup() ); + + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::SetOrientation( Orientation eOrientation ) +{ + if ( mbInPrintPage ) + return sal_False; + + if ( maJobSetup.ImplGetConstData()->meOrientation != eOrientation ) + { + JobSetup aJobSetup = maJobSetup; + ImplJobSetup* pSetupData = aJobSetup.ImplGetData(); + pSetupData->meOrientation = eOrientation; + + if ( IsDisplayPrinter() ) + { + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + return sal_True; + } + + ImplReleaseGraphics(); + if ( mpInfoPrinter->SetData( SAL_JOBSET_ORIENTATION, pSetupData ) ) + { + ImplUpdateJobSetupPaper( aJobSetup ); + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + ImplUpdatePageData(); + ImplUpdateFontList(); + return sal_True; + } + else + return sal_False; + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +Orientation Printer::GetOrientation() const +{ + return maJobSetup.ImplGetConstData()->meOrientation; +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::SetPaperBin( sal_uInt16 nPaperBin ) +{ + if ( mbInPrintPage ) + return sal_False; + + if ( (maJobSetup.ImplGetConstData()->mnPaperBin != nPaperBin) && + (nPaperBin < GetPaperBinCount()) ) + { + JobSetup aJobSetup = maJobSetup; + ImplJobSetup* pSetupData = aJobSetup.ImplGetData(); + pSetupData->mnPaperBin = nPaperBin; + + if ( IsDisplayPrinter() ) + { + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + return sal_True; + } + + ImplReleaseGraphics(); + if ( mpInfoPrinter->SetData( SAL_JOBSET_PAPERBIN, pSetupData ) ) + { + ImplUpdateJobSetupPaper( aJobSetup ); + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + ImplUpdatePageData(); + ImplUpdateFontList(); + return sal_True; + } + else + return sal_False; + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +sal_uInt16 Printer::GetPaperBin() const +{ + return maJobSetup.ImplGetConstData()->mnPaperBin; +} + +// ----------------------------------------------------------------------- + +// Map user paper format to a available printer paper formats +void Printer::ImplFindPaperFormatForUserSize( JobSetup& aJobSetup, bool bMatchNearest ) +{ + ImplJobSetup* pSetupData = aJobSetup.ImplGetData(); + + int nLandscapeAngle = GetLandscapeAngle(); + int nPaperCount = GetPaperInfoCount(); + bool bFound = false; + + PaperInfo aInfo(pSetupData->mnPaperWidth, pSetupData->mnPaperHeight); + + // Alle Papierformate vergleichen und ein passendes raussuchen + for ( int i = 0; i < nPaperCount; i++ ) + { + const PaperInfo& rPaperInfo = GetPaperInfo( i ); + + if ( aInfo.sloppyEqual(rPaperInfo) ) + { + pSetupData->mePaperFormat = ImplGetPaperFormat( rPaperInfo.getWidth(), + rPaperInfo.getHeight() ); + pSetupData->meOrientation = ORIENTATION_PORTRAIT; + bFound = true; + break; + } + } + + // If the printer supports landscape orientation, check paper sizes again + // with landscape orientation. This is necessary as a printer driver provides + // all paper sizes with portrait orientation only!! + if ( pSetupData->mePaperFormat == PAPER_USER && + nLandscapeAngle != 0 && + HasSupport( SUPPORT_SET_ORIENTATION )) + { + + PaperInfo aRotatedInfo(pSetupData->mnPaperHeight, pSetupData->mnPaperWidth); + + for ( int i = 0; i < nPaperCount; i++ ) + { + const PaperInfo& rPaperInfo = GetPaperInfo( i ); + + if ( aRotatedInfo.sloppyEqual( rPaperInfo ) ) + { + pSetupData->mePaperFormat = ImplGetPaperFormat( rPaperInfo.getWidth(), + rPaperInfo.getHeight() ); + pSetupData->meOrientation = ORIENTATION_LANDSCAPE; + bFound = true; + break; + } + } + } + + if( ! bFound && bMatchNearest ) + { + sal_Int64 nBestMatch = SAL_MAX_INT64; + int nBestIndex = 0; + Orientation eBestOrientation = ORIENTATION_PORTRAIT; + for( int i = 0; i < nPaperCount; i++ ) + { + const PaperInfo& rPaperInfo = GetPaperInfo( i ); + + // check protrait match + sal_Int64 nDX = pSetupData->mnPaperWidth - rPaperInfo.getWidth(); + sal_Int64 nDY = pSetupData->mnPaperHeight - rPaperInfo.getHeight(); + sal_Int64 nMatch = nDX*nDX + nDY*nDY; + if( nMatch < nBestMatch ) + { + nBestMatch = nMatch; + nBestIndex = i; + eBestOrientation = ORIENTATION_PORTRAIT; + } + + // check landscape match + nDX = pSetupData->mnPaperWidth - rPaperInfo.getHeight(); + nDY = pSetupData->mnPaperHeight - rPaperInfo.getWidth(); + nMatch = nDX*nDX + nDY*nDY; + if( nMatch < nBestMatch ) + { + nBestMatch = nMatch; + nBestIndex = i; + eBestOrientation = ORIENTATION_LANDSCAPE; + } + } + const PaperInfo& rBestInfo = GetPaperInfo( nBestIndex ); + pSetupData->mePaperFormat = ImplGetPaperFormat( rBestInfo.getWidth(), + rBestInfo.getHeight() ); + pSetupData->meOrientation = eBestOrientation; + } +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::SetPaper( Paper ePaper ) +{ + if ( mbInPrintPage ) + return sal_False; + + if ( maJobSetup.ImplGetConstData()->mePaperFormat != ePaper ) + { + JobSetup aJobSetup = maJobSetup; + ImplJobSetup* pSetupData = aJobSetup.ImplGetData(); + pSetupData->mePaperFormat = ePaper; + if ( ePaper != PAPER_USER ) + { + PaperInfo aInfo(ePaper); + pSetupData->mnPaperWidth = aInfo.getWidth(); + pSetupData->mnPaperHeight = aInfo.getHeight(); + } + + if ( IsDisplayPrinter() ) + { + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + return sal_True; + } + + ImplReleaseGraphics(); + if ( ePaper == PAPER_USER ) + ImplFindPaperFormatForUserSize( aJobSetup, false ); + if ( mpInfoPrinter->SetData( SAL_JOBSET_PAPERSIZE|SAL_JOBSET_ORIENTATION, pSetupData ) ) + { + ImplUpdateJobSetupPaper( aJobSetup ); + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + ImplUpdatePageData(); + ImplUpdateFontList(); + return sal_True; + } + else + return sal_False; + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::SetPaperSizeUser( const Size& rSize ) +{ + return SetPaperSizeUser( rSize, false ); +} + +sal_Bool Printer::SetPaperSizeUser( const Size& rSize, bool bMatchNearest ) +{ + if ( mbInPrintPage ) + return sal_False; + + Size aPixSize = LogicToPixel( rSize ); + Size aPageSize = PixelToLogic( aPixSize, MAP_100TH_MM ); + if ( (maJobSetup.ImplGetConstData()->mePaperFormat != PAPER_USER) || + (maJobSetup.ImplGetConstData()->mnPaperWidth != aPageSize.Width()) || + (maJobSetup.ImplGetConstData()->mnPaperHeight != aPageSize.Height()) ) + { + JobSetup aJobSetup = maJobSetup; + ImplJobSetup* pSetupData = aJobSetup.ImplGetData(); + pSetupData->mePaperFormat = PAPER_USER; + pSetupData->mnPaperWidth = aPageSize.Width(); + pSetupData->mnPaperHeight = aPageSize.Height(); + + if ( IsDisplayPrinter() ) + { + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + return sal_True; + } + + ImplReleaseGraphics(); + ImplFindPaperFormatForUserSize( aJobSetup, bMatchNearest ); + + // Changing the paper size can also change the orientation! + if ( mpInfoPrinter->SetData( SAL_JOBSET_PAPERSIZE|SAL_JOBSET_ORIENTATION, pSetupData ) ) + { + ImplUpdateJobSetupPaper( aJobSetup ); + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + ImplUpdatePageData(); + ImplUpdateFontList(); + return sal_True; + } + else + return sal_False; + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +int Printer::GetPaperInfoCount() const +{ + if( ! mpInfoPrinter ) + return 0; + if( ! mpInfoPrinter->m_bPapersInit ) + mpInfoPrinter->InitPaperFormats( maJobSetup.ImplGetConstData() ); + return mpInfoPrinter->m_aPaperFormats.size(); +} + +// ----------------------------------------------------------------------- + +rtl::OUString Printer::GetPaperName( Paper ePaper ) +{ + ImplSVData* pSVData = ImplGetSVData(); + if( ! pSVData->mpPaperNames ) + { + pSVData->mpPaperNames = new boost::unordered_map< int, rtl::OUString >(); + if( ImplGetResMgr() ) + { + ResStringArray aPaperStrings( VclResId( RID_STR_PAPERNAMES ) ); + static const int PaperIndex[] = + { + PAPER_A0, PAPER_A1, PAPER_A2, PAPER_A3, PAPER_A4, PAPER_A5, + PAPER_B4_ISO, PAPER_B5_ISO, PAPER_LETTER, PAPER_LEGAL, PAPER_TABLOID, + PAPER_USER, PAPER_B6_ISO, PAPER_ENV_C4, PAPER_ENV_C5, PAPER_ENV_C6, PAPER_ENV_C65, + PAPER_ENV_DL, PAPER_SLIDE_DIA, PAPER_SCREEN, PAPER_C, PAPER_D, PAPER_E, + PAPER_EXECUTIVE, PAPER_FANFOLD_LEGAL_DE, PAPER_ENV_MONARCH, PAPER_ENV_PERSONAL, + PAPER_ENV_9, PAPER_ENV_10, PAPER_ENV_11, PAPER_ENV_12, PAPER_KAI16, + PAPER_KAI32, PAPER_KAI32BIG, PAPER_B4_JIS, PAPER_B5_JIS, PAPER_B6_JIS + }; + OSL_ENSURE( sal_uInt32(sizeof(PaperIndex)/sizeof(PaperIndex[0])) == aPaperStrings.Count(), "localized paper name count wrong" ); + for( int i = 0; i < int(sizeof(PaperIndex)/sizeof(PaperIndex[0])); i++ ) + (*pSVData->mpPaperNames)[PaperIndex[i]] = aPaperStrings.GetString(i); + } + } + + boost::unordered_map<int,rtl::OUString>::const_iterator it = pSVData->mpPaperNames->find( (int)ePaper ); + return (it != pSVData->mpPaperNames->end()) ? it->second : rtl::OUString(); +} + +// ----------------------------------------------------------------------- + +rtl::OUString Printer::GetPaperName( bool i_bPaperUser ) const +{ + Size aPageSize = PixelToLogic( GetPaperSizePixel(), MAP_100TH_MM ); + Paper ePaper = ImplGetPaperFormat( aPageSize.Width(), aPageSize.Height() ); + if( ePaper == PAPER_USER ) + ePaper = ImplGetPaperFormat( aPageSize.Height(), aPageSize.Width() ); + return (ePaper != PAPER_USER || i_bPaperUser ) ? GetPaperName( ePaper ) : rtl::OUString(); +} + +// ----------------------------------------------------------------------- + +const PaperInfo& Printer::GetPaperInfo( int nPaper ) const +{ + if( ! mpInfoPrinter ) + return ImplGetEmptyPaper(); + if( ! mpInfoPrinter->m_bPapersInit ) + mpInfoPrinter->InitPaperFormats( maJobSetup.ImplGetConstData() ); + if( mpInfoPrinter->m_aPaperFormats.empty() || nPaper < 0 || nPaper >= int(mpInfoPrinter->m_aPaperFormats.size()) ) + return ImplGetEmptyPaper(); + return mpInfoPrinter->m_aPaperFormats[nPaper]; +} + +// ----------------------------------------------------------------------- + +DuplexMode Printer::GetDuplexMode() const +{ + return maJobSetup.ImplGetConstData()->meDuplexMode; +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::SetDuplexMode( DuplexMode eDuplex ) +{ + if ( mbInPrintPage ) + return sal_False; + + if ( maJobSetup.ImplGetConstData()->meDuplexMode != eDuplex ) + { + JobSetup aJobSetup = maJobSetup; + ImplJobSetup* pSetupData = aJobSetup.ImplGetData(); + pSetupData->meDuplexMode = eDuplex; + + if ( IsDisplayPrinter() ) + { + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + return sal_True; + } + + ImplReleaseGraphics(); + if ( mpInfoPrinter->SetData( SAL_JOBSET_DUPLEXMODE, pSetupData ) ) + { + ImplUpdateJobSetupPaper( aJobSetup ); + mbNewJobSetup = sal_True; + maJobSetup = aJobSetup; + ImplUpdatePageData(); + ImplUpdateFontList(); + return sal_True; + } + else + return sal_False; + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +int Printer::GetLandscapeAngle() const +{ + return mpInfoPrinter ? mpInfoPrinter->GetLandscapeAngle( maJobSetup.ImplGetConstData() ) : 900; +} + +// ----------------------------------------------------------------------- + +Paper Printer::GetPaper() const +{ + return maJobSetup.ImplGetConstData()->mePaperFormat; +} + +// ----------------------------------------------------------------------- + +sal_uInt16 Printer::GetPaperBinCount() const +{ + if ( IsDisplayPrinter() ) + return 0; + + return (sal_uInt16)mpInfoPrinter->GetPaperBinCount( maJobSetup.ImplGetConstData() ); +} + +// ----------------------------------------------------------------------- + +XubString Printer::GetPaperBinName( sal_uInt16 nPaperBin ) const +{ + if ( IsDisplayPrinter() ) + return ImplGetSVEmptyStr(); + + if ( nPaperBin < GetPaperBinCount() ) + return mpInfoPrinter->GetPaperBinName( maJobSetup.ImplGetConstData(), nPaperBin ); + else + return ImplGetSVEmptyStr(); +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::SetCopyCount( sal_uInt16 nCopy, sal_Bool bCollate ) +{ + mnCopyCount = nCopy; + mbCollateCopy = bCollate; + return sal_True; +} + +// ----------------------------------------------------------------------- + +void Printer::Error() +{ + maErrorHdl.Call( this ); +} + +// ----------------------------------------------------------------------- + + +sal_uLong Printer::ImplSalPrinterErrorCodeToVCL( sal_uLong nError ) +{ + sal_uLong nVCLError; + switch ( nError ) + { + case 0: + nVCLError = PRINTER_OK; + break; + case SAL_PRINTER_ERROR_ABORT: + nVCLError = PRINTER_ABORT; + break; + default: + nVCLError = PRINTER_GENERALERROR; + break; + } + + return nVCLError; +} + +// ----------------------------------------------------------------------- + +void Printer::ImplEndPrint() +{ + mbPrinting = sal_False; + mnCurPrintPage = 0; + maJobName.Erase(); +} + +// ----------------------------------------------------------------------- + +IMPL_LINK( Printer, ImplDestroyPrinterAsync, void*, pSalPrinter ) +{ + SalPrinter* pPrinter = (SalPrinter*)pSalPrinter; + ImplSVData* pSVData = ImplGetSVData(); + pSVData->mpDefInst->DestroyPrinter( pPrinter ); + return 0; +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::EndJob() +{ + sal_Bool bRet = sal_False; + if ( !IsJobActive() ) + return bRet; + + DBG_ASSERT( !mbInPrintPage, "Printer::EndJob() - StartPage() without EndPage() called" ); + + mbJobActive = sal_False; + + if ( mpPrinter ) + { + ImplReleaseGraphics(); + + mnCurPage = 0; + + bRet = sal_True; + + mbPrinting = sal_False; + mnCurPrintPage = 0; + maJobName.Erase(); + + mbDevOutput = sal_False; + bRet = mpPrinter->EndJob(); + // Hier den Drucker nicht asyncron zerstoeren, da es + // W95 nicht verkraftet, wenn gleichzeitig gedruckt wird + // und ein Druckerobjekt zerstoert wird + ImplGetSVData()->mpDefInst->DestroyPrinter( mpPrinter ); + mpPrinter = NULL; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +sal_Bool Printer::AbortJob() +{ + // Wenn wir einen Queue-Printer haben, kann man diesen noch mit + // AbortJob() abbrechen, solange dieser noch am Drucken ist + if ( !IsJobActive() && !IsPrinting() ) + return sal_False; + + mbJobActive = sal_False; + mbInPrintPage = sal_False; + mpJobGraphics = NULL; + + if ( mpPrinter ) + { + mbPrinting = sal_False; + mnCurPage = 0; + mnCurPrintPage = 0; + maJobName.Erase(); + + ImplReleaseGraphics(); + mbDevOutput = sal_False; + mpPrinter->AbortJob(); + Application::PostUserEvent( LINK( this, Printer, ImplDestroyPrinterAsync ), mpPrinter ); + mpPrinter = NULL; + + return sal_True; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +void Printer::ImplStartPage() +{ + if ( !IsJobActive() ) + return; + + if ( mpPrinter ) + { + SalGraphics* pGraphics = mpPrinter->StartPage( maJobSetup.ImplGetConstData(), mbNewJobSetup ); + if ( pGraphics ) + { + ImplReleaseGraphics(); + mpJobGraphics = pGraphics; + } + mbDevOutput = sal_True; + + // PrintJob not aborted ??? + if ( IsJobActive() ) + { + mbInPrintPage = sal_True; + mnCurPage++; + mnCurPrintPage++; + } + } +} + +// ----------------------------------------------------------------------- + +void Printer::ImplEndPage() +{ + if ( !IsJobActive() ) + return; + + mbInPrintPage = sal_False; + + if ( mpPrinter ) + { + mpPrinter->EndPage(); + ImplReleaseGraphics(); + mbDevOutput = sal_False; + + mpJobGraphics = NULL; + mbNewJobSetup = sal_False; + } +} + +// ----------------------------------------------------------------------- + +void Printer::updatePrinters() +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplPrnQueueList* pPrnList = pSVData->maGDIData.mpPrinterQueueList; + + if ( pPrnList ) + { + ImplPrnQueueList* pNewList = new ImplPrnQueueList; + pSVData->mpDefInst->GetPrinterQueueInfo( pNewList ); + + bool bChanged = pPrnList->m_aQueueInfos.size() != pNewList->m_aQueueInfos.size(); + for( unsigned int i = 0; ! bChanged && i < pPrnList->m_aQueueInfos.size(); i++ ) + { + ImplPrnQueueData& rInfo = pPrnList->m_aQueueInfos[i]; + ImplPrnQueueData& rNewInfo = pNewList->m_aQueueInfos[i]; + if( ! rInfo.mpSalQueueInfo || ! rNewInfo.mpSalQueueInfo || // sanity check + rInfo.mpSalQueueInfo->maPrinterName != rNewInfo.mpSalQueueInfo->maPrinterName ) + { + bChanged = true; + } + } + if( bChanged ) + { + ImplDeletePrnQueueList(); + pSVData->maGDIData.mpPrinterQueueList = pNewList; + + Application* pApp = GetpApp(); + if( pApp ) + { + DataChangedEvent aDCEvt( DATACHANGED_PRINTER ); + pApp->DataChanged( aDCEvt ); + pApp->NotifyAllWindows( aDCEvt ); + } + } + else + delete pNewList; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/print2.cxx b/vcl/source/gdi/print2.cxx new file mode 100644 index 000000000000..38c589eb12b2 --- /dev/null +++ b/vcl/source/gdi/print2.cxx @@ -0,0 +1,1594 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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/print.h> +#include <vcl/salbtype.hxx> +#include <vcl/print.hxx> +#include <vcl/svapp.hxx> +#include <vcl/sallayout.hxx> +#include <vcl/bmpacc.hxx> + +#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: + OSL_FAIL("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: + OSL_FAIL("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: + OSL_FAIL("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. + + 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 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/print3.cxx b/vcl/source/gdi/print3.cxx new file mode 100644 index 000000000000..4fd2f1c6d6c6 --- /dev/null +++ b/vcl/source/gdi/print3.cxx @@ -0,0 +1,1944 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_vcl.hxx" + +#include "vcl/print.hxx" +#include "vcl/prndlg.hxx" +#include "vcl/svapp.hxx" +#include "vcl/svdata.hxx" +#include "vcl/salinst.hxx" +#include "vcl/salprn.hxx" +#include "vcl/svids.hrc" +#include "vcl/metaact.hxx" +#include "vcl/msgbox.hxx" +#include "vcl/configsettings.hxx" + +#include "tools/urlobj.hxx" + +#include "com/sun/star/ui/dialogs/XFilePicker.hpp" +#include "com/sun/star/ui/dialogs/XFilterManager.hpp" +#include "com/sun/star/ui/dialogs/TemplateDescription.hpp" +#include "com/sun/star/ui/dialogs/ExecutableDialogResults.hpp" +#include "com/sun/star/view/DuplexMode.hpp" +#include "com/sun/star/lang/XMultiServiceFactory.hpp" +#include "com/sun/star/awt/Size.hpp" +#include "comphelper/processfactory.hxx" + +#include <boost/unordered_map.hpp> +#include <boost/unordered_set.hpp> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace vcl; + +class ImplPageCache +{ + struct CacheEntry + { + GDIMetaFile aPage; + PrinterController::PageSize aSize; + }; + + std::vector< CacheEntry > maPages; + std::vector< sal_Int32 > maPageNumbers; + std::vector< sal_Int32 > maCacheRanking; + + static const sal_Int32 nCacheSize = 6; + + void updateRanking( sal_Int32 nLastHit ) + { + if( maCacheRanking[0] != nLastHit ) + { + for( sal_Int32 i = nCacheSize-1; i > 0; i-- ) + maCacheRanking[i] = maCacheRanking[i-1]; + maCacheRanking[0] = nLastHit; + } + } + +public: + ImplPageCache() + : maPages( nCacheSize ) + , maPageNumbers( nCacheSize, -1 ) + , maCacheRanking( nCacheSize ) + { + for( sal_Int32 i = 0; i < nCacheSize; i++ ) + maCacheRanking[i] = nCacheSize - i - 1; + } + + // caution: does not ensure uniqueness + void insert( sal_Int32 i_nPageNo, const GDIMetaFile& i_rPage, const PrinterController::PageSize& i_rSize ) + { + sal_Int32 nReplacePage = maCacheRanking.back(); + maPages[ nReplacePage ].aPage = i_rPage; + maPages[ nReplacePage ].aSize = i_rSize; + maPageNumbers[ nReplacePage ] = i_nPageNo; + // cache insertion means in our case, the page was just queried + // so update the ranking + updateRanking( nReplacePage ); + } + + // caution: bad algorithm; should there ever be reason to increase the cache size beyond 6 + // this needs to be urgently rewritten. However do NOT increase the cache size lightly, + // whole pages can be rather memory intensive + bool get( sal_Int32 i_nPageNo, GDIMetaFile& o_rPageFile, PrinterController::PageSize& o_rSize ) + { + for( sal_Int32 i = 0; i < nCacheSize; ++i ) + { + if( maPageNumbers[i] == i_nPageNo ) + { + updateRanking( i ); + o_rPageFile = maPages[i].aPage; + o_rSize = maPages[i].aSize; + return true; + } + } + return false; + } + + void invalidate() + { + for( sal_Int32 i = 0; i < nCacheSize; ++i ) + { + maPageNumbers[i] = -1; + maPages[i].aPage.Clear(); + maCacheRanking[i] = nCacheSize - i - 1; + } + } +}; + +class vcl::ImplPrinterControllerData +{ +public: + struct ControlDependency + { + rtl::OUString maDependsOnName; + sal_Int32 mnDependsOnEntry; + + ControlDependency() : mnDependsOnEntry( -1 ) {} + }; + + typedef boost::unordered_map< rtl::OUString, size_t, rtl::OUStringHash > PropertyToIndexMap; + typedef boost::unordered_map< rtl::OUString, ControlDependency, rtl::OUStringHash > ControlDependencyMap; + typedef boost::unordered_map< rtl::OUString, Sequence< sal_Bool >, rtl::OUStringHash > ChoiceDisableMap; + + boost::shared_ptr<Printer> mpPrinter; + Sequence< PropertyValue > maUIOptions; + std::vector< PropertyValue > maUIProperties; + std::vector< bool > maUIPropertyEnabled; + PropertyToIndexMap maPropertyToIndex; + Link maOptionChangeHdl; + ControlDependencyMap maControlDependencies; + ChoiceDisableMap maChoiceDisableMap; + sal_Bool mbFirstPage; + sal_Bool mbLastPage; + sal_Bool mbReversePageOrder; + view::PrintableState meJobState; + + vcl::PrinterController::MultiPageSetup maMultiPage; + + vcl::PrintProgressDialog* mpProgress; + + ImplPageCache maPageCache; + + // set by user through printer config dialog + // if set, pages are centered and trimmed onto the fixed page + Size maFixedPageSize; + sal_Int32 mnDefaultPaperBin; + sal_Int32 mnFixedPaperBin; + + ImplPrinterControllerData() : + mbFirstPage( sal_True ), + mbLastPage( sal_False ), + mbReversePageOrder( sal_False ), + meJobState( view::PrintableState_JOB_STARTED ), + mpProgress( NULL ), + mnDefaultPaperBin( -1 ), + mnFixedPaperBin( -1 ) + {} + ~ImplPrinterControllerData() { delete mpProgress; } + + Size getRealPaperSize( const Size& i_rPageSize, bool bNoNUP ) const + { + if( maFixedPageSize.Width() > 0 && maFixedPageSize.Height() > 0 ) + return maFixedPageSize; + if( maMultiPage.nRows * maMultiPage.nColumns > 1 && ! bNoNUP ) + return maMultiPage.aPaperSize; + return i_rPageSize; + } + bool isFixedPageSize() const + { return maFixedPageSize.Width() != 0 && maFixedPageSize.Height() != 0; } + PrinterController::PageSize modifyJobSetup( const Sequence< PropertyValue >& i_rProps, bool bNoNUP ); +}; + +PrinterController::PrinterController() + : mpImplData( new ImplPrinterControllerData ) +{ +} + +PrinterController::PrinterController( const boost::shared_ptr<Printer>& i_pPrinter ) + : mpImplData( new ImplPrinterControllerData ) +{ + mpImplData->mpPrinter = i_pPrinter; +} + +static rtl::OUString queryFile( Printer* pPrinter ) +{ + rtl::OUString aResult; + + uno::Reference< lang::XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() ); + if( xFactory.is() ) + { + uno::Sequence< uno::Any > aTempl( 1 ); + aTempl.getArray()[0] <<= ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION; + uno::Reference< ui::dialogs::XFilePicker > xFilePicker( + xFactory->createInstanceWithArguments( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.dialogs.FilePicker" ) ), + aTempl ), uno::UNO_QUERY ); + DBG_ASSERT( xFilePicker.is(), "could not get FilePicker service" ); + + uno::Reference< ui::dialogs::XFilterManager > xFilterMgr( xFilePicker, uno::UNO_QUERY ); + if( xFilePicker.is() && xFilterMgr.is() ) + { + try + { +#ifdef UNX + // add PostScript and PDF + bool bPS = true, bPDF = true; + if( pPrinter ) + { + if( pPrinter->GetCapabilities( PRINTER_CAPABILITIES_PDF ) ) + bPS = false; + else + bPDF = false; + } + if( bPS ) + xFilterMgr->appendFilter( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PostScript" ) ), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.ps" ) ) ); + if( bPDF ) + xFilterMgr->appendFilter( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Portable Document Format" ) ), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.pdf" ) ) ); +#elif defined WNT + (void)pPrinter; + xFilterMgr->appendFilter( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.PRN" ) ), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.prn" ) ) ); +#endif + // add arbitrary files + xFilterMgr->appendFilter( String( VclResId( SV_STDTEXT_ALLFILETYPES ) ), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.*" ) ) ); + } + catch( lang::IllegalArgumentException rExc ) + { + DBG_ERRORFILE( "caught IllegalArgumentException when registering filter\n" ); + } + + if( xFilePicker->execute() == ui::dialogs::ExecutableDialogResults::OK ) + { + uno::Sequence< ::rtl::OUString > aPathSeq( xFilePicker->getFiles() ); + INetURLObject aObj( aPathSeq[0] ); + aResult = aObj.PathToFileName(); + } + } + } + return aResult; +} + +struct PrintJobAsync +{ + boost::shared_ptr<PrinterController> mpController; + JobSetup maInitSetup; + + PrintJobAsync( const boost::shared_ptr<PrinterController>& i_pController, + const JobSetup& i_rInitSetup + ) + : mpController( i_pController ), maInitSetup( i_rInitSetup ) + {} + + DECL_LINK( ExecJob, void* ); +}; + +IMPL_LINK( PrintJobAsync, ExecJob, void*, EMPTYARG ) +{ + Printer::ImplPrintJob( mpController, maInitSetup ); + + // clean up, do not access members after this + delete this; + + return 0; +} + +void Printer::PrintJob( const boost::shared_ptr<PrinterController>& i_pController, + const JobSetup& i_rInitSetup + ) +{ + sal_Bool bSynchronous = sal_False; + beans::PropertyValue* pVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Wait" ) ) ); + if( pVal ) + pVal->Value >>= bSynchronous; + + if( bSynchronous ) + ImplPrintJob( i_pController, i_rInitSetup ); + else + { + PrintJobAsync* pAsync = new PrintJobAsync( i_pController, i_rInitSetup ); + Application::PostUserEvent( LINK( pAsync, PrintJobAsync, ExecJob ) ); + } +} + +void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pController, + const JobSetup& i_rInitSetup + ) +{ + boost::shared_ptr<PrinterController> pController( i_pController ); + + // check if there is a default printer; if not, show an error box (if appropriate) + if( GetDefaultPrinterName().Len() == 0 ) + { + if( pController->isShowDialogs() + // && ! pController->isDirectPrint() + ) + { + ErrorBox aBox( NULL, VclResId( SV_PRINT_NOPRINTERWARNING ) ); + aBox.Execute(); + } + pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsDirect" ) ), + makeAny( sal_False ) ); + } + + // setup printer + + // #i114306# changed behavior back from persistence + // if no specific printer is already set, create the default printer + if( ! pController->getPrinter() ) + { + rtl::OUString aPrinterName( i_rInitSetup.GetPrinterName() ); + boost::shared_ptr<Printer> pPrinter( new Printer( aPrinterName ) ); + pPrinter->SetJobSetup( i_rInitSetup ); + pController->setPrinter( pPrinter ); + } + + // reset last page property + i_pController->setLastPage( sal_False ); + + // update "PageRange" property inferring from other properties: + // case 1: "Pages" set from UNO API -> + // setup "Print Selection" and insert "PageRange" attribute + // case 2: "All pages" is selected + // update "Page range" attribute to have a sensible default, + // but leave "All" as selected + + // "Pages" attribute from API is now equivalent to "PageRange" + // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1 + // Argh ! That sure needs cleaning up + beans::PropertyValue* pContentVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintRange" ) ) ); + if( ! pContentVal ) + pContentVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintContent" ) ) ); + + // case 1: UNO API has set "Pages" + beans::PropertyValue* pPagesVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Pages" ) ) ); + if( pPagesVal ) + { + rtl::OUString aPagesVal; + pPagesVal->Value >>= aPagesVal; + if( aPagesVal.getLength() ) + { + // "Pages" attribute from API is now equivalent to "PageRange" + // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1 + // Argh ! That sure needs cleaning up + if( pContentVal ) + { + pContentVal->Value = makeAny( sal_Int32( 1 ) ); + i_pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PageRange" ) ), pPagesVal->Value ); + } + } + } + // case 2: is "All" selected ? + else if( pContentVal ) + { + sal_Int32 nContent = -1; + if( pContentVal->Value >>= nContent ) + { + if( nContent == 0 ) + { + // do not overwrite PageRange if it is already set + beans::PropertyValue* pRangeVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PageRange" ) ) ); + rtl::OUString aRange; + if( pRangeVal ) + pRangeVal->Value >>= aRange; + if( aRange.getLength() == 0 ) + { + sal_Int32 nPages = i_pController->getPageCount(); + if( nPages > 0 ) + { + rtl::OUStringBuffer aBuf( 32 ); + aBuf.appendAscii( "1" ); + if( nPages > 1 ) + { + aBuf.appendAscii( "-" ); + aBuf.append( nPages ); + } + i_pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PageRange" ) ), makeAny( aBuf.makeStringAndClear() ) ); + } + } + } + } + } + + beans::PropertyValue* pReverseVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintReverse" ) ) ); + if( pReverseVal ) + { + sal_Bool bReverse = sal_False; + pReverseVal->Value >>= bReverse; + pController->setReversePrint( bReverse ); + } + + // in direct print case check whether there is anything to print. + // if not, show an errorbox (if appropriate) + if( pController->isShowDialogs() && pController->isDirectPrint() ) + { + if( pController->getFilteredPageCount() == 0 ) + { + ErrorBox aBox( NULL, VclResId( SV_PRINT_NOCONTENT ) ); + aBox.Execute(); + return; + } + } + + // check if the printer brings up its own dialog + // in that case leave the work to that dialog + if( ! pController->getPrinter()->GetCapabilities( PRINTER_CAPABILITIES_EXTERNALDIALOG ) && + ! pController->isDirectPrint() && + pController->isShowDialogs() + ) + { + try + { + PrintDialog aDlg( NULL, i_pController ); + if( ! aDlg.Execute() ) + { + GDIMetaFile aPageFile; + i_pController->abortJob(); + return; + } + if( aDlg.isPrintToFile() ) + { + rtl::OUString aFile = queryFile( pController->getPrinter().get() ); + if( ! aFile.getLength() ) + { + i_pController->abortJob(); + return; + } + pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LocalFileName" ) ), + makeAny( aFile ) ); + } + else if( aDlg.isSingleJobs() ) + { + pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ), + makeAny( sal_True ) ); + } + } + catch( std::bad_alloc& ) + { + } + } + + pController->pushPropertiesToPrinter(); + + rtl::OUString aJobName; + beans::PropertyValue* pJobNameVal = pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "JobName" ) ) ); + if( pJobNameVal ) + pJobNameVal->Value >>= aJobName; + + pController->getPrinter()->StartJob( String( aJobName ), pController ); + + pController->jobFinished( pController->getJobState() ); +} + +bool Printer::StartJob( const rtl::OUString& i_rJobName, boost::shared_ptr<vcl::PrinterController>& i_pController ) +{ + mnError = PRINTER_OK; + + if ( IsDisplayPrinter() ) + return sal_False; + + if ( IsJobActive() || IsPrinting() ) + return sal_False; + + sal_uLong nCopies = mnCopyCount; + bool bCollateCopy = mbCollateCopy; + bool bUserCopy = sal_False; + + if ( nCopies > 1 ) + { + sal_uLong nDevCopy; + + if ( bCollateCopy ) + nDevCopy = GetCapabilities( PRINTER_CAPABILITIES_COLLATECOPIES ); + else + nDevCopy = GetCapabilities( PRINTER_CAPABILITIES_COPIES ); + + // need to do copies by hand ? + if ( nCopies > nDevCopy ) + { + bUserCopy = sal_True; + nCopies = 1; + bCollateCopy = sal_False; + } + } + else + bCollateCopy = sal_False; + + + ImplSVData* pSVData = ImplGetSVData(); + mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter ); + + if ( !mpPrinter ) + return sal_False; + + sal_Bool bSinglePrintJobs = sal_False; + beans::PropertyValue* pSingleValue = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ) ); + if( pSingleValue ) + { + pSingleValue->Value >>= bSinglePrintJobs; + } + + beans::PropertyValue* pFileValue = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LocalFileName" ) ) ); + if( pFileValue ) + { + rtl::OUString aFile; + pFileValue->Value >>= aFile; + if( aFile.getLength() ) + { + mbPrintFile = sal_True; + maPrintFile = aFile; + bSinglePrintJobs = sal_False; + } + } + + XubString* pPrintFile = NULL; + if ( mbPrintFile ) + pPrintFile = &maPrintFile; + mpPrinterOptions->ReadFromConfig( mbPrintFile ); + + maJobName = i_rJobName; + mnCurPage = 1; + mnCurPrintPage = 1; + mbPrinting = sal_True; + if( GetCapabilities( PRINTER_CAPABILITIES_USEPULLMODEL ) ) + { + mbJobActive = sal_True; + // sallayer does all necessary page printing + // and also handles showing a dialog + // that also means it must call jobStarted when the dialog is finished + // it also must set the JobState of the Controller + if( mpPrinter->StartJob( pPrintFile, + i_rJobName, + Application::GetDisplayName(), + maJobSetup.ImplGetConstData(), + *i_pController ) ) + { + EndJob(); + } + else + { + mnError = ImplSalPrinterErrorCodeToVCL( mpPrinter->GetErrorCode() ); + if ( !mnError ) + mnError = PRINTER_GENERALERROR; + pSVData->mpDefInst->DestroyPrinter( mpPrinter ); + mnCurPage = 0; + mnCurPrintPage = 0; + mbPrinting = sal_False; + mpPrinter = NULL; + + return false; + } + } + else + { + // possibly a dialog has been shown + // now the real job starts + i_pController->setJobState( view::PrintableState_JOB_STARTED ); + i_pController->jobStarted(); + + int nJobs = 1; + int nOuterRepeatCount = 1; + int nInnerRepeatCount = 1; + if( bUserCopy ) + { + if( mbCollateCopy ) + nOuterRepeatCount = mnCopyCount; + else + nInnerRepeatCount = mnCopyCount; + } + if( bSinglePrintJobs ) + { + nJobs = mnCopyCount; + nCopies = 1; + nOuterRepeatCount = nInnerRepeatCount = 1; + } + + for( int nJobIteration = 0; nJobIteration < nJobs; nJobIteration++ ) + { + bool bError = false, bAborted = false; + if( mpPrinter->StartJob( pPrintFile, + i_rJobName, + Application::GetDisplayName(), + nCopies, + bCollateCopy, + i_pController->isDirectPrint(), + maJobSetup.ImplGetConstData() ) ) + { + mbJobActive = sal_True; + i_pController->createProgressDialog(); + int nPages = i_pController->getFilteredPageCount(); + for( int nOuterIteration = 0; nOuterIteration < nOuterRepeatCount && ! bAborted; nOuterIteration++ ) + { + for( int nPage = 0; nPage < nPages && ! bAborted; nPage++ ) + { + for( int nInnerIteration = 0; nInnerIteration < nInnerRepeatCount && ! bAborted; nInnerIteration++ ) + { + if( nPage == nPages-1 && + nOuterIteration == nOuterRepeatCount-1 && + nInnerIteration == nInnerRepeatCount-1 && + nJobIteration == nJobs-1 ) + { + i_pController->setLastPage( sal_True ); + } + i_pController->printFilteredPage( nPage ); + if( i_pController->isProgressCanceled() ) + { + i_pController->abortJob(); + bAborted = true; + } + } + } + // FIXME: duplex ? + } + EndJob(); + + if( nJobIteration < nJobs-1 ) + { + mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter ); + + if ( mpPrinter ) + { + maJobName = i_rJobName; + mnCurPage = 1; + mnCurPrintPage = 1; + mbPrinting = sal_True; + } + else + bError = true; + } + } + else + bError = true; + + if( bError ) + { + mnError = ImplSalPrinterErrorCodeToVCL( mpPrinter->GetErrorCode() ); + if ( !mnError ) + mnError = PRINTER_GENERALERROR; + i_pController->setJobState( mnError == PRINTER_ABORT + ? view::PrintableState_JOB_ABORTED + : view::PrintableState_JOB_FAILED ); + if( mpPrinter ) + pSVData->mpDefInst->DestroyPrinter( mpPrinter ); + mnCurPage = 0; + mnCurPrintPage = 0; + mbPrinting = sal_False; + mpPrinter = NULL; + + return false; + } + } + + if( i_pController->getJobState() == view::PrintableState_JOB_STARTED ) + i_pController->setJobState( view::PrintableState_JOB_SPOOLED ); + } + + // make last used printer persistent for UI jobs + if( i_pController->isShowDialogs() && ! i_pController->isDirectPrint() ) + { + SettingsConfigItem* pItem = SettingsConfigItem::get(); + pItem->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintDialog" ) ), + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LastPrinterUsed" ) ), + GetName() + ); + } + + return true; +} + +PrinterController::~PrinterController() +{ + delete mpImplData; +} + +view::PrintableState PrinterController::getJobState() const +{ + return mpImplData->meJobState; +} + +void PrinterController::setJobState( view::PrintableState i_eState ) +{ + mpImplData->meJobState = i_eState; +} + +const boost::shared_ptr<Printer>& PrinterController::getPrinter() const +{ + return mpImplData->mpPrinter; +} + +void PrinterController::setPrinter( const boost::shared_ptr<Printer>& i_rPrinter ) +{ + mpImplData->mpPrinter = i_rPrinter; + setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Name" ) ), + makeAny( rtl::OUString( i_rPrinter->GetName() ) ) ); + mpImplData->mnDefaultPaperBin = mpImplData->mpPrinter->GetPaperBin(); + mpImplData->mnFixedPaperBin = -1; +} + +void PrinterController:: resetPrinterOptions( bool i_bFileOutput ) +{ + PrinterOptions aOpt; + aOpt.ReadFromConfig( i_bFileOutput ); + mpImplData->mpPrinter->SetPrinterOptions( aOpt ); +} + +bool PrinterController::setupPrinter( Window* i_pParent ) +{ + bool bRet = false; + if( mpImplData->mpPrinter.get() ) + { + // get old data + Size aPaperSize( mpImplData->mpPrinter->PixelToLogic( + mpImplData->mpPrinter->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) ) ); + sal_uInt16 nPaperBin = mpImplData->mpPrinter->GetPaperBin(); + + // call driver setup + bRet = mpImplData->mpPrinter->Setup( i_pParent ); + if( bRet ) + { + // was papersize or bin overridden ? if so we need to take action + Size aNewPaperSize( mpImplData->mpPrinter->PixelToLogic( + mpImplData->mpPrinter->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) ) ); + sal_uInt16 nNewPaperBin = mpImplData->mpPrinter->GetPaperBin(); + if( aNewPaperSize != aPaperSize || nNewPaperBin != nPaperBin ) + { + mpImplData->maFixedPageSize = aNewPaperSize; + mpImplData->maPageCache.invalidate(); + awt::Size aOverrideSize; + aOverrideSize.Width = aNewPaperSize.Width(); + aOverrideSize.Height = aNewPaperSize.Height(); + setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "OverridePageSize" ) ), + makeAny( aOverrideSize ) ); + mpImplData->mnFixedPaperBin = nNewPaperBin; + } + } + } + return bRet; +} + +PrinterController::PageSize vcl::ImplPrinterControllerData::modifyJobSetup( const Sequence< PropertyValue >& i_rProps, bool bNoNUP ) +{ + PrinterController::PageSize aPageSize; + aPageSize.aSize = mpPrinter->GetPaperSize(); + awt::Size aSetSize, aIsSize; + sal_Int32 nPaperBin = mnDefaultPaperBin; + for( sal_Int32 nProperty = 0, nPropertyCount = i_rProps.getLength(); nProperty < nPropertyCount; ++nProperty ) + { + if( i_rProps[ nProperty ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "PreferredPageSize" ) ) ) + { + i_rProps[ nProperty ].Value >>= aSetSize; + } + else if( i_rProps[ nProperty ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "PageSize" ) ) ) + { + i_rProps[ nProperty ].Value >>= aIsSize; + } + else if( i_rProps[ nProperty ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "PageIncludesNonprintableArea" ) ) ) + { + sal_Bool bVal = sal_False; + i_rProps[ nProperty ].Value >>= bVal; + aPageSize.bFullPaper = static_cast<bool>(bVal); + } + else if( i_rProps[ nProperty ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "PrinterPaperTray" ) ) ) + { + sal_Int32 nBin = -1; + i_rProps[ nProperty ].Value >>= nBin; + if( nBin >= 0 && nBin < mpPrinter->GetPaperBinCount() ) + nPaperBin = nBin; + } + } + + Size aCurSize( mpPrinter->GetPaperSize() ); + if( aSetSize.Width && aSetSize.Height ) + { + Size aSetPaperSize( aSetSize.Width, aSetSize.Height ); + Size aRealPaperSize( getRealPaperSize( aSetPaperSize, bNoNUP ) ); + if( aRealPaperSize != aCurSize ) + aIsSize = aSetSize; + } + + if( aIsSize.Width && aIsSize.Height ) + { + aPageSize.aSize.Width() = aIsSize.Width; + aPageSize.aSize.Height() = aIsSize.Height; + + Size aRealPaperSize( getRealPaperSize( aPageSize.aSize, bNoNUP ) ); + if( aRealPaperSize != aCurSize ) + mpPrinter->SetPaperSizeUser( aRealPaperSize, ! isFixedPageSize() ); + } + + if( nPaperBin != -1 && nPaperBin != mpPrinter->GetPaperBin() ) + mpPrinter->SetPaperBin( nPaperBin ); + + return aPageSize; +} + +int PrinterController::getPageCountProtected() const +{ + const MapMode aMapMode( MAP_100TH_MM ); + + mpImplData->mpPrinter->Push(); + mpImplData->mpPrinter->SetMapMode( aMapMode ); + int nPages = getPageCount(); + mpImplData->mpPrinter->Pop(); + return nPages; +} + +Sequence< beans::PropertyValue > PrinterController::getPageParametersProtected( int i_nPage ) const +{ + const MapMode aMapMode( MAP_100TH_MM ); + + mpImplData->mpPrinter->Push(); + mpImplData->mpPrinter->SetMapMode( aMapMode ); + Sequence< beans::PropertyValue > aResult( getPageParameters( i_nPage ) ); + mpImplData->mpPrinter->Pop(); + return aResult; +} + +PrinterController::PageSize PrinterController::getPageFile( int i_nUnfilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache ) +{ + // update progress if necessary + if( mpImplData->mpProgress ) + { + // do nothing if printing is canceled + if( mpImplData->mpProgress->isCanceled() ) + return PrinterController::PageSize(); + mpImplData->mpProgress->tick(); + Application::Reschedule( true ); + } + + if( i_bMayUseCache ) + { + PrinterController::PageSize aPageSize; + if( mpImplData->maPageCache.get( i_nUnfilteredPage, o_rMtf, aPageSize ) ) + { + return aPageSize; + } + } + else + mpImplData->maPageCache.invalidate(); + + o_rMtf.Clear(); + + // get page parameters + Sequence< PropertyValue > aPageParm( getPageParametersProtected( i_nUnfilteredPage ) ); + const MapMode aMapMode( MAP_100TH_MM ); + + mpImplData->mpPrinter->Push(); + mpImplData->mpPrinter->SetMapMode( aMapMode ); + + // modify job setup if necessary + PrinterController::PageSize aPageSize = mpImplData->modifyJobSetup( aPageParm, true ); + + o_rMtf.SetPrefSize( aPageSize.aSize ); + o_rMtf.SetPrefMapMode( aMapMode ); + + mpImplData->mpPrinter->EnableOutput( sal_False ); + + o_rMtf.Record( mpImplData->mpPrinter.get() ); + + printPage( i_nUnfilteredPage ); + + o_rMtf.Stop(); + o_rMtf.WindStart(); + mpImplData->mpPrinter->Pop(); + + if( i_bMayUseCache ) + mpImplData->maPageCache.insert( i_nUnfilteredPage, o_rMtf, aPageSize ); + + // reset "FirstPage" property to false now we've gotten at least our first one + mpImplData->mbFirstPage = sal_False; + + return aPageSize; +} + +static void appendSubPage( GDIMetaFile& o_rMtf, const Rectangle& i_rClipRect, GDIMetaFile& io_rSubPage, bool i_bDrawBorder ) +{ + // intersect all clipregion actions with our clip rect + io_rSubPage.WindStart(); + io_rSubPage.Clip( i_rClipRect ); + + // save gstate + o_rMtf.AddAction( new MetaPushAction( PUSH_ALL ) ); + + // clip to page rect + o_rMtf.AddAction( new MetaClipRegionAction( Region( i_rClipRect ), sal_True ) ); + + // append the subpage + io_rSubPage.WindStart(); + io_rSubPage.Play( o_rMtf ); + + // restore gstate + o_rMtf.AddAction( new MetaPopAction() ); + + // draw a border + if( i_bDrawBorder ) + { + // save gstate + o_rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR | PUSH_FILLCOLOR | PUSH_CLIPREGION | PUSH_MAPMODE ) ); + o_rMtf.AddAction( new MetaMapModeAction( MapMode( MAP_100TH_MM ) ) ); + + Rectangle aBorderRect( i_rClipRect ); + o_rMtf.AddAction( new MetaLineColorAction( Color( COL_BLACK ), sal_True ) ); + o_rMtf.AddAction( new MetaFillColorAction( Color( COL_TRANSPARENT ), sal_False ) ); + o_rMtf.AddAction( new MetaRectAction( aBorderRect ) ); + + // restore gstate + o_rMtf.AddAction( new MetaPopAction() ); + } +} + +PrinterController::PageSize PrinterController::getFilteredPageFile( int i_nFilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache ) +{ + const MultiPageSetup& rMPS( mpImplData->maMultiPage ); + int nSubPages = rMPS.nRows * rMPS.nColumns; + if( nSubPages < 1 ) + nSubPages = 1; + + // reverse sheet order + if( mpImplData->mbReversePageOrder ) + { + int nDocPages = getFilteredPageCount(); + i_nFilteredPage = nDocPages - 1 - i_nFilteredPage; + } + + // there is no filtering to be done (and possibly the page size of the + // original page is to be set), when N-Up is "neutral" that is there is + // only one subpage and the margins are 0 + if( nSubPages == 1 && + rMPS.nLeftMargin == 0 && rMPS.nRightMargin == 0 && + rMPS.nTopMargin == 0 && rMPS.nBottomMargin == 0 ) + { + PrinterController::PageSize aPageSize = getPageFile( i_nFilteredPage, o_rMtf, i_bMayUseCache ); + Size aPaperSize = mpImplData->getRealPaperSize( aPageSize.aSize, true ); + mpImplData->mpPrinter->SetMapMode( MapMode( MAP_100TH_MM ) ); + mpImplData->mpPrinter->SetPaperSizeUser( aPaperSize, ! mpImplData->isFixedPageSize() ); + if( aPaperSize != aPageSize.aSize ) + { + // user overridden page size, center Metafile + o_rMtf.WindStart(); + long nDX = (aPaperSize.Width() - aPageSize.aSize.Width()) / 2; + long nDY = (aPaperSize.Height() - aPageSize.aSize.Height()) / 2; + o_rMtf.Move( nDX, nDY, mpImplData->mpPrinter->ImplGetDPIX(), mpImplData->mpPrinter->ImplGetDPIY() ); + o_rMtf.WindStart(); + o_rMtf.SetPrefSize( aPaperSize ); + aPageSize.aSize = aPaperSize; + } + return aPageSize; + } + + // set last page property really only on the very last page to be rendered + // that is on the last subpage of a NUp run + sal_Bool bIsLastPage = mpImplData->mbLastPage; + mpImplData->mbLastPage = sal_False; + + Size aPaperSize( mpImplData->getRealPaperSize( mpImplData->maMultiPage.aPaperSize, false ) ); + + // multi page area: page size minus margins + one time spacing right and down + // the added spacing is so each subpage can be calculated including its spacing + Size aMPArea( aPaperSize ); + aMPArea.Width() -= rMPS.nLeftMargin + rMPS.nRightMargin; + aMPArea.Width() += rMPS.nHorizontalSpacing; + aMPArea.Height() -= rMPS.nTopMargin + rMPS.nBottomMargin; + aMPArea.Height() += rMPS.nVerticalSpacing; + + // determine offsets + long nAdvX = aMPArea.Width() / rMPS.nColumns; + long nAdvY = aMPArea.Height() / rMPS.nRows; + + // determine size of a "cell" subpage, leave a little space around pages + Size aSubPageSize( nAdvX - rMPS.nHorizontalSpacing, nAdvY - rMPS.nVerticalSpacing ); + + o_rMtf.Clear(); + o_rMtf.SetPrefSize( aPaperSize ); + o_rMtf.SetPrefMapMode( MapMode( MAP_100TH_MM ) ); + o_rMtf.AddAction( new MetaMapModeAction( MapMode( MAP_100TH_MM ) ) ); + + int nDocPages = getPageCountProtected(); + for( int nSubPage = 0; nSubPage < nSubPages; nSubPage++ ) + { + // map current sub page to real page + int nPage = (i_nFilteredPage * nSubPages + nSubPage) / rMPS.nRepeat; + if( nSubPage == nSubPages-1 || + nPage == nDocPages-1 ) + { + mpImplData->mbLastPage = bIsLastPage; + } + if( nPage >= 0 && nPage < nDocPages ) + { + GDIMetaFile aPageFile; + PrinterController::PageSize aPageSize = getPageFile( nPage, aPageFile, i_bMayUseCache ); + if( aPageSize.aSize.Width() && aPageSize.aSize.Height() ) + { + long nCellX = 0, nCellY = 0; + switch( rMPS.nOrder ) + { + case PrinterController::LRTB: + nCellX = (nSubPage % rMPS.nColumns); + nCellY = (nSubPage / rMPS.nColumns); + break; + case PrinterController::TBLR: + nCellX = (nSubPage / rMPS.nRows); + nCellY = (nSubPage % rMPS.nRows); + break; + case PrinterController::RLTB: + nCellX = rMPS.nColumns - 1 - (nSubPage % rMPS.nColumns); + nCellY = (nSubPage / rMPS.nColumns); + break; + case PrinterController::TBRL: + nCellX = rMPS.nColumns - 1 - (nSubPage / rMPS.nRows); + nCellY = (nSubPage % rMPS.nRows); + break; + } + // scale the metafile down to a sub page size + double fScaleX = double(aSubPageSize.Width())/double(aPageSize.aSize.Width()); + double fScaleY = double(aSubPageSize.Height())/double(aPageSize.aSize.Height()); + double fScale = std::min( fScaleX, fScaleY ); + aPageFile.Scale( fScale, fScale ); + aPageFile.WindStart(); + + // move the subpage so it is centered in its "cell" + long nOffX = (aSubPageSize.Width() - long(double(aPageSize.aSize.Width()) * fScale)) / 2; + long nOffY = (aSubPageSize.Height() - long(double(aPageSize.aSize.Height()) * fScale)) / 2; + long nX = rMPS.nLeftMargin + nOffX + nAdvX * nCellX; + long nY = rMPS.nTopMargin + nOffY + nAdvY * nCellY; + aPageFile.Move( nX, nY, mpImplData->mpPrinter->ImplGetDPIX(), mpImplData->mpPrinter->ImplGetDPIY() ); + aPageFile.WindStart(); + // calculate border rectangle + Rectangle aSubPageRect( Point( nX, nY ), + Size( long(double(aPageSize.aSize.Width())*fScale), + long(double(aPageSize.aSize.Height())*fScale) ) ); + + // append subpage to page + appendSubPage( o_rMtf, aSubPageRect, aPageFile, rMPS.bDrawBorder ); + } + } + } + o_rMtf.WindStart(); + + // subsequent getPageFile calls have changed the paper, reset it to current value + mpImplData->mpPrinter->SetMapMode( MapMode( MAP_100TH_MM ) ); + mpImplData->mpPrinter->SetPaperSizeUser( aPaperSize, ! mpImplData->isFixedPageSize() ); + + return PrinterController::PageSize( aPaperSize, true ); +} + +int PrinterController::getFilteredPageCount() +{ + int nDiv = mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns; + if( nDiv < 1 ) + nDiv = 1; + return (getPageCountProtected() * mpImplData->maMultiPage.nRepeat + (nDiv-1)) / nDiv; +} + +sal_uLong PrinterController::removeTransparencies( GDIMetaFile& i_rIn, GDIMetaFile& o_rOut ) +{ + sal_uLong nRestoreDrawMode = mpImplData->mpPrinter->GetDrawMode(); + sal_Int32 nMaxBmpDPIX = mpImplData->mpPrinter->ImplGetDPIX(); + sal_Int32 nMaxBmpDPIY = mpImplData->mpPrinter->ImplGetDPIY(); + + const PrinterOptions& rPrinterOptions = mpImplData->mpPrinter->GetPrinterOptions(); + + static const sal_Int32 OPTIMAL_BMP_RESOLUTION = 300; + static const sal_Int32 NORMAL_BMP_RESOLUTION = 200; + + + if( rPrinterOptions.IsReduceBitmaps() ) + { + // calculate maximum resolution for bitmap graphics + if( PRINTER_BITMAP_OPTIMAL == rPrinterOptions.GetReducedBitmapMode() ) + { + nMaxBmpDPIX = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIX ); + nMaxBmpDPIY = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIY ); + } + else if( PRINTER_BITMAP_NORMAL == rPrinterOptions.GetReducedBitmapMode() ) + { + nMaxBmpDPIX = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIX ); + nMaxBmpDPIY = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIY ); + } + else + { + nMaxBmpDPIX = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIX ); + nMaxBmpDPIY = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIY ); + } + } + + // convert to greysacles + if( rPrinterOptions.IsConvertToGreyscales() ) + { + mpImplData->mpPrinter->SetDrawMode( mpImplData->mpPrinter->GetDrawMode() | + ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT | + DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) ); + } + + // disable transparency output + if( rPrinterOptions.IsReduceTransparency() && ( PRINTER_TRANSPARENCY_NONE == rPrinterOptions.GetReducedTransparencyMode() ) ) + { + mpImplData->mpPrinter->SetDrawMode( mpImplData->mpPrinter->GetDrawMode() | DRAWMODE_NOTRANSPARENCY ); + } + + Color aBg( COL_TRANSPARENT ); // default: let RemoveTransparenciesFromMetaFile do its own background logic + if( mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns > 1 ) + { + // in N-Up printing we have no "page" background operation + // we also have no way to determine the paper color + // so let's go for white, which will kill 99.9% of the real cases + aBg = Color( COL_WHITE ); + } + mpImplData->mpPrinter->RemoveTransparenciesFromMetaFile( i_rIn, o_rOut, nMaxBmpDPIX, nMaxBmpDPIY, + rPrinterOptions.IsReduceTransparency(), + rPrinterOptions.GetReducedTransparencyMode() == PRINTER_TRANSPARENCY_AUTO, + rPrinterOptions.IsReduceBitmaps() && rPrinterOptions.IsReducedBitmapIncludesTransparency(), + aBg + ); + return nRestoreDrawMode; +} + +void PrinterController::printFilteredPage( int i_nPage ) +{ + if( mpImplData->meJobState != view::PrintableState_JOB_STARTED ) + return; + + GDIMetaFile aPageFile; + PrinterController::PageSize aPageSize = getFilteredPageFile( i_nPage, aPageFile ); + + if( mpImplData->mpProgress ) + { + // do nothing if printing is canceled + if( mpImplData->mpProgress->isCanceled() ) + { + setJobState( view::PrintableState_JOB_ABORTED ); + return; + } + } + + // in N-Up printing set the correct page size + mpImplData->mpPrinter->SetMapMode( MAP_100TH_MM ); + // aPageSize was filtered through mpImplData->getRealPaperSize already by getFilteredPageFile() + mpImplData->mpPrinter->SetPaperSizeUser( aPageSize.aSize, ! mpImplData->isFixedPageSize() ); + if( mpImplData->mnFixedPaperBin != -1 && + mpImplData->mpPrinter->GetPaperBin() != mpImplData->mnFixedPaperBin ) + { + mpImplData->mpPrinter->SetPaperBin( mpImplData->mnFixedPaperBin ); + } + + // if full paper is meant to be used, move the output to accomodate for pageoffset + if( aPageSize.bFullPaper ) + { + Point aPageOffset( mpImplData->mpPrinter->GetPageOffset() ); + aPageFile.WindStart(); + aPageFile.Move( -aPageOffset.X(), -aPageOffset.Y(), mpImplData->mpPrinter->ImplGetDPIX(), mpImplData->mpPrinter->ImplGetDPIY() ); + } + + GDIMetaFile aCleanedFile; + sal_uLong nRestoreDrawMode = removeTransparencies( aPageFile, aCleanedFile ); + + mpImplData->mpPrinter->EnableOutput( sal_True ); + + // actually print the page + mpImplData->mpPrinter->ImplStartPage(); + + mpImplData->mpPrinter->Push(); + aCleanedFile.WindStart(); + aCleanedFile.Play( mpImplData->mpPrinter.get() ); + mpImplData->mpPrinter->Pop(); + + mpImplData->mpPrinter->ImplEndPage(); + + mpImplData->mpPrinter->SetDrawMode( nRestoreDrawMode ); +} + +void PrinterController::jobStarted() +{ +} + +void PrinterController::jobFinished( view::PrintableState ) +{ +} + +void PrinterController::abortJob() +{ + setJobState( view::PrintableState_JOB_ABORTED ); + // applications (well, sw) depend on a page request with "IsLastPage" = true + // to free resources, else they (well, sw) will crash eventually + setLastPage( sal_True ); + delete mpImplData->mpProgress; + mpImplData->mpProgress = NULL; + GDIMetaFile aMtf; + getPageFile( 0, aMtf, false ); +} + +void PrinterController::setLastPage( sal_Bool i_bLastPage ) +{ + mpImplData->mbLastPage = i_bLastPage; +} + +void PrinterController::setReversePrint( sal_Bool i_bReverse ) +{ + mpImplData->mbReversePageOrder = i_bReverse; +} + +bool PrinterController::getReversePrint() const +{ + return mpImplData->mbReversePageOrder; +} + +Sequence< PropertyValue > PrinterController::getJobProperties( const Sequence< PropertyValue >& i_rMergeList ) const +{ + boost::unordered_set< rtl::OUString, rtl::OUStringHash > aMergeSet; + size_t nResultLen = size_t(i_rMergeList.getLength()) + mpImplData->maUIProperties.size() + 3; + for( int i = 0; i < i_rMergeList.getLength(); i++ ) + aMergeSet.insert( i_rMergeList[i].Name ); + + Sequence< PropertyValue > aResult( nResultLen ); + for( int i = 0; i < i_rMergeList.getLength(); i++ ) + aResult[i] = i_rMergeList[i]; + int nCur = i_rMergeList.getLength(); + for( size_t i = 0; i < mpImplData->maUIProperties.size(); i++ ) + { + if( aMergeSet.find( mpImplData->maUIProperties[i].Name ) == aMergeSet.end() ) + aResult[nCur++] = mpImplData->maUIProperties[i]; + } + // append IsFirstPage + if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFirstPage" ) ) ) == aMergeSet.end() ) + { + PropertyValue aVal; + aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFirstPage" ) ); + aVal.Value <<= mpImplData->mbFirstPage; + aResult[nCur++] = aVal; + } + // append IsLastPage + if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsLastPage" ) ) ) == aMergeSet.end() ) + { + PropertyValue aVal; + aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsLastPage" ) ); + aVal.Value <<= mpImplData->mbLastPage; + aResult[nCur++] = aVal; + } + // append IsPrinter + if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsPrinter" ) ) ) == aMergeSet.end() ) + { + PropertyValue aVal; + aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsPrinter" ) ); + aVal.Value <<= sal_True; + aResult[nCur++] = aVal; + } + aResult.realloc( nCur ); + return aResult; +} + +const Sequence< beans::PropertyValue >& PrinterController::getUIOptions() const +{ + return mpImplData->maUIOptions; +} + +beans::PropertyValue* PrinterController::getValue( const rtl::OUString& i_rProperty ) +{ + boost::unordered_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it = + mpImplData->maPropertyToIndex.find( i_rProperty ); + return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : NULL; +} + +const beans::PropertyValue* PrinterController::getValue( const rtl::OUString& i_rProperty ) const +{ + boost::unordered_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it = + mpImplData->maPropertyToIndex.find( i_rProperty ); + return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : NULL; +} + +Sequence< beans::PropertyValue > PrinterController::getValues( const Sequence< rtl::OUString >& i_rNames ) const +{ + Sequence< beans::PropertyValue > aRet( i_rNames.getLength() ); + sal_Int32 nFound = 0; + for( sal_Int32 i = 0; i < i_rNames.getLength(); i++ ) + { + const beans::PropertyValue* pVal = getValue( i_rNames[i] ); + if( pVal ) + aRet[ nFound++ ] = *pVal; + } + aRet.realloc( nFound ); + return aRet; +} + +void PrinterController::setValue( const rtl::OUString& i_rName, const Any& i_rValue ) +{ + beans::PropertyValue aVal; + aVal.Name = i_rName; + aVal.Value = i_rValue; + + setValue( aVal ); +} + +void PrinterController::setValue( const beans::PropertyValue& i_rValue ) +{ + boost::unordered_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it = + mpImplData->maPropertyToIndex.find( i_rValue.Name ); + if( it != mpImplData->maPropertyToIndex.end() ) + mpImplData->maUIProperties[ it->second ] = i_rValue; + else + { + // insert correct index into property map + mpImplData->maPropertyToIndex[ i_rValue.Name ] = mpImplData->maUIProperties.size(); + mpImplData->maUIProperties.push_back( i_rValue ); + mpImplData->maUIPropertyEnabled.push_back( true ); + } +} + +void PrinterController::setUIOptions( const Sequence< beans::PropertyValue >& i_rOptions ) +{ + DBG_ASSERT( mpImplData->maUIOptions.getLength() == 0, "setUIOptions called twice !" ); + + mpImplData->maUIOptions = i_rOptions; + + for( int i = 0; i < i_rOptions.getLength(); i++ ) + { + Sequence< beans::PropertyValue > aOptProp; + i_rOptions[i].Value >>= aOptProp; + bool bIsEnabled = true; + bool bHaveProperty = false; + rtl::OUString aPropName; + vcl::ImplPrinterControllerData::ControlDependency aDep; + Sequence< sal_Bool > aChoicesDisabled; + for( int n = 0; n < aOptProp.getLength(); n++ ) + { + const beans::PropertyValue& rEntry( aOptProp[ n ] ); + if( rEntry.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Property" ) ) ) + { + PropertyValue aVal; + rEntry.Value >>= aVal; + DBG_ASSERT( mpImplData->maPropertyToIndex.find( aVal.Name ) + == mpImplData->maPropertyToIndex.end(), "duplicate property entry" ); + setValue( aVal ); + aPropName = aVal.Name; + bHaveProperty = true; + } + else if( rEntry.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Enabled" ) ) ) + { + sal_Bool bValue = sal_True; + rEntry.Value >>= bValue; + bIsEnabled = bValue; + } + else if( rEntry.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DependsOnName" ) ) ) + { + rEntry.Value >>= aDep.maDependsOnName; + } + else if( rEntry.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DependsOnEntry" ) ) ) + { + rEntry.Value >>= aDep.mnDependsOnEntry; + } + else if( rEntry.Name.equalsAscii( "ChoicesDisabled" ) ) + { + rEntry.Value >>= aChoicesDisabled; + } + } + if( bHaveProperty ) + { + vcl::ImplPrinterControllerData::PropertyToIndexMap::const_iterator it = + mpImplData->maPropertyToIndex.find( aPropName ); + // sanity check + if( it != mpImplData->maPropertyToIndex.end() ) + { + mpImplData->maUIPropertyEnabled[ it->second ] = bIsEnabled; + } + if( aDep.maDependsOnName.getLength() > 0 ) + mpImplData->maControlDependencies[ aPropName ] = aDep; + if( aChoicesDisabled.getLength() > 0 ) + mpImplData->maChoiceDisableMap[ aPropName ] = aChoicesDisabled; + } + } +} + +void PrinterController::enableUIOption( const rtl::OUString& i_rProperty, bool i_bEnable ) +{ + boost::unordered_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it = + mpImplData->maPropertyToIndex.find( i_rProperty ); + if( it != mpImplData->maPropertyToIndex.end() ) + { + // call handler only for actual changes + if( ( mpImplData->maUIPropertyEnabled[ it->second ] && ! i_bEnable ) || + ( ! mpImplData->maUIPropertyEnabled[ it->second ] && i_bEnable ) ) + { + mpImplData->maUIPropertyEnabled[ it->second ] = i_bEnable; + rtl::OUString aPropName( i_rProperty ); + mpImplData->maOptionChangeHdl.Call( &aPropName ); + } + } +} + +bool PrinterController::isUIOptionEnabled( const rtl::OUString& i_rProperty ) const +{ + bool bEnabled = false; + boost::unordered_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator prop_it = + mpImplData->maPropertyToIndex.find( i_rProperty ); + if( prop_it != mpImplData->maPropertyToIndex.end() ) + { + bEnabled = mpImplData->maUIPropertyEnabled[prop_it->second]; + + if( bEnabled ) + { + // check control dependencies + vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it = + mpImplData->maControlDependencies.find( i_rProperty ); + if( it != mpImplData->maControlDependencies.end() ) + { + // check if the dependency is enabled + // if the dependency is disabled, we are too + bEnabled = isUIOptionEnabled( it->second.maDependsOnName ); + + if( bEnabled ) + { + // does the dependency have the correct value ? + const com::sun::star::beans::PropertyValue* pVal = getValue( it->second.maDependsOnName ); + OSL_ENSURE( pVal, "unknown property in dependency" ); + if( pVal ) + { + sal_Int32 nDepVal = 0; + sal_Bool bDepVal = sal_False; + if( pVal->Value >>= nDepVal ) + { + bEnabled = (nDepVal == it->second.mnDependsOnEntry) || (it->second.mnDependsOnEntry == -1); + } + else if( pVal->Value >>= bDepVal ) + { + // could be a dependency on a checked boolean + // in this case the dependency is on a non zero for checked value + bEnabled = ( bDepVal && it->second.mnDependsOnEntry != 0) || + ( ! bDepVal && it->second.mnDependsOnEntry == 0); + } + else + { + // if the type does not match something is awry + OSL_FAIL( "strange type in control dependency" ); + bEnabled = false; + } + } + } + } + } + } + return bEnabled; +} + +bool PrinterController::isUIChoiceEnabled( const rtl::OUString& i_rProperty, sal_Int32 i_nValue ) const +{ + bool bEnabled = true; + ImplPrinterControllerData::ChoiceDisableMap::const_iterator it = + mpImplData->maChoiceDisableMap.find( i_rProperty ); + if(it != mpImplData->maChoiceDisableMap.end() ) + { + const Sequence< sal_Bool >& rDisabled( it->second ); + if( i_nValue >= 0 && i_nValue < rDisabled.getLength() ) + bEnabled = ! rDisabled[i_nValue]; + } + return bEnabled; +} + +rtl::OUString PrinterController::getDependency( const rtl::OUString& i_rProperty ) const +{ + rtl::OUString aDependency; + + vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it = + mpImplData->maControlDependencies.find( i_rProperty ); + if( it != mpImplData->maControlDependencies.end() ) + aDependency = it->second.maDependsOnName; + + return aDependency; +} + +rtl::OUString PrinterController::makeEnabled( const rtl::OUString& i_rProperty ) +{ + rtl::OUString aDependency; + + vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it = + mpImplData->maControlDependencies.find( i_rProperty ); + if( it != mpImplData->maControlDependencies.end() ) + { + if( isUIOptionEnabled( it->second.maDependsOnName ) ) + { + aDependency = it->second.maDependsOnName; + const com::sun::star::beans::PropertyValue* pVal = getValue( aDependency ); + OSL_ENSURE( pVal, "unknown property in dependency" ); + if( pVal ) + { + sal_Int32 nDepVal = 0; + sal_Bool bDepVal = sal_False; + if( pVal->Value >>= nDepVal ) + { + if( it->second.mnDependsOnEntry != -1 ) + { + setValue( aDependency, makeAny( sal_Int32( it->second.mnDependsOnEntry ) ) ); + } + } + else if( pVal->Value >>= bDepVal ) + { + setValue( aDependency, makeAny( sal_Bool( it->second.mnDependsOnEntry != 0 ) ) ); + } + else + { + // if the type does not match something is awry + OSL_FAIL( "strange type in control dependency" ); + } + } + } + } + + return aDependency; +} + +void PrinterController::setOptionChangeHdl( const Link& i_rHdl ) +{ + mpImplData->maOptionChangeHdl = i_rHdl; +} + +void PrinterController::createProgressDialog() +{ + if( ! mpImplData->mpProgress ) + { + sal_Bool bShow = sal_True; + beans::PropertyValue* pMonitor = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MonitorVisible" ) ) ); + if( pMonitor ) + pMonitor->Value >>= bShow; + else + { + const com::sun::star::beans::PropertyValue* pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsApi" ) ) ); + if( pVal ) + { + sal_Bool bApi = sal_False; + pVal->Value >>= bApi; + bShow = ! bApi; + } + } + + if( bShow && ! Application::IsHeadlessModeEnabled() ) + { + mpImplData->mpProgress = new PrintProgressDialog( NULL, getPageCountProtected() ); + mpImplData->mpProgress->Show(); + } + } + else + mpImplData->mpProgress->reset(); +} + +bool PrinterController::isProgressCanceled() const +{ + return mpImplData->mpProgress && mpImplData->mpProgress->isCanceled(); +} + +void PrinterController::setMultipage( const MultiPageSetup& i_rMPS ) +{ + mpImplData->maMultiPage = i_rMPS; +} + +const PrinterController::MultiPageSetup& PrinterController::getMultipage() const +{ + return mpImplData->maMultiPage; +} + +void PrinterController::pushPropertiesToPrinter() +{ + sal_Int32 nCopyCount = 1; + // set copycount and collate + const beans::PropertyValue* pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CopyCount" ) ) ); + if( pVal ) + pVal->Value >>= nCopyCount; + sal_Bool bCollate = sal_False; + pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) ); + if( pVal ) + pVal->Value >>= bCollate; + mpImplData->mpPrinter->SetCopyCount( static_cast<sal_uInt16>(nCopyCount), bCollate ); + + // duplex mode + pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DuplexMode" ) ) ); + if( pVal ) + { + sal_Int16 nDuplex = view::DuplexMode::UNKNOWN; + pVal->Value >>= nDuplex; + switch( nDuplex ) + { + case view::DuplexMode::OFF: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_OFF ); break; + case view::DuplexMode::LONGEDGE: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_LONGEDGE ); break; + case view::DuplexMode::SHORTEDGE: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_SHORTEDGE ); break; + } + } +} + +bool PrinterController::isShowDialogs() const +{ + sal_Bool bApi = getBoolProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsApi" ) ), sal_False ); + return ! bApi && ! Application::IsHeadlessModeEnabled(); +} + +bool PrinterController::isDirectPrint() const +{ + sal_Bool bDirect = getBoolProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsDirect" ) ), sal_False ); + return bDirect == sal_True; +} + +sal_Bool PrinterController::getBoolProperty( const rtl::OUString& i_rProperty, sal_Bool i_bFallback ) const +{ + sal_Bool bRet = i_bFallback; + const com::sun::star::beans::PropertyValue* pVal = getValue( i_rProperty ); + if( pVal ) + pVal->Value >>= bRet; + return bRet; +} + +/* + * PrinterOptionsHelper +**/ +Any PrinterOptionsHelper::getValue( const rtl::OUString& i_rPropertyName ) const +{ + Any aRet; + boost::unordered_map< rtl::OUString, Any, rtl::OUStringHash >::const_iterator it = + m_aPropertyMap.find( i_rPropertyName ); + if( it != m_aPropertyMap.end() ) + aRet = it->second; + return aRet; +} + +void PrinterOptionsHelper::setValue( const rtl::OUString& i_rPropertyName, const Any& i_rValue ) +{ + m_aPropertyMap[ i_rPropertyName ] = i_rValue; +} + +bool PrinterOptionsHelper::hasProperty( const rtl::OUString& i_rPropertyName ) const +{ + Any aRet; + boost::unordered_map< rtl::OUString, Any, rtl::OUStringHash >::const_iterator it = + m_aPropertyMap.find( i_rPropertyName ); + return it != m_aPropertyMap.end(); +} + +sal_Bool PrinterOptionsHelper::getBoolValue( const rtl::OUString& i_rPropertyName, sal_Bool i_bDefault ) const +{ + sal_Bool bRet = sal_False; + Any aVal( getValue( i_rPropertyName ) ); + return (aVal >>= bRet) ? bRet : i_bDefault; +} + +sal_Int64 PrinterOptionsHelper::getIntValue( const rtl::OUString& i_rPropertyName, sal_Int64 i_nDefault ) const +{ + sal_Int64 nRet = 0; + Any aVal( getValue( i_rPropertyName ) ); + return (aVal >>= nRet) ? nRet : i_nDefault; +} + +rtl::OUString PrinterOptionsHelper::getStringValue( const rtl::OUString& i_rPropertyName, const rtl::OUString& i_rDefault ) const +{ + rtl::OUString aRet; + Any aVal( getValue( i_rPropertyName ) ); + return (aVal >>= aRet) ? aRet : i_rDefault; +} + +bool PrinterOptionsHelper::processProperties( const Sequence< PropertyValue >& i_rNewProp, + std::set< rtl::OUString >* o_pChangeProp ) +{ + bool bChanged = false; + + // clear the changed set + if( o_pChangeProp ) + o_pChangeProp->clear(); + + sal_Int32 nElements = i_rNewProp.getLength(); + const PropertyValue* pVals = i_rNewProp.getConstArray(); + for( sal_Int32 i = 0; i < nElements; i++ ) + { + bool bElementChanged = false; + boost::unordered_map< rtl::OUString, Any, rtl::OUStringHash >::iterator it = + m_aPropertyMap.find( pVals[ i ].Name ); + if( it != m_aPropertyMap.end() ) + { + if( it->second != pVals[ i ].Value ) + bElementChanged = true; + } + else + bElementChanged = true; + + if( bElementChanged ) + { + if( o_pChangeProp ) + o_pChangeProp->insert( pVals[ i ].Name ); + m_aPropertyMap[ pVals[i].Name ] = pVals[i].Value; + bChanged = true; + } + } + return bChanged; +} + +void PrinterOptionsHelper::appendPrintUIOptions( uno::Sequence< beans::PropertyValue >& io_rProps ) const +{ + if( m_aUIProperties.getLength() > 0 ) + { + sal_Int32 nIndex = io_rProps.getLength(); + io_rProps.realloc( nIndex+1 ); + PropertyValue aVal; + aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ExtraPrintUIOptions" ) ); + aVal.Value = makeAny( m_aUIProperties ); + io_rProps[ nIndex ] = aVal; + } +} + +Any PrinterOptionsHelper::getUIControlOpt( const rtl::OUString& i_rTitle, + const Sequence< rtl::OUString >& i_rHelpIds, + const rtl::OUString& i_rType, + const PropertyValue* i_pVal, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions + ) +{ + sal_Int32 nElements = + 1 // ControlType + + (i_rTitle.getLength() ? 1 : 0) // Text + + (i_rHelpIds.getLength() ? 1 : 0) // HelpId + + (i_pVal ? 1 : 0) // Property + + i_rControlOptions.maAddProps.getLength() // additional props + + (i_rControlOptions.maGroupHint.getLength() ? 1 : 0) // grouping + + (i_rControlOptions.mbInternalOnly ? 1 : 0) // internal hint + + (i_rControlOptions.mbEnabled ? 0 : 1) // enabled + ; + if( i_rControlOptions.maDependsOnName.getLength() ) + { + nElements += 1; + if( i_rControlOptions.mnDependsOnEntry != -1 ) + nElements += 1; + if( i_rControlOptions.mbAttachToDependency ) + nElements += 1; + } + + Sequence< PropertyValue > aCtrl( nElements ); + sal_Int32 nUsed = 0; + if( i_rTitle.getLength() ) + { + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" ) ); + aCtrl[nUsed++].Value = makeAny( i_rTitle ); + } + if( i_rHelpIds.getLength() ) + { + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HelpId" ) ); + aCtrl[nUsed++].Value = makeAny( i_rHelpIds ); + } + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ControlType" ) ); + aCtrl[nUsed++].Value = makeAny( i_rType ); + if( i_pVal ) + { + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Property" ) ); + aCtrl[nUsed++].Value = makeAny( *i_pVal ); + } + if( i_rControlOptions.maDependsOnName.getLength() ) + { + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DependsOnName" ) ); + aCtrl[nUsed++].Value = makeAny( i_rControlOptions.maDependsOnName ); + if( i_rControlOptions.mnDependsOnEntry != -1 ) + { + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DependsOnEntry" ) ); + aCtrl[nUsed++].Value = makeAny( i_rControlOptions.mnDependsOnEntry ); + } + if( i_rControlOptions.mbAttachToDependency ) + { + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AttachToDependency" ) ); + aCtrl[nUsed++].Value = makeAny( i_rControlOptions.mbAttachToDependency ); + } + } + if( i_rControlOptions.maGroupHint.getLength() ) + { + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "GroupingHint" ) ); + aCtrl[nUsed++].Value <<= i_rControlOptions.maGroupHint; + } + if( i_rControlOptions.mbInternalOnly ) + { + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "InternalUIOnly" ) ); + aCtrl[nUsed++].Value <<= sal_True; + } + if( ! i_rControlOptions.mbEnabled ) + { + aCtrl[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Enabled" ) ); + aCtrl[nUsed++].Value <<= sal_False; + } + + sal_Int32 nAddProps = i_rControlOptions.maAddProps.getLength(); + for( sal_Int32 i = 0; i < nAddProps; i++ ) + aCtrl[ nUsed++ ] = i_rControlOptions.maAddProps[i]; + + DBG_ASSERT( nUsed == nElements, "nUsed != nElements, probable heap corruption" ); + + return makeAny( aCtrl ); +} + +Any PrinterOptionsHelper::getGroupControlOpt( const rtl::OUString& i_rTitle, const rtl::OUString& i_rHelpId ) +{ + Sequence< rtl::OUString > aHelpId; + if( i_rHelpId.getLength() > 0 ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Group" ) ) ); +} + +Any PrinterOptionsHelper::getSubgroupControlOpt( const rtl::OUString& i_rTitle, + const rtl::OUString& i_rHelpId, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions + ) +{ + Sequence< rtl::OUString > aHelpId; + if( i_rHelpId.getLength() > 0 ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Subgroup" ) ), + NULL, i_rControlOptions ); +} + +Any PrinterOptionsHelper::getBoolControlOpt( const rtl::OUString& i_rTitle, + const rtl::OUString& i_rHelpId, + const rtl::OUString& i_rProperty, + sal_Bool i_bValue, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions + ) +{ + Sequence< rtl::OUString > aHelpId; + if( i_rHelpId.getLength() > 0 ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + PropertyValue aVal; + aVal.Name = i_rProperty; + aVal.Value = makeAny( i_bValue ); + return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Bool" ) ), &aVal, i_rControlOptions ); +} + +Any PrinterOptionsHelper::getChoiceControlOpt( const rtl::OUString& i_rTitle, + const Sequence< rtl::OUString >& i_rHelpId, + const rtl::OUString& i_rProperty, + const Sequence< rtl::OUString >& i_rChoices, + sal_Int32 i_nValue, + const rtl::OUString& i_rType, + const Sequence< sal_Bool >& i_rDisabledChoices, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions + ) +{ + UIControlOptions aOpt( i_rControlOptions ); + sal_Int32 nUsed = aOpt.maAddProps.getLength(); + aOpt.maAddProps.realloc( nUsed + 1 + (i_rDisabledChoices.getLength() ? 1 : 0) ); + aOpt.maAddProps[nUsed].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Choices" ) ); + aOpt.maAddProps[nUsed].Value = makeAny( i_rChoices ); + if( i_rDisabledChoices.getLength() ) + { + aOpt.maAddProps[nUsed+1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ChoicesDisabled" ) ); + aOpt.maAddProps[nUsed+1].Value = makeAny( i_rDisabledChoices ); + } + + PropertyValue aVal; + aVal.Name = i_rProperty; + aVal.Value = makeAny( i_nValue ); + return getUIControlOpt( i_rTitle, i_rHelpId, i_rType, &aVal, aOpt ); +} + +Any PrinterOptionsHelper::getRangeControlOpt( const rtl::OUString& i_rTitle, + const rtl::OUString& i_rHelpId, + const rtl::OUString& i_rProperty, + sal_Int32 i_nValue, + sal_Int32 i_nMinValue, + sal_Int32 i_nMaxValue, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions + ) +{ + UIControlOptions aOpt( i_rControlOptions ); + if( i_nMaxValue >= i_nMinValue ) + { + sal_Int32 nUsed = aOpt.maAddProps.getLength(); + aOpt.maAddProps.realloc( nUsed + 2 ); + aOpt.maAddProps[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MinValue" ) ); + aOpt.maAddProps[nUsed++].Value = makeAny( i_nMinValue ); + aOpt.maAddProps[nUsed ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MaxValue" ) ); + aOpt.maAddProps[nUsed++].Value = makeAny( i_nMaxValue ); + } + + Sequence< rtl::OUString > aHelpId; + if( i_rHelpId.getLength() > 0 ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + PropertyValue aVal; + aVal.Name = i_rProperty; + aVal.Value = makeAny( i_nValue ); + return getUIControlOpt( i_rTitle, + aHelpId, + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Range" ) ), + &aVal, + aOpt + ); +} + +Any PrinterOptionsHelper::getEditControlOpt( const rtl::OUString& i_rTitle, + const rtl::OUString& i_rHelpId, + const rtl::OUString& i_rProperty, + const rtl::OUString& i_rValue, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions + ) +{ + Sequence< rtl::OUString > aHelpId; + if( i_rHelpId.getLength() > 0 ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + PropertyValue aVal; + aVal.Name = i_rProperty; + aVal.Value = makeAny( i_rValue ); + return getUIControlOpt( i_rTitle, + aHelpId, + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Edit" ) ), + &aVal, + i_rControlOptions + ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/regband.cxx b/vcl/source/gdi/regband.cxx new file mode 100644 index 000000000000..21c82cd51f3a --- /dev/null +++ b/vcl/source/gdi/regband.cxx @@ -0,0 +1,972 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/debug.hxx> +#include <vcl/salbtype.hxx> +#include <vcl/regband.hxx> + +#include <algorithm> + + +// ======================================================================= +// +// ImplRegionBand +// +// Jedes Band enthaelt die zwischen der enthaltenen Ober- und Untergrenze +// enthaltenen Rechtecke. Bei den Operationen Union, Intersect, XOr und +// Exclude werden immer Rechtecke der gleichen Hoehe ausgewerte; die +// Grenzen der Baender sind immer so zu waehlen, dasz dies moeglich ist. +// +// Die Rechtecke in den Baendern werden nach Moeglichkeit zusammengefaszt. +// +// Bei der Umwandlung von Polygonen werden alle Punkte des Polygons +// in die einzelnen Baender eingetragen (sie stehen fuer jedes Band als +// Punkte in einer Liste). Nach dem Eintragen der Punkte werden diese +// in Rechtecke umgewandelt und die Liste der Punkte geloescht. +// +// ----------------------------------------------------------------------- + +ImplRegionBand::ImplRegionBand( long nTop, long nBottom ) +{ + // save boundaries + mnYTop = nTop; + mnYBottom = nBottom; + + // initialize lists + mpNextBand = NULL; + mpPrevBand = NULL; + mpFirstSep = NULL; + mpFirstBandPoint = NULL; + mbTouched = sal_False; +} + +// ----------------------------------------------------------------------- + +ImplRegionBand::ImplRegionBand( + const ImplRegionBand& rRegionBand, + const bool bIgnorePoints) +{ + // copy boundaries + mnYTop = rRegionBand.mnYTop; + mnYBottom = rRegionBand.mnYBottom; + mbTouched = rRegionBand.mbTouched; + + // initialisation + mpNextBand = NULL; + mpPrevBand = NULL; + mpFirstSep = NULL; + mpFirstBandPoint = NULL; + + // copy all elements of the list with separations + ImplRegionBandSep* pNewSep; + ImplRegionBandSep* pPrevSep = 0; + ImplRegionBandSep* pSep = rRegionBand.mpFirstSep; + while ( pSep ) + { + // create new and copy data + pNewSep = new ImplRegionBandSep; + pNewSep->mnXLeft = pSep->mnXLeft; + pNewSep->mnXRight = pSep->mnXRight; + pNewSep->mbRemoved = pSep->mbRemoved; + pNewSep->mpNextSep = NULL; + if ( pSep == rRegionBand.mpFirstSep ) + mpFirstSep = pNewSep; + else + pPrevSep->mpNextSep = pNewSep; + + pPrevSep = pNewSep; + pSep = pSep->mpNextSep; + } + + if ( ! bIgnorePoints) + { + // Copy points. + ImplRegionBandPoint* pPoint = rRegionBand.mpFirstBandPoint; + ImplRegionBandPoint* pPrevPointCopy = NULL; + while (pPoint != NULL) + { + ImplRegionBandPoint* pPointCopy = new ImplRegionBandPoint(); + pPointCopy->mnX = pPoint->mnX; + pPointCopy->mnLineId = pPoint->mnLineId; + pPointCopy->mbEndPoint = pPoint->mbEndPoint; + pPointCopy->meLineType = pPoint->meLineType; + + if (pPrevPointCopy != NULL) + pPrevPointCopy->mpNextBandPoint = pPointCopy; + else + mpFirstBandPoint = pPointCopy; + + pPrevPointCopy = pPointCopy; + pPoint = pPoint->mpNextBandPoint; + } + } +} + +// ----------------------------------------------------------------------- + +ImplRegionBand::~ImplRegionBand() +{ + DBG_ASSERT( mpFirstBandPoint == NULL, "ImplRegionBand::~ImplRegionBand -> pointlist not empty" ); + + // delete elements of the list + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + ImplRegionBandSep* pTempSep = pSep->mpNextSep; + delete pSep; + pSep = pTempSep; + } + + // delete elements of the list + ImplRegionBandPoint* pPoint = mpFirstBandPoint; + while ( pPoint ) + { + ImplRegionBandPoint* pTempPoint = pPoint->mpNextBandPoint; + delete pPoint; + pPoint = pTempPoint; + } +} + +// ----------------------------------------------------------------------- +// +// generate separations from lines and process union with existing +// separations + +void ImplRegionBand::ProcessPoints() +{ + // check Pointlist + ImplRegionBandPoint* pRegionBandPoint = mpFirstBandPoint; + while ( pRegionBandPoint ) + { + // within list? + if ( pRegionBandPoint && pRegionBandPoint->mpNextBandPoint ) + { + // start/stop? + if ( pRegionBandPoint->mbEndPoint && pRegionBandPoint->mpNextBandPoint->mbEndPoint ) + { + // same direction? -> remove next point! + if ( pRegionBandPoint->meLineType == pRegionBandPoint->mpNextBandPoint->meLineType ) + { + ImplRegionBandPoint* pSaveRegionBandPoint = pRegionBandPoint->mpNextBandPoint; + pRegionBandPoint->mpNextBandPoint = pRegionBandPoint->mpNextBandPoint->mpNextBandPoint; + delete pSaveRegionBandPoint; + } + } + } + + // continue with next element in the list + pRegionBandPoint = pRegionBandPoint->mpNextBandPoint; + } + + pRegionBandPoint = mpFirstBandPoint; + while ( pRegionBandPoint && pRegionBandPoint->mpNextBandPoint ) + { + Union( pRegionBandPoint->mnX, pRegionBandPoint->mpNextBandPoint->mnX ); + + ImplRegionBandPoint* pNextBandPoint = pRegionBandPoint->mpNextBandPoint->mpNextBandPoint; + + // remove allready processed points + delete pRegionBandPoint->mpNextBandPoint; + delete pRegionBandPoint; + + // continue with next element in the list + pRegionBandPoint = pNextBandPoint; + } + + // remove last element if necessary + if ( pRegionBandPoint ) + delete pRegionBandPoint; + + // list is now empty + mpFirstBandPoint = NULL; +} + +// ----------------------------------------------------------------------- +// +// generate separations from lines and process union with existing +// separations + +sal_Bool ImplRegionBand::InsertPoint( long nX, long nLineId, + sal_Bool bEndPoint, LineType eLineType ) +{ + if ( !mpFirstBandPoint ) + { + mpFirstBandPoint = new ImplRegionBandPoint; + mpFirstBandPoint->mnX = nX; + mpFirstBandPoint->mnLineId = nLineId; + mpFirstBandPoint->mbEndPoint = bEndPoint; + mpFirstBandPoint->meLineType = eLineType; + mpFirstBandPoint->mpNextBandPoint = NULL; + return sal_True; + } + + // look if line allready touched the band + ImplRegionBandPoint* pRegionBandPoint = mpFirstBandPoint; + ImplRegionBandPoint* pLastTestedRegionBandPoint = NULL; + while( pRegionBandPoint ) + { + if ( pRegionBandPoint->mnLineId == nLineId ) + { + if ( bEndPoint ) + { + if( !pRegionBandPoint->mbEndPoint ) + { + // remove old band point + if( !mpFirstBandPoint->mpNextBandPoint ) + { + // if we've only got one point => replace first point + pRegionBandPoint->mnX = nX; + pRegionBandPoint->mbEndPoint = sal_True; + return sal_True; + } + else + { + // remove current point + if( !pLastTestedRegionBandPoint ) + { + // remove and delete old first point + ImplRegionBandPoint* pSaveBandPoint = mpFirstBandPoint; + mpFirstBandPoint = mpFirstBandPoint->mpNextBandPoint; + delete pSaveBandPoint; + } + else + { + // remove and delete current band point + pLastTestedRegionBandPoint->mpNextBandPoint = pRegionBandPoint->mpNextBandPoint; + delete pRegionBandPoint; + } + + break; + } + } + } + else + return sal_False; + } + + // use next element + pLastTestedRegionBandPoint = pRegionBandPoint; + pRegionBandPoint = pRegionBandPoint->mpNextBandPoint; + } + + // search appropriate position and insert point into the list + ImplRegionBandPoint* pNewRegionBandPoint; + + pRegionBandPoint = mpFirstBandPoint; + pLastTestedRegionBandPoint = NULL; + while ( pRegionBandPoint ) + { + // new point completly left? -> insert as first point + if ( nX <= pRegionBandPoint->mnX ) + { + pNewRegionBandPoint = new ImplRegionBandPoint; + pNewRegionBandPoint->mnX = nX; + pNewRegionBandPoint->mnLineId = nLineId; + pNewRegionBandPoint->mbEndPoint = bEndPoint; + pNewRegionBandPoint->meLineType = eLineType; + pNewRegionBandPoint->mpNextBandPoint = pRegionBandPoint; + + // connections to the new point + if ( !pLastTestedRegionBandPoint ) + mpFirstBandPoint = pNewRegionBandPoint; + else + pLastTestedRegionBandPoint->mpNextBandPoint = pNewRegionBandPoint; + + return sal_True; + } + + // use next element + pLastTestedRegionBandPoint = pRegionBandPoint; + pRegionBandPoint = pRegionBandPoint->mpNextBandPoint; + } + + // not inserted -> add to the end of the list + pNewRegionBandPoint = new ImplRegionBandPoint; + pNewRegionBandPoint->mnX = nX; + pNewRegionBandPoint->mnLineId = nLineId; + pNewRegionBandPoint->mbEndPoint = bEndPoint; + pNewRegionBandPoint->meLineType = eLineType; + pNewRegionBandPoint->mpNextBandPoint = NULL; + + // connections to the new point + pLastTestedRegionBandPoint->mpNextBandPoint = pNewRegionBandPoint; + + return sal_True; +} + +// ----------------------------------------------------------------------- + +void ImplRegionBand::MoveX( long nHorzMove ) +{ + // move all x-separations + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + pSep->mnXLeft += nHorzMove; + pSep->mnXRight += nHorzMove; + pSep = pSep->mpNextSep; + } +} + +// ----------------------------------------------------------------------- + +void ImplRegionBand::ScaleX( double fHorzScale ) +{ + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + pSep->mnXLeft = FRound( pSep->mnXLeft * fHorzScale ); + pSep->mnXRight = FRound( pSep->mnXRight * fHorzScale ); + pSep = pSep->mpNextSep; + } +} + +// ----------------------------------------------------------------------- +// +// combine overlaping sparations + +sal_Bool ImplRegionBand::OptimizeBand() +{ + ImplRegionBandSep* pPrevSep = 0; + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + // remove? + if ( pSep->mbRemoved || (pSep->mnXRight < pSep->mnXLeft) ) + { + ImplRegionBandSep* pOldSep = pSep; + if ( pSep == mpFirstSep ) + mpFirstSep = pSep->mpNextSep; + else + pPrevSep->mpNextSep = pSep->mpNextSep; + pSep = pSep->mpNextSep; + delete pOldSep; + continue; + } + + // overlaping separations? -> combine! + if ( pSep->mpNextSep ) + { + if ( (pSep->mnXRight+1) >= pSep->mpNextSep->mnXLeft ) + { + if ( pSep->mpNextSep->mnXRight > pSep->mnXRight ) + pSep->mnXRight = pSep->mpNextSep->mnXRight; + + ImplRegionBandSep* pOldSep = pSep->mpNextSep; + pSep->mpNextSep = pOldSep->mpNextSep; + delete pOldSep; + continue; + } + } + + pPrevSep = pSep; + pSep = pSep->mpNextSep; + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +void ImplRegionBand::Union( long nXLeft, long nXRight ) +{ + DBG_ASSERT( nXLeft <= nXRight, "ImplRegionBand::Union(): nxLeft > nXRight" ); + + // band empty? -> add element + if ( !mpFirstSep ) + { + mpFirstSep = new ImplRegionBandSep; + mpFirstSep->mnXLeft = nXLeft; + mpFirstSep->mnXRight = nXRight; + mpFirstSep->mbRemoved = sal_False; + mpFirstSep->mpNextSep = NULL; + return; + } + + // process real union + ImplRegionBandSep* pNewSep; + ImplRegionBandSep* pPrevSep = 0; + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + // new separation completely inside? nothing to do! + if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) ) + return; + + // new separation completly left? -> new separation! + if ( nXRight < pSep->mnXLeft ) + { + pNewSep = new ImplRegionBandSep; + pNewSep->mnXLeft = nXLeft; + pNewSep->mnXRight = nXRight; + pNewSep->mbRemoved = sal_False; + + pNewSep->mpNextSep = pSep; + if ( pSep == mpFirstSep ) + mpFirstSep = pNewSep; + else + pPrevSep->mpNextSep = pNewSep; + break; + } + + // new separation overlaping from left? -> extend boundary + if ( (nXRight >= pSep->mnXLeft) && (nXLeft <= pSep->mnXLeft) ) + pSep->mnXLeft = nXLeft; + + // new separation overlaping from right? -> extend boundary + if ( (nXLeft <= pSep->mnXRight) && (nXRight > pSep->mnXRight) ) + { + pSep->mnXRight = nXRight; + break; + } + + // not inserted, but last element? -> add to the end of the list + if ( !pSep->mpNextSep && (nXLeft > pSep->mnXRight) ) + { + pNewSep = new ImplRegionBandSep; + pNewSep->mnXLeft = nXLeft; + pNewSep->mnXRight = nXRight; + pNewSep->mbRemoved = sal_False; + + pSep->mpNextSep = pNewSep; + pNewSep->mpNextSep = NULL; + break; + } + + pPrevSep = pSep; + pSep = pSep->mpNextSep; + } + + OptimizeBand(); +} + +// ----------------------------------------------------------------------- + +void ImplRegionBand::Intersect( long nXLeft, long nXRight ) +{ + DBG_ASSERT( nXLeft <= nXRight, "ImplRegionBand::Intersect(): nxLeft > nXRight" ); + + // band has been touched + mbTouched = sal_True; + + // band empty? -> nothing to do + if ( !mpFirstSep ) + return; + + // process real intersection + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + // new separation completly outside? -> remove separation + if ( (nXRight < pSep->mnXLeft) || (nXLeft > pSep->mnXRight) ) + // will be removed from the optimizer + pSep->mbRemoved = sal_True; + + // new separation overlaping from left? -> reduce right boundary + if ( (nXLeft <= pSep->mnXLeft) && + (nXRight <= pSep->mnXRight) && + (nXRight >= pSep->mnXLeft) ) + pSep->mnXRight = nXRight; + + // new separation overlaping from right? -> reduce right boundary + if ( (nXLeft >= pSep->mnXLeft) && + (nXLeft <= pSep->mnXRight) && + (nXRight >= pSep->mnXRight) ) + pSep->mnXLeft = nXLeft; + + // new separation within the actual one? -> reduce both boundaries + if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) ) + { + pSep->mnXRight = nXRight; + pSep->mnXLeft = nXLeft; + } + + pSep = pSep->mpNextSep; + } + + OptimizeBand(); +} + +// ----------------------------------------------------------------------- + +void ImplRegionBand::Exclude( long nXLeft, long nXRight ) +{ + DBG_ASSERT( nXLeft <= nXRight, "ImplRegionBand::Exclude(): nxLeft > nXRight" ); + + // band has been touched + mbTouched = sal_True; + + // band empty? -> nothing to do + if ( !mpFirstSep ) + return; + + // process real exclusion + ImplRegionBandSep* pNewSep; + ImplRegionBandSep* pPrevSep = 0; + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + sal_Bool bSepProcessed = sal_False; + + // new separation completely overlapping? -> remove separation + if ( (nXLeft <= pSep->mnXLeft) && (nXRight >= pSep->mnXRight) ) + { + // will be removed from the optimizer + pSep->mbRemoved = sal_True; + bSepProcessed = sal_True; + } + + // new separation overlaping from left? -> reduce boundary + if ( !bSepProcessed ) + { + if ( (nXRight >= pSep->mnXLeft) && (nXLeft <= pSep->mnXLeft) ) + { + pSep->mnXLeft = nXRight+1; + bSepProcessed = sal_True; + } + } + + // new separation overlaping from right? -> reduce boundary + if ( !bSepProcessed ) + { + if ( (nXLeft <= pSep->mnXRight) && (nXRight > pSep->mnXRight) ) + { + pSep->mnXRight = nXLeft-1; + bSepProcessed = sal_True; + } + } + + // new separation within the actual one? -> reduce boundary + // and add new entry for reminder + if ( !bSepProcessed ) + { + if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) ) + { + pNewSep = new ImplRegionBandSep; + pNewSep->mnXLeft = pSep->mnXLeft; + pNewSep->mnXRight = nXLeft-1; + pNewSep->mbRemoved = sal_False; + + pSep->mnXLeft = nXRight+1; + + // connections from the new separation + pNewSep->mpNextSep = pSep; + + // connections to the new separation + if ( pSep == mpFirstSep ) + mpFirstSep = pNewSep; + else + pPrevSep->mpNextSep = pNewSep; + } + } + + pPrevSep = pSep; + pSep = pSep->mpNextSep; + } + + OptimizeBand(); +} + +// ----------------------------------------------------------------------- + +void ImplRegionBand::XOr( long nXLeft, long nXRight ) +{ + DBG_ASSERT( nXLeft <= nXRight, "ImplRegionBand::XOr(): nxLeft > nXRight" ); + + // #i46602# Reworked rectangle Xor + // + // In general, we can distinguish 11 cases of intersection + // (details below). The old implementation explicitely handled 7 + // cases (numbered in the order of appearance, use CVS to get your + // hands on the old version), therefore, I've sticked to that + // order, and added four more cases. The code below references + // those numbers via #1, #2, etc. + // + // Num Mnem newX:oldX newY:oldY Description Result Can quit? + // + // #1 Empty band - - The band is empty, thus, simply add new bandSep just add Yes + // + // #2 apart - - The rectangles are disjunct, add new one as is just add Yes + // + // #3 atop == == The rectangles are _exactly_ the same, remove existing just remove Yes + // + // #4 around < > The new rectangle extends the old to both sides intersect No + // + // #5 left < < The new rectangle is left of the old (but intersects) intersect Yes + // + // #5b left-atop < == The new is left of the old, and coincides on the right intersect Yes + // + // #6 right > > The new is right of the old (but intersects) intersect No + // + // #6b right-atop == > The new is right of the old, and coincides on the left intersect No + // + // #7 inside > < The new is fully inside the old intersect Yes + // + // #8 inside-right > == The new is fully inside the old, coincides on the right intersect Yes + // + // #9 inside-left == < The new is fully inside the old, coincides on the left intersect Yes + // + // + // Then, to correctly perform XOr, the segment that's switched off + // (i.e. the overlapping part of the old and the new segment) must + // be extended by one pixel value at each border: + // 1 1 + // 0 4 0 4 + // 111100000001111 + // + // Clearly, the leading band sep now goes from 0 to 3, and the + // trailing band sep from 11 to 14. This mimicks the xor look of a + // bitmap operation. + // + + // band empty? -> add element + if ( !mpFirstSep ) + { + mpFirstSep = new ImplRegionBandSep; + mpFirstSep->mnXLeft = nXLeft; + mpFirstSep->mnXRight = nXRight; + mpFirstSep->mbRemoved = sal_False; + mpFirstSep->mpNextSep = NULL; + return; + } + + // process real xor + ImplRegionBandSep* pNewSep; + ImplRegionBandSep* pPrevSep = 0; + ImplRegionBandSep* pSep = mpFirstSep; + + while ( pSep ) + { + long nOldLeft( pSep->mnXLeft ); + long nOldRight( pSep->mnXRight ); + + // did the current segment actually touch the new rect? If + // not, skip all comparisons, go on, loop and try to find + // intersecting bandSep + if( nXLeft <= nOldRight ) + { + if( nXRight < nOldLeft ) + { + // #2 + + // add _before_ current bandSep + pNewSep = new ImplRegionBandSep; + pNewSep->mnXLeft = nXLeft; + pNewSep->mnXRight = nXRight; + pNewSep->mpNextSep = pSep; + pNewSep->mbRemoved = sal_False; + + // connections from the new separation + pNewSep->mpNextSep = pSep; + + // connections to the new separation + if ( pSep == mpFirstSep ) + mpFirstSep = pNewSep; + else + pPrevSep->mpNextSep = pNewSep; + pPrevSep = NULL; // do not run accidentally into the "right" case when breaking the loop + break; + } + else if( nXLeft == nOldLeft && nXRight == nOldRight ) + { + // #3 + pSep->mbRemoved = sal_True; + pPrevSep = NULL; // do not run accidentally into the "right" case when breaking the loop + break; + } + else if( nXLeft != nOldLeft && nXRight == nOldRight ) + { + // # 5b, 8 + if( nXLeft < nOldLeft ) + { + nXRight = nOldLeft; // 5b + } + else + { + nXRight = nXLeft; // 8 + nXLeft = nOldLeft; + } + + pSep->mnXLeft = nXLeft; + pSep->mnXRight = nXRight-1; + + pPrevSep = NULL; // do not run accidentally into the "right" case when breaking the loop + break; + } + else if( nXLeft == nOldLeft && nXRight != nOldRight ) + { + // # 6b, 9 + + if( nXRight > nOldRight ) + { + nXLeft = nOldRight+1; // 6b + + // cannot break here, simply mark segment as removed, + // and go on with adapted nXLeft/nXRight + pSep->mbRemoved = sal_True; + } + else + { + pSep->mnXLeft = nXRight+1; // 9 + + pPrevSep = NULL; // do not run accidentally into the "right" case when breaking the loop + break; + } + } + else // if( nXLeft != nOldLeft && nXRight != nOldRight ) follows automatically + { + // #4,5,6,7 + DBG_ASSERT( nXLeft != nOldLeft && nXRight != nOldRight, + "ImplRegionBand::XOr(): Case 4,5,6,7 expected all coordinates to be not equal!" ); + + // The plain-jane check would look like this: + // + // if( nXLeft < nOldLeft ) + // { + // // #4,5 + // if( nXRight > nOldRight ) + // { + // // #4 + // } + // else + // { + // // #5 done! + // } + // } + // else + // { + // // #6,7 + // if( nXRight > nOldRight ) + // { + // // #6 + // } + // else + // { + // // #7 done! + // } + // } + // + // but since we generally don't have to care whether + // it's 4 or 6 (only that we must not stop processing + // here), condensed that in such a way that only the + // coordinates get shuffled into correct ordering. + + if( nXLeft < nOldLeft ) + ::std::swap( nOldLeft, nXLeft ); + + bool bDone( false ); + + if( nXRight < nOldRight ) + { + ::std::swap( nOldRight, nXRight ); + bDone = true; + } + + // now, nOldLeft<nXLeft<=nOldRight<nXRight always + // holds. Note that we need the nXLeft<=nOldRight here, as + // the intersection part might be only one pixel (original + // nXLeft==nXRight) + DBG_ASSERT( nOldLeft<nXLeft && nXLeft<=nOldRight && nOldRight<nXRight, + "ImplRegionBand::XOr(): Case 4,5,6,7 expected coordinates to be ordered now!" ); + + pSep->mnXLeft = nOldLeft; + pSep->mnXRight = nXLeft-1; + + nXLeft = nOldRight+1; + // nxRight is already setup correctly + + if( bDone ) + { + // add behind current bandSep + pNewSep = new ImplRegionBandSep; + + pNewSep->mnXLeft = nXLeft; + pNewSep->mnXRight = nXRight; + pNewSep->mpNextSep = pSep->mpNextSep; + pNewSep->mbRemoved = sal_False; + + // connections from the new separation + pSep->mpNextSep = pNewSep; + + pPrevSep = NULL; // do not run accidentally into the "right" case when breaking the loop + break; + } + } + } + + pPrevSep = pSep; + pSep = pSep->mpNextSep; + } + + // new separation completely right of existing bandSeps ? + if( pPrevSep && nXLeft >= pPrevSep->mnXRight ) + { + pNewSep = new ImplRegionBandSep; + pNewSep->mnXLeft = nXLeft; + pNewSep->mnXRight = nXRight; + pNewSep->mpNextSep = NULL; + pNewSep->mbRemoved = sal_False; + + // connections from the new separation + pPrevSep->mpNextSep = pNewSep; + } + + OptimizeBand(); +} + +// ----------------------------------------------------------------------- + +sal_Bool ImplRegionBand::IsInside( long nX ) +{ + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + if ( (pSep->mnXLeft <= nX) && (pSep->mnXRight >= nX) ) + return sal_True; + + pSep = pSep->mpNextSep; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool ImplRegionBand::IsOver( long nLeft, long nRight ) +{ + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + if ( (pSep->mnXLeft < nRight) && (pSep->mnXRight > nLeft) ) + return sal_True; + + pSep = pSep->mpNextSep; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool ImplRegionBand::IsInside( long nLeft, long nRight ) +{ + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep ) + { + if ( (pSep->mnXLeft >= nLeft) && (nRight <= pSep->mnXRight) ) + return sal_True; + + pSep = pSep->mpNextSep; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +long ImplRegionBand::GetXLeftBoundary() const +{ + DBG_ASSERT( mpFirstSep != NULL, "ImplRegionBand::XLeftBoundary -> no separation in band!" ); + + return mpFirstSep->mnXLeft; +} + +// ----------------------------------------------------------------------- + +long ImplRegionBand::GetXRightBoundary() const +{ + DBG_ASSERT( mpFirstSep != NULL, "ImplRegionBand::XRightBoundary -> no separation in band!" ); + + // search last separation + ImplRegionBandSep* pSep = mpFirstSep; + while ( pSep->mpNextSep ) + pSep = pSep->mpNextSep; + return pSep->mnXRight; +} + +// ----------------------------------------------------------------------- + +sal_Bool ImplRegionBand::operator==( const ImplRegionBand& rRegionBand ) const +{ + ImplRegionBandSep* pOwnRectBandSep = mpFirstSep; + ImplRegionBandSep* pSecondRectBandSep = rRegionBand.mpFirstSep; + while ( pOwnRectBandSep && pSecondRectBandSep ) + { + // get boundaries of current rectangle + long nOwnXLeft = pOwnRectBandSep->mnXLeft; + long nSecondXLeft = pSecondRectBandSep->mnXLeft; + if ( nOwnXLeft != nSecondXLeft ) + return sal_False; + + long nOwnXRight = pOwnRectBandSep->mnXRight; + long nSecondXRight = pSecondRectBandSep->mnXRight; + if ( nOwnXRight != nSecondXRight ) + return sal_False; + + // get next separation from current band + pOwnRectBandSep = pOwnRectBandSep->mpNextSep; + + // get next separation from current band + pSecondRectBandSep = pSecondRectBandSep->mpNextSep; + } + + // differnt number of separations? + if ( pOwnRectBandSep || pSecondRectBandSep ) + return sal_False; + + return sal_True; +} + +// ----------------------------------------------------------------------- + +ImplRegionBand* ImplRegionBand::SplitBand (const sal_Int32 nY) +{ + OSL_ASSERT(nY>mnYTop); + OSL_ASSERT(nY<=mnYBottom); + + // Create a copy of the given band (we tell the constructor to copy the points together + // with the seps.) + ImplRegionBand* pLowerBand = new ImplRegionBand(*this, false); + + // Adapt vertical coordinates. + mnYBottom = nY-1; + pLowerBand->mnYTop = nY; + + // Insert new band into list of bands. + pLowerBand->mpNextBand = mpNextBand; + mpNextBand = pLowerBand; + pLowerBand->mpPrevBand = this; + if (pLowerBand->mpNextBand != NULL) + pLowerBand->mpNextBand->mpPrevBand = pLowerBand; + + return pLowerBand; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/region.cxx b/vcl/source/gdi/region.cxx new file mode 100644 index 000000000000..480a17a9f88b --- /dev/null +++ b/vcl/source/gdi/region.cxx @@ -0,0 +1,3104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <limits.h> +#include <tools/vcompat.hxx> +#include <vcl/salbtype.hxx> +#include <tools/stream.hxx> +#include <tools/debug.hxx> +#include <vcl/region.h> +#include <vcl/region.hxx> +#include <vcl/regband.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +// ======================================================================= +// +// ImplRegionBand +// +// Die Klassen RegionBand/ImplRegionBand speichert Regionen in Form von +// Rechtecken ab. Die Region ist in Y-Richtung in Baendern unterteilt, die +// wiederum ein oder mehrere Rechtecke mit der Hoehe des Bandes enthalten. +// +// Leere Baender werden entfernt. +// +// Polygone und PolyPolygone werden ebenfalls in Rechtecke zerlegt und in +// der Baendern abgelegt. Hierzu werden alle Punkte der einzelnen Polygone +// mit dem Bresenham-Algorithmus berechnet und in die Baender eingetragen. +// Nach der vollstaendigen Berechung aller Kanten werden die entsprechenden +// Rechntecke berechnet + +// ======================================================================= + +static ImplRegionBase aImplNullRegion( 0 ); +static ImplRegionBase aImplEmptyRegion( 0 ); + +// ======================================================================= + +DBG_NAME( Region ) +DBG_NAMEEX( Polygon ) +DBG_NAMEEX( PolyPolygon ) + +namespace { + +/** Return <TRUE/> when the given polygon is rectiliner and oriented so that + all sides are either horizontal or vertical. +*/ +bool ImplIsPolygonRectilinear (const PolyPolygon& rPolyPoly) +{ + // Iterate over all polygons. + const sal_uInt16 nPolyCount = rPolyPoly.Count(); + for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly) + { + const Polygon& aPoly = rPolyPoly.GetObject(nPoly); + + // Iterate over all edges of the current polygon. + const sal_uInt16 nSize = aPoly.GetSize(); + + if (nSize < 2) + continue; + Point aPoint (aPoly.GetPoint(0)); + const Point aLastPoint (aPoint); + for (sal_uInt16 nPoint = 1; nPoint < nSize; ++nPoint) + { + const Point aNextPoint (aPoly.GetPoint(nPoint)); + // When there is at least one edge that is neither vertical nor + // horizontal then the entire polygon is not rectilinear (and + // oriented along primary axes.) + if (aPoint.X() != aNextPoint.X() && aPoint.Y() != aNextPoint.Y()) + return false; + + aPoint = aNextPoint; + } + // Compare closing edge. + if (aLastPoint.X() != aPoint.X() && aLastPoint.Y() != aPoint.Y()) + return false; + } + return true; +} + + + +/** This function is similar to the ImplRegion::InsertBands() method. + It creates a minimal set of missing bands so that the entire vertical + interval from nTop to nBottom is covered by bands. +*/ +void ImplAddMissingBands ( + ImplRegion* pImplRegion, + const long nTop, + const long nBottom) +{ + // Iterate over already existing bands and add missing bands atop the + // first and between two bands. + ImplRegionBand* pPreviousBand = NULL; + ImplRegionBand* pBand = pImplRegion->ImplGetFirstRegionBand(); + long nCurrentTop (nTop); + while (pBand != NULL && nCurrentTop<nBottom) + { + if (nCurrentTop < pBand->mnYTop) + { + // Create new band above the current band. + ImplRegionBand* pAboveBand = new ImplRegionBand( + nCurrentTop, + ::std::min(nBottom,pBand->mnYTop-1)); + pImplRegion->InsertBand(pPreviousBand, pAboveBand); + } + + // Adapt the top of the interval to prevent overlapping bands. + nCurrentTop = ::std::max(nTop, pBand->mnYBottom+1); + + // Advance to next band. + pPreviousBand = pBand; + pBand = pBand->mpNextBand; + } + + // We still have to cover two cases: + // 1. The region does not yet contain any bands. + // 2. The intervall nTop->nBottom extends past the bottom most band. + if (nCurrentTop <= nBottom + && (pBand==NULL || nBottom>pBand->mnYBottom)) + { + // When there is no previous band then the new one will be the + // first. Otherwise the new band is inserted behind the last band. + pImplRegion->InsertBand( + pPreviousBand, + new ImplRegionBand( + nCurrentTop, + nBottom)); + } +} + + + +/** Convert a rectilinear polygon (that is oriented along the primary axes) + to a list of bands. For this special form of polygon we can use an + optimization that prevents the creation of one band per y value. + However, it still is possible that some temporary bands are created that + later can be optimized away. + @param rPolyPolygon + A set of zero, one, or more polygons, nested or not, that are + converted into a list of bands. + @return + A new ImplRegion object is returned that contains the bands that + represent the given poly-polygon. +*/ +ImplRegion* ImplRectilinearPolygonToBands (const PolyPolygon& rPolyPoly) +{ + OSL_ASSERT(ImplIsPolygonRectilinear (rPolyPoly)); + + // Create a new ImplRegion object as container of the bands. + ImplRegion* pImplRegion = new ImplRegion(); + long nLineId = 0L; + + // Iterate over all polygons. + const sal_uInt16 nPolyCount = rPolyPoly.Count(); + for (sal_uInt16 nPoly = 0; nPoly < nPolyCount; ++nPoly) + { + const Polygon& aPoly = rPolyPoly.GetObject(nPoly); + + // Iterate over all edges of the current polygon. + const sal_uInt16 nSize = aPoly.GetSize(); + if (nSize < 2) + continue; + // Avoid fetching every point twice (each point is the start point + // of one and the end point of another edge.) + Point aStart (aPoly.GetPoint(0)); + Point aEnd; + for (sal_uInt16 nPoint = 1; nPoint <= nSize; ++nPoint, aStart=aEnd) + { + // We take the implicit closing edge into account by mapping + // index nSize to 0. + aEnd = aPoly.GetPoint(nPoint%nSize); + if (aStart.Y() == aEnd.Y()) + { + // Horizontal lines are ignored. + continue; + } + + // At this point the line has to be vertical. + OSL_ASSERT(aStart.X() == aEnd.X()); + + // Sort y-coordinates to simplify the algorithm and store the + // direction seperately. The direction is calculated as it is + // in other places (but seems to be the wrong way.) + const long nTop (::std::min(aStart.Y(), aEnd.Y())); + const long nBottom (::std::max(aStart.Y(), aEnd.Y())); + const LineType eLineType (aStart.Y() > aEnd.Y() ? LINE_DESCENDING : LINE_ASCENDING); + + // Make sure that the current line is covered by bands. + ImplAddMissingBands(pImplRegion, nTop,nBottom); + + // Find top-most band that may contain nTop. + ImplRegionBand* pBand = pImplRegion->ImplGetFirstRegionBand(); + while (pBand!=NULL && pBand->mnYBottom < nTop) + pBand = pBand->mpNextBand; + ImplRegionBand* pTopBand = pBand; + // If necessary split the band at nTop so that nTop is contained + // in the lower band. + if (pBand!=NULL + // Prevent the current band from becoming 0 pixel high + && pBand->mnYTop<nTop + // this allows the lowest pixel of the band to be split off + && pBand->mnYBottom>=nTop + // do not split a band that is just one pixel high + && pBand->mnYTop<pBand->mnYBottom) + { + // Split the top band. + pTopBand = pBand->SplitBand(nTop); + } + + // Advance to band that may contain nBottom. + while (pBand!=NULL && pBand->mnYBottom < nBottom) + pBand = pBand->mpNextBand; + // The lowest band may have to be split at nBottom so that + // nBottom itself remains in the upper band. + if (pBand!=NULL + // allow the current band becoming 1 pixel high + && pBand->mnYTop<=nBottom + // prevent splitting off a band that is 0 pixel high + && pBand->mnYBottom>nBottom + // do not split a band that is just one pixel high + && pBand->mnYTop<pBand->mnYBottom) + { + // Split the bottom band. + pBand->SplitBand(nBottom+1); + } + + // Note that we remember the top band (in pTopBand) but not the + // bottom band. The later can be determined by comparing y + // coordinates. + + // Add the x-value as point to all bands in the nTop->nBottom range. + for (pBand=pTopBand; pBand!=NULL&&pBand->mnYTop<=nBottom; pBand=pBand->mpNextBand) + pBand->InsertPoint(aStart.X(), nLineId++, sal_True, eLineType); + } + } + + return pImplRegion; +} + + + + +/** Convert a general polygon (one for which ImplIsPolygonRectilinear() + returns <FALSE/>) to bands. +*/ +ImplRegion* ImplGeneralPolygonToBands ( + const PolyPolygon& rPolyPoly, + const Rectangle& rPolygonBoundingBox) +{ + long nLineID = 0L; + + // initialisation and creation of Bands + ImplRegion* pImplRegion = new ImplRegion(); + pImplRegion->CreateBandRange( rPolygonBoundingBox.Top(), rPolygonBoundingBox.Bottom() ); + + // insert polygons + const sal_uInt16 nPolyCount = rPolyPoly.Count(); + for ( sal_uInt16 nPoly = 0; nPoly < nPolyCount; nPoly++ ) + { + // get reference to current polygon + const Polygon& aPoly = rPolyPoly.GetObject( nPoly ); + const sal_uInt16 nSize = aPoly.GetSize(); + + // not enough points ( <= 2 )? -> nothing to do! + if ( nSize <= 2 ) + continue; + + // band the polygon + for ( sal_uInt16 nPoint = 1; nPoint < nSize; nPoint++ ) + pImplRegion->InsertLine( aPoly.GetPoint(nPoint-1), aPoly.GetPoint(nPoint), nLineID++ ); + + // close polygon with line from first point to last point, if neccesary + const Point rLastPoint = aPoly.GetPoint(nSize-1); + const Point rFirstPoint = aPoly.GetPoint(0); + if ( rLastPoint != rFirstPoint ) + pImplRegion->InsertLine( rLastPoint, rFirstPoint, nLineID++ ); + } + + return pImplRegion; +} + + +} // end of anonymous namespace + + +// ----------------------------------------------------------------------- + +#ifdef DBG_UTIL +const char* ImplDbgTestRegion( const void* pObj ) +{ + Region* pRegion = (Region*)pObj; + ImplRegion* pImplRegion = pRegion->ImplGetImplRegion(); + + if ( aImplNullRegion.mnRefCount ) + return "Null-Region-RefCount modified"; + if ( aImplNullRegion.mnRectCount ) + return "Null-Region-RectCount modified"; + if ( aImplNullRegion.mpPolyPoly ) + return "Null-Region-PolyPoly modified"; + if ( aImplEmptyRegion.mnRefCount ) + return "Emptry-Region-RefCount modified"; + if ( aImplEmptyRegion.mnRectCount ) + return "Emptry-Region-RectCount modified"; + if ( aImplEmptyRegion.mpPolyPoly ) + return "Emptry-Region-PolyPoly modified"; + + if ( (pImplRegion != &aImplEmptyRegion) && (pImplRegion != &aImplNullRegion) ) + { + sal_uLong nCount = 0; + const ImplRegionBand* pBand = pImplRegion->ImplGetFirstRegionBand(); + while ( pBand ) + { + if ( pBand->mnYBottom < pBand->mnYTop ) + return "YBottom < YTop"; + if ( pBand->mpNextBand ) + { + if ( pBand->mnYBottom >= pBand->mpNextBand->mnYTop ) + return "overlapping bands in region"; + } + if ( pBand->mbTouched > 1 ) + return "Band-mbTouched overwrite"; + + ImplRegionBandSep* pSep = pBand->mpFirstSep; + while ( pSep ) + { + if ( pSep->mnXRight < pSep->mnXLeft ) + return "XLeft < XRight"; + if ( pSep->mpNextSep ) + { + if ( pSep->mnXRight >= pSep->mpNextSep->mnXLeft ) + return "overlapping separations in region"; + } + if ( pSep->mbRemoved > 1 ) + return "Sep-mbRemoved overwrite"; + + nCount++; + pSep = pSep->mpNextSep; + } + + pBand = pBand->mpNextBand; + } + + if ( pImplRegion->mnRectCount != nCount ) + return "mnRetCount is not valid"; + } + + return NULL; +} + +void TraceBands (const ImplRegionBand* pFirstBand) +{ + int nBandIndex (0); + const ImplRegionBand* pBand = pFirstBand; + while (pBand != NULL) + { + OSL_TRACE(" band %d %d->%d : ", nBandIndex++, + pBand->mnYTop, pBand->mnYBottom); + + ImplRegionBandPoint* pPoint = pBand->mpFirstBandPoint; + while (pPoint != NULL) + { + OSL_TRACE(" %d ", pPoint->mnX); + pPoint = pPoint->mpNextBandPoint; + } + OSL_TRACE(" | "); + + ImplRegionBandSep* pSep = pBand->mpFirstSep; + while (pSep != NULL) + { + OSL_TRACE(" %d->%d ", pSep->mnXLeft, pSep->mnXRight); + pSep = pSep->mpNextSep; + } + OSL_TRACE("\n"); + + pBand = pBand->mpNextBand; + } +} +#endif + +// ======================================================================= + +inline void Region::ImplPolyPolyRegionToBandRegion() +{ + if( mpImplRegion->mpPolyPoly || mpImplRegion->mpB2DPolyPoly ) + ImplPolyPolyRegionToBandRegionFunc(); +} + +// ======================================================================= + +ImplRegionBase::ImplRegionBase( int nRefCount ) +: mnRefCount( nRefCount ) +, mnRectCount( 0 ) +, mpPolyPoly( NULL ) +, mpB2DPolyPoly( NULL ) +{} + +// ------------------------------------------------------------------------ + +ImplRegion::ImplRegion() +{ + mpFirstBand = NULL; + mpLastCheckedBand = NULL; +} + +// ------------------------------------------------------------------------ + +ImplRegion::ImplRegion( const PolyPolygon& rPolyPoly ) +{ + mpFirstBand = NULL; + mpLastCheckedBand = NULL; + mpPolyPoly = new PolyPolygon( rPolyPoly ); +} + +// ------------------------------------------------------------------------ + +ImplRegion::ImplRegion( const basegfx::B2DPolyPolygon& rPolyPoly ) +{ + mpFirstBand = NULL; + mpLastCheckedBand = NULL; + mpB2DPolyPoly = new basegfx::B2DPolyPolygon( rPolyPoly ); +} + +// ----------------------------------------------------------------------- + +ImplRegion::ImplRegion( const ImplRegion& rImplRegion ) +: ImplRegionBase() +{ + mpFirstBand = NULL; + mpLastCheckedBand = NULL; + mnRectCount = rImplRegion.mnRectCount; + + if ( rImplRegion.mpPolyPoly ) + mpPolyPoly = new PolyPolygon( *rImplRegion.mpPolyPoly ); + else if( rImplRegion.mpB2DPolyPoly ) + mpB2DPolyPoly = new basegfx::B2DPolyPolygon( *rImplRegion.mpB2DPolyPoly ); + + // insert band(s) into the list + ImplRegionBand* pNewBand; + ImplRegionBand* pPrevBand = 0; + ImplRegionBand* pBand = rImplRegion.mpFirstBand; + while ( pBand ) + { + pNewBand = new ImplRegionBand( *pBand ); + + // first element? -> set as first into the list + if ( pBand == rImplRegion.mpFirstBand ) + mpFirstBand = pNewBand; + else + pPrevBand->mpNextBand = pNewBand; + + pPrevBand = pNewBand; + pBand = pBand->mpNextBand; + } +} + +// ----------------------------------------------------------------------- + +ImplRegion::~ImplRegion() +{ + DBG_ASSERT( (this != &aImplEmptyRegion) && (this != &aImplNullRegion), + "ImplRegion::~ImplRegion() - Empty oder NULL-Region" ); + + ImplRegionBand* pBand = mpFirstBand; + while ( pBand ) + { + ImplRegionBand* pTempBand = pBand->mpNextBand; + delete pBand; + pBand = pTempBand; + } +} + +// ----------------------------------------------------------------------- + +ImplRegionBase::~ImplRegionBase() +{ + delete mpPolyPoly; + delete mpB2DPolyPoly; +} + +// ----------------------------------------------------------------------- +// +// create complete range of bands in single steps + +void ImplRegion::CreateBandRange( long nYTop, long nYBottom ) +{ + // add top band + mpFirstBand = new ImplRegionBand( nYTop-1, nYTop-1 ); + + // begin first search from the first element + mpLastCheckedBand = mpFirstBand; + + ImplRegionBand* pBand = mpFirstBand; + for ( int i = nYTop; i <= nYBottom+1; i++ ) + { + // create new band + ImplRegionBand* pNewBand = new ImplRegionBand( i, i ); + pBand->mpNextBand = pNewBand; + if ( pBand != mpFirstBand ) + pNewBand->mpPrevBand = pBand; + + pBand = pBand->mpNextBand; + } +} + +// ----------------------------------------------------------------------- + +sal_Bool ImplRegion::InsertLine( const Point& rStartPt, const Point& rEndPt, + long nLineId ) +{ + long nX, nY; + + // lines consisting of a single point do not interest here + if ( rStartPt == rEndPt ) + return sal_True; + + LineType eLineType = (rStartPt.Y() > rEndPt.Y()) ? LINE_DESCENDING : LINE_ASCENDING; + if ( rStartPt.X() == rEndPt.X() ) + { + // vertical line + const long nEndY = rEndPt.Y(); + + nX = rStartPt.X(); + nY = rStartPt.Y(); + + if( nEndY > nY ) + { + for ( ; nY <= nEndY; nY++ ) + { + Point aNewPoint( nX, nY ); + InsertPoint( aNewPoint, nLineId, + (aNewPoint == rEndPt) || (aNewPoint == rStartPt), + eLineType ); + } + } + else + { + for ( ; nY >= nEndY; nY-- ) + { + Point aNewPoint( nX, nY ); + InsertPoint( aNewPoint, nLineId, + (aNewPoint == rEndPt) || (aNewPoint == rStartPt), + eLineType ); + } + } + } + else if ( rStartPt.Y() != rEndPt.Y() ) + { + const long nDX = labs( rEndPt.X() - rStartPt.X() ); + const long nDY = labs( rEndPt.Y() - rStartPt.Y() ); + const long nStartX = rStartPt.X(); + const long nStartY = rStartPt.Y(); + const long nEndX = rEndPt.X(); + const long nEndY = rEndPt.Y(); + const long nXInc = ( nStartX < nEndX ) ? 1L : -1L; + const long nYInc = ( nStartY < nEndY ) ? 1L : -1L; + + if ( nDX >= nDY ) + { + const long nDYX = ( nDY - nDX ) << 1; + const long nDY2 = nDY << 1; + long nD = nDY2 - nDX; + + for ( nX = nStartX, nY = nStartY; nX != nEndX; nX += nXInc ) + { + InsertPoint( Point( nX, nY ), nLineId, nStartX == nX, eLineType ); + + if ( nD < 0L ) + nD += nDY2; + else + nD += nDYX, nY += nYInc; + } + } + else + { + const long nDYX = ( nDX - nDY ) << 1; + const long nDY2 = nDX << 1; + long nD = nDY2 - nDY; + + for ( nX = nStartX, nY = nStartY; nY != nEndY; nY += nYInc ) + { + InsertPoint( Point( nX, nY ), nLineId, nStartY == nY, eLineType ); + + if ( nD < 0L ) + nD += nDY2; + else + nD += nDYX, nX += nXInc; + } + } + + // last point + InsertPoint( Point( nEndX, nEndY ), nLineId, sal_True, eLineType ); + } + + return sal_True; +} + +// ----------------------------------------------------------------------- +// +// search for appropriate place for the new point + +sal_Bool ImplRegion::InsertPoint( const Point &rPoint, long nLineID, + sal_Bool bEndPoint, LineType eLineType ) +{ + DBG_ASSERT( mpFirstBand != NULL, "ImplRegion::InsertPoint - no bands available!" ); + + if ( rPoint.Y() == mpLastCheckedBand->mnYTop ) + { + mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType ); + return sal_True; + } + + if ( rPoint.Y() > mpLastCheckedBand->mnYTop ) + { + // Search ascending + while ( mpLastCheckedBand ) + { + // Insert point if possible + if ( rPoint.Y() == mpLastCheckedBand->mnYTop ) + { + mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType ); + return sal_True; + } + + mpLastCheckedBand = mpLastCheckedBand->mpNextBand; + } + + OSL_FAIL( "ImplRegion::InsertPoint reached the end of the list!" ); + } + else + { + // Search descending + while ( mpLastCheckedBand ) + { + // Insert point if possible + if ( rPoint.Y() == mpLastCheckedBand->mnYTop ) + { + mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType ); + return sal_True; + } + + mpLastCheckedBand = mpLastCheckedBand->mpPrevBand; + } + + OSL_FAIL( "ImplRegion::InsertPoint reached the beginning of the list!" ); + } + + OSL_FAIL( "ImplRegion::InsertPoint point not inserted!" ); + + // reinitialize pointer (should never be reached!) + mpLastCheckedBand = mpFirstBand; + + return sal_False; +} + +// ----------------------------------------------------------------------- +// +// search for appropriate places for the new bands + +void ImplRegion::InsertBands( long nTop, long nBottom ) +{ + // region empty? -> set rectagle as first entry! + if ( !mpFirstBand ) + { + // add band with boundaries of the rectangle + mpFirstBand = new ImplRegionBand( nTop, nBottom ); + return; + } + + // find/insert bands for the boundaries of the rectangle + sal_Bool bTopBoundaryInserted = sal_False; + sal_Bool bTop2BoundaryInserted = sal_False; + sal_Bool bBottomBoundaryInserted = sal_False; + + // special case: top boundary is above the first band + ImplRegionBand* pNewBand; + if ( nTop < mpFirstBand->mnYTop ) + { + // create new band above the first in the list + pNewBand = new ImplRegionBand( nTop, mpFirstBand->mnYTop ); + if ( nBottom < mpFirstBand->mnYTop ) + pNewBand->mnYBottom = nBottom; + + // insert band into the list + pNewBand->mpNextBand = mpFirstBand; + mpFirstBand = pNewBand; + + bTopBoundaryInserted = sal_True; + } + + // insert band(s) into the list + ImplRegionBand* pBand = mpFirstBand; + while ( pBand ) + { + // Insert Bands if possible + if ( !bTopBoundaryInserted ) + bTopBoundaryInserted = InsertSingleBand( pBand, nTop - 1 ); + + if ( !bTop2BoundaryInserted ) + bTop2BoundaryInserted = InsertSingleBand( pBand, nTop ); + + if ( !bBottomBoundaryInserted && (nTop != nBottom) ) + bBottomBoundaryInserted = InsertSingleBand( pBand, nBottom ); + + // both boundaries inserted? -> nothing more to do + if ( bTopBoundaryInserted && bTop2BoundaryInserted && bBottomBoundaryInserted ) + break; + + // insert bands between two bands if neccessary + if ( pBand->mpNextBand ) + { + if ( (pBand->mnYBottom + 1) < pBand->mpNextBand->mnYTop ) + { + // copy band with list and set new boundary + pNewBand = new ImplRegionBand( pBand->mnYBottom+1, + pBand->mpNextBand->mnYTop-1 ); + + // insert band into the list + pNewBand->mpNextBand = pBand->mpNextBand; + pBand->mpNextBand = pNewBand; + } + } + + pBand = pBand->mpNextBand; + } +} + +// ----------------------------------------------------------------------- +// +// create new band and insert it into the list + +sal_Bool ImplRegion::InsertSingleBand( ImplRegionBand* pBand, + long nYBandPosition ) +{ + // boundary already included in band with height 1? -> nothing to do! + if ( (pBand->mnYTop == pBand->mnYBottom) && + (nYBandPosition == pBand->mnYTop) ) + return sal_True; + + // insert single height band on top? + ImplRegionBand* pNewBand; + if ( nYBandPosition == pBand->mnYTop ) + { + // copy band with list and set new boundary + pNewBand = new ImplRegionBand( *pBand ); + pNewBand->mnYTop = nYBandPosition+1; + + // insert band into the list + pNewBand->mpNextBand = pBand->mpNextBand; + pBand->mnYBottom = nYBandPosition; + pBand->mpNextBand = pNewBand; + + return sal_True; + } + + // top of new rectangle within the current band? -> insert new band and copy data + if ( (nYBandPosition > pBand->mnYTop) && + (nYBandPosition < pBand->mnYBottom) ) + { + // copy band with list and set new boundary + pNewBand = new ImplRegionBand( *pBand ); + pNewBand->mnYTop = nYBandPosition; + + // insert band into the list + pNewBand->mpNextBand = pBand->mpNextBand; + pBand->mnYBottom = nYBandPosition; + pBand->mpNextBand = pNewBand; + + // copy band with list and set new boundary + pNewBand = new ImplRegionBand( *pBand ); + pNewBand->mnYTop = nYBandPosition; + + // insert band into the list + pBand->mpNextBand->mnYTop = nYBandPosition+1; + + pNewBand->mpNextBand = pBand->mpNextBand; + pBand->mnYBottom = nYBandPosition - 1; + pBand->mpNextBand = pNewBand; + + return sal_True; + } + + // create new band behind the current in the list + if ( !pBand->mpNextBand ) + { + if ( nYBandPosition == pBand->mnYBottom ) + { + // copy band with list and set new boundary + pNewBand = new ImplRegionBand( *pBand ); + pNewBand->mnYTop = pBand->mnYBottom; + pNewBand->mnYBottom = nYBandPosition; + + pBand->mnYBottom = nYBandPosition-1; + + // append band to the list + pBand->mpNextBand = pNewBand; + return sal_True; + } + + if ( nYBandPosition > pBand->mnYBottom ) + { + // create new band + pNewBand = new ImplRegionBand( pBand->mnYBottom + 1, nYBandPosition ); + + // append band to the list + pBand->mpNextBand = pNewBand; + return sal_True; + } + } + + return sal_False; +} + +// ------------------------------------------------------------------------ + +void ImplRegion::InsertBand (ImplRegionBand* pPreviousBand, ImplRegionBand* pBandToInsert) +{ + OSL_ASSERT(pBandToInsert!=NULL); + + if (pPreviousBand == NULL) + { + // Insert band before all others. + if (mpFirstBand != NULL) + mpFirstBand->mpPrevBand = pBandToInsert; + pBandToInsert->mpNextBand = mpFirstBand; + mpFirstBand = pBandToInsert; + } + else + { + // Insert band directly after pPreviousBand. + pBandToInsert->mpNextBand = pPreviousBand->mpNextBand; + pPreviousBand->mpNextBand = pBandToInsert; + pBandToInsert->mpPrevBand = pPreviousBand; + } +} + +// ------------------------------------------------------------------------ + +void ImplRegion::Union( long nLeft, long nTop, long nRight, long nBottom ) +{ + DBG_ASSERT( nLeft <= nRight, "ImplRegion::Union() - nLeft > nRight" ); + DBG_ASSERT( nTop <= nBottom, "ImplRegion::Union() - nTop > nBottom" ); + + // process union + ImplRegionBand* pBand = mpFirstBand; + while ( pBand ) + { + if ( pBand->mnYTop >= nTop ) + { + if ( pBand->mnYBottom <= nBottom ) + pBand->Union( nLeft, nRight ); + else + { +#ifdef DBG_UTIL + long nCurY = pBand->mnYBottom; + pBand = pBand->mpNextBand; + while ( pBand ) + { + if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) ) + { + OSL_FAIL( "ImplRegion::Union() - Bands not sorted!" ); + } + pBand = pBand->mpNextBand; + } +#endif + break; + } + } + + pBand = pBand->mpNextBand; + } +} + +// ----------------------------------------------------------------------- + +void ImplRegion::Exclude( long nLeft, long nTop, long nRight, long nBottom ) +{ + DBG_ASSERT( nLeft <= nRight, "ImplRegion::Exclude() - nLeft > nRight" ); + DBG_ASSERT( nTop <= nBottom, "ImplRegion::Exclude() - nTop > nBottom" ); + + // process exclude + ImplRegionBand* pBand = mpFirstBand; + while ( pBand ) + { + if ( pBand->mnYTop >= nTop ) + { + if ( pBand->mnYBottom <= nBottom ) + pBand->Exclude( nLeft, nRight ); + else + { +#ifdef DBG_UTIL + long nCurY = pBand->mnYBottom; + pBand = pBand->mpNextBand; + while ( pBand ) + { + if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) ) + { + OSL_FAIL( "ImplRegion::Exclude() - Bands not sorted!" ); + } + pBand = pBand->mpNextBand; + } +#endif + break; + } + } + + pBand = pBand->mpNextBand; + } +} + +// ----------------------------------------------------------------------- + +void ImplRegion::XOr( long nLeft, long nTop, long nRight, long nBottom ) +{ + DBG_ASSERT( nLeft <= nRight, "ImplRegion::Exclude() - nLeft > nRight" ); + DBG_ASSERT( nTop <= nBottom, "ImplRegion::Exclude() - nTop > nBottom" ); + + // process xor + ImplRegionBand* pBand = mpFirstBand; + while ( pBand ) + { + if ( pBand->mnYTop >= nTop ) + { + if ( pBand->mnYBottom <= nBottom ) + pBand->XOr( nLeft, nRight ); + else + { +#ifdef DBG_UTIL + long nCurY = pBand->mnYBottom; + pBand = pBand->mpNextBand; + while ( pBand ) + { + if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) ) + { + OSL_FAIL( "ImplRegion::XOr() - Bands not sorted!" ); + } + pBand = pBand->mpNextBand; + } +#endif + break; + } + } + + pBand = pBand->mpNextBand; + } +} + +// ----------------------------------------------------------------------- +// +// remove empty bands + +sal_Bool ImplRegion::OptimizeBandList() +{ + DBG_ASSERT( (this != &aImplNullRegion) && (this != &aImplEmptyRegion), + "ImplRegion::OptimizeBandList() - Empty oder NULL-Region" ); + + mnRectCount = 0; + + ImplRegionBand* pPrevBand = 0; + ImplRegionBand* pBand = mpFirstBand; + while ( pBand ) + { + const sal_Bool bBTEqual = pBand->mpNextBand && + (pBand->mnYBottom == pBand->mpNextBand->mnYTop); + + // no separation? -> remove! + if ( pBand->IsEmpty() || (bBTEqual && (pBand->mnYBottom == pBand->mnYTop)) ) + { + // save pointer + ImplRegionBand* pOldBand = pBand; + + // previous element of the list + if ( pBand == mpFirstBand ) + mpFirstBand = pBand->mpNextBand; + else + pPrevBand->mpNextBand = pBand->mpNextBand; + + pBand = pBand->mpNextBand; + delete pOldBand; + } + else + { + // fixup + if ( bBTEqual ) + pBand->mnYBottom = pBand->mpNextBand->mnYTop-1; + + // this and next band with equal separations? -> combine! + if ( pBand->mpNextBand && + ((pBand->mnYBottom+1) == pBand->mpNextBand->mnYTop) && + (*pBand == *pBand->mpNextBand) ) + { + // expand current height + pBand->mnYBottom = pBand->mpNextBand->mnYBottom; + + // remove next band from list + ImplRegionBand* pDeletedBand = pBand->mpNextBand; + pBand->mpNextBand = pDeletedBand->mpNextBand; + delete pDeletedBand; + + // check band again! + } + else + { + // count rectangles within band + ImplRegionBandSep* pSep = pBand->mpFirstSep; + while ( pSep ) + { + mnRectCount++; + pSep = pSep->mpNextSep; + } + + pPrevBand = pBand; + pBand = pBand->mpNextBand; + } + } + } + +#ifdef DBG_UTIL + pBand = mpFirstBand; + while ( pBand ) + { + DBG_ASSERT( pBand->mpFirstSep != NULL, + "Exiting ImplRegion::OptimizeBandList(): empty band in region!" ); + + if ( pBand->mnYBottom < pBand->mnYTop ) + OSL_FAIL( "ImplRegion::OptimizeBandList(): YBottomBoundary < YTopBoundary" ); + + if ( pBand->mpNextBand ) + { + if ( pBand->mnYBottom >= pBand->mpNextBand->mnYTop ) + OSL_FAIL( "ImplRegion::OptimizeBandList(): overlapping bands in region!" ); + } + + pBand = pBand->mpNextBand; + } +#endif + + return (mnRectCount != 0); +} + +// ======================================================================= + +void Region::ImplCopyData() +{ + mpImplRegion->mnRefCount--; + mpImplRegion = new ImplRegion( *mpImplRegion ); +} + +// ======================================================================= + +Region::Region() +{ + DBG_CTOR( Region, ImplDbgTestRegion ); + + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); +} + +// ----------------------------------------------------------------------- + +Region::Region( RegionType eType ) +{ + DBG_CTOR( Region, ImplDbgTestRegion ); + DBG_ASSERT( (eType == REGION_NULL) || (eType == REGION_EMPTY), + "Region( RegionType ) - RegionType != EMPTY/NULL" ); + + if ( eType == REGION_NULL ) + mpImplRegion = (ImplRegion*)(&aImplNullRegion); + else + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); +} + +// ----------------------------------------------------------------------- + +Region::Region( const Rectangle& rRect ) +{ + DBG_CTOR( Region, ImplDbgTestRegion ); + + ImplCreateRectRegion( rRect ); +} + +// ----------------------------------------------------------------------- + +Region::Region( const Polygon& rPolygon ) +{ + DBG_CTOR( Region, ImplDbgTestRegion ); + DBG_CHKOBJ( &rPolygon, Polygon, NULL ); + + ImplCreatePolyPolyRegion( rPolygon ); +} + +// ----------------------------------------------------------------------- + +Region::Region( const PolyPolygon& rPolyPoly ) +{ + DBG_CTOR( Region, ImplDbgTestRegion ); + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + + ImplCreatePolyPolyRegion( rPolyPoly ); +} + +// ----------------------------------------------------------------------- + +Region::Region( const basegfx::B2DPolyPolygon& rPolyPoly ) +{ + DBG_CTOR( Region, ImplDbgTestRegion ); + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + + mpImplRegion = new ImplRegion( rPolyPoly ); +} + +// ----------------------------------------------------------------------- + +Region::Region( const Region& rRegion ) +{ + DBG_CTOR( Region, ImplDbgTestRegion ); + DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); + DBG_ASSERT( rRegion.mpImplRegion->mnRefCount < 0xFFFFFFFE, "Region: RefCount overflow" ); + + // copy pointer to instance of implementation + mpImplRegion = rRegion.mpImplRegion; + if ( mpImplRegion->mnRefCount ) + mpImplRegion->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +Region::~Region() +{ + DBG_DTOR( Region, ImplDbgTestRegion ); + + // statische Object haben RefCount von 0 + if ( mpImplRegion->mnRefCount ) + { + if ( mpImplRegion->mnRefCount > 1 ) + mpImplRegion->mnRefCount--; + else + delete mpImplRegion; + } +} + +// ----------------------------------------------------------------------- + +void Region::ImplCreateRectRegion( const Rectangle& rRect ) +{ + if ( rRect.IsEmpty() ) + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + else + { + // get justified rectangle + long nTop = Min( rRect.Top(), rRect.Bottom() ); + long nBottom = Max( rRect.Top(), rRect.Bottom() ); + long nLeft = Min( rRect.Left(), rRect.Right() ); + long nRight = Max( rRect.Left(), rRect.Right() ); + + // create instance of implementation class + mpImplRegion = new ImplRegion(); + + // add band with boundaries of the rectangle + mpImplRegion->mpFirstBand = new ImplRegionBand( nTop, nBottom ); + + // Set left and right boundaries of the band + mpImplRegion->mpFirstBand->Union( nLeft, nRight ); + mpImplRegion->mnRectCount = 1; + } +} + +// ----------------------------------------------------------------------- + +void Region::ImplCreatePolyPolyRegion( const PolyPolygon& rPolyPoly ) +{ + const sal_uInt16 nPolyCount = rPolyPoly.Count(); + if ( nPolyCount ) + { + // polypolygon empty? -> empty region + const Rectangle aRect( rPolyPoly.GetBoundRect() ); + + if ( !aRect.IsEmpty() ) + { + // width OR height == 1 ? => Rectangular region + if ( (aRect.GetWidth() == 1) + || (aRect.GetHeight() == 1) + || rPolyPoly.IsRect() ) + { + ImplCreateRectRegion( aRect ); + } + else + mpImplRegion = new ImplRegion( rPolyPoly ); + } + else + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + else + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); +} + +// ----------------------------------------------------------------------- + +void Region::ImplPolyPolyRegionToBandRegionFunc() +{ + // ensure to subdivide when bezier segemnts are used, it's going to + // be expanded to rectangles + PolyPolygon aPolyPoly; + GetPolyPolygon().AdaptiveSubdivide(aPolyPoly); + + if ( mpImplRegion->mnRefCount > 1 ) + mpImplRegion->mnRefCount--; + else + delete mpImplRegion; + + if ( aPolyPoly.Count() ) + { + // polypolygon empty? -> empty region + const Rectangle aRect( aPolyPoly.GetBoundRect() ); + + if ( !aRect.IsEmpty() ) + { + if (ImplIsPolygonRectilinear(aPolyPoly)) + { + // For rectilinear polygons there is an optimized band conversion. + mpImplRegion = ImplRectilinearPolygonToBands(aPolyPoly); + } + else + { + mpImplRegion = ImplGeneralPolygonToBands(aPolyPoly, aRect); + } + + // Convert points into seps. + ImplRegionBand* pRegionBand = mpImplRegion->mpFirstBand; + while ( pRegionBand ) + { + // generate separations from the lines and process union + pRegionBand->ProcessPoints(); + pRegionBand = pRegionBand->mpNextBand; + } + + // Optimize list of bands. Adjacent bands with identical lists + // of seps are joined. + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + } + else + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + else + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); +} + +// ----------------------------------------------------------------------- + +void Region::Move( long nHorzMove, long nVertMove ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // no region data? -> nothing to do + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return; + + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + if ( mpImplRegion->mpPolyPoly ) + mpImplRegion->mpPolyPoly->Move( nHorzMove, nVertMove ); + else if( mpImplRegion->mpB2DPolyPoly ) + { + mpImplRegion->mpB2DPolyPoly->transform(basegfx::tools::createTranslateB2DHomMatrix(nHorzMove, nVertMove)); + } + else + { + ImplRegionBand* pBand = mpImplRegion->mpFirstBand; + while ( pBand ) + { + // process the vertical move + if ( nVertMove != 0) + { + pBand->mnYTop = pBand->mnYTop + nVertMove; + pBand->mnYBottom = pBand->mnYBottom + nVertMove; + } + + // process the horizontal move + if ( nHorzMove != 0) + pBand->MoveX( nHorzMove ); + + pBand = pBand->mpNextBand; + } + } +} + +// ----------------------------------------------------------------------- + +void Region::Scale( double fScaleX, double fScaleY ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // no region data? -> nothing to do + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return; + + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + if ( mpImplRegion->mpPolyPoly ) + mpImplRegion->mpPolyPoly->Scale( fScaleX, fScaleY ); + else if( mpImplRegion->mpB2DPolyPoly ) + { + mpImplRegion->mpB2DPolyPoly->transform(basegfx::tools::createScaleB2DHomMatrix(fScaleX, fScaleY)); + } + else + { + ImplRegionBand* pBand = mpImplRegion->mpFirstBand; + while ( pBand ) + { + // process the vertical move + if ( fScaleY != 0.0 ) + { + pBand->mnYTop = FRound( pBand->mnYTop * fScaleY ); + pBand->mnYBottom = FRound( pBand->mnYBottom * fScaleY ); + } + + // process the horizontal move + if ( fScaleX != 0.0 ) + pBand->ScaleX( fScaleX ); + + pBand = pBand->mpNextBand; + } + } +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::Union( const Rectangle& rRect ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // is rectangle empty? -> nothing to do + if ( rRect.IsEmpty() ) + return sal_True; + + if( HasPolyPolygon() ) + { + // get this B2DPolyPolygon + basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); + aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); + + if( aThisPolyPoly.count() == 0 ) + { + *this = rRect; + return true; + } + + // get the other B2DPolyPolygon + basegfx::B2DPolygon aRectPoly( basegfx::tools::createPolygonFromRect( basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); + basegfx::B2DPolyPolygon aOtherPolyPoly( aRectPoly ); + + basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationOr( aThisPolyPoly, aOtherPolyPoly ); + *this = Region( aClip ); + + return sal_True; + } + + ImplPolyPolyRegionToBandRegion(); + + // no instance data? -> create! + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + mpImplRegion = new ImplRegion(); + + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + // get justified rectangle + long nLeft = Min( rRect.Left(), rRect.Right() ); + long nTop = Min( rRect.Top(), rRect.Bottom() ); + long nRight = Max( rRect.Left(), rRect.Right() ); + long nBottom = Max( rRect.Top(), rRect.Bottom() ); + + // insert bands if the boundaries are not allready in the list + mpImplRegion->InsertBands( nTop, nBottom ); + + // process union + mpImplRegion->Union( nLeft, nTop, nRight, nBottom ); + + // cleanup + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::Intersect( const Rectangle& rRect ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // is rectangle empty? -> nothing to do + if ( rRect.IsEmpty() ) + { + // statische Object haben RefCount von 0 + if ( mpImplRegion->mnRefCount ) + { + if ( mpImplRegion->mnRefCount > 1 ) + mpImplRegion->mnRefCount--; + else + delete mpImplRegion; + } + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + return sal_True; + } + + // #103137# Avoid banding for special cases + if ( mpImplRegion->mpPolyPoly ) + { + // #127431# make ImplRegion unique, if not already. + if( mpImplRegion->mnRefCount > 1 ) + { + mpImplRegion->mnRefCount--; + mpImplRegion = new ImplRegion( *mpImplRegion->mpPolyPoly ); + } + + // use the PolyPolygon::Clip method for rectangles, this is + // fairly simple (does not even use GPC) and saves us from + // unnecessary banding + mpImplRegion->mpPolyPoly->Clip( rRect ); + + return sal_True; + } + else if( mpImplRegion->mpB2DPolyPoly ) + { + // #127431# make ImplRegion unique, if not already. + if( mpImplRegion->mnRefCount > 1 ) + { + mpImplRegion->mnRefCount--; + mpImplRegion = new ImplRegion( *mpImplRegion->mpB2DPolyPoly ); + } + + *mpImplRegion->mpB2DPolyPoly = + basegfx::tools::clipPolyPolygonOnRange( *mpImplRegion->mpB2DPolyPoly, + basegfx::B2DRange( rRect.Left(), rRect.Top(), + rRect.Right(), rRect.Bottom() ), + true, false ); + return sal_True; + } + else + ImplPolyPolyRegionToBandRegion(); + + // is region empty? -> nothing to do! + if ( mpImplRegion == &aImplEmptyRegion ) + return sal_True; + + // get justified rectangle + long nLeft = Min( rRect.Left(), rRect.Right() ); + long nTop = Min( rRect.Top(), rRect.Bottom() ); + long nRight = Max( rRect.Left(), rRect.Right() ); + long nBottom = Max( rRect.Top(), rRect.Bottom() ); + + // is own region NULL-region? -> copy data! + if ( mpImplRegion == &aImplNullRegion ) + { + // create instance of implementation class + mpImplRegion = new ImplRegion(); + + // add band with boundaries of the rectangle + mpImplRegion->mpFirstBand = new ImplRegionBand( nTop, nBottom ); + + // Set left and right boundaries of the band + mpImplRegion->mpFirstBand->Union( nLeft, nRight ); + mpImplRegion->mnRectCount = 1; + + return sal_True; + } + + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + // insert bands if the boundaries are not allready in the list + mpImplRegion->InsertBands( nTop, nBottom ); + + // process intersections + ImplRegionBand* pPrevBand = 0; + ImplRegionBand* pBand = mpImplRegion->mpFirstBand; + while ( pBand ) + { + // band within intersection boundary? -> process. otherwise remove + if ( (pBand->mnYTop >= nTop) && + (pBand->mnYBottom <= nBottom) ) + { + // process intersection + pBand->Intersect( nLeft, nRight ); + + pPrevBand = pBand; + pBand = pBand->mpNextBand; + } + else + { + ImplRegionBand* pOldBand = pBand; + if ( pBand == mpImplRegion->mpFirstBand ) + mpImplRegion->mpFirstBand = pBand->mpNextBand; + else + pPrevBand->mpNextBand = pBand->mpNextBand; + pBand = pBand->mpNextBand; + delete pOldBand; + } + } + + // cleanup + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::Exclude( const Rectangle& rRect ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // is rectangle empty? -> nothing to do + if ( rRect.IsEmpty() ) + return sal_True; + + if( HasPolyPolygon() ) + { + // get this B2DPolyPolygon + basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); + aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); + + if( aThisPolyPoly.count() == 0 ) + return sal_True; + + // get the other B2DPolyPolygon + basegfx::B2DPolygon aRectPoly( basegfx::tools::createPolygonFromRect( basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); + basegfx::B2DPolyPolygon aOtherPolyPoly( aRectPoly ); + + basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly ); + *this = Region( aClip ); + + return sal_True; + } + + ImplPolyPolyRegionToBandRegion(); + + // no instance data? -> create! + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return sal_True; + + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + // get justified rectangle + long nLeft = Min( rRect.Left(), rRect.Right() ); + long nTop = Min( rRect.Top(), rRect.Bottom() ); + long nRight = Max( rRect.Left(), rRect.Right() ); + long nBottom = Max( rRect.Top(), rRect.Bottom() ); + + // insert bands if the boundaries are not allready in the list + mpImplRegion->InsertBands( nTop, nBottom ); + + // process exclude + mpImplRegion->Exclude( nLeft, nTop, nRight, nBottom ); + + // cleanup + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::XOr( const Rectangle& rRect ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // is rectangle empty? -> nothing to do + if ( rRect.IsEmpty() ) + return sal_True; + + if( HasPolyPolygon() ) + { + // get this B2DPolyPolygon + basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); + aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); + + if( aThisPolyPoly.count() == 0 ) + { + *this = rRect; + return sal_True; + } + + // get the other B2DPolyPolygon + basegfx::B2DPolygon aRectPoly( basegfx::tools::createPolygonFromRect( basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); + basegfx::B2DPolyPolygon aOtherPolyPoly( aRectPoly ); + + basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly ); + *this = Region( aClip ); + + return sal_True; + } + + ImplPolyPolyRegionToBandRegion(); + + // no instance data? -> create! + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + mpImplRegion = new ImplRegion(); + + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + // get justified rectangle + long nLeft = Min( rRect.Left(), rRect.Right() ); + long nTop = Min( rRect.Top(), rRect.Bottom() ); + long nRight = Max( rRect.Left(), rRect.Right() ); + long nBottom = Max( rRect.Top(), rRect.Bottom() ); + + // insert bands if the boundaries are not allready in the list + mpImplRegion->InsertBands( nTop, nBottom ); + + // process xor + mpImplRegion->XOr( nLeft, nTop, nRight, nBottom ); + + // cleanup + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + + return sal_True; +} + +// ----------------------------------------------------------------------- +void Region::ImplUnionPolyPolygon( const Region& i_rRegion ) +{ + // get this B2DPolyPolygon + basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); + aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); + + if( aThisPolyPoly.count() == 0 ) + { + *this = i_rRegion; + return; + } + + // get the other B2DPolyPolygon + basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); + aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); + + + basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationOr( aThisPolyPoly, aOtherPolyPoly ); + + *this = Region( aClip ); +} + +sal_Bool Region::Union( const Region& rRegion ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) + { + ImplUnionPolyPolygon( rRegion ); + return sal_True; + } + + ImplPolyPolyRegionToBandRegion(); + ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); + + // is region empty or null? -> nothing to do + if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) + return sal_True; + + // no instance data? -> create! + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + mpImplRegion = new ImplRegion(); + + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden + ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand; + while ( pBand ) + { + // insert bands if the boundaries are not allready in the list + mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); + + // process all elements of the list + ImplRegionBandSep* pSep = pBand->mpFirstSep; + while ( pSep ) + { + mpImplRegion->Union( pSep->mnXLeft, pBand->mnYTop, + pSep->mnXRight, pBand->mnYBottom ); + pSep = pSep->mpNextSep; + } + + pBand = pBand->mpNextBand; + } + + // cleanup + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + + return sal_True; +} + +// ----------------------------------------------------------------------- +void Region::ImplIntersectWithPolyPolygon( const Region& i_rRegion ) +{ + // get this B2DPolyPolygon + basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); + if( aThisPolyPoly.count() == 0 ) + { + *this = i_rRegion; + return; + } + + // get the other B2DPolyPolygon + basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); + + basegfx::B2DPolyPolygon aClip = basegfx::tools::clipPolyPolygonOnPolyPolygon( aOtherPolyPoly, aThisPolyPoly, true, false ); + *this = Region( aClip ); +} + +sal_Bool Region::Intersect( const Region& rRegion ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // same instance data? -> nothing to do! + if ( mpImplRegion == rRegion.mpImplRegion ) + return sal_True; + + if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) + { + ImplIntersectWithPolyPolygon( rRegion ); + return sal_True; + } + + ImplPolyPolyRegionToBandRegion(); + ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); + + if ( mpImplRegion == &aImplEmptyRegion ) + return sal_True; + + // is region null? -> nothing to do + if ( rRegion.mpImplRegion == &aImplNullRegion ) + return sal_True; + + // is rectangle empty? -> nothing to do + if ( rRegion.mpImplRegion == &aImplEmptyRegion ) + { + // statische Object haben RefCount von 0 + if ( mpImplRegion->mnRefCount ) + { + if ( mpImplRegion->mnRefCount > 1 ) + mpImplRegion->mnRefCount--; + else + delete mpImplRegion; + } + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + return sal_True; + } + + // is own region NULL-region? -> copy data! + if ( mpImplRegion == &aImplNullRegion) + { + mpImplRegion = rRegion.mpImplRegion; + rRegion.mpImplRegion->mnRefCount++; + return sal_True; + } + + // Wenn wir weniger Rechtecke haben, drehen wir den Intersect-Aufruf um + if ( mpImplRegion->mnRectCount+2 < rRegion.mpImplRegion->mnRectCount ) + { + Region aTempRegion = rRegion; + aTempRegion.Intersect( *this ); + *this = aTempRegion; + } + else + { + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + // mark all bands as untouched + ImplRegionBand* pBand = mpImplRegion->mpFirstBand; + while ( pBand ) + { + pBand->mbTouched = sal_False; + pBand = pBand->mpNextBand; + } + + pBand = rRegion.mpImplRegion->mpFirstBand; + while ( pBand ) + { + // insert bands if the boundaries are not allready in the list + mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); + + // process all elements of the list + ImplRegionBandSep* pSep = pBand->mpFirstSep; + while ( pSep ) + { + // left boundary? + if ( pSep == pBand->mpFirstSep ) + { + // process intersection and do not remove untouched bands + mpImplRegion->Exclude( LONG_MIN+1, pBand->mnYTop, + pSep->mnXLeft-1, pBand->mnYBottom ); + } + + // right boundary? + if ( pSep->mpNextSep == NULL ) + { + // process intersection and do not remove untouched bands + mpImplRegion->Exclude( pSep->mnXRight+1, pBand->mnYTop, + LONG_MAX-1, pBand->mnYBottom ); + } + else + { + // process intersection and do not remove untouched bands + mpImplRegion->Exclude( pSep->mnXRight+1, pBand->mnYTop, + pSep->mpNextSep->mnXLeft-1, pBand->mnYBottom ); + } + + pSep = pSep->mpNextSep; + } + + pBand = pBand->mpNextBand; + } + + // remove all untouched bands if bands allready left + ImplRegionBand* pPrevBand = 0; + pBand = mpImplRegion->mpFirstBand; + while ( pBand ) + { + if ( !pBand->mbTouched ) + { + // save pointer + ImplRegionBand* pOldBand = pBand; + + // previous element of the list + if ( pBand == mpImplRegion->mpFirstBand ) + mpImplRegion->mpFirstBand = pBand->mpNextBand; + else + pPrevBand->mpNextBand = pBand->mpNextBand; + + pBand = pBand->mpNextBand; + delete pOldBand; + } + else + { + pPrevBand = pBand; + pBand = pBand->mpNextBand; + } + } + + // cleanup + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + } + + return sal_True; +} + +// ----------------------------------------------------------------------- +void Region::ImplExcludePolyPolygon( const Region& i_rRegion ) +{ + // get this B2DPolyPolygon + basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); + if( aThisPolyPoly.count() == 0 ) + return; + aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); + + // get the other B2DPolyPolygon + basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); + aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); + + basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationDiff( aThisPolyPoly, aOtherPolyPoly ); + *this = Region( aClip ); +} + +sal_Bool Region::Exclude( const Region& rRegion ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) + { + ImplExcludePolyPolygon( rRegion ); + return sal_True; + } + + ImplPolyPolyRegionToBandRegion(); + ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); + + // is region empty or null? -> nothing to do + if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) + return sal_True; + + // no instance data? -> nothing to do + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return sal_True; + + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden + ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand; + while ( pBand ) + { + // insert bands if the boundaries are not allready in the list + mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); + + // process all elements of the list + ImplRegionBandSep* pSep = pBand->mpFirstSep; + while ( pSep ) + { + mpImplRegion->Exclude( pSep->mnXLeft, pBand->mnYTop, + pSep->mnXRight, pBand->mnYBottom ); + pSep = pSep->mpNextSep; + } + + // Wir optimieren schon in der Schleife, da wir davon + // ausgehen, das wir insgesammt weniger Baender ueberpruefen + // muessen + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + break; + } + + pBand = pBand->mpNextBand; + } + + return sal_True; +} + +// ----------------------------------------------------------------------- +void Region::ImplXOrPolyPolygon( const Region& i_rRegion ) +{ + // get this B2DPolyPolygon + basegfx::B2DPolyPolygon aThisPolyPoly( ConvertToB2DPolyPolygon() ); + if( aThisPolyPoly.count() == 0 ) + { + *this = i_rRegion; + return; + } + aThisPolyPoly = basegfx::tools::prepareForPolygonOperation( aThisPolyPoly ); + + // get the other B2DPolyPolygon + basegfx::B2DPolyPolygon aOtherPolyPoly( const_cast<Region&>(i_rRegion).ConvertToB2DPolyPolygon() ); + aOtherPolyPoly = basegfx::tools::prepareForPolygonOperation( aOtherPolyPoly ); + + basegfx::B2DPolyPolygon aClip = basegfx::tools::solvePolygonOperationXor( aThisPolyPoly, aOtherPolyPoly ); + *this = Region( aClip ); +} + +sal_Bool Region::XOr( const Region& rRegion ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + if( rRegion.HasPolyPolygon() || HasPolyPolygon() ) + { + ImplXOrPolyPolygon( rRegion ); + return sal_True; + } + + ImplPolyPolyRegionToBandRegion(); + ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); + + // is region empty or null? -> nothing to do + if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) + return sal_True; + + // no own instance data? -> XOr = copy + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + { + *this = rRegion; + return sal_True; + } + + // no own instance data? -> make own copy! + if ( mpImplRegion->mnRefCount > 1 ) + ImplCopyData(); + + // Alle Rechtecke aus der uebergebenen Region auf diese Region anwenden + ImplRegionBand* pBand = rRegion.mpImplRegion->mpFirstBand; + while ( pBand ) + { + // insert bands if the boundaries are not allready in the list + mpImplRegion->InsertBands( pBand->mnYTop, pBand->mnYBottom ); + + // process all elements of the list + ImplRegionBandSep* pSep = pBand->mpFirstSep; + while ( pSep ) + { + mpImplRegion->XOr( pSep->mnXLeft, pBand->mnYTop, + pSep->mnXRight, pBand->mnYBottom ); + pSep = pSep->mpNextSep; + } + + pBand = pBand->mpNextBand; + } + + // cleanup + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +Rectangle Region::GetBoundRect() const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + Rectangle aRect; + + // no internal data? -> region is empty! + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return aRect; + + // PolyPolygon data im Imp structure? + if ( mpImplRegion->mpPolyPoly ) + return mpImplRegion->mpPolyPoly->GetBoundRect(); + if( mpImplRegion->mpB2DPolyPoly ) + { + const basegfx::B2DRange aRange = basegfx::tools::getRange( *mpImplRegion->mpB2DPolyPoly ); + aRect.SetPos( Point( (int)aRange.getMinX(), (int)aRange.getMinY() ) ); + aRect.SetSize( Size( (int)aRange.getWidth(), (int)aRange.getHeight() ) ); + return aRect; + } + + // no band in the list? -> region is empty! + if ( !mpImplRegion->mpFirstBand ) + return aRect; + + // get the boundaries of the first band + long nYTop = mpImplRegion->mpFirstBand->mnYTop; + long nYBottom = mpImplRegion->mpFirstBand->mnYBottom; + long nXLeft = mpImplRegion->mpFirstBand->GetXLeftBoundary(); + long nXRight = mpImplRegion->mpFirstBand->GetXRightBoundary(); + + // look in the band list (don't test first band again!) + ImplRegionBand* pBand = mpImplRegion->mpFirstBand->mpNextBand; + while ( pBand ) + { + nYBottom = pBand->mnYBottom; + nXLeft = Min( nXLeft, pBand->GetXLeftBoundary() ); + nXRight = Max( nXRight, pBand->GetXRightBoundary() ); + + pBand = pBand->mpNextBand; + } + + // set rectangle + aRect = Rectangle( nXLeft, nYTop, nXRight, nYBottom ); + return aRect; +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::HasPolyPolygon() const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + if( !mpImplRegion ) + return false; + if( mpImplRegion->mpPolyPoly ) + return true; + if( mpImplRegion->mpB2DPolyPoly ) + return true; + return false; +} + +// ----------------------------------------------------------------------- + +PolyPolygon Region::GetPolyPolygon() const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + PolyPolygon aRet; + + if( mpImplRegion->mpPolyPoly ) + aRet = *mpImplRegion->mpPolyPoly; + else if( mpImplRegion->mpB2DPolyPoly ) + { + // the polygon needs to be converted + aRet = PolyPolygon( *mpImplRegion->mpB2DPolyPoly ); + // TODO: cache the converted polygon? + // mpImplRegion->mpB2DPolyPoly = aRet; + } + + return aRet; +} + +// ----------------------------------------------------------------------- + +const basegfx::B2DPolyPolygon Region::GetB2DPolyPolygon() const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + basegfx::B2DPolyPolygon aRet; + + if( mpImplRegion->mpB2DPolyPoly ) + aRet = *mpImplRegion->mpB2DPolyPoly; + else if( mpImplRegion->mpPolyPoly ) + { + // the polygon needs to be converted + aRet = mpImplRegion->mpPolyPoly->getB2DPolyPolygon(); + // TODO: cache the converted polygon? + // mpImplRegion->mpB2DPolyPoly = aRet; + } + + return aRet; +} + +// ----------------------------------------------------------------------- + +basegfx::B2DPolyPolygon Region::ConvertToB2DPolyPolygon() +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + basegfx::B2DPolyPolygon aRet; + + if( HasPolyPolygon() ) + aRet = GetB2DPolyPolygon(); + else + { + RegionHandle aHdl = BeginEnumRects(); + Rectangle aSubRect; + while( GetNextEnumRect( aHdl, aSubRect ) ) + { + basegfx::B2DPolygon aPoly( basegfx::tools::createPolygonFromRect( + basegfx::B2DRectangle( aSubRect.Left(), aSubRect.Top(), aSubRect.Right(), aSubRect.Bottom() ) ) ); + aRet.append( aPoly ); + } + EndEnumRects( aHdl ); + } + + return aRet; +} + +// ----------------------------------------------------------------------- + +bool Region::ImplGetFirstRect( ImplRegionInfo& rImplRegionInfo, + long& rX, long& rY, + long& rWidth, long& rHeight ) const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + ((Region*)this)->ImplPolyPolyRegionToBandRegion(); + + // no internal data? -> region is empty! + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return false; + + // no band in the list? -> region is empty! + if ( mpImplRegion->mpFirstBand == NULL ) + return false; + + // initialise pointer for first access + ImplRegionBand* pCurrRectBand = mpImplRegion->mpFirstBand; + ImplRegionBandSep* pCurrRectBandSep = pCurrRectBand->mpFirstSep; + + DBG_ASSERT( pCurrRectBandSep != NULL, "Erstes Band wurde nicht optimiert." ); + if ( !pCurrRectBandSep ) + return false; + + // get boundaries of current rectangle + rX = pCurrRectBandSep->mnXLeft; + rY = pCurrRectBand->mnYTop; + rWidth = pCurrRectBandSep->mnXRight - pCurrRectBandSep->mnXLeft + 1; + rHeight = pCurrRectBand->mnYBottom - pCurrRectBand->mnYTop + 1; + + // save pointers + rImplRegionInfo.mpVoidCurrRectBand = (void*)pCurrRectBand; + rImplRegionInfo.mpVoidCurrRectBandSep = (void*)pCurrRectBandSep; + + return true; +} + +// ----------------------------------------------------------------------- + +bool Region::ImplGetNextRect( ImplRegionInfo& rImplRegionInfo, + long& rX, long& rY, + long& rWidth, long& rHeight ) const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // no internal data? -> region is empty! + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return false; + + // get last pointers + ImplRegionBand* pCurrRectBand = (ImplRegionBand*)rImplRegionInfo.mpVoidCurrRectBand; + ImplRegionBandSep* pCurrRectBandSep = (ImplRegionBandSep*)rImplRegionInfo.mpVoidCurrRectBandSep; + + // get next separation from current band + pCurrRectBandSep = pCurrRectBandSep->mpNextSep; + + // no separation found? -> go to next band! + if ( !pCurrRectBandSep ) + { + // get next band + pCurrRectBand = pCurrRectBand->mpNextBand; + + // no band found? -> not further rectangles! + if( !pCurrRectBand ) + return false; + + // get first separation in current band + pCurrRectBandSep = pCurrRectBand->mpFirstSep; + } + + // get boundaries of current rectangle + rX = pCurrRectBandSep->mnXLeft; + rY = pCurrRectBand->mnYTop; + rWidth = pCurrRectBandSep->mnXRight - pCurrRectBandSep->mnXLeft + 1; + rHeight = pCurrRectBand->mnYBottom - pCurrRectBand->mnYTop + 1; + + // save new pointers + rImplRegionInfo.mpVoidCurrRectBand = (void*)pCurrRectBand; + rImplRegionInfo.mpVoidCurrRectBandSep = (void*)pCurrRectBandSep; + + return true; +} + +// ----------------------------------------------------------------------- + +RegionType Region::GetType() const +{ + if ( mpImplRegion == &aImplEmptyRegion ) + return REGION_EMPTY; + else if ( mpImplRegion == &aImplNullRegion ) + return REGION_NULL; + else if ( mpImplRegion->mnRectCount == 1 ) + return REGION_RECTANGLE; + else + return REGION_COMPLEX; +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::IsInside( const Point& rPoint ) const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // PolyPolygon data im Imp structure? + ((Region*)this)->ImplPolyPolyRegionToBandRegion(); +/* + if ( mpImplRegion->mpPolyPoly ) + return mpImplRegion->mpPolyPoly->IsInside( rPoint ); +*/ + + // no instance data? -> not inside + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return sal_False; + + // search band list + ImplRegionBand* pBand = mpImplRegion->mpFirstBand; + while ( pBand ) + { + // is point within band? + if ( (pBand->mnYTop <= rPoint.Y()) && + (pBand->mnYBottom >= rPoint.Y()) ) + { + // is point within separation of the band? + if ( pBand->IsInside( rPoint.X() ) ) + return sal_True; + else + return sal_False; + } + + pBand = pBand->mpNextBand; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::IsInside( const Rectangle& rRect ) const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // is rectangle empty? -> not inside + if ( rRect.IsEmpty() ) + return sal_False; + + // no instance data? -> not inside + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return sal_False; + + // create region from rectangle and intersect own region + Region aRegion = rRect; + aRegion.Exclude( *this ); + + // rectangle is inside if exclusion is empty + return aRegion.IsEmpty(); +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::IsOver( const Rectangle& rRect ) const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return sal_False; + + // Can we optimize this ??? - is used in StarDraw for brushes pointers + // Why we have no IsOver for Regions ??? + // create region from rectangle and intersect own region + Region aRegion = rRect; + aRegion.Intersect( *this ); + + // rectangle is over if include is not empty + return !aRegion.IsEmpty(); +} + +// ----------------------------------------------------------------------- + +void Region::SetNull() +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // statische Object haben RefCount von 0 + if ( mpImplRegion->mnRefCount ) + { + if ( mpImplRegion->mnRefCount > 1 ) + mpImplRegion->mnRefCount--; + else + delete mpImplRegion; + } + + // set new type + mpImplRegion = (ImplRegion*)(&aImplNullRegion); +} + +// ----------------------------------------------------------------------- + +void Region::SetEmpty() +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // statische Object haben RefCount von 0 + if ( mpImplRegion->mnRefCount ) + { + if ( mpImplRegion->mnRefCount > 1 ) + mpImplRegion->mnRefCount--; + else + delete mpImplRegion; + } + + // set new type + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); +} + +// ----------------------------------------------------------------------- + +Region& Region::operator=( const Region& rRegion ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); + DBG_ASSERT( rRegion.mpImplRegion->mnRefCount < 0xFFFFFFFE, "Region: RefCount overflow" ); + + // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann + // RefCount == 0 fuer statische Objekte + if ( rRegion.mpImplRegion->mnRefCount ) + rRegion.mpImplRegion->mnRefCount++; + + // statische Object haben RefCount von 0 + if ( mpImplRegion->mnRefCount ) + { + if ( mpImplRegion->mnRefCount > 1 ) + mpImplRegion->mnRefCount--; + else + delete mpImplRegion; + } + + mpImplRegion = rRegion.mpImplRegion; + return *this; +} + +// ----------------------------------------------------------------------- + +Region& Region::operator=( const Rectangle& rRect ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // statische Object haben RefCount von 0 + if ( mpImplRegion->mnRefCount ) + { + if ( mpImplRegion->mnRefCount > 1 ) + mpImplRegion->mnRefCount--; + else + delete mpImplRegion; + } + + ImplCreateRectRegion( rRect ); + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::operator==( const Region& rRegion ) const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); + + // reference to same object? -> equal! + if ( mpImplRegion == rRegion.mpImplRegion ) + return sal_True; + + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return sal_False; + + if ( (rRegion.mpImplRegion == &aImplEmptyRegion) || (rRegion.mpImplRegion == &aImplNullRegion) ) + return sal_False; + + if ( rRegion.mpImplRegion->mpPolyPoly && mpImplRegion->mpPolyPoly ) + return *rRegion.mpImplRegion->mpPolyPoly == *mpImplRegion->mpPolyPoly; + else + { + ((Region*)this)->ImplPolyPolyRegionToBandRegion(); + ((Region*)&rRegion)->ImplPolyPolyRegionToBandRegion(); + + // Eine der beiden Regions kann jetzt Empty sein + if ( mpImplRegion == rRegion.mpImplRegion ) + return sal_True; + + if ( mpImplRegion == &aImplEmptyRegion ) + return sal_False; + + if ( rRegion.mpImplRegion == &aImplEmptyRegion ) + return sal_False; + } + + // initialise pointers + ImplRegionBand* pOwnRectBand = mpImplRegion->mpFirstBand; + ImplRegionBandSep* pOwnRectBandSep = pOwnRectBand->mpFirstSep; + ImplRegionBand* pSecondRectBand = rRegion.mpImplRegion->mpFirstBand; + ImplRegionBandSep* pSecondRectBandSep = pSecondRectBand->mpFirstSep; + while ( pOwnRectBandSep && pSecondRectBandSep ) + { + // get boundaries of current rectangle + long nOwnXLeft = pOwnRectBandSep->mnXLeft; + long nSecondXLeft = pSecondRectBandSep->mnXLeft; + if ( nOwnXLeft != nSecondXLeft ) + return sal_False; + + long nOwnYTop = pOwnRectBand->mnYTop; + long nSecondYTop = pSecondRectBand->mnYTop; + if ( nOwnYTop != nSecondYTop ) + return sal_False; + + long nOwnXRight = pOwnRectBandSep->mnXRight; + long nSecondXRight = pSecondRectBandSep->mnXRight; + if ( nOwnXRight != nSecondXRight ) + return sal_False; + + long nOwnYBottom = pOwnRectBand->mnYBottom; + long nSecondYBottom = pSecondRectBand->mnYBottom; + if ( nOwnYBottom != nSecondYBottom ) + return sal_False; + + // get next separation from current band + pOwnRectBandSep = pOwnRectBandSep->mpNextSep; + + // no separation found? -> go to next band! + if ( !pOwnRectBandSep ) + { + // get next band + pOwnRectBand = pOwnRectBand->mpNextBand; + + // get first separation in current band + if( pOwnRectBand ) + pOwnRectBandSep = pOwnRectBand->mpFirstSep; + } + + // get next separation from current band + pSecondRectBandSep = pSecondRectBandSep->mpNextSep; + + // no separation found? -> go to next band! + if ( !pSecondRectBandSep ) + { + // get next band + pSecondRectBand = pSecondRectBand->mpNextBand; + + // get first separation in current band + if( pSecondRectBand ) + pSecondRectBandSep = pSecondRectBand->mpFirstSep; + } + + if ( pOwnRectBandSep && !pSecondRectBandSep ) + return sal_False; + + if ( !pOwnRectBandSep && pSecondRectBandSep ) + return sal_False; + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +enum StreamEntryType { STREAMENTRY_BANDHEADER, STREAMENTRY_SEPARATION, STREAMENTRY_END }; + +SvStream& operator>>( SvStream& rIStrm, Region& rRegion ) +{ + DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); + + VersionCompat aCompat( rIStrm, STREAM_READ ); + sal_uInt16 nVersion; + sal_uInt16 nTmp16; + + // statische Object haben RefCount von 0 + if ( rRegion.mpImplRegion->mnRefCount ) + { + if ( rRegion.mpImplRegion->mnRefCount > 1 ) + rRegion.mpImplRegion->mnRefCount--; + else + delete rRegion.mpImplRegion; + } + + // get version of streamed region + rIStrm >> nVersion; + + // get type of region + rIStrm >> nTmp16; + + RegionType meStreamedType = (RegionType)nTmp16; + + switch( meStreamedType ) + { + case REGION_NULL: + rRegion.mpImplRegion = (ImplRegion*)&aImplNullRegion; + break; + + case REGION_EMPTY: + rRegion.mpImplRegion = (ImplRegion*)&aImplEmptyRegion; + break; + + default: + { + // create instance of implementation class + rRegion.mpImplRegion = new ImplRegion(); + + // get header from first element + rIStrm >> nTmp16; + + // get all bands + rRegion.mpImplRegion->mnRectCount = 0; + ImplRegionBand* pCurrBand = NULL; + while ( (StreamEntryType)nTmp16 != STREAMENTRY_END ) + { + // insert new band or new separation? + if ( (StreamEntryType)nTmp16 == STREAMENTRY_BANDHEADER ) + { + long nYTop; + long nYBottom; + + rIStrm >> nYTop; + rIStrm >> nYBottom; + + // create band + ImplRegionBand* pNewBand = new ImplRegionBand( nYTop, nYBottom ); + + // first element? -> set as first into the list + if ( !pCurrBand ) + rRegion.mpImplRegion->mpFirstBand = pNewBand; + else + pCurrBand->mpNextBand = pNewBand; + + // save pointer for next creation + pCurrBand = pNewBand; + } + else + { + long nXLeft; + long nXRight; + + rIStrm >> nXLeft; + rIStrm >> nXRight; + + // add separation + if ( pCurrBand ) + { + pCurrBand->Union( nXLeft, nXRight ); + rRegion.mpImplRegion->mnRectCount++; + } + } + + if( rIStrm.IsEof() ) + { + OSL_FAIL( "premature end of region stream" ); + delete rRegion.mpImplRegion; + rRegion.mpImplRegion = (ImplRegion*)&aImplEmptyRegion; + return rIStrm; + } + + // get next header + rIStrm >> nTmp16; + } + + if( aCompat.GetVersion() >= 2 ) + { + sal_Bool bHasPolyPolygon; + + rIStrm >> bHasPolyPolygon; + + if( bHasPolyPolygon ) + { + delete rRegion.mpImplRegion->mpPolyPoly; + rRegion.mpImplRegion->mpPolyPoly = new PolyPolygon; + rIStrm >> *( rRegion.mpImplRegion->mpPolyPoly ); + } + } + } + break; + } + + return rIStrm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStrm, const Region& rRegion ) +{ + DBG_CHKOBJ( &rRegion, Region, ImplDbgTestRegion ); + + sal_uInt16 nVersion = 2; + VersionCompat aCompat( rOStrm, STREAM_WRITE, nVersion ); + Region aTmpRegion( rRegion ); + + // use tmp region to avoid destruction of internal region (polypolygon) of rRegion + aTmpRegion.ImplPolyPolyRegionToBandRegion(); + + // put version + rOStrm << nVersion; + + // put type + rOStrm << (sal_uInt16)aTmpRegion.GetType(); + + // put all bands if not null or empty + if ( (aTmpRegion.mpImplRegion != &aImplEmptyRegion) && (aTmpRegion.mpImplRegion != &aImplNullRegion) ) + { + ImplRegionBand* pBand = aTmpRegion.mpImplRegion->mpFirstBand; + while ( pBand ) + { + // put boundaries + rOStrm << (sal_uInt16) STREAMENTRY_BANDHEADER; + rOStrm << pBand->mnYTop; + rOStrm << pBand->mnYBottom; + + // put separations of current band + ImplRegionBandSep* pSep = pBand->mpFirstSep; + while ( pSep ) + { + // put separation + rOStrm << (sal_uInt16) STREAMENTRY_SEPARATION; + rOStrm << pSep->mnXLeft; + rOStrm << pSep->mnXRight; + + // next separation from current band + pSep = pSep->mpNextSep; + } + + pBand = pBand->mpNextBand; + } + + // put endmarker + rOStrm << (sal_uInt16) STREAMENTRY_END; + + // write polypolygon if available + const sal_Bool bHasPolyPolygon = rRegion.HasPolyPolygon(); + rOStrm << bHasPolyPolygon; + + if( bHasPolyPolygon ) + { + // #i105373# + PolyPolygon aNoCurvePolyPolygon; + rRegion.GetPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon); + + rOStrm << aNoCurvePolyPolygon; + } + } + + return rOStrm; +} + +// ----------------------------------------------------------------------- + +void Region::ImplBeginAddRect() +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + // statische Object haben RefCount von 0 + if ( mpImplRegion->mnRefCount ) + { + if ( mpImplRegion->mnRefCount > 1 ) + mpImplRegion->mnRefCount--; + else + delete mpImplRegion; + } + + // create fresh region + mpImplRegion = new ImplRegion(); +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::ImplAddRect( const Rectangle& rRect ) +{ + // Hier kein CheckThis, da nicht alle Daten auf Stand + + if ( rRect.IsEmpty() ) + return sal_True; + + // get justified rectangle + long nTop; + long nBottom; + long nLeft; + long nRight; + if ( rRect.Top() <= rRect.Bottom() ) + { + nTop = rRect.Top(); + nBottom = rRect.Bottom(); + } + else + { + nTop = rRect.Bottom(); + nBottom = rRect.Top(); + } + if ( rRect.Left() <= rRect.Right() ) + { + nLeft = rRect.Left(); + nRight = rRect.Right(); + } + else + { + nLeft = rRect.Right(); + nRight = rRect.Left(); + } + + if ( !mpImplRegion->mpLastCheckedBand ) + { + // create new band + mpImplRegion->mpLastCheckedBand = new ImplRegionBand( nTop, nBottom ); + + // set band as current + mpImplRegion->mpFirstBand = mpImplRegion->mpLastCheckedBand; + mpImplRegion->mpLastCheckedBand->Union( nLeft, nRight ); + } + else + { + DBG_ASSERT( nTop >= mpImplRegion->mpLastCheckedBand->mnYTop, + "Region::ImplAddRect() - nTopY < nLastTopY" ); + + // new band? create it! + if ( (nTop != mpImplRegion->mpLastCheckedBand->mnYTop) || + (nBottom != mpImplRegion->mpLastCheckedBand->mnYBottom) ) + { + // create new band + ImplRegionBand* pNewRegionBand = new ImplRegionBand( nTop, nBottom ); + + // append band to the end + mpImplRegion->mpLastCheckedBand->mpNextBand = pNewRegionBand; + + // skip to the new band + mpImplRegion->mpLastCheckedBand = mpImplRegion->mpLastCheckedBand->mpNextBand; + } + + // Insert Sep + mpImplRegion->mpLastCheckedBand->Union( nLeft, nRight ); + } + + return sal_True; +} + +// ----------------------------------------------------------------------- + +void Region::ImplEndAddRect() +{ + // check if we are empty + if ( !mpImplRegion->mpFirstBand ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + return; + } + + // check if we have somthing to optimize + if ( !mpImplRegion->mpFirstBand->mpNextBand ) + { + // update mpImplRegion->mnRectCount, because no OptimizeBandList is called + ImplRegionBandSep* pSep = mpImplRegion->mpFirstBand->mpFirstSep; + mpImplRegion->mnRectCount = 0; + while( pSep ) + { + mpImplRegion->mnRectCount++; + pSep = pSep->mpNextSep; + } + + // Erst hier testen, da hier die Daten wieder stimmen + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + return; + } + + // have to revert list? -> do it now! + if ( mpImplRegion->mpFirstBand->mnYTop > + mpImplRegion->mpFirstBand->mpNextBand->mnYTop ) + { + ImplRegionBand * pNewFirstRegionBand; + + // initialize temp list with first element + pNewFirstRegionBand = mpImplRegion->mpFirstBand; + mpImplRegion->mpFirstBand = mpImplRegion->mpFirstBand->mpNextBand; + pNewFirstRegionBand->mpNextBand = NULL; + + // insert elements to the temp list + while ( mpImplRegion->mpFirstBand ) + { + ImplRegionBand * pSavedRegionBand = pNewFirstRegionBand; + pNewFirstRegionBand = mpImplRegion->mpFirstBand; + mpImplRegion->mpFirstBand = mpImplRegion->mpFirstBand->mpNextBand; + pNewFirstRegionBand->mpNextBand = pSavedRegionBand; + } + + // set temp list as new list + mpImplRegion->mpFirstBand = pNewFirstRegionBand; + } + + // cleanup + if ( !mpImplRegion->OptimizeBandList() ) + { + delete mpImplRegion; + mpImplRegion = (ImplRegion*)(&aImplEmptyRegion); + } + + // Erst hier testen, da hier die Daten wieder stimmen + DBG_CHKTHIS( Region, ImplDbgTestRegion ); +} + +// ----------------------------------------------------------------------- + +sal_uLong Region::GetRectCount() const +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + ((Region*)this)->ImplPolyPolyRegionToBandRegion(); + +#ifdef DBG_UTIL + sal_uLong nCount = 0; + + // all bands if not null or empty + if ( (mpImplRegion != &aImplEmptyRegion) && (mpImplRegion != &aImplNullRegion) ) + { + ImplRegionBand* pBand = mpImplRegion->mpFirstBand; + while ( pBand ) + { + ImplRegionBandSep* pSep = pBand->mpFirstSep; + while( pSep ) + { + nCount++; + pSep = pSep->mpNextSep; + } + + pBand = pBand->mpNextBand; + } + } + + DBG_ASSERT( mpImplRegion->mnRectCount == nCount, "Region: invalid mnRectCount!" ); +#endif + + return mpImplRegion->mnRectCount; +} + +// ----------------------------------------------------------------------- + +RegionHandle Region::BeginEnumRects() +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + ImplPolyPolyRegionToBandRegion(); + + // no internal data? -> region is empty! + if ( (mpImplRegion == &aImplEmptyRegion) || (mpImplRegion == &aImplNullRegion) ) + return 0; + + // no band in the list? -> region is empty! + if ( mpImplRegion->mpFirstBand == NULL ) + { + DBG_ASSERT( mpImplRegion->mpFirstBand, "Region::BeginEnumRects() First Band is Empty!" ); + return 0; + } + + ImplRegionHandle* pData = new ImplRegionHandle; + pData->mpRegion = new Region( *this ); + pData->mbFirst = sal_True; + + // save pointers + pData->mpCurrRectBand = pData->mpRegion->mpImplRegion->mpFirstBand; + pData->mpCurrRectBandSep = pData->mpCurrRectBand->mpFirstSep; + + return (RegionHandle)pData; +} + +// ----------------------------------------------------------------------- + +sal_Bool Region::GetEnumRects( RegionHandle pVoidData, Rectangle& rRect ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + ImplRegionHandle* pData = (ImplRegionHandle*)pVoidData; + if ( !pData ) + return sal_False; + + if ( pData->mbFirst ) + pData->mbFirst = sal_False; + else + { + // get next separation from current band + pData->mpCurrRectBandSep = pData->mpCurrRectBandSep->mpNextSep; + + // no separation found? -> go to next band! + if ( !pData->mpCurrRectBandSep ) + { + // get next band + pData->mpCurrRectBand = pData->mpCurrRectBand->mpNextBand; + + // no band found? -> not further rectangles! + if ( !pData->mpCurrRectBand ) + return sal_False; + + // get first separation in current band + pData->mpCurrRectBandSep = pData->mpCurrRectBand->mpFirstSep; + } + } + + // get boundaries of current rectangle + rRect.Top() = pData->mpCurrRectBand->mnYTop; + rRect.Bottom() = pData->mpCurrRectBand->mnYBottom; + rRect.Left() = pData->mpCurrRectBandSep->mnXLeft; + rRect.Right() = pData->mpCurrRectBandSep->mnXRight; + return sal_True; +} + +// ----------------------------------------------------------------------- + +void Region::EndEnumRects( RegionHandle pVoidData ) +{ + DBG_CHKTHIS( Region, ImplDbgTestRegion ); + + ImplRegionHandle* pData = (ImplRegionHandle*)pVoidData; + if ( !pData ) + return; + + // cleanup + delete pData->mpRegion; + delete pData; +} + +// ----------------------------------------------------------------------- + +static inline bool ImplPolygonRectTest( const Polygon& rPoly, Rectangle* pRectOut = NULL ) +{ + bool bIsRect = false; + const Point* pPoints = rPoly.GetConstPointAry(); + sal_uInt16 nPoints = rPoly.GetSize(); + if( nPoints == 4 || (nPoints == 5 && pPoints[0] == pPoints[4]) ) + { + long nX1 = pPoints[0].X(), nX2 = pPoints[2].X(), + nY1 = pPoints[0].Y(), nY2 = pPoints[2].Y(); + if( ( (pPoints[1].X() == nX1 && pPoints[3].X() == nX2) && + (pPoints[1].Y() == nY2 && pPoints[3].Y() == nY1) ) + || + ( (pPoints[1].X() == nX2 && pPoints[3].X() == nX1) && + (pPoints[1].Y() == nY1 && pPoints[3].Y() == nY2) ) ) + { + bIsRect = true; + if( pRectOut ) + { + long nSwap; + if( nX2 < nX1 ) + { + nSwap = nX2; + nX2 = nX1; + nX1 = nSwap; + } + if( nY2 < nY1 ) + { + nSwap = nY2; + nY2 = nY1; + nY1 = nSwap; + } + if( nX2 != nX1 ) + nX2--; + if( nY2 != nY1 ) + nY2--; + pRectOut->Left() = nX1; + pRectOut->Right() = nX2; + pRectOut->Top() = nY1; + pRectOut->Bottom() = nY2; + } + } + } + return bIsRect; +} + +Region Region::GetRegionFromPolyPolygon( const PolyPolygon& rPolyPoly ) +{ + //return Region( rPolyPoly ); + + // check if it's worth extracting the XOr'ing the Rectangles + // empiricism shows that break even between XOr'ing rectangles separately + // and ImplPolyPolyRegionToBandRegion is at half rectangles/half polygons + int nPolygonRects = 0, nPolygonPolygons = 0; + int nPolygons = rPolyPoly.Count(); + + for( sal_uInt16 i = 0; i < nPolygons; i++ ) + { + const Polygon& rPoly = rPolyPoly[i]; + if( ImplPolygonRectTest( rPoly ) ) + nPolygonRects++; + else + nPolygonPolygons++; + } + if( nPolygonPolygons > nPolygonRects ) + return Region( rPolyPoly ); + + Region aResult; + Rectangle aRect; + for( sal_uInt16 i = 0; i < nPolygons; i++ ) + { + const Polygon& rPoly = rPolyPoly[i]; + if( ImplPolygonRectTest( rPoly, &aRect ) ) + aResult.XOr( aRect ); + else + aResult.XOr( Region(rPoly) ); + } + return aResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx new file mode 100644 index 000000000000..9fa4d813b2b7 --- /dev/null +++ b/vcl/source/gdi/salgdilayout.cxx @@ -0,0 +1,828 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <tools/ref.hxx> +#include <svsys.h> +#include <vcl/salgdi.hxx> +#include <vcl/salframe.hxx> +#include <vcl/salvd.hxx> +#include <vcl/salprn.hxx> +#include <tools/debug.hxx> +#include <vcl/svdata.hxx> +#include <vcl/svapp.hxx> +#include <tools/poly.hxx> +#include <vcl/region.hxx> +#include <vcl/region.h> +#include <vcl/virdev.hxx> +#include <vcl/window.h> +#include <vcl/window.hxx> +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/outdata.hxx> +#include <vcl/print.hxx> +#include <vcl/outdev.h> +#include <vcl/outdev.hxx> +#include <vcl/unowrap.hxx> +#include <vcl/sallayout.hxx> +#include "basegfx/polygon/b2dpolygon.hxx" + +// ---------------------------------------------------------------------------- +// The only common SalFrame method +// ---------------------------------------------------------------------------- + +SalFrameGeometry SalFrame::GetGeometry() +{ + // mirror frame coordinates at parent + SalFrame *pParent = GetParent(); + if( pParent && Application::GetSettings().GetLayoutRTL() ) + { + SalFrameGeometry aGeom = maGeometry; + int parent_x = aGeom.nX - pParent->maGeometry.nX; + aGeom.nX = pParent->maGeometry.nX + pParent->maGeometry.nWidth - maGeometry.nWidth - parent_x; + return aGeom; + } + else + return maGeometry; +} + +// ---------------------------------------------------------------------------- + +SalGraphics::SalGraphics() +: m_nLayout( 0 ), + m_bAntiAliasB2DDraw(false) +{ + // read global RTL settings + if( Application::GetSettings().GetLayoutRTL() ) + m_nLayout = SAL_LAYOUT_BIDI_RTL; +} + +SalGraphics::~SalGraphics() +{ +} + +// ---------------------------------------------------------------------------- + +bool SalGraphics::drawAlphaBitmap( const SalTwoRect&, + const SalBitmap&, const SalBitmap& ) +{ + return false; +} + +// ---------------------------------------------------------------------------- + +void SalGraphics::mirror( long& x, const OutputDevice *pOutDev, bool bBack ) const +{ + long w; + if( pOutDev && pOutDev->GetOutDevType() == OUTDEV_VIRDEV ) + w = pOutDev->GetOutputWidthPixel(); + else + w = GetGraphicsWidth(); + + if( w ) + { + if( pOutDev && pOutDev->ImplIsAntiparallel() ) + { + OutputDevice *pOutDevRef = (OutputDevice*) pOutDev; + // mirror this window back + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) ) + { + long devX = w-pOutDevRef->GetOutputWidthPixel()-pOutDevRef->GetOutOffXPixel(); // re-mirrored mnOutOffX + if( bBack ) + x = x - devX + pOutDevRef->GetOutOffXPixel(); + else + x = devX + (x - pOutDevRef->GetOutOffXPixel()); + } + else + { + long devX = pOutDevRef->GetOutOffXPixel(); // re-mirrored mnOutOffX + if( bBack ) + x = x - pOutDevRef->GetOutputWidthPixel() + devX - pOutDevRef->GetOutOffXPixel() + 1; + else + x = pOutDevRef->GetOutputWidthPixel() - (x - devX) + pOutDevRef->GetOutOffXPixel() - 1; + } + } + else if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) ) + x = w-1-x; + } +} + +void SalGraphics::mirror( long& x, long& nWidth, const OutputDevice *pOutDev, bool bBack ) const +{ + long w; + if( pOutDev && pOutDev->GetOutDevType() == OUTDEV_VIRDEV ) + w = pOutDev->GetOutputWidthPixel(); + else + w = GetGraphicsWidth(); + + if( w ) + { + if( pOutDev && pOutDev->ImplIsAntiparallel() ) + { + OutputDevice *pOutDevRef = (OutputDevice*) pOutDev; + // mirror this window back + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) ) + { + long devX = w-pOutDevRef->GetOutputWidthPixel()-pOutDevRef->GetOutOffXPixel(); // re-mirrored mnOutOffX + if( bBack ) + x = x - devX + pOutDevRef->GetOutOffXPixel(); + else + x = devX + (x - pOutDevRef->GetOutOffXPixel()); + } + else + { + long devX = pOutDevRef->GetOutOffXPixel(); // re-mirrored mnOutOffX + if( bBack ) + x = x - pOutDevRef->GetOutputWidthPixel() + devX - pOutDevRef->GetOutOffXPixel() + nWidth; + else + x = pOutDevRef->GetOutputWidthPixel() - (x - devX) + pOutDevRef->GetOutOffXPixel() - nWidth; + } + } + else if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) ) + x = w-nWidth-x; + + } +} + +sal_Bool SalGraphics::mirror( sal_uInt32 nPoints, const SalPoint *pPtAry, SalPoint *pPtAry2, const OutputDevice *pOutDev, bool bBack ) const +{ + long w; + if( pOutDev && pOutDev->GetOutDevType() == OUTDEV_VIRDEV ) + w = pOutDev->GetOutputWidthPixel(); + else + w = GetGraphicsWidth(); + + if( w ) + { + sal_uInt32 i, j; + + if( pOutDev && pOutDev->ImplIsAntiparallel() ) + { + OutputDevice *pOutDevRef = (OutputDevice*) pOutDev; + // mirror this window back + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) ) + { + long devX = w-pOutDevRef->GetOutputWidthPixel()-pOutDevRef->GetOutOffXPixel(); // re-mirrored mnOutOffX + if( bBack ) + { + for( i=0, j=nPoints-1; i<nPoints; i++,j-- ) + { + //long x = w-1-pPtAry[i].mnX; + //pPtAry2[j].mnX = devX + ( pOutDevRef->mnOutWidth - 1 - (x - devX) ); + pPtAry2[j].mnX = pOutDevRef->GetOutOffXPixel() + (pPtAry[i].mnX - devX); + pPtAry2[j].mnY = pPtAry[i].mnY; + } + } + else + { + for( i=0, j=nPoints-1; i<nPoints; i++,j-- ) + { + //long x = w-1-pPtAry[i].mnX; + //pPtAry2[j].mnX = devX + ( pOutDevRef->mnOutWidth - 1 - (x - devX) ); + pPtAry2[j].mnX = devX + (pPtAry[i].mnX - pOutDevRef->GetOutOffXPixel()); + pPtAry2[j].mnY = pPtAry[i].mnY; + } + } + } + else + { + long devX = pOutDevRef->GetOutOffXPixel(); // re-mirrored mnOutOffX + if( bBack ) + { + for( i=0, j=nPoints-1; i<nPoints; i++,j-- ) + { + //long x = w-1-pPtAry[i].mnX; + //pPtAry2[j].mnX = devX + ( pOutDevRef->mnOutWidth - 1 - (x - devX) ); + pPtAry2[j].mnX = pPtAry[i].mnX - pOutDevRef->GetOutputWidthPixel() + devX - pOutDevRef->GetOutOffXPixel() + 1; + pPtAry2[j].mnY = pPtAry[i].mnY; + } + } + else + { + for( i=0, j=nPoints-1; i<nPoints; i++,j-- ) + { + //long x = w-1-pPtAry[i].mnX; + //pPtAry2[j].mnX = devX + ( pOutDevRef->mnOutWidth - 1 - (x - devX) ); + pPtAry2[j].mnX = pOutDevRef->GetOutputWidthPixel() - (pPtAry[i].mnX - devX) + pOutDevRef->GetOutOffXPixel() - 1; + pPtAry2[j].mnY = pPtAry[i].mnY; + } + } + } + } + else if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) ) + { + for( i=0, j=nPoints-1; i<nPoints; i++,j-- ) + { + pPtAry2[j].mnX = w-1-pPtAry[i].mnX; + pPtAry2[j].mnY = pPtAry[i].mnY; + } + } + return sal_True; + } + else + return sal_False; +} + +void SalGraphics::mirror( Region& rRgn, const OutputDevice *pOutDev, bool bBack ) const +{ + if( rRgn.HasPolyPolygon() ) + { + basegfx::B2DPolyPolygon aPolyPoly( rRgn.ConvertToB2DPolyPolygon() ); + aPolyPoly = mirror( aPolyPoly, pOutDev, bBack ); + rRgn = Region( aPolyPoly ); + } + else + { + ImplRegionInfo aInfo; + bool bRegionRect; + Region aMirroredRegion; + long nX, nY, nWidth, nHeight; + + bRegionRect = rRgn.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight ); + while ( bRegionRect ) + { + Rectangle aRect( Point(nX, nY), Size(nWidth, nHeight) ); + mirror( aRect, pOutDev, bBack ); + aMirroredRegion.Union( aRect ); + bRegionRect = rRgn.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight ); + } + rRgn = aMirroredRegion; + } +} + +void SalGraphics::mirror( Rectangle& rRect, const OutputDevice *pOutDev, bool bBack ) const +{ + long nWidth = rRect.GetWidth(); + long x = rRect.Left(); + long x_org = x; + + mirror( x, nWidth, pOutDev, bBack ); + rRect.Move( x - x_org, 0 ); +} + +basegfx::B2DPoint SalGraphics::mirror( const basegfx::B2DPoint& i_rPoint, const OutputDevice *i_pOutDev, bool i_bBack ) const +{ + long w; + if( i_pOutDev && i_pOutDev->GetOutDevType() == OUTDEV_VIRDEV ) + w = i_pOutDev->GetOutputWidthPixel(); + else + w = GetGraphicsWidth(); + + DBG_ASSERT( w, "missing graphics width" ); + + basegfx::B2DPoint aRet( i_rPoint ); + if( w ) + { + if( i_pOutDev && !i_pOutDev->IsRTLEnabled() ) + { + OutputDevice *pOutDevRef = (OutputDevice*)i_pOutDev; + // mirror this window back + double devX = w-pOutDevRef->GetOutputWidthPixel()-pOutDevRef->GetOutOffXPixel(); // re-mirrored mnOutOffX + if( i_bBack ) + aRet.setX( i_rPoint.getX() - devX + pOutDevRef->GetOutOffXPixel() ); + else + aRet.setX( devX + (i_rPoint.getX() - pOutDevRef->GetOutOffXPixel()) ); + } + else + aRet.setX( w-1-i_rPoint.getX() ); + } + return aRet; +} + +basegfx::B2DPolygon SalGraphics::mirror( const basegfx::B2DPolygon& i_rPoly, const OutputDevice *i_pOutDev, bool i_bBack ) const +{ + long w; + if( i_pOutDev && i_pOutDev->GetOutDevType() == OUTDEV_VIRDEV ) + w = i_pOutDev->GetOutputWidthPixel(); + else + w = GetGraphicsWidth(); + + DBG_ASSERT( w, "missing graphics width" ); + + basegfx::B2DPolygon aRet; + if( w ) + { + sal_Int32 nPoints = i_rPoly.count(); + for( sal_Int32 i = 0; i < nPoints; i++ ) + { + aRet.append( mirror( i_rPoly.getB2DPoint( i ), i_pOutDev, i_bBack ) ); + if( i_rPoly.isPrevControlPointUsed( i ) ) + aRet.setPrevControlPoint( i, mirror( i_rPoly.getPrevControlPoint( i ), i_pOutDev, i_bBack ) ); + if( i_rPoly.isNextControlPointUsed( i ) ) + aRet.setNextControlPoint( i, mirror( i_rPoly.getNextControlPoint( i ), i_pOutDev, i_bBack ) ); + } + aRet.setClosed( i_rPoly.isClosed() ); + aRet.flip(); + } + else + aRet = i_rPoly; + return aRet; +} + +basegfx::B2DPolyPolygon SalGraphics::mirror( const basegfx::B2DPolyPolygon& i_rPoly, const OutputDevice *i_pOutDev, bool i_bBack ) const +{ + long w; + if( i_pOutDev && i_pOutDev->GetOutDevType() == OUTDEV_VIRDEV ) + w = i_pOutDev->GetOutputWidthPixel(); + else + w = GetGraphicsWidth(); + + DBG_ASSERT( w, "missing graphics width" ); + + basegfx::B2DPolyPolygon aRet; + if( w ) + { + sal_Int32 nPoly = i_rPoly.count(); + for( sal_Int32 i = 0; i < nPoly; i++ ) + aRet.append( mirror( i_rPoly.getB2DPolygon( i ), i_pOutDev, i_bBack ) ); + aRet.setClosed( i_rPoly.isClosed() ); + aRet.flip(); + } + else + aRet = i_rPoly; + return aRet; +} + +// ---------------------------------------------------------------------------- + +bool SalGraphics::SetClipRegion( const Region& i_rClip, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + Region aMirror( i_rClip ); + mirror( aMirror, pOutDev ); + return setClipRegion( aMirror ); + } + return setClipRegion( i_rClip ); +} + +void SalGraphics::DrawPixel( long nX, long nY, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + mirror( nX, pOutDev ); + drawPixel( nX, nY ); +} +void SalGraphics::DrawPixel( long nX, long nY, SalColor nSalColor, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + mirror( nX, pOutDev ); + drawPixel( nX, nY, nSalColor ); +} +void SalGraphics::DrawLine( long nX1, long nY1, long nX2, long nY2, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + mirror( nX1, pOutDev ); + mirror( nX2, pOutDev ); + } + drawLine( nX1, nY1, nX2, nY2 ); +} +void SalGraphics::DrawRect( long nX, long nY, long nWidth, long nHeight, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + mirror( nX, nWidth, pOutDev ); + drawRect( nX, nY, nWidth, nHeight ); +} +bool SalGraphics::drawPolyLine( + const basegfx::B2DPolygon& /*rPolyPolygon*/, + double /*fTransparency*/, + const basegfx::B2DVector& /*rLineWidths*/, + basegfx::B2DLineJoin /*eLineJoin*/) +{ + return false; +} + +void SalGraphics::DrawPolyLine( sal_uLong nPoints, const SalPoint* pPtAry, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalPoint* pPtAry2 = new SalPoint[nPoints]; + sal_Bool bCopied = mirror( nPoints, pPtAry, pPtAry2, pOutDev ); + drawPolyLine( nPoints, bCopied ? pPtAry2 : pPtAry ); + delete [] pPtAry2; + } + else + drawPolyLine( nPoints, pPtAry ); +} + +void SalGraphics::DrawPolygon( sal_uLong nPoints, const SalPoint* pPtAry, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalPoint* pPtAry2 = new SalPoint[nPoints]; + sal_Bool bCopied = mirror( nPoints, pPtAry, pPtAry2, pOutDev ); + drawPolygon( nPoints, bCopied ? pPtAry2 : pPtAry ); + delete [] pPtAry2; + } + else + drawPolygon( nPoints, pPtAry ); +} + +void SalGraphics::DrawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + // TODO: optimize, reduce new/delete calls + SalPoint **pPtAry2 = new SalPoint*[nPoly]; + sal_uLong i; + for(i=0; i<nPoly; i++) + { + sal_uLong nPoints = pPoints[i]; + pPtAry2[i] = new SalPoint[ nPoints ]; + mirror( nPoints, pPtAry[i], pPtAry2[i], pOutDev ); + } + + drawPolyPolygon( nPoly, pPoints, (PCONSTSALPOINT*)pPtAry2 ); + + for(i=0; i<nPoly; i++) + delete [] pPtAry2[i]; + delete [] pPtAry2; + } + else + drawPolyPolygon( nPoly, pPoints, pPtAry ); +} + +bool SalGraphics::DrawPolyPolygon( const ::basegfx::B2DPolyPolygon& i_rPolyPolygon, double i_fTransparency, const OutputDevice* i_pOutDev ) +{ + bool bRet = false; + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (i_pOutDev && i_pOutDev->IsRTLEnabled()) ) + { + basegfx::B2DPolyPolygon aMirror( mirror( i_rPolyPolygon, i_pOutDev ) ); + bRet = drawPolyPolygon( aMirror, i_fTransparency ); + } + else + bRet = drawPolyPolygon( i_rPolyPolygon, i_fTransparency ); + return bRet; +} + +bool SalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double /*fTransparency*/) +{ + return false; +} + +sal_Bool SalGraphics::DrawPolyLineBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry, const OutputDevice* pOutDev ) +{ + sal_Bool bResult = sal_False; + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalPoint* pPtAry2 = new SalPoint[nPoints]; + sal_Bool bCopied = mirror( nPoints, pPtAry, pPtAry2, pOutDev ); + bResult = drawPolyLineBezier( nPoints, bCopied ? pPtAry2 : pPtAry, pFlgAry ); + delete [] pPtAry2; + } + else + bResult = drawPolyLineBezier( nPoints, pPtAry, pFlgAry ); + return bResult; +} + +sal_Bool SalGraphics::DrawPolygonBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry, const OutputDevice* pOutDev ) +{ + sal_Bool bResult = sal_False; + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalPoint* pPtAry2 = new SalPoint[nPoints]; + sal_Bool bCopied = mirror( nPoints, pPtAry, pPtAry2, pOutDev ); + bResult = drawPolygonBezier( nPoints, bCopied ? pPtAry2 : pPtAry, pFlgAry ); + delete [] pPtAry2; + } + else + bResult = drawPolygonBezier( nPoints, pPtAry, pFlgAry ); + return bResult; +} + +sal_Bool SalGraphics::DrawPolyPolygonBezier( sal_uInt32 i_nPoly, const sal_uInt32* i_pPoints, + const SalPoint* const* i_pPtAry, const sal_uInt8* const* i_pFlgAry, const OutputDevice* i_pOutDev ) +{ + sal_Bool bRet = sal_False; + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (i_pOutDev && i_pOutDev->IsRTLEnabled()) ) + { + // TODO: optimize, reduce new/delete calls + SalPoint **pPtAry2 = new SalPoint*[i_nPoly]; + sal_uLong i; + for(i=0; i<i_nPoly; i++) + { + sal_uLong nPoints = i_pPoints[i]; + pPtAry2[i] = new SalPoint[ nPoints ]; + mirror( nPoints, i_pPtAry[i], pPtAry2[i], i_pOutDev ); + } + + bRet = drawPolyPolygonBezier( i_nPoly, i_pPoints, (PCONSTSALPOINT*)pPtAry2, i_pFlgAry ); + + for(i=0; i<i_nPoly; i++) + delete [] pPtAry2[i]; + delete [] pPtAry2; + } + else + bRet = drawPolyPolygonBezier( i_nPoly, i_pPoints, i_pPtAry, i_pFlgAry ); + return bRet; +} + +bool SalGraphics::DrawPolyLine( const ::basegfx::B2DPolygon& i_rPolygon, double fTransparency, + const ::basegfx::B2DVector& i_rLineWidth, basegfx::B2DLineJoin i_eLineJoin, + const OutputDevice* i_pOutDev ) +{ + bool bRet = false; + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (i_pOutDev && i_pOutDev->IsRTLEnabled()) ) + { + basegfx::B2DPolygon aMirror( mirror( i_rPolygon, i_pOutDev ) ); + bRet = drawPolyLine( aMirror, fTransparency, i_rLineWidth, i_eLineJoin ); + } + else + bRet = drawPolyLine( i_rPolygon, fTransparency, i_rLineWidth, i_eLineJoin ); + return bRet; +} + +void SalGraphics::CopyArea( long nDestX, long nDestY, + long nSrcX, long nSrcY, + long nSrcWidth, long nSrcHeight, + sal_uInt16 nFlags, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + mirror( nDestX, nSrcWidth, pOutDev ); + mirror( nSrcX, nSrcWidth, pOutDev ); + } + copyArea( nDestX, nDestY, nSrcX, nSrcY, nSrcWidth, nSrcHeight, nFlags ); +} +void SalGraphics::CopyBits( const SalTwoRect* pPosAry, + SalGraphics* pSrcGraphics, const OutputDevice *pOutDev, const OutputDevice *pSrcOutDev ) +{ + if( ( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) || + (pSrcGraphics && ( (pSrcGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) || (pSrcOutDev && pSrcOutDev->IsRTLEnabled()) ) ) ) + { + SalTwoRect pPosAry2 = *pPosAry; + if( (pSrcGraphics && (pSrcGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL)) || (pSrcOutDev && pSrcOutDev->IsRTLEnabled()) ) + mirror( pPosAry2.mnSrcX, pPosAry2.mnSrcWidth, pSrcOutDev ); + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + mirror( pPosAry2.mnDestX, pPosAry2.mnDestWidth, pOutDev ); + copyBits( &pPosAry2, pSrcGraphics ); + } + else + copyBits( pPosAry, pSrcGraphics ); +} +void SalGraphics::DrawBitmap( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalTwoRect pPosAry2 = *pPosAry; + mirror( pPosAry2.mnDestX, pPosAry2.mnDestWidth, pOutDev ); + drawBitmap( &pPosAry2, rSalBitmap ); + } + else + drawBitmap( pPosAry, rSalBitmap ); +} +void SalGraphics::DrawBitmap( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + SalColor nTransparentColor, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalTwoRect pPosAry2 = *pPosAry; + mirror( pPosAry2.mnDestX, pPosAry2.mnDestWidth, pOutDev ); + drawBitmap( &pPosAry2, rSalBitmap, nTransparentColor ); + } + else + drawBitmap( pPosAry, rSalBitmap, nTransparentColor ); +} +void SalGraphics::DrawBitmap( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + const SalBitmap& rTransparentBitmap, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalTwoRect pPosAry2 = *pPosAry; + mirror( pPosAry2.mnDestX, pPosAry2.mnDestWidth, pOutDev ); + drawBitmap( &pPosAry2, rSalBitmap, rTransparentBitmap ); + } + else + drawBitmap( pPosAry, rSalBitmap, rTransparentBitmap ); +} +void SalGraphics::DrawMask( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + SalColor nMaskColor, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalTwoRect pPosAry2 = *pPosAry; + mirror( pPosAry2.mnDestX, pPosAry2.mnDestWidth, pOutDev ); + drawMask( &pPosAry2, rSalBitmap, nMaskColor ); + } + else + drawMask( pPosAry, rSalBitmap, nMaskColor ); +} +SalBitmap* SalGraphics::GetBitmap( long nX, long nY, long nWidth, long nHeight, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + mirror( nX, nWidth, pOutDev ); + return getBitmap( nX, nY, nWidth, nHeight ); +} +SalColor SalGraphics::GetPixel( long nX, long nY, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + mirror( nX, pOutDev ); + return getPixel( nX, nY ); +} +void SalGraphics::Invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + mirror( nX, nWidth, pOutDev ); + invert( nX, nY, nWidth, nHeight, nFlags ); +} +void SalGraphics::Invert( sal_uLong nPoints, const SalPoint* pPtAry, SalInvert nFlags, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalPoint* pPtAry2 = new SalPoint[nPoints]; + sal_Bool bCopied = mirror( nPoints, pPtAry, pPtAry2, pOutDev ); + invert( nPoints, bCopied ? pPtAry2 : pPtAry, nFlags ); + delete [] pPtAry2; + } + else + invert( nPoints, pPtAry, nFlags ); +} + +sal_Bool SalGraphics::DrawEPS( long nX, long nY, long nWidth, long nHeight, void* pPtr, sal_uLong nSize, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + mirror( nX, nWidth, pOutDev ); + return drawEPS( nX, nY, nWidth, nHeight, pPtr, nSize ); +} + +sal_Bool SalGraphics::HitTestNativeControl( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + const Point& aPos, sal_Bool& rIsInside, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + Point pt( aPos ); + Rectangle rgn( rControlRegion ); + mirror( pt.X(), pOutDev ); + mirror( rgn, pOutDev ); + return hitTestNativeControl( nType, nPart, rgn, pt, rIsInside ); + } + else + return hitTestNativeControl( nType, nPart, rControlRegion, aPos, rIsInside ); +} + +void SalGraphics::mirror( ControlType , const ImplControlValue& rVal, const OutputDevice* pOutDev, bool bBack ) const +{ + switch( rVal.getType() ) + { + case CTRL_SLIDER: + { + SliderValue* pSlVal = static_cast<SliderValue*>(const_cast<ImplControlValue*>(&rVal)); + mirror(pSlVal->maThumbRect,pOutDev,bBack); + } + break; + case CTRL_SCROLLBAR: + { + ScrollbarValue* pScVal = static_cast<ScrollbarValue*>(const_cast<ImplControlValue*>(&rVal)); + mirror(pScVal->maThumbRect,pOutDev,bBack); + mirror(pScVal->maButton1Rect,pOutDev,bBack); + mirror(pScVal->maButton2Rect,pOutDev,bBack); + } + break; + case CTRL_SPINBOX: + case CTRL_SPINBUTTONS: + { + SpinbuttonValue* pSpVal = static_cast<SpinbuttonValue*>(const_cast<ImplControlValue*>(&rVal)); + mirror(pSpVal->maUpperRect,pOutDev,bBack); + mirror(pSpVal->maLowerRect,pOutDev,bBack); + } + break; + case CTRL_TOOLBAR: + { + ToolbarValue* pTVal = static_cast<ToolbarValue*>(const_cast<ImplControlValue*>(&rVal)); + mirror(pTVal->maGripRect,pOutDev,bBack); + } + break; + } +} + +sal_Bool SalGraphics::DrawNativeControl( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + ControlState nState, const ImplControlValue& aValue, + const OUString& aCaption, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + Rectangle rgn( rControlRegion ); + mirror( rgn, pOutDev ); + mirror( nType, aValue, pOutDev ); + sal_Bool bRet = drawNativeControl( nType, nPart, rgn, nState, aValue, aCaption ); + mirror( nType, aValue, pOutDev, true ); + return bRet; + } + else + return drawNativeControl( nType, nPart, rControlRegion, nState, aValue, aCaption ); +} + +sal_Bool SalGraphics::DrawNativeControlText( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + ControlState nState, const ImplControlValue& aValue, + const OUString& aCaption, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + Rectangle rgn( rControlRegion ); + mirror( rgn, pOutDev ); + mirror( nType, aValue, pOutDev ); + sal_Bool bRet = drawNativeControlText( nType, nPart, rgn, nState, aValue, aCaption ); + mirror( nType, aValue, pOutDev, true ); + return bRet; + } + else + return drawNativeControlText( nType, nPart, rControlRegion, nState, aValue, aCaption ); +} + +sal_Bool SalGraphics::GetNativeControlRegion( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, ControlState nState, + const ImplControlValue& aValue, const OUString& aCaption, + Rectangle &rNativeBoundingRegion, Rectangle &rNativeContentRegion, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + Rectangle rgn( rControlRegion ); + mirror( rgn, pOutDev ); + mirror( nType, aValue, pOutDev ); + if( getNativeControlRegion( nType, nPart, rgn, nState, aValue, aCaption, + rNativeBoundingRegion, rNativeContentRegion ) ) + { + mirror( rNativeBoundingRegion, pOutDev, true ); + mirror( rNativeContentRegion, pOutDev, true ); + mirror( nType, aValue, pOutDev, true ); + return sal_True; + } + else + { + mirror( nType, aValue, pOutDev, true ); + return sal_False; + } + } + else + return getNativeControlRegion( nType, nPart, rControlRegion, nState, aValue, aCaption, + rNativeBoundingRegion, rNativeContentRegion ); +} + +bool SalGraphics::DrawAlphaBitmap( const SalTwoRect& rPosAry, + const SalBitmap& rSourceBitmap, + const SalBitmap& rAlphaBitmap, + const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + { + SalTwoRect pPosAry2 = rPosAry; + mirror( pPosAry2.mnDestX, pPosAry2.mnDestWidth, pOutDev ); + return drawAlphaBitmap( pPosAry2, rSourceBitmap, rAlphaBitmap ); + } + else + return drawAlphaBitmap( rPosAry, rSourceBitmap, rAlphaBitmap ); +} + +bool SalGraphics::DrawAlphaRect( long nX, long nY, long nWidth, long nHeight, + sal_uInt8 nTransparency, const OutputDevice *pOutDev ) +{ + if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (pOutDev && pOutDev->IsRTLEnabled()) ) + mirror( nX, nWidth, pOutDev ); + + return drawAlphaRect( nX, nY, nWidth, nHeight, nTransparency ); +} + +bool SalGraphics::filterText( const String&, String&, xub_StrLen, xub_StrLen&, xub_StrLen&, xub_StrLen& ) +{ + return false; +} + +void SalGraphics::AddDevFontSubstitute( OutputDevice* pOutDev, + const String& rFontName, + const String& rReplaceFontName, + sal_uInt16 nFlags ) +{ + pOutDev->ImplAddDevFontSubstitute( rFontName, rReplaceFontName, nFlags ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx new file mode 100644 index 000000000000..970b0ff3cd0b --- /dev/null +++ b/vcl/source/gdi/sallayout.cxx @@ -0,0 +1,2257 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <cstdio> + +#define _USE_MATH_DEFINES +#include <math.h> +#include <sal/alloca.h> + +#include <svsys.h> +#include <vcl/salgdi.hxx> +#include <vcl/sallayout.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <i18npool/lang.h> + +#include <tools/debug.hxx> + +#include <limits.h> + +#if defined _MSC_VER +#pragma warning(push, 1) +#endif +#include <unicode/ubidi.h> +#include <unicode/uchar.h> +#if defined _MSC_VER +#pragma warning(pop) +#endif + +#include <algorithm> + +#ifdef DEBUG +//#define MULTI_SL_DEBUG +#endif + +#ifdef MULTI_SL_DEBUG +#include <string> +FILE * mslLogFile = NULL; +FILE * mslLog() +{ +#ifdef MSC + std::string logFileName(getenv("TEMP")); + logFileName.append("\\msllayout.log"); + if (mslLogFile == NULL) mslLogFile = fopen(logFileName.c_str(),"w"); + else fflush(mslLogFile); + return mslLogFile; +#else + return stdout; +#endif +} +#endif +// ======================================================================= + +// TODO: ask the glyph directly, for now we need this method because of #i99367# +// true if a codepoint doesn't influence the logical text width +bool IsDiacritic( sal_UCS4 nChar ) +{ + // shortcut abvious non-diacritics + if( nChar < 0x0300 ) + return false; + if( nChar >= 0x2100 ) + return false; + + // TODO: #i105058# use icu uchar.h's character classification instead of the handcrafted table + struct DiaRange { sal_UCS4 mnMin, mnEnd;}; + static const DiaRange aRanges[] = { + {0x0300, 0x0370}, + {0x0590, 0x05BE}, {0x05BF, 0x05C0}, {0x05C1, 0x05C3}, {0x05C4, 0x05C6}, {0x05C7, 0x05C8}, + {0x0610, 0x061B}, {0x064B, 0x0660}, {0x0670, 0x0671}, {0x06D6, 0x06DD}, {0x06DF, 0x06E5}, {0x06E7, 0x06E9}, {0x06EA,0x06EF}, + {0x0730, 0x074D}, {0x07A6, 0x07B1}, {0x07EB, 0x07F4}, + {0x1DC0, 0x1E00}, + {0x205F, 0x2070}, {0x20D0, 0x2100}, + {0xFB1E, 0xFB1F} + }; + + // TODO: almost anything is faster than an O(n) search + static const int nCount = sizeof(aRanges) / sizeof(*aRanges); + const DiaRange* pRange = &aRanges[0]; + for( int i = nCount; --i >= 0; ++pRange ) + if( (pRange->mnMin <= nChar) && (nChar < pRange->mnEnd) ) + return true; + + return false; +} + +// ======================================================================= + +int GetVerticalFlags( sal_UCS4 nChar ) +{ + if( (nChar >= 0x1100 && nChar <= 0x11f9) // Hangul Jamo + || (nChar == 0x2030 || nChar == 0x2031) // per mille sign + || (nChar >= 0x3000 && nChar <= 0xfaff) // unified CJK + || (nChar >= 0xfe20 && nChar <= 0xfe6f) // CJK compatibility + || (nChar >= 0xff00 && nChar <= 0xfffd) ) // other CJK + { + /* #i52932# remember: + nChar == 0x2010 || nChar == 0x2015 + nChar == 0x2016 || nChar == 0x2026 + are GF_NONE also, but already handled in the outer if condition + */ + if((nChar >= 0x3008 && nChar <= 0x301C && nChar != 0x3012) + || (nChar == 0xFF3B || nChar == 0xFF3D) + || (nChar >= 0xFF5B && nChar <= 0xFF9F) // halfwidth forms + || (nChar == 0xFFE3) ) + return GF_NONE; // not rotated + else if( nChar == 0x30fc ) + return GF_ROTR; // right + return GF_ROTL; // left + } + else if( (nChar >= 0x20000) && (nChar <= 0x3FFFF) ) // all SIP/TIP ideographs + return GF_ROTL; // left + + return GF_NONE; // not rotated as default +} + +// ----------------------------------------------------------------------- + +sal_UCS4 GetVerticalChar( sal_UCS4 ) +{ + return 0; // #i14788# input method is responsible vertical char changes +} + +// ----------------------------------------------------------------------- + +VCL_DLLPUBLIC sal_UCS4 GetMirroredChar( sal_UCS4 nChar ) +{ + nChar = u_charMirror( nChar ); + return nChar; +} + +// ----------------------------------------------------------------------- + +// Get simple approximations for unicodes +const char* GetAutofallback( sal_UCS4 nChar ) +{ + const char* pStr = NULL; + switch( nChar ) + { + case 0x01C0: + case 0x2223: + case 0x2758: + pStr = "|"; break; + case 0x02DC: + pStr = "~"; break; + case 0x037E: + pStr = ";"; break; + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + case 0x2008: + case 0x2009: + case 0x200A: + case 0x202F: + pStr = " "; break; + case 0x2010: + case 0x2011: + case 0x2012: + case 0x2013: + case 0x2014: + pStr = "-"; break; + case 0x2015: + pStr = "--"; break; + case 0x2016: + pStr = "||"; break; + case 0x2017: + pStr = "_"; break; + case 0x2018: + case 0x2019: + case 0x201B: + pStr = "\'"; break; + case 0x201A: + pStr = ","; break; + case 0x201C: + case 0x201D: + case 0x201E: + case 0x201F: + case 0x2033: + pStr = "\""; break; + case 0x2039: + pStr = "<"; break; + case 0x203A: + pStr = ">"; break; + case 0x203C: + pStr = "!!"; break; + case 0x203D: + pStr = "?"; break; + case 0x2044: + case 0x2215: + pStr = "/"; break; + case 0x2048: + pStr = "?!"; break; + case 0x2049: + pStr = "!?"; break; + case 0x2216: + pStr = "\\"; break; + case 0x2217: + pStr = "*"; break; + case 0x2236: + pStr = ":"; break; + case 0x2264: + pStr = "<="; break; + case 0x2265: + pStr = "<="; break; + case 0x2303: + pStr = "^"; break; + } + + return pStr; +} + +// ----------------------------------------------------------------------- + +sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang ) +{ + // currently only conversion from ASCII digits is interesting + if( (nChar < '0') || ('9' < nChar) ) + return nChar; + + int nOffset; + // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region. + // CAVEAT! To some like Mongolian MS assigned the same primary language + // although the script type is different! + switch( eLang & LANGUAGE_MASK_PRIMARY ) + { + default: + nOffset = 0; + break; + case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0660 - '0'; // arabic-indic digits + break; + case LANGUAGE_FARSI & LANGUAGE_MASK_PRIMARY: + case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY: + case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //??? + case LANGUAGE_SINDHI & LANGUAGE_MASK_PRIMARY: + nOffset = 0x06F0 - '0'; // eastern arabic-indic digits + break; + case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY: + nOffset = 0x09E6 - '0'; // bengali + break; + case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0966 - '0'; // devanagari + break; + case LANGUAGE_AMHARIC_ETHIOPIA & LANGUAGE_MASK_PRIMARY: + case LANGUAGE_TIGRIGNA_ETHIOPIA & LANGUAGE_MASK_PRIMARY: + // TODO case: + nOffset = 0x1369 - '0'; // ethiopic + break; + case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0AE6 - '0'; // gujarati + break; +#ifdef LANGUAGE_GURMUKHI // TODO case: + case LANGUAGE_GURMUKHI & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0A66 - '0'; // gurmukhi + break; +#endif + case LANGUAGE_KANNADA & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0CE6 - '0'; // kannada + break; + case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY: + nOffset = 0x17E0 - '0'; // khmer + break; + case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0ED0 - '0'; // lao + break; + case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0D66 - '0'; // malayalam + break; + case LANGUAGE_MONGOLIAN & LANGUAGE_MASK_PRIMARY: + if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN) + nOffset = 0x1810 - '0'; // mongolian + else + nOffset = 0; // mongolian cyrillic + break; + case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY: + nOffset = 0x1040 - '0'; // myanmar + break; + case LANGUAGE_ORIYA & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0B66 - '0'; // oriya + break; + case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0BE7 - '0'; // tamil + break; + case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0C66 - '0'; // telugu + break; + case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0E50 - '0'; // thai + break; + case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY: + nOffset = 0x0F20 - '0'; // tibetan + break; + } + + nChar += nOffset; + return nChar; +} + +// ----------------------------------------------------------------------- + +inline bool IsControlChar( sal_UCS4 cChar ) +{ + // C0 control characters + if( (0x0001 <= cChar) && (cChar <= 0x001F) ) + return true; + // formatting characters + if( (0x200E <= cChar) && (cChar <= 0x200F) ) + return true; + if( (0x2028 <= cChar) && (cChar <= 0x202E) ) + return true; + // deprecated formatting characters + if( (0x206A <= cChar) && (cChar <= 0x206F) ) + return true; + if( (0x2060 == cChar) ) + return true; + // byte order markers and invalid unicode + if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) ) + return true; + return false; +} + +// ======================================================================= + +bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL ) +{ + // check if charpos could extend current run + int nIndex = maRuns.size(); + if( nIndex >= 2 ) + { + int nRunPos0 = maRuns[ nIndex-2 ]; + int nRunPos1 = maRuns[ nIndex-1 ]; + if( ((nCharPos + bRTL) == nRunPos1) + && ((nRunPos0 > nRunPos1) == bRTL) ) + { + // extend current run by new charpos + maRuns[ nIndex-1 ] = nCharPos + !bRTL; + return false; + } + // ignore new charpos when it is in current run + if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) ) + return false; + if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) ) + return false; + } + + // else append a new run consisting of the new charpos + maRuns.push_back( nCharPos + (bRTL ? 1 : 0) ); + maRuns.push_back( nCharPos + (bRTL ? 0 : 1) ); + return true; +} + +// ----------------------------------------------------------------------- + +bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL ) +{ + if( nCharPos0 == nCharPos1 ) + return false; + + // swap if needed + if( bRTL == (nCharPos0 < nCharPos1) ) + { + int nTemp = nCharPos0; + nCharPos0 = nCharPos1; + nCharPos1 = nTemp; + } + + // append new run + maRuns.push_back( nCharPos0 ); + maRuns.push_back( nCharPos1 ); + return true; +} + +// ----------------------------------------------------------------------- + +bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const +{ + if( mnRunIndex >= (int)maRuns.size() ) + return false; + + int nMinCharPos = maRuns[ mnRunIndex+0 ]; + int nEndCharPos = maRuns[ mnRunIndex+1 ]; + if( nMinCharPos > nEndCharPos ) // reversed in RTL case + { + int nTemp = nMinCharPos; + nMinCharPos = nEndCharPos; + nEndCharPos = nTemp; + } + + if( nCharPos < nMinCharPos ) + return false; + if( nCharPos >= nEndCharPos ) + return false; + return true; +} + +bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const +{ + bool bRet = false; + int nRunIndex = mnRunIndex; + + ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this); + + pThis->ResetPos(); + + for (size_t i = 0; i < maRuns.size(); i+=2) + { + if( (bRet = PosIsInRun( nCharPos )) == true ) + break; + pThis->NextRun(); + } + + pThis->mnRunIndex = nRunIndex; + return bRet; +} + + +// ----------------------------------------------------------------------- + +bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft ) +{ + // negative nCharPos => reset to first run + if( *nCharPos < 0 ) + mnRunIndex = 0; + + // return false when all runs completed + if( mnRunIndex >= (int)maRuns.size() ) + return false; + + int nRunPos0 = maRuns[ mnRunIndex+0 ]; + int nRunPos1 = maRuns[ mnRunIndex+1 ]; + *bRightToLeft = (nRunPos0 > nRunPos1); + + if( *nCharPos < 0 ) + { + // get first valid nCharPos in run + *nCharPos = nRunPos0; + } + else + { + // advance to next nCharPos for LTR case + if( !*bRightToLeft ) + ++(*nCharPos); + + // advance to next run if current run is completed + if( *nCharPos == nRunPos1 ) + { + if( (mnRunIndex += 2) >= (int)maRuns.size() ) + return false; + nRunPos0 = maRuns[ mnRunIndex+0 ]; + nRunPos1 = maRuns[ mnRunIndex+1 ]; + *bRightToLeft = (nRunPos0 > nRunPos1); + *nCharPos = nRunPos0; + } + } + + // advance to next nCharPos for RTL case + if( *bRightToLeft ) + --(*nCharPos); + + return true; +} + +// ----------------------------------------------------------------------- + +bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const +{ + if( mnRunIndex >= (int)maRuns.size() ) + return false; + + int nRunPos0 = maRuns[ mnRunIndex+0 ]; + int nRunPos1 = maRuns[ mnRunIndex+1 ]; + *bRightToLeft = (nRunPos1 < nRunPos0) ; + if( !*bRightToLeft ) + { + *nMinRunPos = nRunPos0; + *nEndRunPos = nRunPos1; + } + else + { + *nMinRunPos = nRunPos1; + *nEndRunPos = nRunPos0; + } + return true; +} + +// ======================================================================= + +ImplLayoutArgs::ImplLayoutArgs( const xub_Unicode* pStr, int nLen, + int nMinCharPos, int nEndCharPos, int nFlags ) +: + mnFlags( nFlags ), + mnLength( nLen ), + mnMinCharPos( nMinCharPos ), + mnEndCharPos( nEndCharPos ), + mpStr( pStr ), + mpDXArray( NULL ), + mnLayoutWidth( 0 ), + mnOrientation( 0 ) +{ + if( mnFlags & SAL_LAYOUT_BIDI_STRONG ) + { + // handle strong BiDi mode + + // do not bother to BiDi analyze strong LTR/RTL + // TODO: can we assume these strings do not have unicode control chars? + // if not remove the control characters from the runs + bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0); + AddRun( mnMinCharPos, mnEndCharPos, bRTL ); + } + else + { + // handle weak BiDi mode + + UBiDiLevel nLevel = UBIDI_DEFAULT_LTR; + if( mnFlags & SAL_LAYOUT_BIDI_RTL ) + nLevel = UBIDI_DEFAULT_RTL; + + // prepare substring for BiDi analysis + // TODO: reuse allocated pParaBidi + UErrorCode rcI18n = U_ZERO_ERROR; + UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n ); + if( !pParaBidi ) + return; + ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n ); // UChar != sal_Unicode in MinGW + + UBiDi* pLineBidi = pParaBidi; + int nSubLength = mnEndCharPos - mnMinCharPos; + if( nSubLength != mnLength ) + { + pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n ); + ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n ); + } + + // run BiDi algorithm + const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n ); + //maRuns.resize( 2 * nRunCount ); + for( int i = 0; i < nRunCount; ++i ) + { + int32_t nMinPos, nLength; + const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength ); + const int nPos0 = nMinPos + mnMinCharPos; + const int nPos1 = nPos0 + nLength; + + const bool bRTL = (nDir == UBIDI_RTL); + AddRun( nPos0, nPos1, bRTL ); + } + + // cleanup BiDi engine + if( pLineBidi != pParaBidi ) + ubidi_close( pLineBidi ); + ubidi_close( pParaBidi ); + } + + // prepare calls to GetNextPos/GetNextRun + maRuns.ResetPos(); +} + +// ----------------------------------------------------------------------- + +// add a run after splitting it up to get rid of control chars +void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL ) +{ + DBG_ASSERT( nCharPos0 <= nCharPos1, "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" ); + + // remove control characters from runs by splitting them up + if( !bRTL ) + { + for( int i = nCharPos0; i < nCharPos1; ++i ) + if( IsControlChar( mpStr[i] ) ) + { + // add run until control char + maRuns.AddRun( nCharPos0, i, bRTL ); + nCharPos0 = i + 1; + } + } + else + { + for( int i = nCharPos1; --i >= nCharPos0; ) + if( IsControlChar( mpStr[i] ) ) + { + // add run until control char + maRuns.AddRun( i+1, nCharPos1, bRTL ); + nCharPos1 = i; + } + } + + // add remainder of run + maRuns.AddRun( nCharPos0, nCharPos1, bRTL ); +} + +// ----------------------------------------------------------------------- + +bool ImplLayoutArgs::PrepareFallback() +{ + // short circuit if no fallback is needed + if( maReruns.IsEmpty() ) + { + maRuns.Clear(); + return false; + } + + // convert the fallback requests to layout requests + bool bRTL; + int nMin, nEnd; + + // get the individual fallback requests + typedef std::vector<int> IntVector; + IntVector aPosVector; + aPosVector.reserve( mnLength ); + maReruns.ResetPos(); + for(; maReruns.GetRun( &nMin, &nEnd, &bRTL ); maReruns.NextRun() ) + for( int i = nMin; i < nEnd; ++i ) + aPosVector.push_back( i ); + maReruns.Clear(); + + // sort the individual fallback requests + std::sort( aPosVector.begin(), aPosVector.end() ); + + // adjust fallback runs to have the same order and limits of the original runs + ImplLayoutRuns aNewRuns; + maRuns.ResetPos(); + for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() ) + { + if( !bRTL) { + IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin ); + for(; (it != aPosVector.end()) && (*it < nEnd); ++it ) + aNewRuns.AddPos( *it, bRTL ); + } else { + IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd ); + while( (it != aPosVector.begin()) && (*--it >= nMin) ) + aNewRuns.AddPos( *it, bRTL ); + } + } + + maRuns = aNewRuns; // TODO: use vector<>::swap() + maRuns.ResetPos(); + return true; +} + +// ----------------------------------------------------------------------- + +bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL ) +{ + bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL ); + maRuns.NextRun(); + return bValid; +} + +// ======================================================================= + +SalLayout::SalLayout() +: mnMinCharPos( -1 ), + mnEndCharPos( -1 ), + mnLayoutFlags( 0 ), + mnUnitsPerPixel( 1 ), + mnOrientation( 0 ), + mnRefCount( 1 ), + maDrawOffset( 0, 0 ) +{} + +// ----------------------------------------------------------------------- + +SalLayout::~SalLayout() +{} + +// ----------------------------------------------------------------------- + +void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) +{ + mnMinCharPos = rArgs.mnMinCharPos; + mnEndCharPos = rArgs.mnEndCharPos; + mnLayoutFlags = rArgs.mnFlags; + mnOrientation = rArgs.mnOrientation; +} + +// ----------------------------------------------------------------------- + +void SalLayout::Reference() const +{ + // TODO: protect when multiple threads can access this + ++mnRefCount; +} + +// ----------------------------------------------------------------------- + +void SalLayout::Release() const +{ + // TODO: protect when multiple threads can access this + if( --mnRefCount > 0 ) + return; + // const_cast because some compilers violate ANSI C++ spec + delete const_cast<SalLayout*>(this); +} + +// ----------------------------------------------------------------------- + +Point SalLayout::GetDrawPosition( const Point& rRelative ) const +{ + Point aPos = maDrawBase; + Point aOfs = rRelative + maDrawOffset; + + if( mnOrientation == 0 ) + aPos += aOfs; + else + { + // cache trigonometric results + static int nOldOrientation = 0; + static double fCos = 1.0, fSin = 0.0; + if( nOldOrientation != mnOrientation ) + { + nOldOrientation = mnOrientation; + double fRad = mnOrientation * (M_PI / 1800.0); + fCos = cos( fRad ); + fSin = sin( fRad ); + } + + double fX = aOfs.X(); + double fY = aOfs.Y(); + long nX = static_cast<long>( +fCos * fX + fSin * fY ); + long nY = static_cast<long>( +fCos * fY - fSin * fX ); + aPos += Point( nX, nY ); + } + + return aPos; +} + +// ----------------------------------------------------------------------- + +// returns asian kerning values in quarter of character width units +// to enable automatic halfwidth substitution for fullwidth punctuation +// return value is negative for l, positive for r, zero for neutral + +// If the range doesn't match in 0x3000 and 0x30FB, please change +// also ImplCalcKerning. + +int SalLayout::CalcAsianKerning( sal_UCS4 c, bool bLeft, bool /*TODO:? bVertical*/ ) +{ + // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html + static signed char nTable[0x30] = + { + 0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2, + +2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2 + }; + + int nResult = 0; + if( (c >= 0x3000) && (c < 0x3030) ) + nResult = nTable[ c - 0x3000 ]; + else switch( c ) + { + case 0x30FB: + nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom + break; + case 0x2019: case 0x201D: + case 0xFF01: case 0xFF09: case 0xFF0C: + case 0xFF1A: case 0xFF1B: + nResult = -2; + break; + case 0x2018: case 0x201C: + case 0xFF08: + nResult = +2; + break; + default: + break; + } + + return nResult; +} + +// ----------------------------------------------------------------------- + +bool SalLayout::GetOutline( SalGraphics& rSalGraphics, + ::basegfx::B2DPolyPolygonVector& rVector ) const +{ + bool bAllOk = true; + bool bOneOk = false; + + Point aPos; + ::basegfx::B2DPolyPolygon aGlyphOutline; + for( int nStart = 0;;) + { + sal_GlyphId nLGlyph; + if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) ) + break; + + // get outline of individual glyph, ignoring "empty" glyphs + bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline ); + bAllOk &= bSuccess; + bOneOk |= bSuccess; + // only add non-empty outlines + if( bSuccess && (aGlyphOutline.count() > 0) ) + { + if( aPos.X() || aPos.Y() ) + { + aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y())); + } + + // insert outline at correct position + rVector.push_back( aGlyphOutline ); + } + } + + return (bAllOk & bOneOk); +} + +// ----------------------------------------------------------------------- + +bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const +{ + bool bRet = false; + rRect.SetEmpty(); + + Point aPos; + Rectangle aRectangle; + for( int nStart = 0;;) + { + sal_GlyphId nLGlyph; + if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) ) + break; + + // get bounding rectangle of individual glyph + if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) ) + { + // merge rectangle + aRectangle += aPos; + rRect.Union( aRectangle ); + bRet = true; + } + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph ) const +{ + bool bRet = false; + if( nGlyph & GF_ISCHAR ) + { + long nChar = nGlyph & GF_IDXMASK; + bRet = (nChar <= 0x0020) // blank + //|| (nChar == 0x00A0) // non breaking space + || (nChar >= 0x2000 && nChar <= 0x200F) // whitespace + || (nChar == 0x3000); // ideographic space + } + else + bRet = ((nGlyph & GF_IDXMASK) == 3); + return bRet; +} + +// ----------------------------------------------------------------------- + +const ImplFontData* SalLayout::GetFallbackFontData( sal_GlyphId /*nGlyphId*/ ) const +{ + return NULL; +} + +// ======================================================================= + +GenericSalLayout::GenericSalLayout() +: mpGlyphItems(0), + mnGlyphCount(0), + mnGlyphCapacity(0) +{} + +// ----------------------------------------------------------------------- + +GenericSalLayout::~GenericSalLayout() +{ + delete[] mpGlyphItems; +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem ) +{ + // TODO: use std::list<GlyphItem> + if( mnGlyphCount >= mnGlyphCapacity ) + { + mnGlyphCapacity += 16 + 3 * mnGlyphCount; + GlyphItem* pNewGI = new GlyphItem[ mnGlyphCapacity ]; + if( mpGlyphItems ) + { + for( int i = 0; i < mnGlyphCount; ++i ) + pNewGI[ i ] = mpGlyphItems[ i ]; + delete[] mpGlyphItems; + } + mpGlyphItems = pNewGI; + } + + mpGlyphItems[ mnGlyphCount++ ] = rGlyphItem; +} + +// ----------------------------------------------------------------------- + +bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const +{ + // initialize character extents buffer + int nCharCount = mnEndCharPos - mnMinCharPos; + for( int n = 0; n < nCharCount; ++n ) + pCharWidths[n] = 0; + + // determine cluster extents + const GlyphItem* const pEnd = mpGlyphItems + mnGlyphCount; + for( const GlyphItem* pG = mpGlyphItems; pG < pEnd; ++pG ) + { + // use cluster start to get char index + if( !pG->IsClusterStart() ) + continue; + + int n = pG->mnCharPos; + if( n >= mnEndCharPos ) + continue; + n -= mnMinCharPos; + if( n < 0 ) + continue; + + // left glyph in cluster defines default extent + long nXPosMin = pG->maLinearPos.X(); + long nXPosMax = nXPosMin + pG->mnNewWidth; + + // calculate right x-position for this glyph cluster + // break if no more glyphs in layout + // break at next glyph cluster start + while( (pG+1 < pEnd) && !pG[1].IsClusterStart() ) + { + // advance to next glyph in cluster + ++pG; + + if( pG->IsDiacritic() ) + continue; // ignore diacritics + // get leftmost x-extent of this glyph + long nXPos = pG->maLinearPos.X(); + if( nXPosMin > nXPos ) + nXPosMin = nXPos; + + // get rightmost x-extent of this glyph + nXPos += pG->mnNewWidth; + if( nXPosMax < nXPos ) + nXPosMax = nXPos; + } + + // when the current cluster overlaps with the next one assume + // rightmost cluster edge is the leftmost edge of next cluster + // for clusters that do not have x-sorted glyphs + // TODO: avoid recalculation of left bound in next cluster iteration + for( const GlyphItem* pN = pG; ++pN < pEnd; ) + { + if( pN->IsClusterStart() ) + break; + if( pN->IsDiacritic() ) + continue; // ignore diacritics + if( nXPosMax > pN->maLinearPos.X() ) + nXPosMax = pN->maLinearPos.X(); + } + if( nXPosMax < nXPosMin ) + nXPosMin = nXPosMax = 0; + + // character width is sum of glyph cluster widths + pCharWidths[n] += nXPosMax - nXPosMin; + } + + // TODO: distribute the cluster width proportionally to the characters + // clusters (e.g. ligatures) correspond to more than one char index, + // so some character widths are still uninitialized. This is solved + // by setting the first charwidth of the cluster to the cluster width + + return true; +} + +// ----------------------------------------------------------------------- + +long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const +{ + if( pCharWidths ) + if( !GetCharWidths( pCharWidths ) ) + return 0; + + long nWidth = GetTextWidth(); + return nWidth; +} + +// ----------------------------------------------------------------------- + +// the text width is the maximum logical extent of all glyphs +long GenericSalLayout::GetTextWidth() const +{ + if( mnGlyphCount <= 0 ) + return 0; + + // initialize the extent + long nMinPos = 0; + long nMaxPos = 0; + + const GlyphItem* pG = mpGlyphItems; + for( int i = mnGlyphCount; --i >= 0; ++pG ) + { + // update the text extent with the glyph extent + long nXPos = pG->maLinearPos.X(); + if( nMinPos > nXPos ) + nMinPos = nXPos; + nXPos += pG->mnNewWidth; + if( nMaxPos < nXPos ) + nMaxPos = nXPos; + } + + long nWidth = nMaxPos - nMinPos; + return nWidth; +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) +{ + SalLayout::AdjustLayout( rArgs ); + + if( rArgs.mpDXArray ) + ApplyDXArray( rArgs ); + else if( rArgs.mnLayoutWidth ) + Justify( rArgs.mnLayoutWidth ); +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs ) +{ + if( mnGlyphCount <= 0 ) + return; + + // determine cluster boundaries and x base offset + const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos; + int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) ); + int i, n; + long nBasePointX = -1; + if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK ) + nBasePointX = 0; + for( i = 0; i < nCharCount; ++i ) + pLogCluster[ i ] = -1; + GlyphItem* pG = mpGlyphItems; + for( i = 0; i < mnGlyphCount; ++i, ++pG ) + { + n = pG->mnCharPos - rArgs.mnMinCharPos; + if( (n < 0) || (nCharCount <= n) ) + continue; + if( pLogCluster[ n ] < 0 ) + pLogCluster[ n ] = i; + if( nBasePointX < 0 ) + nBasePointX = pG->maLinearPos.X(); + } + // retarget unresolved pLogCluster[n] to a glyph inside the cluster + // TODO: better do it while the deleted-glyph markers are still there + for( n = 0; n < nCharCount; ++n ) + if( (i = pLogCluster[0]) >= 0 ) + break; + if( n >= nCharCount ) + return; + for( n = 0; n < nCharCount; ++n ) + { + if( pLogCluster[ n ] < 0 ) + pLogCluster[ n ] = i; + else + i = pLogCluster[ n ]; + } + + // calculate adjusted cluster widths + sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( mnGlyphCount * sizeof(long) ); + for( i = 0; i < mnGlyphCount; ++i ) + pNewGlyphWidths[ i ] = 0; + + bool bRTL; + for( int nCharPos = i = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); ) + { + n = nCharPos - rArgs.mnMinCharPos; + if( (n < 0) || (nCharCount <= n) ) continue; + + if( pLogCluster[ n ] >= 0 ) + i = pLogCluster[ n ]; + if( i >= 0 ) + { + long nDelta = rArgs.mpDXArray[ n ] ; + if( n > 0 ) + nDelta -= rArgs.mpDXArray[ n-1 ]; + pNewGlyphWidths[ i ] += nDelta * mnUnitsPerPixel; + } + } + + // move cluster positions using the adjusted widths + long nDelta = 0; + long nNewPos = 0; + pG = mpGlyphItems; + for( i = 0; i < mnGlyphCount; ++i, ++pG ) + { + if( pG->IsClusterStart() ) + { + // calculate original and adjusted cluster width + int nOldClusterWidth = pG->mnNewWidth; + int nNewClusterWidth = pNewGlyphWidths[i]; + GlyphItem* pClusterG = pG + 1; + for( int j = i; ++j < mnGlyphCount; ++pClusterG ) + { + if( pClusterG->IsClusterStart() ) + break; + if( !pClusterG->IsDiacritic() ) // #i99367# ignore diacritics + nOldClusterWidth += pClusterG->mnNewWidth; + nNewClusterWidth += pNewGlyphWidths[j]; + } + const int nDiff = nNewClusterWidth - nOldClusterWidth; + + // adjust cluster glyph widths and positions + nDelta = nBasePointX + (nNewPos - pG->maLinearPos.X()); + if( !pG->IsRTLGlyph() ) + { + // for LTR case extend rightmost glyph in cluster + pClusterG[-1].mnNewWidth += nDiff; + } + else + { + // right align cluster in new space for RTL case + pG->mnNewWidth += nDiff; + nDelta += nDiff; + } + + nNewPos += nNewClusterWidth; + } + + pG->maLinearPos.X() += nDelta; + } +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::Justify( long nNewWidth ) +{ + nNewWidth *= mnUnitsPerPixel; + int nOldWidth = GetTextWidth(); + if( !nOldWidth || nNewWidth==nOldWidth ) + return; + + // find rightmost glyph, it won't get stretched + GlyphItem* pGRight = mpGlyphItems + mnGlyphCount - 1; + + // count stretchable glyphs + GlyphItem* pG; + int nStretchable = 0; + int nMaxGlyphWidth = 0; + for( pG = mpGlyphItems; pG < pGRight; ++pG ) + { + if( !pG->IsDiacritic() ) + ++nStretchable; + if( nMaxGlyphWidth < pG->mnOrigWidth ) + nMaxGlyphWidth = pG->mnOrigWidth; + } + + // move rightmost glyph to requested position + nOldWidth -= pGRight->mnOrigWidth; + if( nOldWidth <= 0 ) + return; + if( nNewWidth < nMaxGlyphWidth) + nNewWidth = nMaxGlyphWidth; + nNewWidth -= pGRight->mnOrigWidth; + pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth; + + // justify glyph widths and positions + int nDiffWidth = nNewWidth - nOldWidth; + if( nDiffWidth >= 0) // expanded case + { + // expand width by distributing space between glyphs evenly + int nDeltaSum = 0; + for( pG = mpGlyphItems; pG < pGRight; ++pG ) + { + // move glyph to justified position + pG->maLinearPos.X() += nDeltaSum; + + // do not stretch non-stretchable glyphs + if( pG->IsDiacritic() || (nStretchable <= 0) ) + continue; + + // distribute extra space equally to stretchable glyphs + int nDeltaWidth = nDiffWidth / nStretchable--; + nDiffWidth -= nDeltaWidth; + pG->mnNewWidth += nDeltaWidth; + nDeltaSum += nDeltaWidth; + } + } + else // condensed case + { + // squeeze width by moving glyphs proportionally + double fSqueeze = (double)nNewWidth / nOldWidth; + for( pG = mpGlyphItems; ++pG < pGRight;) + { + int nX = pG->maLinearPos.X() - maBasePoint.X(); + nX = (int)(nX * fSqueeze); + pG->maLinearPos.X() = nX + maBasePoint.X(); + } + // adjust glyph widths to new positions + for( pG = mpGlyphItems; pG < pGRight; ++pG ) + pG->mnNewWidth = pG[1].maLinearPos.X() - pG[0].maLinearPos.X(); + } +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::ApplyAsianKerning( const sal_Unicode* pStr, int nLength ) +{ + long nOffset = 0; + + GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount; + for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG ) + { + const int n = pG->mnCharPos; + if( n < nLength - 1) + { + // ignore code ranges that are not affected by asian punctuation compression + const sal_Unicode cHere = pStr[n]; + if( ((0x3000 != (cHere & 0xFF00)) && (0x2010 != (cHere & 0xFFF0))) || (0xFF00 != (cHere & 0xFF00)) ) + continue; + const sal_Unicode cNext = pStr[n+1]; + if( ((0x3000 != (cNext & 0xFF00)) && (0x2010 != (cNext & 0xFFF0))) || (0xFF00 != (cNext & 0xFF00)) ) + continue; + + // calculate compression values + const bool bVertical = false; + long nKernFirst = +CalcAsianKerning( cHere, true, bVertical ); + long nKernNext = -CalcAsianKerning( cNext, false, bVertical ); + + // apply punctuation compression to logical glyph widths + long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext; + if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 ) + { + int nGlyphWidth = pG->mnOrigWidth; + nDelta = (nDelta * nGlyphWidth + 2) / 4; + if( pG+1 == pGEnd ) + pG->mnNewWidth += nDelta; + nOffset += nDelta; + } + } + + // adjust the glyph positions to the new glyph widths + if( pG+1 != pGEnd ) + pG->maLinearPos.X() += nOffset; + } +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth ) +{ + // TODO: reimplement method when container type for GlyphItems changes + + // skip if the kashida glyph in the font looks suspicious + if( nKashidaWidth <= 0 ) + return; + + // calculate max number of needed kashidas + const GlyphItem* pG1 = mpGlyphItems; + int nKashidaCount = 0, i; + for( i = 0; i < mnGlyphCount; ++i, ++pG1 ) + { + // only inject kashidas in RTL contexts + if( !pG1->IsRTLGlyph() ) + continue; + // no kashida-injection for blank justified expansion either + if( IsSpacingGlyph( pG1->mnGlyphIndex ) ) + continue; + + // calculate gap, ignore if too small + const int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth; + // worst case is one kashida even for mini-gaps + if( 3 * nGapWidth >= nKashidaWidth ) + nKashidaCount += 1 + (nGapWidth / nKashidaWidth); + } + + if( !nKashidaCount ) + return; + + // reallocate glyph array for additional kashidas + // TODO: reuse array if additional glyphs would fit + mnGlyphCapacity = mnGlyphCount + nKashidaCount; + GlyphItem* pNewGlyphItems = new GlyphItem[ mnGlyphCapacity ]; + GlyphItem* pG2 = pNewGlyphItems; + pG1 = mpGlyphItems; + for( i = mnGlyphCount; --i >= 0; ++pG1, ++pG2 ) + { + // default action is to copy array element + *pG2 = *pG1; + + // only inject kashida in RTL contexts + if( !pG1->IsRTLGlyph() ) + continue; + // no kashida-injection for blank justified expansion either + if( IsSpacingGlyph( pG1->mnGlyphIndex ) ) + continue; + + // calculate gap, skip if too small + int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth; + if( 3*nGapWidth < nKashidaWidth ) + continue; + + // fill gap with kashidas + nKashidaCount = 0; + Point aPos = pG1->maLinearPos; + aPos.X() -= nGapWidth; // cluster is already right aligned + for(; nGapWidth > 0; nGapWidth -= nKashidaWidth, ++nKashidaCount ) + { + *(pG2++) = GlyphItem( pG1->mnCharPos, nKashidaIndex, aPos, + GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth ); + aPos.X() += nKashidaWidth; + } + + // fixup rightmost kashida for gap remainder + if( nGapWidth < 0 ) + { + aPos.X() += nGapWidth; + if( nKashidaCount <= 1 ) + nGapWidth /= 2; // for small gap move kashida to middle + pG2[-1].mnNewWidth += nGapWidth; // adjust kashida width to gap width + pG2[-1].maLinearPos.X() += nGapWidth; + } + + // when kashidas were inserted move the original cluster + // to the right and shrink it to it's original width + *pG2 = *pG1; + pG2->maLinearPos.X() = aPos.X(); + pG2->mnNewWidth = pG2->mnOrigWidth; + } + + // use the new glyph array + DBG_ASSERT( mnGlyphCapacity >= pG2-pNewGlyphItems, "KashidaJustify overflow" ); + delete[] mpGlyphItems; + mpGlyphItems = pNewGlyphItems; + mnGlyphCount = pG2 - pNewGlyphItems; +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const +{ + // initialize result array + long nXPos = -1; + int i; + for( i = 0; i < nMaxIndex; ++i ) + pCaretXArray[ i ] = nXPos; + + // calculate caret positions using glyph array + const GlyphItem* pG = mpGlyphItems; + for( i = mnGlyphCount; --i >= 0; ++pG ) + { + nXPos = pG->maLinearPos.X(); + long nXRight = nXPos + pG->mnOrigWidth; + int n = pG->mnCharPos; + int nCurrIdx = 2 * (n - mnMinCharPos); + if( !pG->IsRTLGlyph() ) + { + // normal positions for LTR case + pCaretXArray[ nCurrIdx ] = nXPos; + pCaretXArray[ nCurrIdx+1 ] = nXRight; + } + else + { + // reverse positions for RTL case + pCaretXArray[ nCurrIdx ] = nXRight; + pCaretXArray[ nCurrIdx+1 ] = nXPos; + } + } +} + +// ----------------------------------------------------------------------- + +int GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const +{ + int nCharCapacity = mnEndCharPos - mnMinCharPos; + sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) ); + if( !GetCharWidths( pCharWidths ) ) + return STRING_LEN; + + long nWidth = 0; + for( int i = mnMinCharPos; i < mnEndCharPos; ++i ) + { + nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor; + if( nWidth >= nMaxWidth ) + return i; + nWidth += nCharExtra; + } + + return STRING_LEN; +} + +// ----------------------------------------------------------------------- + +int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, + int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const +{ + const GlyphItem* pG = mpGlyphItems + nStart; + + // find next glyph in substring + for(; nStart < mnGlyphCount; ++nStart, ++pG ) + { + int n = pG->mnCharPos; + if( (mnMinCharPos <= n) && (n < mnEndCharPos) ) + break; + } + + // return zero if no more glyph found + if( nStart >= mnGlyphCount ) + return 0; + + // calculate absolute position in pixel units + Point aRelativePos = pG->maLinearPos - maBasePoint; + + // find more glyphs which can be merged into one drawing instruction + int nCount = 0; + long nYPos = pG->maLinearPos.Y(); + long nOldFlags = pG->mnGlyphIndex; + for(;;) + { + // update return data with glyph info + ++nCount; + *(pGlyphs++) = pG->mnGlyphIndex; + if( pCharPosAry ) + *(pCharPosAry++) = pG->mnCharPos; + if( pGlyphAdvAry ) + *pGlyphAdvAry = pG->mnNewWidth; + + // break at end of glyph list + if( ++nStart >= mnGlyphCount ) + break; + // break when enough glyphs + if( nCount >= nLen ) + break; + + long nGlyphAdvance = pG[1].maLinearPos.X() - pG->maLinearPos.X(); + if( pGlyphAdvAry ) + { + // override default advance width with correct value + *(pGlyphAdvAry++) = nGlyphAdvance; + } + else + { + // stop when next x-position is unexpected + if( pG->mnOrigWidth != nGlyphAdvance ) + break; + } + + // advance to next glyph + ++pG; + + // stop when next y-position is unexpected + if( nYPos != pG->maLinearPos.Y() ) + break; + + // stop when no longer in string + int n = pG->mnCharPos; + if( (n < mnMinCharPos) || (mnEndCharPos <= n) ) + break; + + // stop when glyph flags change + if( (nOldFlags ^ pG->mnGlyphIndex) & GF_FLAGMASK ) + break; + + nOldFlags = pG->mnGlyphIndex; // &GF_FLAGMASK not needed for test above + } + + aRelativePos.X() /= mnUnitsPerPixel; + aRelativePos.Y() /= mnUnitsPerPixel; + rPos = GetDrawPosition( aRelativePos ); + + return nCount; +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos ) +{ + if( nStart >= mnGlyphCount ) + return; + + GlyphItem* pG = mpGlyphItems + nStart; + // the nNewXPos argument determines the new cell position + // as RTL-glyphs are right justified in their cell + // the cell position needs to be adjusted to the glyph position + if( pG->IsRTLGlyph() ) + nNewXPos += pG->mnNewWidth - pG->mnOrigWidth; + // calculate the x-offset to the old position + long nXDelta = nNewXPos - pG->maLinearPos.X(); + // adjust all following glyph positions if needed + if( nXDelta != 0 ) + { + GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount; + for(; pG < pGEnd; ++pG ) + pG->maLinearPos.X() += nXDelta; + } +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::DropGlyph( int nStart ) +{ + if( nStart >= mnGlyphCount ) + return; + GlyphItem* pG = mpGlyphItems + nStart; + pG->mnGlyphIndex = GF_DROPPED; + pG->mnCharPos = -1; +} + +// ----------------------------------------------------------------------- + +void GenericSalLayout::Simplify( bool bIsBase ) +{ + const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0; + + // remove dropped glyphs inplace + GlyphItem* pGDst = mpGlyphItems; + const GlyphItem* pGSrc = mpGlyphItems; + const GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount; + for(; pGSrc < pGEnd; ++pGSrc ) + { + if( pGSrc->mnGlyphIndex == nDropMarker ) + continue; + if( pGDst != pGSrc ) + *pGDst = *pGSrc; + ++pGDst; + } + mnGlyphCount = pGDst - mpGlyphItems; +} + +// ----------------------------------------------------------------------- + +// make sure GlyphItems are sorted left to right +void GenericSalLayout::SortGlyphItems() +{ + // move cluster components behind their cluster start (especially for RTL) + // using insertion sort because the glyph items are "almost sorted" + const GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount; + for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG ) + { + // find a cluster starting with a diacritic + if( !pG->IsDiacritic() ) + continue; + if( !pG->IsClusterStart() ) + continue; + for( GlyphItem* pBaseGlyph = pG; ++pBaseGlyph < pGEnd; ) + { + // find the base glyph matching to the misplaced diacritic + if( pBaseGlyph->IsClusterStart() ) + break; + if( pBaseGlyph->IsDiacritic() ) + continue; + + // found the matching base glyph + // => this base glyph becomes the new cluster start + const GlyphItem aDiacritic = *pG; + *pG = *pBaseGlyph; + *pBaseGlyph = aDiacritic; + + // update glyph flags of swapped glyphitems + pG->mnFlags &= ~GlyphItem::IS_IN_CLUSTER; + pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER; + // prepare for checking next cluster + pG = pBaseGlyph; + break; + } + } +} + +// ======================================================================= + +MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const ImplFontData* pBaseFont ) +: SalLayout() +, mnLevel( 1 ) +, mbInComplete( false ) +{ + //maFallbackRuns[0].Clear(); + mpFallbackFonts[ 0 ] = pBaseFont; + mpLayouts[ 0 ] = &rBaseLayout; + mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel(); +} + +void MultiSalLayout::SetInComplete(bool bInComplete) +{ + mbInComplete = bInComplete; + maFallbackRuns[mnLevel-1] = ImplLayoutRuns(); +} + +// ----------------------------------------------------------------------- + +MultiSalLayout::~MultiSalLayout() +{ + for( int i = 0; i < mnLevel; ++i ) + mpLayouts[ i ]->Release(); +} + +// ----------------------------------------------------------------------- + +bool MultiSalLayout::AddFallback( SalLayout& rFallback, + ImplLayoutRuns& rFallbackRuns, const ImplFontData* pFallbackFont ) +{ + if( mnLevel >= MAX_FALLBACK ) + return false; + + mpFallbackFonts[ mnLevel ] = pFallbackFont; + mpLayouts[ mnLevel ] = &rFallback; + maFallbackRuns[ mnLevel-1 ] = rFallbackRuns; + ++mnLevel; + return true; +} + +// ----------------------------------------------------------------------- + +bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs ) +{ + if( mnLevel <= 1 ) + return false; + if (!mbInComplete) + maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns; + return true; +} + +// ----------------------------------------------------------------------- + +void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) +{ + SalLayout::AdjustLayout( rArgs ); + ImplLayoutArgs aMultiArgs = rArgs; + + if( !rArgs.mpDXArray && rArgs.mnLayoutWidth ) + { + // for stretched text in a MultiSalLayout the target width needs to be + // distributed by individually adjusting its virtual character widths + long nTargetWidth = aMultiArgs.mnLayoutWidth; + nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units + aMultiArgs.mnLayoutWidth = 0; + + // we need to get the original unmodified layouts ready + for( int n = 0; n < mnLevel; ++n ) + mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs ); + // then we can measure the unmodified metrics + int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos; + sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) ); + FillDXArray( pJustificationArray ); + // #i17359# multilayout is not simplified yet, so calculating the + // unjustified width needs handholding; also count the number of + // stretchable virtual char widths + long nOrigWidth = 0; + int nStretchable = 0; + for( int i = 0; i < nCharCount; ++i ) + { + // convert array from widths to sum of widths + nOrigWidth += pJustificationArray[i]; + if( pJustificationArray[i] > 0 ) + ++nStretchable; + } + + // now we are able to distribute the extra width over the virtual char widths + if( nOrigWidth && (nTargetWidth != nOrigWidth) ) + { + int nDiffWidth = nTargetWidth - nOrigWidth; + int nWidthSum = 0; + for( int i = 0; i < nCharCount; ++i ) + { + int nJustWidth = pJustificationArray[i]; + if( (nJustWidth > 0) && (nStretchable > 0) ) + { + int nDeltaWidth = nDiffWidth / nStretchable; + nJustWidth += nDeltaWidth; + nDiffWidth -= nDeltaWidth; + --nStretchable; + } + nWidthSum += nJustWidth; + pJustificationArray[i] = nWidthSum; + } + if( nWidthSum != nTargetWidth ) + pJustificationArray[ nCharCount-1 ] = nTargetWidth; + + // the justification array is still in base level units + // => convert it to pixel units + if( mnUnitsPerPixel > 1 ) + { + for( int i = 0; i < nCharCount; ++i ) + { + sal_Int32 nVal = pJustificationArray[ i ]; + nVal += (mnUnitsPerPixel + 1) / 2; + pJustificationArray[ i ] = nVal / mnUnitsPerPixel; + } + } + + // change the mpDXArray temporarilly (just for the justification) + aMultiArgs.mpDXArray = pJustificationArray; + } + } + + // Compute rtl flags, since in some scripts glyphs/char order can be + // reversed for a few character sequencies e.g. Myanmar + std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false); + rArgs.ResetPos(); + bool bRtl; + int nRunStart, nRunEnd; + while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl)) + { + if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos), + vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true); + } + rArgs.ResetPos(); + + // prepare "merge sort" + int nStartOld[ MAX_FALLBACK ]; + int nStartNew[ MAX_FALLBACK ]; + int nCharPos[ MAX_FALLBACK ]; + sal_Int32 nGlyphAdv[ MAX_FALLBACK ]; + int nValid[ MAX_FALLBACK ] = {0}; + + sal_GlyphId nDummy; + Point aPos; + int nLevel = 0, n; + for( n = 0; n < mnLevel; ++n ) + { + // now adjust the individual components + if( n > 0 ) + { + aMultiArgs.maRuns = maFallbackRuns[ n-1 ]; + aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK; + } + mpLayouts[n]->AdjustLayout( aMultiArgs ); + + // disable glyph-injection for glyph-fallback SalLayout iteration + mpLayouts[n]->DisableGlyphInjection( true ); + + // remove unused parts of component + if( n > 0 ) + { + if (mbInComplete && (n == mnLevel-1)) + mpLayouts[n]->Simplify( true ); + else + mpLayouts[n]->Simplify( false ); + } + + // prepare merging components + nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0; + nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos, + nStartNew[ nLevel ], &nGlyphAdv[ nLevel ], &nCharPos[ nLevel ] ); +#ifdef MULTI_SL_DEBUG + if (nValid[nLevel]) fprintf(mslLog(), "layout[%d]->GetNextGlyphs %d,%d x%d a%d c%d %x\n", n, nStartOld[nLevel], nStartNew[nLevel], aPos.X(), nGlyphAdv[nLevel], nCharPos[nLevel], + rArgs.mpStr[nCharPos[nLevel]]); +#endif + if( (n > 0) && !nValid[ nLevel ] ) + { + // an empty fallback layout can be released + mpLayouts[n]->Release(); + } + else + { + // reshuffle used fallbacks if needed + if( nLevel != n ) + { + mpLayouts[ nLevel ] = mpLayouts[ n ]; + mpFallbackFonts[ nLevel ] = mpFallbackFonts[ n ]; + maFallbackRuns[ nLevel ] = maFallbackRuns[ n ]; + } + ++nLevel; + } + } + mnLevel = nLevel; + + // merge the fallback levels + long nXPos = 0; + double fUnitMul = 1.0; + for( n = 0; n < nLevel; ++n ) + maFallbackRuns[n].ResetPos(); + int nActiveCharPos = nCharPos[0]; + int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])? + rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1; + int nRunVisibleEndChar = nCharPos[0]; + while( nValid[0] && (nLevel > 0)) + { + // find best fallback level + for( n = 0; n < nLevel; ++n ) + if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) ) + // fallback level n wins when it requested no further fallback + break; + int nFBLevel = n; + + if( n < nLevel ) + { + // use base(n==0) or fallback(n>=1) level + fUnitMul = mnUnitsPerPixel; + fUnitMul /= mpLayouts[n]->GetUnitsPerPixel(); + long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5); + mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos ); + } + else + { + n = 0; // keep NotDef in base level + fUnitMul = 1.0; + } + + if( n > 0 ) + { + // drop the NotDef glyphs in the base layout run if a fallback run exists + while ( + (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) && + (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) ) + ) + { + mpLayouts[0]->DropGlyph( nStartOld[0] ); + nStartOld[0] = nStartNew[0]; + nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos, + nStartNew[0], &nGlyphAdv[0], &nCharPos[0] ); +#ifdef MULTI_SL_DEBUG + if (nValid[0]) fprintf(mslLog(), "layout[0]->GetNextGlyphs %d,%d x%d a%d c%d %x\n", nStartOld[0], nStartNew[0], aPos.X(), nGlyphAdv[0], nCharPos[0], rArgs.mpStr[nCharPos[0]]); +#endif + if( !nValid[0] ) + break; + } + } + + // skip to end of layout run and calculate its advance width + int nRunAdvance = 0; + bool bKeepNotDef = (nFBLevel >= nLevel); + for(;;) + { + nRunAdvance += nGlyphAdv[n]; + + // proceed to next glyph + nStartOld[n] = nStartNew[n]; + int nOrigCharPos = nCharPos[n]; + nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos, + nStartNew[n], &nGlyphAdv[n], &nCharPos[n] ); +#ifdef MULTI_SL_DEBUG + if (nValid[n]) fprintf(mslLog(), "layout[%d]->GetNextGlyphs %d,%d a%d c%d %x\n", n, nStartOld[n], nStartNew[n], nGlyphAdv[n], nCharPos[n], rArgs.mpStr[nCharPos[n]]); +#endif + // break after last glyph of active layout + if( !nValid[n] ) + { + // performance optimization (when a fallback layout is no longer needed) + if( n >= nLevel-1 ) + --nLevel; + break; + } + + //If the next character is one which belongs to the next level, then we + //are finished here for now, and we'll pick up after the next level has + //been processed + if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos)) + { + if (nOrigCharPos < nCharPos[n]) + { + if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n])) + break; + } + else if (nOrigCharPos > nCharPos[n]) + { + if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos)) + break; + } + } + + // break at end of layout run + if( n > 0 ) + { + // skip until end of fallback run + if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) ) + break; + } + else + { + // break when a fallback is needed and available + bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] ); + if( bNeedFallback ) + if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) ) + break; + // break when change from resolved to unresolved base layout run + if( bKeepNotDef && !bNeedFallback ) + { maFallbackRuns[0].NextRun(); break; } + bKeepNotDef = bNeedFallback; + } + // check for reordered glyphs + if (aMultiArgs.mpDXArray && + nRunVisibleEndChar < mnEndCharPos && + nRunVisibleEndChar >= mnMinCharPos && + nCharPos[n] < mnEndCharPos && + nCharPos[n] >= mnMinCharPos) + { + if (vRtl[nActiveCharPos - mnMinCharPos]) + { + if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos] + >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos]) + { + nRunVisibleEndChar = nCharPos[n]; + } + } + else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos] + <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos]) + { + nRunVisibleEndChar = nCharPos[n]; + } + } + } + + // if a justification array is available + // => use it directly to calculate the corresponding run width + if( aMultiArgs.mpDXArray ) + { + // the run advance is the width from the first char + // in the run to the first char in the next run + nRunAdvance = 0; +#ifdef MULTI_SL_DEBUG + const bool bLTR = !(vRtl[nActiveCharPos - mnMinCharPos]);//(nActiveCharPos < nCharPos[0]); + int nOldRunAdv = 0; + int nDXIndex = nCharPos[0] - mnMinCharPos - bLTR; + if( nDXIndex >= 0 ) + nOldRunAdv += aMultiArgs.mpDXArray[ nDXIndex ]; + nDXIndex = nActiveCharPos - mnMinCharPos - bLTR; + if( nDXIndex >= 0 ) + nOldRunAdv -= aMultiArgs.mpDXArray[ nDXIndex ]; + if( !bLTR ) + nOldRunAdv = -nOldRunAdv; +#endif + if (vRtl[nActiveCharPos - mnMinCharPos]) + { + if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos) + nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos]; + if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos) + nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos]; +#ifdef MULTI_SL_DEBUG + fprintf(mslLog(), "rtl visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar-1, nRunVisibleEndChar-1, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv); +#endif + } + else + { + if (nRunVisibleEndChar >= mnMinCharPos) + nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos]; + if (nLastRunEndChar >= mnMinCharPos) + nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos]; +#ifdef MULTI_SL_DEBUG + fprintf(mslLog(), "visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar, nRunVisibleEndChar, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv); +#endif + } + nLastRunEndChar = nRunVisibleEndChar; + nRunVisibleEndChar = nCharPos[0]; + // the requested width is still in pixel units + // => convert it to base level font units + nRunAdvance *= mnUnitsPerPixel; + } + else + { + // the measured width is still in fallback font units + // => convert it to base level font units + if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0) + nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5); + } + + // calculate new x position (in base level units) + nXPos += nRunAdvance; + + // prepare for next fallback run + nActiveCharPos = nCharPos[0]; + // it essential that the runs don't get ahead of themselves and in the + // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may + // have already been reached on the base level + for( int i = nFBLevel; --i >= 0;) + { + if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl)) + { + if (bRtl) + { + if (nRunStart > nActiveCharPos) + maFallbackRuns[i].NextRun(); + } + else + { + if (nRunEnd <= nActiveCharPos) + maFallbackRuns[i].NextRun(); + } + } + } +// if( !maFallbackRuns[i].PosIsInRun( nActiveCharPos ) ) +// maFallbackRuns[i].NextRun(); + } + + mpLayouts[0]->Simplify( true ); + + // reenable glyph-injection + for( n = 0; n < mnLevel; ++n ) + mpLayouts[n]->DisableGlyphInjection( false ); +} + +// ----------------------------------------------------------------------- + +void MultiSalLayout::InitFont() const +{ + if( mnLevel > 0 ) + mpLayouts[0]->InitFont(); +} + +// ----------------------------------------------------------------------- + +const ImplFontData* MultiSalLayout::GetFallbackFontData( sal_GlyphId nGlyphId ) const +{ + int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT; + return mpFallbackFonts[ nFallbackLevel ]; +} + +// ----------------------------------------------------------------------- + +void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const +{ + for( int i = mnLevel; --i >= 0; ) + { + SalLayout& rLayout = *mpLayouts[ i ]; + rLayout.DrawBase() += maDrawBase; + rLayout.DrawOffset() += maDrawOffset; + rLayout.InitFont(); + rLayout.DrawText( rGraphics ); + rLayout.DrawOffset() -= maDrawOffset; + rLayout.DrawBase() -= maDrawBase; + } + // NOTE: now the baselevel font is active again +} + + // ----------------------------------------------------------------------- + +int MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const +{ + if( mnLevel <= 0 ) + return STRING_LEN; + if( mnLevel == 1 ) + return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor ); + + int nCharCount = mnEndCharPos - mnMinCharPos; + sal_Int32* pCharWidths = (sal_Int32*)alloca( 2*nCharCount * sizeof(sal_Int32) ); + mpLayouts[0]->FillDXArray( pCharWidths ); + + for( int n = 1; n < mnLevel; ++n ) + { + SalLayout& rLayout = *mpLayouts[ n ]; + rLayout.FillDXArray( pCharWidths + nCharCount ); + double fUnitMul = mnUnitsPerPixel; + fUnitMul /= rLayout.GetUnitsPerPixel(); + for( int i = 0; i < nCharCount; ++i ) + { + long w = pCharWidths[ i + nCharCount ]; + w = static_cast<long>(w*fUnitMul + 0.5); + pCharWidths[ i ] += w; + } + } + + long nWidth = 0; + for( int i = 0; i < nCharCount; ++i ) + { + nWidth += pCharWidths[ i ] * nFactor; + if( nWidth > nMaxWidth ) + return (i + mnMinCharPos); + nWidth += nCharExtra; + } + + return STRING_LEN; +} + +// ----------------------------------------------------------------------- + +long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const +{ + long nMaxWidth = 0; + + // prepare merging of fallback levels + sal_Int32* pTempWidths = NULL; + const int nCharCount = mnEndCharPos - mnMinCharPos; + if( pCharWidths ) + { + for( int i = 0; i < nCharCount; ++i ) + pCharWidths[i] = 0; + pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) ); + } + + for( int n = mnLevel; --n >= 0; ) + { + // query every fallback level + long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths ); + if( !nTextWidth ) + continue; + // merge results from current level + double fUnitMul = mnUnitsPerPixel; + fUnitMul /= mpLayouts[n]->GetUnitsPerPixel(); + nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5); + if( nMaxWidth < nTextWidth ) + nMaxWidth = nTextWidth; + if( !pCharWidths ) + continue; + // calculate virtual char widths using most probable fallback layout + for( int i = 0; i < nCharCount; ++i ) + { + // #i17359# restriction: + // one char cannot be resolved from different fallbacks + if( pCharWidths[i] != 0 ) + continue; + long nCharWidth = pTempWidths[i]; + if( !nCharWidth ) + continue; + nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5); + pCharWidths[i] = nCharWidth; + } + } + + return nMaxWidth; +} + +// ----------------------------------------------------------------------- + +void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const +{ + SalLayout& rLayout = *mpLayouts[ 0 ]; + rLayout.GetCaretPositions( nMaxIndex, pCaretXArray ); + + if( mnLevel > 1 ) + { + sal_Int32* pTempPos = (sal_Int32*)alloca( nMaxIndex * sizeof(sal_Int32) ); + for( int n = 1; n < mnLevel; ++n ) + { + mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos ); + double fUnitMul = mnUnitsPerPixel; + fUnitMul /= mpLayouts[n]->GetUnitsPerPixel(); + for( int i = 0; i < nMaxIndex; ++i ) + if( pTempPos[i] >= 0 ) + { + long w = pTempPos[i]; + w = static_cast<long>(w*fUnitMul + 0.5); + pCaretXArray[i] = w; + } + } + } +} + +// ----------------------------------------------------------------------- + +int MultiSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdxAry, Point& rPos, + int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const +{ + // for multi-level fallback only single glyphs should be used + if( mnLevel > 1 && nLen > 1 ) + nLen = 1; + + // NOTE: nStart is tagged with current font index + int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT; + nStart &= ~GF_FONTMASK; + for(; nLevel < mnLevel; ++nLevel, nStart=0 ) + { + SalLayout& rLayout = *mpLayouts[ nLevel ]; + rLayout.InitFont(); + int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos, + nStart, pGlyphAdvAry, pCharPosAry ); + if( nRetVal ) + { + int nFontTag = nLevel << GF_FONTSHIFT; + nStart |= nFontTag; + double fUnitMul = mnUnitsPerPixel; + fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel(); + for( int i = 0; i < nRetVal; ++i ) + { + if( pGlyphAdvAry ) + { + long w = pGlyphAdvAry[i]; + w = static_cast<long>(w * fUnitMul + 0.5); + pGlyphAdvAry[i] = w; + } + pGlyphIdxAry[ i ] |= nFontTag; + } + rPos += maDrawBase; + rPos += maDrawOffset; + return nRetVal; + } + } + + // #111016# reset to base level font when done + mpLayouts[0]->InitFont(); + return 0; +} + +// ----------------------------------------------------------------------- + +bool MultiSalLayout::GetOutline( SalGraphics& rGraphics, + ::basegfx::B2DPolyPolygonVector& rPPV ) const +{ + bool bRet = false; + + for( int i = mnLevel; --i >= 0; ) + { + SalLayout& rLayout = *mpLayouts[ i ]; + rLayout.DrawBase() = maDrawBase; + rLayout.DrawOffset() += maDrawOffset; + rLayout.InitFont(); + bRet |= rLayout.GetOutline( rGraphics, rPPV ); + rLayout.DrawOffset() -= maDrawOffset; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const +{ + bool bRet = false; + + Rectangle aRectangle; + for( int i = mnLevel; --i >= 0; ) + { + SalLayout& rLayout = *mpLayouts[ i ]; + rLayout.DrawBase() = maDrawBase; + rLayout.DrawOffset() += maDrawOffset; + rLayout.InitFont(); + if( rLayout.GetBoundRect( rGraphics, aRectangle ) ) + { + rRect.Union( aRectangle ); + bRet = true; + } + rLayout.DrawOffset() -= maDrawOffset; + } + + return bRet; +} + +// ======================================================================= + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/salmisc.cxx b/vcl/source/gdi/salmisc.cxx new file mode 100644 index 000000000000..3a9bd669f54f --- /dev/null +++ b/vcl/source/gdi/salmisc.cxx @@ -0,0 +1,519 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <rtl/memory.h> +#include <vcl/bmpacc.hxx> +#include <vcl/salbtype.hxx> +#include <vcl/bmpfast.hxx> + +// ----------- +// - Defines - +// ----------- + +#define IMPL_CASE_GET_FORMAT( Format ) \ +case( BMP_FORMAT##Format ): \ + pFncGetPixel = BitmapReadAccess::GetPixelFor##Format; \ +break + +// ----------------------------------------------------------------------------- + +#define IMPL_CASE_SET_FORMAT( Format, BitCount ) \ +case( BMP_FORMAT##Format ): \ +{ \ + pFncSetPixel = BitmapReadAccess::SetPixelFor##Format; \ + pDstBuffer->mnBitCount = BitCount; \ +} \ +break + +// ----------------------------------------------------------------------------- + +#define DOUBLE_SCANLINES() \ +while( ( nActY < nHeight1 ) && ( pMapY[ nActY + 1 ] == nMapY ) ) \ +{ \ + memcpy( pDstScanMap[ nActY + 1L ], pDstScan, rDstBuffer.mnScanlineSize ); \ + nActY++; \ +} + +// ----------- +// - Inlines - +// ----------- + +#define TC_TO_PAL_COLORS 4096 + +static long ImplIndexFromColor( const BitmapColor& rCol ) +{ +#if TC_TO_PAL_COLORS == 4096 + + return( ( ( (long) rCol.GetBlue() >> 4L) << 8L ) | + ( ( (long) rCol.GetGreen() >> 4L ) << 4L ) | + ( (long) rCol.GetRed() >> 4L ) ); + +#elif TC_TO_PAL_COLORS == 32768 + + return( ( ( (long) rCol.GetBlue() >> 3L) << 10L ) | + ( ( (long) rCol.GetGreen() >> 3L ) << 5L ) | + ( (long) rCol.GetRed() >> 3L ) ); + +#endif +} + + +#define COLOR_TO_INDEX( _def_rCol ) + +// ------------------------ +// - conversion functions - +// ------------------------ + +static void ImplPALToPAL( const BitmapBuffer& rSrcBuffer, BitmapBuffer& rDstBuffer, + FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel, + Scanline* pSrcScanMap, Scanline* pDstScanMap, long* pMapX, long* pMapY ) +{ + const long nWidth = rDstBuffer.mnWidth, nHeight = rDstBuffer.mnHeight, nHeight1 = nHeight - 1; + const ColorMask& rSrcMask = rSrcBuffer.maColorMask; + const ColorMask& rDstMask = rDstBuffer.maColorMask; + BitmapPalette aColMap( rSrcBuffer.maPalette.GetEntryCount() ); + BitmapColor* pColMapBuf = aColMap.ImplGetColorBuffer(); + BitmapColor aIndex( 0 ); + + for( sal_uInt16 i = 0, nSrcCount = aColMap.GetEntryCount(), nDstCount = rDstBuffer.maPalette.GetEntryCount(); i < nSrcCount; i++ ) + { + if( ( i < nDstCount ) && ( rSrcBuffer.maPalette[ i ] == rDstBuffer.maPalette[ i ] ) ) + aIndex.SetIndex( sal::static_int_cast<sal_uInt8>(i) ); + else + aIndex.SetIndex( sal::static_int_cast<sal_uInt8>(rDstBuffer.maPalette.GetBestIndex( rSrcBuffer.maPalette[ i ] )) ); + + pColMapBuf[ i ] = aIndex; + } + + for( long nActY = 0, nMapY; nActY < nHeight; nActY++ ) + { + Scanline pSrcScan( pSrcScanMap[ nMapY = pMapY[ nActY ] ] ), pDstScan( pDstScanMap[ nActY ] ); + + for( long nX = 0L; nX < nWidth; nX++ ) + pFncSetPixel( pDstScan, nX, pColMapBuf[ pFncGetPixel( pSrcScan, pMapX[ nX ], rSrcMask ).GetIndex() ], rDstMask ); + + DOUBLE_SCANLINES(); + } +} + +// ----------------------------------------------------------------------------- + +static void ImplPALToTC( const BitmapBuffer& rSrcBuffer, BitmapBuffer& rDstBuffer, + FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel, + Scanline* pSrcScanMap, Scanline* pDstScanMap, long* pMapX, long* pMapY ) +{ + const long nWidth = rDstBuffer.mnWidth, nHeight = rDstBuffer.mnHeight, nHeight1 = nHeight - 1; + const ColorMask& rSrcMask = rSrcBuffer.maColorMask; + const ColorMask& rDstMask = rDstBuffer.maColorMask; + const BitmapColor* pColBuf = rSrcBuffer.maPalette.ImplGetColorBuffer(); + + if( BMP_SCANLINE_FORMAT( rSrcBuffer.mnFormat ) == BMP_FORMAT_1BIT_MSB_PAL ) + { + const BitmapColor aCol0( pColBuf[ 0 ] ); + const BitmapColor aCol1( pColBuf[ 1 ] ); + long nMapX; + + for( long nActY = 0, nMapY; nActY < nHeight; nActY++ ) + { + Scanline pSrcScan( pSrcScanMap[ nMapY = pMapY[ nActY ] ] ), pDstScan( pDstScanMap[ nActY ] ); + + for( long nX = 0L; nX < nWidth; ) + { + nMapX = pMapX[ nX ]; + pFncSetPixel( pDstScan, nX++, + pSrcScan[ nMapX >> 3 ] & ( 1 << ( 7 - ( nMapX & 7 ) ) ) ? aCol1 : aCol0, + rDstMask ); + } + + DOUBLE_SCANLINES(); + } + } + else if( BMP_SCANLINE_FORMAT( rSrcBuffer.mnFormat ) == BMP_FORMAT_4BIT_MSN_PAL ) + { + long nMapX; + + for( long nActY = 0, nMapY; nActY < nHeight; nActY++ ) + { + Scanline pSrcScan( pSrcScanMap[ nMapY = pMapY[ nActY ] ] ), pDstScan( pDstScanMap[ nActY ] ); + + for( long nX = 0L; nX < nWidth; ) + { + nMapX = pMapX[ nX ]; + pFncSetPixel( pDstScan, nX++, + pColBuf[ ( pSrcScan[ nMapX >> 1 ] >> ( nMapX & 1 ? 0 : 4 ) ) & 0x0f ], + rDstMask ); + } + + DOUBLE_SCANLINES(); + } + } + else if( BMP_SCANLINE_FORMAT( rSrcBuffer.mnFormat ) == BMP_FORMAT_8BIT_PAL ) + { + for( long nActY = 0, nMapY; nActY < nHeight; nActY++ ) + { + Scanline pSrcScan( pSrcScanMap[ nMapY = pMapY[ nActY ] ] ), pDstScan( pDstScanMap[ nActY ] ); + + for( long nX = 0L; nX < nWidth; nX++ ) + pFncSetPixel( pDstScan, nX, pColBuf[ pSrcScan[ pMapX[ nX ] ] ], rDstMask ); + + DOUBLE_SCANLINES(); + } + } + else + { + for( long nActY = 0, nMapY; nActY < nHeight; nActY++ ) + { + Scanline pSrcScan( pSrcScanMap[ nMapY = pMapY[ nActY ] ] ), pDstScan( pDstScanMap[ nActY ] ); + + for( long nX = 0L; nX < nWidth; nX++ ) + pFncSetPixel( pDstScan, nX, pColBuf[ pFncGetPixel( pSrcScan, pMapX[ nX ], rSrcMask ).GetIndex() ], rDstMask ); + + DOUBLE_SCANLINES(); + } + } +} + +// ----------------------------------------------------------------------------- + +static void ImplTCToTC( const BitmapBuffer& rSrcBuffer, BitmapBuffer& rDstBuffer, + FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel, + Scanline* pSrcScanMap, Scanline* pDstScanMap, long* pMapX, long* pMapY ) +{ + const long nWidth = rDstBuffer.mnWidth, nHeight = rDstBuffer.mnHeight, nHeight1 = nHeight - 1; + const ColorMask& rSrcMask = rSrcBuffer.maColorMask; + const ColorMask& rDstMask = rDstBuffer.maColorMask; + + if( BMP_SCANLINE_FORMAT( rSrcBuffer.mnFormat ) == BMP_FORMAT_24BIT_TC_BGR ) + { + BitmapColor aCol; + sal_uInt8* pPixel = NULL; + + for( long nActY = 0, nMapY; nActY < nHeight; nActY++ ) + { + Scanline pSrcScan( pSrcScanMap[ nMapY = pMapY[ nActY ] ] ), pDstScan( pDstScanMap[ nActY ] ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aCol.SetBlue( *( pPixel = ( pSrcScan + pMapX[ nX ] * 3 ) )++ ); + aCol.SetGreen( *pPixel++ ); + aCol.SetRed( *pPixel ); + pFncSetPixel( pDstScan, nX, aCol, rDstMask ); + } + + DOUBLE_SCANLINES() + } + } + else + { + for( long nActY = 0, nMapY; nActY < nHeight; nActY++ ) + { + Scanline pSrcScan( pSrcScanMap[ nMapY = pMapY[ nActY ] ] ), pDstScan( pDstScanMap[ nActY ] ); + + for( long nX = 0L; nX < nWidth; nX++ ) + pFncSetPixel( pDstScan, nX, pFncGetPixel( pSrcScan, pMapX[ nX ], rSrcMask ), rDstMask ); + + DOUBLE_SCANLINES(); + } + } +} + +// ----------------------------------------------------------------------------- + +static void ImplTCToPAL( const BitmapBuffer& rSrcBuffer, BitmapBuffer& rDstBuffer, + FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel, + Scanline* pSrcScanMap, Scanline* pDstScanMap, long* pMapX, long* pMapY ) +{ + const long nWidth = rDstBuffer.mnWidth, nHeight = rDstBuffer.mnHeight, nHeight1 = nHeight - 1; + const ColorMask& rSrcMask = rSrcBuffer.maColorMask; + const ColorMask& rDstMask = rDstBuffer.maColorMask; + BitmapPalette aColMap( rSrcBuffer.maPalette.GetEntryCount() ); + sal_uInt8* pColToPalMap = new sal_uInt8[ TC_TO_PAL_COLORS ]; + BitmapColor aIndex( 0 ); + + for( long nR = 0; nR < 16; nR++ ) + { + for( long nG = 0; nG < 16; nG++ ) + { + for( long nB = 0; nB < 16; nB++ ) + { + BitmapColor aCol( sal::static_int_cast<sal_uInt8>(nR << 4), + sal::static_int_cast<sal_uInt8>(nG << 4), + sal::static_int_cast<sal_uInt8>(nB << 4) ); + pColToPalMap[ ImplIndexFromColor( aCol ) ] = (sal_uInt8) rDstBuffer.maPalette.GetBestIndex( aCol ); + } + } + } + + for( long nActY = 0, nMapY; nActY < nHeight; nActY++ ) + { + Scanline pSrcScan( pSrcScanMap[ nMapY = pMapY[ nActY ] ] ), pDstScan( pDstScanMap[ nActY ] ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aIndex.SetIndex( pColToPalMap[ ImplIndexFromColor( pFncGetPixel( pSrcScan, pMapX[ nX ], rSrcMask ) ) ] ); + pFncSetPixel( pDstScan, nX, aIndex, rDstMask ); + } + + DOUBLE_SCANLINES(); + } + + delete[] pColToPalMap; +} + +// ----------------------------------------------------------------------------- + +// --------------------- +// - StretchAndConvert - +// --------------------- + +BitmapBuffer* StretchAndConvert( const BitmapBuffer& rSrcBuffer, const SalTwoRect& rTwoRect, + sal_uLong nDstBitmapFormat, BitmapPalette* pDstPal, ColorMask* pDstMask ) +{ + FncGetPixel pFncGetPixel; + FncSetPixel pFncSetPixel; + BitmapBuffer* pDstBuffer = new BitmapBuffer; + long i; + + // set function for getting pixels + switch( BMP_SCANLINE_FORMAT( rSrcBuffer.mnFormat ) ) + { + IMPL_CASE_GET_FORMAT( _1BIT_MSB_PAL ); + IMPL_CASE_GET_FORMAT( _1BIT_LSB_PAL ); + IMPL_CASE_GET_FORMAT( _4BIT_MSN_PAL ); + IMPL_CASE_GET_FORMAT( _4BIT_LSN_PAL ); + IMPL_CASE_GET_FORMAT( _8BIT_PAL ); + IMPL_CASE_GET_FORMAT( _8BIT_TC_MASK ); + IMPL_CASE_GET_FORMAT( _16BIT_TC_MSB_MASK ); + IMPL_CASE_GET_FORMAT( _16BIT_TC_LSB_MASK ); + IMPL_CASE_GET_FORMAT( _24BIT_TC_BGR ); + IMPL_CASE_GET_FORMAT( _24BIT_TC_RGB ); + IMPL_CASE_GET_FORMAT( _24BIT_TC_MASK ); + IMPL_CASE_GET_FORMAT( _32BIT_TC_ABGR ); + IMPL_CASE_GET_FORMAT( _32BIT_TC_ARGB ); + IMPL_CASE_GET_FORMAT( _32BIT_TC_BGRA ); + IMPL_CASE_GET_FORMAT( _32BIT_TC_RGBA ); + IMPL_CASE_GET_FORMAT( _32BIT_TC_MASK ); + + default: + // should never come here + // initialize pFncGetPixel to something valid that is + // least likely to crash + pFncGetPixel = BitmapReadAccess::GetPixelFor_1BIT_MSB_PAL; + OSL_FAIL( "unknown read format" ); + break; + } + + // set function for setting pixels + const sal_uLong nDstScanlineFormat = BMP_SCANLINE_FORMAT( nDstBitmapFormat ); + switch( nDstScanlineFormat ) + { + IMPL_CASE_SET_FORMAT( _1BIT_MSB_PAL, 1 ); + IMPL_CASE_SET_FORMAT( _1BIT_LSB_PAL, 1 ); + IMPL_CASE_SET_FORMAT( _4BIT_MSN_PAL, 1 ); + IMPL_CASE_SET_FORMAT( _4BIT_LSN_PAL, 4 ); + IMPL_CASE_SET_FORMAT( _8BIT_PAL, 8 ); + IMPL_CASE_SET_FORMAT( _8BIT_TC_MASK, 8 ); + IMPL_CASE_SET_FORMAT( _16BIT_TC_MSB_MASK, 16 ); + IMPL_CASE_SET_FORMAT( _16BIT_TC_LSB_MASK, 16 ); + IMPL_CASE_SET_FORMAT( _24BIT_TC_BGR, 24 ); + IMPL_CASE_SET_FORMAT( _24BIT_TC_RGB, 24 ); + IMPL_CASE_SET_FORMAT( _24BIT_TC_MASK, 24 ); + IMPL_CASE_SET_FORMAT( _32BIT_TC_ABGR, 32 ); + IMPL_CASE_SET_FORMAT( _32BIT_TC_ARGB, 32 ); + IMPL_CASE_SET_FORMAT( _32BIT_TC_BGRA, 32 ); + IMPL_CASE_SET_FORMAT( _32BIT_TC_RGBA, 32 ); + IMPL_CASE_SET_FORMAT( _32BIT_TC_MASK, 32 ); + + default: + // should never come here + // initialize pFncSetPixel to something valid that is + // least likely to crash + pFncSetPixel = BitmapReadAccess::SetPixelFor_1BIT_MSB_PAL; + pDstBuffer->mnBitCount = 1; + OSL_FAIL( "unknown write format" ); + break; + } + + // fill destination buffer + pDstBuffer->mnFormat = nDstBitmapFormat; + pDstBuffer->mnWidth = rTwoRect.mnDestWidth; + pDstBuffer->mnHeight = rTwoRect.mnDestHeight; + pDstBuffer->mnScanlineSize = AlignedWidth4Bytes( pDstBuffer->mnBitCount * pDstBuffer->mnWidth ); + try + { + pDstBuffer->mpBits = new sal_uInt8[ pDstBuffer->mnScanlineSize * pDstBuffer->mnHeight ]; + } + catch( const std::bad_alloc& ) + { + // memory exception, clean up + pDstBuffer->mpBits = NULL; + delete pDstBuffer; + return NULL; + } + + // do we need a destination palette or color mask? + if( ( nDstScanlineFormat == BMP_FORMAT_1BIT_MSB_PAL ) || + ( nDstScanlineFormat == BMP_FORMAT_1BIT_LSB_PAL ) || + ( nDstScanlineFormat == BMP_FORMAT_4BIT_MSN_PAL ) || + ( nDstScanlineFormat == BMP_FORMAT_4BIT_LSN_PAL ) || + ( nDstScanlineFormat == BMP_FORMAT_8BIT_PAL ) ) + { + DBG_ASSERT( pDstPal, "destination buffer requires palette" ); + pDstBuffer->maPalette = *pDstPal; + } + else if( ( nDstScanlineFormat == BMP_FORMAT_8BIT_TC_MASK ) || + ( nDstScanlineFormat == BMP_FORMAT_16BIT_TC_MSB_MASK ) || + ( nDstScanlineFormat == BMP_FORMAT_16BIT_TC_LSB_MASK ) || + ( nDstScanlineFormat == BMP_FORMAT_24BIT_TC_MASK ) || + ( nDstScanlineFormat == BMP_FORMAT_32BIT_TC_MASK ) ) + { + DBG_ASSERT( pDstMask, "destination buffer requires color mask" ); + pDstBuffer->maColorMask = *pDstMask; + } + + // short circuit the most important conversions + bool bFastConvert = ImplFastBitmapConversion( *pDstBuffer, rSrcBuffer, rTwoRect ); + if( bFastConvert ) + return pDstBuffer; + + const long nSrcX = rTwoRect.mnSrcX, nSrcY = rTwoRect.mnSrcY; + const long nSrcDX = rTwoRect.mnSrcWidth, nSrcDY = rTwoRect.mnSrcHeight; + const long nDstDX = rTwoRect.mnDestWidth, nDstDY = rTwoRect.mnDestHeight; + Scanline* pSrcScan = NULL; + Scanline* pDstScan = NULL; + long* pMapX = NULL; + long* pMapY = NULL; + long nTmp, nOffset; + + try + { + pSrcScan = new Scanline[ rSrcBuffer.mnHeight ]; + pDstScan = new Scanline[ nDstDY ]; + pMapX = new long[ nDstDX ]; + pMapY = new long[ nDstDY ]; + } + catch( const std::bad_alloc& ) + { + // memory exception, clean up + // remark: the buffer ptr causing the exception + // is still NULL here + delete[] pSrcScan; + delete[] pDstScan; + delete[] pMapX; + delete[] pMapY; + delete pDstBuffer; + return NULL; + } + + // horizontal mapping table + if( nDstDX != nSrcDX ) + { + const double fFactorX = ( nDstDX > 1 ) ? (double) ( nSrcDX - 1 ) / ( nDstDX - 1 ) : 0.0; + + for( i = 0L; i < nDstDX; i++ ) + pMapX[ i ] = nSrcX + FRound( i * fFactorX ); + } + else + { + for( i = 0L, nTmp = nSrcX; i < nDstDX; i++ ) + pMapX[ i ] = nTmp++; + } + + // vertical mapping table + if( nDstDY != nSrcDY ) + { + const double fFactorY = ( nDstDY > 1 ) ? (double) ( nSrcDY - 1 ) / ( nDstDY - 1 ) : 0.0; + + for( i = 0L; i < nDstDY; i++ ) + pMapY[ i ] = nSrcY + FRound( i * fFactorY ); + } + else + { + for( i = 0L, nTmp = nSrcY; i < nDstDY; i++ ) + pMapY[ i ] = nTmp++; + } + + // source scanline buffer + Scanline pTmpScan; + if( BMP_SCANLINE_ADJUSTMENT( rSrcBuffer.mnFormat ) == BMP_FORMAT_TOP_DOWN ) + pTmpScan = rSrcBuffer.mpBits, nOffset = rSrcBuffer.mnScanlineSize; + else + { + pTmpScan = rSrcBuffer.mpBits + ( rSrcBuffer.mnHeight - 1 ) * rSrcBuffer.mnScanlineSize; + nOffset = -rSrcBuffer.mnScanlineSize; + } + + for( i = 0L; i < rSrcBuffer.mnHeight; i++, pTmpScan += nOffset ) + pSrcScan[ i ] = pTmpScan; + + // destination scanline buffer + if( BMP_SCANLINE_ADJUSTMENT( pDstBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN ) + pTmpScan = pDstBuffer->mpBits, nOffset = pDstBuffer->mnScanlineSize; + else + { + pTmpScan = pDstBuffer->mpBits + ( nDstDY - 1 ) * pDstBuffer->mnScanlineSize; + nOffset = -pDstBuffer->mnScanlineSize; + } + + for( i = 0L; i < nDstDY; i++, pTmpScan += nOffset ) + pDstScan[ i ] = pTmpScan; + + // do buffer scaling and conversion + if( rSrcBuffer.mnBitCount <= 8 && pDstBuffer->mnBitCount <= 8 ) + { + ImplPALToPAL( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel, + pSrcScan, pDstScan, pMapX, pMapY ); + } + else if( rSrcBuffer.mnBitCount <= 8 && pDstBuffer->mnBitCount > 8 ) + { + ImplPALToTC( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel, + pSrcScan, pDstScan, pMapX, pMapY ); + } + else if( rSrcBuffer.mnBitCount > 8 && pDstBuffer->mnBitCount > 8 ) + { + ImplTCToTC( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel, + pSrcScan, pDstScan, pMapX, pMapY ); + } + else + { + ImplTCToPAL( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel, + pSrcScan, pDstScan, pMapX, pMapY ); + } + + // cleanup + delete[] pSrcScan; + delete[] pDstScan; + delete[] pMapX; + delete[] pMapY; + + return pDstBuffer; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/salnativewidgets-none.cxx b/vcl/source/gdi/salnativewidgets-none.cxx new file mode 100644 index 000000000000..cc204069c2c4 --- /dev/null +++ b/vcl/source/gdi/salnativewidgets-none.cxx @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <vcl/salgdi.hxx> + +using ::rtl::OUString; + +/**************************************************************** + * Placeholder for no native widgets + ***************************************************************/ + + +/* + * IsNativeControlSupported() + * + * Returns sal_True if the platform supports native + * drawing of the control defined by nPart + */ +sal_Bool SalGraphics::IsNativeControlSupported( ControlType, ControlPart ) +{ + return( sal_False ); +} + + +/* + * HitTestNativeControl() + * + * If the return value is sal_True, bIsInside contains information whether + * aPos was or was not inside the native widget specified by the + * nType/nPart combination. + */ +sal_Bool SalGraphics::hitTestNativeControl( ControlType, + ControlPart, + const Rectangle&, + const Point&, + sal_Bool& ) +{ + return( sal_False ); +} + + +/* + * DrawNativeControl() + * + * Draws the requested control described by nPart/nState. + * + * rControlRegion: The bounding region of the complete control in VCL frame coordinates. + * aValue: An optional value (tristate/numerical/string) + * aCaption: A caption or title string (like button text etc) + */ +sal_Bool SalGraphics::drawNativeControl( ControlType, + ControlPart, + const Rectangle&, + ControlState, + const ImplControlValue&, + const OUString& ) +{ + return( sal_False ); +} + + +/* + * DrawNativeControlText() + * + * OPTIONAL. Draws the requested text for the control described by nPart/nState. + * Used if text not drawn by DrawNativeControl(). + * + * rControlRegion: The bounding region of the complete control in VCL frame coordinates. + * aValue: An optional value (tristate/numerical/string) + * aCaption: A caption or title string (like button text etc) + */ +sal_Bool SalGraphics::drawNativeControlText( ControlType, + ControlPart, + const Rectangle&, + ControlState, + const ImplControlValue&, + const OUString& ) +{ + return( sal_False ); +} + + +/* + * GetNativeControlRegion() + * + * If the return value is sal_True, rNativeBoundingRegion + * contains the sal_True bounding region covered by the control + * including any adornment, while rNativeContentRegion contains the area + * within the control that can be safely drawn into without drawing over + * the borders of the control. + * + * rControlRegion: The bounding region of the control in VCL frame coordinates. + * aValue: An optional value (tristate/numerical/string) + * aCaption: A caption or title string (like button text etc) + */ +sal_Bool SalGraphics::getNativeControlRegion( ControlType, + ControlPart, + const Rectangle&, + ControlState, + const ImplControlValue&, + const OUString&, + Rectangle &, + Rectangle & ) +{ + return( sal_False ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/svcompat.cxx b/vcl/source/gdi/svcompat.cxx new file mode 100644 index 000000000000..c594dd1f93d7 --- /dev/null +++ b/vcl/source/gdi/svcompat.cxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/stream.hxx> +#include <svcompat.hxx> + +// -------------- +// - ImplCompat - +// -------------- + +ImplCompat::ImplCompat( SvStream& rStm, sal_uInt16 nStreamMode, sal_uInt16 nVersion ) : + mpRWStm ( &rStm ), + mnStmMode ( nStreamMode ), + mnVersion ( nVersion ) +{ + if( !mpRWStm->GetError() ) + { + if( STREAM_WRITE == mnStmMode ) + { + *mpRWStm << mnVersion; + mnTotalSize = ( mnCompatPos = mpRWStm->Tell() ) + 4UL; + mpRWStm->SeekRel( 4L ); + } + else + { + *mpRWStm >> mnVersion; + *mpRWStm >> mnTotalSize; + mnCompatPos = mpRWStm->Tell(); + } + } +} + +// ------------------------------------------------------------------------ + +ImplCompat::~ImplCompat() +{ + if( STREAM_WRITE == mnStmMode ) + { + const sal_uInt32 nEndPos = mpRWStm->Tell(); + + mpRWStm->Seek( mnCompatPos ); + *mpRWStm << ( nEndPos - mnTotalSize ); + mpRWStm->Seek( nEndPos ); + } + else + { + const sal_uInt32 nReadSize = mpRWStm->Tell() - mnCompatPos; + + if( mnTotalSize > nReadSize ) + mpRWStm->SeekRel( mnTotalSize - nReadSize ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/textlayout.cxx b/vcl/source/gdi/textlayout.cxx new file mode 100644 index 000000000000..1afa8840fcf3 --- /dev/null +++ b/vcl/source/gdi/textlayout.cxx @@ -0,0 +1,389 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ***********************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "vcl/ctrl.hxx" +#include "vcl/outdev.hxx" +#include "vcl/outfont.hxx" +#include "vcl/textlayout.hxx" + +#include <com/sun/star/i18n/ScriptDirection.hpp> + +#include <tools/diagnose_ex.h> + +#if OSL_DEBUG_LEVEL > 1 +#include <rtl/strbuf.hxx> +#endif + +//........................................................................ +namespace vcl +{ +//........................................................................ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Exception; + namespace ScriptDirection = ::com::sun::star::i18n::ScriptDirection; + + //==================================================================== + //= DefaultTextLayout + //==================================================================== + //-------------------------------------------------------------------- + DefaultTextLayout::~DefaultTextLayout() + { + } + + //-------------------------------------------------------------------- + long DefaultTextLayout::GetTextWidth( const XubString& _rText, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const + { + return m_rTargetDevice.GetTextWidth( _rText, _nStartIndex, _nLength ); + } + + //-------------------------------------------------------------------- + void DefaultTextLayout::DrawText( const Point& _rStartPoint, const XubString& _rText, xub_StrLen _nStartIndex, + xub_StrLen _nLength, MetricVector* _pVector, String* _pDisplayText ) + { + m_rTargetDevice.DrawText( _rStartPoint, _rText, _nStartIndex, _nLength, _pVector, _pDisplayText ); + } + + //-------------------------------------------------------------------- + bool DefaultTextLayout::GetCaretPositions( const XubString& _rText, sal_Int32* _pCaretXArray, + xub_StrLen _nStartIndex, xub_StrLen _nLength ) const + { + return m_rTargetDevice.GetCaretPositions( _rText, _pCaretXArray, _nStartIndex, _nLength ); + } + + //-------------------------------------------------------------------- + xub_StrLen DefaultTextLayout::GetTextBreak( const XubString& _rText, long _nMaxTextWidth, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const + { + return m_rTargetDevice.GetTextBreak( _rText, _nMaxTextWidth, _nStartIndex, _nLength ); + } + + //-------------------------------------------------------------------- + bool DefaultTextLayout::DecomposeTextRectAction() const + { + return false; + } + + //==================================================================== + //= ReferenceDeviceTextLayout + //==================================================================== + class ReferenceDeviceTextLayout : public ITextLayout + { + public: + ReferenceDeviceTextLayout( const Control& _rControl, OutputDevice& _rTargetDevice, OutputDevice& _rReferenceDevice ); + virtual ~ReferenceDeviceTextLayout(); + + // ITextLayout + virtual long GetTextWidth( const XubString& rStr, xub_StrLen nIndex, xub_StrLen nLen ) const; + virtual void DrawText( const Point& _rStartPoint, const XubString& _rText, xub_StrLen _nStartIndex, xub_StrLen _nLength, MetricVector* _pVector, String* _pDisplayText ); + virtual bool GetCaretPositions( const XubString& _rText, sal_Int32* _pCaretXArray, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const; + virtual xub_StrLen GetTextBreak( const XubString& _rText, long _nMaxTextWidth, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const; + virtual bool DecomposeTextRectAction() const; + + public: + // equivalents to the respective OutputDevice methods, which take the reference device into account + long GetTextArray( const XubString& _rText, sal_Int32* _pDXAry, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const; + Rectangle DrawText( const Rectangle& _rRect, const XubString& _rText, sal_uInt16 _nStyle, MetricVector* _pVector, String* _pDisplayText ); + + protected: + void onBeginDrawText() + { + m_aCompleteTextRect.SetEmpty(); + } + Rectangle onEndDrawText() + { + return m_aCompleteTextRect; + } + + private: + OutputDevice& m_rTargetDevice; + OutputDevice& m_rReferenceDevice; + Font m_aUnzoomedPointFont; + const Fraction m_aZoom; + const bool m_bRTLEnabled; + + Rectangle m_aCompleteTextRect; + }; + + //==================================================================== + //= ControlTextRenderer + //==================================================================== + ReferenceDeviceTextLayout::ReferenceDeviceTextLayout( const Control& _rControl, OutputDevice& _rTargetDevice, + OutputDevice& _rReferenceDevice ) + :m_rTargetDevice( _rTargetDevice ) + ,m_rReferenceDevice( _rReferenceDevice ) + ,m_aUnzoomedPointFont( _rControl.GetUnzoomedControlPointFont() ) + ,m_aZoom( _rControl.GetZoom() ) + ,m_bRTLEnabled( _rControl.IsRTLEnabled() ) + { + m_rTargetDevice.Push( PUSH_MAPMODE | PUSH_FONT | PUSH_TEXTLAYOUTMODE ); + + MapMode aTargetMapMode( m_rTargetDevice.GetMapMode() ); + OSL_ENSURE( aTargetMapMode.GetOrigin() == Point(), "ReferenceDeviceTextLayout::ReferenceDeviceTextLayout: uhm, the code below won't work here ..." ); + + // normally, controls simulate "zoom" by "zooming" the font. This is responsible for (part of) the discrepancies + // between text in Writer and text in controls in Writer, though both have the same font. + // So, if we have a zoom set at the control, then we do not scale the font, but instead modify the map mode + // to accomodate for the zoom. + aTargetMapMode.SetScaleX( m_aZoom ); // TODO: shouldn't this be "current_scale * zoom"? + aTargetMapMode.SetScaleY( m_aZoom ); + + // also, use a higher-resolution map unit than "pixels", which should save us some rounding errors when + // translating coordinates between the reference device and the target device. + OSL_ENSURE( aTargetMapMode.GetMapUnit() == MAP_PIXEL, + "ReferenceDeviceTextLayout::ReferenceDeviceTextLayout: this class is not expected to work with such target devices!" ); + // we *could* adjust all the code in this class to handle this case, but at the moment, it's not necessary + const MapUnit eTargetMapUnit = m_rReferenceDevice.GetMapMode().GetMapUnit(); + aTargetMapMode.SetMapUnit( eTargetMapUnit ); + OSL_ENSURE( aTargetMapMode.GetMapUnit() != MAP_PIXEL, + "ReferenceDeviceTextLayout::ReferenceDeviceTextLayout: a reference device which has map mode PIXEL?!" ); + + m_rTargetDevice.SetMapMode( aTargetMapMode ); + + // now that the Zoom is part of the map mode, reset the target device's font to the "unzoomed" version + Font aDrawFont( m_aUnzoomedPointFont ); + aDrawFont.SetSize( m_rTargetDevice.LogicToLogic( aDrawFont.GetSize(), MAP_POINT, eTargetMapUnit ) ); + _rTargetDevice.SetFont( aDrawFont ); + + // transfer font to the reference device + m_rReferenceDevice.Push( PUSH_FONT | PUSH_TEXTLAYOUTMODE ); + Font aRefFont( m_aUnzoomedPointFont ); + aRefFont.SetSize( OutputDevice::LogicToLogic( + aRefFont.GetSize(), MAP_POINT, m_rReferenceDevice.GetMapMode().GetMapUnit() ) ); + m_rReferenceDevice.SetFont( aRefFont ); + } + + //-------------------------------------------------------------------- + ReferenceDeviceTextLayout::~ReferenceDeviceTextLayout() + { + m_rReferenceDevice.Pop(); + m_rTargetDevice.Pop(); + } + + //-------------------------------------------------------------------- + namespace + { + //................................................................ + bool lcl_normalizeLength( const XubString& _rText, const xub_StrLen _nStartIndex, xub_StrLen& _io_nLength ) + { + xub_StrLen nTextLength = _rText.Len(); + if ( _nStartIndex > nTextLength ) + return false; + if ( _nStartIndex + _io_nLength > nTextLength ) + _io_nLength = nTextLength - _nStartIndex; + return true; + } + } + + //-------------------------------------------------------------------- + long ReferenceDeviceTextLayout::GetTextArray( const XubString& _rText, sal_Int32* _pDXAry, xub_StrLen _nStartIndex, + xub_StrLen _nLength ) const + { + if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) ) + return 0; + + // retrieve the character widths from the reference device + long nTextWidth = m_rReferenceDevice.GetTextArray( _rText, _pDXAry, _nStartIndex, _nLength ); +#if OSL_DEBUG_LEVEL > 1 + if ( _pDXAry ) + { + ::rtl::OStringBuffer aTrace; + aTrace.append( "ReferenceDeviceTextLayout::GetTextArray( " ); + aTrace.append( ::rtl::OUStringToOString( _rText, RTL_TEXTENCODING_UTF8 ) ); + aTrace.append( " ): " ); + aTrace.append( nTextWidth ); + aTrace.append( " = ( " ); + for ( size_t i=0; i<_nLength; ) + { + aTrace.append( _pDXAry[i] ); + if ( ++i < _nLength ) + aTrace.append( ", " ); + } + aTrace.append( ")" ); + OSL_TRACE( "%s", aTrace.makeStringAndClear().getStr() ); + } +#endif + return nTextWidth; + } + + //-------------------------------------------------------------------- + long ReferenceDeviceTextLayout::GetTextWidth( const XubString& _rText, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const + { + return GetTextArray( _rText, NULL, _nStartIndex, _nLength ); + } + + //-------------------------------------------------------------------- + void ReferenceDeviceTextLayout::DrawText( const Point& _rStartPoint, const XubString& _rText, xub_StrLen _nStartIndex, xub_StrLen _nLength, MetricVector* _pVector, String* _pDisplayText ) + { + if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) ) + return; + + if ( _pVector && _pDisplayText ) + { + MetricVector aGlyphBounds; + m_rReferenceDevice.GetGlyphBoundRects( _rStartPoint, _rText, _nStartIndex, _nLength, _nStartIndex, aGlyphBounds ); + ::std::copy( + aGlyphBounds.begin(), aGlyphBounds.end(), + ::std::insert_iterator< MetricVector > ( *_pVector, _pVector->end() ) ); + _pDisplayText->Append( _rText.Copy( _nStartIndex, _nLength ) ); + return; + } + + sal_Int32* pCharWidths = new sal_Int32[ _nLength ]; + long nTextWidth = GetTextArray( _rText, pCharWidths, _nStartIndex, _nLength ); + m_rTargetDevice.DrawTextArray( _rStartPoint, _rText, pCharWidths, _nStartIndex, _nLength ); + delete[] pCharWidths; + + m_aCompleteTextRect.Union( Rectangle( _rStartPoint, Size( nTextWidth, m_rTargetDevice.GetTextHeight() ) ) ); + } + + //-------------------------------------------------------------------- + bool ReferenceDeviceTextLayout::GetCaretPositions( const XubString& _rText, sal_Int32* _pCaretXArray, + xub_StrLen _nStartIndex, xub_StrLen _nLength ) const + { + if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) ) + return false; + + // retrieve the caret positions from the reference device + if ( !m_rReferenceDevice.GetCaretPositions( _rText, _pCaretXArray, _nStartIndex, _nLength ) ) + return false; + + return true; + } + + //-------------------------------------------------------------------- + xub_StrLen ReferenceDeviceTextLayout::GetTextBreak( const XubString& _rText, long _nMaxTextWidth, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const + { + if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) ) + return 0; + + return m_rReferenceDevice.GetTextBreak( _rText, _nMaxTextWidth, _nStartIndex, _nLength ); + } + + //-------------------------------------------------------------------- + bool ReferenceDeviceTextLayout::DecomposeTextRectAction() const + { + return true; + } + + //-------------------------------------------------------------------- + namespace + { + long zoomBy( long _value, const Fraction& _zoom ) + { + double n = (double)_value; + n *= (double)_zoom.GetNumerator(); + n /= (double)_zoom.GetDenominator(); + return (long)::rtl::math::round( n ); + } + long unzoomBy( long _value, const Fraction& _zoom ) + { + return zoomBy( _value, Fraction( _zoom.GetDenominator(), _zoom.GetNumerator() ) ); + } + } + + //-------------------------------------------------------------------- + Rectangle ReferenceDeviceTextLayout::DrawText( const Rectangle& _rRect, const XubString& _rText, sal_uInt16 _nStyle, MetricVector* _pVector, String* _pDisplayText ) + { + if ( !_rText.Len() ) + return Rectangle(); + + // determine text layout mode from the RTL-ness of the control whose text we render + sal_uLong nTextLayoutMode = m_bRTLEnabled ? TEXT_LAYOUT_BIDI_RTL : TEXT_LAYOUT_BIDI_LTR; + m_rReferenceDevice.SetLayoutMode( nTextLayoutMode ); + m_rTargetDevice.SetLayoutMode( nTextLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT ); + // TEXT_LAYOUT_TEXTORIGIN_LEFT is because when we do actually draw the text (in DrawText( Point, ... )), then + // our caller gives us the left border of the draw position, regardless of script type, text layout, + // and the like + + // in our ctor, we set the map mode of the target device from pixel to twip, but our caller doesn't know this, + // but passed pixel coordinates. So, adjust the rect. + Rectangle aRect( m_rTargetDevice.PixelToLogic( _rRect ) ); + + onBeginDrawText(); + m_rTargetDevice.DrawText( aRect, _rText, _nStyle, _pVector, _pDisplayText, this ); + Rectangle aTextRect = onEndDrawText(); + + if ( aTextRect.IsEmpty() && !aRect.IsEmpty() ) + { + // this happens for instance if we're in a PaintToDevice call, where only a MetaFile is recorded, + // but no actual painting happens, so our "DrawText( Point, ... )" is never called + // In this case, calculate the rect from what OutputDevice::GetTextRect would give us. This has + // the disadvantage of less accuracy, compared with the approach to calculate the rect from the + // single "DrawText( Point, ... )" calls, since more intermediate arithmetics will translate + // from ref- to target-units. + aTextRect = m_rTargetDevice.GetTextRect( aRect, _rText, _nStyle, NULL, this ); + } + + // similar to above, the text rect now contains TWIPs (or whatever unit the ref device has), but the caller + // expects pixel coordinates + aTextRect = m_rTargetDevice.LogicToPixel( aTextRect ); + + // convert the metric vector + if ( _pVector ) + { + for ( MetricVector::iterator charRect = _pVector->begin(); + charRect != _pVector->end(); + ++charRect + ) + { + *charRect = m_rTargetDevice.LogicToPixel( *charRect ); + } + } + + return aTextRect; + } + + //==================================================================== + //= ControlTextRenderer + //==================================================================== + //-------------------------------------------------------------------- + ControlTextRenderer::ControlTextRenderer( const Control& _rControl, OutputDevice& _rTargetDevice, OutputDevice& _rReferenceDevice ) + :m_pImpl( new ReferenceDeviceTextLayout( _rControl, _rTargetDevice, _rReferenceDevice ) ) + { + } + + //-------------------------------------------------------------------- + ControlTextRenderer::~ControlTextRenderer() + { + } + + //-------------------------------------------------------------------- + Rectangle ControlTextRenderer::DrawText( const Rectangle& _rRect, const XubString& _rText, sal_uInt16 _nStyle, + MetricVector* _pVector, String* _pDisplayText ) + { + return m_pImpl->DrawText( _rRect, _rText, _nStyle, _pVector, _pDisplayText ); + } + +//........................................................................ +} // namespace vcl +//........................................................................ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/virdev.cxx b/vcl/source/gdi/virdev.cxx new file mode 100644 index 000000000000..981fc38b7375 --- /dev/null +++ b/vcl/source/gdi/virdev.cxx @@ -0,0 +1,449 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <svsys.h> +#include <vcl/salinst.hxx> +#include <vcl/salgdi.hxx> +#include <vcl/salframe.hxx> +#include <vcl/salvd.hxx> +#include <tools/debug.hxx> +#include <vcl/svdata.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/outdev.h> +#include <vcl/virdev.hxx> + +using namespace ::com::sun::star::uno; + +// ======================================================================= + +void VirtualDevice::ImplInitVirDev( const OutputDevice* pOutDev, + long nDX, long nDY, sal_uInt16 nBitCount, const SystemGraphicsData *pData ) +{ + DBG_ASSERT( nBitCount <= 1, + "VirtualDevice::VirtualDevice(): Only 0 or 1 is for BitCount allowed" ); + + if ( nDX < 1 ) + nDX = 1; + + if ( nDY < 1 ) + nDY = 1; + + ImplSVData* pSVData = ImplGetSVData(); + + if ( !pOutDev ) + pOutDev = ImplGetDefaultWindow(); + if( !pOutDev ) + return; + + SalGraphics* pGraphics; + if ( !pOutDev->mpGraphics ) + ((OutputDevice*)pOutDev)->ImplGetGraphics(); + pGraphics = pOutDev->mpGraphics; + if ( pGraphics ) + mpVirDev = pSVData->mpDefInst->CreateVirtualDevice( pGraphics, nDX, nDY, nBitCount, pData ); + else + mpVirDev = NULL; + if ( !mpVirDev ) + { + // do not abort but throw an exception, may be the current thread terminates anyway (plugin-scenario) + throw ::com::sun::star::uno::RuntimeException( + OUString( RTL_CONSTASCII_USTRINGPARAM( "Could not create system bitmap!" ) ), + ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >() ); + //GetpApp()->Exception( EXC_SYSOBJNOTCREATED ); + } + + mnBitCount = ( nBitCount ? nBitCount : pOutDev->GetBitCount() ); + mnOutWidth = nDX; + mnOutHeight = nDY; + mbScreenComp = sal_True; + mnAlphaDepth = -1; + + // #i59315# init vdev size from system object, when passed a + // SystemGraphicsData. Otherwise, output size will always + // incorrectly stay at (1,1) + if( pData && mpVirDev ) + mpVirDev->GetSize(mnOutWidth,mnOutHeight); + + if( mnBitCount < 8 ) + SetAntialiasing( ANTIALIASING_DISABLE_TEXT ); + + if ( pOutDev->GetOutDevType() == OUTDEV_PRINTER ) + mbScreenComp = sal_False; + else if ( pOutDev->GetOutDevType() == OUTDEV_VIRDEV ) + mbScreenComp = ((VirtualDevice*)pOutDev)->mbScreenComp; + + meOutDevType = OUTDEV_VIRDEV; + mbDevOutput = sal_True; + mpFontList = pSVData->maGDIData.mpScreenFontList; + mpFontCache = pSVData->maGDIData.mpScreenFontCache; + mnDPIX = pOutDev->mnDPIX; + mnDPIY = pOutDev->mnDPIY; + maFont = pOutDev->maFont; + + if( maTextColor != pOutDev->maTextColor ) + { + maTextColor = pOutDev->maTextColor; + mbInitTextColor = true; + } + + // Virtuelle Devices haben defaultmaessig einen weissen Hintergrund + SetBackground( Wallpaper( Color( COL_WHITE ) ) ); + + // #i59283# don't erase user-provided surface + if( !pData ) + Erase(); + + // VirDev in Liste eintragen + mpNext = pSVData->maGDIData.mpFirstVirDev; + mpPrev = NULL; + if ( mpNext ) + mpNext->mpPrev = this; + else + pSVData->maGDIData.mpLastVirDev = this; + pSVData->maGDIData.mpFirstVirDev = this; +} + +// ----------------------------------------------------------------------- + +VirtualDevice::VirtualDevice( sal_uInt16 nBitCount ) +: mpVirDev( NULL ), + meRefDevMode( REFDEV_NONE ) +{ + OSL_TRACE( "VirtualDevice::VirtualDevice( %hu )", nBitCount ); + + ImplInitVirDev( Application::GetDefaultDevice(), 1, 1, nBitCount ); +} + +// ----------------------------------------------------------------------- + +VirtualDevice::VirtualDevice( const OutputDevice& rCompDev, sal_uInt16 nBitCount ) + : mpVirDev( NULL ), + meRefDevMode( REFDEV_NONE ) +{ + OSL_TRACE( "VirtualDevice::VirtualDevice( %hu )", nBitCount ); + + ImplInitVirDev( &rCompDev, 1, 1, nBitCount ); +} + +// ----------------------------------------------------------------------- + +VirtualDevice::VirtualDevice( const OutputDevice& rCompDev, sal_uInt16 nBitCount, sal_uInt16 nAlphaBitCount ) + : mpVirDev( NULL ), + meRefDevMode( REFDEV_NONE ) +{ + OSL_TRACE( "VirtualDevice::VirtualDevice( %hu )", nBitCount ); + + ImplInitVirDev( &rCompDev, 1, 1, nBitCount ); + + // #110958# Enable alpha channel + mnAlphaDepth = sal::static_int_cast<sal_Int8>(nAlphaBitCount); +} + +// ----------------------------------------------------------------------- + +VirtualDevice::VirtualDevice( const SystemGraphicsData *pData, sal_uInt16 nBitCount ) +: mpVirDev( NULL ), + meRefDevMode( REFDEV_NONE ) +{ + OSL_TRACE( "VirtualDevice::VirtualDevice( %hu )", nBitCount ); + + ImplInitVirDev( Application::GetDefaultDevice(), 1, 1, nBitCount, pData ); +} + +// ----------------------------------------------------------------------- + +VirtualDevice::~VirtualDevice() +{ + OSL_TRACE( "VirtualDevice::~VirtualDevice()" ); + + ImplSVData* pSVData = ImplGetSVData(); + + ImplReleaseGraphics(); + + if ( mpVirDev ) + pSVData->mpDefInst->DestroyVirtualDevice( mpVirDev ); + + // remove this VirtualDevice from the double-linked global list + if( mpPrev ) + mpPrev->mpNext = mpNext; + else + pSVData->maGDIData.mpFirstVirDev = mpNext; + + if( mpNext ) + mpNext->mpPrev = mpPrev; + else + pSVData->maGDIData.mpLastVirDev = mpPrev; +} + +// ----------------------------------------------------------------------- + +sal_Bool VirtualDevice::ImplSetOutputSizePixel( const Size& rNewSize, sal_Bool bErase ) +{ + OSL_TRACE( "VirtualDevice::ImplSetOutputSizePixel( %ld, %ld, %d )", rNewSize.Width(), rNewSize.Height(), (int)bErase ); + + if ( !mpVirDev ) + return sal_False; + else if ( rNewSize == GetOutputSizePixel() ) + { + if ( bErase ) + Erase(); + return sal_True; + } + + sal_Bool bRet; + long nNewWidth = rNewSize.Width(), nNewHeight = rNewSize.Height(); + + if ( nNewWidth < 1 ) + nNewWidth = 1; + + if ( nNewHeight < 1 ) + nNewHeight = 1; + + if ( bErase ) + { + bRet = mpVirDev->SetSize( nNewWidth, nNewHeight ); + + if ( bRet ) + { + mnOutWidth = rNewSize.Width(); + mnOutHeight = rNewSize.Height(); + Erase(); + } + } + else + { + SalVirtualDevice* pNewVirDev; + ImplSVData* pSVData = ImplGetSVData(); + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return sal_False; + } + + pNewVirDev = pSVData->mpDefInst->CreateVirtualDevice( mpGraphics, nNewWidth, nNewHeight, mnBitCount ); + if ( pNewVirDev ) + { + SalGraphics* pGraphics = pNewVirDev->GetGraphics(); + if ( pGraphics ) + { + SalTwoRect aPosAry; + long nWidth; + long nHeight; + if ( mnOutWidth < nNewWidth ) + nWidth = mnOutWidth; + else + nWidth = nNewWidth; + if ( mnOutHeight < nNewHeight ) + nHeight = mnOutHeight; + else + nHeight = nNewHeight; + aPosAry.mnSrcX = 0; + aPosAry.mnSrcY = 0; + aPosAry.mnSrcWidth = nWidth; + aPosAry.mnSrcHeight = nHeight; + aPosAry.mnDestX = 0; + aPosAry.mnDestY = 0; + aPosAry.mnDestWidth = nWidth; + aPosAry.mnDestHeight = nHeight; + + pGraphics->CopyBits( &aPosAry, mpGraphics, this, this ); + pNewVirDev->ReleaseGraphics( pGraphics ); + ImplReleaseGraphics(); + pSVData->mpDefInst->DestroyVirtualDevice( mpVirDev ); + mpVirDev = pNewVirDev; + mnOutWidth = rNewSize.Width(); + mnOutHeight = rNewSize.Height(); + bRet = sal_True; + } + else + { + bRet = sal_False; + pSVData->mpDefInst->DestroyVirtualDevice( pNewVirDev ); + } + } + else + bRet = sal_False; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +// #i32109#: Fill opaque areas correctly (without relying on +// fill/linecolor state) +void VirtualDevice::ImplFillOpaqueRectangle( const Rectangle& rRect ) +{ + // Set line and fill color to black (->opaque), + // fill rect with that (linecolor, too, because of + // those pesky missing pixel problems) + Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + SetLineColor( COL_BLACK ); + SetFillColor( COL_BLACK ); + DrawRect( rRect ); + Pop(); +} + +// ----------------------------------------------------------------------- + +sal_Bool VirtualDevice::SetOutputSizePixel( const Size& rNewSize, sal_Bool bErase ) +{ + if( ImplSetOutputSizePixel(rNewSize, bErase) ) + { + if( mnAlphaDepth != -1 ) + { + // #110958# Setup alpha bitmap + if(mpAlphaVDev && mpAlphaVDev->GetOutputSizePixel() != rNewSize) + { + delete mpAlphaVDev; + mpAlphaVDev = 0L; + } + + if( !mpAlphaVDev ) + { + mpAlphaVDev = new VirtualDevice( *this, mnAlphaDepth ); + mpAlphaVDev->ImplSetOutputSizePixel(rNewSize, bErase); + } + + // TODO: copy full outdev state to new one, here. Also needed in outdev2.cxx:DrawOutDev + if( GetLineColor() != Color( COL_TRANSPARENT ) ) + mpAlphaVDev->SetLineColor( COL_BLACK ); + + if( GetFillColor() != Color( COL_TRANSPARENT ) ) + mpAlphaVDev->SetFillColor( COL_BLACK ); + + mpAlphaVDev->SetMapMode( GetMapMode() ); + } + + return sal_True; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +void VirtualDevice::SetReferenceDevice( RefDevMode i_eRefDevMode ) +{ + sal_Int32 nDPIX = 600, nDPIY = 600; + switch( i_eRefDevMode ) + { + case REFDEV_NONE: + default: + DBG_ASSERT( sal_False, "VDev::SetRefDev illegal argument!" ); + break; + case REFDEV_MODE06: + nDPIX = nDPIY = 600; + break; + case REFDEV_MODE48: + nDPIX = nDPIY = 4800; + break; + case REFDEV_MODE_MSO1: + nDPIX = nDPIY = 6*1440; + break; + case REFDEV_MODE_PDF1: + nDPIX = nDPIY = 720; + break; + } + ImplSetReferenceDevice( i_eRefDevMode, nDPIX, nDPIY ); +} + +void VirtualDevice::SetReferenceDevice( sal_Int32 i_nDPIX, sal_Int32 i_nDPIY ) +{ + ImplSetReferenceDevice( REFDEV_CUSTOM, i_nDPIX, i_nDPIY ); +} + +void VirtualDevice::ImplSetReferenceDevice( RefDevMode i_eRefDevMode, sal_Int32 i_nDPIX, sal_Int32 i_nDPIY ) +{ + mnDPIX = i_nDPIX; + mnDPIY = i_nDPIY; + + EnableOutput( sal_False ); // prevent output on reference device + mbScreenComp = sal_False; + + // invalidate currently selected fonts + mbInitFont = sal_True; + mbNewFont = sal_True; + + // avoid adjusting font lists when already in refdev mode + sal_uInt8 nOldRefDevMode = meRefDevMode; + sal_uInt8 nOldCompatFlag = (sal_uInt8)meRefDevMode & REFDEV_FORCE_ZERO_EXTLEAD; + meRefDevMode = (sal_uInt8)(i_eRefDevMode | nOldCompatFlag); + if( (nOldRefDevMode ^ nOldCompatFlag) != REFDEV_NONE ) + return; + + // the reference device should have only scalable fonts + // => clean up the original font lists before getting new ones + if ( mpFontEntry ) + { + mpFontCache->Release( mpFontEntry ); + mpFontEntry = NULL; + } + if ( mpGetDevFontList ) + { + delete mpGetDevFontList; + mpGetDevFontList = NULL; + } + if ( mpGetDevSizeList ) + { + delete mpGetDevSizeList; + mpGetDevSizeList = NULL; + } + + // preserve global font lists + ImplSVData* pSVData = ImplGetSVData(); + if( mpFontList && (mpFontList != pSVData->maGDIData.mpScreenFontList) ) + delete mpFontList; + if( mpFontCache && (mpFontCache != pSVData->maGDIData.mpScreenFontCache) ) + delete mpFontCache; + + // get font list with scalable fonts only + ImplGetGraphics(); + mpFontList = pSVData->maGDIData.mpScreenFontList->Clone( true, false ); + + // prepare to use new font lists + mpFontCache = new ImplFontCache( false ); +} + +// ----------------------------------------------------------------------- + +void VirtualDevice::Compat_ZeroExtleadBug() +{ + meRefDevMode = (sal_uInt8)meRefDevMode | REFDEV_FORCE_ZERO_EXTLEAD; +} + +// ----------------------------------------------------------------------- + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/wall.cxx b/vcl/source/gdi/wall.cxx new file mode 100644 index 000000000000..600f78941cbe --- /dev/null +++ b/vcl/source/gdi/wall.cxx @@ -0,0 +1,642 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <tools/debug.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/gradient.hxx> +#include <vcl/wall.hxx> +#include <vcl/wall2.hxx> +#include <vcl/svapp.hxx> + + +DBG_NAME( Wallpaper ); + +// ----------------------------------------------------------------------- + +ImplWallpaper::ImplWallpaper() : + maColor( COL_TRANSPARENT ) +{ + mnRefCount = 1; + mpBitmap = NULL; + mpCache = NULL; + mpGradient = NULL; + mpRect = NULL; + meStyle = WALLPAPER_NULL; +} + +// ----------------------------------------------------------------------- + +ImplWallpaper::ImplWallpaper( const ImplWallpaper& rImplWallpaper ) : + maColor( rImplWallpaper.maColor ) +{ + mnRefCount = 1; + meStyle = rImplWallpaper.meStyle; + + if ( rImplWallpaper.mpBitmap ) + mpBitmap = new BitmapEx( *rImplWallpaper.mpBitmap ); + else + mpBitmap = NULL; + if( rImplWallpaper.mpCache ) + mpCache = new BitmapEx( *rImplWallpaper.mpCache ); + else + mpCache = NULL; + if ( rImplWallpaper.mpGradient ) + mpGradient = new Gradient( *rImplWallpaper.mpGradient ); + else + mpGradient = NULL; + if ( rImplWallpaper.mpRect ) + mpRect = new Rectangle( *rImplWallpaper.mpRect ); + else + mpRect = NULL; +} + +// ----------------------------------------------------------------------- + +ImplWallpaper::~ImplWallpaper() +{ + delete mpBitmap; + delete mpCache; + delete mpGradient; + delete mpRect; +} + +// ----------------------------------------------------------------------- + +void ImplWallpaper::ImplSetCachedBitmap( BitmapEx& rBmp ) +{ + if( !mpCache ) + mpCache = new BitmapEx( rBmp ); + else + *mpCache = rBmp; +} + +// ----------------------------------------------------------------------- + +void ImplWallpaper::ImplReleaseCachedBitmap() +{ + delete mpCache; + mpCache = NULL; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, ImplWallpaper& rImplWallpaper ) +{ + VersionCompat aCompat( rIStm, STREAM_READ ); + sal_uInt16 nTmp16; + + delete rImplWallpaper.mpRect; + rImplWallpaper.mpRect = NULL; + + delete rImplWallpaper.mpGradient; + rImplWallpaper.mpGradient = NULL; + + delete rImplWallpaper.mpBitmap; + rImplWallpaper.mpBitmap = NULL; + + // version 1 + rIStm >> rImplWallpaper.maColor; + rIStm >> nTmp16; rImplWallpaper.meStyle = (WallpaperStyle) nTmp16; + + // version 2 + if( aCompat.GetVersion() >= 2 ) + { + sal_Bool bRect, bGrad, bBmp, bDummy; + + rIStm >> bRect >> bGrad >> bBmp >> bDummy >> bDummy >> bDummy; + + if( bRect ) + { + rImplWallpaper.mpRect = new Rectangle; + rIStm >> *rImplWallpaper.mpRect; + } + + if( bGrad ) + { + rImplWallpaper.mpGradient = new Gradient; + rIStm >> *rImplWallpaper.mpGradient; + } + + if( bBmp ) + { + rImplWallpaper.mpBitmap = new BitmapEx; + rIStm >> *rImplWallpaper.mpBitmap; + } + + // version 3 (new color format) + if( aCompat.GetVersion() >= 3 ) + { + rImplWallpaper.maColor.Read( rIStm, sal_True ); + } + } + + return rIStm; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const ImplWallpaper& rImplWallpaper ) +{ + VersionCompat aCompat( rOStm, STREAM_WRITE, 3 ); + sal_Bool bRect = ( rImplWallpaper.mpRect != NULL ); + sal_Bool bGrad = ( rImplWallpaper.mpGradient != NULL ); + sal_Bool bBmp = ( rImplWallpaper.mpBitmap != NULL ); + sal_Bool bDummy = sal_False; + + // version 1 + rOStm << rImplWallpaper.maColor << (sal_uInt16) rImplWallpaper.meStyle; + + // version 2 + rOStm << bRect << bGrad << bBmp << bDummy << bDummy << bDummy; + + if( bRect ) + rOStm << *rImplWallpaper.mpRect; + + if( bGrad ) + rOStm << *rImplWallpaper.mpGradient; + + if( bBmp ) + rOStm << *rImplWallpaper.mpBitmap; + + // version 3 (new color format) + ( (Color&) rImplWallpaper.maColor ).Write( rOStm, sal_True ); + + return rOStm; +} + +// ----------------------------------------------------------------------- + +inline void Wallpaper::ImplMakeUnique( sal_Bool bReleaseCache ) +{ + // Falls noch andere Referenzen bestehen, dann kopieren + if ( mpImplWallpaper->mnRefCount != 1 ) + { + if ( mpImplWallpaper->mnRefCount ) + mpImplWallpaper->mnRefCount--; + mpImplWallpaper = new ImplWallpaper( *(mpImplWallpaper) ); + } + + if( bReleaseCache ) + mpImplWallpaper->ImplReleaseCachedBitmap(); +} + +// ----------------------------------------------------------------------- + +Wallpaper::Wallpaper() +{ + DBG_CTOR( Wallpaper, NULL ); + + static ImplWallpaper aStaticImplWallpaper; + + aStaticImplWallpaper.mnRefCount = 0; + mpImplWallpaper = &aStaticImplWallpaper; +} + +// ----------------------------------------------------------------------- + +Wallpaper::Wallpaper( const Wallpaper& rWallpaper ) +{ + DBG_CTOR( Wallpaper, NULL ); + DBG_CHKOBJ( &rWallpaper, Wallpaper, NULL ); + DBG_ASSERT( rWallpaper.mpImplWallpaper->mnRefCount < 0xFFFFFFFE, "Wallpaper: RefCount overflow" ); + + // Instance Daten uebernehmen und Referenzcounter erhoehen + mpImplWallpaper = rWallpaper.mpImplWallpaper; + // RefCount == 0 fuer statische Objekte + if ( mpImplWallpaper->mnRefCount ) + mpImplWallpaper->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +Wallpaper::Wallpaper( const Color& rColor ) +{ + DBG_CTOR( Wallpaper, NULL ); + + mpImplWallpaper = new ImplWallpaper; + mpImplWallpaper->maColor = rColor; + mpImplWallpaper->meStyle = WALLPAPER_TILE; +} + +// ----------------------------------------------------------------------- + +Wallpaper::Wallpaper( const BitmapEx& rBmpEx ) +{ + DBG_CTOR( Wallpaper, NULL ); + + mpImplWallpaper = new ImplWallpaper; + mpImplWallpaper->mpBitmap = new BitmapEx( rBmpEx ); + mpImplWallpaper->meStyle = WALLPAPER_TILE; +} + +// ----------------------------------------------------------------------- + +Wallpaper::Wallpaper( const Gradient& rGradient ) +{ + DBG_CTOR( Wallpaper, NULL ); + + mpImplWallpaper = new ImplWallpaper; + mpImplWallpaper->mpGradient = new Gradient( rGradient ); + mpImplWallpaper->meStyle = WALLPAPER_TILE; +} + +// ----------------------------------------------------------------------- + +Wallpaper::~Wallpaper() +{ + DBG_DTOR( Wallpaper, NULL ); + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplWallpaper->mnRefCount ) + { + if ( mpImplWallpaper->mnRefCount == 1 ) + delete mpImplWallpaper; + else + mpImplWallpaper->mnRefCount--; + } +} + +// ----------------------------------------------------------------------- + +void Wallpaper::SetColor( const Color& rColor ) +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + ImplMakeUnique(); + mpImplWallpaper->maColor = rColor; + + if( WALLPAPER_NULL == mpImplWallpaper->meStyle || WALLPAPER_APPLICATIONGRADIENT == mpImplWallpaper->meStyle ) + mpImplWallpaper->meStyle = WALLPAPER_TILE; +} + +// ----------------------------------------------------------------------- + +const Color& Wallpaper::GetColor() const +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + return mpImplWallpaper->maColor; +} + +// ----------------------------------------------------------------------- + +void Wallpaper::SetStyle( WallpaperStyle eStyle ) +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + ImplMakeUnique( sal_False ); + + if( eStyle == WALLPAPER_APPLICATIONGRADIENT ) + // set a dummy gradient, the correct gradient + // will be created dynamically in GetGradient() + SetGradient( ImplGetApplicationGradient() ); + + mpImplWallpaper->meStyle = eStyle; +} + +// ----------------------------------------------------------------------- + +WallpaperStyle Wallpaper::GetStyle() const +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + return mpImplWallpaper->meStyle; +} + +// ----------------------------------------------------------------------- + +void Wallpaper::SetBitmap( const BitmapEx& rBitmap ) +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + if ( !rBitmap ) + { + if ( mpImplWallpaper->mpBitmap ) + { + ImplMakeUnique(); + delete mpImplWallpaper->mpBitmap; + mpImplWallpaper->mpBitmap = NULL; + } + } + else + { + ImplMakeUnique(); + if ( mpImplWallpaper->mpBitmap ) + *(mpImplWallpaper->mpBitmap) = rBitmap; + else + mpImplWallpaper->mpBitmap = new BitmapEx( rBitmap ); + } + + if( WALLPAPER_NULL == mpImplWallpaper->meStyle || WALLPAPER_APPLICATIONGRADIENT == mpImplWallpaper->meStyle) + mpImplWallpaper->meStyle = WALLPAPER_TILE; +} + +// ----------------------------------------------------------------------- + +void Wallpaper::SetBitmap() +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + if ( mpImplWallpaper->mpBitmap ) + { + ImplMakeUnique(); + delete mpImplWallpaper->mpBitmap; + mpImplWallpaper->mpBitmap = NULL; + } +} + +// ----------------------------------------------------------------------- + +BitmapEx Wallpaper::GetBitmap() const +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + if ( mpImplWallpaper->mpBitmap ) + return *(mpImplWallpaper->mpBitmap); + else + { + BitmapEx aBmp; + return aBmp; + } +} + +// ----------------------------------------------------------------------- + +sal_Bool Wallpaper::IsBitmap() const +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + return (mpImplWallpaper->mpBitmap != 0); +} + + +// ----------------------------------------------------------------------- + +void Wallpaper::SetGradient( const Gradient& rGradient ) +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + ImplMakeUnique(); + + if ( mpImplWallpaper->mpGradient ) + *(mpImplWallpaper->mpGradient) = rGradient; + else + mpImplWallpaper->mpGradient = new Gradient( rGradient ); + + if( WALLPAPER_NULL == mpImplWallpaper->meStyle || WALLPAPER_APPLICATIONGRADIENT == mpImplWallpaper->meStyle ) + mpImplWallpaper->meStyle = WALLPAPER_TILE; +} + +// ----------------------------------------------------------------------- + +void Wallpaper::SetGradient() +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + if ( mpImplWallpaper->mpGradient ) + { + ImplMakeUnique(); + delete mpImplWallpaper->mpGradient; + mpImplWallpaper->mpGradient = NULL; + } +} + +// ----------------------------------------------------------------------- + +Gradient Wallpaper::GetGradient() const +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + if( WALLPAPER_APPLICATIONGRADIENT == mpImplWallpaper->meStyle ) + return ImplGetApplicationGradient(); + else if ( mpImplWallpaper->mpGradient ) + return *(mpImplWallpaper->mpGradient); + else + { + Gradient aGradient; + return aGradient; + } +} + +// ----------------------------------------------------------------------- + +sal_Bool Wallpaper::IsGradient() const +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + return (mpImplWallpaper->mpGradient != 0); +} + + +// ----------------------------------------------------------------------- + +Gradient Wallpaper::ImplGetApplicationGradient() const +{ + Gradient g; + g.SetAngle( 900 ); + g.SetStyle( GRADIENT_LINEAR ); + g.SetStartColor( Application::GetSettings().GetStyleSettings().GetFaceColor() ); + // no 'extreme' gradient when high contrast + if( Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + g.SetEndColor( Application::GetSettings().GetStyleSettings().GetFaceColor() ); + else + g.SetEndColor( Application::GetSettings().GetStyleSettings().GetFaceGradientColor() ); + return g; +} + +// ----------------------------------------------------------------------- + +void Wallpaper::SetRect( const Rectangle& rRect ) +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + ImplMakeUnique( sal_False ); + + if ( rRect.IsEmpty() ) + { + if ( mpImplWallpaper->mpRect ) + { + delete mpImplWallpaper->mpRect; + mpImplWallpaper->mpRect = NULL; + } + } + else + { + if ( mpImplWallpaper->mpRect ) + *(mpImplWallpaper->mpRect) = rRect; + else + mpImplWallpaper->mpRect = new Rectangle( rRect ); + } +} + +// ----------------------------------------------------------------------- + +void Wallpaper::SetRect() +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + if ( mpImplWallpaper->mpRect ) + { + ImplMakeUnique( sal_False ); + delete mpImplWallpaper->mpRect; + mpImplWallpaper->mpRect = NULL; + } +} + +// ----------------------------------------------------------------------- + +Rectangle Wallpaper::GetRect() const +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + if ( mpImplWallpaper->mpRect ) + return *(mpImplWallpaper->mpRect); + else + { + Rectangle aRect; + return aRect; + } +} + +// ----------------------------------------------------------------------- + +sal_Bool Wallpaper::IsRect() const +{ + DBG_CHKTHIS( Wallpaper, NULL ); + + return (mpImplWallpaper->mpRect != 0); +} + + +// ----------------------------------------------------------------------- + +sal_Bool Wallpaper::IsFixed() const +{ + if ( mpImplWallpaper->meStyle == WALLPAPER_NULL ) + return sal_False; + else + return (!mpImplWallpaper->mpBitmap && !mpImplWallpaper->mpGradient); +} + +// ----------------------------------------------------------------------- + +sal_Bool Wallpaper::IsScrollable() const +{ + if ( mpImplWallpaper->meStyle == WALLPAPER_NULL ) + return sal_False; + else if ( !mpImplWallpaper->mpBitmap && !mpImplWallpaper->mpGradient ) + return sal_True; + else if ( mpImplWallpaper->mpBitmap ) + return (mpImplWallpaper->meStyle == WALLPAPER_TILE); + else + return sal_False; +} + +// ----------------------------------------------------------------------- + +Wallpaper& Wallpaper::operator=( const Wallpaper& rWallpaper ) +{ + DBG_CHKTHIS( Wallpaper, NULL ); + DBG_CHKOBJ( &rWallpaper, Wallpaper, NULL ); + DBG_ASSERT( rWallpaper.mpImplWallpaper->mnRefCount < 0xFFFFFFFE, "Wallpaper: RefCount overflow" ); + + // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann + if ( rWallpaper.mpImplWallpaper->mnRefCount ) + rWallpaper.mpImplWallpaper->mnRefCount++; + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplWallpaper->mnRefCount ) + { + if ( mpImplWallpaper->mnRefCount == 1 ) + delete mpImplWallpaper; + else + mpImplWallpaper->mnRefCount--; + } + + mpImplWallpaper = rWallpaper.mpImplWallpaper; + + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool Wallpaper::operator==( const Wallpaper& rWallpaper ) const +{ + DBG_CHKTHIS( Wallpaper, NULL ); + DBG_CHKOBJ( &rWallpaper, Wallpaper, NULL ); + + if ( mpImplWallpaper == rWallpaper.mpImplWallpaper ) + return sal_True; + + if ( ( mpImplWallpaper->meStyle != rWallpaper.mpImplWallpaper->meStyle ) || + ( mpImplWallpaper->maColor != rWallpaper.mpImplWallpaper->maColor ) ) + return sal_False; + + if ( mpImplWallpaper->mpRect != rWallpaper.mpImplWallpaper->mpRect + && ( !mpImplWallpaper->mpRect + || !rWallpaper.mpImplWallpaper->mpRect + || *(mpImplWallpaper->mpRect) != *(rWallpaper.mpImplWallpaper->mpRect) ) ) + return sal_False; + + if ( mpImplWallpaper->mpBitmap != rWallpaper.mpImplWallpaper->mpBitmap + && ( !mpImplWallpaper->mpBitmap + || !rWallpaper.mpImplWallpaper->mpBitmap + || *(mpImplWallpaper->mpBitmap) != *(rWallpaper.mpImplWallpaper->mpBitmap) ) ) + return sal_False; + + if ( mpImplWallpaper->mpGradient != rWallpaper.mpImplWallpaper->mpGradient + && ( !mpImplWallpaper->mpGradient + || !rWallpaper.mpImplWallpaper->mpGradient + || *(mpImplWallpaper->mpGradient) != *(rWallpaper.mpImplWallpaper->mpGradient) ) ) + return sal_False; + + return sal_True; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStm, Wallpaper& rWallpaper ) +{ + rWallpaper.ImplMakeUnique(); + return( rIStm >> *rWallpaper.mpImplWallpaper ); +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStm, const Wallpaper& rWallpaper ) +{ + return( rOStm << *rWallpaper.mpImplWallpaper ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |