summaryrefslogtreecommitdiff
path: root/vcl/source/gdi
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/gdi')
-rw-r--r--vcl/source/gdi/alpha.cxx437
-rw-r--r--vcl/source/gdi/animate.cxx942
-rw-r--r--vcl/source/gdi/base14.cxx691
-rw-r--r--vcl/source/gdi/bitmap.cxx1941
-rw-r--r--vcl/source/gdi/bitmap2.cxx1303
-rw-r--r--vcl/source/gdi/bitmap3.cxx2207
-rw-r--r--vcl/source/gdi/bitmap4.cxx1011
-rw-r--r--vcl/source/gdi/bitmapex.cxx938
-rw-r--r--vcl/source/gdi/bmpacc.cxx450
-rw-r--r--vcl/source/gdi/bmpacc2.cxx334
-rw-r--r--vcl/source/gdi/bmpacc3.cxx413
-rw-r--r--vcl/source/gdi/bmpconv.cxx217
-rw-r--r--vcl/source/gdi/bmpfast.cxx986
-rw-r--r--vcl/source/gdi/configsettings.cxx207
-rw-r--r--vcl/source/gdi/cvtgrf.cxx176
-rw-r--r--vcl/source/gdi/cvtsvm.cxx2512
-rw-r--r--vcl/source/gdi/extoutdevdata.cxx44
-rw-r--r--vcl/source/gdi/font.cxx1120
-rw-r--r--vcl/source/gdi/gdimtf.cxx3265
-rw-r--r--vcl/source/gdi/gfxlink.cxx472
-rw-r--r--vcl/source/gdi/gradient.cxx440
-rw-r--r--vcl/source/gdi/graph.cxx817
-rw-r--r--vcl/source/gdi/graphictools.cxx762
-rw-r--r--vcl/source/gdi/hatch.cxx223
-rw-r--r--vcl/source/gdi/image.cxx974
-rw-r--r--vcl/source/gdi/imagerepository.cxx64
-rw-r--r--vcl/source/gdi/impanmvw.cxx358
-rw-r--r--vcl/source/gdi/impanmvw.hxx99
-rw-r--r--vcl/source/gdi/impbmp.cxx135
-rw-r--r--vcl/source/gdi/impgraph.cxx1678
-rw-r--r--vcl/source/gdi/impimage.cxx619
-rw-r--r--vcl/source/gdi/impimagetree.cxx390
-rw-r--r--vcl/source/gdi/impvect.cxx1205
-rw-r--r--vcl/source/gdi/impvect.hxx66
-rw-r--r--vcl/source/gdi/jobset.cxx455
-rw-r--r--vcl/source/gdi/lineinfo.cxx372
-rwxr-xr-xvcl/source/gdi/makefile.mk125
-rw-r--r--vcl/source/gdi/mapmod.cxx314
-rw-r--r--vcl/source/gdi/metaact.cxx4326
-rw-r--r--vcl/source/gdi/metric.cxx929
-rw-r--r--vcl/source/gdi/octree.cxx372
-rw-r--r--vcl/source/gdi/oldprintadaptor.cxx119
-rw-r--r--vcl/source/gdi/outdev.cxx2694
-rw-r--r--vcl/source/gdi/outdev2.cxx2271
-rw-r--r--vcl/source/gdi/outdev3.cxx8150
-rw-r--r--vcl/source/gdi/outdev4.cxx1355
-rw-r--r--vcl/source/gdi/outdev5.cxx314
-rw-r--r--vcl/source/gdi/outdev6.cxx1241
-rw-r--r--vcl/source/gdi/outdevnative.cxx345
-rw-r--r--vcl/source/gdi/outmap.cxx2421
-rw-r--r--vcl/source/gdi/pdfextoutdevdata.cxx799
-rw-r--r--vcl/source/gdi/pdffontcache.cxx87
-rw-r--r--vcl/source/gdi/pdffontcache.hxx84
-rw-r--r--vcl/source/gdi/pdfwriter.cxx578
-rw-r--r--vcl/source/gdi/pdfwriter_impl.cxx12124
-rw-r--r--vcl/source/gdi/pdfwriter_impl.hxx1339
-rw-r--r--vcl/source/gdi/pdfwriter_impl2.cxx2037
-rw-r--r--vcl/source/gdi/pngread.cxx1564
-rw-r--r--vcl/source/gdi/pngwrite.cxx739
-rw-r--r--vcl/source/gdi/polyscan.cxx361
-rw-r--r--vcl/source/gdi/print.cxx1631
-rw-r--r--vcl/source/gdi/print2.cxx1594
-rw-r--r--vcl/source/gdi/print3.cxx1944
-rw-r--r--vcl/source/gdi/regband.cxx972
-rw-r--r--vcl/source/gdi/region.cxx3104
-rw-r--r--vcl/source/gdi/salgdilayout.cxx828
-rw-r--r--vcl/source/gdi/sallayout.cxx2257
-rw-r--r--vcl/source/gdi/salmisc.cxx519
-rw-r--r--vcl/source/gdi/salnativewidgets-none.cxx136
-rw-r--r--vcl/source/gdi/svcompat.cxx81
-rw-r--r--vcl/source/gdi/textlayout.cxx389
-rw-r--r--vcl/source/gdi/virdev.cxx449
-rw-r--r--vcl/source/gdi/wall.cxx642
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( "&amp;" ) );
+ break;
+ case sal_Unicode('<'):
+ rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&lt;" ) );
+ break;
+ case sal_Unicode('>'):
+ rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&gt;" ) );
+ break;
+ case sal_Unicode('\''):
+ rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&apos;" ) );
+ break;
+ case sal_Unicode('"'):
+ rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&quot;" ) );
+ 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: */