summaryrefslogtreecommitdiff
path: root/vcl/win/source/gdi
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/win/source/gdi')
-rw-r--r--vcl/win/source/gdi/MAKEFILE.MK69
-rw-r--r--vcl/win/source/gdi/salbmp.cxx632
-rw-r--r--vcl/win/source/gdi/salgdi.cxx1798
-rw-r--r--vcl/win/source/gdi/salgdi2.cxx821
-rw-r--r--vcl/win/source/gdi/salgdi3.cxx3245
-rw-r--r--vcl/win/source/gdi/salgdi_gdiplus.cxx265
-rwxr-xr-xvcl/win/source/gdi/salnativewidgets-luna.cxx1290
-rw-r--r--vcl/win/source/gdi/salprn.cxx2364
-rw-r--r--vcl/win/source/gdi/salvd.cxx256
-rw-r--r--vcl/win/source/gdi/winlayout.cxx3225
-rw-r--r--vcl/win/source/gdi/wntgdi.cxx67
11 files changed, 14032 insertions, 0 deletions
diff --git a/vcl/win/source/gdi/MAKEFILE.MK b/vcl/win/source/gdi/MAKEFILE.MK
new file mode 100644
index 000000000000..7489be633f2b
--- /dev/null
+++ b/vcl/win/source/gdi/MAKEFILE.MK
@@ -0,0 +1,69 @@
+#*************************************************************************
+#
+# 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=salgdi
+
+.INCLUDE : $(PRJ)$/util$/makefile.pmk
+
+# --- Settings -----------------------------------------------------
+
+.INCLUDE : settings.mk
+.INCLUDE : $(PRJ)$/util$/makefile2.pmk
+
+# --- #105371#
+.IF "$(COM)"=="GCC"
+.ELSE
+CFLAGS += -DWINVER=0x0400
+.ENDIF
+
+# --- Files --------------------------------------------------------
+
+SLOFILES= $(SLO)$/salgdi.obj \
+ $(SLO)$/salgdi2.obj \
+ $(SLO)$/salgdi3.obj \
+ $(SLO)$/salgdi_gdiplus.obj \
+ $(SLO)$/salvd.obj \
+ $(SLO)$/salprn.obj \
+ $(SLO)$/salbmp.obj \
+ $(SLO)$/winlayout.obj \
+ $(SLO)$/wntgdi.obj \
+ $(SLO)$/salnativewidgets-luna.obj
+
+
+EXCEPTIONSFILES= $(SLO)$/salprn.obj
+
+.IF "$(ENABLE_GRAPHITE)" == "TRUE"
+CFLAGS+=-DENABLE_GRAPHITE
+.ENDIF
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE : target.mk
diff --git a/vcl/win/source/gdi/salbmp.cxx b/vcl/win/source/gdi/salbmp.cxx
new file mode 100644
index 000000000000..141c812dcd31
--- /dev/null
+++ b/vcl/win/source/gdi/salbmp.cxx
@@ -0,0 +1,632 @@
+/*************************************************************************
+ *
+ * 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/svwin.h>
+#include <wincomp.hxx>
+#include <vcl/salbtype.hxx>
+#include <salgdi.h>
+#include <saldata.hxx>
+#include <salbmp.h>
+#include <vcl/bitmap.hxx> // for BitmapSystemData
+#include <string.h>
+
+// -----------
+// - Inlines -
+// -----------
+
+inline void ImplSetPixel4( const HPBYTE pScanline, long nX, const BYTE cIndex )
+{
+ BYTE& rByte = pScanline[ nX >> 1 ];
+
+ ( nX & 1 ) ? ( rByte &= 0xf0, rByte |= ( cIndex & 0x0f ) ) :
+ ( rByte &= 0x0f, rByte |= ( cIndex << 4 ) );
+}
+
+// ----------------
+// - WinSalBitmap -
+// ----------------
+
+WinSalBitmap::WinSalBitmap() :
+ mhDIB ( 0 ),
+ mhDDB ( 0 ),
+ mnBitCount ( 0 )
+{
+}
+
+// ------------------------------------------------------------------
+
+WinSalBitmap::~WinSalBitmap()
+{
+ Destroy();
+}
+
+// ------------------------------------------------------------------
+
+bool WinSalBitmap::Create( HANDLE hBitmap, bool bDIB, bool bCopyHandle )
+{
+ bool bRet = TRUE;
+
+ if( bDIB )
+ mhDIB = (HGLOBAL) ( bCopyHandle ? ImplCopyDIBOrDDB( hBitmap, TRUE ) : hBitmap );
+ else
+ mhDDB = (HBITMAP) ( bCopyHandle ? ImplCopyDIBOrDDB( hBitmap, FALSE ) : hBitmap );
+
+ if( mhDIB )
+ {
+ PBITMAPINFOHEADER pBIH = (PBITMAPINFOHEADER) GlobalLock( mhDIB );
+
+ maSize = Size( pBIH->biWidth, pBIH->biHeight );
+ mnBitCount = pBIH->biBitCount;
+
+ if( mnBitCount )
+ mnBitCount = ( mnBitCount <= 1 ) ? 1 : ( mnBitCount <= 4 ) ? 4 : ( mnBitCount <= 8 ) ? 8 : 24;
+
+ GlobalUnlock( mhDIB );
+ }
+ else if( mhDDB )
+ {
+ BITMAP aDDBInfo;
+
+ if( WIN_GetObject( mhDDB, sizeof( BITMAP ), &aDDBInfo ) )
+ {
+ maSize = Size( aDDBInfo.bmWidth, aDDBInfo.bmHeight );
+ mnBitCount = aDDBInfo.bmPlanes * aDDBInfo.bmBitsPixel;
+
+ if( mnBitCount )
+ {
+ mnBitCount = ( mnBitCount <= 1 ) ? 1 :
+ ( mnBitCount <= 4 ) ? 4 :
+ ( mnBitCount <= 8 ) ? 8 : 24;
+ }
+ }
+ else
+ {
+ mhDDB = 0;
+ bRet = FALSE;
+ }
+ }
+ else
+ bRet = FALSE;
+
+ return bRet;
+}
+
+// ------------------------------------------------------------------
+
+bool WinSalBitmap::Create( const Size& rSize, USHORT nBitCount, const BitmapPalette& rPal )
+{
+ bool bRet = FALSE;
+
+ mhDIB = ImplCreateDIB( rSize, nBitCount, rPal );
+
+ if( mhDIB )
+ {
+ maSize = rSize;
+ mnBitCount = nBitCount;
+ bRet = TRUE;
+ }
+
+ return bRet;
+}
+
+// ------------------------------------------------------------------
+
+bool WinSalBitmap::Create( const SalBitmap& rSSalBitmap )
+{
+ bool bRet = FALSE;
+ const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
+
+ if ( rSalBitmap.mhDIB || rSalBitmap.mhDDB )
+ {
+ HANDLE hNewHdl = ImplCopyDIBOrDDB( rSalBitmap.mhDIB ? rSalBitmap.mhDIB : rSalBitmap.mhDDB,
+ rSalBitmap.mhDIB != 0 );
+
+ if ( hNewHdl )
+ {
+ if( rSalBitmap.mhDIB )
+ mhDIB = (HGLOBAL) hNewHdl;
+ else if( rSalBitmap.mhDDB )
+ mhDDB = (HBITMAP) hNewHdl;
+
+ maSize = rSalBitmap.maSize;
+ mnBitCount = rSalBitmap.mnBitCount;
+
+ bRet = TRUE;
+ }
+ }
+
+ return bRet;
+}
+
+// ------------------------------------------------------------------
+
+bool WinSalBitmap::Create( const SalBitmap& rSSalBmp, SalGraphics* pSGraphics )
+{
+ bool bRet = FALSE;
+
+ const WinSalBitmap& rSalBmp = static_cast<const WinSalBitmap&>(rSSalBmp);
+ WinSalGraphics* pGraphics = static_cast<WinSalGraphics*>(pSGraphics);
+
+ if( rSalBmp.mhDIB )
+ {
+ PBITMAPINFO pBI = (PBITMAPINFO) GlobalLock( rSalBmp.mhDIB );
+ PBITMAPINFOHEADER pBIH = (PBITMAPINFOHEADER) pBI;
+ HDC hDC = pGraphics->mhDC;
+ HBITMAP hNewDDB;
+ BITMAP aDDBInfo;
+ PBYTE pBits = (PBYTE) pBI + *(DWORD*) pBI +
+ ImplGetDIBColorCount( rSalBmp.mhDIB ) * sizeof( RGBQUAD );
+
+ if( pBIH->biBitCount == 1 )
+ {
+ hNewDDB = CreateBitmap( pBIH->biWidth, pBIH->biHeight, 1, 1, NULL );
+
+ if( hNewDDB )
+ SetDIBits( hDC, hNewDDB, 0, pBIH->biHeight, pBits, pBI, DIB_RGB_COLORS );
+ }
+ else
+ hNewDDB = CreateDIBitmap( hDC, (PBITMAPINFOHEADER) pBI, CBM_INIT, pBits, pBI, DIB_RGB_COLORS );
+
+ GlobalUnlock( rSalBmp.mhDIB );
+
+ if( hNewDDB && WIN_GetObject( hNewDDB, sizeof( BITMAP ), &aDDBInfo ) )
+ {
+ mhDDB = hNewDDB;
+ maSize = Size( aDDBInfo.bmWidth, aDDBInfo.bmHeight );
+ mnBitCount = aDDBInfo.bmPlanes * aDDBInfo.bmBitsPixel;
+
+ bRet = TRUE;
+ }
+ else if( hNewDDB )
+ DeleteObject( hNewDDB );
+ }
+
+ return bRet;
+}
+
+// ------------------------------------------------------------------
+
+bool WinSalBitmap::Create( const SalBitmap& rSSalBmp, USHORT nNewBitCount )
+{
+ bool bRet = FALSE;
+
+ const WinSalBitmap& rSalBmp = static_cast<const WinSalBitmap&>(rSSalBmp);
+
+ if( rSalBmp.mhDDB )
+ {
+ mhDIB = ImplCreateDIB( rSalBmp.maSize, nNewBitCount, BitmapPalette() );
+
+ if( mhDIB )
+ {
+ PBITMAPINFO pBI = (PBITMAPINFO) GlobalLock( mhDIB );
+ const int nLines = (int) rSalBmp.maSize.Height();
+ HDC hDC = GetDC( 0 );
+ PBYTE pBits = (PBYTE) pBI + *(DWORD*) pBI +
+ ImplGetDIBColorCount( mhDIB ) * sizeof( RGBQUAD );
+ SalData* pSalData = GetSalData();
+ HPALETTE hOldPal = 0;
+
+ if ( pSalData->mhDitherPal )
+ {
+ hOldPal = SelectPalette( hDC, pSalData->mhDitherPal, TRUE );
+ RealizePalette( hDC );
+ }
+
+ if( GetDIBits( hDC, rSalBmp.mhDDB, 0, nLines, pBits, pBI, DIB_RGB_COLORS ) == nLines )
+ {
+ GlobalUnlock( mhDIB );
+ maSize = rSalBmp.maSize;
+ mnBitCount = nNewBitCount;
+ bRet = TRUE;
+ }
+ else
+ {
+ GlobalUnlock( mhDIB );
+ GlobalFree( mhDIB );
+ mhDIB = 0;
+ }
+
+ if( hOldPal )
+ SelectPalette( hDC, hOldPal, TRUE );
+
+ ReleaseDC( 0, hDC );
+ }
+ }
+
+ return bRet;
+}
+
+// ------------------------------------------------------------------
+
+void WinSalBitmap::Destroy()
+{
+ if( mhDIB )
+ GlobalFree( mhDIB );
+ else if( mhDDB )
+ DeleteObject( mhDDB );
+
+ maSize = Size();
+ mnBitCount = 0;
+}
+
+// ------------------------------------------------------------------
+
+USHORT WinSalBitmap::ImplGetDIBColorCount( HGLOBAL hDIB )
+{
+ USHORT nColors = 0;
+
+ if( hDIB )
+ {
+ PBITMAPINFO pBI = (PBITMAPINFO) GlobalLock( hDIB );
+ PBITMAPINFOHEADER pBIH = (PBITMAPINFOHEADER) pBI;
+
+ if ( pBIH->biSize != sizeof( BITMAPCOREHEADER ) )
+ {
+ if( pBIH->biBitCount <= 8 )
+ {
+ if ( pBIH->biClrUsed )
+ nColors = (USHORT) pBIH->biClrUsed;
+ else
+ nColors = 1 << pBIH->biBitCount;
+ }
+ }
+ else if( ( (PBITMAPCOREHEADER) pBI )->bcBitCount <= 8 )
+ nColors = 1 << ( (PBITMAPCOREHEADER) pBI )->bcBitCount;
+
+ GlobalUnlock( hDIB );
+ }
+
+ return nColors;
+}
+
+// ------------------------------------------------------------------
+
+HGLOBAL WinSalBitmap::ImplCreateDIB( const Size& rSize, USHORT nBits, const BitmapPalette& rPal )
+{
+ DBG_ASSERT( nBits == 1 || nBits == 4 || nBits == 8 || nBits == 16 || nBits == 24, "Unsupported BitCount!" );
+
+ HGLOBAL hDIB = 0;
+
+ if ( rSize.Width() && rSize.Height() )
+ {
+ const ULONG nImageSize = AlignedWidth4Bytes( nBits * rSize.Width() ) * rSize.Height();
+ const USHORT nColors = ( nBits <= 8 ) ? ( 1 << nBits ) : 0;
+
+ hDIB = GlobalAlloc( GHND, sizeof( BITMAPINFOHEADER ) + nColors * sizeof( RGBQUAD ) + nImageSize );
+
+ if( hDIB )
+ {
+ PBITMAPINFO pBI = (PBITMAPINFO) GlobalLock( hDIB );
+ PBITMAPINFOHEADER pBIH = (PBITMAPINFOHEADER) pBI;
+
+ pBIH->biSize = sizeof( BITMAPINFOHEADER );
+ pBIH->biWidth = rSize.Width();
+ pBIH->biHeight = rSize.Height();
+ pBIH->biPlanes = 1;
+ pBIH->biBitCount = nBits;
+ pBIH->biCompression = BI_RGB;
+ pBIH->biSizeImage = nImageSize;
+ pBIH->biXPelsPerMeter = 0;
+ pBIH->biYPelsPerMeter = 0;
+ pBIH->biClrUsed = 0;
+ pBIH->biClrImportant = 0;
+
+ if ( nColors )
+ {
+ const USHORT nMinCount = Min( nColors, rPal.GetEntryCount() );
+
+ if( nMinCount )
+ memcpy( pBI->bmiColors, rPal.ImplGetColorBuffer(), nMinCount * sizeof( RGBQUAD ) );
+ }
+
+ GlobalUnlock( hDIB );
+ }
+ }
+
+ return hDIB;
+}
+
+// ------------------------------------------------------------------
+
+HANDLE WinSalBitmap::ImplCopyDIBOrDDB( HANDLE hHdl, bool bDIB )
+{
+ HANDLE hCopy = 0;
+
+ if ( bDIB && hHdl )
+ {
+ const ULONG nSize = GlobalSize( hHdl );
+
+ if ( (hCopy = GlobalAlloc( GHND, nSize )) != 0 )
+ {
+ memcpy( (LPSTR) GlobalLock( hCopy ), (LPSTR) GlobalLock( hHdl ), nSize );
+
+ GlobalUnlock( hCopy );
+ GlobalUnlock( hHdl );
+ }
+ }
+ else if ( hHdl )
+ {
+ BITMAP aBmp;
+
+ // Source-Bitmap nach Groesse befragen
+ WIN_GetObject( hHdl, sizeof( BITMAP ), (LPSTR) &aBmp );
+
+ // Destination-Bitmap erzeugen
+ if ( (hCopy = CreateBitmapIndirect( &aBmp )) != 0 )
+ {
+ HDC hBmpDC = CreateCompatibleDC( 0 );
+ HBITMAP hBmpOld = (HBITMAP) SelectObject( hBmpDC, hHdl );
+ HDC hCopyDC = CreateCompatibleDC( hBmpDC );
+ HBITMAP hCopyOld = (HBITMAP) SelectObject( hCopyDC, hCopy );
+
+ BitBlt( hCopyDC, 0, 0, aBmp.bmWidth, aBmp.bmHeight, hBmpDC, 0, 0, SRCCOPY );
+
+ SelectObject( hCopyDC, hCopyOld );
+ DeleteDC( hCopyDC );
+
+ SelectObject( hBmpDC, hBmpOld );
+ DeleteDC( hBmpDC );
+ }
+ }
+
+ return hCopy;
+}
+
+// ------------------------------------------------------------------
+
+BitmapBuffer* WinSalBitmap::AcquireBuffer( bool /*bReadOnly*/ )
+{
+ BitmapBuffer* pBuffer = NULL;
+
+ if( mhDIB )
+ {
+ PBITMAPINFO pBI = (PBITMAPINFO) GlobalLock( mhDIB );
+ PBITMAPINFOHEADER pBIH = (PBITMAPINFOHEADER) pBI;
+
+ if( ( pBIH->biCompression == BI_RLE4 ) || ( pBIH->biCompression == BI_RLE8 ) )
+ {
+ Size aSizePix( pBIH->biWidth, pBIH->biHeight );
+ HGLOBAL hNewDIB = ImplCreateDIB( aSizePix, pBIH->biBitCount, BitmapPalette() );
+
+ if( hNewDIB )
+ {
+ PBITMAPINFO pNewBI = (PBITMAPINFO) GlobalLock( hNewDIB );
+ PBITMAPINFOHEADER pNewBIH = (PBITMAPINFOHEADER) pNewBI;
+ const USHORT nColorCount = ImplGetDIBColorCount( hNewDIB );
+ const ULONG nOffset = *(DWORD*) pBI + nColorCount * sizeof( RGBQUAD );
+ BYTE* pOldBits = (PBYTE) pBI + nOffset;
+ BYTE* pNewBits = (PBYTE) pNewBI + nOffset;
+
+ memcpy( pNewBI, pBI, nOffset );
+ pNewBIH->biCompression = 0;
+ ImplDecodeRLEBuffer( pOldBits, pNewBits, aSizePix, pBIH->biCompression == BI_RLE4 );
+
+ GlobalUnlock( mhDIB );
+ GlobalFree( mhDIB );
+ mhDIB = hNewDIB;
+ pBI = pNewBI;
+ pBIH = pNewBIH;
+ }
+ }
+
+ if( pBIH->biPlanes == 1 )
+ {
+ pBuffer = new BitmapBuffer;
+
+ pBuffer->mnFormat = BMP_FORMAT_BOTTOM_UP |
+ ( pBIH->biBitCount == 1 ? BMP_FORMAT_1BIT_MSB_PAL :
+ pBIH->biBitCount == 4 ? BMP_FORMAT_4BIT_MSN_PAL :
+ pBIH->biBitCount == 8 ? BMP_FORMAT_8BIT_PAL :
+ pBIH->biBitCount == 16 ? BMP_FORMAT_16BIT_TC_LSB_MASK :
+ pBIH->biBitCount == 24 ? BMP_FORMAT_24BIT_TC_BGR :
+ pBIH->biBitCount == 32 ? BMP_FORMAT_32BIT_TC_MASK : 0UL );
+
+ if( BMP_SCANLINE_FORMAT( pBuffer->mnFormat ) )
+ {
+ pBuffer->mnWidth = maSize.Width();
+ pBuffer->mnHeight = maSize.Height();
+ pBuffer->mnScanlineSize = AlignedWidth4Bytes( maSize.Width() * pBIH->biBitCount );
+ pBuffer->mnBitCount = (USHORT) pBIH->biBitCount;
+
+ if( pBuffer->mnBitCount <= 8 )
+ {
+ const USHORT nPalCount = ImplGetDIBColorCount( mhDIB );
+
+ pBuffer->maPalette.SetEntryCount( nPalCount );
+ memcpy( pBuffer->maPalette.ImplGetColorBuffer(), pBI->bmiColors, nPalCount * sizeof( RGBQUAD ) );
+ pBuffer->mpBits = (PBYTE) pBI + *(DWORD*) pBI + nPalCount * sizeof( RGBQUAD );
+ }
+ else if( ( pBIH->biBitCount == 16 ) || ( pBIH->biBitCount == 32 ) )
+ {
+ ULONG nOffset = 0UL;
+
+ if( pBIH->biCompression == BI_BITFIELDS )
+ {
+ nOffset = 3 * sizeof( RGBQUAD );
+ pBuffer->maColorMask = ColorMask( *(UINT32*) &pBI->bmiColors[ 0 ],
+ *(UINT32*) &pBI->bmiColors[ 1 ],
+ *(UINT32*) &pBI->bmiColors[ 2 ] );
+ }
+ else if( pBIH->biBitCount == 16 )
+ pBuffer->maColorMask = ColorMask( 0x00007c00UL, 0x000003e0UL, 0x0000001fUL );
+ else
+ pBuffer->maColorMask = ColorMask( 0x00ff0000UL, 0x0000ff00UL, 0x000000ffUL );
+
+ pBuffer->mpBits = (PBYTE) pBI + *(DWORD*) pBI + nOffset;
+ }
+ else
+ pBuffer->mpBits = (PBYTE) pBI + *(DWORD*) pBI;
+ }
+ else
+ {
+ GlobalUnlock( mhDIB );
+ delete pBuffer;
+ pBuffer = NULL;
+ }
+ }
+ else
+ GlobalUnlock( mhDIB );
+ }
+
+ return pBuffer;
+}
+
+// ------------------------------------------------------------------
+
+void WinSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, bool bReadOnly )
+{
+ if( pBuffer )
+ {
+ if( mhDIB )
+ {
+ if( !bReadOnly && !!pBuffer->maPalette )
+ {
+ PBITMAPINFO pBI = (PBITMAPINFO) GlobalLock( mhDIB );
+ const USHORT nCount = pBuffer->maPalette.GetEntryCount();
+ const USHORT nDIBColorCount = ImplGetDIBColorCount( mhDIB );
+ memcpy( pBI->bmiColors, pBuffer->maPalette.ImplGetColorBuffer(), Min( nDIBColorCount, nCount ) * sizeof( RGBQUAD ) );
+ GlobalUnlock( mhDIB );
+ }
+
+ GlobalUnlock( mhDIB );
+ }
+
+ delete pBuffer;
+ }
+}
+
+// ------------------------------------------------------------------
+
+void WinSalBitmap::ImplDecodeRLEBuffer( const BYTE* pSrcBuf, BYTE* pDstBuf,
+ const Size& rSizePixel, bool bRLE4 )
+{
+ HPBYTE pRLE = (HPBYTE) pSrcBuf;
+ HPBYTE pDIB = (HPBYTE) pDstBuf;
+ HPBYTE pRow = (HPBYTE) pDstBuf;
+ ULONG nWidthAl = AlignedWidth4Bytes( rSizePixel.Width() * ( bRLE4 ? 4UL : 8UL ) );
+ HPBYTE pLast = pDIB + rSizePixel.Height() * nWidthAl - 1;
+ ULONG nCountByte;
+ ULONG nRunByte;
+ ULONG nX = 0;
+ ULONG i;
+ BYTE cTmp;
+ bool bEndDecoding = FALSE;
+
+ if( pRLE && pDIB )
+ {
+ do
+ {
+ if( ( nCountByte = *pRLE++ ) == 0 )
+ {
+ nRunByte = *pRLE++;
+
+ if( nRunByte > 2UL )
+ {
+ if( bRLE4 )
+ {
+ nCountByte = nRunByte >> 1UL;
+
+ for( i = 0; i < nCountByte; i++ )
+ {
+ cTmp = *pRLE++;
+ ImplSetPixel4( pDIB, nX++, cTmp >> 4 );
+ ImplSetPixel4( pDIB, nX++, cTmp & 0x0f );
+ }
+
+ if( nRunByte & 1 )
+ ImplSetPixel4( pDIB, nX++, *pRLE++ >> 4 );
+
+ if( ( ( nRunByte + 1 ) >> 1 ) & 1 )
+ pRLE++;
+ }
+ else
+ {
+ memcpy( &pDIB[ nX ], pRLE, nRunByte );
+ pRLE += nRunByte;
+ nX += nRunByte;
+
+ if( nRunByte & 1 )
+ pRLE++;
+ }
+ }
+ else if( !nRunByte )
+ {
+ pDIB = ( pRow += nWidthAl );
+ nX = 0UL;
+ }
+ else if( nRunByte == 1 )
+ bEndDecoding = TRUE;
+ else
+ {
+ nX += *pRLE++;
+ pDIB = ( pRow += ( *pRLE++ ) * nWidthAl );
+ }
+ }
+ else
+ {
+ cTmp = *pRLE++;
+
+ if( bRLE4 )
+ {
+ nRunByte = nCountByte >> 1;
+
+ for( i = 0; i < nRunByte; i++ )
+ {
+ ImplSetPixel4( pDIB, nX++, cTmp >> 4 );
+ ImplSetPixel4( pDIB, nX++, cTmp & 0x0f );
+ }
+
+ if( nCountByte & 1 )
+ ImplSetPixel4( pDIB, nX++, cTmp >> 4 );
+ }
+ else
+ {
+ for( i = 0; i < nCountByte; i++ )
+ pDIB[ nX++ ] = cTmp;
+ }
+ }
+ }
+ while( !bEndDecoding && ( pDIB <= pLast ) );
+ }
+}
+
+bool WinSalBitmap::GetSystemData( BitmapSystemData& rData )
+{
+ bool bRet = false;
+ if( mhDIB || mhDDB )
+ {
+ bRet = true;
+ rData.pDIB = mhDIB;
+ rData.pDDB = mhDDB;
+ const Size& rSize = GetSize ();
+ rData.mnWidth = rSize.Width();
+ rData.mnHeight = rSize.Height();
+ }
+ return bRet;
+}
diff --git a/vcl/win/source/gdi/salgdi.cxx b/vcl/win/source/gdi/salgdi.cxx
new file mode 100644
index 000000000000..eb260eb808c6
--- /dev/null
+++ b/vcl/win/source/gdi/salgdi.cxx
@@ -0,0 +1,1798 @@
+/*************************************************************************
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <tools/svwin.h>
+#include <wincomp.hxx>
+#include <saldata.hxx>
+#include <salgdi.h>
+#include <tools/debug.hxx>
+#include <salframe.h>
+#include <tools/poly.hxx>
+#ifndef _RTL_STRINGBUF_HXX
+#include <rtl/strbuf.hxx>
+#endif
+
+using namespace rtl;
+
+// =======================================================================
+
+// comment out to prevent use of beziers on GDI functions
+#define USE_GDI_BEZIERS
+
+// =======================================================================
+
+#define DITHER_PAL_DELTA 51
+#define DITHER_PAL_STEPS 6
+#define DITHER_PAL_COUNT (DITHER_PAL_STEPS*DITHER_PAL_STEPS*DITHER_PAL_STEPS)
+#define DITHER_MAX_SYSCOLOR 16
+#define DITHER_EXTRA_COLORS 1
+#define DMAP( _def_nVal, _def_nThres ) ((pDitherDiff[_def_nVal]>(_def_nThres))?pDitherHigh[_def_nVal]:pDitherLow[_def_nVal])
+
+// =======================================================================
+
+struct SysColorEntry
+{
+ DWORD nRGB;
+ SysColorEntry* pNext;
+};
+
+// =======================================================================
+
+static SysColorEntry* pFirstSysColor = NULL;
+static SysColorEntry* pActSysColor = NULL;
+
+// -----------------------------------------------------------------------------
+
+// Blue7
+static PALETTEENTRY aImplExtraColor1 =
+{
+ 0, 184, 255, 0
+};
+
+// -----------------------------------------------------------------------------
+
+static PALETTEENTRY aImplSalSysPalEntryAry[ DITHER_MAX_SYSCOLOR ] =
+{
+{ 0, 0, 0, 0 },
+{ 0, 0, 0x80, 0 },
+{ 0, 0x80, 0, 0 },
+{ 0, 0x80, 0x80, 0 },
+{ 0x80, 0, 0, 0 },
+{ 0x80, 0, 0x80, 0 },
+{ 0x80, 0x80, 0, 0 },
+{ 0x80, 0x80, 0x80, 0 },
+{ 0xC0, 0xC0, 0xC0, 0 },
+{ 0, 0, 0xFF, 0 },
+{ 0, 0xFF, 0, 0 },
+{ 0, 0xFF, 0xFF, 0 },
+{ 0xFF, 0, 0, 0 },
+{ 0xFF, 0, 0xFF, 0 },
+{ 0xFF, 0xFF, 0, 0 },
+{ 0xFF, 0xFF, 0xFF, 0 }
+};
+
+// -----------------------------------------------------------------------------
+
+static BYTE aOrdDither8Bit[8][8] =
+{
+ 0, 38, 9, 48, 2, 40, 12, 50,
+ 25, 12, 35, 22, 28, 15, 37, 24,
+ 6, 44, 3, 41, 8, 47, 5, 44,
+ 32, 19, 28, 16, 34, 21, 31, 18,
+ 1, 40, 11, 49, 0, 39, 10, 48,
+ 27, 14, 36, 24, 26, 13, 36, 23,
+ 8, 46, 4, 43, 7, 45, 4, 42,
+ 33, 20, 30, 17, 32, 20, 29, 16
+};
+
+// -----------------------------------------------------------------------------
+
+static BYTE aOrdDither16Bit[8][8] =
+{
+ 0, 6, 1, 7, 0, 6, 1, 7,
+ 4, 2, 5, 3, 4, 2, 5, 3,
+ 1, 7, 0, 6, 1, 7, 0, 6,
+ 5, 3, 4, 2, 5, 3, 4, 2,
+ 0, 6, 1, 7, 0, 6, 1, 7,
+ 4, 2, 5, 3, 4, 2, 5, 3,
+ 1, 7, 0, 6, 1, 7, 0, 6,
+ 5, 3, 4, 2, 5, 3, 4, 2
+};
+
+// =======================================================================
+
+// Pens muessen wir mit 1 Pixel-Breite erzeugen, da ansonsten die S3-Karte
+// viele Paintprobleme hat, wenn Polygone/PolyLines gezeichnet werden und
+// eine komplexe ClipRegion gesetzt ist
+#define GSL_PEN_WIDTH 1
+
+// =======================================================================
+
+#define SAL_POLYPOLYCOUNT_STACKBUF 8
+#define SAL_POLYPOLYPOINTS_STACKBUF 64
+
+// =======================================================================
+
+void ImplInitSalGDI()
+{
+ SalData* pSalData = GetSalData();
+
+ // init stock brushes
+ pSalData->maStockPenColorAry[0] = PALETTERGB( 0, 0, 0 );
+ pSalData->maStockPenColorAry[1] = PALETTERGB( 0xFF, 0xFF, 0xFF );
+ pSalData->maStockPenColorAry[2] = PALETTERGB( 0xC0, 0xC0, 0xC0 );
+ pSalData->maStockPenColorAry[3] = PALETTERGB( 0x80, 0x80, 0x80 );
+ pSalData->mhStockPenAry[0] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[0] );
+ pSalData->mhStockPenAry[1] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[1] );
+ pSalData->mhStockPenAry[2] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[2] );
+ pSalData->mhStockPenAry[3] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[3] );
+ pSalData->mnStockPenCount = 4;
+
+ pSalData->maStockBrushColorAry[0] = PALETTERGB( 0, 0, 0 );
+ pSalData->maStockBrushColorAry[1] = PALETTERGB( 0xFF, 0xFF, 0xFF );
+ pSalData->maStockBrushColorAry[2] = PALETTERGB( 0xC0, 0xC0, 0xC0 );
+ pSalData->maStockBrushColorAry[3] = PALETTERGB( 0x80, 0x80, 0x80 );
+ pSalData->mhStockBrushAry[0] = CreateSolidBrush( pSalData->maStockBrushColorAry[0] );
+ pSalData->mhStockBrushAry[1] = CreateSolidBrush( pSalData->maStockBrushColorAry[1] );
+ pSalData->mhStockBrushAry[2] = CreateSolidBrush( pSalData->maStockBrushColorAry[2] );
+ pSalData->mhStockBrushAry[3] = CreateSolidBrush( pSalData->maStockBrushColorAry[3] );
+ pSalData->mnStockBrushCount = 4;
+
+ // initialize cache of device contexts
+ pSalData->mpHDCCache = new HDCCache[ CACHESIZE_HDC ];
+ memset( pSalData->mpHDCCache, 0, CACHESIZE_HDC * sizeof( HDCCache ) );
+
+ // initialize temporary font list
+ pSalData->mpTempFontItem = NULL;
+
+ // support palettes for 256 color displays
+ HDC hDC = GetDC( 0 );
+ int nBitsPixel = GetDeviceCaps( hDC, BITSPIXEL );
+ int nPlanes = GetDeviceCaps( hDC, PLANES );
+ int nRasterCaps = GetDeviceCaps( hDC, RASTERCAPS );
+ int nBitCount = nBitsPixel * nPlanes;
+
+ if ( (nBitCount > 8) && (nBitCount < 24) )
+ {
+ // test, if we have to dither
+ HDC hMemDC = ::CreateCompatibleDC( hDC );
+ HBITMAP hMemBmp = ::CreateCompatibleBitmap( hDC, 8, 8 );
+ HBITMAP hBmpOld = (HBITMAP) ::SelectObject( hMemDC, hMemBmp );
+ HBRUSH hMemBrush = ::CreateSolidBrush( PALETTERGB( 175, 171, 169 ) );
+ HBRUSH hBrushOld = (HBRUSH) ::SelectObject( hMemDC, hMemBrush );
+ BOOL bDither16 = TRUE;
+
+ ::PatBlt( hMemDC, 0, 0, 8, 8, PATCOPY );
+ const COLORREF aCol( ::GetPixel( hMemDC, 0, 0 ) );
+
+ for( int nY = 0; ( nY < 8 ) && bDither16; nY++ )
+ for( int nX = 0; ( nX < 8 ) && bDither16; nX++ )
+ if( ::GetPixel( hMemDC, nX, nY ) != aCol )
+ bDither16 = FALSE;
+
+ ::SelectObject( hMemDC, hBrushOld ), ::DeleteObject( hMemBrush );
+ ::SelectObject( hMemDC, hBmpOld ), ::DeleteObject( hMemBmp );
+ ::DeleteDC( hMemDC );
+
+ if( bDither16 )
+ {
+ // create DIBPattern for 16Bit dithering
+ long n;
+
+ pSalData->mhDitherDIB = GlobalAlloc( GMEM_FIXED, sizeof( BITMAPINFOHEADER ) + 192 );
+ pSalData->mpDitherDIB = (BYTE*) GlobalLock( pSalData->mhDitherDIB );
+ pSalData->mpDitherDiff = new long[ 256 ];
+ pSalData->mpDitherLow = new BYTE[ 256 ];
+ pSalData->mpDitherHigh = new BYTE[ 256 ];
+ pSalData->mpDitherDIBData = pSalData->mpDitherDIB + sizeof( BITMAPINFOHEADER );
+ memset( pSalData->mpDitherDIB, 0, sizeof( BITMAPINFOHEADER ) );
+
+ BITMAPINFOHEADER* pBIH = (BITMAPINFOHEADER*) pSalData->mpDitherDIB;
+
+ pBIH->biSize = sizeof( BITMAPINFOHEADER );
+ pBIH->biWidth = 8;
+ pBIH->biHeight = 8;
+ pBIH->biPlanes = 1;
+ pBIH->biBitCount = 24;
+
+ for( n = 0; n < 256L; n++ )
+ pSalData->mpDitherDiff[ n ] = n - ( n & 248L );
+
+ for( n = 0; n < 256L; n++ )
+ pSalData->mpDitherLow[ n ] = (BYTE) ( n & 248 );
+
+ for( n = 0; n < 256L; n++ )
+ pSalData->mpDitherHigh[ n ] = (BYTE) Min( pSalData->mpDitherLow[ n ] + 8L, 255L );
+ }
+ }
+ else if ( (nRasterCaps & RC_PALETTE) && (nBitCount == 8) )
+ {
+ BYTE nRed, nGreen, nBlue;
+ BYTE nR, nG, nB;
+ PALETTEENTRY* pPalEntry;
+ LOGPALETTE* pLogPal;
+ const USHORT nDitherPalCount = DITHER_PAL_COUNT;
+ ULONG nTotalCount = DITHER_MAX_SYSCOLOR + nDitherPalCount + DITHER_EXTRA_COLORS;
+
+ // create logical palette
+ pLogPal = (LOGPALETTE*) new char[ sizeof( LOGPALETTE ) + ( nTotalCount * sizeof( PALETTEENTRY ) ) ];
+ pLogPal->palVersion = 0x0300;
+ pLogPal->palNumEntries = (USHORT) nTotalCount;
+ pPalEntry = pLogPal->palPalEntry;
+
+ // Standard colors
+ memcpy( pPalEntry, aImplSalSysPalEntryAry, DITHER_MAX_SYSCOLOR * sizeof( PALETTEENTRY ) );
+ pPalEntry += DITHER_MAX_SYSCOLOR;
+
+ // own palette (6/6/6)
+ for( nB=0, nBlue=0; nB < DITHER_PAL_STEPS; nB++, nBlue += DITHER_PAL_DELTA )
+ {
+ for( nG=0, nGreen=0; nG < DITHER_PAL_STEPS; nG++, nGreen += DITHER_PAL_DELTA )
+ {
+ for( nR=0, nRed=0; nR < DITHER_PAL_STEPS; nR++, nRed += DITHER_PAL_DELTA )
+ {
+ pPalEntry->peRed = nRed;
+ pPalEntry->peGreen = nGreen;
+ pPalEntry->peBlue = nBlue;
+ pPalEntry->peFlags = 0;
+ pPalEntry++;
+ }
+ }
+ }
+
+ // insert special 'Blue' as standard drawing color
+ *pPalEntry++ = aImplExtraColor1;
+
+ // create palette
+ pSalData->mhDitherPal = CreatePalette( pLogPal );
+ delete[] (char*) pLogPal;
+
+ if( pSalData->mhDitherPal )
+ {
+ // create DIBPattern for 8Bit dithering
+ long nSize = sizeof( BITMAPINFOHEADER ) + ( 256 * sizeof( short ) ) + 64;
+ long n;
+
+ pSalData->mhDitherDIB = GlobalAlloc( GMEM_FIXED, nSize );
+ pSalData->mpDitherDIB = (BYTE*) GlobalLock( pSalData->mhDitherDIB );
+ pSalData->mpDitherDiff = new long[ 256 ];
+ pSalData->mpDitherLow = new BYTE[ 256 ];
+ pSalData->mpDitherHigh = new BYTE[ 256 ];
+ pSalData->mpDitherDIBData = pSalData->mpDitherDIB + sizeof( BITMAPINFOHEADER ) + ( 256 * sizeof( short ) );
+ memset( pSalData->mpDitherDIB, 0, sizeof( BITMAPINFOHEADER ) );
+
+ BITMAPINFOHEADER* pBIH = (BITMAPINFOHEADER*) pSalData->mpDitherDIB;
+ short* pColors = (short*) ( pSalData->mpDitherDIB + sizeof( BITMAPINFOHEADER ) );
+
+ pBIH->biSize = sizeof( BITMAPINFOHEADER );
+ pBIH->biWidth = 8;
+ pBIH->biHeight = 8;
+ pBIH->biPlanes = 1;
+ pBIH->biBitCount = 8;
+
+ for( n = 0; n < nDitherPalCount; n++ )
+ pColors[ n ] = (short)( n + DITHER_MAX_SYSCOLOR );
+
+ for( n = 0; n < 256L; n++ )
+ pSalData->mpDitherDiff[ n ] = n % 51L;
+
+ for( n = 0; n < 256L; n++ )
+ pSalData->mpDitherLow[ n ] = (BYTE) ( n / 51L );
+
+ for( n = 0; n < 256L; n++ )
+ pSalData->mpDitherHigh[ n ] = (BYTE)Min( pSalData->mpDitherLow[ n ] + 1, 5 );
+ }
+
+ // get system color entries
+ ImplUpdateSysColorEntries();
+ }
+
+ ReleaseDC( 0, hDC );
+}
+
+// -----------------------------------------------------------------------
+
+void ImplFreeSalGDI()
+{
+ SalData* pSalData = GetSalData();
+
+ // destroy stock objects
+ int i;
+ for ( i = 0; i < pSalData->mnStockPenCount; i++ )
+ DeletePen( pSalData->mhStockPenAry[i] );
+ for ( i = 0; i < pSalData->mnStockBrushCount; i++ )
+ DeleteBrush( pSalData->mhStockBrushAry[i] );
+
+ // 50% Brush loeschen
+ if ( pSalData->mh50Brush )
+ {
+ DeleteBrush( pSalData->mh50Brush );
+ pSalData->mh50Brush = 0;
+ }
+
+ // 50% Bitmap loeschen
+ if ( pSalData->mh50Bmp )
+ {
+ DeleteBitmap( pSalData->mh50Bmp );
+ pSalData->mh50Bmp = 0;
+ }
+
+ ImplClearHDCCache( pSalData );
+ delete[] pSalData->mpHDCCache;
+
+ // Ditherpalette loeschen, wenn vorhanden
+ if ( pSalData->mhDitherPal )
+ {
+ DeleteObject( pSalData->mhDitherPal );
+ pSalData->mhDitherPal = 0;
+ }
+
+ // delete buffers for dithering DIB patterns, if neccessary
+ if ( pSalData->mhDitherDIB )
+ {
+ GlobalUnlock( pSalData->mhDitherDIB );
+ GlobalFree( pSalData->mhDitherDIB );
+ pSalData->mhDitherDIB = 0;
+ delete[] pSalData->mpDitherDiff;
+ delete[] pSalData->mpDitherLow;
+ delete[] pSalData->mpDitherHigh;
+ }
+
+ // delete SysColorList
+ SysColorEntry* pEntry = pFirstSysColor;
+ while( pEntry )
+ {
+ SysColorEntry* pTmp = pEntry->pNext;
+ delete pEntry;
+ pEntry = pTmp;
+ }
+ pFirstSysColor = NULL;
+
+ // delete icon cache
+ SalIcon* pIcon = pSalData->mpFirstIcon;
+ pSalData->mpFirstIcon = NULL;
+ while( pIcon )
+ {
+ SalIcon* pTmp = pIcon->pNext;
+ DestroyIcon( pIcon->hIcon );
+ DestroyIcon( pIcon->hSmallIcon );
+ delete pIcon;
+ pIcon = pTmp;
+ }
+
+ // delete temporary font list
+ ImplReleaseTempFonts( *pSalData );
+}
+
+// -----------------------------------------------------------------------
+
+static int ImplIsPaletteEntry( BYTE nRed, BYTE nGreen, BYTE nBlue )
+{
+ // dither color?
+ if ( !(nRed % DITHER_PAL_DELTA) && !(nGreen % DITHER_PAL_DELTA) && !(nBlue % DITHER_PAL_DELTA) )
+ return TRUE;
+
+ PALETTEENTRY* pPalEntry = aImplSalSysPalEntryAry;
+
+ // standard palette color?
+ for ( USHORT i = 0; i < DITHER_MAX_SYSCOLOR; i++, pPalEntry++ )
+ {
+ if( pPalEntry->peRed == nRed && pPalEntry->peGreen == nGreen && pPalEntry->peBlue == nBlue )
+ return TRUE;
+ }
+
+ // extra color?
+ if ( aImplExtraColor1.peRed == nRed &&
+ aImplExtraColor1.peGreen == nGreen &&
+ aImplExtraColor1.peBlue == nBlue )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// =======================================================================
+
+int ImplIsSysColorEntry( SalColor nSalColor )
+{
+ SysColorEntry* pEntry = pFirstSysColor;
+ const DWORD nTestRGB = (DWORD)RGB( SALCOLOR_RED( nSalColor ),
+ SALCOLOR_GREEN( nSalColor ),
+ SALCOLOR_BLUE( nSalColor ) );
+
+ while ( pEntry )
+ {
+ if ( pEntry->nRGB == nTestRGB )
+ return TRUE;
+ pEntry = pEntry->pNext;
+ }
+
+ return FALSE;
+}
+
+// =======================================================================
+
+static void ImplInsertSysColorEntry( int nSysIndex )
+{
+ const DWORD nRGB = GetSysColor( nSysIndex );
+
+ if ( !ImplIsPaletteEntry( GetRValue( nRGB ), GetGValue( nRGB ), GetBValue( nRGB ) ) )
+ {
+ if ( !pFirstSysColor )
+ {
+ pActSysColor = pFirstSysColor = new SysColorEntry;
+ pFirstSysColor->nRGB = nRGB;
+ pFirstSysColor->pNext = NULL;
+ }
+ else
+ {
+ pActSysColor = pActSysColor->pNext = new SysColorEntry;
+ pActSysColor->nRGB = nRGB;
+ pActSysColor->pNext = NULL;
+ }
+ }
+}
+
+// =======================================================================
+
+void ImplUpdateSysColorEntries()
+{
+ // delete old SysColorList
+ SysColorEntry* pEntry = pFirstSysColor;
+ while( pEntry )
+ {
+ SysColorEntry* pTmp = pEntry->pNext;
+ delete pEntry;
+ pEntry = pTmp;
+ }
+ pActSysColor = pFirstSysColor = NULL;
+
+ // create new sys color list
+ ImplInsertSysColorEntry( COLOR_ACTIVEBORDER );
+ ImplInsertSysColorEntry( COLOR_INACTIVEBORDER );
+ if( aSalShlData.mnVersion >= 410 )
+ {
+ ImplInsertSysColorEntry( COLOR_GRADIENTACTIVECAPTION );
+ ImplInsertSysColorEntry( COLOR_GRADIENTINACTIVECAPTION );
+ }
+ ImplInsertSysColorEntry( COLOR_3DFACE );
+ ImplInsertSysColorEntry( COLOR_3DHILIGHT );
+ ImplInsertSysColorEntry( COLOR_3DLIGHT );
+ ImplInsertSysColorEntry( COLOR_3DSHADOW );
+ ImplInsertSysColorEntry( COLOR_3DDKSHADOW );
+ ImplInsertSysColorEntry( COLOR_INFOBK );
+ ImplInsertSysColorEntry( COLOR_INFOTEXT );
+ ImplInsertSysColorEntry( COLOR_BTNTEXT );
+ ImplInsertSysColorEntry( COLOR_WINDOW );
+ ImplInsertSysColorEntry( COLOR_WINDOWTEXT );
+ ImplInsertSysColorEntry( COLOR_HIGHLIGHT );
+ ImplInsertSysColorEntry( COLOR_HIGHLIGHTTEXT );
+ ImplInsertSysColorEntry( COLOR_MENU );
+ ImplInsertSysColorEntry( COLOR_MENUTEXT );
+ ImplInsertSysColorEntry( COLOR_ACTIVECAPTION );
+ ImplInsertSysColorEntry( COLOR_CAPTIONTEXT );
+ ImplInsertSysColorEntry( COLOR_INACTIVECAPTION );
+ ImplInsertSysColorEntry( COLOR_INACTIVECAPTIONTEXT );
+}
+
+// -----------------------------------------------------------------------
+
+static SalColor ImplGetROPSalColor( SalROPColor nROPColor )
+{
+ SalColor nSalColor;
+ if ( nROPColor == SAL_ROP_0 )
+ nSalColor = MAKE_SALCOLOR( 0, 0, 0 );
+ else
+ nSalColor = MAKE_SALCOLOR( 255, 255, 255 );
+ return nSalColor;
+}
+
+// =======================================================================
+
+void ImplSalInitGraphics( WinSalGraphics* pData )
+{
+ // Beim Printer berechnen wir die minimale Linienstaerke
+ if ( pData->mbPrinter )
+ {
+ int nDPIX = GetDeviceCaps( pData->mhDC, LOGPIXELSX );
+ if ( nDPIX <= 300 )
+ pData->mnPenWidth = 0;
+ else
+ pData->mnPenWidth = nDPIX/300;
+ }
+
+ ::SetTextAlign( pData->mhDC, TA_BASELINE | TA_LEFT | TA_NOUPDATECP );
+ ::SetBkMode( pData->mhDC, TRANSPARENT );
+ ::SetROP2( pData->mhDC, R2_COPYPEN );
+}
+
+// -----------------------------------------------------------------------
+
+void ImplSalDeInitGraphics( WinSalGraphics* pData )
+{
+ // Default Objekte selektieren
+ if ( pData->mhDefPen )
+ SelectPen( pData->mhDC, pData->mhDefPen );
+ if ( pData->mhDefBrush )
+ SelectBrush( pData->mhDC, pData->mhDefBrush );
+ if ( pData->mhDefFont )
+ SelectFont( pData->mhDC, pData->mhDefFont );
+}
+
+// =======================================================================
+
+HDC ImplGetCachedDC( ULONG nID, HBITMAP hBmp )
+{
+ SalData* pSalData = GetSalData();
+ HDCCache* pC = &pSalData->mpHDCCache[ nID ];
+
+ if( !pC->mhDC )
+ {
+ HDC hDC = GetDC( 0 );
+
+ // neuen DC mit DefaultBitmap anlegen
+ pC->mhDC = CreateCompatibleDC( hDC );
+
+ if( pSalData->mhDitherPal )
+ {
+ pC->mhDefPal = SelectPalette( pC->mhDC, pSalData->mhDitherPal, TRUE );
+ RealizePalette( pC->mhDC );
+ }
+
+ pC->mhSelBmp = CreateCompatibleBitmap( hDC, CACHED_HDC_DEFEXT, CACHED_HDC_DEFEXT );
+ pC->mhDefBmp = (HBITMAP) SelectObject( pC->mhDC, pC->mhSelBmp );
+
+ ReleaseDC( 0, hDC );
+ }
+
+ if ( hBmp )
+ SelectObject( pC->mhDC, pC->mhActBmp = hBmp );
+ else
+ pC->mhActBmp = 0;
+
+ return pC->mhDC;
+}
+
+// =======================================================================
+
+void ImplReleaseCachedDC( ULONG nID )
+{
+ SalData* pSalData = GetSalData();
+ HDCCache* pC = &pSalData->mpHDCCache[ nID ];
+
+ if ( pC->mhActBmp )
+ SelectObject( pC->mhDC, pC->mhSelBmp );
+}
+
+// =======================================================================
+
+void ImplClearHDCCache( SalData* pData )
+{
+ for( ULONG i = 0; i < CACHESIZE_HDC; i++ )
+ {
+ HDCCache* pC = &pData->mpHDCCache[ i ];
+
+ if( pC->mhDC )
+ {
+ SelectObject( pC->mhDC, pC->mhDefBmp );
+
+ if( pC->mhDefPal )
+ SelectPalette( pC->mhDC, pC->mhDefPal, TRUE );
+
+ DeleteDC( pC->mhDC );
+ DeleteObject( pC->mhSelBmp );
+ }
+ }
+}
+
+// =======================================================================
+
+// #100127# Fill point and flag memory from array of points which
+// might also contain bezier control points for the PolyDraw() GDI method
+// Make sure pWinPointAry and pWinFlagAry are big enough
+void ImplPreparePolyDraw( bool bCloseFigures,
+ ULONG nPoly,
+ const ULONG* pPoints,
+ const SalPoint* const* pPtAry,
+ const BYTE* const* pFlgAry,
+ POINT* pWinPointAry,
+ BYTE* pWinFlagAry )
+{
+ ULONG nCurrPoly;
+ for( nCurrPoly=0; nCurrPoly<nPoly; ++nCurrPoly )
+ {
+ const POINT* pCurrPoint = reinterpret_cast<const POINT*>( *pPtAry++ );
+ const BYTE* pCurrFlag = *pFlgAry++;
+ const ULONG nCurrPoints = *pPoints++;
+ const bool bHaveFlagArray( pCurrFlag );
+ ULONG nCurrPoint;
+
+ if( nCurrPoints )
+ {
+ // start figure
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_MOVETO;
+ ++pCurrFlag;
+
+ for( nCurrPoint=1; nCurrPoint<nCurrPoints; )
+ {
+ // #102067# Check existence of flag array
+ if( bHaveFlagArray &&
+ ( nCurrPoint + 2 ) < nCurrPoints )
+ {
+ BYTE P4( pCurrFlag[ 2 ] );
+
+ if( ( POLY_CONTROL == pCurrFlag[ 0 ] ) &&
+ ( POLY_CONTROL == pCurrFlag[ 1 ] ) &&
+ ( POLY_NORMAL == P4 || POLY_SMOOTH == P4 || POLY_SYMMTR == P4 ) )
+ {
+ // control point one
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_BEZIERTO;
+
+ // control point two
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_BEZIERTO;
+
+ // end point
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_BEZIERTO;
+
+ nCurrPoint += 3;
+ pCurrFlag += 3;
+ continue;
+ }
+ }
+
+ // regular line point
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_LINETO;
+ ++pCurrFlag;
+ ++nCurrPoint;
+ }
+
+ // end figure?
+ if( bCloseFigures )
+ pWinFlagAry[-1] |= PT_CLOSEFIGURE;
+ }
+ }
+}
+
+// =======================================================================
+
+// #100127# draw an array of points which might also contain bezier control points
+void ImplRenderPath( HDC hdc, ULONG nPoints, const SalPoint* pPtAry, const BYTE* pFlgAry )
+{
+ if( nPoints )
+ {
+ USHORT i;
+ // TODO: profile whether the following options are faster:
+ // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
+ // b) convert our flag array to window's and use PolyDraw
+
+ MoveToEx( hdc, pPtAry->mnX, pPtAry->mnY, NULL );
+ ++pPtAry; ++pFlgAry;
+
+ for( i=1; i<nPoints; ++i, ++pPtAry, ++pFlgAry )
+ {
+ if( *pFlgAry != POLY_CONTROL )
+ {
+ LineTo( hdc, pPtAry->mnX, pPtAry->mnY );
+ }
+ else if( nPoints - i > 2 )
+ {
+ PolyBezierTo( hdc, reinterpret_cast<const POINT*>(pPtAry), 3 );
+ i += 2; pPtAry += 2; pFlgAry += 2;
+ }
+ }
+ }
+}
+
+// =======================================================================
+
+WinSalGraphics::WinSalGraphics()
+{
+ for( int i = 0; i < MAX_FALLBACK; ++i )
+ {
+ mhFonts[ i ] = 0;
+ mpWinFontData[ i ] = NULL;
+ mpWinFontEntry[ i ] = NULL;
+ }
+
+ mfFontScale = 1.0;
+
+ mhDC = 0;
+ mhPen = 0;
+ mhBrush = 0;
+ mhRegion = 0;
+ mhDefPen = 0;
+ mhDefBrush = 0;
+ mhDefFont = 0;
+ mhDefPal = 0;
+ mpStdClipRgnData = NULL;
+ mpLogFont = NULL;
+ mpFontCharSets = NULL;
+ mpFontAttrCache = NULL;
+ mnFontCharSetCount = 0;
+ mpFontKernPairs = NULL;
+ mnFontKernPairCount = 0;
+ mbFontKernInit = FALSE;
+ mbXORMode = FALSE;
+ mnPenWidth = GSL_PEN_WIDTH;
+}
+
+// -----------------------------------------------------------------------
+
+WinSalGraphics::~WinSalGraphics()
+{
+ // free obsolete GDI objekts
+ ReleaseFonts();
+
+ if ( mhPen )
+ {
+ if ( !mbStockPen )
+ DeletePen( mhPen );
+ }
+ if ( mhBrush )
+ {
+ if ( !mbStockBrush )
+ DeleteBrush( mhBrush );
+ }
+
+ if ( mhRegion )
+ {
+ DeleteRegion( mhRegion );
+ mhRegion = 0;
+ }
+
+ // Cache-Daten zerstoeren
+ if ( mpStdClipRgnData )
+ delete [] mpStdClipRgnData;
+
+ if ( mpLogFont )
+ delete mpLogFont;
+
+ if ( mpFontCharSets )
+ delete mpFontCharSets;
+
+ if ( mpFontKernPairs )
+ delete mpFontKernPairs;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::GetResolution( long& rDPIX, long& rDPIY )
+{
+ rDPIX = GetDeviceCaps( mhDC, LOGPIXELSX );
+ rDPIY = GetDeviceCaps( mhDC, LOGPIXELSY );
+
+ // #111139# this fixes the symptom of div by zero on startup
+ // however, printing will fail most likely as communication with
+ // the printer seems not to work in this case
+ if( !rDPIX || !rDPIY )
+ rDPIX = rDPIY = 600;
+}
+
+// -----------------------------------------------------------------------
+
+USHORT WinSalGraphics::GetBitCount()
+{
+ return (USHORT)GetDeviceCaps( mhDC, BITSPIXEL );
+}
+
+// -----------------------------------------------------------------------
+
+long WinSalGraphics::GetGraphicsWidth() const
+{
+ if( mhWnd && IsWindow( mhWnd ) )
+ {
+ WinSalFrame* pFrame = GetWindowPtr( mhWnd );
+ if( pFrame )
+ {
+ if( pFrame->maGeometry.nWidth )
+ return pFrame->maGeometry.nWidth;
+ else
+ {
+ // TODO: perhaps not needed, maGeometry should always be up-to-date
+ RECT aRect;
+ GetClientRect( mhWnd, &aRect );
+ return aRect.right;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::ResetClipRegion()
+{
+ if ( mhRegion )
+ {
+ DeleteRegion( mhRegion );
+ mhRegion = 0;
+ }
+
+ SelectClipRgn( mhDC, 0 );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::BeginSetClipRegion( ULONG nRectCount )
+{
+ if ( mhRegion )
+ {
+ DeleteRegion( mhRegion );
+ mhRegion = 0;
+ }
+
+ ULONG nRectBufSize = sizeof(RECT)*nRectCount;
+ if ( nRectCount < SAL_CLIPRECT_COUNT )
+ {
+ if ( !mpStdClipRgnData )
+ mpStdClipRgnData = (RGNDATA*)new BYTE[sizeof(RGNDATA)-1+(SAL_CLIPRECT_COUNT*sizeof(RECT))];
+ mpClipRgnData = mpStdClipRgnData;
+ }
+ else
+ mpClipRgnData = (RGNDATA*)new BYTE[sizeof(RGNDATA)-1+nRectBufSize];
+ mpClipRgnData->rdh.dwSize = sizeof( RGNDATAHEADER );
+ mpClipRgnData->rdh.iType = RDH_RECTANGLES;
+ mpClipRgnData->rdh.nCount = nRectCount;
+ mpClipRgnData->rdh.nRgnSize = nRectBufSize;
+ SetRectEmpty( &(mpClipRgnData->rdh.rcBound) );
+ mpNextClipRect = (RECT*)(&(mpClipRgnData->Buffer));
+ mbFirstClipRect = TRUE;
+}
+
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalGraphics::unionClipRegion( long nX, long nY, long nWidth, long nHeight )
+{
+ if ( nWidth && nHeight )
+ {
+ RECT* pRect = mpNextClipRect;
+ RECT* pBoundRect = &(mpClipRgnData->rdh.rcBound);
+ long nRight = nX + nWidth;
+ long nBottom = nY + nHeight;
+
+ if ( mbFirstClipRect )
+ {
+ pBoundRect->left = nX;
+ pBoundRect->top = nY;
+ pBoundRect->right = nRight;
+ pBoundRect->bottom = nBottom;
+ mbFirstClipRect = FALSE;
+ }
+ else
+ {
+ if ( nX < pBoundRect->left )
+ pBoundRect->left = (int)nX;
+
+ if ( nY < pBoundRect->top )
+ pBoundRect->top = (int)nY;
+
+ if ( nRight > pBoundRect->right )
+ pBoundRect->right = (int)nRight;
+
+ if ( nBottom > pBoundRect->bottom )
+ pBoundRect->bottom = (int)nBottom;
+ }
+
+ pRect->left = (int)nX;
+ pRect->top = (int)nY;
+ pRect->right = (int)nRight;
+ pRect->bottom = (int)nBottom;
+ mpNextClipRect++;
+ }
+ else
+ {
+ mpClipRgnData->rdh.nCount--;
+ mpClipRgnData->rdh.nRgnSize -= sizeof( RECT );
+ }
+
+ return TRUE;
+}
+
+// -----------------------------------------------------------------------
+
+bool WinSalGraphics::unionClipRegion( const ::basegfx::B2DPolyPolygon& )
+{
+ // TODO: implement and advertise OutDevSupport_B2DClip support
+ return false;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::EndSetClipRegion()
+{
+ // create clip region from ClipRgnData
+ if ( mpClipRgnData->rdh.nCount == 1 )
+ {
+ RECT* pRect = &(mpClipRgnData->rdh.rcBound);
+ mhRegion = CreateRectRgn( pRect->left, pRect->top,
+ pRect->right, pRect->bottom );
+ }
+ else
+ {
+ ULONG nSize = mpClipRgnData->rdh.nRgnSize+sizeof(RGNDATAHEADER);
+ mhRegion = ExtCreateRegion( NULL, nSize, mpClipRgnData );
+
+ // if ExtCreateRegion(...) is not supported
+ if( !mhRegion )
+ {
+ RGNDATAHEADER* pHeader = (RGNDATAHEADER*) mpClipRgnData;
+
+ if( pHeader->nCount )
+ {
+ RECT* pRect = (RECT*) mpClipRgnData->Buffer;
+ mhRegion = CreateRectRgn( pRect->left, pRect->top, pRect->right, pRect->bottom );
+ pRect++;
+
+ for( ULONG n = 1; n < pHeader->nCount; n++, pRect++ )
+ {
+ HRGN hRgn = CreateRectRgn( pRect->left, pRect->top, pRect->right, pRect->bottom );
+ CombineRgn( mhRegion, mhRegion, hRgn, RGN_OR );
+ DeleteRegion( hRgn );
+ }
+ }
+ }
+
+ if ( mpClipRgnData != mpStdClipRgnData )
+ delete [] mpClipRgnData;
+ }
+
+ SelectClipRgn( mhDC, mhRegion );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::SetLineColor()
+{
+ // create and select new pen
+ HPEN hNewPen = GetStockPen( NULL_PEN );
+ HPEN hOldPen = SelectPen( mhDC, hNewPen );
+
+ // destory or save old pen
+ if ( mhPen )
+ {
+ if ( !mbStockPen )
+ DeletePen( mhPen );
+ }
+ else
+ mhDefPen = hOldPen;
+
+ // set new data
+ mhPen = hNewPen;
+ mbPen = FALSE;
+ mbStockPen = TRUE;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::SetLineColor( SalColor nSalColor )
+{
+ maLineColor = nSalColor;
+ COLORREF nPenColor = PALETTERGB( SALCOLOR_RED( nSalColor ),
+ SALCOLOR_GREEN( nSalColor ),
+ SALCOLOR_BLUE( nSalColor ) );
+ HPEN hNewPen = 0;
+ BOOL bStockPen = FALSE;
+
+ // search for stock pen (only screen, because printer have problems,
+ // when we use stock objects)
+ if ( !mbPrinter )
+ {
+ SalData* pSalData = GetSalData();
+ for ( USHORT i = 0; i < pSalData->mnStockPenCount; i++ )
+ {
+ if ( nPenColor == pSalData->maStockPenColorAry[i] )
+ {
+ hNewPen = pSalData->mhStockPenAry[i];
+ bStockPen = TRUE;
+ break;
+ }
+ }
+ }
+
+ // create new pen
+ if ( !hNewPen )
+ {
+ if ( !mbPrinter )
+ {
+ if ( GetSalData()->mhDitherPal && ImplIsSysColorEntry( nSalColor ) )
+ nPenColor = PALRGB_TO_RGB( nPenColor );
+ }
+
+ hNewPen = CreatePen( PS_SOLID, mnPenWidth, nPenColor );
+ bStockPen = FALSE;
+ }
+
+ // select new pen
+ HPEN hOldPen = SelectPen( mhDC, hNewPen );
+
+ // destory or save old pen
+ if ( mhPen )
+ {
+ if ( !mbStockPen )
+ DeletePen( mhPen );
+ }
+ else
+ mhDefPen = hOldPen;
+
+ // set new data
+ mnPenColor = nPenColor;
+ mhPen = hNewPen;
+ mbPen = TRUE;
+ mbStockPen = bStockPen;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::SetFillColor()
+{
+ // create and select new brush
+ HBRUSH hNewBrush = GetStockBrush( NULL_BRUSH );
+ HBRUSH hOldBrush = SelectBrush( mhDC, hNewBrush );
+
+ // destory or save old brush
+ if ( mhBrush )
+ {
+ if ( !mbStockBrush )
+ DeleteBrush( mhBrush );
+ }
+ else
+ mhDefBrush = hOldBrush;
+
+ // set new data
+ mhBrush = hNewBrush;
+ mbBrush = FALSE;
+ mbStockBrush = TRUE;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::SetFillColor( SalColor nSalColor )
+{
+ maFillColor = nSalColor;
+ SalData* pSalData = GetSalData();
+ BYTE nRed = SALCOLOR_RED( nSalColor );
+ BYTE nGreen = SALCOLOR_GREEN( nSalColor );
+ BYTE nBlue = SALCOLOR_BLUE( nSalColor );
+ COLORREF nBrushColor = PALETTERGB( nRed, nGreen, nBlue );
+ HBRUSH hNewBrush = 0;
+ BOOL bStockBrush = FALSE;
+
+ // search for stock brush (only screen, because printer have problems,
+ // when we use stock objects)
+ if ( !mbPrinter )
+ {
+ for ( USHORT i = 0; i < pSalData->mnStockBrushCount; i++ )
+ {
+ if ( nBrushColor == pSalData->maStockBrushColorAry[ i ] )
+ {
+ hNewBrush = pSalData->mhStockBrushAry[i];
+ bStockBrush = TRUE;
+ break;
+ }
+ }
+ }
+
+ // create new brush
+ if ( !hNewBrush )
+ {
+ if ( mbPrinter || !pSalData->mhDitherDIB )
+ hNewBrush = CreateSolidBrush( nBrushColor );
+ else
+ {
+ if ( 24 == ((BITMAPINFOHEADER*)pSalData->mpDitherDIB)->biBitCount )
+ {
+ BYTE* pTmp = pSalData->mpDitherDIBData;
+ long* pDitherDiff = pSalData->mpDitherDiff;
+ BYTE* pDitherLow = pSalData->mpDitherLow;
+ BYTE* pDitherHigh = pSalData->mpDitherHigh;
+
+ for( long nY = 0L; nY < 8L; nY++ )
+ {
+ for( long nX = 0L; nX < 8L; nX++ )
+ {
+ const long nThres = aOrdDither16Bit[ nY ][ nX ];
+ *pTmp++ = DMAP( nBlue, nThres );
+ *pTmp++ = DMAP( nGreen, nThres );
+ *pTmp++ = DMAP( nRed, nThres );
+ }
+ }
+
+ hNewBrush = CreateDIBPatternBrush( pSalData->mhDitherDIB, DIB_RGB_COLORS );
+ }
+ else if ( ImplIsSysColorEntry( nSalColor ) )
+ {
+ nBrushColor = PALRGB_TO_RGB( nBrushColor );
+ hNewBrush = CreateSolidBrush( nBrushColor );
+ }
+ else if ( ImplIsPaletteEntry( nRed, nGreen, nBlue ) )
+ hNewBrush = CreateSolidBrush( nBrushColor );
+ else
+ {
+ BYTE* pTmp = pSalData->mpDitherDIBData;
+ long* pDitherDiff = pSalData->mpDitherDiff;
+ BYTE* pDitherLow = pSalData->mpDitherLow;
+ BYTE* pDitherHigh = pSalData->mpDitherHigh;
+
+ for ( long nY = 0L; nY < 8L; nY++ )
+ {
+ for ( long nX = 0L; nX < 8L; nX++ )
+ {
+ const long nThres = aOrdDither8Bit[ nY ][ nX ];
+ *pTmp = DMAP( nRed, nThres ) + DMAP( nGreen, nThres ) * 6 + DMAP( nBlue, nThres ) * 36;
+ pTmp++;
+ }
+ }
+
+ hNewBrush = CreateDIBPatternBrush( pSalData->mhDitherDIB, DIB_PAL_COLORS );
+ }
+ }
+
+ bStockBrush = FALSE;
+ }
+
+ // select new brush
+ HBRUSH hOldBrush = SelectBrush( mhDC, hNewBrush );
+
+ // destory or save old brush
+ if ( mhBrush )
+ {
+ if ( !mbStockBrush )
+ DeleteBrush( mhBrush );
+ }
+ else
+ mhDefBrush = hOldBrush;
+
+ // set new data
+ mnBrushColor = nBrushColor;
+ mhBrush = hNewBrush;
+ mbBrush = TRUE;
+ mbStockBrush = bStockBrush;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::SetXORMode( bool bSet, bool )
+{
+ mbXORMode = bSet;
+ ::SetROP2( mhDC, bSet ? R2_XORPEN : R2_COPYPEN );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::SetROPLineColor( SalROPColor nROPColor )
+{
+ SetLineColor( ImplGetROPSalColor( nROPColor ) );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::SetROPFillColor( SalROPColor nROPColor )
+{
+ SetFillColor( ImplGetROPSalColor( nROPColor ) );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawPixel( long nX, long nY )
+{
+ if ( mbXORMode )
+ {
+ HBRUSH hBrush = CreateSolidBrush( mnPenColor );
+ HBRUSH hOldBrush = SelectBrush( mhDC, hBrush );
+ PatBlt( mhDC, (int)nX, (int)nY, (int)1, (int)1, PATINVERT );
+ SelectBrush( mhDC, hOldBrush );
+ DeleteBrush( hBrush );
+ }
+ else
+ SetPixel( mhDC, (int)nX, (int)nY, mnPenColor );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawPixel( long nX, long nY, SalColor nSalColor )
+{
+ COLORREF nCol = PALETTERGB( SALCOLOR_RED( nSalColor ),
+ SALCOLOR_GREEN( nSalColor ),
+ SALCOLOR_BLUE( nSalColor ) );
+
+ if ( !mbPrinter &&
+ GetSalData()->mhDitherPal &&
+ ImplIsSysColorEntry( nSalColor ) )
+ nCol = PALRGB_TO_RGB( nCol );
+
+ if ( mbXORMode )
+ {
+ HBRUSH hBrush = CreateSolidBrush( nCol );
+ HBRUSH hOldBrush = SelectBrush( mhDC, hBrush );
+ PatBlt( mhDC, (int)nX, (int)nY, (int)1, (int)1, PATINVERT );
+ SelectBrush( mhDC, hOldBrush );
+ DeleteBrush( hBrush );
+ }
+ else
+ ::SetPixel( mhDC, (int)nX, (int)nY, nCol );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 )
+{
+ MoveToEx( mhDC, (int)nX1, (int)nY1, NULL );
+
+ // we must paint the endpoint
+ int bPaintEnd = TRUE;
+ if ( nX1 == nX2 )
+ {
+ bPaintEnd = FALSE;
+ if ( nY1 <= nY2 )
+ nY2++;
+ else
+ nY2--;
+ }
+ if ( nY1 == nY2 )
+ {
+ bPaintEnd = FALSE;
+ if ( nX1 <= nX2 )
+ nX2++;
+ else
+ nX2--;
+ }
+
+ LineTo( mhDC, (int)nX2, (int)nY2 );
+
+ if ( bPaintEnd && !mbPrinter )
+ {
+ if ( mbXORMode )
+ {
+ HBRUSH hBrush = CreateSolidBrush( mnPenColor );
+ HBRUSH hOldBrush = SelectBrush( mhDC, hBrush );
+ PatBlt( mhDC, (int)nX2, (int)nY2, (int)1, (int)1, PATINVERT );
+ SelectBrush( mhDC, hOldBrush );
+ DeleteBrush( hBrush );
+ }
+ else
+ SetPixel( mhDC, (int)nX2, (int)nY2, mnPenColor );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawRect( long nX, long nY, long nWidth, long nHeight )
+{
+ if ( !mbPen )
+ {
+ if ( !mbPrinter )
+ {
+ PatBlt( mhDC, (int)nX, (int)nY, (int)nWidth, (int)nHeight,
+ mbXORMode ? PATINVERT : PATCOPY );
+ }
+ else
+ {
+ RECT aWinRect;
+ aWinRect.left = nX;
+ aWinRect.top = nY;
+ aWinRect.right = nX+nWidth;
+ aWinRect.bottom = nY+nHeight;
+ ::FillRect( mhDC, &aWinRect, mhBrush );
+ }
+ }
+ else
+ WIN_Rectangle( mhDC, (int)nX, (int)nY, (int)(nX+nWidth), (int)(nY+nHeight) );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawPolyLine( ULONG nPoints, const SalPoint* pPtAry )
+{
+ // Unter NT koennen wir das Array direkt weiterreichen
+ DBG_ASSERT( sizeof( POINT ) == sizeof( SalPoint ),
+ "WinSalGraphics::DrawPolyLine(): POINT != SalPoint" );
+
+ POINT* pWinPtAry = (POINT*)pPtAry;
+ // Wegen Windows 95 und der Beschraenkung auf eine maximale Anzahl
+ // von Punkten
+ if ( !Polyline( mhDC, pWinPtAry, (int)nPoints ) && (nPoints > MAX_64KSALPOINTS) )
+ Polyline( mhDC, pWinPtAry, MAX_64KSALPOINTS );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawPolygon( ULONG nPoints, const SalPoint* pPtAry )
+{
+ // Unter NT koennen wir das Array direkt weiterreichen
+ DBG_ASSERT( sizeof( POINT ) == sizeof( SalPoint ),
+ "WinSalGraphics::DrawPolygon(): POINT != SalPoint" );
+
+ POINT* pWinPtAry = (POINT*)pPtAry;
+ // Wegen Windows 95 und der Beschraenkung auf eine maximale Anzahl
+ // von Punkten
+ if ( !WIN_Polygon( mhDC, pWinPtAry, (int)nPoints ) && (nPoints > MAX_64KSALPOINTS) )
+ WIN_Polygon( mhDC, pWinPtAry, MAX_64KSALPOINTS );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ PCONSTSALPOINT* pPtAry )
+{
+ UINT aWinPointAry[SAL_POLYPOLYCOUNT_STACKBUF];
+ UINT* pWinPointAry;
+ UINT nPolyPolyPoints = 0;
+ UINT nPoints;
+ UINT i;
+
+ if ( nPoly <= SAL_POLYPOLYCOUNT_STACKBUF )
+ pWinPointAry = aWinPointAry;
+ else
+ pWinPointAry = new UINT[nPoly];
+
+ for ( i = 0; i < (UINT)nPoly; i++ )
+ {
+ nPoints = (UINT)pPoints[i]+1;
+ pWinPointAry[i] = nPoints;
+ nPolyPolyPoints += nPoints;
+ }
+
+ POINT aWinPointAryAry[SAL_POLYPOLYPOINTS_STACKBUF];
+ POINT* pWinPointAryAry;
+ if ( nPolyPolyPoints <= SAL_POLYPOLYPOINTS_STACKBUF )
+ pWinPointAryAry = aWinPointAryAry;
+ else
+ pWinPointAryAry = new POINT[nPolyPolyPoints];
+ // Unter NT koennen wir das Array direkt weiterreichen
+ DBG_ASSERT( sizeof( POINT ) == sizeof( SalPoint ),
+ "WinSalGraphics::DrawPolyPolygon(): POINT != SalPoint" );
+ const SalPoint* pPolyAry;
+ UINT n = 0;
+ for ( i = 0; i < (UINT)nPoly; i++ )
+ {
+ nPoints = pWinPointAry[i];
+ pPolyAry = pPtAry[i];
+ memcpy( pWinPointAryAry+n, pPolyAry, (nPoints-1)*sizeof(POINT) );
+ pWinPointAryAry[n+nPoints-1] = pWinPointAryAry[n];
+ n += nPoints;
+ }
+
+ if ( !WIN_PolyPolygon( mhDC, pWinPointAryAry, (int*)pWinPointAry, (UINT)nPoly ) &&
+ (nPolyPolyPoints > MAX_64KSALPOINTS) )
+ {
+ nPolyPolyPoints = 0;
+ nPoly = 0;
+ do
+ {
+ nPolyPolyPoints += pWinPointAry[(UINT)nPoly];
+ nPoly++;
+ }
+ while ( nPolyPolyPoints < MAX_64KSALPOINTS );
+ nPoly--;
+ if ( pWinPointAry[(UINT)nPoly] > MAX_64KSALPOINTS )
+ pWinPointAry[(UINT)nPoly] = MAX_64KSALPOINTS;
+ if ( nPoly == 1 )
+ WIN_Polygon( mhDC, pWinPointAryAry, *pWinPointAry );
+ else
+ WIN_PolyPolygon( mhDC, pWinPointAryAry, (int*)pWinPointAry, nPoly );
+ }
+
+ if ( pWinPointAry != aWinPointAry )
+ delete [] pWinPointAry;
+ if ( pWinPointAryAry != aWinPointAryAry )
+ delete [] pWinPointAryAry;
+}
+
+// -----------------------------------------------------------------------
+
+#define SAL_POLY_STACKBUF 32
+
+// -----------------------------------------------------------------------
+
+sal_Bool WinSalGraphics::drawPolyLineBezier( ULONG nPoints, const SalPoint* pPtAry, const BYTE* pFlgAry )
+{
+#ifdef USE_GDI_BEZIERS
+ // Unter NT koennen wir das Array direkt weiterreichen
+ DBG_ASSERT( sizeof( POINT ) == sizeof( SalPoint ),
+ "WinSalGraphics::DrawPolyLineBezier(): POINT != SalPoint" );
+
+ ImplRenderPath( mhDC, nPoints, pPtAry, pFlgAry );
+
+ return sal_True;
+#else
+ return sal_False;
+#endif
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool WinSalGraphics::drawPolygonBezier( ULONG nPoints, const SalPoint* pPtAry, const BYTE* pFlgAry )
+{
+#ifdef USE_GDI_BEZIERS
+ // Unter NT koennen wir das Array direkt weiterreichen
+ DBG_ASSERT( sizeof( POINT ) == sizeof( SalPoint ),
+ "WinSalGraphics::DrawPolygonBezier(): POINT != SalPoint" );
+
+ POINT aStackAry1[SAL_POLY_STACKBUF];
+ BYTE aStackAry2[SAL_POLY_STACKBUF];
+ POINT* pWinPointAry;
+ BYTE* pWinFlagAry;
+ if( nPoints > SAL_POLY_STACKBUF )
+ {
+ pWinPointAry = new POINT[ nPoints ];
+ pWinFlagAry = new BYTE[ nPoints ];
+ }
+ else
+ {
+ pWinPointAry = aStackAry1;
+ pWinFlagAry = aStackAry2;
+ }
+
+ ImplPreparePolyDraw(true, 1, &nPoints, &pPtAry, &pFlgAry, pWinPointAry, pWinFlagAry);
+
+ sal_Bool bRet( sal_False );
+
+ if( BeginPath( mhDC ) )
+ {
+ PolyDraw(mhDC, pWinPointAry, pWinFlagAry, nPoints);
+
+ if( EndPath( mhDC ) )
+ {
+ if( StrokeAndFillPath( mhDC ) )
+ bRet = sal_True;
+ }
+ }
+
+ if( pWinPointAry != aStackAry1 )
+ {
+ delete [] pWinPointAry;
+ delete [] pWinFlagAry;
+ }
+
+ return bRet;
+#else
+ return sal_False;
+#endif
+}
+
+// -----------------------------------------------------------------------
+
+sal_Bool WinSalGraphics::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ const SalPoint* const* pPtAry, const BYTE* const* pFlgAry )
+{
+#ifdef USE_GDI_BEZIERS
+ // Unter NT koennen wir das Array direkt weiterreichen
+ DBG_ASSERT( sizeof( POINT ) == sizeof( SalPoint ),
+ "WinSalGraphics::DrawPolyPolygonBezier(): POINT != SalPoint" );
+
+ ULONG nCurrPoly, nTotalPoints;
+ const ULONG* pCurrPoints = pPoints;
+ for( nCurrPoly=0, nTotalPoints=0; nCurrPoly<nPoly; ++nCurrPoly )
+ nTotalPoints += *pCurrPoints++;
+
+ POINT aStackAry1[SAL_POLY_STACKBUF];
+ BYTE aStackAry2[SAL_POLY_STACKBUF];
+ POINT* pWinPointAry;
+ BYTE* pWinFlagAry;
+ if( nTotalPoints > SAL_POLY_STACKBUF )
+ {
+ pWinPointAry = new POINT[ nTotalPoints ];
+ pWinFlagAry = new BYTE[ nTotalPoints ];
+ }
+ else
+ {
+ pWinPointAry = aStackAry1;
+ pWinFlagAry = aStackAry2;
+ }
+
+ ImplPreparePolyDraw(true, nPoly, pPoints, pPtAry, pFlgAry, pWinPointAry, pWinFlagAry);
+
+ sal_Bool bRet( sal_False );
+
+ if( BeginPath( mhDC ) )
+ {
+ PolyDraw(mhDC, pWinPointAry, pWinFlagAry, nTotalPoints);
+
+ if( EndPath( mhDC ) )
+ {
+ if( StrokeAndFillPath( mhDC ) )
+ bRet = sal_True;
+ }
+ }
+
+ if( pWinPointAry != aStackAry1 )
+ {
+ delete [] pWinPointAry;
+ delete [] pWinFlagAry;
+ }
+
+ return bRet;
+#else
+ return sal_False;
+#endif
+}
+
+// -----------------------------------------------------------------------
+
+#define POSTSCRIPT_BUFSIZE 0x4000 // MAXIMUM BUFSIZE EQ 0xFFFF
+#define POSTSCRIPT_BOUNDINGSEARCH 0x1000 // we only try to get the BoundingBox
+ // in the first 4096 bytes
+
+static BYTE* ImplSearchEntry( BYTE* pSource, BYTE* pDest, ULONG nComp, ULONG nSize )
+{
+ while ( nComp-- >= nSize )
+ {
+ ULONG i;
+ for ( i = 0; i < nSize; i++ )
+ {
+ if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
+ break;
+ }
+ if ( i == nSize )
+ return pSource;
+ pSource++;
+ }
+ return NULL;
+}
+
+static BOOL ImplGetBoundingBox( double* nNumb, BYTE* pSource, ULONG nSize )
+{
+ BOOL bRetValue = FALSE;
+ BYTE* pDest = ImplSearchEntry( pSource, (BYTE*)"%%BoundingBox:", nSize, 14 );
+ if ( pDest )
+ {
+ nNumb[0] = nNumb[1] = nNumb[2] = nNumb[3] = 0;
+ pDest += 14;
+
+ int nSizeLeft = nSize - ( pDest - pSource );
+ if ( nSizeLeft > 100 )
+ nSizeLeft = 100; // only 100 bytes following the bounding box will be checked
+
+ int i;
+ for ( i = 0; ( i < 4 ) && nSizeLeft; i++ )
+ {
+ int nDivision = 1;
+ BOOL bDivision = FALSE;
+ BOOL bNegative = FALSE;
+ BOOL bValid = TRUE;
+
+ while ( ( --nSizeLeft ) && ( *pDest == ' ' ) || ( *pDest == 0x9 ) ) pDest++;
+ BYTE nByte = *pDest;
+ while ( nSizeLeft && ( nByte != ' ' ) && ( nByte != 0x9 ) && ( nByte != 0xd ) && ( nByte != 0xa ) )
+ {
+ switch ( nByte )
+ {
+ case '.' :
+ if ( bDivision )
+ bValid = FALSE;
+ else
+ bDivision = TRUE;
+ break;
+ case '-' :
+ bNegative = TRUE;
+ break;
+ default :
+ if ( ( nByte < '0' ) || ( nByte > '9' ) )
+ nSizeLeft = 1; // error parsing the bounding box values
+ else if ( bValid )
+ {
+ if ( bDivision )
+ nDivision*=10;
+ nNumb[i] *= 10;
+ nNumb[i] += nByte - '0';
+ }
+ break;
+ }
+ nSizeLeft--;
+ nByte = *(++pDest);
+ }
+ if ( bNegative )
+ nNumb[i] = -nNumb[i];
+ if ( bDivision && ( nDivision != 1 ) )
+ nNumb[i] /= nDivision;
+ }
+ if ( i == 4 )
+ bRetValue = TRUE;
+ }
+ return bRetValue;
+}
+
+BOOL WinSalGraphics::drawEPS( long nX, long nY, long nWidth, long nHeight, void* pPtr, ULONG nSize )
+{
+ BOOL bRetValue = FALSE;
+
+ if ( mbPrinter )
+ {
+ int nEscape = POSTSCRIPT_PASSTHROUGH;
+
+ if ( Escape( mhDC, QUERYESCSUPPORT, sizeof( int ), ( LPSTR )&nEscape, 0 ) )
+ {
+ double nBoundingBox[4];
+
+ if ( ImplGetBoundingBox( nBoundingBox, (BYTE*)pPtr, nSize ) )
+ {
+ OStringBuffer aBuf( POSTSCRIPT_BUFSIZE );
+
+ // reserve place for a USHORT
+ aBuf.append( "aa" );
+
+ // #107797# Write out EPS encapsulation header
+ // ----------------------------------------------------------------------------------
+
+ // directly taken from the PLRM 3.0, p. 726. Note:
+ // this will definitely cause problems when
+ // recursively creating and embedding PostScript files
+ // in OOo, since we use statically-named variables
+ // here (namely, b4_Inc_state_salWin, dict_count_salWin and
+ // op_count_salWin). Currently, I have no idea on how to
+ // work around that, except from scanning and
+ // interpreting the EPS for unused identifiers.
+
+ // append the real text
+ aBuf.append( "\n\n/b4_Inc_state_salWin save def\n"
+ "/dict_count_salWin countdictstack def\n"
+ "/op_count_salWin count 1 sub def\n"
+ "userdict begin\n"
+ "/showpage {} def\n"
+ "0 setgray 0 setlinecap\n"
+ "1 setlinewidth 0 setlinejoin\n"
+ "10 setmiterlimit [] 0 setdash newpath\n"
+ "/languagelevel where\n"
+ "{\n"
+ " pop languagelevel\n"
+ " 1 ne\n"
+ " {\n"
+ " false setstrokeadjust false setoverprint\n"
+ " } if\n"
+ "} if\n\n" );
+
+
+ // #i10737# Apply clipping manually
+ // ----------------------------------------------------------------------------------
+
+ // Windows seems to ignore any clipping at the HDC,
+ // when followed by a POSTSCRIPT_PASSTHROUGH
+
+ // Check whether we've got a clipping, consisting of
+ // exactly one rect (other cases should be, but aren't
+ // handled currently)
+
+ // TODO: Handle more than one rectangle here (take
+ // care, the buffer can handle only POSTSCRIPT_BUFSIZE
+ // characters!)
+ if ( mhRegion != 0 &&
+ mpStdClipRgnData != NULL &&
+ mpClipRgnData == mpStdClipRgnData &&
+ mpClipRgnData->rdh.nCount == 1 )
+ {
+ RECT* pRect = &(mpClipRgnData->rdh.rcBound);
+
+ aBuf.append( "\nnewpath\n" );
+ aBuf.append( pRect->left );
+ aBuf.append( " " );
+ aBuf.append( pRect->top );
+ aBuf.append( " moveto\n" );
+ aBuf.append( pRect->right );
+ aBuf.append( " " );
+ aBuf.append( pRect->top );
+ aBuf.append( " lineto\n" );
+ aBuf.append( pRect->right );
+ aBuf.append( " " );
+ aBuf.append( pRect->bottom );
+ aBuf.append( " lineto\n" );
+ aBuf.append( pRect->left );
+ aBuf.append( " " );
+ aBuf.append( pRect->bottom );
+ aBuf.append( " lineto\n"
+ "closepath\n"
+ "clip\n"
+ "newpath\n" );
+ }
+
+ // #107797# Write out buffer
+ // ----------------------------------------------------------------------------------
+ *((USHORT*)aBuf.getStr()) = (USHORT)( aBuf.getLength() - 2 );
+ Escape ( mhDC, nEscape, aBuf.getLength(), (LPTSTR)aBuf.getStr(), 0 );
+
+
+ // #107797# Write out EPS transformation code
+ // ----------------------------------------------------------------------------------
+ double dM11 = nWidth / ( nBoundingBox[2] - nBoundingBox[0] );
+ double dM22 = nHeight / (nBoundingBox[1] - nBoundingBox[3] );
+ // reserve a USHORT again
+ aBuf.setLength( 2 );
+ aBuf.append( "\n\n[" );
+ aBuf.append( dM11 );
+ aBuf.append( " 0 0 " );
+ aBuf.append( dM22 );
+ aBuf.append( ' ' );
+ aBuf.append( nX - ( dM11 * nBoundingBox[0] ) );
+ aBuf.append( ' ' );
+ aBuf.append( nY - ( dM22 * nBoundingBox[3] ) );
+ aBuf.append( "] concat\n"
+ "%%BeginDocument:\n" );
+ *((USHORT*)aBuf.getStr()) = (USHORT)( aBuf.getLength() - 2 );
+ Escape ( mhDC, nEscape, aBuf.getLength(), (LPTSTR)aBuf.getStr(), 0 );
+
+
+ // #107797# Write out actual EPS content
+ // ----------------------------------------------------------------------------------
+ ULONG nToDo = nSize;
+ ULONG nDoNow;
+ while ( nToDo )
+ {
+ nDoNow = nToDo;
+ if ( nToDo > POSTSCRIPT_BUFSIZE - 2 )
+ nDoNow = POSTSCRIPT_BUFSIZE - 2;
+ // the following is based on the string buffer allocation
+ // of size POSTSCRIPT_BUFSIZE at construction time of aBuf
+ *((USHORT*)aBuf.getStr()) = (USHORT)nDoNow;
+ memcpy( (void*)(aBuf.getStr() + 2), (BYTE*)pPtr + nSize - nToDo, nDoNow );
+ ULONG nResult = Escape ( mhDC, nEscape, nDoNow + 2, (LPTSTR)aBuf.getStr(), 0 );
+ if (!nResult )
+ break;
+ nToDo -= nResult;
+ }
+
+
+ // #107797# Write out EPS encapsulation footer
+ // ----------------------------------------------------------------------------------
+ // reserve a USHORT again
+ aBuf.setLength( 2 );
+ aBuf.append( "%%EndDocument\n"
+ "count op_count_salWin sub {pop} repeat\n"
+ "countdictstack dict_count_salWin sub {end} repeat\n"
+ "b4_Inc_state_salWin restore\n\n" );
+ *((USHORT*)aBuf.getStr()) = (USHORT)( aBuf.getLength() - 2 );
+ Escape ( mhDC, nEscape, aBuf.getLength(), (LPTSTR)aBuf.getStr(), 0 );
+ bRetValue = TRUE;
+ }
+ }
+ }
+
+ return bRetValue;
+}
+
+// -----------------------------------------------------------------------
+
+SystemGraphicsData WinSalGraphics::GetGraphicsData() const
+{
+ SystemGraphicsData aRes;
+ aRes.nSize = sizeof(aRes);
+ aRes.hDC = mhDC;
+ return aRes;
+}
+
+// -----------------------------------------------------------------------
diff --git a/vcl/win/source/gdi/salgdi2.cxx b/vcl/win/source/gdi/salgdi2.cxx
new file mode 100644
index 000000000000..803c0886f429
--- /dev/null
+++ b/vcl/win/source/gdi/salgdi2.cxx
@@ -0,0 +1,821 @@
+/*************************************************************************
+ *
+ * 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 <stdlib.h>
+#include <tools/svwin.h>
+#include <tools/debug.hxx>
+#include <wincomp.hxx>
+#include <salbmp.h>
+#include <saldata.hxx>
+#ifndef _SV_SALIDS_HRC
+#include <salids.hrc>
+#endif
+#include <salgdi.h>
+#include <salframe.h>
+
+bool WinSalGraphics::supportsOperation( OutDevSupportType eType ) const
+{
+ static bool bAllowForTest(true);
+ bool bRet = false;
+
+ switch( eType )
+ {
+ case OutDevSupport_TransparentRect:
+ bRet = mbVirDev || mbWindow;
+ break;
+ case OutDevSupport_B2DDraw:
+ bRet = bAllowForTest;
+ default: break;
+ }
+ return bRet;
+}
+
+// =======================================================================
+
+void WinSalGraphics::copyBits( const SalTwoRect* pPosAry, SalGraphics* pSrcGraphics )
+{
+ HDC hSrcDC;
+ DWORD nRop;
+
+ if ( pSrcGraphics )
+ hSrcDC = static_cast<WinSalGraphics*>(pSrcGraphics)->mhDC;
+ else
+ hSrcDC = mhDC;
+
+ if ( mbXORMode )
+ nRop = SRCINVERT;
+ else
+ nRop = SRCCOPY;
+
+ if ( (pPosAry->mnSrcWidth == pPosAry->mnDestWidth) &&
+ (pPosAry->mnSrcHeight == pPosAry->mnDestHeight) )
+ {
+ BitBlt( mhDC,
+ (int)pPosAry->mnDestX, (int)pPosAry->mnDestY,
+ (int)pPosAry->mnDestWidth, (int)pPosAry->mnDestHeight,
+ hSrcDC,
+ (int)pPosAry->mnSrcX, (int)pPosAry->mnSrcY,
+ nRop );
+ }
+ else
+ {
+ int nOldStretchMode = SetStretchBltMode( mhDC, STRETCH_DELETESCANS );
+ StretchBlt( mhDC,
+ (int)pPosAry->mnDestX, (int)pPosAry->mnDestY,
+ (int)pPosAry->mnDestWidth, (int)pPosAry->mnDestHeight,
+ hSrcDC,
+ (int)pPosAry->mnSrcX, (int)pPosAry->mnSrcY,
+ (int)pPosAry->mnSrcWidth, (int)pPosAry->mnSrcHeight,
+ nRop );
+ SetStretchBltMode( mhDC, nOldStretchMode );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void ImplCalcOutSideRgn( const RECT& rSrcRect,
+ int nLeft, int nTop, int nRight, int nBottom,
+ HRGN& rhInvalidateRgn )
+{
+ HRGN hTempRgn;
+
+ // Bereiche ausserhalb des sichtbaren Bereiches berechnen
+ if ( rSrcRect.left < nLeft )
+ {
+ if ( !rhInvalidateRgn )
+ rhInvalidateRgn = CreateRectRgnIndirect( &rSrcRect );
+ hTempRgn = CreateRectRgn( -31999, 0, nLeft, 31999 );
+ CombineRgn( rhInvalidateRgn, rhInvalidateRgn, hTempRgn, RGN_DIFF );
+ DeleteRegion( hTempRgn );
+ }
+ if ( rSrcRect.top < nTop )
+ {
+ if ( !rhInvalidateRgn )
+ rhInvalidateRgn = CreateRectRgnIndirect( &rSrcRect );
+ hTempRgn = CreateRectRgn( 0, -31999, 31999, nTop );
+ CombineRgn( rhInvalidateRgn, rhInvalidateRgn, hTempRgn, RGN_DIFF );
+ DeleteRegion( hTempRgn );
+ }
+ if ( rSrcRect.right > nRight )
+ {
+ if ( !rhInvalidateRgn )
+ rhInvalidateRgn = CreateRectRgnIndirect( &rSrcRect );
+ hTempRgn = CreateRectRgn( nRight, 0, 31999, 31999 );
+ CombineRgn( rhInvalidateRgn, rhInvalidateRgn, hTempRgn, RGN_DIFF );
+ DeleteRegion( hTempRgn );
+ }
+ if ( rSrcRect.bottom > nBottom )
+ {
+ if ( !rhInvalidateRgn )
+ rhInvalidateRgn = CreateRectRgnIndirect( &rSrcRect );
+ hTempRgn = CreateRectRgn( 0, nBottom, 31999, 31999 );
+ CombineRgn( rhInvalidateRgn, rhInvalidateRgn, hTempRgn, RGN_DIFF );
+ DeleteRegion( hTempRgn );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::copyArea( long nDestX, long nDestY,
+ long nSrcX, long nSrcY,
+ long nSrcWidth, long nSrcHeight,
+ USHORT nFlags )
+{
+ bool bRestoreClipRgn = false;
+ HRGN hOldClipRgn = 0;
+ int nOldClipRgnType = ERROR;
+ HRGN hInvalidateRgn = 0;
+
+ // Muessen die ueberlappenden Bereiche auch invalidiert werden?
+ if ( (nFlags & SAL_COPYAREA_WINDOWINVALIDATE) && mbWindow )
+ {
+ // compute and invalidate those parts that were either off-screen or covered by other windows
+ // while performing the above BitBlt
+ // those regions then have to be invalidated as they contain useless/wrong data
+ RECT aSrcRect;
+ RECT aClipRect;
+ RECT aTempRect;
+ RECT aTempRect2;
+ HRGN hTempRgn;
+ HWND hWnd;
+ int nRgnType;
+
+ // restrict srcRect to this window (calc intersection)
+ aSrcRect.left = (int)nSrcX;
+ aSrcRect.top = (int)nSrcY;
+ aSrcRect.right = aSrcRect.left+(int)nSrcWidth;
+ aSrcRect.bottom = aSrcRect.top+(int)nSrcHeight;
+ GetClientRect( mhWnd, &aClipRect );
+ if ( IntersectRect( &aSrcRect, &aSrcRect, &aClipRect ) )
+ {
+ // transform srcRect to screen coordinates
+ POINT aPt;
+ aPt.x = 0;
+ aPt.y = 0;
+ ClientToScreen( mhWnd, &aPt );
+ aSrcRect.left += aPt.x;
+ aSrcRect.top += aPt.y;
+ aSrcRect.right += aPt.x;
+ aSrcRect.bottom += aPt.y;
+ hInvalidateRgn = 0;
+
+ // compute the parts that are off screen (ie invisible)
+ RECT theScreen;
+ ImplSalGetWorkArea( NULL, &theScreen, NULL ); // find the screen area taking multiple monitors into account
+ ImplCalcOutSideRgn( aSrcRect, theScreen.left, theScreen.top, theScreen.right, theScreen.bottom, hInvalidateRgn );
+
+ // Bereiche die von anderen Fenstern ueberlagert werden berechnen
+ HRGN hTempRgn2 = 0;
+ HWND hWndTopWindow = mhWnd;
+ // Find the TopLevel Window, because only Windows which are in
+ // in the foreground of our TopLevel window must be considered
+ if ( GetWindowStyle( hWndTopWindow ) & WS_CHILD )
+ {
+ RECT aTempRect3 = aSrcRect;
+ do
+ {
+ hWndTopWindow = ::GetParent( hWndTopWindow );
+
+ // Test, if the Parent clips our window
+ GetClientRect( hWndTopWindow, &aTempRect );
+ POINT aPt2;
+ aPt2.x = 0;
+ aPt2.y = 0;
+ ClientToScreen( hWndTopWindow, &aPt2 );
+ aTempRect.left += aPt2.x;
+ aTempRect.top += aPt2.y;
+ aTempRect.right += aPt2.x;
+ aTempRect.bottom += aPt2.y;
+ IntersectRect( &aTempRect3, &aTempRect3, &aTempRect );
+ }
+ while ( GetWindowStyle( hWndTopWindow ) & WS_CHILD );
+
+ // If one or more Parents clip our window, than we must
+ // calculate the outside area
+ if ( !EqualRect( &aSrcRect, &aTempRect3 ) )
+ {
+ ImplCalcOutSideRgn( aSrcRect,
+ aTempRect3.left, aTempRect3.top,
+ aTempRect3.right, aTempRect3.bottom,
+ hInvalidateRgn );
+ }
+ }
+ // retrieve the top-most (z-order) child window
+ hWnd = GetWindow( GetDesktopWindow(), GW_CHILD );
+ while ( hWnd )
+ {
+ if ( hWnd == hWndTopWindow )
+ break;
+ if ( IsWindowVisible( hWnd ) && !IsIconic( hWnd ) )
+ {
+ GetWindowRect( hWnd, &aTempRect );
+ if ( IntersectRect( &aTempRect2, &aSrcRect, &aTempRect ) )
+ {
+ // hWnd covers part or all of aSrcRect
+ if ( !hInvalidateRgn )
+ hInvalidateRgn = CreateRectRgnIndirect( &aSrcRect );
+
+ // get full bounding box of hWnd
+ hTempRgn = CreateRectRgnIndirect( &aTempRect );
+
+ // get region of hWnd (the window may be shaped)
+ if ( !hTempRgn2 )
+ hTempRgn2 = CreateRectRgn( 0, 0, 0, 0 );
+ nRgnType = GetWindowRgn( hWnd, hTempRgn2 );
+ if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
+ {
+ // convert window region to screen coordinates
+ OffsetRgn( hTempRgn2, aTempRect.left, aTempRect.top );
+ // and intersect with the window's bounding box
+ CombineRgn( hTempRgn, hTempRgn, hTempRgn2, RGN_AND );
+ }
+ // finally compute that part of aSrcRect which is not covered by any parts of hWnd
+ CombineRgn( hInvalidateRgn, hInvalidateRgn, hTempRgn, RGN_DIFF );
+ DeleteRegion( hTempRgn );
+ }
+ }
+ // retrieve the next window in the z-order, i.e. the window below hwnd
+ hWnd = GetWindow( hWnd, GW_HWNDNEXT );
+ }
+ if ( hTempRgn2 )
+ DeleteRegion( hTempRgn2 );
+ if ( hInvalidateRgn )
+ {
+ // hInvalidateRgn contains the fully visible parts of the original srcRect
+ hTempRgn = CreateRectRgnIndirect( &aSrcRect );
+ // substract it from the original rect to get the occluded parts
+ nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_DIFF );
+ DeleteRegion( hTempRgn );
+
+ if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
+ {
+ // move the occluded parts to the destination pos
+ int nOffX = (int)(nDestX-nSrcX);
+ int nOffY = (int)(nDestY-nSrcY);
+ OffsetRgn( hInvalidateRgn, nOffX-aPt.x, nOffY-aPt.y );
+
+ // by excluding hInvalidateRgn from the system's clip region
+ // we will prevent bitblt from copying useless data
+ // epsecially now shadows from overlapping windows will appear (#i36344)
+ hOldClipRgn = CreateRectRgn( 0, 0, 0, 0 );
+ nOldClipRgnType = GetClipRgn( mhDC, hOldClipRgn );
+
+ bRestoreClipRgn = TRUE; // indicate changed clipregion and force invalidate
+ ExtSelectClipRgn( mhDC, hInvalidateRgn, RGN_DIFF );
+ }
+ }
+ }
+ }
+
+ BitBlt( mhDC,
+ (int)nDestX, (int)nDestY,
+ (int)nSrcWidth, (int)nSrcHeight,
+ mhDC,
+ (int)nSrcX, (int)nSrcY,
+ SRCCOPY );
+
+ if( bRestoreClipRgn )
+ {
+ // restore old clip region
+ if( nOldClipRgnType != ERROR )
+ SelectClipRgn( mhDC, hOldClipRgn);
+ DeleteRegion( hOldClipRgn );
+
+ // invalidate regions that were not copied
+ bool bInvalidate = true;
+
+ // Combine Invalidate Region with existing ClipRegion
+ HRGN hTempRgn = CreateRectRgn( 0, 0, 0, 0 );
+ if ( GetClipRgn( mhDC, hTempRgn ) == 1 )
+ {
+ int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_AND );
+ if ( (nRgnType == ERROR) || (nRgnType == NULLREGION) )
+ bInvalidate = false;
+ }
+ DeleteRegion( hTempRgn );
+
+ if ( bInvalidate )
+ {
+ InvalidateRgn( mhWnd, hInvalidateRgn, TRUE );
+ // Hier loesen wir nur ein Update aus, wenn es der
+ // MainThread ist, damit es beim Bearbeiten der
+ // Paint-Message keinen Deadlock gibt, da der
+ // SolarMutex durch diesen Thread schon gelockt ist
+ SalData* pSalData = GetSalData();
+ DWORD nCurThreadId = GetCurrentThreadId();
+ if ( pSalData->mnAppThreadId == nCurThreadId )
+ UpdateWindow( mhWnd );
+ }
+
+ DeleteRegion( hInvalidateRgn );
+ }
+
+}
+
+// -----------------------------------------------------------------------
+
+void ImplDrawBitmap( HDC hDC,
+ const SalTwoRect* pPosAry, const WinSalBitmap& rSalBitmap,
+ BOOL bPrinter, int nDrawMode )
+{
+ if( hDC )
+ {
+ HGLOBAL hDrawDIB;
+ HBITMAP hDrawDDB = rSalBitmap.ImplGethDDB();
+ WinSalBitmap* pTmpSalBmp = NULL;
+ BOOL bPrintDDB = ( bPrinter && hDrawDDB );
+
+ if( bPrintDDB )
+ {
+ pTmpSalBmp = new WinSalBitmap;
+ pTmpSalBmp->Create( rSalBitmap, rSalBitmap.GetBitCount() );
+ hDrawDIB = pTmpSalBmp->ImplGethDIB();
+ }
+ else
+ hDrawDIB = rSalBitmap.ImplGethDIB();
+
+ if( hDrawDIB )
+ {
+ PBITMAPINFO pBI = (PBITMAPINFO) GlobalLock( hDrawDIB );
+ PBITMAPINFOHEADER pBIH = (PBITMAPINFOHEADER) pBI;
+ PBYTE pBits = (PBYTE) pBI + *(DWORD*) pBI +
+ rSalBitmap.ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD );
+ const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
+
+ StretchDIBits( hDC,
+ (int)pPosAry->mnDestX, (int)pPosAry->mnDestY,
+ (int)pPosAry->mnDestWidth, (int)pPosAry->mnDestHeight,
+ (int)pPosAry->mnSrcX, (int)(pBIH->biHeight - pPosAry->mnSrcHeight - pPosAry->mnSrcY),
+ (int)pPosAry->mnSrcWidth, (int)pPosAry->mnSrcHeight,
+ pBits, pBI, DIB_RGB_COLORS, nDrawMode );
+
+ GlobalUnlock( hDrawDIB );
+ SetStretchBltMode( hDC, nOldStretchMode );
+ }
+ else if( hDrawDDB && !bPrintDDB )
+ {
+ HDC hBmpDC = ImplGetCachedDC( CACHED_HDC_DRAW, hDrawDDB );
+ COLORREF nOldBkColor = RGB(0xFF,0xFF,0xFF);
+ COLORREF nOldTextColor = RGB(0,0,0);
+ BOOL bMono = ( rSalBitmap.GetBitCount() == 1 );
+
+ if( bMono )
+ {
+ nOldBkColor = SetBkColor( hDC, RGB( 0xFF, 0xFF, 0xFF ) );
+ nOldTextColor = ::SetTextColor( hDC, RGB( 0x00, 0x00, 0x00 ) );
+ }
+
+ if ( (pPosAry->mnSrcWidth == pPosAry->mnDestWidth) &&
+ (pPosAry->mnSrcHeight == pPosAry->mnDestHeight) )
+ {
+ BitBlt( hDC,
+ (int)pPosAry->mnDestX, (int)pPosAry->mnDestY,
+ (int)pPosAry->mnDestWidth, (int)pPosAry->mnDestHeight,
+ hBmpDC,
+ (int)pPosAry->mnSrcX, (int)pPosAry->mnSrcY,
+ nDrawMode );
+ }
+ else
+ {
+ const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
+
+ StretchBlt( hDC,
+ (int)pPosAry->mnDestX, (int)pPosAry->mnDestY,
+ (int)pPosAry->mnDestWidth, (int)pPosAry->mnDestHeight,
+ hBmpDC,
+ (int)pPosAry->mnSrcX, (int)pPosAry->mnSrcY,
+ (int)pPosAry->mnSrcWidth, (int)pPosAry->mnSrcHeight,
+ nDrawMode );
+
+ SetStretchBltMode( hDC, nOldStretchMode );
+ }
+
+ if( bMono )
+ {
+ SetBkColor( hDC, nOldBkColor );
+ ::SetTextColor( hDC, nOldTextColor );
+ }
+
+ ImplReleaseCachedDC( CACHED_HDC_DRAW );
+ }
+
+ if( bPrintDDB )
+ delete pTmpSalBmp;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawBitmap( const SalTwoRect* pPosAry,
+ const SalBitmap& rSalBitmap )
+{
+ ImplDrawBitmap( mhDC, pPosAry, static_cast<const WinSalBitmap&>(rSalBitmap),
+ mbPrinter,
+ mbXORMode ? SRCINVERT : SRCCOPY );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawBitmap( const SalTwoRect* pPosAry,
+ const SalBitmap& rSSalBitmap,
+ SalColor nTransparentColor )
+{
+ DBG_ASSERT( !mbPrinter, "No transparency print possible!" );
+
+ const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
+
+ WinSalBitmap* pMask = new WinSalBitmap;
+ const Point aPoint;
+ const Size aSize( rSalBitmap.GetSize() );
+ HBITMAP hMaskBitmap = CreateBitmap( (int) aSize.Width(), (int) aSize.Height(), 1, 1, NULL );
+ HDC hMaskDC = ImplGetCachedDC( CACHED_HDC_1, hMaskBitmap );
+ const BYTE cRed = SALCOLOR_RED( nTransparentColor );
+ const BYTE cGreen = SALCOLOR_GREEN( nTransparentColor );
+ const BYTE cBlue = SALCOLOR_BLUE( nTransparentColor );
+
+ if( rSalBitmap.ImplGethDDB() )
+ {
+ HDC hSrcDC = ImplGetCachedDC( CACHED_HDC_2, rSalBitmap.ImplGethDDB() );
+ COLORREF aOldCol = SetBkColor( hSrcDC, RGB( cRed, cGreen, cBlue ) );
+
+ BitBlt( hMaskDC, 0, 0, (int) aSize.Width(), (int) aSize.Height(), hSrcDC, 0, 0, SRCCOPY );
+
+ SetBkColor( hSrcDC, aOldCol );
+ ImplReleaseCachedDC( CACHED_HDC_2 );
+ }
+ else
+ {
+ WinSalBitmap* pTmpSalBmp = new WinSalBitmap;
+
+ if( pTmpSalBmp->Create( rSalBitmap, this ) )
+ {
+ HDC hSrcDC = ImplGetCachedDC( CACHED_HDC_2, pTmpSalBmp->ImplGethDDB() );
+ COLORREF aOldCol = SetBkColor( hSrcDC, RGB( cRed, cGreen, cBlue ) );
+
+ BitBlt( hMaskDC, 0, 0, (int) aSize.Width(), (int) aSize.Height(), hSrcDC, 0, 0, SRCCOPY );
+
+ SetBkColor( hSrcDC, aOldCol );
+ ImplReleaseCachedDC( CACHED_HDC_2 );
+ }
+
+ delete pTmpSalBmp;
+ }
+
+ ImplReleaseCachedDC( CACHED_HDC_1 );
+
+ // hMaskBitmap is destroyed by new SalBitmap 'pMask' ( bDIB==FALSE, bCopy == FALSE )
+ if( pMask->Create( hMaskBitmap, FALSE, FALSE ) )
+ drawBitmap( pPosAry, rSalBitmap, *pMask );
+
+ delete pMask;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawBitmap( const SalTwoRect* pPosAry,
+ const SalBitmap& rSSalBitmap,
+ const SalBitmap& rSTransparentBitmap )
+{
+ DBG_ASSERT( !mbPrinter, "No transparency print possible!" );
+
+ const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
+ const WinSalBitmap& rTransparentBitmap = static_cast<const WinSalBitmap&>(rSTransparentBitmap);
+
+ SalTwoRect aPosAry = *pPosAry;
+ int nDstX = (int)aPosAry.mnDestX;
+ int nDstY = (int)aPosAry.mnDestY;
+ int nDstWidth = (int)aPosAry.mnDestWidth;
+ int nDstHeight = (int)aPosAry.mnDestHeight;
+ HDC hDC = mhDC;
+ HBITMAP hMemBitmap = 0;
+ HBITMAP hMaskBitmap = 0;
+
+ if( ( nDstWidth > CACHED_HDC_DEFEXT ) || ( nDstHeight > CACHED_HDC_DEFEXT ) )
+ {
+ hMemBitmap = CreateCompatibleBitmap( hDC, nDstWidth, nDstHeight );
+ hMaskBitmap = CreateCompatibleBitmap( hDC, nDstWidth, nDstHeight );
+ }
+
+ HDC hMemDC = ImplGetCachedDC( CACHED_HDC_1, hMemBitmap );
+ HDC hMaskDC = ImplGetCachedDC( CACHED_HDC_2, hMaskBitmap );
+
+ aPosAry.mnDestX = aPosAry.mnDestY = 0;
+ BitBlt( hMemDC, 0, 0, nDstWidth, nDstHeight, hDC, nDstX, nDstY, SRCCOPY );
+
+ // bei Paletten-Displays hat WIN/WNT offenbar ein kleines Problem,
+ // die Farben der Maske richtig auf die Palette abzubilden,
+ // wenn wir die DIB direkt ausgeben => DDB-Ausgabe
+ if( ( GetBitCount() <= 8 ) && rTransparentBitmap.ImplGethDIB() && rTransparentBitmap.GetBitCount() == 1 )
+ {
+ WinSalBitmap aTmp;
+
+ if( aTmp.Create( rTransparentBitmap, this ) )
+ ImplDrawBitmap( hMaskDC, &aPosAry, aTmp, FALSE, SRCCOPY );
+ }
+ else
+ ImplDrawBitmap( hMaskDC, &aPosAry, rTransparentBitmap, FALSE, SRCCOPY );
+
+ // now MemDC contains background, MaskDC the transparency mask
+
+ // #105055# Respect XOR mode
+ if( mbXORMode )
+ {
+ ImplDrawBitmap( hMaskDC, &aPosAry, rSalBitmap, FALSE, SRCERASE );
+ // now MaskDC contains the bitmap area with black background
+ BitBlt( hMemDC, 0, 0, nDstWidth, nDstHeight, hMaskDC, 0, 0, SRCINVERT );
+ // now MemDC contains background XORed bitmap area ontop
+ }
+ else
+ {
+ BitBlt( hMemDC, 0, 0, nDstWidth, nDstHeight, hMaskDC, 0, 0, SRCAND );
+ // now MemDC contains background with masked-out bitmap area
+ ImplDrawBitmap( hMaskDC, &aPosAry, rSalBitmap, FALSE, SRCERASE );
+ // now MaskDC contains the bitmap area with black background
+ BitBlt( hMemDC, 0, 0, nDstWidth, nDstHeight, hMaskDC, 0, 0, SRCPAINT );
+ // now MemDC contains background and bitmap merged together
+ }
+ // copy to output DC
+ BitBlt( hDC, nDstX, nDstY, nDstWidth, nDstHeight, hMemDC, 0, 0, SRCCOPY );
+
+ ImplReleaseCachedDC( CACHED_HDC_1 );
+ ImplReleaseCachedDC( CACHED_HDC_2 );
+
+ // hMemBitmap != 0 ==> hMaskBitmap != 0
+ if( hMemBitmap )
+ {
+ DeleteObject( hMemBitmap );
+ DeleteObject( hMaskBitmap );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool WinSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR,
+ const SalBitmap& rSrcBitmap,
+ const SalBitmap& rAlphaBmp )
+{
+ (void)rTR; (void)rSrcBitmap; (void)rAlphaBmp;
+
+ // TODO(P3): implement alpha bmp blits. Catch: Windows only
+ // handles 32bpp, premultiplied bitmaps
+ return false;
+}
+
+// -----------------------------------------------------------------------
+
+bool WinSalGraphics::drawAlphaRect( long nX, long nY, long nWidth,
+ long nHeight, sal_uInt8 nTransparency )
+{
+ if( mbPen || !mbBrush || mbXORMode )
+ return false; // can only perform solid fills without XOR.
+
+ HDC hMemDC = ImplGetCachedDC( CACHED_HDC_1, 0 );
+ SetPixel( hMemDC, (int)0, (int)0, mnBrushColor );
+
+ BLENDFUNCTION aFunc = {
+ AC_SRC_OVER,
+ 0,
+ 255 - 255L*nTransparency/100,
+ 0
+ };
+
+ // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
+ // that to dest hdc
+ bool bRet = AlphaBlend( mhDC, nX, nY, nWidth, nHeight,
+ hMemDC, 0,0,1,1,
+ aFunc ) == TRUE;
+
+ ImplReleaseCachedDC( CACHED_HDC_1 );
+
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::drawMask( const SalTwoRect* pPosAry,
+ const SalBitmap& rSSalBitmap,
+ SalColor nMaskColor )
+{
+ DBG_ASSERT( !mbPrinter, "No transparency print possible!" );
+
+ const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
+
+ SalTwoRect aPosAry = *pPosAry;
+ const BYTE cRed = SALCOLOR_RED( nMaskColor );
+ const BYTE cGreen = SALCOLOR_GREEN( nMaskColor );
+ const BYTE cBlue = SALCOLOR_BLUE( nMaskColor );
+ HDC hDC = mhDC;
+ HBRUSH hMaskBrush = CreateSolidBrush( RGB( cRed, cGreen, cBlue ) );
+ HBRUSH hOldBrush = SelectBrush( hDC, hMaskBrush );
+
+ // bei Paletten-Displays hat WIN/WNT offenbar ein kleines Problem,
+ // die Farben der Maske richtig auf die Palette abzubilden,
+ // wenn wir die DIB direkt ausgeben => DDB-Ausgabe
+ if( ( GetBitCount() <= 8 ) && rSalBitmap.ImplGethDIB() && rSalBitmap.GetBitCount() == 1 )
+ {
+ WinSalBitmap aTmp;
+
+ if( aTmp.Create( rSalBitmap, this ) )
+ ImplDrawBitmap( hDC, &aPosAry, aTmp, FALSE, 0x00B8074AUL );
+ }
+ else
+ ImplDrawBitmap( hDC, &aPosAry, rSalBitmap, FALSE, 0x00B8074AUL );
+
+ SelectBrush( hDC, hOldBrush );
+ DeleteBrush( hMaskBrush );
+}
+
+// -----------------------------------------------------------------------
+
+SalBitmap* WinSalGraphics::getBitmap( long nX, long nY, long nDX, long nDY )
+{
+ DBG_ASSERT( !mbPrinter, "No ::GetBitmap() from printer possible!" );
+
+ WinSalBitmap* pSalBitmap = NULL;
+
+ nDX = labs( nDX );
+ nDY = labs( nDY );
+
+ HDC hDC = mhDC;
+ HBITMAP hBmpBitmap = CreateCompatibleBitmap( hDC, nDX, nDY );
+ HDC hBmpDC = ImplGetCachedDC( CACHED_HDC_1, hBmpBitmap );
+ BOOL bRet;
+ DWORD err = 0;
+
+ bRet = BitBlt( hBmpDC, 0, 0, (int) nDX, (int) nDY, hDC, (int) nX, (int) nY, SRCCOPY ) ? TRUE : FALSE;
+ ImplReleaseCachedDC( CACHED_HDC_1 );
+
+ if( bRet )
+ {
+ pSalBitmap = new WinSalBitmap;
+
+ if( !pSalBitmap->Create( hBmpBitmap, FALSE, FALSE ) )
+ {
+ delete pSalBitmap;
+ pSalBitmap = NULL;
+ }
+ }
+ else
+ {
+ err = GetLastError();
+ // #124826# avoid resource leak ! happens when runing without desktop access (remote desktop, service, may be screensavers)
+ DeleteBitmap( hBmpBitmap );
+ }
+
+ return pSalBitmap;
+}
+
+// -----------------------------------------------------------------------
+
+SalColor WinSalGraphics::getPixel( long nX, long nY )
+{
+ COLORREF aWinCol = ::GetPixel( mhDC, (int) nX, (int) nY );
+
+ if ( CLR_INVALID == aWinCol )
+ return MAKE_SALCOLOR( 0, 0, 0 );
+ else
+ return MAKE_SALCOLOR( GetRValue( aWinCol ),
+ GetGValue( aWinCol ),
+ GetBValue( aWinCol ) );
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags )
+{
+ if ( nFlags & SAL_INVERT_TRACKFRAME )
+ {
+ HPEN hDotPen = CreatePen( PS_DOT, 0, 0 );
+ HPEN hOldPen = SelectPen( mhDC, hDotPen );
+ HBRUSH hOldBrush = SelectBrush( mhDC, GetStockBrush( NULL_BRUSH ) );
+ int nOldROP = SetROP2( mhDC, R2_NOT );
+
+ WIN_Rectangle( mhDC, (int)nX, (int)nY, (int)(nX+nWidth), (int)(nY+nHeight) );
+
+ SetROP2( mhDC, nOldROP );
+ SelectPen( mhDC, hOldPen );
+ SelectBrush( mhDC, hOldBrush );
+ DeletePen( hDotPen );
+ }
+ else if ( nFlags & SAL_INVERT_50 )
+ {
+ SalData* pSalData = GetSalData();
+ if ( !pSalData->mh50Brush )
+ {
+ if ( !pSalData->mh50Bmp )
+ pSalData->mh50Bmp = ImplLoadSalBitmap( SAL_RESID_BITMAP_50 );
+ pSalData->mh50Brush = CreatePatternBrush( pSalData->mh50Bmp );
+ }
+
+ COLORREF nOldTextColor = ::SetTextColor( mhDC, 0 );
+ HBRUSH hOldBrush = SelectBrush( mhDC, pSalData->mh50Brush );
+ PatBlt( mhDC, nX, nY, nWidth, nHeight, PATINVERT );
+ ::SetTextColor( mhDC, nOldTextColor );
+ SelectBrush( mhDC, hOldBrush );
+ }
+ else
+ {
+ RECT aRect;
+ aRect.left = (int)nX;
+ aRect.top = (int)nY;
+ aRect.right = (int)nX+nWidth;
+ aRect.bottom = (int)nY+nHeight;
+ ::InvertRect( mhDC, &aRect );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::invert( ULONG nPoints, const SalPoint* pPtAry, SalInvert nSalFlags )
+{
+ HPEN hPen;
+ HPEN hOldPen;
+ HBRUSH hBrush;
+ HBRUSH hOldBrush = 0;
+ COLORREF nOldTextColor RGB(0,0,0);
+ int nOldROP = SetROP2( mhDC, R2_NOT );
+
+ if ( nSalFlags & SAL_INVERT_TRACKFRAME )
+ hPen = CreatePen( PS_DOT, 0, 0 );
+ else
+ {
+
+ if ( nSalFlags & SAL_INVERT_50 )
+ {
+ SalData* pSalData = GetSalData();
+ if ( !pSalData->mh50Brush )
+ {
+ if ( !pSalData->mh50Bmp )
+ pSalData->mh50Bmp = ImplLoadSalBitmap( SAL_RESID_BITMAP_50 );
+ pSalData->mh50Brush = CreatePatternBrush( pSalData->mh50Bmp );
+ }
+
+ hBrush = pSalData->mh50Brush;
+ }
+ else
+ hBrush = GetStockBrush( BLACK_BRUSH );
+
+ hPen = GetStockPen( NULL_PEN );
+ nOldTextColor = ::SetTextColor( mhDC, 0 );
+ hOldBrush = SelectBrush( mhDC, hBrush );
+ }
+ hOldPen = SelectPen( mhDC, hPen );
+
+ POINT* pWinPtAry;
+ // Unter NT koennen wir das Array direkt weiterreichen
+ DBG_ASSERT( sizeof( POINT ) == sizeof( SalPoint ),
+ "WinSalGraphics::DrawPolyLine(): POINT != SalPoint" );
+
+ pWinPtAry = (POINT*)pPtAry;
+ // Wegen Windows 95 und der Beschraenkung auf eine maximale Anzahl
+ // von Punkten
+ if ( nSalFlags & SAL_INVERT_TRACKFRAME )
+ {
+ if ( !Polyline( mhDC, pWinPtAry, (int)nPoints ) && (nPoints > MAX_64KSALPOINTS) )
+ Polyline( mhDC, pWinPtAry, MAX_64KSALPOINTS );
+ }
+ else
+ {
+ if ( !WIN_Polygon( mhDC, pWinPtAry, (int)nPoints ) && (nPoints > MAX_64KSALPOINTS) )
+ WIN_Polygon( mhDC, pWinPtAry, MAX_64KSALPOINTS );
+ }
+
+ SetROP2( mhDC, nOldROP );
+ SelectPen( mhDC, hOldPen );
+
+ if ( nSalFlags & SAL_INVERT_TRACKFRAME )
+ DeletePen( hPen );
+ else
+ {
+ ::SetTextColor( mhDC, nOldTextColor );
+ SelectBrush( mhDC, hOldBrush );
+ }
+}
diff --git a/vcl/win/source/gdi/salgdi3.cxx b/vcl/win/source/gdi/salgdi3.cxx
new file mode 100644
index 000000000000..c8e0210196e6
--- /dev/null
+++ b/vcl/win/source/gdi/salgdi3.cxx
@@ -0,0 +1,3245 @@
+/*************************************************************************
+ *
+ * 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 <malloc.h>
+
+#include <tools/prewin.h>
+#include <windows.h>
+#include <tools/postwin.h>
+#include <vcl/sysdata.hxx>
+#include "tools/svwin.h"
+
+#include "wincomp.hxx"
+#include "saldata.hxx"
+#include "salgdi.h"
+
+#include "vcl/svapp.hxx"
+#include "vcl/outfont.hxx"
+#include "vcl/font.hxx"
+#include "vcl/fontsubset.hxx"
+#include "vcl/sallayout.hxx"
+
+#include "vcl/outdev.h" // for ImplGlyphFallbackFontSubstitution
+#include "unotools/fontcfg.hxx" // for IMPL_FONT_ATTR_SYMBOL
+
+#include "rtl/logfile.hxx"
+#include "rtl/tencinfo.h"
+#include "rtl/textcvt.h"
+#include "rtl/bootstrap.hxx"
+
+#include "i18npool/mslangid.hxx"
+
+#include "osl/module.h"
+#include "osl/file.hxx"
+#include "osl/thread.hxx"
+#include "osl/process.h"
+
+#include "tools/poly.hxx"
+#include "tools/debug.hxx"
+#include "tools/stream.hxx"
+
+#include "basegfx/polygon/b2dpolygon.hxx"
+#include "basegfx/polygon/b2dpolypolygon.hxx"
+#include "basegfx/matrix/b2dhommatrix.hxx"
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+#include "sft.hxx"
+
+#ifdef GCP_KERN_HACK
+#include <algorithm>
+#endif
+
+#ifdef ENABLE_GRAPHITE
+#include <graphite/GrClient.h>
+#include <graphite/WinFont.h>
+#endif
+
+#include <vector>
+#include <set>
+#include <map>
+
+using namespace vcl;
+
+static const int MAXFONTHEIGHT = 2048;
+
+// -----------
+// - Inlines -
+// -----------
+
+inline FIXED FixedFromDouble( double d )
+{
+ const long l = (long) ( d * 65536. );
+ return *(FIXED*) &l;
+}
+
+// -----------------------------------------------------------------------
+
+inline int IntTimes256FromFixed(FIXED f)
+{
+ int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
+ return nFixedTimes256;
+}
+
+// =======================================================================
+
+// these variables can be static because they store system wide settings
+static bool bImplSalCourierScalable = false;
+static bool bImplSalCourierNew = false;
+
+
+// =======================================================================
+
+// -----------------------------------------------------------------------
+
+// TODO: also support temporary TTC font files
+typedef std::map< String, ImplDevFontAttributes > FontAttrMap;
+
+class ImplFontAttrCache
+{
+private:
+ FontAttrMap aFontAttributes;
+ rtl::OUString aCacheFileName;
+ String aBaseURL;
+ BOOL bModified;
+
+protected:
+ String OptimizeURL( const String& rURL ) const;
+
+ enum{ MAGIC = 0x12349876 }; // change if fontattrcache format changes
+
+public:
+ ImplFontAttrCache( const String& rCacheFileName, const String& rBaseURL );
+ ~ImplFontAttrCache();
+
+ ImplDevFontAttributes GetFontAttr( const String& rFontFileName ) const;
+ void AddFontAttr( const String& rFontFileName, const ImplDevFontAttributes& );
+};
+
+ImplFontAttrCache::ImplFontAttrCache( const String& rFileNameURL, const String& rBaseURL ) : aBaseURL( rBaseURL )
+{
+ bModified = FALSE;
+ aBaseURL.ToLowerAscii(); // Windows only, no problem...
+
+ // open the cache file
+ osl::FileBase::getSystemPathFromFileURL( rFileNameURL, aCacheFileName );
+ SvFileStream aCacheFile( aCacheFileName, STREAM_READ );
+ if( !aCacheFile.IsOpen() )
+ return;
+
+ // check the cache version
+ sal_uInt32 nCacheMagic;
+ aCacheFile >> nCacheMagic;
+ if( nCacheMagic != ImplFontAttrCache::MAGIC )
+ return; // ignore cache and rewrite if no match
+
+ // read the cache entries from the file
+ String aFontFileURL, aFontName;
+ ImplDevFontAttributes aDFA;
+ for(;;)
+ {
+ aCacheFile.ReadByteString( aFontFileURL, RTL_TEXTENCODING_UTF8 );
+ if( !aFontFileURL.Len() )
+ break;
+ aCacheFile.ReadByteString( aDFA.maName, RTL_TEXTENCODING_UTF8 );
+
+ short n;
+ aCacheFile >> n; aDFA.meWeight = static_cast<FontWeight>(n);
+ aCacheFile >> n; aDFA.meItalic = static_cast<FontItalic>(n);
+ aCacheFile >> n; aDFA.mePitch = static_cast<FontPitch>(n);
+ aCacheFile >> n; aDFA.meWidthType = static_cast<FontWidth>(n);
+ aCacheFile >> n; aDFA.meFamily = static_cast<FontFamily>(n);
+ aCacheFile >> n; aDFA.mbSymbolFlag = (n != 0);
+
+ aCacheFile.ReadByteStringLine( aDFA.maStyleName, RTL_TEXTENCODING_UTF8 );
+
+ aFontAttributes[ aFontFileURL ] = aDFA;
+ }
+}
+
+ImplFontAttrCache::~ImplFontAttrCache()
+{
+ if ( bModified )
+ {
+ SvFileStream aCacheFile( aCacheFileName, STREAM_WRITE|STREAM_TRUNC );
+ if ( aCacheFile.IsWritable() )
+ {
+ sal_uInt32 nCacheMagic = ImplFontAttrCache::MAGIC;
+ aCacheFile << nCacheMagic;
+
+ // write the cache entries to the file
+ FontAttrMap::const_iterator aIter = aFontAttributes.begin();
+ while ( aIter != aFontAttributes.end() )
+ {
+ const String rFontFileURL( (*aIter).first );
+ const ImplDevFontAttributes& rDFA( (*aIter).second );
+ aCacheFile.WriteByteString( rFontFileURL, RTL_TEXTENCODING_UTF8 );
+ aCacheFile.WriteByteString( rDFA.maName, RTL_TEXTENCODING_UTF8 );
+
+ aCacheFile << static_cast<short>(rDFA.meWeight);
+ aCacheFile << static_cast<short>(rDFA.meItalic);
+ aCacheFile << static_cast<short>(rDFA.mePitch);
+ aCacheFile << static_cast<short>(rDFA.meWidthType);
+ aCacheFile << static_cast<short>(rDFA.meFamily);
+ aCacheFile << static_cast<short>(rDFA.mbSymbolFlag != false);
+
+ aCacheFile.WriteByteStringLine( rDFA.maStyleName, RTL_TEXTENCODING_UTF8 );
+
+ aIter++;
+ }
+ // EOF Marker
+ String aEmptyStr;
+ aCacheFile.WriteByteString( aEmptyStr, RTL_TEXTENCODING_UTF8 );
+ }
+ }
+}
+
+String ImplFontAttrCache::OptimizeURL( const String& rURL ) const
+{
+ String aOptimizedFontFileURL( rURL );
+ aOptimizedFontFileURL.ToLowerAscii(); // Windows only, no problem...
+ if ( aOptimizedFontFileURL.CompareTo( aBaseURL, aBaseURL.Len() ) == COMPARE_EQUAL )
+ aOptimizedFontFileURL = aOptimizedFontFileURL.Copy( aBaseURL.Len() );
+ return aOptimizedFontFileURL;
+}
+
+ImplDevFontAttributes ImplFontAttrCache::GetFontAttr( const String& rFontFileName ) const
+{
+ ImplDevFontAttributes aDFA;
+ FontAttrMap::const_iterator it = aFontAttributes.find( OptimizeURL( rFontFileName ) );
+ if( it != aFontAttributes.end() )
+ {
+ aDFA = it->second;
+ }
+ return aDFA;
+}
+
+void ImplFontAttrCache::AddFontAttr( const String& rFontFileName, const ImplDevFontAttributes& rDFA )
+{
+ DBG_ASSERT( rFontFileName.Len() && rDFA.maName.Len(), "ImplFontNameCache::AddFontName - invalid data!" );
+ if ( rFontFileName.Len() && rDFA.maName.Len() )
+ {
+ aFontAttributes.insert( FontAttrMap::value_type( OptimizeURL( rFontFileName ), rDFA ) );
+ bModified = TRUE;
+ }
+}
+
+// =======================================================================
+
+// raw font data with a scoped lifetime
+class RawFontData
+{
+public:
+ explicit RawFontData( HDC, DWORD nTableTag=0 );
+ ~RawFontData() { delete[] mpRawBytes; }
+ const unsigned char* get() const { return mpRawBytes; }
+ const unsigned char* steal() { unsigned char* p = mpRawBytes; mpRawBytes = NULL; return p; }
+ const int size() const { return mnByteCount; }
+
+private:
+ unsigned char* mpRawBytes;
+ int mnByteCount;
+};
+
+RawFontData::RawFontData( HDC hDC, DWORD nTableTag )
+: mpRawBytes( NULL )
+, mnByteCount( 0 )
+{
+ // get required size in bytes
+ mnByteCount = ::GetFontData( hDC, nTableTag, 0, NULL, 0 );
+ if( mnByteCount == GDI_ERROR )
+ return;
+ else if( !mnByteCount )
+ return;
+
+ // allocate the array
+ mpRawBytes = new unsigned char[ mnByteCount ];
+
+ // get raw data in chunks small enough for GetFontData()
+ int nRawDataOfs = 0;
+ DWORD nMaxChunkSize = 0x100000;
+ for(;;)
+ {
+ // calculate remaining raw data to get
+ DWORD nFDGet = mnByteCount - nRawDataOfs;
+ if( nFDGet <= 0 )
+ break;
+ // #i56745# limit GetFontData requests
+ if( nFDGet > nMaxChunkSize )
+ nFDGet = nMaxChunkSize;
+ const DWORD nFDGot = ::GetFontData( hDC, nTableTag, nRawDataOfs,
+ (void*)(mpRawBytes + nRawDataOfs), nFDGet );
+ if( !nFDGot )
+ break;
+ else if( nFDGot != GDI_ERROR )
+ nRawDataOfs += nFDGot;
+ else
+ {
+ // was the chunk too big? reduce it
+ nMaxChunkSize /= 2;
+ if( nMaxChunkSize < 0x10000 )
+ break;
+ }
+ }
+
+ // cleanup if the raw data is incomplete
+ if( nRawDataOfs != mnByteCount )
+ {
+ delete[] mpRawBytes;
+ mpRawBytes = NULL;
+ }
+}
+
+// ===========================================================================
+// platform specific font substitution hooks for glyph fallback enhancement
+// TODO: move into i18n module (maybe merge with svx/ucsubset.*
+// or merge with i18nutil/source/utility/unicode_data.h)
+struct Unicode2LangType
+{
+ sal_UCS4 mnMinCode;
+ sal_UCS4 mnMaxCode;
+ LanguageType mnLangID;
+};
+
+// entries marked with default-CJK get replaced with the default-CJK language
+#define LANGUAGE_DEFAULT_CJK 0xFFF0
+
+// map unicode ranges to languages supported by OOo
+// NOTE: due to the binary search used this list must be sorted by mnMinCode
+static Unicode2LangType aLangFromCodeChart[]= {
+ {0x0000, 0x007F, LANGUAGE_ENGLISH}, // Basic Latin
+ {0x0080, 0x024F, LANGUAGE_ENGLISH}, // Latin Extended-A and Latin Extended-B
+ {0x0250, 0x02AF, LANGUAGE_SYSTEM}, // IPA Extensions
+ {0x0370, 0x03FF, LANGUAGE_GREEK}, // Greek
+ {0x0590, 0x05FF, LANGUAGE_HEBREW}, // Hebrew
+ {0x0600, 0x06FF, LANGUAGE_ARABIC_PRIMARY_ONLY}, // Arabic
+ {0x0900, 0x097F, LANGUAGE_HINDI}, // Devanagari
+ {0x0980, 0x09FF, LANGUAGE_BENGALI}, // Bengali
+ {0x0A80, 0x0AFF, LANGUAGE_GUJARATI}, // Gujarati
+ {0x0B00, 0x0B7F, LANGUAGE_ORIYA}, // Oriya
+ {0x0B80, 0x0BFF, LANGUAGE_TAMIL}, // Tamil
+ {0x0C00, 0x0C7F, LANGUAGE_TELUGU}, // Telugu
+ {0x0C80, 0x0CFF, LANGUAGE_KANNADA}, // Kannada
+ {0x0D00, 0x0D7F, LANGUAGE_MALAYALAM}, // Malayalam
+ {0x0D80, 0x0D7F, LANGUAGE_SINHALESE_SRI_LANKA}, // Sinhala
+ {0x0E00, 0x0E7F, LANGUAGE_THAI}, // Thai
+ {0x0E80, 0x0EFF, LANGUAGE_LAO}, // Lao
+ {0x0F00, 0x0FFF, LANGUAGE_TIBETAN}, // Tibetan
+ {0x1000, 0x109F, LANGUAGE_BURMESE}, // Burmese
+ {0x10A0, 0x10FF, LANGUAGE_GEORGIAN}, // Georgian
+ {0x1100, 0x11FF, LANGUAGE_KOREAN}, // Hangul Jamo, Korean-specific
+// {0x1200, 0x139F, LANGUAGE_AMHARIC_ETHIOPIA}, // Ethiopic
+// {0x1200, 0x139F, LANGUAGE_TIGRIGNA_ETHIOPIA}, // Ethiopic
+ {0x13A0, 0x13FF, LANGUAGE_CHEROKEE_UNITED_STATES}, // Cherokee
+// {0x1400, 0x167F, LANGUAGE_CANADIAN_ABORIGINAL}, // Canadian Aboriginial Syllabics
+// {0x1680, 0x169F, LANGUAGE_OGHAM}, // Ogham
+// {0x16A0, 0x16F0, LANGUAGE_RUNIC}, // Runic
+// {0x1700, 0x171F, LANGUAGE_TAGALOG}, // Tagalog
+// {0x1720, 0x173F, LANGUAGE_HANUNOO}, // Hanunoo
+// {0x1740, 0x175F, LANGUAGE_BUHID}, // Buhid
+// {0x1760, 0x177F, LANGUAGE_TAGBANWA}, // Tagbanwa
+ {0x1780, 0x17FF, LANGUAGE_KHMER}, // Khmer
+ {0x18A0, 0x18AF, LANGUAGE_MONGOLIAN}, // Mongolian
+// {0x1900, 0x194F, LANGUAGE_LIMBU}, // Limbu
+// {0x1950, 0x197F, LANGUAGE_TAILE}, // Tai Le
+// {0x1980, 0x19DF, LANGUAGE_TAILUE}, // Tai Lue
+ {0x19E0, 0x19FF, LANGUAGE_KHMER}, // Khmer Symbols
+// {0x1A00, 0x1A1F, LANGUAGE_BUGINESE}, // Buginese/Lontara
+// {0x1B00, 0x1B7F, LANGUAGE_BALINESE}, // Balinese
+// {0x1D00, 0x1DFF, LANGUAGE_NONE}, // Phonetic Symbols
+ {0x1E00, 0x1EFF, LANGUAGE_ENGLISH}, // Latin Extended Additional
+ {0x1F00, 0x1FFF, LANGUAGE_GREEK}, // Greek Extended
+ {0x2C60, 0x2C7F, LANGUAGE_ENGLISH}, // Latin Extended-C
+ {0x2E80, 0x2FFf, LANGUAGE_CHINESE_SIMPLIFIED}, // CJK Radicals Supplement + Kangxi Radical + Ideographic Description Characters
+ {0x3000, 0x303F, LANGUAGE_DEFAULT_CJK}, // CJK Symbols and punctuation
+ {0x3040, 0x30FF, LANGUAGE_JAPANESE}, // Japanese Hiragana + Katakana
+ {0x3100, 0x312F, LANGUAGE_CHINESE_TRADITIONAL}, // Bopomofo
+ {0x3130, 0x318F, LANGUAGE_KOREAN}, // Hangul Compatibility Jamo, Kocrean-specific
+ {0x3190, 0x319F, LANGUAGE_JAPANESE}, // Kanbun
+ {0x31A0, 0x31BF, LANGUAGE_CHINESE_TRADITIONAL}, // Bopomofo Extended
+ {0x31C0, 0x31EF, LANGUAGE_DEFAULT_CJK}, // CJK Ideographs
+ {0x31F0, 0x31FF, LANGUAGE_JAPANESE}, // Japanese Katakana Phonetic Extensions
+ {0x3200, 0x321F, LANGUAGE_KOREAN}, // Parenthesized Hangul
+ {0x3220, 0x325F, LANGUAGE_DEFAULT_CJK}, // Parenthesized Ideographs
+ {0x3260, 0x327F, LANGUAGE_KOREAN}, // Circled Hangul
+ {0x3280, 0x32CF, LANGUAGE_DEFAULT_CJK}, // Circled Ideographs
+ {0x32d0, 0x32FF, LANGUAGE_JAPANESE}, // Japanese Circled Katakana
+ {0x3400, 0x4DBF, LANGUAGE_DEFAULT_CJK}, // CJK Unified Ideographs Extension A
+ {0x4E00, 0x9FCF, LANGUAGE_DEFAULT_CJK}, // Unified CJK Ideographs
+ {0xA720, 0xA7FF, LANGUAGE_ENGLISH}, // Latin Extended-D
+ {0xAC00, 0xD7AF, LANGUAGE_KOREAN}, // Hangul Syllables, Korean-specific
+ {0xF900, 0xFAFF, LANGUAGE_DEFAULT_CJK}, // CJK Compatibility Ideographs
+ {0xFB00, 0xFB4F, LANGUAGE_HEBREW}, // Hebrew Presentation Forms
+ {0xFB50, 0xFDFF, LANGUAGE_ARABIC_PRIMARY_ONLY}, // Arabic Presentation Forms-A
+ {0xFE70, 0xFEFE, LANGUAGE_ARABIC_PRIMARY_ONLY}, // Arabic Presentation Forms-B
+ {0xFF65, 0xFF9F, LANGUAGE_JAPANESE}, // Japanese Halfwidth Katakana variant
+ {0xFFA0, 0xFFDC, LANGUAGE_KOREAN}, // Kocrean halfwidth hangual variant
+ {0x10140, 0x1018F, LANGUAGE_GREEK}, // Ancient Greak numbers
+ {0x1D200, 0x1D24F, LANGUAGE_GREEK}, // Ancient Greek Musical
+ {0x20000, 0x2A6DF, LANGUAGE_DEFAULT_CJK}, // CJK Unified Ideographs Extension B
+ {0x2F800, 0x2FA1F, LANGUAGE_DEFAULT_CJK} // CJK Compatibility Ideographs Supplement
+};
+
+// get language matching to the missing char
+LanguageType MapCharToLanguage( sal_UCS4 uChar )
+{
+ // entries marked with default-CJK get replaced with the prefered CJK language
+ static bool bFirst = true;
+ if( bFirst )
+ {
+ bFirst = false;
+
+ // use method suggested in #i97086# to determnine the systems default language
+ // TODO: move into i18npool or sal/osl/w32/nlsupport.c
+ LanguageType nDefaultLang = 0;
+ HKEY hKey = NULL;
+ LONG lResult = ::RegOpenKeyExA( HKEY_LOCAL_MACHINE,
+ "SYSTEM\\CurrentControlSet\\Control\\Nls\\Language",
+ 0, KEY_QUERY_VALUE, &hKey );
+ char aKeyValBuf[16];
+ DWORD nKeyValSize = sizeof(aKeyValBuf);
+ if( ERROR_SUCCESS == lResult )
+ lResult = RegQueryValueExA( hKey, "Default", NULL, NULL, (LPBYTE)aKeyValBuf, &nKeyValSize );
+ aKeyValBuf[ sizeof(aKeyValBuf)-1 ] = '\0';
+ if( ERROR_SUCCESS == lResult )
+ nDefaultLang = (LanguageType)rtl_str_toInt32( aKeyValBuf, 16 );
+
+ // TODO: use the default-CJK language selected in
+ // Tools->Options->LangSettings->Languages when it becomes available here
+ if( !nDefaultLang )
+ nDefaultLang = Application::GetSettings().GetUILanguage();
+
+ LanguageType nDefaultCJK = LANGUAGE_CHINESE;
+ switch( nDefaultLang )
+ {
+ case LANGUAGE_JAPANESE:
+ case LANGUAGE_KOREAN:
+ case LANGUAGE_KOREAN_JOHAB:
+ case LANGUAGE_CHINESE_SIMPLIFIED:
+ case LANGUAGE_CHINESE_TRADITIONAL:
+ case LANGUAGE_CHINESE_SINGAPORE:
+ case LANGUAGE_CHINESE_HONGKONG:
+ case LANGUAGE_CHINESE_MACAU:
+ nDefaultCJK = nDefaultLang;
+ break;
+ default:
+ nDefaultCJK = LANGUAGE_CHINESE;
+ break;
+ }
+
+ // change the marked entries to prefered language
+ static const int nCount = (sizeof(aLangFromCodeChart) / sizeof(*aLangFromCodeChart));
+ for( int i = 0; i < nCount; ++i )
+ {
+ if( aLangFromCodeChart[ i].mnLangID == LANGUAGE_DEFAULT_CJK )
+ aLangFromCodeChart[ i].mnLangID = nDefaultCJK;
+ }
+ }
+
+ // binary search
+ int nLow = 0;
+ int nHigh = (sizeof(aLangFromCodeChart) / sizeof(*aLangFromCodeChart)) - 1;
+ while( nLow <= nHigh )
+ {
+ int nMiddle = (nHigh + nLow) / 2;
+ if( uChar < aLangFromCodeChart[ nMiddle].mnMinCode )
+ nHigh = nMiddle - 1;
+ else if( uChar > aLangFromCodeChart[ nMiddle].mnMaxCode )
+ nLow = nMiddle + 1;
+ else
+ return aLangFromCodeChart[ nMiddle].mnLangID;
+ }
+
+ return LANGUAGE_DONTKNOW;
+}
+
+class WinGlyphFallbackSubstititution
+: public ImplGlyphFallbackFontSubstitution
+{
+public:
+ explicit WinGlyphFallbackSubstititution( HDC );
+
+ bool FindFontSubstitute( ImplFontSelectData&, rtl::OUString& rMissingChars ) const;
+private:
+ HDC mhDC;
+ bool HasMissingChars( const ImplFontData*, const rtl::OUString& rMissingChars ) const;
+};
+
+inline WinGlyphFallbackSubstititution::WinGlyphFallbackSubstititution( HDC hDC )
+: mhDC( hDC )
+{}
+
+void ImplGetLogFontFromFontSelect( HDC, const ImplFontSelectData*,
+ LOGFONTW&, bool /*bTestVerticalAvail*/ );
+
+// does a font face hold the given missing characters?
+bool WinGlyphFallbackSubstititution::HasMissingChars( const ImplFontData* pFace, const rtl::OUString& rMissingChars ) const
+{
+ const ImplWinFontData* pWinFont = static_cast<const ImplWinFontData*>(pFace);
+ const ImplFontCharMap* pCharMap = pWinFont->GetImplFontCharMap();
+ if( !pCharMap )
+ {
+ // construct a Size structure as the parameter of constructor of class ImplFontSelectData
+ const Size aSize( pFace->GetWidth(), pFace->GetHeight() );
+ // create a ImplFontSelectData object for getting s LOGFONT
+ const ImplFontSelectData aFSD( *pFace, aSize, (float)aSize.Height(), 0, false );
+ // construct log font
+ LOGFONTW aLogFont;
+ ImplGetLogFontFromFontSelect( mhDC, &aFSD, aLogFont, true );
+
+ // create HFONT from log font
+ HFONT hNewFont = ::CreateFontIndirectW( &aLogFont );
+ // select the new font into device
+ HFONT hOldFont = ::SelectFont( mhDC, hNewFont );
+
+ // read CMAP table to update their pCharMap
+ pWinFont->UpdateFromHDC( mhDC );;
+
+ // cleanup temporary font
+ ::SelectFont( mhDC, hOldFont );
+ ::DeleteFont( hNewFont );
+
+ // get the new charmap
+ pCharMap = pWinFont->GetImplFontCharMap();
+ }
+
+ // avoid fonts with unknown CMAP subtables for glyph fallback
+ if( !pCharMap || pCharMap->IsDefaultMap() )
+ return false;
+ pCharMap->AddReference();
+
+ int nMatchCount = 0;
+ // static const int nMaxMatchCount = 1; // TODO: tolerate more missing characters?
+ const sal_Int32 nStrLen = rMissingChars.getLength();
+ for( sal_Int32 nStrIdx = 0; nStrIdx < nStrLen; ++nStrIdx )
+ {
+ const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
+ nMatchCount += pCharMap->HasChar( uChar );
+ break; // for now
+ }
+ pCharMap->DeReference();
+
+ const bool bHasMatches = (nMatchCount > 0);
+ return bHasMatches;
+}
+
+// find a fallback font for missing characters
+// TODO: should stylistic matches be searched and prefered?
+bool WinGlyphFallbackSubstititution::FindFontSubstitute( ImplFontSelectData& rFontSelData, rtl::OUString& rMissingChars ) const
+{
+ // guess a locale matching to the missing chars
+ com::sun::star::lang::Locale aLocale;
+
+ sal_Int32 nStrIdx = 0;
+ const sal_Int32 nStrLen = rMissingChars.getLength();
+ while( nStrIdx < nStrLen )
+ {
+ const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
+ const LanguageType eLang = MapCharToLanguage( uChar );
+ if( eLang == LANGUAGE_DONTKNOW )
+ continue;
+ MsLangId::convertLanguageToLocale( eLang, aLocale );
+ break;
+ }
+
+ // fall back to default UI locale if the missing characters are inconclusive
+ if( nStrIdx >= nStrLen )
+ aLocale = Application::GetSettings().GetUILocale();
+
+ // first level fallback:
+ // try use the locale specific default fonts defined in VCL.xcu
+ const ImplDevFontList* pDevFontList = ImplGetSVData()->maGDIData.mpScreenFontList;
+ /*const*/ ImplDevFontListData* pDevFont = pDevFontList->ImplFindByLocale( aLocale );
+ if( pDevFont )
+ {
+ const ImplFontData* pFace = pDevFont->FindBestFontFace( rFontSelData );
+ if( HasMissingChars( pFace, rMissingChars ) )
+ {
+ rFontSelData.maSearchName = pDevFont->GetSearchName();
+ return true;
+ }
+ }
+
+ // are the missing characters symbols?
+ pDevFont = pDevFontList->ImplFindByAttributes( IMPL_FONT_ATTR_SYMBOL,
+ rFontSelData.meWeight, rFontSelData.meWidthType,
+ rFontSelData.meFamily, rFontSelData.meItalic, rFontSelData.maSearchName );
+ if( pDevFont )
+ {
+ const ImplFontData* pFace = pDevFont->FindBestFontFace( rFontSelData );
+ if( HasMissingChars( pFace, rMissingChars ) )
+ {
+ rFontSelData.maSearchName = pDevFont->GetSearchName();
+ return true;
+ }
+ }
+
+ // last level fallback, check each font type face one by one
+ const ImplGetDevFontList* pTestFontList = pDevFontList->GetDevFontList();
+ // limit the count of fonts to be checked to prevent hangs
+ static const int MAX_GFBFONT_COUNT = 600;
+ int nTestFontCount = pTestFontList->Count();
+ if( nTestFontCount > MAX_GFBFONT_COUNT )
+ nTestFontCount = MAX_GFBFONT_COUNT;
+
+ for( int i = 0; i < nTestFontCount; ++i )
+ {
+ const ImplFontData* pFace = pTestFontList->Get( i );
+ if( !HasMissingChars( pFace, rMissingChars ) )
+ continue;
+ rFontSelData.maSearchName = pFace->maName;
+ return true;
+ }
+
+ return false;
+}
+
+// =======================================================================
+
+struct ImplEnumInfo
+{
+ HDC mhDC;
+ ImplDevFontList* mpList;
+ String* mpName;
+ LOGFONTA* mpLogFontA;
+ LOGFONTW* mpLogFontW;
+ UINT mnPreferedCharSet;
+ bool mbCourier;
+ bool mbImplSalCourierScalable;
+ bool mbImplSalCourierNew;
+ bool mbPrinter;
+ int mnFontCount;
+};
+
+// =======================================================================
+
+static CharSet ImplCharSetToSal( BYTE nCharSet )
+{
+ rtl_TextEncoding eTextEncoding;
+
+ if ( nCharSet == OEM_CHARSET )
+ {
+ UINT nCP = (USHORT)GetOEMCP();
+ switch ( nCP )
+ {
+ // It is unclear why these two (undefined?) code page numbers are
+ // handled specially here:
+ case 1004: eTextEncoding = RTL_TEXTENCODING_MS_1252; break;
+ case 65400: eTextEncoding = RTL_TEXTENCODING_SYMBOL; break;
+ default:
+ eTextEncoding = rtl_getTextEncodingFromWindowsCodePage(nCP);
+ break;
+ };
+ }
+ else
+ {
+ if( nCharSet )
+ eTextEncoding = rtl_getTextEncodingFromWindowsCharset( nCharSet );
+ else
+ eTextEncoding = RTL_TEXTENCODING_UNICODE;
+ }
+
+ return eTextEncoding;
+}
+
+// -----------------------------------------------------------------------
+
+static FontFamily ImplFamilyToSal( BYTE nFamily )
+{
+ switch ( nFamily & 0xF0 )
+ {
+ case FF_DECORATIVE:
+ return FAMILY_DECORATIVE;
+
+ case FF_MODERN:
+ return FAMILY_MODERN;
+
+ case FF_ROMAN:
+ return FAMILY_ROMAN;
+
+ case FF_SCRIPT:
+ return FAMILY_SCRIPT;
+
+ case FF_SWISS:
+ return FAMILY_SWISS;
+
+ default:
+ break;
+ }
+
+ return FAMILY_DONTKNOW;
+}
+
+// -----------------------------------------------------------------------
+
+static BYTE ImplFamilyToWin( FontFamily eFamily )
+{
+ switch ( eFamily )
+ {
+ case FAMILY_DECORATIVE:
+ return FF_DECORATIVE;
+
+ case FAMILY_MODERN:
+ return FF_MODERN;
+
+ case FAMILY_ROMAN:
+ return FF_ROMAN;
+
+ case FAMILY_SCRIPT:
+ return FF_SCRIPT;
+
+ case FAMILY_SWISS:
+ return FF_SWISS;
+
+ case FAMILY_SYSTEM:
+ return FF_SWISS;
+
+ default:
+ break;
+ }
+
+ return FF_DONTCARE;
+}
+
+// -----------------------------------------------------------------------
+
+static FontWeight ImplWeightToSal( int nWeight )
+{
+ if ( nWeight <= FW_THIN )
+ return WEIGHT_THIN;
+ else if ( nWeight <= FW_ULTRALIGHT )
+ return WEIGHT_ULTRALIGHT;
+ else if ( nWeight <= FW_LIGHT )
+ return WEIGHT_LIGHT;
+ else if ( nWeight < FW_MEDIUM )
+ return WEIGHT_NORMAL;
+ else if ( nWeight == FW_MEDIUM )
+ return WEIGHT_MEDIUM;
+ else if ( nWeight <= FW_SEMIBOLD )
+ return WEIGHT_SEMIBOLD;
+ else if ( nWeight <= FW_BOLD )
+ return WEIGHT_BOLD;
+ else if ( nWeight <= FW_ULTRABOLD )
+ return WEIGHT_ULTRABOLD;
+ else
+ return WEIGHT_BLACK;
+}
+
+// -----------------------------------------------------------------------
+
+static int ImplWeightToWin( FontWeight eWeight )
+{
+ switch ( eWeight )
+ {
+ case WEIGHT_THIN:
+ return FW_THIN;
+
+ case WEIGHT_ULTRALIGHT:
+ return FW_ULTRALIGHT;
+
+ case WEIGHT_LIGHT:
+ return FW_LIGHT;
+
+ case WEIGHT_SEMILIGHT:
+ case WEIGHT_NORMAL:
+ return FW_NORMAL;
+
+ case WEIGHT_MEDIUM:
+ return FW_MEDIUM;
+
+ case WEIGHT_SEMIBOLD:
+ return FW_SEMIBOLD;
+
+ case WEIGHT_BOLD:
+ return FW_BOLD;
+
+ case WEIGHT_ULTRABOLD:
+ return FW_ULTRABOLD;
+
+ case WEIGHT_BLACK:
+ return FW_BLACK;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+inline FontPitch ImplLogPitchToSal( BYTE nPitch )
+{
+ if ( nPitch & FIXED_PITCH )
+ return PITCH_FIXED;
+ else
+ return PITCH_VARIABLE;
+}
+
+// -----------------------------------------------------------------------
+
+inline FontPitch ImplMetricPitchToSal( BYTE nPitch )
+{
+ // Sausaecke bei MS !! siehe NT Hilfe
+ if ( !(nPitch & TMPF_FIXED_PITCH) )
+ return PITCH_FIXED;
+ else
+ return PITCH_VARIABLE;
+}
+
+// -----------------------------------------------------------------------
+
+inline BYTE ImplPitchToWin( FontPitch ePitch )
+{
+ if ( ePitch == PITCH_FIXED )
+ return FIXED_PITCH;
+ else if ( ePitch == PITCH_VARIABLE )
+ return VARIABLE_PITCH;
+ else
+ return DEFAULT_PITCH;
+}
+
+// -----------------------------------------------------------------------
+
+static ImplDevFontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXA& rEnumFont,
+ const NEWTEXTMETRICA& rMetric, DWORD nFontType )
+{
+ ImplDevFontAttributes aDFA;
+
+ const LOGFONTA rLogFont = rEnumFont.elfLogFont;
+
+ // get font face attributes
+ aDFA.meFamily = ImplFamilyToSal( rLogFont.lfPitchAndFamily );
+ aDFA.meWidthType = WIDTH_DONTKNOW;
+ aDFA.meWeight = ImplWeightToSal( rLogFont.lfWeight );
+ aDFA.meItalic = (rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE;
+ aDFA.mePitch = ImplLogPitchToSal( rLogFont.lfPitchAndFamily );
+ aDFA.mbSymbolFlag = (rLogFont.lfCharSet == SYMBOL_CHARSET);
+
+ // get the font face name
+ aDFA.maName = ImplSalGetUniString( rLogFont.lfFaceName );
+
+ // use the face's style name only if it looks reasonable
+ const char* pStyleName = (const char*)rEnumFont.elfStyle;
+ const char* pEnd = pStyleName + sizeof( rEnumFont.elfStyle );
+ const char* p = pStyleName;
+ for(; *p && (p < pEnd); ++p )
+ if( (0x00 < *p) && (*p < 0x20) )
+ break;
+ if( p < pEnd )
+ aDFA.maStyleName = ImplSalGetUniString( pStyleName );
+
+ // get device specific font attributes
+ aDFA.mbOrientation = (nFontType & RASTER_FONTTYPE) == 0;
+ aDFA.mbDevice = (rMetric.tmPitchAndFamily & TMPF_DEVICE) != 0;
+
+ aDFA.mbEmbeddable = false;
+ aDFA.mbSubsettable = false;
+ if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE))
+ || 0 != (rMetric.tmPitchAndFamily & TMPF_TRUETYPE))
+ aDFA.mbSubsettable = true;
+ else if( 0 != (rMetric.ntmFlags & NTM_TYPE1) ) // TODO: implement subsetting for type1 too
+ aDFA.mbEmbeddable = true;
+
+ // heuristics for font quality
+ // - standard-type1 > opentypeTT > truetype > non-standard-type1 > raster
+ // - subsetting > embedding > none
+ aDFA.mnQuality = 0;
+ if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
+ aDFA.mnQuality += 50;
+ if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
+ aDFA.mnQuality += 10;
+ if( aDFA.mbSubsettable )
+ aDFA.mnQuality += 200;
+ else if( aDFA.mbEmbeddable )
+ aDFA.mnQuality += 100;
+
+ // #i38665# prefer Type1 versions of the standard postscript fonts
+ if( aDFA.mbEmbeddable )
+ {
+ if( aDFA.maName.EqualsAscii( "AvantGarde" )
+ || aDFA.maName.EqualsAscii( "Bookman" )
+ || aDFA.maName.EqualsAscii( "Courier" )
+ || aDFA.maName.EqualsAscii( "Helvetica" )
+ || aDFA.maName.EqualsAscii( "NewCenturySchlbk" )
+ || aDFA.maName.EqualsAscii( "Palatino" )
+ || aDFA.maName.EqualsAscii( "Symbol" )
+ || aDFA.maName.EqualsAscii( "Times" )
+ || aDFA.maName.EqualsAscii( "ZapfChancery" )
+ || aDFA.maName.EqualsAscii( "ZapfDingbats" ) )
+ aDFA.mnQuality += 500;
+ }
+
+ // TODO: add alias names
+ return aDFA;
+}
+
+// -----------------------------------------------------------------------
+
+static ImplDevFontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rEnumFont,
+ const NEWTEXTMETRICW& rMetric, DWORD nFontType )
+{
+ ImplDevFontAttributes aDFA;
+
+ const LOGFONTW rLogFont = rEnumFont.elfLogFont;
+
+ // get font face attributes
+ aDFA.meFamily = ImplFamilyToSal( rLogFont.lfPitchAndFamily );
+ aDFA.meWidthType = WIDTH_DONTKNOW;
+ aDFA.meWeight = ImplWeightToSal( rLogFont.lfWeight );
+ aDFA.meItalic = (rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE;
+ aDFA.mePitch = ImplLogPitchToSal( rLogFont.lfPitchAndFamily );
+ aDFA.mbSymbolFlag = (rLogFont.lfCharSet == SYMBOL_CHARSET);
+
+ // get the font face name
+ aDFA.maName = reinterpret_cast<const sal_Unicode*>(rLogFont.lfFaceName);
+
+ // use the face's style name only if it looks reasonable
+ const wchar_t* pStyleName = rEnumFont.elfStyle;
+ const wchar_t* pEnd = pStyleName + sizeof(rEnumFont.elfStyle)/sizeof(*rEnumFont.elfStyle);
+ const wchar_t* p = pStyleName;
+ for(; *p && (p < pEnd); ++p )
+ if( *p < 0x0020 )
+ break;
+ if( p < pEnd )
+ aDFA.maStyleName = reinterpret_cast<const sal_Unicode*>(pStyleName);
+
+ // get device specific font attributes
+ aDFA.mbOrientation = (nFontType & RASTER_FONTTYPE) == 0;
+ aDFA.mbDevice = (rMetric.tmPitchAndFamily & TMPF_DEVICE) != 0;
+
+ aDFA.mbEmbeddable = false;
+ aDFA.mbSubsettable = false;
+ if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE))
+ || 0 != (rMetric.tmPitchAndFamily & TMPF_TRUETYPE))
+ aDFA.mbSubsettable = true;
+ else if( 0 != (rMetric.ntmFlags & NTM_TYPE1) ) // TODO: implement subsetting for type1 too
+ aDFA.mbEmbeddable = true;
+
+ // heuristics for font quality
+ // - standard-type1 > opentypeTT > truetype > non-standard-type1 > raster
+ // - subsetting > embedding > none
+ aDFA.mnQuality = 0;
+ if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
+ aDFA.mnQuality += 50;
+ if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
+ aDFA.mnQuality += 10;
+ if( aDFA.mbSubsettable )
+ aDFA.mnQuality += 200;
+ else if( aDFA.mbEmbeddable )
+ aDFA.mnQuality += 100;
+
+ // #i38665# prefer Type1 versions of the standard postscript fonts
+ if( aDFA.mbEmbeddable )
+ {
+ if( aDFA.maName.EqualsAscii( "AvantGarde" )
+ || aDFA.maName.EqualsAscii( "Bookman" )
+ || aDFA.maName.EqualsAscii( "Courier" )
+ || aDFA.maName.EqualsAscii( "Helvetica" )
+ || aDFA.maName.EqualsAscii( "NewCenturySchlbk" )
+ || aDFA.maName.EqualsAscii( "Palatino" )
+ || aDFA.maName.EqualsAscii( "Symbol" )
+ || aDFA.maName.EqualsAscii( "Times" )
+ || aDFA.maName.EqualsAscii( "ZapfChancery" )
+ || aDFA.maName.EqualsAscii( "ZapfDingbats" ) )
+ aDFA.mnQuality += 500;
+ }
+
+ // TODO: add alias names
+ return aDFA;
+}
+
+// -----------------------------------------------------------------------
+
+static ImplWinFontData* ImplLogMetricToDevFontDataA( const ENUMLOGFONTEXA* pLogFont,
+ const NEWTEXTMETRICA* pMetric,
+ DWORD nFontType )
+{
+ int nHeight = 0;
+ if ( nFontType & RASTER_FONTTYPE )
+ nHeight = pMetric->tmHeight - pMetric->tmInternalLeading;
+
+ ImplWinFontData* pData = new ImplWinFontData(
+ WinFont2DevFontAttributes(*pLogFont, *pMetric, nFontType),
+ nHeight,
+ pLogFont->elfLogFont.lfCharSet,
+ pMetric->tmPitchAndFamily );
+
+ return pData;
+}
+
+// -----------------------------------------------------------------------
+
+static ImplWinFontData* ImplLogMetricToDevFontDataW( const ENUMLOGFONTEXW* pLogFont,
+ const NEWTEXTMETRICW* pMetric,
+ DWORD nFontType )
+{
+ int nHeight = 0;
+ if ( nFontType & RASTER_FONTTYPE )
+ nHeight = pMetric->tmHeight - pMetric->tmInternalLeading;
+
+ ImplWinFontData* pData = new ImplWinFontData(
+ WinFont2DevFontAttributes(*pLogFont, *pMetric, nFontType),
+ nHeight,
+ pLogFont->elfLogFont.lfCharSet,
+ pMetric->tmPitchAndFamily );
+
+ return pData;
+}
+
+// -----------------------------------------------------------------------
+
+void ImplSalLogFontToFontA( HDC hDC, const LOGFONTA& rLogFont, Font& rFont )
+{
+ String aFontName( ImplSalGetUniString( rLogFont.lfFaceName ) );
+ if ( aFontName.Len() )
+ {
+ rFont.SetName( aFontName );
+ rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
+ rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
+
+ long nFontHeight = rLogFont.lfHeight;
+ if ( nFontHeight < 0 )
+ nFontHeight = -nFontHeight;
+ long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
+ if( !nDPIY )
+ nDPIY = 600;
+ nFontHeight *= 72;
+ nFontHeight += nDPIY/2;
+ nFontHeight /= nDPIY;
+ rFont.SetSize( Size( 0, nFontHeight ) );
+ rFont.SetOrientation( (short)rLogFont.lfEscapement );
+ if ( rLogFont.lfItalic )
+ rFont.SetItalic( ITALIC_NORMAL );
+ else
+ rFont.SetItalic( ITALIC_NONE );
+ if ( rLogFont.lfUnderline )
+ rFont.SetUnderline( UNDERLINE_SINGLE );
+ else
+ rFont.SetUnderline( UNDERLINE_NONE );
+ if ( rLogFont.lfStrikeOut )
+ rFont.SetStrikeout( STRIKEOUT_SINGLE );
+ else
+ rFont.SetStrikeout( STRIKEOUT_NONE );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void ImplSalLogFontToFontW( HDC hDC, const LOGFONTW& rLogFont, Font& rFont )
+{
+ XubString aFontName( reinterpret_cast<const xub_Unicode*>(rLogFont.lfFaceName) );
+ if ( aFontName.Len() )
+ {
+ rFont.SetName( aFontName );
+ rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
+ rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
+
+ long nFontHeight = rLogFont.lfHeight;
+ if ( nFontHeight < 0 )
+ nFontHeight = -nFontHeight;
+ long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
+ if( !nDPIY )
+ nDPIY = 600;
+ nFontHeight *= 72;
+ nFontHeight += nDPIY/2;
+ nFontHeight /= nDPIY;
+ rFont.SetSize( Size( 0, nFontHeight ) );
+ rFont.SetOrientation( (short)rLogFont.lfEscapement );
+ if ( rLogFont.lfItalic )
+ rFont.SetItalic( ITALIC_NORMAL );
+ else
+ rFont.SetItalic( ITALIC_NONE );
+ if ( rLogFont.lfUnderline )
+ rFont.SetUnderline( UNDERLINE_SINGLE );
+ else
+ rFont.SetUnderline( UNDERLINE_NONE );
+ if ( rLogFont.lfStrikeOut )
+ rFont.SetStrikeout( STRIKEOUT_SINGLE );
+ else
+ rFont.SetStrikeout( STRIKEOUT_NONE );
+ }
+}
+
+// =======================================================================
+
+ImplWinFontData::ImplWinFontData( const ImplDevFontAttributes& rDFS,
+ int nHeight, WIN_BYTE eWinCharSet, WIN_BYTE nPitchAndFamily )
+: ImplFontData( rDFS, 0 ),
+ meWinCharSet( eWinCharSet ),
+ mnPitchAndFamily( nPitchAndFamily ),
+ mpFontCharSets( NULL ),
+ mpUnicodeMap( NULL ),
+ mbGsubRead( false ),
+ mbDisableGlyphApi( false ),
+ mbHasKoreanRange( false ),
+ mbHasCJKSupport( false ),
+#ifdef ENABLE_GRAPHITE
+ mbHasGraphiteSupport( false ),
+#endif
+ mbHasArabicSupport ( false ),
+ mbAliasSymbolsLow( false ),
+ mbAliasSymbolsHigh( false ),
+ mnId( 0 ),
+ mpEncodingVector( NULL )
+{
+ SetBitmapSize( 0, nHeight );
+
+ if( eWinCharSet == SYMBOL_CHARSET )
+ {
+ if( (nPitchAndFamily & TMPF_TRUETYPE) != 0 )
+ {
+ // truetype fonts need their symbols as U+F0xx
+ mbAliasSymbolsHigh = true;
+ }
+ else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_DEVICE))
+ == (TMPF_VECTOR|TMPF_DEVICE) )
+ {
+ // scalable device fonts (e.g. builtin printer fonts)
+ // need their symbols as U+00xx
+ mbAliasSymbolsLow = true;
+ }
+ else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE)) == 0 )
+ {
+ // bitmap fonts need their symbols as U+F0xx
+ mbAliasSymbolsHigh = true;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+ImplWinFontData::~ImplWinFontData()
+{
+ delete[] mpFontCharSets;
+
+ if( mpUnicodeMap )
+ mpUnicodeMap->DeReference();
+ delete mpEncodingVector;
+}
+
+// -----------------------------------------------------------------------
+
+sal_IntPtr ImplWinFontData::GetFontId() const
+{
+ return mnId;
+}
+
+// -----------------------------------------------------------------------
+
+void ImplWinFontData::UpdateFromHDC( HDC hDC ) const
+{
+ // short circuit if already initialized
+ if( mpUnicodeMap != NULL )
+ return;
+
+ ReadCmapTable( hDC );
+ ReadOs2Table( hDC );
+#ifdef ENABLE_GRAPHITE
+ static const char* pDisableGraphiteText = getenv( "SAL_DISABLE_GRAPHITE" );
+ if( !pDisableGraphiteText || (pDisableGraphiteText[0] == '0') )
+ {
+ mbHasGraphiteSupport = gr::WinFont::FontHasGraphiteTables(hDC);
+ }
+#endif
+
+ // even if the font works some fonts have problems with the glyph API
+ // => the heuristic below tries to figure out which fonts have the problem
+ TEXTMETRICA aTextMetric;
+ if( ::GetTextMetricsA( hDC, &aTextMetric ) )
+ if( !(aTextMetric.tmPitchAndFamily & TMPF_TRUETYPE)
+ || (aTextMetric.tmPitchAndFamily & TMPF_DEVICE) )
+ mbDisableGlyphApi = true;
+
+#if 0
+ // #110548# more important than #107885# => TODO: better solution
+ DWORD nFLI = GetFontLanguageInfo( hDC );
+ if( 0 == (nFLI & GCP_GLYPHSHAPE) )
+ mbDisableGlyphApi = true;
+#endif
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplWinFontData::HasGSUBstitutions( HDC hDC ) const
+{
+ if( !mbGsubRead )
+ ReadGsubTable( hDC );
+ return !maGsubTable.empty();
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplWinFontData::IsGSUBstituted( sal_UCS4 cChar ) const
+{
+ return( maGsubTable.find( cChar ) != maGsubTable.end() );
+}
+
+// -----------------------------------------------------------------------
+
+const ImplFontCharMap* ImplWinFontData::GetImplFontCharMap() const
+{
+ if( !mpUnicodeMap )
+ return NULL;
+ return mpUnicodeMap;
+}
+
+// -----------------------------------------------------------------------
+
+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 signed GetSShort( const unsigned char* p ){ return((short)((p[0]<<8)+p[1]));}
+static inline DWORD CalcTag( const char p[4]) { return (p[0]+(p[1]<<8)+(p[2]<<16)+(p[3]<<24)); }
+
+void ImplWinFontData::ReadOs2Table( HDC hDC ) const
+{
+ const DWORD Os2Tag = CalcTag( "OS/2" );
+ DWORD nLength = ::GetFontData( hDC, Os2Tag, 0, NULL, 0 );
+ if( (nLength == GDI_ERROR) || !nLength )
+ return;
+ std::vector<unsigned char> aOS2map( nLength );
+ unsigned char* pOS2map = &aOS2map[0];
+ ::GetFontData( hDC, Os2Tag, 0, pOS2map, nLength );
+ sal_uInt32 nVersion = GetUShort( pOS2map );
+ if ( nVersion >= 0x0001 && nLength >= 58 )
+ {
+ // We need at least version 0x0001 (TrueType rev 1.66)
+ // to have access to the needed struct members.
+ sal_uInt32 ulUnicodeRange1 = GetUInt( pOS2map + 42 );
+ sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 );
+#if 0
+ sal_uInt32 ulUnicodeRange3 = GetUInt( pOS2map + 50 );
+ sal_uInt32 ulUnicodeRange4 = GetUInt( pOS2map + 54 );
+#endif
+
+ // Check for CJK capabilities of the current font
+ mbHasCJKSupport = (ulUnicodeRange2 & 0x2DF00000);
+ mbHasKoreanRange= (ulUnicodeRange1 & 0x10000000)
+ | (ulUnicodeRange2 & 0x01100000);
+ mbHasArabicSupport = (ulUnicodeRange1 & 0x00002000);
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void ImplWinFontData::ReadGsubTable( HDC hDC ) const
+{
+ mbGsubRead = true;
+
+ // check the existence of a GSUB table
+ const DWORD GsubTag = CalcTag( "GSUB" );
+ DWORD nRC = ::GetFontData( hDC, GsubTag, 0, NULL, 0 );
+ if( (nRC == GDI_ERROR) || !nRC )
+ return;
+
+ // parse the GSUB table through sft
+ // TODO: parse it directly
+
+ // sft needs the full font file data => get it
+ const RawFontData aRawFontData( hDC );
+ if( !aRawFontData.get() )
+ return;
+
+ // open font file
+ sal_uInt32 nFaceNum = 0;
+ if( !*aRawFontData.get() ) // TTC candidate
+ nFaceNum = ~0U; // indicate "TTC font extracts only"
+
+ TrueTypeFont* pTTFont = NULL;
+ ::OpenTTFontBuffer( (void*)aRawFontData.get(), aRawFontData.size(), nFaceNum, &pTTFont );
+ if( !pTTFont )
+ return;
+
+ // add vertically substituted characters to list
+ static const sal_Unicode aGSUBCandidates[] = {
+ 0x0020, 0x0080, // ASCII
+ 0x2000, 0x2600, // misc
+ 0x3000, 0x3100, // CJK punctutation
+ 0x3300, 0x3400, // squared words
+ 0xFF00, 0xFFF0, // halfwidth|fullwidth forms
+ 0 };
+
+ for( const sal_Unicode* pPair = aGSUBCandidates; *pPair; pPair += 2 )
+ for( sal_Unicode cChar = pPair[0]; cChar < pPair[1]; ++cChar )
+ if( ::MapChar( pTTFont, cChar, 0 ) != ::MapChar( pTTFont, cChar, 1 ) )
+ maGsubTable.insert( cChar ); // insert GSUBbed unicodes
+
+ CloseTTFont( pTTFont );
+}
+
+// -----------------------------------------------------------------------
+
+void ImplWinFontData::ReadCmapTable( HDC hDC ) const
+{
+ if( mpUnicodeMap != NULL )
+ return;
+
+ bool bIsSymbolFont = (meWinCharSet == SYMBOL_CHARSET);
+ // get the CMAP table from the font which is selected into the DC
+ const DWORD nCmapTag = CalcTag( "cmap" );
+ const RawFontData aRawFontData( hDC, nCmapTag );
+ // parse the CMAP table if available
+ if( aRawFontData.get() ) {
+ CmapResult aResult;
+ ParseCMAP( aRawFontData.get(), aRawFontData.size(), aResult );
+ mbDisableGlyphApi |= aResult.mbRecoded;
+ aResult.mbSymbolic = bIsSymbolFont;
+ if( aResult.mnRangeCount > 0 )
+ mpUnicodeMap = new ImplFontCharMap( aResult );
+ }
+
+ if( !mpUnicodeMap )
+ mpUnicodeMap = ImplFontCharMap::GetDefaultMap( bIsSymbolFont );
+ mpUnicodeMap->AddReference();
+}
+
+// =======================================================================
+
+void WinSalGraphics::SetTextColor( SalColor nSalColor )
+{
+ COLORREF aCol = PALETTERGB( SALCOLOR_RED( nSalColor ),
+ SALCOLOR_GREEN( nSalColor ),
+ SALCOLOR_BLUE( nSalColor ) );
+
+ if( !mbPrinter &&
+ GetSalData()->mhDitherPal &&
+ ImplIsSysColorEntry( nSalColor ) )
+ {
+ aCol = PALRGB_TO_RGB( aCol );
+ }
+
+ ::SetTextColor( mhDC, aCol );
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumQueryFontProcExW( const ENUMLOGFONTEXW*,
+ const NEWTEXTMETRICEXW*,
+ DWORD, LPARAM lParam )
+{
+ *((bool*)(void*)lParam) = true;
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumQueryFontProcExA( const ENUMLOGFONTEXA*,
+ const NEWTEXTMETRICEXA*,
+ DWORD, LPARAM lParam )
+{
+ *((bool*)(void*)lParam) = true;
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplIsFontAvailable( HDC hDC, const UniString& rName )
+{
+ bool bAvailable = false;
+
+ if ( aSalShlData.mbWNT )
+ {
+ // Test, if Font available
+ LOGFONTW aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+
+ UINT nNameLen = rName.Len();
+ if ( nNameLen > (sizeof( aLogFont.lfFaceName )/sizeof( wchar_t ))-1 )
+ nNameLen = (sizeof( aLogFont.lfFaceName )/sizeof( wchar_t ))-1;
+ memcpy( aLogFont.lfFaceName, rName.GetBuffer(), nNameLen*sizeof( wchar_t ) );
+ aLogFont.lfFaceName[nNameLen] = 0;
+
+ EnumFontFamiliesExW( hDC, &aLogFont, (FONTENUMPROCW)SalEnumQueryFontProcExW,
+ (LPARAM)(void*)&bAvailable, 0 );
+ }
+ else
+ {
+ ByteString aTemp = ImplSalGetWinAnsiString( rName );
+
+ // Test, if Font available
+ LOGFONTA aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+
+ UINT nNameLen = aTemp.Len();
+ if ( nNameLen > sizeof( aLogFont.lfFaceName )-1 )
+ nNameLen = sizeof( aLogFont.lfFaceName )-1;
+ memcpy( aLogFont.lfFaceName, aTemp.GetBuffer(), nNameLen );
+ aLogFont.lfFaceName[nNameLen] = 0;
+
+ EnumFontFamiliesExA( hDC, &aLogFont, (FONTENUMPROCA)SalEnumQueryFontProcExA,
+ (LPARAM)(void*)&bAvailable, 0 );
+ }
+
+ return bAvailable;
+}
+
+// -----------------------------------------------------------------------
+
+void ImplGetLogFontFromFontSelect( HDC hDC,
+ const ImplFontSelectData* pFont,
+ LOGFONTW& rLogFont,
+ bool /*bTestVerticalAvail*/ )
+{
+ UniString aName;
+ if ( pFont->mpFontData )
+ aName = pFont->mpFontData->maName;
+ else
+ aName = pFont->maName.GetToken( 0 );
+
+ UINT nNameLen = aName.Len();
+ if ( nNameLen > (sizeof( rLogFont.lfFaceName )/sizeof( wchar_t ))-1 )
+ nNameLen = (sizeof( rLogFont.lfFaceName )/sizeof( wchar_t ))-1;
+ memcpy( rLogFont.lfFaceName, aName.GetBuffer(), nNameLen*sizeof( wchar_t ) );
+ rLogFont.lfFaceName[nNameLen] = 0;
+
+ if( !pFont->mpFontData )
+ {
+ rLogFont.lfCharSet = pFont->IsSymbolFont() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
+ rLogFont.lfPitchAndFamily = ImplPitchToWin( pFont->mePitch )
+ | ImplFamilyToWin( pFont->meFamily );
+ }
+ else
+ {
+ const ImplWinFontData* pWinFontData = static_cast<const ImplWinFontData*>( pFont->mpFontData );
+ rLogFont.lfCharSet = pWinFontData->GetCharSet();
+ rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
+ }
+
+ rLogFont.lfWeight = ImplWeightToWin( pFont->meWeight );
+ rLogFont.lfHeight = (LONG)-pFont->mnHeight;
+ rLogFont.lfWidth = (LONG)pFont->mnWidth;
+ rLogFont.lfUnderline = 0;
+ rLogFont.lfStrikeOut = 0;
+ rLogFont.lfItalic = (pFont->meItalic) != ITALIC_NONE;
+ rLogFont.lfEscapement = pFont->mnOrientation;
+ rLogFont.lfOrientation = rLogFont.lfEscapement;
+ rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ rLogFont.lfQuality = DEFAULT_QUALITY;
+ rLogFont.lfOutPrecision = OUT_TT_PRECIS;
+ if ( pFont->mnOrientation )
+ rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
+
+ // disable antialiasing if requested
+ if ( pFont->mbNonAntialiased )
+ rLogFont.lfQuality = NONANTIALIASED_QUALITY;
+
+ // select vertical mode if requested and available
+ if( pFont->mbVertical && nNameLen )
+ {
+ // vertical fonts start with an '@'
+ memmove( &rLogFont.lfFaceName[1], &rLogFont.lfFaceName[0],
+ sizeof(rLogFont.lfFaceName)-sizeof(rLogFont.lfFaceName[0]) );
+ rLogFont.lfFaceName[0] = '@';
+
+ // check availability of vertical mode for this font
+ bool bAvailable = false;
+ EnumFontFamiliesExW( hDC, &rLogFont, (FONTENUMPROCW)SalEnumQueryFontProcExW,
+ (LPARAM)&bAvailable, 0 );
+
+ if( !bAvailable )
+ {
+ // restore non-vertical name if not vertical mode isn't available
+ memcpy( &rLogFont.lfFaceName[0], aName.GetBuffer(), nNameLen*sizeof(wchar_t) );
+ if( nNameLen < LF_FACESIZE )
+ rLogFont.lfFaceName[nNameLen] = '\0';
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static void ImplGetLogFontFromFontSelect( HDC hDC,
+ const ImplFontSelectData* pFont,
+ LOGFONTA& rLogFont,
+ bool /*bTestVerticalAvail*/ )
+{
+ ByteString aName;
+ if( pFont->mpFontData )
+ aName = ImplSalGetWinAnsiString( pFont->mpFontData->maName );
+ else
+ aName = ImplSalGetWinAnsiString( pFont->maName.GetToken( 0 ) );
+
+ int nNameLen = aName.Len();
+ if( nNameLen > LF_FACESIZE )
+ nNameLen = LF_FACESIZE;
+ memcpy( rLogFont.lfFaceName, aName.GetBuffer(), nNameLen );
+ if( nNameLen < LF_FACESIZE )
+ rLogFont.lfFaceName[nNameLen] = '\0';
+
+ if( !pFont->mpFontData )
+ {
+ rLogFont.lfCharSet = pFont->IsSymbolFont() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
+ rLogFont.lfPitchAndFamily = ImplPitchToWin( pFont->mePitch )
+ | ImplFamilyToWin( pFont->meFamily );
+ }
+ else
+ {
+ const ImplWinFontData* pWinFontData = static_cast<const ImplWinFontData*>( pFont->mpFontData );
+ rLogFont.lfCharSet = pWinFontData->GetCharSet();
+ rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
+ }
+
+ rLogFont.lfWeight = ImplWeightToWin( pFont->meWeight );
+ rLogFont.lfHeight = (LONG)-pFont->mnHeight;
+ rLogFont.lfWidth = (LONG)pFont->mnWidth;
+ rLogFont.lfUnderline = 0;
+ rLogFont.lfStrikeOut = 0;
+ rLogFont.lfItalic = (pFont->meItalic) != ITALIC_NONE;
+ rLogFont.lfEscapement = pFont->mnOrientation;
+ rLogFont.lfOrientation = rLogFont.lfEscapement; // ignored by W98
+ rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ rLogFont.lfQuality = DEFAULT_QUALITY;
+ rLogFont.lfOutPrecision = OUT_TT_PRECIS;
+ if( pFont->mnOrientation )
+ rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
+
+ // disable antialiasing if requested
+ if( pFont->mbNonAntialiased )
+ rLogFont.lfQuality = NONANTIALIASED_QUALITY;
+
+ // select vertical mode if requested and available
+ if( pFont->mbVertical && nNameLen )
+ {
+ // vertical fonts start with an '@'
+ memmove( &rLogFont.lfFaceName[1], &rLogFont.lfFaceName[0],
+ sizeof(rLogFont.lfFaceName)-sizeof(rLogFont.lfFaceName[0]) );
+ rLogFont.lfFaceName[0] = '@';
+
+ // check availability of vertical mode for this font
+ bool bAvailable = false;
+ EnumFontFamiliesExA( hDC, &rLogFont, (FONTENUMPROCA)SalEnumQueryFontProcExA,
+ (LPARAM)&bAvailable, 0 );
+
+ if( !bAvailable )
+ {
+ // restore non-vertical name if vertical mode is not supported
+ memcpy( rLogFont.lfFaceName, aName.GetBuffer(), nNameLen );
+ if( nNameLen < LF_FACESIZE )
+ rLogFont.lfFaceName[nNameLen] = '\0';
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+HFONT WinSalGraphics::ImplDoSetFont( ImplFontSelectData* i_pFont, float& o_rFontScale, HFONT& o_rOldFont )
+{
+ HFONT hNewFont = 0;
+
+ HDC hdcScreen = 0;
+ if( mbVirDev )
+ // only required for virtual devices, see below for details
+ hdcScreen = GetDC(0);
+
+ if( aSalShlData.mbWNT )
+ {
+ LOGFONTW aLogFont;
+ ImplGetLogFontFromFontSelect( mhDC, i_pFont, aLogFont, true );
+
+ // on the display we prefer Courier New when Courier is a
+ // bitmap only font and we need to stretch or rotate it
+ if( mbScreen
+ && (i_pFont->mnWidth != 0
+ || i_pFont->mnOrientation != 0
+ || i_pFont->mpFontData == NULL
+ || (i_pFont->mpFontData->GetHeight() != i_pFont->mnHeight))
+ && !bImplSalCourierScalable
+ && bImplSalCourierNew
+ && (ImplSalWICompareAscii( aLogFont.lfFaceName, "Courier" ) == 0) )
+ lstrcpynW( aLogFont.lfFaceName, L"Courier New", 11 );
+
+ // #i47675# limit font requests to MAXFONTHEIGHT
+ // TODO: share MAXFONTHEIGHT font instance
+ if( (-aLogFont.lfHeight <= MAXFONTHEIGHT)
+ && (+aLogFont.lfWidth <= MAXFONTHEIGHT) )
+ {
+ o_rFontScale = 1.0;
+ }
+ else if( -aLogFont.lfHeight >= +aLogFont.lfWidth )
+ {
+ o_rFontScale = -aLogFont.lfHeight / (float)MAXFONTHEIGHT;
+ aLogFont.lfHeight = -MAXFONTHEIGHT;
+ aLogFont.lfWidth = FRound( aLogFont.lfWidth / o_rFontScale );
+ }
+ else // #i95867# also limit font widths
+ {
+ o_rFontScale = +aLogFont.lfWidth / (float)MAXFONTHEIGHT;
+ aLogFont.lfWidth = +MAXFONTHEIGHT;
+ aLogFont.lfHeight = FRound( aLogFont.lfHeight / o_rFontScale );
+ }
+
+ hNewFont = ::CreateFontIndirectW( &aLogFont );
+ if( hdcScreen )
+ {
+ // select font into screen hdc first to get an antialiased font
+ // see knowledge base article 305290:
+ // "PRB: Fonts Not Drawn Antialiased on Device Context for DirectDraw Surface"
+ SelectFont( hdcScreen, SelectFont( hdcScreen , hNewFont ) );
+ }
+ o_rOldFont = ::SelectFont( mhDC, hNewFont );
+
+ TEXTMETRICW aTextMetricW;
+ if( !::GetTextMetricsW( mhDC, &aTextMetricW ) )
+ {
+ // the selected font doesn't work => try a replacement
+ // TODO: use its font fallback instead
+ lstrcpynW( aLogFont.lfFaceName, L"Courier New", 11 );
+ aLogFont.lfPitchAndFamily = FIXED_PITCH;
+ HFONT hNewFont2 = CreateFontIndirectW( &aLogFont );
+ SelectFont( mhDC, hNewFont2 );
+ DeleteFont( hNewFont );
+ hNewFont = hNewFont2;
+ }
+ }
+ else
+ {
+ if( !mpLogFont )
+ // mpLogFont is needed for getting the kerning pairs
+ // TODO: get them from somewhere else
+ mpLogFont = new LOGFONTA;
+ LOGFONTA& aLogFont = *mpLogFont;
+ ImplGetLogFontFromFontSelect( mhDC, i_pFont, aLogFont, true );
+
+ // on the display we prefer Courier New when Courier is a
+ // bitmap only font and we need to stretch or rotate it
+ if( mbScreen
+ && (i_pFont->mnWidth != 0
+ || i_pFont->mnOrientation != 0
+ || i_pFont->mpFontData == NULL
+ || (i_pFont->mpFontData->GetHeight() != i_pFont->mnHeight))
+ && !bImplSalCourierScalable
+ && bImplSalCourierNew
+ && (stricmp( aLogFont.lfFaceName, "Courier" ) == 0) )
+ strncpy( aLogFont.lfFaceName, "Courier New", 11 );
+
+ // limit font requests to MAXFONTHEIGHT to work around driver problems
+ // TODO: share MAXFONTHEIGHT font instance
+ if( -aLogFont.lfHeight <= MAXFONTHEIGHT )
+ o_rFontScale = 1.0;
+ else
+ {
+ o_rFontScale = -aLogFont.lfHeight / (float)MAXFONTHEIGHT;
+ aLogFont.lfHeight = -MAXFONTHEIGHT;
+ aLogFont.lfWidth = static_cast<LONG>( aLogFont.lfWidth / o_rFontScale );
+ }
+
+ hNewFont = ::CreateFontIndirectA( &aLogFont );
+ if( hdcScreen )
+ {
+ // select font into screen hdc first to get an antialiased font
+ // see knowledge base article 305290:
+ // "PRB: Fonts Not Drawn Antialiased on Device Context for DirectDraw Surface"
+ ::SelectFont( hdcScreen, ::SelectFont( hdcScreen , hNewFont ) );
+ }
+ o_rOldFont = ::SelectFont( mhDC, hNewFont );
+
+ TEXTMETRICA aTextMetricA;
+ // when the font doesn't work try a replacement
+ if ( !::GetTextMetricsA( mhDC, &aTextMetricA ) )
+ {
+ // the selected font doesn't work => try a replacement
+ // TODO: use its font fallback instead
+ LOGFONTA aTempLogFont = aLogFont;
+ strncpy( aTempLogFont.lfFaceName, "Courier New", 11 );
+ aTempLogFont.lfPitchAndFamily = FIXED_PITCH;
+ HFONT hNewFont2 = CreateFontIndirectA( &aTempLogFont );
+ ::SelectFont( mhDC, hNewFont2 );
+ ::DeleteFont( hNewFont );
+ hNewFont = hNewFont2;
+ }
+ }
+
+ if( hdcScreen )
+ ::ReleaseDC( NULL, hdcScreen );
+
+ return hNewFont;
+}
+
+USHORT WinSalGraphics::SetFont( ImplFontSelectData* pFont, int nFallbackLevel )
+{
+ // return early if there is no new font
+ if( !pFont )
+ {
+ // deselect still active font
+ if( mhDefFont )
+ ::SelectFont( mhDC, mhDefFont );
+ // release no longer referenced font handles
+ for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
+ {
+ if( mhFonts[i] )
+ ::DeleteFont( mhFonts[i] );
+ mhFonts[ i ] = 0;
+ }
+ mhDefFont = 0;
+ return 0;
+ }
+
+ DBG_ASSERT( pFont->mpFontData, "WinSalGraphics mpFontData==NULL");
+ mpWinFontEntry[ nFallbackLevel ] = reinterpret_cast<ImplWinFontEntry*>( pFont->mpFontEntry );
+ mpWinFontData[ nFallbackLevel ] = static_cast<const ImplWinFontData*>( pFont->mpFontData );
+
+ HFONT hOldFont = 0;
+ HFONT hNewFont = ImplDoSetFont( pFont, mfFontScale, hOldFont );
+
+ if( !mhDefFont )
+ {
+ // keep default font
+ mhDefFont = hOldFont;
+ }
+ else
+ {
+ // release no longer referenced font handles
+ for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
+ {
+ if( mhFonts[i] )
+ {
+ ::DeleteFont( mhFonts[i] );
+ mhFonts[i] = 0;
+ }
+ }
+ }
+
+ // store new font in correct layer
+ mhFonts[ nFallbackLevel ] = hNewFont;
+ // now the font is live => update font face
+ if( mpWinFontData[ nFallbackLevel ] )
+ mpWinFontData[ nFallbackLevel ]->UpdateFromHDC( mhDC );
+
+ if( !nFallbackLevel )
+ {
+ mbFontKernInit = TRUE;
+ if ( mpFontKernPairs )
+ {
+ delete[] mpFontKernPairs;
+ mpFontKernPairs = NULL;
+ }
+ mnFontKernPairCount = 0;
+ }
+
+ mnFontCharSetCount = 0;
+
+ // some printers have higher internal resolution, so their
+ // text output would be different from what we calculated
+ // => suggest DrawTextArray to workaround this problem
+ if ( mbPrinter )
+ return SAL_SETFONT_USEDRAWTEXTARRAY;
+ else
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel )
+{
+ // temporarily change the HDC to the font in the fallback level
+ HFONT hOldFont = SelectFont( mhDC, mhFonts[nFallbackLevel] );
+
+ if ( aSalShlData.mbWNT )
+ {
+ wchar_t aFaceName[LF_FACESIZE+60];
+ if( ::GetTextFaceW( mhDC, sizeof(aFaceName)/sizeof(wchar_t), aFaceName ) )
+ pMetric->maName = reinterpret_cast<const sal_Unicode*>(aFaceName);
+ }
+ else
+ {
+ char aFaceName[LF_FACESIZE+60];
+ if( ::GetTextFaceA( mhDC, sizeof(aFaceName), aFaceName ) )
+ pMetric->maName = ImplSalGetUniString( aFaceName );
+ }
+
+ // get the font metric
+ TEXTMETRICA aWinMetric;
+ const bool bOK = GetTextMetricsA( mhDC, &aWinMetric );
+ // restore the HDC to the font in the base level
+ SelectFont( mhDC, hOldFont );
+ if( !bOK )
+ return;
+
+ // device independent font attributes
+ pMetric->meFamily = ImplFamilyToSal( aWinMetric.tmPitchAndFamily );;
+ pMetric->mbSymbolFlag = (aWinMetric.tmCharSet == SYMBOL_CHARSET);
+ pMetric->meWeight = ImplWeightToSal( aWinMetric.tmWeight );
+ pMetric->mePitch = ImplMetricPitchToSal( aWinMetric.tmPitchAndFamily );
+ pMetric->meItalic = aWinMetric.tmItalic ? ITALIC_NORMAL : ITALIC_NONE;
+ pMetric->mnSlant = 0;
+
+ // device dependend font attributes
+ pMetric->mbDevice = (aWinMetric.tmPitchAndFamily & TMPF_DEVICE) != 0;
+ pMetric->mbScalableFont = (aWinMetric.tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE)) != 0;
+ if( pMetric->mbScalableFont )
+ {
+ // check if there are kern pairs
+ // TODO: does this work with GPOS kerning?
+ DWORD nKernPairs = ::GetKerningPairsA( mhDC, 0, NULL );
+ pMetric->mbKernableFont = (nKernPairs > 0);
+ }
+ else
+ {
+ // bitmap fonts cannot be rotated directly
+ pMetric->mnOrientation = 0;
+ // bitmap fonts have no kerning
+ pMetric->mbKernableFont = false;
+ }
+
+ // transformation dependend font metrics
+ pMetric->mnWidth = static_cast<int>( mfFontScale * aWinMetric.tmAveCharWidth );
+ pMetric->mnIntLeading = static_cast<int>( mfFontScale * aWinMetric.tmInternalLeading );
+ pMetric->mnExtLeading = static_cast<int>( mfFontScale * aWinMetric.tmExternalLeading );
+ pMetric->mnAscent = static_cast<int>( mfFontScale * aWinMetric.tmAscent );
+ pMetric->mnDescent = static_cast<int>( mfFontScale * aWinMetric.tmDescent );
+
+ // #107888# improved metric compatibility for Asian fonts...
+ // TODO: assess workaround below for CWS >= extleading
+ // TODO: evaluate use of aWinMetric.sTypo* members for CJK
+ if( mpWinFontData[nFallbackLevel] && mpWinFontData[nFallbackLevel]->SupportsCJK() )
+ {
+ pMetric->mnIntLeading += pMetric->mnExtLeading;
+
+ // #109280# The line height for Asian fonts is too small.
+ // Therefore we add half of the external leading to the
+ // ascent, the other half is added to the descent.
+ const long nHalfTmpExtLeading = pMetric->mnExtLeading / 2;
+ const long nOtherHalfTmpExtLeading = pMetric->mnExtLeading - nHalfTmpExtLeading;
+
+ // #110641# external leading for Asian fonts.
+ // The factor 0.3 has been confirmed with experiments.
+ long nCJKExtLeading = static_cast<long>(0.30 * (pMetric->mnAscent + pMetric->mnDescent));
+ nCJKExtLeading -= pMetric->mnExtLeading;
+ pMetric->mnExtLeading = (nCJKExtLeading > 0) ? nCJKExtLeading : 0;
+
+ pMetric->mnAscent += nHalfTmpExtLeading;
+ pMetric->mnDescent += nOtherHalfTmpExtLeading;
+
+ // #109280# HACK korean only: increase descent for wavelines and impr
+ if( !aSalShlData.mbWNT )
+ if( mpWinFontData[nFallbackLevel]->SupportsKorean() )
+ pMetric->mnDescent += pMetric->mnExtLeading;
+ }
+
+ pMetric->mnMinKashida = GetMinKashidaWidth();
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumCharSetsProcExA( const ENUMLOGFONTEXA* pLogFont,
+ const NEWTEXTMETRICEXA* /*pMetric*/,
+ DWORD /*nFontType*/, LPARAM lParam )
+{
+ WinSalGraphics* pData = (WinSalGraphics*)lParam;
+ // Charset already in the list?
+ for ( BYTE i = 0; i < pData->mnFontCharSetCount; i++ )
+ {
+ if ( pData->mpFontCharSets[i] == pLogFont->elfLogFont.lfCharSet )
+ return 1;
+ }
+ pData->mpFontCharSets[pData->mnFontCharSetCount] = pLogFont->elfLogFont.lfCharSet;
+ pData->mnFontCharSetCount++;
+ return 1;
+}
+
+// -----------------------------------------------------------------------
+
+static void ImplGetAllFontCharSets( WinSalGraphics* pData )
+{
+ if ( !pData->mpFontCharSets )
+ pData->mpFontCharSets = new BYTE[256];
+
+ LOGFONTA aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+ GetTextFaceA( pData->mhDC, sizeof( aLogFont.lfFaceName ), aLogFont.lfFaceName );
+ EnumFontFamiliesExA( pData->mhDC, &aLogFont, (FONTENUMPROCA)SalEnumCharSetsProcExA,
+ (LPARAM)(void*)pData, 0 );
+}
+
+// -----------------------------------------------------------------------
+
+static void ImplAddKerningPairs( WinSalGraphics* pData )
+{
+ ULONG nPairs = ::GetKerningPairsA( pData->mhDC, 0, NULL );
+ if ( !nPairs )
+ return;
+
+ CHARSETINFO aInfo;
+ if ( !TranslateCharsetInfo( (DWORD*)(ULONG)GetTextCharset( pData->mhDC ), &aInfo, TCI_SRCCHARSET ) )
+ return;
+
+ if ( !pData->mpFontKernPairs )
+ pData->mpFontKernPairs = new KERNINGPAIR[nPairs];
+ else
+ {
+ KERNINGPAIR* pOldPairs = pData->mpFontKernPairs;
+ pData->mpFontKernPairs = new KERNINGPAIR[nPairs+pData->mnFontKernPairCount];
+ memcpy( pData->mpFontKernPairs, pOldPairs,
+ pData->mnFontKernPairCount*sizeof( KERNINGPAIR ) );
+ delete[] pOldPairs;
+ }
+
+ UINT nCP = aInfo.ciACP;
+ ULONG nOldPairs = pData->mnFontKernPairCount;
+ KERNINGPAIR* pTempPair = pData->mpFontKernPairs+pData->mnFontKernPairCount;
+ nPairs = ::GetKerningPairsA( pData->mhDC, nPairs, pTempPair );
+ for ( ULONG i = 0; i < nPairs; i++ )
+ {
+ unsigned char aBuf[2];
+ wchar_t nChar;
+ int nLen;
+ BOOL bAdd = TRUE;
+
+ // None-ASCII?, then we must convert the char
+ if ( (pTempPair->wFirst > 125) || (pTempPair->wFirst == 92) )
+ {
+ if ( pTempPair->wFirst < 256 )
+ {
+ aBuf[0] = (unsigned char)pTempPair->wFirst;
+ nLen = 1;
+ }
+ else
+ {
+ aBuf[0] = (unsigned char)(pTempPair->wFirst >> 8);
+ aBuf[1] = (unsigned char)(pTempPair->wFirst & 0xFF);
+ nLen = 2;
+ }
+ if ( MultiByteToWideChar( nCP, MB_PRECOMPOSED | MB_USEGLYPHCHARS,
+ (const char*)aBuf, nLen, &nChar, 1 ) )
+ pTempPair->wFirst = nChar;
+ else
+ bAdd = FALSE;
+ }
+ if ( (pTempPair->wSecond > 125) || (pTempPair->wSecond == 92) )
+ {
+ if ( pTempPair->wSecond < 256 )
+ {
+ aBuf[0] = (unsigned char)pTempPair->wSecond;
+ nLen = 1;
+ }
+ else
+ {
+ aBuf[0] = (unsigned char)(pTempPair->wSecond >> 8);
+ aBuf[1] = (unsigned char)(pTempPair->wSecond & 0xFF);
+ nLen = 2;
+ }
+ if ( MultiByteToWideChar( nCP, MB_PRECOMPOSED | MB_USEGLYPHCHARS,
+ (const char*)aBuf, nLen, &nChar, 1 ) )
+ pTempPair->wSecond = nChar;
+ else
+ bAdd = FALSE;
+ }
+
+ // TODO: get rid of linear search!
+ KERNINGPAIR* pTempPair2 = pData->mpFontKernPairs;
+ for ( ULONG j = 0; j < nOldPairs; j++ )
+ {
+ if ( (pTempPair2->wFirst == pTempPair->wFirst) &&
+ (pTempPair2->wSecond == pTempPair->wSecond) )
+ {
+ bAdd = FALSE;
+ break;
+ }
+ pTempPair2++;
+ }
+
+ if ( bAdd )
+ {
+ KERNINGPAIR* pDestPair = pData->mpFontKernPairs+pData->mnFontKernPairCount;
+ if ( pDestPair != pTempPair )
+ memcpy( pDestPair, pTempPair, sizeof( KERNINGPAIR ) );
+ pData->mnFontKernPairCount++;
+ }
+
+ pTempPair++;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+ULONG WinSalGraphics::GetKernPairs( ULONG nPairs, ImplKernPairData* pKernPairs )
+{
+ DBG_ASSERT( sizeof( KERNINGPAIR ) == sizeof( ImplKernPairData ),
+ "WinSalGraphics::GetKernPairs(): KERNINGPAIR != ImplKernPairData" );
+
+ if ( mbFontKernInit )
+ {
+ if( mpFontKernPairs )
+ {
+ delete[] mpFontKernPairs;
+ mpFontKernPairs = NULL;
+ }
+ mnFontKernPairCount = 0;
+
+ if ( aSalShlData.mbWNT )
+ {
+ KERNINGPAIR* pPairs = NULL;
+ int nCount = ::GetKerningPairsW( mhDC, 0, NULL );
+ if( nCount )
+ {
+#ifdef GCP_KERN_HACK
+ pPairs = new KERNINGPAIR[ nCount+1 ];
+ mpFontKernPairs = pPairs;
+ mnFontKernPairCount = nCount;
+ ::GetKerningPairsW( mhDC, nCount, pPairs );
+#else // GCP_KERN_HACK
+ pPairs = pKernPairs;
+ nCount = (nCount < nPairs) : nCount : nPairs;
+ ::GetKerningPairsW( mhDC, nCount, pPairs );
+ return nCount;
+#endif // GCP_KERN_HACK
+ }
+ }
+ else
+ {
+ if ( !mnFontCharSetCount )
+ ImplGetAllFontCharSets( this );
+
+ if ( mnFontCharSetCount <= 1 )
+ ImplAddKerningPairs( this );
+ else
+ {
+ // Query All Kerning Pairs from all possible CharSets
+ for ( BYTE i = 0; i < mnFontCharSetCount; i++ )
+ {
+ mpLogFont->lfCharSet = mpFontCharSets[i];
+ HFONT hNewFont = CreateFontIndirectA( mpLogFont );
+ HFONT hOldFont = SelectFont( mhDC, hNewFont );
+ ImplAddKerningPairs( this );
+ SelectFont( mhDC, hOldFont );
+ DeleteFont( hNewFont );
+ }
+ }
+ }
+
+ mbFontKernInit = FALSE;
+
+ std::sort( mpFontKernPairs, mpFontKernPairs + mnFontKernPairCount, ImplCmpKernData );
+ }
+
+ if( !pKernPairs )
+ return mnFontKernPairCount;
+ else if( mpFontKernPairs )
+ {
+ if ( nPairs < mnFontKernPairCount )
+ nPairs = mnFontKernPairCount;
+ memcpy( pKernPairs, mpFontKernPairs,
+ nPairs*sizeof( ImplKernPairData ) );
+ return nPairs;
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+const ImplFontCharMap* WinSalGraphics::GetImplFontCharMap() const
+{
+ if( !mpWinFontData[0] )
+ return ImplFontCharMap::GetDefaultMap();
+ return mpWinFontData[0]->GetImplFontCharMap();
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumFontsProcExA( const ENUMLOGFONTEXA* pLogFont,
+ const NEWTEXTMETRICEXA* pMetric,
+ DWORD nFontType, LPARAM lParam )
+{
+ ImplEnumInfo* pInfo = (ImplEnumInfo*)(void*)lParam;
+ if ( !pInfo->mpName )
+ {
+ // Ignore vertical fonts
+ if ( pLogFont->elfLogFont.lfFaceName[0] != '@' )
+ {
+ if ( !pInfo->mbImplSalCourierNew )
+ pInfo->mbImplSalCourierNew = stricmp( pLogFont->elfLogFont.lfFaceName, "Courier New" ) == 0;
+ if ( !pInfo->mbImplSalCourierScalable )
+ pInfo->mbCourier = stricmp( pLogFont->elfLogFont.lfFaceName, "Courier" ) == 0;
+ else
+ pInfo->mbCourier = FALSE;
+ String aName( ImplSalGetUniString( pLogFont->elfLogFont.lfFaceName ) );
+ pInfo->mpName = &aName;
+ strncpy( pInfo->mpLogFontA->lfFaceName, pLogFont->elfLogFont.lfFaceName, LF_FACESIZE );
+ pInfo->mpLogFontA->lfCharSet = pLogFont->elfLogFont.lfCharSet;
+ EnumFontFamiliesExA( pInfo->mhDC, pInfo->mpLogFontA, (FONTENUMPROCA)SalEnumFontsProcExA,
+ (LPARAM)(void*)pInfo, 0 );
+ pInfo->mpLogFontA->lfFaceName[0] = '\0';
+ pInfo->mpLogFontA->lfCharSet = DEFAULT_CHARSET;
+ pInfo->mpName = NULL;
+ pInfo->mbCourier = FALSE;
+ }
+ }
+ else
+ {
+ // ignore non-scalable non-device font on printer
+ if( pInfo->mbPrinter )
+ if( (nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE) )
+ return 1;
+
+ ImplWinFontData* pData = ImplLogMetricToDevFontDataA( pLogFont, &(pMetric->ntmTm), nFontType );
+ pData->SetFontId( sal_IntPtr( pInfo->mnFontCount++ ) );
+
+ // prefer the system character set, so that we get as much as
+ // possible important characters. In the other case we could only
+ // display a limited set of characters (#87309#)
+ if ( pInfo->mnPreferedCharSet == pLogFont->elfLogFont.lfCharSet )
+ pData->mnQuality += 100;
+
+ // knowing Courier to be scalable is nice
+ if( pInfo->mbCourier )
+ pInfo->mbImplSalCourierScalable |= pData->IsScalable();
+
+ pInfo->mpList->Add( pData );
+ }
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumFontsProcExW( const ENUMLOGFONTEXW* pLogFont,
+ const NEWTEXTMETRICEXW* pMetric,
+ DWORD nFontType, LPARAM lParam )
+{
+ ImplEnumInfo* pInfo = (ImplEnumInfo*)(void*)lParam;
+ if ( !pInfo->mpName )
+ {
+ // Ignore vertical fonts
+ if ( pLogFont->elfLogFont.lfFaceName[0] != '@' )
+ {
+ if ( !pInfo->mbImplSalCourierNew )
+ pInfo->mbImplSalCourierNew = ImplSalWICompareAscii( pLogFont->elfLogFont.lfFaceName, "Courier New" ) == 0;
+ if ( !pInfo->mbImplSalCourierScalable )
+ pInfo->mbCourier = ImplSalWICompareAscii( pLogFont->elfLogFont.lfFaceName, "Courier" ) == 0;
+ else
+ pInfo->mbCourier = FALSE;
+ String aName( reinterpret_cast<const sal_Unicode*>(pLogFont->elfLogFont.lfFaceName) );
+ pInfo->mpName = &aName;
+ memcpy( pInfo->mpLogFontW->lfFaceName, pLogFont->elfLogFont.lfFaceName, (aName.Len()+1)*sizeof( wchar_t ) );
+ pInfo->mpLogFontW->lfCharSet = pLogFont->elfLogFont.lfCharSet;
+ EnumFontFamiliesExW( pInfo->mhDC, pInfo->mpLogFontW, (FONTENUMPROCW)SalEnumFontsProcExW,
+ (LPARAM)(void*)pInfo, 0 );
+ pInfo->mpLogFontW->lfFaceName[0] = '\0';
+ pInfo->mpLogFontW->lfCharSet = DEFAULT_CHARSET;
+ pInfo->mpName = NULL;
+ pInfo->mbCourier = FALSE;
+ }
+ }
+ else
+ {
+ // ignore non-scalable non-device font on printer
+ if( pInfo->mbPrinter )
+ if( (nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE) )
+ return 1;
+
+ ImplWinFontData* pData = ImplLogMetricToDevFontDataW( pLogFont, &(pMetric->ntmTm), nFontType );
+ pData->SetFontId( sal_IntPtr( pInfo->mnFontCount++ ) );
+
+ // knowing Courier to be scalable is nice
+ if( pInfo->mbCourier )
+ pInfo->mbImplSalCourierScalable |= pData->IsScalable();
+
+ pInfo->mpList->Add( pData );
+ }
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------
+
+struct TempFontItem
+{
+ ::rtl::OUString maFontFilePath;
+ ::rtl::OString maResourcePath;
+ TempFontItem* mpNextItem;
+};
+
+#ifdef FR_PRIVATE
+static int WINAPI __AddFontResourceExW( LPCWSTR lpszfileName, DWORD fl, PVOID pdv )
+{
+ typedef int (WINAPI *AddFontResourceExW_FUNC)(LPCWSTR, DWORD, PVOID );
+
+ static AddFontResourceExW_FUNC pFunc = NULL;
+ static HMODULE hmGDI = NULL;
+
+ if ( !pFunc && !hmGDI )
+ {
+ hmGDI = GetModuleHandleA( "GDI32" );
+ if ( hmGDI )
+ pFunc = reinterpret_cast<AddFontResourceExW_FUNC>( GetProcAddress( hmGDI, "AddFontResourceExW" ) );
+ }
+
+ if ( pFunc )
+ return pFunc( lpszfileName, fl, pdv );
+ else
+ {
+ SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
+ return 0;
+ }
+}
+#endif
+
+bool ImplAddTempFont( SalData& rSalData, const String& rFontFileURL )
+{
+ int nRet = 0;
+ ::rtl::OUString aUSytemPath;
+ OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) );
+
+#ifdef FR_PRIVATE
+ nRet = __AddFontResourceExW( reinterpret_cast<LPCWSTR>(aUSytemPath.getStr()), FR_PRIVATE, NULL );
+#endif
+
+ if ( !nRet )
+ {
+ static int nCounter = 0;
+ char aFileName[] = "soAA.fot";
+ aFileName[2] = sal::static_int_cast<char>('A' + (15 & (nCounter>>4)));
+ aFileName[3] = sal::static_int_cast<char>('A' + (15 & nCounter));
+ char aResourceName[512];
+ int nMaxLen = sizeof(aResourceName)/sizeof(*aResourceName) - 16;
+ int nLen = ::GetTempPathA( nMaxLen, aResourceName );
+ ::strncpy( aResourceName + nLen, aFileName, sizeof( aResourceName )- nLen );
+ // security: end buffer in any case
+ aResourceName[ (sizeof(aResourceName)/sizeof(*aResourceName))-1 ] = 0;
+ ::DeleteFileA( aResourceName );
+
+ rtl_TextEncoding theEncoding = osl_getThreadTextEncoding();
+ ::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, theEncoding );
+ // TODO: font should be private => need to investigate why it doesn't work then
+ if( !::CreateScalableFontResourceA( 0, aResourceName, aCFileName.getStr(), NULL ) )
+ return false;
+ ++nCounter;
+
+ nRet = ::AddFontResourceA( aResourceName );
+ if( nRet > 0 )
+ {
+ TempFontItem* pNewItem = new TempFontItem;
+ pNewItem->maResourcePath = rtl::OString( aResourceName );
+ pNewItem->maFontFilePath = aUSytemPath.getStr();
+ pNewItem->mpNextItem = rSalData.mpTempFontItem;
+ rSalData.mpTempFontItem = pNewItem;
+ }
+ }
+
+ return (nRet > 0);
+}
+
+// -----------------------------------------------------------------------
+
+void ImplReleaseTempFonts( SalData& rSalData )
+{
+ int nCount = 0;
+ while( TempFontItem* p = rSalData.mpTempFontItem )
+ {
+ ++nCount;
+ if( p->maResourcePath.getLength() )
+ {
+ const char* pResourcePath = p->maResourcePath.getStr();
+ ::RemoveFontResourceA( pResourcePath );
+ ::DeleteFileA( pResourcePath );
+ }
+ else
+ {
+ if( aSalShlData.mbWNT )
+ ::RemoveFontResourceW( reinterpret_cast<LPCWSTR>(p->maFontFilePath.getStr()) );
+ else
+ {
+ // poor man's string conversion because converter is gone
+ int nLen = p->maFontFilePath.getLength();
+ char* pNameA = new char[ nLen + 1 ];
+ for( int i = 0; i < nLen; ++i )
+ pNameA[i] = (char)(p->maFontFilePath.getStr())[i];
+ pNameA[ nLen ] = 0;
+ ::RemoveFontResourceA( pNameA );
+ delete[] pNameA;
+ }
+ }
+
+ rSalData.mpTempFontItem = p->mpNextItem;
+ delete p;
+ }
+
+#ifndef FR_PRIVATE
+ // notify every other application
+ // unless the temp fonts were installed as private fonts
+ if( nCount > 0 )
+ ::PostMessage( HWND_BROADCAST, WM_FONTCHANGE, 0, NULL );
+#endif // FR_PRIVATE
+}
+
+// -----------------------------------------------------------------------
+
+static bool ImplGetFontAttrFromFile( const String& rFontFileURL,
+ ImplDevFontAttributes& rDFA )
+{
+ ::rtl::OUString aUSytemPath;
+ OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) );
+
+ // get FontAttributes from a *fot file
+ // TODO: use GetTTGlobalFontInfo() to access the font directly
+ rDFA.mnQuality = 1000;
+ rDFA.mbDevice = true;
+ rDFA.meFamily = FAMILY_DONTKNOW;
+ rDFA.meWidthType = WIDTH_DONTKNOW;
+ rDFA.meWeight = WEIGHT_DONTKNOW;
+ rDFA.meItalic = ITALIC_DONTKNOW;
+ rDFA.mePitch = PITCH_DONTKNOW;;
+ rDFA.mbSubsettable= true;
+ rDFA.mbEmbeddable = false;
+
+ // Create temporary file name
+ char aFileName[] = "soAAT.fot";
+ char aResourceName[512];
+ int nMaxLen = sizeof(aResourceName)/sizeof(*aResourceName) - 16;
+ int nLen = ::GetTempPathA( nMaxLen, aResourceName );
+ ::strncpy( aResourceName + nLen, aFileName, Max( 0, nMaxLen - nLen ));
+ ::DeleteFileA( aResourceName );
+
+ // Create font resource file (typically with a .fot file name extension).
+ rtl_TextEncoding theEncoding = osl_getThreadTextEncoding();
+ ::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, theEncoding );
+ ::CreateScalableFontResourceA( 0, aResourceName, aCFileName.getStr(), NULL );
+
+ // Open and read the font resource file
+ rtl::OUString aFotFileName = rtl::OStringToOUString( aResourceName, osl_getThreadTextEncoding() );
+ osl::FileBase::getFileURLFromSystemPath( aFotFileName, aFotFileName );
+ osl::File aFotFile( aFotFileName );
+ osl::FileBase::RC aError = aFotFile.open( osl_File_OpenFlag_Read );
+ if( aError != osl::FileBase::E_None )
+ return false;
+
+ sal_uInt64 nBytesRead = 0;
+ char aBuffer[4096];
+ aFotFile.read( aBuffer, sizeof( aBuffer ), nBytesRead );
+ // clean up temporary resource file
+ aFotFile.close();
+ ::DeleteFileA( aResourceName );
+
+ // retrieve font family name from byte offset 0x4F6
+ int i = 0x4F6;
+ int nNameOfs = i;
+ while( (i < nBytesRead) && (aBuffer[i++] != 0) );
+ // skip full name
+ while( (i < nBytesRead) && (aBuffer[i++] != 0) );
+ // retrieve font style name
+ int nStyleOfs = i;
+ while( (i < nBytesRead) && (aBuffer[i++] != 0) );
+ if( i >= nBytesRead )
+ return false;
+
+ // convert byte strings to unicode
+ rDFA.maName = String( aBuffer + nNameOfs, osl_getThreadTextEncoding() );
+ rDFA.maStyleName = String( aBuffer + nStyleOfs, osl_getThreadTextEncoding() );
+
+ // byte offset 0x4C7: OS2_fsSelection
+ const char nFSS = aBuffer[ 0x4C7 ];
+ if( nFSS & 0x01 ) // italic
+ rDFA.meItalic = ITALIC_NORMAL;
+ //if( nFSS & 0x20 ) // bold
+ // rDFA.meWeight = WEIGHT_BOLD;
+ if( nFSS & 0x40 ) // regular
+ {
+ rDFA.meWeight = WEIGHT_NORMAL;
+ rDFA.meItalic = ITALIC_NONE;
+ }
+
+ // byte offsets 0x4D7/0x4D8: wingdi's FW_WEIGHT
+ int nWinWeight = (aBuffer[0x4D7] & 0xFF) + ((aBuffer[0x4D8] & 0xFF) << 8);
+ rDFA.meWeight = ImplWeightToSal( nWinWeight );
+
+ rDFA.mbSymbolFlag = false; // TODO
+ rDFA.mePitch = PITCH_DONTKNOW; // TODO
+
+ // byte offset 0x4DE: pitch&family
+ rDFA.meFamily = ImplFamilyToSal( aBuffer[0x4DE] );
+
+ // byte offsets 0x4C8/0x4C9: emunits
+ // byte offsets 0x4CE/0x4CF: winascent
+ // byte offsets 0x4D0/0x4D1: winascent+windescent-emunits
+ // byte offsets 0x4DF/0x4E0: avgwidth
+ //...
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool WinSalGraphics::AddTempDevFont( ImplDevFontList* pFontList,
+ const String& rFontFileURL, const String& rFontName )
+{
+ RTL_LOGFILE_TRACE1( "WinSalGraphics::AddTempDevFont(): %s", rtl::OUStringToOString( rFontFileURL, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ ImplDevFontAttributes aDFA;
+ aDFA.maName = rFontName;
+ aDFA.mnQuality = 1000;
+ aDFA.mbDevice = true;
+
+ // Search Font Name in Cache
+ if( !rFontName.Len() && mpFontAttrCache )
+ aDFA = mpFontAttrCache->GetFontAttr( rFontFileURL );
+
+ // Retrieve font name from font resource
+ if( !aDFA.maName.Len() )
+ {
+ ImplGetFontAttrFromFile( rFontFileURL, aDFA );
+ if( mpFontAttrCache && aDFA.maName.Len() )
+ mpFontAttrCache->AddFontAttr( rFontFileURL, aDFA );
+ }
+
+ if ( !aDFA.maName.Len() )
+ return false;
+
+ // remember temp font for cleanup later
+ if( !ImplAddTempFont( *GetSalData(), rFontFileURL ) )
+ return false;
+
+ UINT nPreferedCharSet = DEFAULT_CHARSET;
+ if ( !aSalShlData.mbWNT )
+ {
+ // for W98 guess charset preference from active codepage
+ CHARSETINFO aCharSetInfo;
+ DWORD nCP = GetACP();
+ if ( TranslateCharsetInfo( (DWORD*)nCP, &aCharSetInfo, TCI_SRCCODEPAGE ) )
+ nPreferedCharSet = aCharSetInfo.ciCharset;
+ }
+
+ // create matching FontData struct
+ aDFA.mbSymbolFlag = false; // TODO: how to know it without accessing the font?
+ aDFA.meFamily = FAMILY_DONTKNOW;
+ aDFA.meWidthType = WIDTH_DONTKNOW;
+ aDFA.meWeight = WEIGHT_DONTKNOW;
+ aDFA.meItalic = ITALIC_DONTKNOW;
+ aDFA.mePitch = PITCH_DONTKNOW;;
+ aDFA.mbSubsettable= true;
+ aDFA.mbEmbeddable = false;
+
+ /*
+ // TODO: improve ImplDevFontAttributes using the "font resource file"
+ aDFS.maName = // using "FONTRES:" from file
+ if( rFontName != aDFS.maName )
+ aDFS.maMapName = aFontName;
+ */
+
+ ImplWinFontData* pFontData = new ImplWinFontData( aDFA, 0,
+ sal::static_int_cast<WIN_BYTE>(nPreferedCharSet),
+ sal::static_int_cast<WIN_BYTE>(TMPF_VECTOR|TMPF_TRUETYPE) );
+ pFontData->SetFontId( reinterpret_cast<sal_IntPtr>(pFontData) );
+ pFontList->Add( pFontData );
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::GetDevFontList( ImplDevFontList* pFontList )
+{
+ // make sure all fonts are registered at least temporarily
+ static bool bOnce = true;
+ if( bOnce )
+ {
+ bOnce = false;
+
+ // determine font path
+ // since we are only interested in fonts that could not be
+ // registered before because of missing administration rights
+ // only the font path of the user installation is needed
+ ::rtl::OUString aPath;
+ osl_getExecutableFile( &aPath.pData );
+ ::rtl::OUString aExecutableFile( aPath );
+ aPath = aPath.copy( 0, aPath.lastIndexOf('/') );
+ String aFontDirUrl = aPath.copy( 0, aPath.lastIndexOf('/') );
+ aFontDirUrl += String( RTL_CONSTASCII_USTRINGPARAM("/Basis/share/fonts/truetype") );
+
+ // collect fonts in font path that could not be registered
+ osl::Directory aFontDir( aFontDirUrl );
+ osl::FileBase::RC rcOSL = aFontDir.open();
+ if( rcOSL == osl::FileBase::E_None )
+ {
+ osl::DirectoryItem aDirItem;
+ String aEmptyString;
+
+ ::rtl::OUString aBootStrap;
+ rtl::Bootstrap::get( String( RTL_CONSTASCII_USTRINGPARAM( "BRAND_BASE_DIR" ) ), aBootStrap );
+ aBootStrap += String( RTL_CONSTASCII_USTRINGPARAM( "/program/" SAL_CONFIGFILE( "bootstrap" ) ) );
+ rtl::Bootstrap aBootstrap( aBootStrap );
+ ::rtl::OUString aUserPath;
+ aBootstrap.getFrom( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "UserInstallation" ) ), aUserPath );
+ aUserPath += String( RTL_CONSTASCII_USTRINGPARAM("/user/config/fontnames.dat") );
+ String aBaseURL = aPath.copy( 0, aPath.lastIndexOf('/')+1 );
+ mpFontAttrCache = new ImplFontAttrCache( aUserPath, aBaseURL );
+
+ while( aFontDir.getNextItem( aDirItem, 10 ) == osl::FileBase::E_None )
+ {
+ osl::FileStatus aFileStatus( FileStatusMask_FileURL );
+ rcOSL = aDirItem.getFileStatus( aFileStatus );
+ if ( rcOSL == osl::FileBase::E_None )
+ AddTempDevFont( pFontList, aFileStatus.getFileURL(), aEmptyString );
+ }
+
+ delete mpFontAttrCache; // destructor rewrites the cache file if needed
+ mpFontAttrCache = NULL;
+ }
+ }
+
+ ImplEnumInfo aInfo;
+ aInfo.mhDC = mhDC;
+ aInfo.mpList = pFontList;
+ aInfo.mpName = NULL;
+ aInfo.mpLogFontA = NULL;
+ aInfo.mpLogFontW = NULL;
+ aInfo.mbCourier = false;
+ aInfo.mbPrinter = mbPrinter;
+ aInfo.mnFontCount = 0;
+ if ( !mbPrinter )
+ {
+ aInfo.mbImplSalCourierScalable = false;
+ aInfo.mbImplSalCourierNew = false;
+ }
+ else
+ {
+ aInfo.mbImplSalCourierScalable = true;
+ aInfo.mbImplSalCourierNew = true;
+ }
+
+ aInfo.mnPreferedCharSet = DEFAULT_CHARSET;
+ DWORD nCP = GetACP();
+ CHARSETINFO aCharSetInfo;
+ if ( TranslateCharsetInfo( (DWORD*)nCP, &aCharSetInfo, TCI_SRCCODEPAGE ) )
+ aInfo.mnPreferedCharSet = aCharSetInfo.ciCharset;
+
+ if ( aSalShlData.mbWNT )
+ {
+ LOGFONTW aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+ aInfo.mpLogFontW = &aLogFont;
+ EnumFontFamiliesExW( mhDC, &aLogFont,
+ (FONTENUMPROCW)SalEnumFontsProcExW, (LPARAM)(void*)&aInfo, 0 );
+ }
+ else
+ {
+ LOGFONTA aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+ aInfo.mpLogFontA = &aLogFont;
+ EnumFontFamiliesExA( mhDC, &aLogFont,
+ (FONTENUMPROCA)SalEnumFontsProcExA, (LPARAM)(void*)&aInfo, 0 );
+ }
+
+ // Feststellen, was es fuer Courier-Schriften auf dem Bildschirm gibt,
+ // um in SetFont() evt. Courier auf Courier New zu mappen
+ if ( !mbPrinter )
+ {
+ bImplSalCourierScalable = aInfo.mbImplSalCourierScalable;
+ bImplSalCourierNew = aInfo.mbImplSalCourierNew;
+ }
+
+ // set glyph fallback hook
+ static WinGlyphFallbackSubstititution aSubstFallback( mhDC );
+ pFontList->SetFallbackHook( &aSubstFallback );
+}
+
+// ----------------------------------------------------------------------------
+
+void WinSalGraphics::GetDevFontSubstList( OutputDevice* )
+{}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalGraphics::GetGlyphBoundRect( long nIndex, Rectangle& rRect )
+{
+ HDC hDC = mhDC;
+
+ // use unity matrix
+ MAT2 aMat;
+ aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
+ aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
+
+ UINT nGGOFlags = GGO_METRICS;
+ if( !(nIndex & GF_ISCHAR) )
+ nGGOFlags |= GGO_GLYPH_INDEX;
+ nIndex &= GF_IDXMASK;
+
+ GLYPHMETRICS aGM;
+ aGM.gmptGlyphOrigin.x = aGM.gmptGlyphOrigin.y = 0;
+ aGM.gmBlackBoxX = aGM.gmBlackBoxY = 0;
+ DWORD nSize = GDI_ERROR;
+ if ( aSalShlData.mbWNT )
+ nSize = ::GetGlyphOutlineW( hDC, nIndex, nGGOFlags, &aGM, 0, NULL, &aMat );
+ else if( (nGGOFlags & GGO_GLYPH_INDEX) || (nIndex <= 255) )
+ nSize = ::GetGlyphOutlineA( hDC, nIndex, nGGOFlags, &aGM, 0, NULL, &aMat );
+
+ if( nSize == GDI_ERROR )
+ return false;
+
+ rRect = Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ),
+ Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
+ rRect.Left() = static_cast<int>( mfFontScale * rRect.Left() );
+ rRect.Right() = static_cast<int>( mfFontScale * rRect.Right() );
+ rRect.Top() = static_cast<int>( mfFontScale * rRect.Top() );
+ rRect.Bottom() = static_cast<int>( mfFontScale * rRect.Bottom() );
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalGraphics::GetGlyphOutline( long nIndex,
+ ::basegfx::B2DPolyPolygon& rB2DPolyPoly )
+{
+ rB2DPolyPoly.clear();
+
+ BOOL bRet = FALSE;
+ HDC hDC = mhDC;
+
+ // use unity matrix
+ MAT2 aMat;
+ aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
+ aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
+
+ UINT nGGOFlags = GGO_NATIVE;
+ if( !(nIndex & GF_ISCHAR) )
+ nGGOFlags |= GGO_GLYPH_INDEX;
+ nIndex &= GF_IDXMASK;
+
+ GLYPHMETRICS aGlyphMetrics;
+ DWORD nSize1 = GDI_ERROR;
+ if ( aSalShlData.mbWNT )
+ nSize1 = ::GetGlyphOutlineW( hDC, nIndex, nGGOFlags, &aGlyphMetrics, 0, NULL, &aMat );
+ else if( (nGGOFlags & GGO_GLYPH_INDEX) || (nIndex <= 255) )
+ nSize1 = ::GetGlyphOutlineA( hDC, nIndex, nGGOFlags, &aGlyphMetrics, 0, NULL, &aMat );
+
+ if( !nSize1 ) // blank glyphs are ok
+ bRet = TRUE;
+ else if( nSize1 != GDI_ERROR )
+ {
+ BYTE* pData = new BYTE[ nSize1 ];
+ DWORD nSize2;
+ if ( aSalShlData.mbWNT )
+ nSize2 = ::GetGlyphOutlineW( hDC, nIndex, nGGOFlags,
+ &aGlyphMetrics, nSize1, pData, &aMat );
+ else
+ nSize2 = ::GetGlyphOutlineA( hDC, nIndex, nGGOFlags,
+ &aGlyphMetrics, nSize1, pData, &aMat );
+
+ if( nSize1 == nSize2 )
+ {
+ bRet = TRUE;
+
+ int nPtSize = 512;
+ Point* pPoints = new Point[ nPtSize ];
+ BYTE* pFlags = new BYTE[ nPtSize ];
+
+ TTPOLYGONHEADER* pHeader = (TTPOLYGONHEADER*)pData;
+ while( (BYTE*)pHeader < pData+nSize2 )
+ {
+ // only outline data is interesting
+ if( pHeader->dwType != TT_POLYGON_TYPE )
+ break;
+
+ // get start point; next start points are end points
+ // of previous segment
+ USHORT nPnt = 0;
+
+ long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
+ long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
+ pPoints[ nPnt ] = Point( nX, nY );
+ pFlags[ nPnt++ ] = POLY_NORMAL;
+
+ bool bHasOfflinePoints = false;
+ TTPOLYCURVE* pCurve = (TTPOLYCURVE*)( pHeader + 1 );
+ pHeader = (TTPOLYGONHEADER*)( (BYTE*)pHeader + pHeader->cb );
+ while( (BYTE*)pCurve < (BYTE*)pHeader )
+ {
+ int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
+ if( nPtSize < nNeededSize )
+ {
+ Point* pOldPoints = pPoints;
+ BYTE* pOldFlags = pFlags;
+ nPtSize = 2 * nNeededSize;
+ pPoints = new Point[ nPtSize ];
+ pFlags = new BYTE[ nPtSize ];
+ for( USHORT i = 0; i < nPnt; ++i )
+ {
+ pPoints[ i ] = pOldPoints[ i ];
+ pFlags[ i ] = pOldFlags[ i ];
+ }
+ delete[] pOldPoints;
+ delete[] pOldFlags;
+ }
+
+ int i = 0;
+ if( TT_PRIM_LINE == pCurve->wType )
+ {
+ while( i < pCurve->cpfx )
+ {
+ nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
+ nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
+ ++i;
+ pPoints[ nPnt ] = Point( nX, nY );
+ pFlags[ nPnt ] = POLY_NORMAL;
+ ++nPnt;
+ }
+ }
+ else if( TT_PRIM_QSPLINE == pCurve->wType )
+ {
+ bHasOfflinePoints = true;
+ while( i < pCurve->cpfx )
+ {
+ // get control point of quadratic bezier spline
+ nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
+ nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
+ ++i;
+ Point aControlP( nX, nY );
+
+ // calculate first cubic control point
+ // P0 = 1/3 * (PBeg + 2 * PQControl)
+ nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
+ nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
+ pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
+ pFlags[ nPnt+0 ] = POLY_CONTROL;
+
+ // calculate endpoint of segment
+ nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
+ nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
+
+ if ( i+1 >= pCurve->cpfx )
+ {
+ // endpoint is either last point in segment => advance
+ ++i;
+ }
+ else
+ {
+ // or endpoint is the middle of two control points
+ nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
+ nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
+ nX = (nX + 1) / 2;
+ nY = (nY + 1) / 2;
+ // no need to advance, because the current point
+ // is the control point in next bezier spline
+ }
+
+ pPoints[ nPnt+2 ] = Point( nX, nY );
+ pFlags[ nPnt+2 ] = POLY_NORMAL;
+
+ // calculate second cubic control point
+ // P1 = 1/3 * (PEnd + 2 * PQControl)
+ nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
+ nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
+ pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
+ pFlags[ nPnt+1 ] = POLY_CONTROL;
+
+ nPnt += 3;
+ }
+ }
+
+ // next curve segment
+ pCurve = (TTPOLYCURVE*)&pCurve->apfx[ i ];
+ }
+
+ // end point is start point for closed contour
+ // disabled, because Polygon class closes the contour itself
+ // pPoints[nPnt++] = pPoints[0];
+ // #i35928#
+ // Added again, but add only when not yet closed
+ if(pPoints[nPnt - 1] != pPoints[0])
+ {
+ if( bHasOfflinePoints )
+ pFlags[nPnt] = pFlags[0];
+
+ pPoints[nPnt++] = pPoints[0];
+ }
+
+ // convert y-coordinates W32 -> VCL
+ for( int i = 0; i < nPnt; ++i )
+ pPoints[i].Y() = -pPoints[i].Y();
+
+ // insert into polypolygon
+ Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : NULL) );
+ // convert to B2DPolyPolygon
+ // TODO: get rid of the intermediate PolyPolygon
+ rB2DPolyPoly.append( aPoly.getB2DPolygon() );
+ }
+
+ delete[] pPoints;
+ delete[] pFlags;
+ }
+
+ delete[] pData;
+ }
+
+ // rescaling needed for the PolyPolygon conversion
+ if( rB2DPolyPoly.count() )
+ {
+ const double fFactor(mfFontScale/256);
+ rB2DPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(fFactor, fFactor));
+ }
+
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+class ScopedFont
+{
+public:
+ explicit ScopedFont(WinSalGraphics & rData);
+
+ ~ScopedFont();
+
+private:
+ WinSalGraphics & m_rData;
+ HFONT m_hOrigFont;
+};
+
+ScopedFont::ScopedFont(WinSalGraphics & rData): m_rData(rData)
+{
+ m_hOrigFont = m_rData.mhFonts[0];
+ m_rData.mhFonts[0] = 0; // avoid deletion of current font
+}
+
+ScopedFont::~ScopedFont()
+{
+ if( m_hOrigFont )
+ {
+ // restore original font, destroy temporary font
+ HFONT hTempFont = m_rData.mhFonts[0];
+ m_rData.mhFonts[0] = m_hOrigFont;
+ SelectObject( m_rData.mhDC, m_hOrigFont );
+ DeleteObject( hTempFont );
+ }
+}
+
+class ScopedTrueTypeFont
+{
+public:
+ inline ScopedTrueTypeFont(): m_pFont(0) {}
+
+ ~ScopedTrueTypeFont();
+
+ int open(void * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum);
+
+ inline TrueTypeFont * get() const { return m_pFont; }
+
+private:
+ TrueTypeFont * m_pFont;
+};
+
+ScopedTrueTypeFont::~ScopedTrueTypeFont()
+{
+ if (m_pFont != 0)
+ CloseTTFont(m_pFont);
+}
+
+int ScopedTrueTypeFont::open(void * pBuffer, sal_uInt32 nLen,
+ sal_uInt32 nFaceNum)
+{
+ OSL_ENSURE(m_pFont == 0, "already open");
+ return OpenTTFontBuffer(pBuffer, nLen, nFaceNum, &m_pFont);
+}
+
+BOOL WinSalGraphics::CreateFontSubset( const rtl::OUString& rToFile,
+ const ImplFontData* pFont, long* pGlyphIDs, sal_uInt8* pEncoding,
+ sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
+{
+ // TODO: use more of the central font-subsetting code, move stuff there if needed
+
+ // create matching ImplFontSelectData
+ // we need just enough to get to the font file data
+ // use height=1000 for easier debugging (to match psprint's font units)
+ ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
+
+ // TODO: much better solution: move SetFont and restoration of old font to caller
+ ScopedFont aOldFont(*this);
+ float fScale = 1.0;
+ HFONT hOldFont = 0;
+ ImplDoSetFont( &aIFSD, fScale, hOldFont );
+
+ ImplWinFontData* pWinFontData = (ImplWinFontData*)aIFSD.mpFontData;
+
+#if OSL_DEBUG_LEVEL > 1
+ // get font metrics
+ TEXTMETRICA aWinMetric;
+ if( !::GetTextMetricsA( mhDC, &aWinMetric ) )
+ return FALSE;
+
+ DBG_ASSERT( !(aWinMetric.tmPitchAndFamily & TMPF_DEVICE), "cannot subset device font" );
+ DBG_ASSERT( aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE, "can only subset TT font" );
+#endif
+
+ rtl::OUString aSysPath;
+ if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
+ return FALSE;
+ const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
+ const ByteString aToFile( aSysPath.getStr(), (xub_StrLen)aSysPath.getLength(), aThreadEncoding );
+
+ // check if the font has a CFF-table
+ const DWORD nCffTag = CalcTag( "CFF " );
+ const RawFontData aRawCffData( mhDC, nCffTag );
+ if( aRawCffData.get() )
+ {
+ pWinFontData->UpdateFromHDC( mhDC );
+ const ImplFontCharMap* pCharMap = pWinFontData->GetImplFontCharMap();
+ pCharMap->AddReference();
+
+ long nRealGlyphIds[ 256 ];
+ for( int i = 0; i < nGlyphCount; ++i )
+ {
+ // TODO: remap notdef glyph if needed
+ // TODO: use GDI's GetGlyphIndices instead? Does it handle GSUB properly?
+ sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
+ if( pGlyphIDs[i] & GF_ISCHAR ) // remaining pseudo-glyphs need to be translated
+ nGlyphIdx = pCharMap->GetGlyphIndex( nGlyphIdx );
+ if( (pGlyphIDs[i] & (GF_ROTMASK|GF_GSUB)) != 0) // TODO: vertical substitution
+ {/*####*/}
+
+ nRealGlyphIds[i] = nGlyphIdx;
+ }
+
+ pCharMap->DeReference(); // TODO: and and use a RAII object
+
+ // provide a font subset from the CFF-table
+ FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" );
+ rInfo.LoadFont( FontSubsetInfo::CFF_FONT, aRawCffData.get(), aRawCffData.size() );
+ bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL,
+ nRealGlyphIds, pEncoding, nGlyphCount, pGlyphWidths );
+ fclose( pOutFile );
+ return bRC;
+ }
+
+ // get raw font file data
+ const RawFontData xRawFontData( mhDC, NULL );
+ if( !xRawFontData.get() )
+ return FALSE;
+
+ // open font file
+ sal_uInt32 nFaceNum = 0;
+ if( !*xRawFontData.get() ) // TTC candidate
+ nFaceNum = ~0U; // indicate "TTC font extracts only"
+
+ ScopedTrueTypeFont aSftTTF;
+ int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum );
+ if( nRC != SF_OK )
+ return FALSE;
+
+ TTGlobalFontInfo aTTInfo;
+ ::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo );
+ rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF;
+ rInfo.m_aPSName = ImplSalGetUniString( aTTInfo.psname );
+ rInfo.m_nAscent = +aTTInfo.winAscent;
+ rInfo.m_nDescent = -aTTInfo.winDescent;
+ rInfo.m_aFontBBox = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ),
+ Point( aTTInfo.xMax, aTTInfo.yMax ) );
+ rInfo.m_nCapHeight = aTTInfo.yMax; // Well ...
+
+ // subset TTF-glyphs and get their properties
+ // take care that subset fonts require the NotDef glyph in pos 0
+ int nOrigCount = nGlyphCount;
+ USHORT aShortIDs[ 256 ];
+ sal_uInt8 aTempEncs[ 256 ];
+
+ int nNotDef=-1, i;
+ for( i = 0; i < nGlyphCount; ++i )
+ {
+ aTempEncs[i] = pEncoding[i];
+ sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
+ if( pGlyphIDs[i] & GF_ISCHAR )
+ {
+ sal_Unicode cChar = static_cast<sal_Unicode>(nGlyphIdx); // TODO: sal_UCS4
+ const bool bVertical = ((pGlyphIDs[i] & (GF_ROTMASK|GF_GSUB)) != 0);
+ nGlyphIdx = ::MapChar( aSftTTF.get(), cChar, bVertical );
+ if( (nGlyphIdx == 0) && pFont->IsSymbolFont() )
+ {
+ // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX
+ cChar = (cChar & 0xF000) ? (cChar & 0x00FF) : (cChar | 0xF000);
+ nGlyphIdx = ::MapChar( aSftTTF.get(), cChar, bVertical );
+ }
+ }
+ aShortIDs[i] = static_cast<USHORT>( nGlyphIdx );
+ if( !nGlyphIdx )
+ if( nNotDef < 0 )
+ nNotDef = i; // first NotDef glyph found
+ }
+
+ if( nNotDef != 0 )
+ {
+ // add fake NotDef glyph if needed
+ if( nNotDef < 0 )
+ nNotDef = nGlyphCount++;
+
+ // NotDef glyph must be in pos 0 => swap glyphids
+ aShortIDs[ nNotDef ] = aShortIDs[0];
+ aTempEncs[ nNotDef ] = aTempEncs[0];
+ aShortIDs[0] = 0;
+ aTempEncs[0] = 0;
+ }
+ DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" );
+
+ // fill pWidth array
+ TTSimpleGlyphMetrics* pMetrics =
+ ::GetTTSimpleGlyphMetrics( aSftTTF.get(), aShortIDs, nGlyphCount, aIFSD.mbVertical );
+ if( !pMetrics )
+ return FALSE;
+ sal_uInt16 nNotDefAdv = pMetrics[0].adv;
+ pMetrics[0].adv = pMetrics[nNotDef].adv;
+ pMetrics[nNotDef].adv = nNotDefAdv;
+ for( i = 0; i < nOrigCount; ++i )
+ pGlyphWidths[i] = pMetrics[i].adv;
+ free( pMetrics );
+
+ // write subset into destination file
+ nRC = ::CreateTTFromTTGlyphs( aSftTTF.get(), aToFile.GetBuffer(), aShortIDs,
+ aTempEncs, nGlyphCount, 0, NULL, 0 );
+ return (nRC == SF_OK);
+}
+
+//--------------------------------------------------------------------------
+
+const void* WinSalGraphics::GetEmbedFontData( const ImplFontData* pFont,
+ const sal_Unicode* pUnicodes, sal_Int32* pCharWidths,
+ FontSubsetInfo& rInfo, long* pDataLen )
+{
+ // create matching ImplFontSelectData
+ // we need just enough to get to the font file data
+ ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
+
+ // TODO: much better solution: move SetFont and restoration of old font to caller
+ ScopedFont aOldFont(*this);
+ SetFont( &aIFSD, 0 );
+
+ // get the raw font file data
+ RawFontData aRawFontData( mhDC );
+ *pDataLen = aRawFontData.size();
+ if( !aRawFontData.get() )
+ return NULL;
+
+ // get important font properties
+ TEXTMETRICA aTm;
+ if( !::GetTextMetricsA( mhDC, &aTm ) )
+ *pDataLen = 0;
+ const bool bPFA = (*aRawFontData.get() < 0x80);
+ rInfo.m_nFontType = bPFA ? FontSubsetInfo::TYPE1_PFA : FontSubsetInfo::TYPE1_PFB;
+ WCHAR aFaceName[64];
+ int nFNLen = ::GetTextFaceW( mhDC, 64, aFaceName );
+ // #i59854# strip eventual null byte
+ while( nFNLen > 0 && aFaceName[nFNLen-1] == 0 )
+ nFNLen--;
+ if( nFNLen == 0 )
+ *pDataLen = 0;
+ rInfo.m_aPSName = String( reinterpret_cast<const sal_Unicode*>(aFaceName), sal::static_int_cast<USHORT>(nFNLen) );
+ rInfo.m_nAscent = +aTm.tmAscent;
+ rInfo.m_nDescent = -aTm.tmDescent;
+ rInfo.m_aFontBBox = Rectangle( Point( -aTm.tmOverhang, -aTm.tmDescent ),
+ Point( aTm.tmMaxCharWidth, aTm.tmAscent+aTm.tmExternalLeading ) );
+ rInfo.m_nCapHeight = aTm.tmAscent; // Well ...
+
+ // get individual character widths
+ for( int i = 0; i < 256; ++i )
+ {
+ int nCharWidth = 0;
+ const sal_Unicode cChar = pUnicodes[i];
+ if( !::GetCharWidth32W( mhDC, cChar, cChar, &nCharWidth ) )
+ *pDataLen = 0;
+ pCharWidths[i] = nCharWidth;
+ }
+
+ if( !*pDataLen )
+ return NULL;
+
+ const unsigned char* pData = aRawFontData.steal();
+ return (void*)pData;
+}
+
+//--------------------------------------------------------------------------
+
+void WinSalGraphics::FreeEmbedFontData( const void* pData, long /*nLen*/ )
+{
+ delete[] reinterpret_cast<char*>(const_cast<void*>(pData));
+}
+
+//--------------------------------------------------------------------------
+
+const Ucs2SIntMap* WinSalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded )
+{
+ // TODO: even for builtin fonts we get here... why?
+ if( !pFont->IsEmbeddable() )
+ return NULL;
+
+ // fill the encoding vector
+ // currently no nonencoded vector
+ if( pNonEncoded )
+ *pNonEncoded = NULL;
+
+ const ImplWinFontData* pWinFontData = static_cast<const ImplWinFontData*>(pFont);
+ const Ucs2SIntMap* pEncoding = pWinFontData->GetEncodingVector();
+ if( pEncoding == NULL )
+ {
+ Ucs2SIntMap* pNewEncoding = new Ucs2SIntMap;
+ #if 0
+ // TODO: get correct encoding vector
+ GLYPHSET aGlyphSet;
+ aGlyphSet.cbThis = sizeof(aGlyphSet);
+ DWORD aW = ::GetFontUnicodeRanges( mhDC, &aGlyphSet);
+ #else
+ for( sal_Unicode i = 32; i < 256; ++i )
+ (*pNewEncoding)[i] = i;
+ #endif
+ pWinFontData->SetEncodingVector( pNewEncoding );
+ pEncoding = pNewEncoding;
+ }
+
+ return pEncoding;
+}
+
+//--------------------------------------------------------------------------
+
+void WinSalGraphics::GetGlyphWidths( const ImplFontData* pFont,
+ bool bVertical,
+ Int32Vector& rWidths,
+ Ucs2UIntMap& rUnicodeEnc )
+{
+ // create matching ImplFontSelectData
+ // we need just enough to get to the font file data
+ ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
+
+ // TODO: much better solution: move SetFont and restoration of old font to caller
+ ScopedFont aOldFont(*this);
+
+ float fScale = 0.0;
+ HFONT hOldFont = 0;
+ ImplDoSetFont( &aIFSD, fScale, hOldFont );
+
+ if( pFont->IsSubsettable() )
+ {
+ // get raw font file data
+ const RawFontData xRawFontData( mhDC );
+ if( !xRawFontData.get() )
+ return;
+
+ // open font file
+ sal_uInt32 nFaceNum = 0;
+ if( !*xRawFontData.get() ) // TTC candidate
+ nFaceNum = ~0U; // indicate "TTC font extracts only"
+
+ ScopedTrueTypeFont aSftTTF;
+ int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum );
+ if( nRC != SF_OK )
+ return;
+
+ int nGlyphs = GetTTGlyphCount( aSftTTF.get() );
+ if( nGlyphs > 0 )
+ {
+ rWidths.resize(nGlyphs);
+ std::vector<sal_uInt16> aGlyphIds(nGlyphs);
+ for( int i = 0; i < nGlyphs; i++ )
+ aGlyphIds[i] = sal_uInt16(i);
+ TTSimpleGlyphMetrics* pMetrics = ::GetTTSimpleGlyphMetrics( aSftTTF.get(),
+ &aGlyphIds[0],
+ nGlyphs,
+ bVertical ? 1 : 0 );
+ if( pMetrics )
+ {
+ for( int i = 0; i< nGlyphs; i++ )
+ rWidths[i] = pMetrics[i].adv;
+ free( pMetrics );
+ rUnicodeEnc.clear();
+ }
+ const ImplWinFontData* pWinFont = static_cast<const ImplWinFontData*>(pFont);
+ const ImplFontCharMap* pMap = pWinFont->GetImplFontCharMap();
+ DBG_ASSERT( pMap && pMap->GetCharCount(), "no map" );
+ pMap->AddReference();
+
+ int nCharCount = pMap->GetCharCount();
+ sal_uInt32 nChar = pMap->GetFirstChar();
+ for( int i = 0; i < nCharCount; i++ )
+ {
+ if( nChar < 0x00010000 )
+ {
+ sal_uInt16 nGlyph = ::MapChar( aSftTTF.get(),
+ static_cast<sal_Ucs>(nChar),
+ bVertical ? 1 : 0 );
+ if( nGlyph )
+ rUnicodeEnc[ static_cast<sal_Unicode>(nChar) ] = nGlyph;
+ }
+ nChar = pMap->GetNextChar( nChar );
+ }
+
+ pMap->DeReference(); // TODO: and and use a RAII object
+ }
+ }
+ else if( pFont->IsEmbeddable() )
+ {
+ // get individual character widths
+ rWidths.clear();
+ rUnicodeEnc.clear();
+ rWidths.reserve( 224 );
+ for( sal_Unicode i = 32; i < 256; ++i )
+ {
+ int nCharWidth = 0;
+ if( ::GetCharWidth32W( mhDC, i, i, &nCharWidth ) )
+ {
+ rUnicodeEnc[ i ] = rWidths.size();
+ rWidths.push_back( nCharWidth );
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+
+void WinSalGraphics::DrawServerFontLayout( const ServerFontLayout& )
+{}
+
+//--------------------------------------------------------------------------
+
+SystemFontData WinSalGraphics::GetSysFontData( int nFallbacklevel ) const
+{
+ SystemFontData aSysFontData;
+
+ if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1;
+ if (nFallbacklevel < 0 ) nFallbacklevel = 0;
+
+ aSysFontData.nSize = sizeof( SystemFontData );
+ aSysFontData.hFont = mhFonts[nFallbacklevel];
+ aSysFontData.bFakeBold = false;
+ aSysFontData.bFakeItalic = false;
+ aSysFontData.bAntialias = true;
+ aSysFontData.bVerticalCharacterType = false;
+
+ OSL_TRACE("\r\n:WinSalGraphics::GetSysFontData(): FontID: %p, Fallback level: %d",
+ aSysFontData.hFont,
+ nFallbacklevel);
+
+ return aSysFontData;
+}
+
+//--------------------------------------------------------------------------
+
diff --git a/vcl/win/source/gdi/salgdi_gdiplus.cxx b/vcl/win/source/gdi/salgdi_gdiplus.cxx
new file mode 100644
index 000000000000..88efbb29d30a
--- /dev/null
+++ b/vcl/win/source/gdi/salgdi_gdiplus.cxx
@@ -0,0 +1,265 @@
+/*************************************************************************
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <tools/svwin.h>
+#include <wincomp.hxx>
+#include <saldata.hxx>
+#include <salgdi.h>
+#include <tools/debug.hxx>
+
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#if defined _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <GdiPlus.h>
+#include <GdiPlusEnums.h>
+#include <GdiPlusColor.h>
+
+#if defined _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+// -----------------------------------------------------------------------
+
+void impAddB2DPolygonToGDIPlusGraphicsPathReal(Gdiplus::GraphicsPath& rPath, const basegfx::B2DPolygon& rPolygon, bool bNoLineJoin)
+{
+ sal_uInt32 nCount(rPolygon.count());
+
+ if(nCount)
+ {
+ const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
+ const bool bControls(rPolygon.areControlPointsUsed());
+ basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
+ Gdiplus::PointF aFCurr(Gdiplus::REAL(aCurr.getX()), Gdiplus::REAL(aCurr.getY()));
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nCount);
+ const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
+ const Gdiplus::PointF aFNext(Gdiplus::REAL(aNext.getX()), Gdiplus::REAL(aNext.getY()));
+
+ if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex)))
+ {
+ const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
+ const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
+
+ rPath.AddBezier(
+ aFCurr,
+ Gdiplus::PointF(Gdiplus::REAL(aCa.getX()), Gdiplus::REAL(aCa.getY())),
+ Gdiplus::PointF(Gdiplus::REAL(aCb.getX()), Gdiplus::REAL(aCb.getY())),
+ aFNext);
+ }
+ else
+ {
+ rPath.AddLine(aFCurr, aFNext);
+ }
+
+ if(a + 1 < nEdgeCount)
+ {
+ aFCurr = aFNext;
+
+ if(bNoLineJoin)
+ {
+ rPath.StartFigure();
+ }
+ }
+ }
+ }
+}
+
+void impAddB2DPolygonToGDIPlusGraphicsPathInteger(Gdiplus::GraphicsPath& rPath, const basegfx::B2DPolygon& rPolygon, bool bNoLineJoin)
+{
+ sal_uInt32 nCount(rPolygon.count());
+
+ if(nCount)
+ {
+ const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
+ const bool bControls(rPolygon.areControlPointsUsed());
+ basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
+ Gdiplus::Point aICurr(INT(aCurr.getX()), INT(aCurr.getY()));
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nCount);
+ const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
+ const Gdiplus::Point aINext(INT(aNext.getX()), INT(aNext.getY()));
+
+ if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex)))
+ {
+ const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
+ const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
+
+ rPath.AddBezier(
+ aICurr,
+ Gdiplus::Point(INT(aCa.getX()), INT(aCa.getY())),
+ Gdiplus::Point(INT(aCb.getX()), INT(aCb.getY())),
+ aINext);
+ }
+ else
+ {
+ rPath.AddLine(aICurr, aINext);
+ }
+
+ if(a + 1 < nEdgeCount)
+ {
+ aICurr = aINext;
+
+ if(bNoLineJoin)
+ {
+ rPath.StartFigure();
+ }
+ }
+ }
+ }
+}
+
+bool WinSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
+{
+ const sal_uInt32 nCount(rPolyPolygon.count());
+
+ if(mbBrush && nCount && (fTransparency >= 0.0 && fTransparency < 1.0))
+ {
+ Gdiplus::Graphics aGraphics(mhDC);
+ const sal_uInt8 aTrans((sal_uInt8)255 - (sal_uInt8)basegfx::fround(fTransparency * 255.0));
+ Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maFillColor), SALCOLOR_GREEN(maFillColor), SALCOLOR_BLUE(maFillColor));
+ Gdiplus::SolidBrush aTestBrush(aTestColor);
+ Gdiplus::GraphicsPath aPath;
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ if(0 != a)
+ {
+ aPath.StartFigure(); // #i101491# not needed for first run
+ }
+
+ impAddB2DPolygonToGDIPlusGraphicsPathReal(aPath, rPolyPolygon.getB2DPolygon(a), false);
+ aPath.CloseFigure();
+ }
+
+ if(getAntiAliasB2DDraw())
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
+ }
+ else
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
+ }
+
+ aGraphics.FillPath(&aTestBrush, &aPath);
+ }
+
+ return true;
+}
+
+bool WinSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolygon, double fTransparency, const basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin eLineJoin )
+{
+ const sal_uInt32 nCount(rPolygon.count());
+
+ if(mbPen && nCount)
+ {
+ Gdiplus::Graphics aGraphics(mhDC);
+ const sal_uInt8 aTrans = (sal_uInt8)basegfx::fround( 255 * (1.0 - fTransparency) );
+ Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor));
+ Gdiplus::Pen aTestPen(aTestColor, Gdiplus::REAL(rLineWidths.getX()));
+ Gdiplus::GraphicsPath aPath;
+ bool bNoLineJoin(false);
+
+ switch(eLineJoin)
+ {
+ default : // basegfx::B2DLINEJOIN_NONE :
+ {
+ if(basegfx::fTools::more(rLineWidths.getX(), 0.0))
+ {
+ bNoLineJoin = true;
+ }
+ break;
+ }
+ case basegfx::B2DLINEJOIN_BEVEL :
+ {
+ aTestPen.SetLineJoin(Gdiplus::LineJoinBevel);
+ break;
+ }
+ case basegfx::B2DLINEJOIN_MIDDLE :
+ case basegfx::B2DLINEJOIN_MITER :
+ {
+ const Gdiplus::REAL aMiterLimit(15.0);
+ aTestPen.SetMiterLimit(aMiterLimit);
+ aTestPen.SetLineJoin(Gdiplus::LineJoinMiter);
+ break;
+ }
+ case basegfx::B2DLINEJOIN_ROUND :
+ {
+ aTestPen.SetLineJoin(Gdiplus::LineJoinRound);
+ break;
+ }
+ }
+
+ if(nCount > 250 && basegfx::fTools::more(rLineWidths.getX(), 1.5))
+ {
+ impAddB2DPolygonToGDIPlusGraphicsPathInteger(aPath, rPolygon, bNoLineJoin);
+ }
+ else
+ {
+ impAddB2DPolygonToGDIPlusGraphicsPathReal(aPath, rPolygon, bNoLineJoin);
+ }
+
+ if(rPolygon.isClosed() && !bNoLineJoin)
+ {
+ // #i101491# needed to create the correct line joins
+ aPath.CloseFigure();
+ }
+
+ if(getAntiAliasB2DDraw())
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
+ }
+ else
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
+ }
+
+ aGraphics.DrawPath(&aTestPen, &aPath);
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
diff --git a/vcl/win/source/gdi/salnativewidgets-luna.cxx b/vcl/win/source/gdi/salnativewidgets-luna.cxx
new file mode 100755
index 000000000000..8197fb37cd6d
--- /dev/null
+++ b/vcl/win/source/gdi/salnativewidgets-luna.cxx
@@ -0,0 +1,1290 @@
+/*************************************************************************
+ *
+ * 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 _SV_SALNATIVEWIDGETS_CXX
+
+#include "svsys.h"
+#include "salgdi.h"
+#include "saldata.hxx"
+#include "vcl/svapp.hxx"
+
+#include "rtl/ustring.h"
+#include "osl/module.h"
+
+#include "uxtheme.h"
+#include "vssym32.h"
+
+#include <map>
+#include <string>
+
+using namespace rtl;
+using namespace std;
+
+typedef map< wstring, HTHEME > ThemeMap;
+static ThemeMap aThemeMap;
+
+
+/****************************************************
+ wrap visual styles API to avoid linking against it
+ it is not available on all Windows platforms
+*****************************************************/
+
+class VisualStylesAPI
+{
+private:
+ typedef HTHEME (WINAPI * OpenThemeData_Proc_T) ( HWND hwnd, LPCWSTR pszClassList );
+ typedef HRESULT (WINAPI * CloseThemeData_Proc_T) ( HTHEME hTheme );
+ typedef HRESULT (WINAPI * GetThemeBackgroundContentRect_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pBoundingRect, RECT *pContentRect );
+ typedef HRESULT (WINAPI * DrawThemeBackground_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect );
+ typedef HRESULT (WINAPI * DrawThemeText_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect );
+ typedef HRESULT (WINAPI * GetThemePartSize_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *psz );
+
+ OpenThemeData_Proc_T lpfnOpenThemeData;
+ CloseThemeData_Proc_T lpfnCloseThemeData;
+ GetThemeBackgroundContentRect_Proc_T lpfnGetThemeBackgroundContentRect;
+ DrawThemeBackground_Proc_T lpfnDrawThemeBackground;
+ DrawThemeText_Proc_T lpfnDrawThemeText;
+ GetThemePartSize_Proc_T lpfnGetThemePartSize;
+
+ oslModule mhModule;
+
+public:
+ VisualStylesAPI();
+ ~VisualStylesAPI();
+ BOOL IsAvailable() { return (mhModule != NULL); }
+
+ HTHEME OpenThemeData( HWND hwnd, LPCWSTR pszClassList );
+ HRESULT CloseThemeData( HTHEME hTheme );
+ HRESULT GetThemeBackgroundContentRect( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pBoundingRect, RECT *pContentRect );
+ HRESULT DrawThemeBackground( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect );
+ HRESULT DrawThemeText( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect );
+ HRESULT GetThemePartSize( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *psz );
+};
+
+static VisualStylesAPI vsAPI;
+
+VisualStylesAPI::VisualStylesAPI()
+{
+ OUString aLibraryName( RTL_CONSTASCII_USTRINGPARAM( "uxtheme.dll" ) );
+ mhModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT );
+
+ if ( mhModule )
+ {
+ lpfnOpenThemeData = (OpenThemeData_Proc_T)osl_getAsciiFunctionSymbol( mhModule, "OpenThemeData" );
+ lpfnCloseThemeData = (CloseThemeData_Proc_T)osl_getAsciiFunctionSymbol( mhModule, "CloseThemeData" );
+ lpfnGetThemeBackgroundContentRect = (GetThemeBackgroundContentRect_Proc_T)osl_getAsciiFunctionSymbol( mhModule, "GetThemeBackgroundContentRect" );
+ lpfnDrawThemeBackground = (DrawThemeBackground_Proc_T)osl_getAsciiFunctionSymbol( mhModule, "DrawThemeBackground" );
+ lpfnDrawThemeText = (DrawThemeText_Proc_T)osl_getAsciiFunctionSymbol( mhModule, "DrawThemeText" );
+ lpfnGetThemePartSize = (GetThemePartSize_Proc_T)osl_getAsciiFunctionSymbol( mhModule, "GetThemePartSize" );
+ }
+ else
+ {
+ lpfnOpenThemeData = NULL;
+ lpfnCloseThemeData = NULL;
+ lpfnGetThemeBackgroundContentRect = NULL;
+ lpfnDrawThemeBackground = NULL;
+ lpfnDrawThemeText = NULL;
+ lpfnGetThemePartSize = NULL;
+ }
+}
+VisualStylesAPI::~VisualStylesAPI()
+{
+ if( mhModule )
+ osl_unloadModule( mhModule );
+}
+HTHEME VisualStylesAPI::OpenThemeData( HWND hwnd, LPCWSTR pszClassList )
+{
+ if(lpfnOpenThemeData)
+ return (*lpfnOpenThemeData) (hwnd, pszClassList);
+ else
+ return NULL;
+}
+
+HRESULT VisualStylesAPI::CloseThemeData( HTHEME hTheme )
+{
+ if(lpfnCloseThemeData)
+ return (*lpfnCloseThemeData) (hTheme);
+ else
+ return S_FALSE;
+}
+HRESULT VisualStylesAPI::GetThemeBackgroundContentRect( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pBoundingRect, RECT *pContentRect )
+{
+ if(lpfnGetThemeBackgroundContentRect)
+ return (*lpfnGetThemeBackgroundContentRect) ( hTheme, hdc, iPartId, iStateId, pBoundingRect, pContentRect );
+ else
+ return S_FALSE;
+}
+HRESULT VisualStylesAPI::DrawThemeBackground( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect )
+{
+ if(lpfnDrawThemeBackground)
+ return (*lpfnDrawThemeBackground) (hTheme, hdc, iPartId, iStateId, pRect, pClipRect);
+ else
+ return S_FALSE;
+}
+HRESULT VisualStylesAPI::DrawThemeText( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect )
+{
+ if(lpfnDrawThemeText)
+ return (*lpfnDrawThemeText) (hTheme, hdc, iPartId, iStateId, pszText, iCharCount, dwTextFlags, dwTextFlags2, pRect);
+ else
+ return S_FALSE;
+}
+HRESULT VisualStylesAPI::GetThemePartSize( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *psz )
+{
+ if(lpfnGetThemePartSize)
+ return (*lpfnGetThemePartSize) (hTheme, hdc, iPartId, iStateId, prc, eSize, psz);
+ else
+ return S_FALSE;
+}
+
+
+/*********************************************************
+ * Initialize XP theming and local stuff
+ *********************************************************/
+void SalData::initNWF( void )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // the menu bar and the top docking area should have a common background (gradient)
+ pSVData->maNWFData.mbMenuBarDockingAreaCommonBG = true;
+}
+
+
+// *********************************************************
+// * Release theming handles
+// ********************************************************
+void SalData::deInitNWF( void )
+{
+ ThemeMap::iterator iter = aThemeMap.begin();
+ while( iter != aThemeMap.end() )
+ {
+ vsAPI.CloseThemeData(iter->second);
+ iter++;
+ }
+ aThemeMap.clear();
+}
+
+static HTHEME getThemeHandle( HWND hWnd, LPCWSTR name )
+{
+ if( GetSalData()->mbThemeChanged )
+ {
+ // throw away invalid theme handles
+ GetSalData()->deInitNWF();
+ GetSalData()->mbThemeChanged = FALSE;
+ }
+
+ ThemeMap::iterator iter;
+ if( (iter = aThemeMap.find( name )) != aThemeMap.end() )
+ return iter->second;
+ // theme not found -> add it to map
+ HTHEME hTheme = vsAPI.OpenThemeData( hWnd, name );
+ if( hTheme != NULL )
+ aThemeMap[name] = hTheme;
+ return hTheme;
+}
+
+/*
+ * IsNativeControlSupported()
+ *
+ * Returns TRUE if the platform supports native
+ * drawing of the control defined by nPart
+ */
+BOOL WinSalGraphics::IsNativeControlSupported( ControlType nType, ControlPart nPart )
+{
+ HTHEME hTheme = NULL;
+
+ switch( nType )
+ {
+ case CTRL_PUSHBUTTON:
+ case CTRL_RADIOBUTTON:
+ case CTRL_CHECKBOX:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Button");
+ break;
+ case CTRL_SCROLLBAR:
+ if( nPart == PART_DRAW_BACKGROUND_HORZ || nPart == PART_DRAW_BACKGROUND_VERT )
+ return FALSE; // no background painting needed
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Scrollbar");
+ break;
+ case CTRL_COMBOBOX:
+ if( nPart == HAS_BACKGROUND_TEXTURE )
+ return FALSE; // we do not paint the inner part (ie the selection background/focus indication)
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ else if( nPart == PART_BUTTON_DOWN )
+ hTheme = getThemeHandle( mhWnd, L"Combobox");
+ break;
+ case CTRL_SPINBOX:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ else if( nPart == PART_ALL_BUTTONS ||
+ nPart == PART_BUTTON_UP || nPart == PART_BUTTON_DOWN ||
+ nPart == PART_BUTTON_LEFT|| nPart == PART_BUTTON_RIGHT )
+ hTheme = getThemeHandle( mhWnd, L"Spin");
+ break;
+ case CTRL_SPINBUTTONS:
+ if( nPart == PART_ENTIRE_CONTROL || nPart == PART_ALL_BUTTONS )
+ hTheme = getThemeHandle( mhWnd, L"Spin");
+ break;
+ case CTRL_EDITBOX:
+ case CTRL_MULTILINE_EDITBOX:
+ if( nPart == HAS_BACKGROUND_TEXTURE )
+ return FALSE; // we do not paint the inner part (ie the selection background/focus indication)
+ //return TRUE;
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ break;
+ case CTRL_LISTBOX:
+ if( nPart == HAS_BACKGROUND_TEXTURE )
+ return FALSE; // we do not paint the inner part (ie the selection background/focus indication)
+ if( nPart == PART_ENTIRE_CONTROL || nPart == PART_WINDOW )
+ hTheme = getThemeHandle( mhWnd, L"Listview");
+ else if( nPart == PART_BUTTON_DOWN )
+ hTheme = getThemeHandle( mhWnd, L"Combobox");
+ break;
+ case CTRL_TAB_PANE:
+ case CTRL_TAB_BODY:
+ case CTRL_TAB_ITEM:
+ case CTRL_FIXEDBORDER:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Tab");
+ break;
+ case CTRL_TOOLBAR:
+ if( nPart == PART_ENTIRE_CONTROL || nPart == PART_BUTTON )
+ hTheme = getThemeHandle( mhWnd, L"Toolbar");
+ else
+ // use rebar theme for grip and background
+ hTheme = getThemeHandle( mhWnd, L"Rebar");
+ break;
+ case CTRL_MENUBAR:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Rebar");
+ break;
+ case CTRL_PROGRESS:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Progress");
+ break;
+ case CTRL_SLIDER:
+ if( nPart == PART_TRACK_HORZ_AREA || nPart == PART_TRACK_VERT_AREA )
+ hTheme = getThemeHandle( mhWnd, L"Trackbar" );
+ break;
+ case CTRL_LISTNODE:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"TreeView" );
+ break;
+ default:
+ hTheme = NULL;
+ break;
+ }
+
+ return (hTheme != NULL);
+}
+
+
+/*
+ * HitTestNativeControl()
+ *
+ * If the return value is TRUE, bIsInside contains information whether
+ * aPos was or was not inside the native widget specified by the
+ * nType/nPart combination.
+ */
+BOOL WinSalGraphics::hitTestNativeControl( ControlType,
+ ControlPart,
+ const Rectangle&,
+ const Point&,
+ BOOL& )
+{
+ return FALSE;
+}
+
+BOOL ImplDrawTheme( HTHEME hTheme, HDC hDC, int iPart, int iState, RECT rc, const OUString& aStr)
+{
+ HRESULT hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, 0);
+
+ if( aStr.getLength() )
+ {
+ RECT rcContent;
+ hr = vsAPI.GetThemeBackgroundContentRect( hTheme, hDC, iPart, iState, &rc, &rcContent);
+ hr = vsAPI.DrawThemeText( hTheme, hDC, iPart, iState,
+ reinterpret_cast<LPCWSTR>(aStr.getStr()), -1,
+ DT_CENTER | DT_VCENTER | DT_SINGLELINE,
+ 0, &rcContent);
+ }
+ return (hr == S_OK);
+}
+
+
+Rectangle ImplGetThemeRect( HTHEME hTheme, HDC hDC, int iPart, int iState, const Rectangle& aRect, THEMESIZE eTS = TS_TRUE )
+{
+ SIZE aSz;
+ RECT rc;
+ rc.left = aRect.nLeft;
+ rc.right = aRect.nRight;
+ rc.top = aRect.nTop;
+ rc.bottom = aRect.nBottom;
+ HRESULT hr = vsAPI.GetThemePartSize( hTheme, hDC, iPart, iState, NULL, eTS, &aSz ); // TS_TRUE returns optimal size
+ if( hr == S_OK )
+ return Rectangle( 0, 0, aSz.cx, aSz.cy );
+ else
+ return Rectangle();
+}
+
+// Helper functions
+// ----
+
+void ImplConvertSpinbuttonValues( int nControlPart, const ControlState& rState, const Rectangle& rRect,
+ int* pLunaPart, int *pLunaState, RECT *pRect )
+{
+ if( nControlPart == PART_BUTTON_DOWN )
+ {
+ *pLunaPart = SPNP_DOWN;
+ if( rState & CTRL_STATE_PRESSED )
+ *pLunaState = DNS_PRESSED;
+ else if( !(rState & CTRL_STATE_ENABLED) )
+ *pLunaState = DNS_DISABLED;
+ else if( rState & CTRL_STATE_ROLLOVER )
+ *pLunaState = DNS_HOT;
+ else
+ *pLunaState = DNS_NORMAL;
+ }
+ if( nControlPart == PART_BUTTON_UP )
+ {
+ *pLunaPart = SPNP_UP;
+ if( rState & CTRL_STATE_PRESSED )
+ *pLunaState = UPS_PRESSED;
+ else if( !(rState & CTRL_STATE_ENABLED) )
+ *pLunaState = UPS_DISABLED;
+ else if( rState & CTRL_STATE_ROLLOVER )
+ *pLunaState = UPS_HOT;
+ else
+ *pLunaState = UPS_NORMAL;
+ }
+ if( nControlPart == PART_BUTTON_RIGHT )
+ {
+ *pLunaPart = SPNP_UPHORZ;
+ if( rState & CTRL_STATE_PRESSED )
+ *pLunaState = DNHZS_PRESSED;
+ else if( !(rState & CTRL_STATE_ENABLED) )
+ *pLunaState = DNHZS_DISABLED;
+ else if( rState & CTRL_STATE_ROLLOVER )
+ *pLunaState = DNHZS_HOT;
+ else
+ *pLunaState = DNHZS_NORMAL;
+ }
+ if( nControlPart == PART_BUTTON_LEFT )
+ {
+ *pLunaPart = SPNP_DOWNHORZ;
+ if( rState & CTRL_STATE_PRESSED )
+ *pLunaState = UPHZS_PRESSED;
+ else if( !(rState & CTRL_STATE_ENABLED) )
+ *pLunaState = UPHZS_DISABLED;
+ else if( rState & CTRL_STATE_ROLLOVER )
+ *pLunaState = UPHZS_HOT;
+ else
+ *pLunaState = UPHZS_NORMAL;
+ }
+
+ pRect->left = rRect.Left();
+ pRect->right = rRect.Right()+1;
+ pRect->top = rRect.Top();
+ pRect->bottom = rRect.Bottom()+1;
+}
+
+// ----
+
+BOOL ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
+ ControlType nType,
+ ControlPart nPart,
+ ControlState nState,
+ const ImplControlValue& aValue,
+ OUString aCaption )
+{
+ // a listbox dropdown is actually a combobox dropdown
+ if( nType == CTRL_LISTBOX )
+ if( nPart == PART_BUTTON_DOWN )
+ nType = CTRL_COMBOBOX;
+
+ // draw entire combobox as a large edit box
+ if( nType == CTRL_COMBOBOX )
+ if( nPart == PART_ENTIRE_CONTROL )
+ nType = CTRL_EDITBOX;
+
+ // draw entire spinbox as a large edit box
+ if( nType == CTRL_SPINBOX )
+ if( nPart == PART_ENTIRE_CONTROL )
+ nType = CTRL_EDITBOX;
+
+ int iPart(0), iState(0);
+ if( nType == CTRL_SCROLLBAR )
+ {
+ HRESULT hr;
+ if( nPart == PART_BUTTON_UP )
+ {
+ iPart = SBP_ARROWBTN;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = ABS_UPPRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = ABS_UPDISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = ABS_UPHOT;
+ else
+ iState = ABS_UPNORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, 0);
+ return (hr == S_OK);
+ }
+ if( nPart == PART_BUTTON_DOWN )
+ {
+ iPart = SBP_ARROWBTN;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = ABS_DOWNPRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = ABS_DOWNDISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = ABS_DOWNHOT;
+ else
+ iState = ABS_DOWNNORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, 0);
+ return (hr == S_OK);
+ }
+ if( nPart == PART_BUTTON_LEFT )
+ {
+ iPart = SBP_ARROWBTN;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = ABS_LEFTPRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = ABS_LEFTDISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = ABS_LEFTHOT;
+ else
+ iState = ABS_LEFTNORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, 0);
+ return (hr == S_OK);
+ }
+ if( nPart == PART_BUTTON_RIGHT )
+ {
+ iPart = SBP_ARROWBTN;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = ABS_RIGHTPRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = ABS_RIGHTDISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = ABS_RIGHTHOT;
+ else
+ iState = ABS_RIGHTNORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, 0);
+ return (hr == S_OK);
+ }
+ if( nPart == PART_THUMB_HORZ || nPart == PART_THUMB_VERT )
+ {
+ iPart = (nPart == PART_THUMB_HORZ) ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = SCRBS_PRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = SCRBS_DISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = SCRBS_HOT;
+ else
+ iState = SCRBS_NORMAL;
+
+ SIZE sz;
+ vsAPI.GetThemePartSize(hTheme, hDC, iPart, iState, NULL, TS_MIN, &sz);
+ vsAPI.GetThemePartSize(hTheme, hDC, iPart, iState, NULL, TS_TRUE, &sz);
+ vsAPI.GetThemePartSize(hTheme, hDC, iPart, iState, NULL, TS_DRAW, &sz);
+
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, 0);
+ // paint gripper on thumb if enough space
+ if( ( (nPart == PART_THUMB_VERT) && (rc.bottom-rc.top > 12) ) ||
+ ( (nPart == PART_THUMB_HORZ) && (rc.right-rc.left > 12) ) )
+ {
+ iPart = (nPart == PART_THUMB_HORZ) ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
+ iState = 0;
+ vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, 0);
+ }
+ return (hr == S_OK);
+ }
+ if( nPart == PART_TRACK_HORZ_LEFT || nPart == PART_TRACK_HORZ_RIGHT || nPart == PART_TRACK_VERT_UPPER || nPart == PART_TRACK_VERT_LOWER )
+ {
+ switch( nPart )
+ {
+ case PART_TRACK_HORZ_LEFT: iPart = SBP_UPPERTRACKHORZ; break;
+ case PART_TRACK_HORZ_RIGHT: iPart = SBP_LOWERTRACKHORZ; break;
+ case PART_TRACK_VERT_UPPER: iPart = SBP_UPPERTRACKVERT; break;
+ case PART_TRACK_VERT_LOWER: iPart = SBP_LOWERTRACKVERT; break;
+ }
+
+ if( nState & CTRL_STATE_PRESSED )
+ iState = SCRBS_PRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = SCRBS_DISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = SCRBS_HOT;
+ else
+ iState = SCRBS_NORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, 0);
+ return (hr == S_OK);
+ }
+ }
+ if( nType == CTRL_SPINBUTTONS && nPart == PART_ALL_BUTTONS )
+ {
+ if( aValue.getType() == CTRL_SPINBUTTONS )
+ {
+ const SpinbuttonValue *pValue = static_cast<const SpinbuttonValue*>(&aValue);
+ BOOL bOk = FALSE;
+
+ RECT rect;
+ ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect );
+ bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
+
+ if( bOk )
+ {
+ ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect );
+ bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
+ }
+
+ return bOk;
+ }
+ }
+ if( nType == CTRL_SPINBOX )
+ {
+ // decrease spinbutton rects a little
+ //rc.right--;
+ //rc.bottom--;
+ if( nPart == PART_ALL_BUTTONS )
+ {
+ if( aValue.getType() == CTRL_SPINBUTTONS )
+ {
+ const SpinbuttonValue *pValue = static_cast<const SpinbuttonValue*>(&aValue);
+ BOOL bOk = FALSE;
+
+ RECT rect;
+ ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect );
+ bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
+
+ if( bOk )
+ {
+ ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect );
+ bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
+ }
+
+ return bOk;
+ }
+ }
+
+ if( nPart == PART_BUTTON_DOWN )
+ {
+ iPart = SPNP_DOWN;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = DNS_PRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = DNS_DISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = DNS_HOT;
+ else
+ iState = DNS_NORMAL;
+ }
+ if( nPart == PART_BUTTON_UP )
+ {
+ iPart = SPNP_UP;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = UPS_PRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = UPS_DISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = UPS_HOT;
+ else
+ iState = UPS_NORMAL;
+ }
+ if( nPart == PART_BUTTON_RIGHT )
+ {
+ iPart = SPNP_DOWNHORZ;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = DNHZS_PRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = DNHZS_DISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = DNHZS_HOT;
+ else
+ iState = DNHZS_NORMAL;
+ }
+ if( nPart == PART_BUTTON_LEFT )
+ {
+ iPart = SPNP_UPHORZ;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = UPHZS_PRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = UPHZS_DISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = UPHZS_HOT;
+ else
+ iState = UPHZS_NORMAL;
+ }
+ if( nPart == PART_BUTTON_LEFT || nPart == PART_BUTTON_RIGHT || nPart == PART_BUTTON_UP || nPart == PART_BUTTON_DOWN )
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ if( nType == CTRL_COMBOBOX )
+ {
+ if( nPart == PART_BUTTON_DOWN )
+ {
+ iPart = CP_DROPDOWNBUTTON;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = CBXS_PRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = CBXS_DISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = CBXS_HOT;
+ else
+ iState = CBXS_NORMAL;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ }
+ if( nType == CTRL_PUSHBUTTON )
+ {
+ iPart = BP_PUSHBUTTON;
+ if( nState & CTRL_STATE_PRESSED )
+ iState = PBS_PRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = PBS_DISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = PBS_HOT;
+ else if( nState & CTRL_STATE_DEFAULT )
+ iState = PBS_DEFAULTED;
+ //else if( nState & CTRL_STATE_FOCUSED )
+ // iState = PBS_DEFAULTED; // may need to draw focus rect
+ else
+ iState = PBS_NORMAL;
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == CTRL_RADIOBUTTON )
+ {
+ iPart = BP_RADIOBUTTON;
+ BOOL bChecked = ( aValue.getTristateVal() == BUTTONVALUE_ON );
+
+ if( nState & CTRL_STATE_PRESSED )
+ iState = bChecked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = bChecked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = bChecked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
+ else
+ iState = bChecked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
+
+ //if( nState & CTRL_STATE_FOCUSED )
+ // iState |= PBS_DEFAULTED; // may need to draw focus rect
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == CTRL_CHECKBOX )
+ {
+ iPart = BP_CHECKBOX;
+ ButtonValue v = aValue.getTristateVal();
+
+ if( nState & CTRL_STATE_PRESSED )
+ iState = (v == BUTTONVALUE_ON) ? CBS_CHECKEDPRESSED :
+ ( (v == BUTTONVALUE_OFF) ? CBS_UNCHECKEDPRESSED : CBS_MIXEDPRESSED );
+ else if( !(nState & CTRL_STATE_ENABLED) )
+ iState = (v == BUTTONVALUE_ON) ? CBS_CHECKEDDISABLED :
+ ( (v == BUTTONVALUE_OFF) ? CBS_UNCHECKEDDISABLED : CBS_MIXEDDISABLED );
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = (v == BUTTONVALUE_ON) ? CBS_CHECKEDHOT :
+ ( (v == BUTTONVALUE_OFF) ? CBS_UNCHECKEDHOT : CBS_MIXEDHOT );
+ else
+ iState = (v == BUTTONVALUE_ON) ? CBS_CHECKEDNORMAL :
+ ( (v == BUTTONVALUE_OFF) ? CBS_UNCHECKEDNORMAL : CBS_MIXEDNORMAL );
+
+ //if( nState & CTRL_STATE_FOCUSED )
+ // iState |= PBS_DEFAULTED; // may need to draw focus rect
+
+ //SIZE sz;
+ //THEMESIZE eSize = TS_DRAW; // TS_MIN, TS_TRUE, TS_DRAW
+ //vsAPI.GetThemePartSize( hTheme, hDC, iPart, iState, &rc, eSize, &sz);
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( ( nType == CTRL_EDITBOX ) || ( nType == CTRL_MULTILINE_EDITBOX ) )
+ {
+ iPart = EP_EDITTEXT;
+ if( !(nState & CTRL_STATE_ENABLED) )
+ iState = ETS_DISABLED;
+ else if( nState & CTRL_STATE_FOCUSED )
+ iState = ETS_FOCUSED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = ETS_HOT;
+ else
+ iState = ETS_NORMAL;
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == CTRL_LISTBOX )
+ {
+ if( nPart == PART_ENTIRE_CONTROL || nPart == PART_WINDOW )
+ {
+ iPart = LVP_EMPTYTEXT; // ??? no idea which part to choose here
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ }
+
+ if( nType == CTRL_TAB_PANE )
+ {
+ iPart = TABP_PANE;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == CTRL_FIXEDBORDER )
+ {
+ /*
+ iPart = BP_GROUPBOX;
+ if( !(nState & CTRL_STATE_ENABLED) )
+ iState = GBS_DISABLED;
+ else
+ iState = GBS_NORMAL;
+ */
+ // The fixed border is only used around the tools->options tabpage where
+ // TABP_PANE fits best
+ iPart = TABP_PANE;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == CTRL_TAB_BODY )
+ {
+ iPart = TABP_BODY;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == CTRL_TAB_ITEM )
+ {
+ iPart = TABP_TABITEMLEFTEDGE;
+ rc.bottom--;
+
+ OSL_ASSERT( aValue.getType() == CTRL_TAB_ITEM );
+
+ const TabitemValue *pValue = static_cast<const TabitemValue*>(&aValue);
+ if( pValue->isBothAligned() )
+ {
+ iPart = TABP_TABITEMLEFTEDGE;
+ rc.right--;
+ }
+ else if( pValue->isLeftAligned() )
+ iPart = TABP_TABITEMLEFTEDGE;
+ else if( pValue->isRightAligned() )
+ iPart = TABP_TABITEMRIGHTEDGE;
+ else iPart = TABP_TABITEM;
+
+ if( !(nState & CTRL_STATE_ENABLED) )
+ iState = TILES_DISABLED;
+ else if( nState & CTRL_STATE_SELECTED )
+ {
+ iState = TILES_SELECTED;
+ // increase the selected tab
+ rc.left-=2;
+ if( pValue && !pValue->isBothAligned() )
+ {
+ if( pValue->isLeftAligned() || pValue->isNotAligned() )
+ rc.right+=2;
+ if( pValue->isRightAligned() )
+ rc.right+=1;
+ }
+ rc.top-=2;
+ rc.bottom+=2;
+ }
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = TILES_HOT;
+ else if( nState & CTRL_STATE_FOCUSED )
+ iState = TILES_FOCUSED; // may need to draw focus rect
+ else
+ iState = TILES_NORMAL;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == CTRL_TOOLBAR )
+ {
+ if( nPart == PART_BUTTON )
+ {
+ iPart = TP_BUTTON;
+ BOOL bChecked = ( aValue.getTristateVal() == BUTTONVALUE_ON );
+ if( !(nState & CTRL_STATE_ENABLED) )
+ //iState = TS_DISABLED;
+ // disabled buttons are typically not painted at all but we need visual
+ // feedback when travelling by keyboard over disabled entries
+ iState = TS_HOT;
+ else if( nState & CTRL_STATE_PRESSED )
+ iState = TS_PRESSED;
+ else if( nState & CTRL_STATE_ROLLOVER )
+ iState = bChecked ? TS_HOTCHECKED : TS_HOT;
+ else
+ iState = bChecked ? TS_CHECKED : TS_NORMAL;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ else if( nPart == PART_THUMB_HORZ || nPart == PART_THUMB_VERT )
+ {
+ // the vertical gripper is not supported in most themes and it makes no
+ // sense to only support horizontal gripper
+ //iPart = (nPart == PART_THUMB_HORZ) ? RP_GRIPPERVERT : RP_GRIPPER;
+ //return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ else if( nPart == PART_DRAW_BACKGROUND_HORZ || nPart == PART_DRAW_BACKGROUND_VERT )
+ {
+ if( aValue.getType() == CTRL_TOOLBAR )
+ {
+ const ToolbarValue *pValue = static_cast<const ToolbarValue*>(&aValue);
+ if( pValue->mbIsTopDockingArea )
+ rc.top = 0; // extend potential gradient to cover menu bar as well
+ }
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ }
+
+ if( nType == CTRL_MENUBAR )
+ {
+ if( nPart != PART_ENTIRE_CONTROL )
+ return FALSE;
+
+ if( aValue.getType() == CTRL_MENUBAR )
+ {
+ const MenubarValue *pValue = static_cast<const MenubarValue*>(&aValue);
+ rc.bottom += pValue->maTopDockingAreaHeight; // extend potential gradient to cover docking area as well
+ }
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == CTRL_PROGRESS )
+ {
+ if( nPart != PART_ENTIRE_CONTROL )
+ return FALSE;
+
+ if( ! ImplDrawTheme( hTheme, hDC, PP_BAR, iState, rc, aCaption) )
+ return false;
+ RECT aProgressRect = rc;
+ if( vsAPI.GetThemeBackgroundContentRect( hTheme, hDC, PP_BAR, iState, &rc, &aProgressRect) != S_OK )
+ return false;
+
+ long nProgressWidth = aValue.getNumericVal();
+ nProgressWidth *= (aProgressRect.right - aProgressRect.left);
+ nProgressWidth /= (rc.right - rc.left);
+ if( Application::GetSettings().GetLayoutRTL() )
+ aProgressRect.left = aProgressRect.right - nProgressWidth;
+ else
+ aProgressRect.right = aProgressRect.left + nProgressWidth;
+
+ return ImplDrawTheme( hTheme, hDC, PP_CHUNK, iState, aProgressRect, aCaption );
+ }
+
+ if( nType == CTRL_SLIDER )
+ {
+ iPart = (nPart == PART_TRACK_HORZ_AREA) ? TKP_TRACK : TKP_TRACKVERT;
+ iState = (nPart == PART_TRACK_HORZ_AREA) ? TRS_NORMAL : TRVS_NORMAL;
+
+ Rectangle aTrackRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, Rectangle() );
+ RECT aTRect = rc;
+ if( nPart == PART_TRACK_HORZ_AREA )
+ {
+ long nH = aTrackRect.GetHeight();
+ aTRect.top += (rc.bottom - rc.top - nH)/2;
+ aTRect.bottom = aTRect.top + nH;
+ }
+ else
+ {
+ long nW = aTrackRect.GetWidth();
+ aTRect.left += (rc.right - rc.left - nW)/2;
+ aTRect.right = aTRect.left + nW;
+ }
+ ImplDrawTheme( hTheme, hDC, iPart, iState, aTRect, aCaption );
+
+ RECT aThumbRect;
+ OSL_ASSERT( aValue.getType() == CTRL_SLIDER );
+ const SliderValue* pVal = static_cast<const SliderValue*>(&aValue);
+ aThumbRect.left = pVal->maThumbRect.Left();
+ aThumbRect.top = pVal->maThumbRect.Top();
+ aThumbRect.right = pVal->maThumbRect.Right();
+ aThumbRect.bottom = pVal->maThumbRect.Bottom();
+ iPart = (nPart == PART_TRACK_HORZ_AREA) ? TKP_THUMB : TKP_THUMBVERT;
+ iState = (nState & CTRL_STATE_ENABLED) ? TUS_NORMAL : TUS_DISABLED;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, aThumbRect, aCaption );
+ }
+
+ if( nType == CTRL_LISTNODE )
+ {
+ if( nPart != PART_ENTIRE_CONTROL )
+ return FALSE;
+
+ ButtonValue aButtonValue = aValue.getTristateVal();
+ iPart = TVP_GLYPH;
+ switch( aButtonValue )
+ {
+ case BUTTONVALUE_ON:
+ iState = GLPS_OPENED;
+ break;
+ case BUTTONVALUE_OFF:
+ iState = GLPS_CLOSED;
+ break;
+ default:
+ return FALSE;
+ }
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption );
+ }
+
+ return 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)
+ */
+BOOL WinSalGraphics::drawNativeControl( ControlType nType,
+ ControlPart nPart,
+ const Rectangle& rControlRegion,
+ ControlState nState,
+ const ImplControlValue& aValue,
+ const OUString& aCaption )
+{
+ BOOL bOk = false;
+ HTHEME hTheme = NULL;
+
+ switch( nType )
+ {
+ case CTRL_PUSHBUTTON:
+ case CTRL_RADIOBUTTON:
+ case CTRL_CHECKBOX:
+ hTheme = getThemeHandle( mhWnd, L"Button");
+ break;
+ case CTRL_SCROLLBAR:
+ hTheme = getThemeHandle( mhWnd, L"Scrollbar");
+ break;
+ case CTRL_COMBOBOX:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ else if( nPart == PART_BUTTON_DOWN )
+ hTheme = getThemeHandle( mhWnd, L"Combobox");
+ break;
+ case CTRL_SPINBOX:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ else
+ hTheme = getThemeHandle( mhWnd, L"Spin");
+ break;
+ case CTRL_SPINBUTTONS:
+ hTheme = getThemeHandle( mhWnd, L"Spin");
+ break;
+ case CTRL_EDITBOX:
+ case CTRL_MULTILINE_EDITBOX:
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ break;
+ case CTRL_LISTBOX:
+ if( nPart == PART_ENTIRE_CONTROL || nPart == PART_WINDOW )
+ hTheme = getThemeHandle( mhWnd, L"Listview");
+ else if( nPart == PART_BUTTON_DOWN )
+ hTheme = getThemeHandle( mhWnd, L"Combobox");
+ break;
+ case CTRL_TAB_PANE:
+ case CTRL_TAB_BODY:
+ case CTRL_TAB_ITEM:
+ case CTRL_FIXEDBORDER:
+ hTheme = getThemeHandle( mhWnd, L"Tab");
+ break;
+ case CTRL_TOOLBAR:
+ if( nPart == PART_ENTIRE_CONTROL || nPart == PART_BUTTON )
+ hTheme = getThemeHandle( mhWnd, L"Toolbar");
+ else
+ // use rebar for grip and background
+ hTheme = getThemeHandle( mhWnd, L"Rebar");
+ break;
+ case CTRL_MENUBAR:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Rebar");
+ break;
+ case CTRL_PROGRESS:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"Progress");
+ break;
+ case CTRL_LISTNODE:
+ if( nPart == PART_ENTIRE_CONTROL )
+ hTheme = getThemeHandle( mhWnd, L"TreeView");
+ break;
+ case CTRL_SLIDER:
+ if( nPart == PART_TRACK_HORZ_AREA || nPart == PART_TRACK_VERT_AREA )
+ hTheme = getThemeHandle( mhWnd, L"Trackbar" );
+ break;
+ default:
+ hTheme = NULL;
+ break;
+ }
+
+ if( !hTheme )
+ return false;
+
+ Rectangle buttonRect = rControlRegion;
+ RECT rc;
+ rc.left = buttonRect.Left();
+ rc.right = buttonRect.Right()+1;
+ rc.top = buttonRect.Top();
+ rc.bottom = buttonRect.Bottom()+1;
+
+ // set default text alignment
+ int ta = SetTextAlign( mhDC, TA_LEFT|TA_TOP|TA_NOUPDATECP );
+
+ OUString aCaptionStr( aCaption.replace('~', '&') ); // translate mnemonics
+ bOk = ImplDrawNativeControl(mhDC, hTheme, rc,
+ nType, nPart, nState, aValue,
+ aCaptionStr );
+
+ // restore alignment
+ SetTextAlign( mhDC, ta );
+
+
+ //GdiFlush();
+
+ return bOk;
+}
+
+
+/*
+ * 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)
+ */
+BOOL WinSalGraphics::drawNativeControlText( ControlType,
+ ControlPart,
+ const Rectangle&,
+ ControlState,
+ const ImplControlValue&,
+ const OUString& )
+{
+ return( false );
+}
+
+
+/*
+ * GetNativeControlRegion()
+ *
+ * If the return value is TRUE, rNativeBoundingRegion
+ * contains the 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)
+ */
+BOOL WinSalGraphics::getNativeControlRegion( ControlType nType,
+ ControlPart nPart,
+ const Rectangle& rControlRegion,
+ ControlState nState,
+ const ImplControlValue& rControlValue,
+ const OUString&,
+ Rectangle &rNativeBoundingRegion,
+ Rectangle &rNativeContentRegion )
+{
+ BOOL bRet = FALSE;
+
+ HDC hDC = GetDC( mhWnd );
+ if( nType == CTRL_TOOLBAR )
+ {
+ if( nPart == PART_THUMB_HORZ || nPart == PART_THUMB_VERT )
+ {
+ /*
+ // the vertical gripper is not supported in most themes and it makes no
+ // sense to only support horizontal gripper
+
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Rebar");
+ if( hTheme )
+ {
+ Rectangle aRect( ImplGetThemeRect( hTheme, hDC, nPart == PART_THUMB_HORZ ? RP_GRIPPERVERT : RP_GRIPPER,
+ 0, rControlRegion.GetBoundRect() ) );
+ if( nPart == PART_THUMB_HORZ && !aRect.IsEmpty() )
+ {
+ Rectangle aVertRect( 0, 0, aRect.getHeight(), aRect.getWidth() );
+ rNativeContentRegion = aVertRect;
+ }
+ else
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ if( !rNativeContentRegion.IsEmpty() )
+ bRet = TRUE;
+ }
+ */
+ }
+ if( nPart == PART_BUTTON )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Toolbar");
+ if( hTheme )
+ {
+ Rectangle aRect( ImplGetThemeRect( hTheme, hDC, TP_SPLITBUTTONDROPDOWN,
+ TS_HOT, rControlRegion ) );
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ if( !rNativeContentRegion.IsEmpty() )
+ bRet = TRUE;
+ }
+ }
+ }
+ if( nType == CTRL_PROGRESS && nPart == PART_ENTIRE_CONTROL )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Progress");
+ if( hTheme )
+ {
+ Rectangle aRect( ImplGetThemeRect( hTheme, hDC, PP_BAR,
+ 0, rControlRegion ) );
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ if( !rNativeContentRegion.IsEmpty() )
+ bRet = TRUE;
+ }
+ }
+ if( (nType == CTRL_LISTBOX || nType == CTRL_COMBOBOX ) && nPart == PART_ENTIRE_CONTROL )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Combobox");
+ if( hTheme )
+ {
+ Rectangle aBoxRect( rControlRegion );
+ Rectangle aRect( ImplGetThemeRect( hTheme, hDC, CP_DROPDOWNBUTTON,
+ CBXS_NORMAL, aBoxRect ) );
+ if( aRect.GetHeight() > aBoxRect.GetHeight() )
+ aBoxRect.Bottom() = aBoxRect.Top() + aRect.GetHeight();
+ if( aRect.GetWidth() > aBoxRect.GetWidth() )
+ aBoxRect.Right() = aBoxRect.Left() + aRect.GetWidth();
+ rNativeContentRegion = aBoxRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ if( !aRect.IsEmpty() )
+ bRet = TRUE;
+ }
+ }
+
+ if( (nType == CTRL_EDITBOX || nType == CTRL_SPINBOX) && nPart == PART_ENTIRE_CONTROL )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Edit");
+ if( hTheme )
+ {
+ // get borderr size
+ Rectangle aBoxRect( rControlRegion );
+ Rectangle aRect( ImplGetThemeRect( hTheme, hDC, EP_BACKGROUNDWITHBORDER,
+ EBWBS_HOT, aBoxRect ) );
+ // ad app font height
+ NONCLIENTMETRICSW aNonClientMetrics;
+ aNonClientMetrics.cbSize = sizeof( aNonClientMetrics );
+ if ( SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, sizeof( aNonClientMetrics ), &aNonClientMetrics, 0 ) )
+ {
+ long nFontHeight = aNonClientMetrics.lfMessageFont.lfHeight;
+ if( nFontHeight < 0 )
+ nFontHeight = -nFontHeight;
+
+ if( aRect.GetHeight() && nFontHeight )
+ {
+ aRect.Bottom() += aRect.GetHeight();
+ aRect.Bottom() += nFontHeight;
+ if( aRect.GetHeight() > aBoxRect.GetHeight() )
+ aBoxRect.Bottom() = aBoxRect.Top() + aRect.GetHeight();
+ if( aRect.GetWidth() > aBoxRect.GetWidth() )
+ aBoxRect.Right() = aBoxRect.Left() + aRect.GetWidth();
+ rNativeContentRegion = aBoxRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ bRet = TRUE;
+ }
+ }
+ }
+ }
+
+ if( nType == CTRL_SLIDER && ( (nPart == PART_THUMB_HORZ) || (nPart == PART_THUMB_VERT) ) )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Trackbar");
+ if( hTheme )
+ {
+ int iPart = (nPart == PART_THUMB_HORZ) ? TKP_THUMB : TKP_THUMBVERT;
+ int iState = (nPart == PART_THUMB_HORZ) ? TUS_NORMAL : TUVS_NORMAL;
+ Rectangle aThumbRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, Rectangle() );
+ if( nPart == PART_THUMB_HORZ )
+ {
+ long nW = aThumbRect.GetWidth();
+ Rectangle aRect( rControlRegion );
+ aRect.Right() = aRect.Left() + nW - 1;
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ }
+ else
+ {
+ long nH = aThumbRect.GetHeight();
+ Rectangle aRect( rControlRegion );
+ aRect.Bottom() = aRect.Top() + nH - 1;
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ }
+ bRet = TRUE;
+ }
+ }
+
+ if ( ( nType == CTRL_TAB_ITEM ) && ( nPart == PART_ENTIRE_CONTROL ) )
+ {
+ Rectangle aControlRect( rControlRegion );
+ rNativeContentRegion = aControlRect;
+
+ --aControlRect.Bottom();
+
+ if( rControlValue.getType() == CTRL_TAB_ITEM )
+ {
+ const TabitemValue *pValue = static_cast<const TabitemValue*>(&rControlValue);
+ if ( pValue->isBothAligned() )
+ --aControlRect.Right();
+
+ if ( nState & CTRL_STATE_SELECTED )
+ {
+ aControlRect.Left() -= 2;
+ if ( pValue && !pValue->isBothAligned() )
+ {
+ if ( pValue->isLeftAligned() || pValue->isNotAligned() )
+ aControlRect.Right() += 2;
+ if ( pValue->isRightAligned() )
+ aControlRect.Right() += 1;
+ }
+ aControlRect.Top() -= 2;
+ aControlRect.Bottom() += 2;
+ }
+ }
+ rNativeBoundingRegion = aControlRect;
+ bRet = TRUE;
+ }
+
+ ReleaseDC( mhWnd, hDC );
+ return( bRet );
+}
+
diff --git a/vcl/win/source/gdi/salprn.cxx b/vcl/win/source/gdi/salprn.cxx
new file mode 100644
index 000000000000..9d8d41723f64
--- /dev/null
+++ b/vcl/win/source/gdi/salprn.cxx
@@ -0,0 +1,2364 @@
+/*************************************************************************
+ *
+ * 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/svwin.h>
+
+#ifdef __MINGW32__
+#include <excpt.h>
+#endif
+
+#ifndef _OSL_MODULE_H
+#include <osl/module.h>
+#endif
+#include <wincomp.hxx>
+#include <saldata.hxx>
+#include <salinst.h>
+#include <salgdi.h>
+#include <salframe.h>
+#include <vcl/salptype.hxx>
+#include <salprn.h>
+#include <vcl/print.h>
+#include <vcl/jobset.h>
+
+#include <tools/urlobj.hxx>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker.hpp>
+#include <com/sun/star/ui/dialogs/XFilterManager.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <comphelper/processfactory.hxx>
+
+#include <malloc.h>
+
+#ifdef __MINGW32__
+#define CATCH_DRIVER_EX_BEGIN \
+ jmp_buf jmpbuf; \
+ __SEHandler han; \
+ if (__builtin_setjmp(jmpbuf) == 0) \
+ { \
+ han.Set(jmpbuf, NULL, (__SEHandler::PF)EXCEPTION_EXECUTE_HANDLER)
+
+#define CATCH_DRIVER_EX_END(mes, p) \
+ } \
+ han.Reset()
+#define CATCH_DRIVER_EX_END_2(mes) \
+ } \
+ han.Reset()
+#else
+#define CATCH_DRIVER_EX_BEGIN \
+ __try \
+ {
+#define CATCH_DRIVER_EX_END(mes, p) \
+ } \
+ __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))\
+ { \
+ DBG_ERROR( mes ); \
+ p->markInvalid(); \
+ }
+#define CATCH_DRIVER_EX_END_2(mes) \
+ } \
+ __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))\
+ { \
+ DBG_ERROR( mes ); \
+ }
+#endif
+
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::ui::dialogs;
+using namespace rtl;
+
+// =======================================================================
+
+static char aImplWindows[] = "windows";
+static char aImplDevices[] = "devices";
+static char aImplDevice[] = "device";
+
+static LPDEVMODEA SAL_DEVMODE_A( const ImplJobSetup* pSetupData )
+{
+ LPDEVMODEA pRet = NULL;
+ SalDriverData* pDrv = (SalDriverData*)pSetupData->mpDriverData;
+ if( pDrv->mnVersion == SAL_DRIVERDATA_VERSION_A &&
+ pSetupData->mnDriverDataLen >= sizeof(DEVMODEA)+sizeof(SalDriverData)-1
+ )
+ pRet = ((LPDEVMODEA)((pSetupData->mpDriverData) + (pDrv->mnDriverOffset)));
+ return pRet;
+}
+
+static LPDEVMODEW SAL_DEVMODE_W( const ImplJobSetup* pSetupData )
+{
+ LPDEVMODEW pRet = NULL;
+ SalDriverData* pDrv = (SalDriverData*)pSetupData->mpDriverData;
+ if( pDrv->mnVersion == SAL_DRIVERDATA_VERSION_W &&
+ pSetupData->mnDriverDataLen >= sizeof(DEVMODEW)+sizeof(SalDriverData)-1
+ )
+ pRet = ((LPDEVMODEW)((pSetupData->mpDriverData) + (pDrv->mnDriverOffset)));
+ return pRet;
+}
+
+// =======================================================================
+
+static ULONG ImplWinQueueStatusToSal( DWORD nWinStatus )
+{
+ ULONG nStatus = 0;
+ if ( nWinStatus & PRINTER_STATUS_PAUSED )
+ nStatus |= QUEUE_STATUS_PAUSED;
+ if ( nWinStatus & PRINTER_STATUS_ERROR )
+ nStatus |= QUEUE_STATUS_ERROR;
+ if ( nWinStatus & PRINTER_STATUS_PENDING_DELETION )
+ nStatus |= QUEUE_STATUS_PENDING_DELETION;
+ if ( nWinStatus & PRINTER_STATUS_PAPER_JAM )
+ nStatus |= QUEUE_STATUS_PAPER_JAM;
+ if ( nWinStatus & PRINTER_STATUS_PAPER_OUT )
+ nStatus |= QUEUE_STATUS_PAPER_OUT;
+ if ( nWinStatus & PRINTER_STATUS_MANUAL_FEED )
+ nStatus |= QUEUE_STATUS_MANUAL_FEED;
+ if ( nWinStatus & PRINTER_STATUS_PAPER_PROBLEM )
+ nStatus |= QUEUE_STATUS_PAPER_PROBLEM;
+ if ( nWinStatus & PRINTER_STATUS_OFFLINE )
+ nStatus |= QUEUE_STATUS_OFFLINE;
+ if ( nWinStatus & PRINTER_STATUS_IO_ACTIVE )
+ nStatus |= QUEUE_STATUS_IO_ACTIVE;
+ if ( nWinStatus & PRINTER_STATUS_BUSY )
+ nStatus |= QUEUE_STATUS_BUSY;
+ if ( nWinStatus & PRINTER_STATUS_PRINTING )
+ nStatus |= QUEUE_STATUS_PRINTING;
+ if ( nWinStatus & PRINTER_STATUS_OUTPUT_BIN_FULL )
+ nStatus |= QUEUE_STATUS_OUTPUT_BIN_FULL;
+ if ( nWinStatus & PRINTER_STATUS_WAITING )
+ nStatus |= QUEUE_STATUS_WAITING;
+ if ( nWinStatus & PRINTER_STATUS_PROCESSING )
+ nStatus |= QUEUE_STATUS_PROCESSING;
+ if ( nWinStatus & PRINTER_STATUS_INITIALIZING )
+ nStatus |= QUEUE_STATUS_INITIALIZING;
+ if ( nWinStatus & PRINTER_STATUS_WARMING_UP )
+ nStatus |= QUEUE_STATUS_WARMING_UP;
+ if ( nWinStatus & PRINTER_STATUS_TONER_LOW )
+ nStatus |= QUEUE_STATUS_TONER_LOW;
+ if ( nWinStatus & PRINTER_STATUS_NO_TONER )
+ nStatus |= QUEUE_STATUS_NO_TONER;
+ if ( nWinStatus & PRINTER_STATUS_PAGE_PUNT )
+ nStatus |= QUEUE_STATUS_PAGE_PUNT;
+ if ( nWinStatus & PRINTER_STATUS_USER_INTERVENTION )
+ nStatus |= QUEUE_STATUS_USER_INTERVENTION;
+ if ( nWinStatus & PRINTER_STATUS_OUT_OF_MEMORY )
+ nStatus |= QUEUE_STATUS_OUT_OF_MEMORY;
+ if ( nWinStatus & PRINTER_STATUS_DOOR_OPEN )
+ nStatus |= QUEUE_STATUS_DOOR_OPEN;
+ if ( nWinStatus & PRINTER_STATUS_SERVER_UNKNOWN )
+ nStatus |= QUEUE_STATUS_SERVER_UNKNOWN;
+ if ( nWinStatus & PRINTER_STATUS_POWER_SAVE )
+ nStatus |= QUEUE_STATUS_POWER_SAVE;
+ if ( !nStatus && !(nWinStatus & PRINTER_STATUS_NOT_AVAILABLE) )
+ nStatus |= QUEUE_STATUS_READY;
+ return nStatus;
+}
+
+// -----------------------------------------------------------------------
+
+static void getPrinterQueueInfoOldStyle( ImplPrnQueueList* pList )
+{
+ DWORD i;
+ DWORD n;
+ DWORD nBytes = 0;
+ DWORD nInfoPrn2;
+ BOOL bFound = FALSE;
+ PRINTER_INFO_2* pWinInfo2 = NULL;
+ PRINTER_INFO_2* pGetInfo2;
+ EnumPrintersA( PRINTER_ENUM_LOCAL, NULL, 2, NULL, 0, &nBytes, &nInfoPrn2 );
+ if ( nBytes )
+ {
+ pWinInfo2 = (PRINTER_INFO_2*) rtl_allocateMemory( nBytes );
+ if ( EnumPrintersA( PRINTER_ENUM_LOCAL, NULL, 2, (LPBYTE)pWinInfo2, nBytes, &nBytes, &nInfoPrn2 ) )
+ {
+ pGetInfo2 = pWinInfo2;
+ for ( i = 0; i < nInfoPrn2; i++ )
+ {
+ SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
+ pInfo->maPrinterName = ImplSalGetUniString( pGetInfo2->pPrinterName );
+ pInfo->maDriver = ImplSalGetUniString( pGetInfo2->pDriverName );
+ XubString aPortName;
+ if ( pGetInfo2->pPortName )
+ aPortName = ImplSalGetUniString( pGetInfo2->pPortName );
+ // pLocation can be 0 (the Windows docu doesn't describe this)
+ if ( pGetInfo2->pLocation && strlen( pGetInfo2->pLocation ) )
+ pInfo->maLocation = ImplSalGetUniString( pGetInfo2->pLocation );
+ else
+ pInfo->maLocation = aPortName;
+ // pComment can be 0 (the Windows docu doesn't describe this)
+ if ( pGetInfo2->pComment )
+ pInfo->maComment = ImplSalGetUniString( pGetInfo2->pComment );
+ pInfo->mnStatus = ImplWinQueueStatusToSal( pGetInfo2->Status );
+ pInfo->mnJobs = pGetInfo2->cJobs;
+ pInfo->mpSysData = new XubString( aPortName );
+ pList->Add( pInfo );
+ pGetInfo2++;
+ }
+
+ bFound = TRUE;
+ }
+ }
+
+ // read printers from win.ini
+ // TODO: MSDN: GetProfileString() should not be called from server
+ // code because it is just there for WIN16 compatibility
+ UINT nSize = 4096;
+ char* pBuf = new char[nSize];
+ UINT nRead = GetProfileStringA( aImplDevices, NULL, "", pBuf, nSize );
+ while ( nRead >= nSize-2 )
+ {
+ nSize += 2048;
+ delete []pBuf;
+ pBuf = new char[nSize];
+ nRead = GetProfileStringA( aImplDevices, NULL, "", pBuf, nSize );
+ }
+
+ // extract printer names from buffer and fill list
+ char* pName = pBuf;
+ while ( *pName )
+ {
+ char* pPortName;
+ char* pTmp;
+ char aPortBuf[256];
+ GetProfileStringA( aImplDevices, pName, "", aPortBuf, sizeof( aPortBuf ) );
+
+ pPortName = aPortBuf;
+
+ // create name
+ xub_StrLen nNameLen = sal::static_int_cast<xub_StrLen>(strlen( pName ));
+ XubString aName( ImplSalGetUniString( pName, nNameLen ) );
+
+ // get driver name
+ pTmp = pPortName;
+ while ( *pTmp != ',' )
+ pTmp++;
+ XubString aDriver( ImplSalGetUniString( pPortName, (USHORT)(pTmp-pPortName) ) );
+ pPortName = pTmp;
+
+ // get port names
+ do
+ {
+ pPortName++;
+ pTmp = pPortName;
+ while ( *pTmp && (*pTmp != ',') )
+ pTmp++;
+
+ String aPortName( ImplSalGetUniString( pPortName, (USHORT)(pTmp-pPortName) ) );
+
+ // create new entry
+ // look up if printer was already found in first loop
+ BOOL bAdd = TRUE;
+ if ( pWinInfo2 )
+ {
+ pGetInfo2 = pWinInfo2;
+ for ( n = 0; n < nInfoPrn2; n++ )
+ {
+ if ( aName.EqualsIgnoreCaseAscii( pGetInfo2->pPrinterName ) )
+ {
+ bAdd = FALSE;
+ break;
+ }
+ pGetInfo2++;
+ }
+ }
+ // if it's a new printer, add it
+ if ( bAdd )
+ {
+ SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
+ pInfo->maPrinterName = aName;
+ pInfo->maDriver = aDriver;
+ pInfo->maLocation = aPortName;
+ pInfo->mnStatus = 0;
+ pInfo->mnJobs = QUEUE_JOBS_DONTKNOW;
+ pInfo->mpSysData = new XubString( aPortName );
+ pList->Add( pInfo );
+ }
+ }
+ while ( *pTmp == ',' );
+
+ pName += nNameLen + 1;
+ }
+
+ delete []pBuf;
+ rtl_freeMemory( pWinInfo2 );
+}
+
+void WinSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
+{
+ if( ! aSalShlData.mbWPrinter )
+ {
+ getPrinterQueueInfoOldStyle( pList );
+ return;
+ }
+ DWORD i;
+ DWORD nBytes = 0;
+ DWORD nInfoPrn4 = 0;
+ PRINTER_INFO_4W* pWinInfo4 = NULL;
+ EnumPrintersW( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, NULL, 0, &nBytes, &nInfoPrn4 );
+ if ( nBytes )
+ {
+ pWinInfo4 = (PRINTER_INFO_4W*) rtl_allocateMemory( nBytes );
+ if ( EnumPrintersW( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, (LPBYTE)pWinInfo4, nBytes, &nBytes, &nInfoPrn4 ) )
+ {
+ for ( i = 0; i < nInfoPrn4; i++ )
+ {
+ SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
+ pInfo->maPrinterName = UniString( reinterpret_cast< const sal_Unicode* >(pWinInfo4[i].pPrinterName) );
+ pInfo->mnStatus = 0;
+ pInfo->mnJobs = 0;
+ pInfo->mpSysData = NULL;
+ pList->Add( pInfo );
+ }
+ }
+ rtl_freeMemory( pWinInfo4 );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static void getPrinterQueueStateOldStyle( SalPrinterQueueInfo* pInfo )
+{
+ DWORD nBytes = 0;
+ DWORD nInfoRet;
+ PRINTER_INFO_2* pWinInfo2;
+ EnumPrintersA( PRINTER_ENUM_LOCAL, NULL, 2, NULL, 0, &nBytes, &nInfoRet );
+ if ( nBytes )
+ {
+ pWinInfo2 = (PRINTER_INFO_2*) rtl_allocateMemory( nBytes );
+ if ( EnumPrintersA( PRINTER_ENUM_LOCAL, NULL, 2, (LPBYTE)pWinInfo2, nBytes, &nBytes, &nInfoRet ) )
+ {
+ PRINTER_INFO_2* pGetInfo2 = pWinInfo2;
+ for ( DWORD i = 0; i < nInfoRet; i++ )
+ {
+ if ( pInfo->maPrinterName.EqualsAscii( pGetInfo2->pPrinterName ) &&
+ ( pInfo->maDriver.Len() == 0 ||
+ pInfo->maDriver.EqualsAscii( pGetInfo2->pDriverName ) )
+ )
+ {
+ XubString aPortName;
+ if ( pGetInfo2->pPortName )
+ aPortName = ImplSalGetUniString( pGetInfo2->pPortName );
+ // pLocation can be 0 (the Windows docu doesn't describe this)
+ if ( pGetInfo2->pLocation && strlen( pGetInfo2->pLocation ) )
+ pInfo->maLocation = ImplSalGetUniString( pGetInfo2->pLocation );
+ else
+ pInfo->maLocation = aPortName;
+ // pComment can be 0 (the Windows docu doesn't describe this)
+ if ( pGetInfo2->pComment )
+ pInfo->maComment = ImplSalGetUniString( pGetInfo2->pComment );
+ pInfo->mnStatus = ImplWinQueueStatusToSal( pGetInfo2->Status );
+ pInfo->mnJobs = pGetInfo2->cJobs;
+ if( ! pInfo->mpSysData )
+ pInfo->mpSysData = new XubString( aPortName );
+ break;
+ }
+
+ pGetInfo2++;
+ }
+ }
+
+ rtl_freeMemory( pWinInfo2 );
+ }
+}
+
+void WinSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* pInfo )
+{
+ if( ! aSalShlData.mbWPrinter )
+ {
+ getPrinterQueueStateOldStyle( pInfo );
+ return;
+ }
+
+ HANDLE hPrinter = 0;
+ LPWSTR pPrnName = reinterpret_cast<LPWSTR>(const_cast<sal_Unicode*>(pInfo->maPrinterName.GetBuffer()));
+ if( OpenPrinterW( pPrnName, &hPrinter, NULL ) )
+ {
+ DWORD nBytes = 0;
+ GetPrinterW( hPrinter, 2, NULL, 0, &nBytes );
+ if( nBytes )
+ {
+ PRINTER_INFO_2W* pWinInfo2 = (PRINTER_INFO_2W*)rtl_allocateMemory(nBytes);
+ if( GetPrinterW( hPrinter, 2, (LPBYTE)pWinInfo2, nBytes, &nBytes ) )
+ {
+ if( pWinInfo2->pDriverName )
+ pInfo->maDriver = String( reinterpret_cast< const sal_Unicode* >(pWinInfo2->pDriverName) );
+ XubString aPortName;
+ if ( pWinInfo2->pPortName )
+ aPortName = String( reinterpret_cast< const sal_Unicode* >(pWinInfo2->pPortName) );
+ // pLocation can be 0 (the Windows docu doesn't describe this)
+ if ( pWinInfo2->pLocation && *pWinInfo2->pLocation )
+ pInfo->maLocation = String( reinterpret_cast< const sal_Unicode* >(pWinInfo2->pLocation) );
+ else
+ pInfo->maLocation = aPortName;
+ // pComment can be 0 (the Windows docu doesn't describe this)
+ if ( pWinInfo2->pComment )
+ pInfo->maComment = String( reinterpret_cast< const sal_Unicode* >(pWinInfo2->pComment) );
+ pInfo->mnStatus = ImplWinQueueStatusToSal( pWinInfo2->Status );
+ pInfo->mnJobs = pWinInfo2->cJobs;
+ if( ! pInfo->mpSysData )
+ pInfo->mpSysData = new XubString( aPortName );
+ }
+ rtl_freeMemory(pWinInfo2);
+ }
+ ClosePrinter( hPrinter );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
+{
+ delete (String*)(pInfo->mpSysData);
+ delete pInfo;
+}
+
+// -----------------------------------------------------------------------
+XubString WinSalInstance::GetDefaultPrinter()
+{
+ static bool bGetDefPrtAPI = true;
+ static BOOL(WINAPI*pGetDefaultPrinter)(LPWSTR,LPDWORD) = NULL;
+ // try to use GetDefaultPrinter API (not available prior to W2000)
+ if( bGetDefPrtAPI )
+ {
+ bGetDefPrtAPI = false;
+ // check for W2k and XP
+ if( aSalShlData.maVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && aSalShlData.maVersionInfo.dwMajorVersion >= 5 )
+ {
+ OUString aLibraryName( RTL_CONSTASCII_USTRINGPARAM( "winspool.drv" ) );
+ oslModule pLib = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT );
+ oslGenericFunction pFunc = NULL;
+ if( pLib )
+ {
+ OUString queryFuncName( RTL_CONSTASCII_USTRINGPARAM( "GetDefaultPrinterW" ) );
+ pFunc = osl_getFunctionSymbol( pLib, queryFuncName.pData );
+ }
+
+ pGetDefaultPrinter = (BOOL(WINAPI*)(LPWSTR,LPDWORD)) pFunc;
+ }
+ }
+ if( pGetDefaultPrinter )
+ {
+ DWORD nChars = 0;
+ pGetDefaultPrinter( NULL, &nChars );
+ if( nChars )
+ {
+ LPWSTR pStr = (LPWSTR)rtl_allocateMemory(nChars*sizeof(WCHAR));
+ XubString aDefPrt;
+ if( pGetDefaultPrinter( pStr, &nChars ) )
+ {
+ aDefPrt = reinterpret_cast<sal_Unicode* >(pStr);
+ }
+ rtl_freeMemory( pStr );
+ if( aDefPrt.Len() )
+ return aDefPrt;
+ }
+ }
+
+ // get default printer from win.ini
+ char szBuffer[256];
+ GetProfileStringA( aImplWindows, aImplDevice, "", szBuffer, sizeof( szBuffer ) );
+ if ( szBuffer[0] )
+ {
+ // Printername suchen
+ char* pBuf = szBuffer;
+ char* pTmp = pBuf;
+ while ( *pTmp && (*pTmp != ',') )
+ pTmp++;
+ return ImplSalGetUniString( pBuf, (xub_StrLen)(pTmp-pBuf) );
+ }
+ else
+ return XubString();
+}
+
+// =======================================================================
+
+static DWORD ImplDeviceCaps( WinSalInfoPrinter* pPrinter, WORD nCaps,
+ BYTE* pOutput, const ImplJobSetup* pSetupData )
+{
+ if( aSalShlData.mbWPrinter )
+ {
+ DEVMODEW* pDevMode;
+ if ( !pSetupData || !pSetupData->mpDriverData )
+ pDevMode = NULL;
+ else
+ pDevMode = SAL_DEVMODE_W( pSetupData );
+
+ return DeviceCapabilitiesW( reinterpret_cast<LPCWSTR>(pPrinter->maDeviceName.GetBuffer()),
+ reinterpret_cast<LPCWSTR>(pPrinter->maPortName.GetBuffer()),
+ nCaps, (LPWSTR)pOutput, pDevMode );
+ }
+ else
+ {
+ DEVMODEA* pDevMode;
+ if ( !pSetupData || !pSetupData->mpDriverData )
+ pDevMode = NULL;
+ else
+ pDevMode = SAL_DEVMODE_A( pSetupData );
+
+ return DeviceCapabilitiesA( ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE ).GetBuffer(),
+ ImplSalGetWinAnsiString( pPrinter->maPortName, TRUE ).GetBuffer(),
+ nCaps, (LPSTR)pOutput, pDevMode );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static BOOL ImplTestSalJobSetup( WinSalInfoPrinter* pPrinter,
+ ImplJobSetup* pSetupData, BOOL bDelete )
+{
+ if ( pSetupData && pSetupData->mpDriverData )
+ {
+ // signature and size must fit to avoid using
+ // JobSetups from a wrong system
+
+ // initialize versions from jobsetup
+ // those will be overwritten with driver's version
+ DEVMODEA* pDevModeA = NULL;
+ DEVMODEW* pDevModeW = NULL;
+ LONG dmSpecVersion = -1;
+ LONG dmDriverVersion = -1;
+ SalDriverData* pSalDriverData = (SalDriverData*)pSetupData->mpDriverData;
+ BYTE* pDriverData = ((BYTE*)pSalDriverData) + pSalDriverData->mnDriverOffset;
+ if( pSalDriverData->mnVersion == SAL_DRIVERDATA_VERSION_W )
+ {
+ if( aSalShlData.mbWPrinter )
+ pDevModeW = (DEVMODEW*)pDriverData;
+ }
+ else if( pSalDriverData->mnVersion == SAL_DRIVERDATA_VERSION_A )
+ {
+ if( ! aSalShlData.mbWPrinter )
+ pDevModeA = (DEVMODEA*)pDriverData;
+ }
+
+ long nSysJobSize = -1;
+ if( pPrinter && ( pDevModeA || pDevModeW ) )
+ {
+ // just too many driver crashes in that area -> check the dmSpecVersion and dmDriverVersion fields always !!!
+ // this prevents using the jobsetup between different Windows versions (eg from XP to 9x) but we
+ // can avoid potential driver crashes as their jobsetups are often not compatible
+ // #110800#, #111151#, #112381#, #i16580#, #i14173# and perhaps #112375#
+ ByteString aPrinterNameA= ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE );
+ HANDLE hPrn;
+ LPWSTR pPrinterNameW = reinterpret_cast<LPWSTR>(const_cast<sal_Unicode*>(pPrinter->maDeviceName.GetBuffer()));
+ if ( ! aSalShlData.mbWPrinter )
+ {
+ if ( !OpenPrinterA( (LPSTR)aPrinterNameA.GetBuffer(), &hPrn, NULL ) )
+ return FALSE;
+ }
+ else
+ if ( !OpenPrinterW( pPrinterNameW, &hPrn, NULL ) )
+ return FALSE;
+
+ // #131642# hPrn==HGDI_ERROR even though OpenPrinter() succeeded!
+ if( hPrn == HGDI_ERROR )
+ return FALSE;
+
+ if( aSalShlData.mbWPrinter )
+ {
+ nSysJobSize = DocumentPropertiesW( 0, hPrn,
+ pPrinterNameW,
+ NULL, NULL, 0 );
+ }
+ else
+ {
+ nSysJobSize = DocumentPropertiesA( 0, hPrn,
+ (LPSTR)aPrinterNameA.GetBuffer(),
+ NULL, NULL, 0 );
+ }
+
+ if( nSysJobSize < 0 )
+ {
+ ClosePrinter( hPrn );
+ return FALSE;
+ }
+ BYTE *pBuffer = (BYTE*)_alloca( nSysJobSize );
+ LONG nRet = -1;
+ if( aSalShlData.mbWPrinter )
+ {
+ nRet = DocumentPropertiesW( 0, hPrn,
+ pPrinterNameW,
+ (LPDEVMODEW)pBuffer, NULL, DM_OUT_BUFFER );
+ }
+ else
+ {
+ nRet = DocumentPropertiesA( 0, hPrn,
+ (LPSTR)aPrinterNameA.GetBuffer(),
+ (LPDEVMODEA)pBuffer, NULL, DM_OUT_BUFFER );
+ }
+ if( nRet < 0 )
+ {
+ ClosePrinter( hPrn );
+ return FALSE;
+ }
+
+ // the spec version differs between the windows platforms, ie 98,NT,2000/XP
+ // this allows us to throw away printer settings from other platforms that might crash a buggy driver
+ // we check the driver version as well
+ dmSpecVersion = aSalShlData.mbWPrinter ? ((DEVMODEW*)pBuffer)->dmSpecVersion : ((DEVMODEA*)pBuffer)->dmSpecVersion;
+ dmDriverVersion = aSalShlData.mbWPrinter ? ((DEVMODEW*)pBuffer)->dmDriverVersion : ((DEVMODEA*)pBuffer)->dmDriverVersion;
+
+ ClosePrinter( hPrn );
+ }
+ SalDriverData* pSetupDriverData = (SalDriverData*)(pSetupData->mpDriverData);
+ if ( (pSetupData->mnSystem == JOBSETUP_SYSTEM_WINDOWS) &&
+ (pPrinter->maDriverName == pSetupData->maDriver) &&
+ (pSetupData->mnDriverDataLen > sizeof( SalDriverData )) &&
+ (long)(pSetupData->mnDriverDataLen - pSetupDriverData->mnDriverOffset) == nSysJobSize &&
+ pSetupDriverData->mnSysSignature == SAL_DRIVERDATA_SYSSIGN )
+ {
+ if( pDevModeA &&
+ (dmSpecVersion == pDevModeA->dmSpecVersion) &&
+ (dmDriverVersion == pDevModeA->dmDriverVersion) )
+ return TRUE;
+ if( pDevModeW &&
+ (dmSpecVersion == pDevModeW->dmSpecVersion) &&
+ (dmDriverVersion == pDevModeW->dmDriverVersion) )
+ return TRUE;
+ }
+ if ( bDelete )
+ {
+ rtl_freeMemory( pSetupData->mpDriverData );
+ pSetupData->mpDriverData = NULL;
+ pSetupData->mnDriverDataLen = 0;
+ }
+ }
+
+ return FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+static BOOL ImplUpdateSalJobSetup( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData,
+ BOOL bIn, WinSalFrame* pVisibleDlgParent )
+{
+ ByteString aPrinterNameA = ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE );
+ HANDLE hPrn;
+ LPWSTR pPrinterNameW = reinterpret_cast<LPWSTR>(const_cast<sal_Unicode*>(pPrinter->maDeviceName.GetBuffer()));
+ if( aSalShlData.mbWPrinter )
+ {
+ if ( !OpenPrinterW( pPrinterNameW, &hPrn, NULL ) )
+ return FALSE;
+ }
+ else
+ {
+ if ( !OpenPrinterA( (LPSTR)aPrinterNameA.GetBuffer(), &hPrn, NULL ) )
+ return FALSE;
+ }
+ // #131642# hPrn==HGDI_ERROR even though OpenPrinter() succeeded!
+ if( hPrn == HGDI_ERROR )
+ return FALSE;
+
+ LONG nRet;
+ LONG nSysJobSize = -1;
+ HWND hWnd = 0;
+ DWORD nMode = DM_OUT_BUFFER;
+ ULONG nDriverDataLen = 0;
+ SalDriverData* pOutBuffer = NULL;
+ BYTE* pInBuffer = NULL;
+
+ if( aSalShlData.mbWPrinter )
+ {
+ nSysJobSize = DocumentPropertiesW( hWnd, hPrn,
+ pPrinterNameW,
+ NULL, NULL, 0 );
+ }
+ else
+ nSysJobSize = DocumentPropertiesA( hWnd, hPrn,
+ (LPSTR)ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE ).GetBuffer(),
+ NULL, NULL, 0 );
+ if ( nSysJobSize < 0 )
+ {
+ ClosePrinter( hPrn );
+ return FALSE;
+ }
+
+ // Outputbuffer anlegen
+ nDriverDataLen = sizeof(SalDriverData) + nSysJobSize-1;
+ pOutBuffer = (SalDriverData*)rtl_allocateZeroMemory( nDriverDataLen );
+ pOutBuffer->mnSysSignature = SAL_DRIVERDATA_SYSSIGN;
+ pOutBuffer->mnVersion = aSalShlData.mbWPrinter ? SAL_DRIVERDATA_VERSION_W : SAL_DRIVERDATA_VERSION_A;
+ // calculate driver data offset including structure padding
+ pOutBuffer->mnDriverOffset = sal::static_int_cast<USHORT>(
+ (char*)pOutBuffer->maDriverData -
+ (char*)pOutBuffer );
+
+ // Testen, ob wir einen geeigneten Inputbuffer haben
+ if ( bIn && ImplTestSalJobSetup( pPrinter, pSetupData, FALSE ) )
+ {
+ pInBuffer = (BYTE*)pSetupData->mpDriverData + ((SalDriverData*)pSetupData->mpDriverData)->mnDriverOffset;
+ nMode |= DM_IN_BUFFER;
+ }
+
+ // Testen, ob Dialog angezeigt werden soll
+ if ( pVisibleDlgParent )
+ {
+ hWnd = pVisibleDlgParent->mhWnd;
+ nMode |= DM_IN_PROMPT;
+ }
+
+ // Release mutex, in the other case we don't get paints and so on
+ ULONG nMutexCount=0;
+ if ( pVisibleDlgParent )
+ nMutexCount = ImplSalReleaseYieldMutex();
+
+ BYTE* pOutDevMode = (((BYTE*)pOutBuffer) + pOutBuffer->mnDriverOffset);
+ if( aSalShlData.mbWPrinter )
+ {
+ nRet = DocumentPropertiesW( hWnd, hPrn,
+ pPrinterNameW,
+ (LPDEVMODEW)pOutDevMode, (LPDEVMODEW)pInBuffer, nMode );
+ }
+ else
+ {
+ nRet = DocumentPropertiesA( hWnd, hPrn,
+ (LPSTR)ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE ).GetBuffer(),
+ (LPDEVMODEA)pOutDevMode, (LPDEVMODEA)pInBuffer, nMode );
+ }
+ if ( pVisibleDlgParent )
+ ImplSalAcquireYieldMutex( nMutexCount );
+ ClosePrinter( hPrn );
+
+ if( (nRet < 0) || (pVisibleDlgParent && (nRet == IDCANCEL)) )
+ {
+ rtl_freeMemory( pOutBuffer );
+ return FALSE;
+ }
+
+ // fill up string buffers with 0 so they do not influence a JobSetup's memcmp
+ if( aSalShlData.mbWPrinter )
+ {
+ if( ((LPDEVMODEW)pOutDevMode)->dmSize >= 64 )
+ {
+ sal_Int32 nLen = rtl_ustr_getLength( (const sal_Unicode*)((LPDEVMODEW)pOutDevMode)->dmDeviceName );
+ if ( nLen < sizeof( ((LPDEVMODEW)pOutDevMode)->dmDeviceName )/sizeof(sal_Unicode) )
+ memset( ((LPDEVMODEW)pOutDevMode)->dmDeviceName+nLen, 0, sizeof( ((LPDEVMODEW)pOutDevMode)->dmDeviceName )-(nLen*sizeof(sal_Unicode)) );
+ }
+ if( ((LPDEVMODEW)pOutDevMode)->dmSize >= 166 )
+ {
+ sal_Int32 nLen = rtl_ustr_getLength( (const sal_Unicode*)((LPDEVMODEW)pOutDevMode)->dmFormName );
+ if ( nLen < sizeof( ((LPDEVMODEW)pOutDevMode)->dmFormName )/sizeof(sal_Unicode) )
+ memset( ((LPDEVMODEW)pOutDevMode)->dmFormName+nLen, 0, sizeof( ((LPDEVMODEW)pOutDevMode)->dmFormName )-(nLen*sizeof(sal_Unicode)) );
+ }
+ }
+ else
+ {
+ if( ((LPDEVMODEA)pOutDevMode)->dmSize >= 32 )
+ {
+ sal_Int32 nLen = strlen( (const char*)((LPDEVMODEA)pOutDevMode)->dmDeviceName );
+ if ( nLen < sizeof( ((LPDEVMODEA)pOutDevMode)->dmDeviceName ) )
+ memset( ((LPDEVMODEA)pOutDevMode)->dmDeviceName+nLen, 0, sizeof( ((LPDEVMODEA)pOutDevMode)->dmDeviceName )-nLen );
+ }
+ if( ((LPDEVMODEA)pOutDevMode)->dmSize >= 102 )
+ {
+ sal_Int32 nLen = strlen( (const char*)((LPDEVMODEA)pOutDevMode)->dmFormName );
+ if ( nLen < sizeof( ((LPDEVMODEA)pOutDevMode)->dmFormName ) )
+ memset( ((LPDEVMODEA)pOutDevMode)->dmFormName+nLen, 0, sizeof( ((LPDEVMODEA)pOutDevMode)->dmFormName )-nLen );
+ }
+ }
+
+ // update data
+ if ( pSetupData->mpDriverData )
+ rtl_freeMemory( pSetupData->mpDriverData );
+ pSetupData->mnDriverDataLen = nDriverDataLen;
+ pSetupData->mpDriverData = (BYTE*)pOutBuffer;
+ pSetupData->mnSystem = JOBSETUP_SYSTEM_WINDOWS;
+
+ return TRUE;
+}
+
+// -----------------------------------------------------------------------
+
+#define DECLARE_DEVMODE( i )\
+ DEVMODEA* pDevModeA = SAL_DEVMODE_A(i);\
+ DEVMODEW* pDevModeW = SAL_DEVMODE_W(i);\
+ if( pDevModeA == NULL && pDevModeW == NULL )\
+ return
+
+#define CHOOSE_DEVMODE(i)\
+ (pDevModeW ? pDevModeW->i : pDevModeA->i)
+
+static void ImplDevModeToJobSetup( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData, ULONG nFlags )
+{
+ if ( !pSetupData || !pSetupData->mpDriverData )
+ return;
+
+ DECLARE_DEVMODE( pSetupData );
+
+ // Orientation
+ if ( nFlags & SAL_JOBSET_ORIENTATION )
+ {
+ if ( CHOOSE_DEVMODE(dmOrientation) == DMORIENT_PORTRAIT )
+ pSetupData->meOrientation = ORIENTATION_PORTRAIT;
+ else if ( CHOOSE_DEVMODE(dmOrientation) == DMORIENT_LANDSCAPE )
+ pSetupData->meOrientation = ORIENTATION_LANDSCAPE;
+ }
+
+ // PaperBin
+ if ( nFlags & SAL_JOBSET_PAPERBIN )
+ {
+ ULONG nCount = ImplDeviceCaps( pPrinter, DC_BINS, NULL, pSetupData );
+
+ if ( nCount && (nCount != GDI_ERROR) )
+ {
+ WORD* pBins = (WORD*)rtl_allocateZeroMemory( nCount*sizeof(WORD) );
+ ImplDeviceCaps( pPrinter, DC_BINS, (BYTE*)pBins, pSetupData );
+ pSetupData->mnPaperBin = 0;
+
+ // search the right bin and assign index to mnPaperBin
+ for( ULONG i = 0; i < nCount; i++ )
+ {
+ if( CHOOSE_DEVMODE(dmDefaultSource) == pBins[ i ] )
+ {
+ pSetupData->mnPaperBin = (USHORT)i;
+ break;
+ }
+ }
+
+ rtl_freeMemory( pBins );
+ }
+ }
+
+ // PaperSize
+ if ( nFlags & SAL_JOBSET_PAPERSIZE )
+ {
+ if( (CHOOSE_DEVMODE(dmFields) & (DM_PAPERWIDTH|DM_PAPERLENGTH)) == (DM_PAPERWIDTH|DM_PAPERLENGTH) )
+ {
+ pSetupData->mnPaperWidth = CHOOSE_DEVMODE(dmPaperWidth)*10;
+ pSetupData->mnPaperHeight = CHOOSE_DEVMODE(dmPaperLength)*10;
+ }
+ else
+ {
+ ULONG nPaperCount = ImplDeviceCaps( pPrinter, DC_PAPERS, NULL, pSetupData );
+ WORD* pPapers = NULL;
+ ULONG nPaperSizeCount = ImplDeviceCaps( pPrinter, DC_PAPERSIZE, NULL, pSetupData );
+ POINT* pPaperSizes = NULL;
+ if ( nPaperCount && (nPaperCount != GDI_ERROR) )
+ {
+ pPapers = (WORD*)rtl_allocateZeroMemory(nPaperCount*sizeof(WORD));
+ ImplDeviceCaps( pPrinter, DC_PAPERS, (BYTE*)pPapers, pSetupData );
+ }
+ if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) )
+ {
+ pPaperSizes = (POINT*)rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT));
+ ImplDeviceCaps( pPrinter, DC_PAPERSIZE, (BYTE*)pPaperSizes, pSetupData );
+ }
+ if( nPaperSizeCount == nPaperCount && pPaperSizes && pPapers )
+ {
+ for( ULONG i = 0; i < nPaperCount; i++ )
+ {
+ if( pPapers[ i ] == CHOOSE_DEVMODE(dmPaperSize) )
+ {
+ pSetupData->mnPaperWidth = pPaperSizes[ i ].x*10;
+ pSetupData->mnPaperHeight = pPaperSizes[ i ].y*10;
+ break;
+ }
+ }
+ }
+ if( pPapers )
+ rtl_freeMemory( pPapers );
+ if( pPaperSizes )
+ rtl_freeMemory( pPaperSizes );
+ }
+ switch( CHOOSE_DEVMODE(dmPaperSize) )
+ {
+ case( DMPAPER_LETTER ):
+ pSetupData->mePaperFormat = PAPER_LETTER;
+ break;
+ case( DMPAPER_TABLOID ):
+ pSetupData->mePaperFormat = PAPER_TABLOID;
+ break;
+ case( DMPAPER_LEDGER ):
+ pSetupData->mePaperFormat = PAPER_LEDGER;
+ break;
+ case( DMPAPER_LEGAL ):
+ pSetupData->mePaperFormat = PAPER_LEGAL;
+ break;
+ case( DMPAPER_STATEMENT ):
+ pSetupData->mePaperFormat = PAPER_STATEMENT;
+ break;
+ case( DMPAPER_EXECUTIVE ):
+ pSetupData->mePaperFormat = PAPER_EXECUTIVE;
+ break;
+ case( DMPAPER_A3 ):
+ pSetupData->mePaperFormat = PAPER_A3;
+ break;
+ case( DMPAPER_A4 ):
+ pSetupData->mePaperFormat = PAPER_A4;
+ break;
+ case( DMPAPER_A5 ):
+ pSetupData->mePaperFormat = PAPER_A5;
+ break;
+ //See http://wiki.services.openoffice.org/wiki/DefaultPaperSize
+ //i.e.
+ //http://msdn.microsoft.com/en-us/library/dd319099(VS.85).aspx
+ //DMPAPER_B4 12 B4 (JIS) 257 x 364 mm
+ //http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf
+ //also says that the MS DMPAPER_B4 is JIS, which makes most sense. And
+ //matches our Excel filter's belief about the matching XlPaperSize
+ //enumeration.
+ //
+ //http://msdn.microsoft.com/en-us/library/ms776398(VS.85).aspx said
+ ////"DMPAPER_B4 12 B4 (JIS) 250 x 354"
+ //which is bogus as it's either JIS 257 × 364 or ISO 250 × 353
+ //(cmc)
+ case( DMPAPER_B4 ):
+ pSetupData->mePaperFormat = PAPER_B4_JIS;
+ break;
+ case( DMPAPER_B5 ):
+ pSetupData->mePaperFormat = PAPER_B5_JIS;
+ break;
+ case( DMPAPER_QUARTO ):
+ pSetupData->mePaperFormat = PAPER_QUARTO;
+ break;
+ case( DMPAPER_10X14 ):
+ pSetupData->mePaperFormat = PAPER_10x14;
+ break;
+ case( DMPAPER_NOTE ):
+ pSetupData->mePaperFormat = PAPER_LETTER;
+ break;
+ case( DMPAPER_ENV_9 ):
+ pSetupData->mePaperFormat = PAPER_ENV_9;
+ break;
+ case( DMPAPER_ENV_10 ):
+ pSetupData->mePaperFormat = PAPER_ENV_10;
+ break;
+ case( DMPAPER_ENV_11 ):
+ pSetupData->mePaperFormat = PAPER_ENV_11;
+ break;
+ case( DMPAPER_ENV_12 ):
+ pSetupData->mePaperFormat = PAPER_ENV_12;
+ break;
+ case( DMPAPER_ENV_14 ):
+ pSetupData->mePaperFormat = PAPER_ENV_14;
+ break;
+ case( DMPAPER_CSHEET ):
+ pSetupData->mePaperFormat = PAPER_C;
+ break;
+ case( DMPAPER_DSHEET ):
+ pSetupData->mePaperFormat = PAPER_D;
+ break;
+ case( DMPAPER_ESHEET ):
+ pSetupData->mePaperFormat = PAPER_E;
+ break;
+ case( DMPAPER_ENV_DL):
+ pSetupData->mePaperFormat = PAPER_ENV_DL;
+ break;
+ case( DMPAPER_ENV_C5):
+ pSetupData->mePaperFormat = PAPER_ENV_C5;
+ break;
+ case( DMPAPER_ENV_C3):
+ pSetupData->mePaperFormat = PAPER_ENV_C3;
+ break;
+ case( DMPAPER_ENV_C4):
+ pSetupData->mePaperFormat = PAPER_ENV_C4;
+ break;
+ case( DMPAPER_ENV_C6):
+ pSetupData->mePaperFormat = PAPER_ENV_C6;
+ break;
+ case( DMPAPER_ENV_C65):
+ pSetupData->mePaperFormat = PAPER_ENV_C65;
+ break;
+ case( DMPAPER_ENV_ITALY ):
+ pSetupData->mePaperFormat = PAPER_ENV_ITALY;
+ break;
+ case( DMPAPER_ENV_MONARCH ):
+ pSetupData->mePaperFormat = PAPER_ENV_MONARCH;
+ break;
+ case( DMPAPER_ENV_PERSONAL ):
+ pSetupData->mePaperFormat = PAPER_ENV_PERSONAL;
+ break;
+ case( DMPAPER_FANFOLD_US ):
+ pSetupData->mePaperFormat = PAPER_FANFOLD_US;
+ break;
+ case( DMPAPER_FANFOLD_STD_GERMAN ):
+ pSetupData->mePaperFormat = PAPER_FANFOLD_DE;
+ break;
+ case( DMPAPER_FANFOLD_LGL_GERMAN ):
+ pSetupData->mePaperFormat = PAPER_FANFOLD_LEGAL_DE;
+ break;
+ case( DMPAPER_ISO_B4 ):
+ pSetupData->mePaperFormat = PAPER_B4_ISO;
+ break;
+ case( DMPAPER_JAPANESE_POSTCARD ):
+ pSetupData->mePaperFormat = PAPER_POSTCARD_JP;
+ break;
+ case( DMPAPER_9X11 ):
+ pSetupData->mePaperFormat = PAPER_9x11;
+ break;
+ case( DMPAPER_10X11 ):
+ pSetupData->mePaperFormat = PAPER_10x11;
+ break;
+ case( DMPAPER_15X11 ):
+ pSetupData->mePaperFormat = PAPER_15x11;
+ break;
+ case( DMPAPER_ENV_INVITE ):
+ pSetupData->mePaperFormat = PAPER_ENV_INVITE;
+ break;
+ case( DMPAPER_A_PLUS ):
+ pSetupData->mePaperFormat = PAPER_A_PLUS;
+ break;
+ case( DMPAPER_B_PLUS ):
+ pSetupData->mePaperFormat = PAPER_B_PLUS;
+ break;
+ case( DMPAPER_LETTER_PLUS ):
+ pSetupData->mePaperFormat = PAPER_LETTER_PLUS;
+ break;
+ case( DMPAPER_A4_PLUS ):
+ pSetupData->mePaperFormat = PAPER_A4_PLUS;
+ break;
+ case( DMPAPER_A2 ):
+ pSetupData->mePaperFormat = PAPER_A2;
+ break;
+ case( DMPAPER_DBL_JAPANESE_POSTCARD ):
+ pSetupData->mePaperFormat = PAPER_DOUBLEPOSTCARD_JP;
+ break;
+ case( DMPAPER_A6 ):
+ pSetupData->mePaperFormat = PAPER_A6;
+ break;
+ case( DMPAPER_B6_JIS ):
+ pSetupData->mePaperFormat = PAPER_B6_JIS;
+ break;
+ case( DMPAPER_12X11 ):
+ pSetupData->mePaperFormat = PAPER_12x11;
+ break;
+ default:
+ pSetupData->mePaperFormat = PAPER_USER;
+ break;
+ }
+ }
+
+ if( nFlags & SAL_JOBSET_DUPLEXMODE )
+ {
+ DuplexMode eDuplex = DUPLEX_UNKNOWN;
+ if( (CHOOSE_DEVMODE(dmFields) & DM_DUPLEX) )
+ {
+ if( CHOOSE_DEVMODE(dmDuplex) == DMDUP_SIMPLEX )
+ eDuplex = DUPLEX_OFF;
+ else if( CHOOSE_DEVMODE(dmDuplex) == DMDUP_VERTICAL )
+ eDuplex = DUPLEX_LONGEDGE;
+ else if( CHOOSE_DEVMODE(dmDuplex) == DMDUP_HORIZONTAL )
+ eDuplex = DUPLEX_SHORTEDGE;
+ }
+ pSetupData->meDuplexMode = eDuplex;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static void ImplJobSetupToDevMode( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData, ULONG nFlags )
+{
+ if ( !pSetupData || !pSetupData->mpDriverData )
+ return;
+
+ DECLARE_DEVMODE( pSetupData );
+
+ // Orientation
+ if ( nFlags & SAL_JOBSET_ORIENTATION )
+ {
+ CHOOSE_DEVMODE(dmFields) |= DM_ORIENTATION;
+ if ( pSetupData->meOrientation == ORIENTATION_PORTRAIT )
+ CHOOSE_DEVMODE(dmOrientation) = DMORIENT_PORTRAIT;
+ else
+ CHOOSE_DEVMODE(dmOrientation) = DMORIENT_LANDSCAPE;
+ }
+
+ // PaperBin
+ if ( nFlags & SAL_JOBSET_PAPERBIN )
+ {
+ ULONG nCount = ImplDeviceCaps( pPrinter, DC_BINS, NULL, pSetupData );
+
+ if ( nCount && (nCount != GDI_ERROR) )
+ {
+ WORD* pBins = (WORD*)rtl_allocateZeroMemory(nCount*sizeof(WORD));
+ ImplDeviceCaps( pPrinter, DC_BINS, (BYTE*)pBins, pSetupData );
+ CHOOSE_DEVMODE(dmFields) |= DM_DEFAULTSOURCE;
+ CHOOSE_DEVMODE(dmDefaultSource) = pBins[ pSetupData->mnPaperBin ];
+ rtl_freeMemory( pBins );
+ }
+ }
+
+ // PaperSize
+ if ( nFlags & SAL_JOBSET_PAPERSIZE )
+ {
+ CHOOSE_DEVMODE(dmFields) |= DM_PAPERSIZE;
+ CHOOSE_DEVMODE(dmPaperWidth) = 0;
+ CHOOSE_DEVMODE(dmPaperLength) = 0;
+
+ switch( pSetupData->mePaperFormat )
+ {
+ case( PAPER_A2 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A2;
+ break;
+ case( PAPER_A3 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A3;
+ break;
+ case( PAPER_A4 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A4;
+ break;
+ case( PAPER_A5 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A5;
+ break;
+ case( PAPER_B4_ISO):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ISO_B4;
+ break;
+ case( PAPER_LETTER ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_LETTER;
+ break;
+ case( PAPER_LEGAL ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_LEGAL;
+ break;
+ case( PAPER_TABLOID ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_TABLOID;
+ break;
+#if 0
+ //http://msdn.microsoft.com/en-us/library/ms776398(VS.85).aspx
+ //DMPAPER_ENV_B6 is documented as:
+ //"DMPAPER_ENV_B6 35 Envelope B6 176 x 125 mm"
+ //which is the wrong way around, it is surely 125 x 176, i.e.
+ //compare DMPAPER_ENV_B4 and DMPAPER_ENV_B4 as
+ //DMPAPER_ENV_B4 33 Envelope B4 250 x 353 mm
+ //DMPAPER_ENV_B5 34 Envelope B5 176 x 250 mm
+ case( PAPER_B6_ISO ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_B6;
+ break;
+#endif
+ case( PAPER_ENV_C4 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C4;
+ break;
+ case( PAPER_ENV_C5 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C5;
+ break;
+ case( PAPER_ENV_C6 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C6;
+ break;
+ case( PAPER_ENV_C65 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C65;
+ break;
+ case( PAPER_ENV_DL ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_DL;
+ break;
+ case( PAPER_C ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_CSHEET;
+ break;
+ case( PAPER_D ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_DSHEET;
+ break;
+ case( PAPER_E ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ESHEET;
+ break;
+ case( PAPER_EXECUTIVE ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_EXECUTIVE;
+ break;
+ case( PAPER_FANFOLD_LEGAL_DE ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_FANFOLD_LGL_GERMAN;
+ break;
+ case( PAPER_ENV_MONARCH ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_MONARCH;
+ break;
+ case( PAPER_ENV_PERSONAL ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_PERSONAL;
+ break;
+ case( PAPER_ENV_9 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_9;
+ break;
+ case( PAPER_ENV_10 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_10;
+ break;
+ case( PAPER_ENV_11 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_11;
+ break;
+ case( PAPER_ENV_12 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_12;
+ break;
+ //See the comments on DMPAPER_B4 above
+ case( PAPER_B4_JIS ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_B4;
+ break;
+ case( PAPER_B5_JIS ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_B5;
+ break;
+ case( PAPER_B6_JIS ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_B6_JIS;
+ break;
+ case( PAPER_LEDGER ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_LEDGER;
+ break;
+ case( PAPER_STATEMENT ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_STATEMENT;
+ break;
+ case( PAPER_10x14 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_10X14;
+ break;
+ case( PAPER_ENV_14 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_14;
+ break;
+ case( PAPER_ENV_C3 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C3;
+ break;
+ case( PAPER_ENV_ITALY ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_ITALY;
+ break;
+ case( PAPER_FANFOLD_US ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_FANFOLD_US;
+ break;
+ case( PAPER_FANFOLD_DE ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_FANFOLD_STD_GERMAN;
+ break;
+ case( PAPER_POSTCARD_JP ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_JAPANESE_POSTCARD;
+ break;
+ case( PAPER_9x11 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_9X11;
+ break;
+ case( PAPER_10x11 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_10X11;
+ break;
+ case( PAPER_15x11 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_15X11;
+ break;
+ case( PAPER_ENV_INVITE ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_INVITE;
+ break;
+ case( PAPER_A_PLUS ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A_PLUS;
+ break;
+ case( PAPER_B_PLUS ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_B_PLUS;
+ break;
+ case( PAPER_LETTER_PLUS ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_LETTER_PLUS;
+ break;
+ case( PAPER_A4_PLUS ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A4_PLUS;
+ break;
+ case( PAPER_DOUBLEPOSTCARD_JP ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_DBL_JAPANESE_POSTCARD;
+ break;
+ case( PAPER_A6 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A6;
+ break;
+ case( PAPER_12x11 ):
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_12X11;
+ break;
+ default:
+ {
+ short nPaper = 0;
+ ULONG nPaperCount = ImplDeviceCaps( pPrinter, DC_PAPERS, NULL, pSetupData );
+ WORD* pPapers = NULL;
+ ULONG nPaperSizeCount = ImplDeviceCaps( pPrinter, DC_PAPERSIZE, NULL, pSetupData );
+ POINT* pPaperSizes = NULL;
+ DWORD nLandscapeAngle = ImplDeviceCaps( pPrinter, DC_ORIENTATION, NULL, pSetupData );
+ if ( nPaperCount && (nPaperCount != GDI_ERROR) )
+ {
+ pPapers = (WORD*)rtl_allocateZeroMemory(nPaperCount*sizeof(WORD));
+ ImplDeviceCaps( pPrinter, DC_PAPERS, (BYTE*)pPapers, pSetupData );
+ }
+ if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) )
+ {
+ pPaperSizes = (POINT*)rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT));
+ ImplDeviceCaps( pPrinter, DC_PAPERSIZE, (BYTE*)pPaperSizes, pSetupData );
+ }
+ if ( (nPaperSizeCount == nPaperCount) && pPapers && pPaperSizes )
+ {
+ PaperInfo aInfo(pSetupData->mnPaperWidth, pSetupData->mnPaperHeight);
+ // compare paper formats and select a good match
+ for ( ULONG i = 0; i < nPaperCount; i++ )
+ {
+ if ( aInfo.sloppyEqual(PaperInfo(pPaperSizes[i].x*10, pPaperSizes[i].y*10)))
+ {
+ nPaper = pPapers[i];
+ 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 ( !nPaper && nLandscapeAngle != 0 )
+ {
+ PaperInfo aRotatedInfo(pSetupData->mnPaperHeight, pSetupData->mnPaperWidth);
+ for ( ULONG i = 0; i < nPaperCount; i++ )
+ {
+ if ( aRotatedInfo.sloppyEqual(PaperInfo(pPaperSizes[i].x*10, pPaperSizes[i].y*10)) )
+ {
+ nPaper = pPapers[i];
+ break;
+ }
+ }
+ }
+
+ if ( nPaper )
+ CHOOSE_DEVMODE(dmPaperSize) = nPaper;
+ }
+
+ if ( !nPaper )
+ {
+ CHOOSE_DEVMODE(dmFields) |= DM_PAPERLENGTH | DM_PAPERWIDTH;
+ CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_USER;
+ CHOOSE_DEVMODE(dmPaperWidth) = (short)(pSetupData->mnPaperWidth/10);
+ CHOOSE_DEVMODE(dmPaperLength) = (short)(pSetupData->mnPaperHeight/10);
+ }
+
+ if ( pPapers )
+ rtl_freeMemory(pPapers);
+ if ( pPaperSizes )
+ rtl_freeMemory(pPaperSizes);
+
+ break;
+ }
+ }
+ }
+ if( (nFlags & SAL_JOBSET_DUPLEXMODE) )
+ {
+ switch( pSetupData->meDuplexMode )
+ {
+ case DUPLEX_OFF:
+ CHOOSE_DEVMODE(dmFields) |= DM_DUPLEX;
+ CHOOSE_DEVMODE(dmDuplex) = DMDUP_SIMPLEX;
+ break;
+ case DUPLEX_SHORTEDGE:
+ CHOOSE_DEVMODE(dmFields) |= DM_DUPLEX;
+ CHOOSE_DEVMODE(dmDuplex) = DMDUP_HORIZONTAL;
+ break;
+ case DUPLEX_LONGEDGE:
+ CHOOSE_DEVMODE(dmFields) |= DM_DUPLEX;
+ CHOOSE_DEVMODE(dmDuplex) = DMDUP_VERTICAL;
+ break;
+ case DUPLEX_UNKNOWN:
+ break;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static HDC ImplCreateICW_WithCatch( LPWSTR pDriver,
+ LPCWSTR pDevice,
+ LPDEVMODEW pDevMode )
+{
+ HDC hDC = 0;
+ CATCH_DRIVER_EX_BEGIN;
+ hDC = CreateICW( pDriver, pDevice, 0, pDevMode );
+ CATCH_DRIVER_EX_END_2( "exception in CreateICW" );
+ return hDC;
+}
+
+static HDC ImplCreateICA_WithCatch( char* pDriver,
+ char* pDevice,
+ LPDEVMODEA pDevMode )
+{
+ HDC hDC = 0;
+ CATCH_DRIVER_EX_BEGIN;
+ hDC = CreateICA( pDriver, pDevice, 0, pDevMode );
+ CATCH_DRIVER_EX_END_2( "exception in CreateICW" );
+ return hDC;
+}
+
+
+static HDC ImplCreateSalPrnIC( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData )
+{
+ HDC hDC = 0;
+ if( aSalShlData.mbWPrinter )
+ {
+ LPDEVMODEW pDevMode;
+ if ( pSetupData && pSetupData->mpDriverData )
+ pDevMode = SAL_DEVMODE_W( pSetupData );
+ else
+ pDevMode = NULL;
+ // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateIC, although declared const - so provide some space
+ // pl: does this hold true for Unicode functions ?
+ if( pPrinter->maDriverName.Len() > 2048 || pPrinter->maDeviceName.Len() > 2048 )
+ return 0;
+ sal_Unicode pDriverName[ 4096 ];
+ sal_Unicode pDeviceName[ 4096 ];
+ rtl_copyMemory( pDriverName, pPrinter->maDriverName.GetBuffer(), pPrinter->maDriverName.Len()*sizeof(sal_Unicode));
+ memset( pDriverName+pPrinter->maDriverName.Len(), 0, 32 );
+ rtl_copyMemory( pDeviceName, pPrinter->maDeviceName.GetBuffer(), pPrinter->maDeviceName.Len()*sizeof(sal_Unicode));
+ memset( pDeviceName+pPrinter->maDeviceName.Len(), 0, 32 );
+ hDC = ImplCreateICW_WithCatch( reinterpret_cast< LPWSTR >(pDriverName),
+ reinterpret_cast< LPCWSTR >(pDeviceName),
+ pDevMode );
+ }
+ else
+ {
+ LPDEVMODEA pDevMode;
+ if ( pSetupData && pSetupData->mpDriverData )
+ pDevMode = SAL_DEVMODE_A( pSetupData );
+ else
+ pDevMode = NULL;
+ // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateIC, although declared const - so provide some space
+ ByteString aDriver ( ImplSalGetWinAnsiString( pPrinter->maDriverName, TRUE ) );
+ ByteString aDevice ( ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE ) );
+ int n = aDriver.Len() > aDevice.Len() ? aDriver.Len() : aDevice.Len();
+ // #125813# under some circumstances many printer drivers really
+ // seem to have a problem with the names and their conversions.
+ // We need to get on to of this, but haven't been able to reproduce
+ // the problem yet. Put the names on the stack so we get them
+ // with an eventual crash report.
+ if( n >= 2048 )
+ return 0;
+ n += 2048;
+ char lpszDriverName[ 4096 ];
+ char lpszDeviceName[ 4096 ];
+ strncpy( lpszDriverName, aDriver.GetBuffer(), n );
+ strncpy( lpszDeviceName, aDevice.GetBuffer(), n );
+ // HDU: the crashes usually happen in a MBCS to unicode conversion,
+ // so I suspect the MBCS string's end is not properly recognized.
+ // The longest MBCS encoding I'm aware of has six bytes per code
+ // => add a couple of zeroes...
+ memset( lpszDriverName+aDriver.Len(), 0, 16 );
+ memset( lpszDeviceName+aDevice.Len(), 0, 16 );
+ hDC = ImplCreateICA_WithCatch( lpszDriverName,
+ lpszDeviceName,
+ pDevMode );
+ }
+ return hDC;
+}
+
+// -----------------------------------------------------------------------
+
+static WinSalGraphics* ImplCreateSalPrnGraphics( HDC hDC )
+{
+ WinSalGraphics* pGraphics = new WinSalGraphics;
+ pGraphics->SetLayout( 0 );
+ pGraphics->mhDC = hDC;
+ pGraphics->mhWnd = 0;
+ pGraphics->mbPrinter = TRUE;
+ pGraphics->mbVirDev = FALSE;
+ pGraphics->mbWindow = FALSE;
+ pGraphics->mbScreen = FALSE;
+ ImplSalInitGraphics( pGraphics );
+ return pGraphics;
+}
+
+// -----------------------------------------------------------------------
+
+static BOOL ImplUpdateSalPrnIC( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData )
+{
+ HDC hNewDC = ImplCreateSalPrnIC( pPrinter, pSetupData );
+ if ( !hNewDC )
+ return FALSE;
+
+ if ( pPrinter->mpGraphics )
+ {
+ ImplSalDeInitGraphics( pPrinter->mpGraphics );
+ DeleteDC( pPrinter->mpGraphics->mhDC );
+ delete pPrinter->mpGraphics;
+ }
+
+ pPrinter->mpGraphics = ImplCreateSalPrnGraphics( hNewDC );
+ pPrinter->mhDC = hNewDC;
+
+ return TRUE;
+}
+
+// =======================================================================
+
+SalInfoPrinter* WinSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
+ ImplJobSetup* pSetupData )
+{
+ WinSalInfoPrinter* pPrinter = new WinSalInfoPrinter;
+ if( ! pQueueInfo->mpSysData )
+ GetPrinterQueueState( pQueueInfo );
+ pPrinter->maDriverName = pQueueInfo->maDriver;
+ pPrinter->maDeviceName = pQueueInfo->maPrinterName;
+ pPrinter->maPortName = pQueueInfo->mpSysData ?
+ *(String*)(pQueueInfo->mpSysData)
+ : String();
+
+ // check if the provided setup data match the actual printer
+ ImplTestSalJobSetup( pPrinter, pSetupData, TRUE );
+
+ HDC hDC = ImplCreateSalPrnIC( pPrinter, pSetupData );
+ if ( !hDC )
+ {
+ delete pPrinter;
+ return NULL;
+ }
+
+ pPrinter->mpGraphics = ImplCreateSalPrnGraphics( hDC );
+ pPrinter->mhDC = hDC;
+ if ( !pSetupData->mpDriverData )
+ ImplUpdateSalJobSetup( pPrinter, pSetupData, FALSE, NULL );
+ ImplDevModeToJobSetup( pPrinter, pSetupData, SAL_JOBSET_ALL );
+ pSetupData->mnSystem = JOBSETUP_SYSTEM_WINDOWS;
+
+ return pPrinter;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
+{
+ delete pPrinter;
+}
+
+// =======================================================================
+
+WinSalInfoPrinter::WinSalInfoPrinter() :
+ mpGraphics( NULL ),
+ mhDC( 0 ),
+ mbGraphics( FALSE )
+{
+ m_bPapersInit = FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+WinSalInfoPrinter::~WinSalInfoPrinter()
+{
+ if ( mpGraphics )
+ {
+ ImplSalDeInitGraphics( mpGraphics );
+ DeleteDC( mpGraphics->mhDC );
+ delete mpGraphics;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalInfoPrinter::InitPaperFormats( const ImplJobSetup* pSetupData )
+{
+ m_aPaperFormats.clear();
+
+ DWORD nCount = ImplDeviceCaps( this, DC_PAPERSIZE, NULL, pSetupData );
+ if( nCount == GDI_ERROR )
+ nCount = 0;
+
+ POINT* pPaperSizes = NULL;
+ if( nCount )
+ {
+ pPaperSizes = (POINT*)rtl_allocateZeroMemory(nCount*sizeof(POINT));
+ ImplDeviceCaps( this, DC_PAPERSIZE, (BYTE*)pPaperSizes, pSetupData );
+
+ if( aSalShlData.mbWPrinter )
+ {
+ sal_Unicode* pNamesBuffer = (sal_Unicode*)rtl_allocateMemory(nCount*64*sizeof(sal_Unicode));
+ ImplDeviceCaps( this, DC_PAPERNAMES, (BYTE*)pNamesBuffer, pSetupData );
+ for( DWORD i = 0; i < nCount; ++i )
+ {
+ PaperInfo aInfo(pPaperSizes[i].x * 10, pPaperSizes[i].y * 10);
+ m_aPaperFormats.push_back( aInfo );
+ }
+ rtl_freeMemory( pNamesBuffer );
+ }
+ else
+ {
+ char* pNamesBuffer = (char*)rtl_allocateMemory(nCount*64);
+ ImplDeviceCaps( this, DC_PAPERNAMES, (BYTE*)pNamesBuffer, pSetupData );
+ for( DWORD i = 0; i < nCount; ++i )
+ {
+ PaperInfo aInfo(pPaperSizes[i].x * 10, pPaperSizes[i].y * 10);
+ m_aPaperFormats.push_back( aInfo );
+ }
+ rtl_freeMemory( pNamesBuffer );
+ }
+ rtl_freeMemory( pPaperSizes );
+ }
+
+ m_bPapersInit = true;
+}
+
+// -----------------------------------------------------------------------
+
+int WinSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* pSetupData )
+{
+ int nRet = ImplDeviceCaps( this, DC_ORIENTATION, NULL, pSetupData );
+
+ if( nRet != GDI_ERROR )
+ return nRet * 10;
+ else
+ return 900; // guess
+}
+
+// -----------------------------------------------------------------------
+
+SalGraphics* WinSalInfoPrinter::GetGraphics()
+{
+ if ( mbGraphics )
+ return NULL;
+
+ if ( mpGraphics )
+ mbGraphics = TRUE;
+
+ return mpGraphics;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalInfoPrinter::ReleaseGraphics( SalGraphics* )
+{
+ mbGraphics = FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalInfoPrinter::Setup( SalFrame* pFrame, ImplJobSetup* pSetupData )
+{
+ if ( ImplUpdateSalJobSetup( this, pSetupData, TRUE, static_cast<WinSalFrame*>(pFrame) ) )
+ {
+ ImplDevModeToJobSetup( this, pSetupData, SAL_JOBSET_ALL );
+ return ImplUpdateSalPrnIC( this, pSetupData );
+ }
+
+ return FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalInfoPrinter::SetPrinterData( ImplJobSetup* pSetupData )
+{
+ if ( !ImplTestSalJobSetup( this, pSetupData, FALSE ) )
+ return FALSE;
+ return ImplUpdateSalPrnIC( this, pSetupData );
+}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalInfoPrinter::SetData( ULONG nFlags, ImplJobSetup* pSetupData )
+{
+ ImplJobSetupToDevMode( this, pSetupData, nFlags );
+ if ( ImplUpdateSalJobSetup( this, pSetupData, TRUE, NULL ) )
+ {
+ ImplDevModeToJobSetup( this, pSetupData, nFlags );
+ return ImplUpdateSalPrnIC( this, pSetupData );
+ }
+
+ return FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+ULONG WinSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pSetupData )
+{
+ DWORD nRet = ImplDeviceCaps( this, DC_BINS, NULL, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return nRet;
+ else
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+XubString WinSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pSetupData, ULONG nPaperBin )
+{
+ XubString aPaperBinName;
+
+ DWORD nBins = ImplDeviceCaps( this, DC_BINNAMES, NULL, pSetupData );
+ if ( (nPaperBin < nBins) && (nBins != GDI_ERROR) )
+ {
+ if( aSalShlData.mbWPrinter )
+ {
+ sal_Unicode* pBuffer = new sal_Unicode[nBins*24];
+ DWORD nRet = ImplDeviceCaps( this, DC_BINNAMES, (BYTE*)pBuffer, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ aPaperBinName = pBuffer + (nPaperBin*24);
+ delete [] pBuffer;
+ }
+ else
+ {
+ char* pBuffer = new char[nBins*24];
+ DWORD nRet = ImplDeviceCaps( this, DC_BINNAMES, (BYTE*)pBuffer, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ aPaperBinName = ImplSalGetUniString( (const char*)(pBuffer + (nPaperBin*24)) );
+ delete [] pBuffer;
+ }
+ }
+
+ return aPaperBinName;
+}
+
+// -----------------------------------------------------------------------
+
+ULONG WinSalInfoPrinter::GetCapabilities( const ImplJobSetup* pSetupData, USHORT nType )
+{
+ DWORD nRet;
+
+ switch ( nType )
+ {
+ case PRINTER_CAPABILITIES_SUPPORTDIALOG:
+ return TRUE;
+ case PRINTER_CAPABILITIES_COPIES:
+ nRet = ImplDeviceCaps( this, DC_COPIES, NULL, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return nRet;
+ return 0;
+ case PRINTER_CAPABILITIES_COLLATECOPIES:
+ if ( aSalShlData.mbW40 )
+ {
+ nRet = ImplDeviceCaps( this, DC_COLLATE, NULL, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ {
+ nRet = ImplDeviceCaps( this, DC_COPIES, NULL, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return nRet;
+ }
+ }
+ return 0;
+
+ case PRINTER_CAPABILITIES_SETORIENTATION:
+ nRet = ImplDeviceCaps( this, DC_ORIENTATION, NULL, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return TRUE;
+ return FALSE;
+
+ case PRINTER_CAPABILITIES_SETPAPERBIN:
+ nRet = ImplDeviceCaps( this, DC_BINS, NULL, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return TRUE;
+ return FALSE;
+
+ case PRINTER_CAPABILITIES_SETPAPERSIZE:
+ case PRINTER_CAPABILITIES_SETPAPER:
+ nRet = ImplDeviceCaps( this, DC_PAPERS, NULL, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return TRUE;
+ return FALSE;
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalInfoPrinter::GetPageInfo( const ImplJobSetup*,
+ long& rOutWidth, long& rOutHeight,
+ long& rPageOffX, long& rPageOffY,
+ long& rPageWidth, long& rPageHeight )
+{
+ HDC hDC = mhDC;
+
+ rOutWidth = GetDeviceCaps( hDC, HORZRES );
+ rOutHeight = GetDeviceCaps( hDC, VERTRES );
+
+ rPageOffX = GetDeviceCaps( hDC, PHYSICALOFFSETX );
+ rPageOffY = GetDeviceCaps( hDC, PHYSICALOFFSETY );
+ rPageWidth = GetDeviceCaps( hDC, PHYSICALWIDTH );
+ rPageHeight = GetDeviceCaps( hDC, PHYSICALHEIGHT );
+}
+
+// =======================================================================
+
+SalPrinter* WinSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
+{
+ WinSalPrinter* pPrinter = new WinSalPrinter;
+ pPrinter->mpInfoPrinter = static_cast<WinSalInfoPrinter*>(pInfoPrinter);
+ return pPrinter;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalInstance::DestroyPrinter( SalPrinter* pPrinter )
+{
+ delete pPrinter;
+}
+
+// =======================================================================
+
+WIN_BOOL CALLBACK SalPrintAbortProc( HDC hPrnDC, int /* nError */ )
+{
+ SalData* pSalData = GetSalData();
+ WinSalPrinter* pPrinter;
+ BOOL bWhile = TRUE;
+ int i = 0;
+
+ do
+ {
+ // Messages verarbeiten
+ MSG aMsg;
+ if ( ImplPeekMessage( &aMsg, 0, 0, 0, PM_REMOVE ) )
+ {
+ TranslateMessage( &aMsg );
+ ImplDispatchMessage( &aMsg );
+ i++;
+ if ( i > 15 )
+ bWhile = FALSE;
+ }
+ else
+ bWhile = FALSE;
+
+ pPrinter = pSalData->mpFirstPrinter;
+ while ( pPrinter )
+ {
+ if( pPrinter->mhDC == hPrnDC )
+ break;
+
+ pPrinter = pPrinter->mpNextPrinter;
+ }
+
+ if ( !pPrinter || pPrinter->mbAbort )
+ return FALSE;
+ }
+ while ( bWhile );
+
+ return TRUE;
+}
+
+// -----------------------------------------------------------------------
+
+static LPDEVMODEA ImplSalSetCopies( LPDEVMODEA pDevMode, ULONG nCopies, BOOL bCollate )
+{
+ LPDEVMODEA pNewDevMode = pDevMode;
+ if ( pDevMode && (nCopies > 1) )
+ {
+ if ( nCopies > 32765 )
+ nCopies = 32765;
+ ULONG nDevSize = pDevMode->dmSize+pDevMode->dmDriverExtra;
+ pNewDevMode = (LPDEVMODEA)rtl_allocateMemory( nDevSize );
+ memcpy( pNewDevMode, pDevMode, nDevSize );
+ pDevMode = pNewDevMode;
+ pDevMode->dmFields |= DM_COPIES;
+ pDevMode->dmCopies = (short)(USHORT)nCopies;
+ if ( aSalShlData.mbW40 )
+ {
+ pDevMode->dmFields |= DM_COLLATE;
+ if ( bCollate )
+ pDevMode->dmCollate = DMCOLLATE_TRUE;
+ else
+ pDevMode->dmCollate = DMCOLLATE_FALSE;
+ }
+ }
+
+ return pNewDevMode;
+}
+
+static LPDEVMODEW ImplSalSetCopies( LPDEVMODEW pDevMode, ULONG nCopies, BOOL bCollate )
+{
+ LPDEVMODEW pNewDevMode = pDevMode;
+ if ( pDevMode && (nCopies > 1) )
+ {
+ if ( nCopies > 32765 )
+ nCopies = 32765;
+ ULONG nDevSize = pDevMode->dmSize+pDevMode->dmDriverExtra;
+ pNewDevMode = (LPDEVMODEW)rtl_allocateMemory( nDevSize );
+ memcpy( pNewDevMode, pDevMode, nDevSize );
+ pDevMode = pNewDevMode;
+ pDevMode->dmFields |= DM_COPIES;
+ pDevMode->dmCopies = (short)(USHORT)nCopies;
+ if ( aSalShlData.mbW40 )
+ {
+ pDevMode->dmFields |= DM_COLLATE;
+ if ( bCollate )
+ pDevMode->dmCollate = DMCOLLATE_TRUE;
+ else
+ pDevMode->dmCollate = DMCOLLATE_FALSE;
+ }
+ }
+
+ return pNewDevMode;
+}
+
+// -----------------------------------------------------------------------
+
+WinSalPrinter::WinSalPrinter() :
+ mpGraphics( NULL ),
+ mpInfoPrinter( NULL ),
+ mpNextPrinter( NULL ),
+ mhDC( 0 ),
+ mnError( 0 ),
+ mnCopies( 0 ),
+ mbCollate( FALSE ),
+ mbAbort( FALSE ),
+ mbValid( true )
+{
+ SalData* pSalData = GetSalData();
+ // insert printer in printerlist
+ mpNextPrinter = pSalData->mpFirstPrinter;
+ pSalData->mpFirstPrinter = this;
+}
+
+// -----------------------------------------------------------------------
+
+WinSalPrinter::~WinSalPrinter()
+{
+ SalData* pSalData = GetSalData();
+
+ // release DC if there is one still around because of AbortJob
+ HDC hDC = mhDC;
+ if ( hDC )
+ {
+ if ( mpGraphics )
+ {
+ ImplSalDeInitGraphics( mpGraphics );
+ delete mpGraphics;
+ }
+
+ DeleteDC( hDC );
+ }
+
+ // remove printer from printerlist
+ if ( this == pSalData->mpFirstPrinter )
+ pSalData->mpFirstPrinter = mpNextPrinter;
+ else
+ {
+ WinSalPrinter* pTempPrinter = pSalData->mpFirstPrinter;
+
+ while( pTempPrinter->mpNextPrinter != this )
+ pTempPrinter = pTempPrinter->mpNextPrinter;
+
+ pTempPrinter->mpNextPrinter = mpNextPrinter;
+ }
+ mbValid = false;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalPrinter::markInvalid()
+{
+ mbValid = false;
+}
+
+// -----------------------------------------------------------------------
+
+// need wrappers for StarTocW/A to use structured exception handling
+// since SEH does not mix with standard exception handling's cleanup
+static int lcl_StartDocW( HDC hDC, DOCINFOW* pInfo, WinSalPrinter* pPrt )
+{
+ int nRet = 0;
+ CATCH_DRIVER_EX_BEGIN;
+ nRet = ::StartDocW( hDC, pInfo );
+ CATCH_DRIVER_EX_END( "exception in StartDocW", pPrt );
+ return nRet;
+}
+
+static int lcl_StartDocA( HDC hDC, DOCINFOA* pInfo, WinSalPrinter* pPrt )
+{
+ int nRet = 0;
+ CATCH_DRIVER_EX_BEGIN;
+ nRet = ::StartDocA( hDC, pInfo );
+ CATCH_DRIVER_EX_END( "exception in StartDocW", pPrt );
+ return nRet;
+}
+
+BOOL WinSalPrinter::StartJob( const XubString* pFileName,
+ const XubString& rJobName,
+ const XubString&,
+ ULONG nCopies,
+ bool bCollate,
+ bool /*bDirect*/,
+ ImplJobSetup* pSetupData )
+{
+ mnError = 0;
+ mbAbort = FALSE;
+ mnCopies = nCopies;
+ mbCollate = bCollate;
+
+ LPDEVMODEA pOrgDevModeA = NULL;
+ LPDEVMODEA pDevModeA = NULL;
+ LPDEVMODEW pOrgDevModeW = NULL;
+ LPDEVMODEW pDevModeW = NULL;
+ HDC hDC = 0;
+ if( aSalShlData.mbWPrinter )
+ {
+ if ( pSetupData && pSetupData->mpDriverData )
+ {
+ pOrgDevModeW = SAL_DEVMODE_W( pSetupData );
+ pDevModeW = ImplSalSetCopies( pOrgDevModeW, nCopies, bCollate );
+ }
+ else
+ pDevModeW = NULL;
+
+ // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateDC, although declared const - so provide some space
+ sal_Unicode aDrvBuf[4096];
+ sal_Unicode aDevBuf[4096];
+ rtl_copyMemory( aDrvBuf, mpInfoPrinter->maDriverName.GetBuffer(), (mpInfoPrinter->maDriverName.Len()+1)*sizeof(sal_Unicode));
+ rtl_copyMemory( aDevBuf, mpInfoPrinter->maDeviceName.GetBuffer(), (mpInfoPrinter->maDeviceName.Len()+1)*sizeof(sal_Unicode));
+ hDC = CreateDCW( reinterpret_cast<LPCWSTR>(aDrvBuf),
+ reinterpret_cast<LPCWSTR>(aDevBuf),
+ NULL,
+ pDevModeW );
+
+ if ( pDevModeW != pOrgDevModeW )
+ rtl_freeMemory( pDevModeW );
+ }
+ else
+ {
+ if ( pSetupData && pSetupData->mpDriverData )
+ {
+ pOrgDevModeA = SAL_DEVMODE_A( pSetupData );
+ pDevModeA = ImplSalSetCopies( pOrgDevModeA, nCopies, bCollate );
+ }
+ else
+ pDevModeA = NULL;
+
+ // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateDC, although declared const - so provide some space
+ ByteString aDriver ( ImplSalGetWinAnsiString( mpInfoPrinter->maDriverName, TRUE ) );
+ ByteString aDevice ( ImplSalGetWinAnsiString( mpInfoPrinter->maDeviceName, TRUE ) );
+ int n = aDriver.Len() > aDevice.Len() ? aDriver.Len() : aDevice.Len();
+ n += 2048;
+ char *lpszDriverName = new char[n];
+ char *lpszDeviceName = new char[n];
+ strncpy( lpszDriverName, aDriver.GetBuffer(), n );
+ strncpy( lpszDeviceName, aDevice.GetBuffer(), n );
+ hDC = CreateDCA( lpszDriverName,
+ lpszDeviceName,
+ NULL,
+ pDevModeA );
+
+ delete [] lpszDriverName;
+ delete [] lpszDeviceName;
+
+ if ( pDevModeA != pOrgDevModeA )
+ rtl_freeMemory( pDevModeA );
+ }
+
+ if ( !hDC )
+ {
+ mnError = SAL_PRINTER_ERROR_GENERALERROR;
+ return FALSE;
+ }
+
+ // make sure mhDC is set before the printer driver may call our abortproc
+ mhDC = hDC;
+ if ( SetAbortProc( hDC, SalPrintAbortProc ) <= 0 )
+ {
+ mnError = SAL_PRINTER_ERROR_GENERALERROR;
+ return FALSE;
+ }
+
+ mnError = 0;
+ mbAbort = FALSE;
+
+ // Wegen Telocom Balloon Fax-Treiber, der uns unsere Messages
+ // ansonsten oefters schickt, versuchen wir vorher alle
+ // zu verarbeiten und dann eine Dummy-Message reinstellen
+ BOOL bWhile = TRUE;
+ int i = 0;
+ do
+ {
+ // Messages verarbeiten
+ MSG aMsg;
+ if ( ImplPeekMessage( &aMsg, 0, 0, 0, PM_REMOVE ) )
+ {
+ TranslateMessage( &aMsg );
+ ImplDispatchMessage( &aMsg );
+ i++;
+ if ( i > 15 )
+ bWhile = FALSE;
+ }
+ else
+ bWhile = FALSE;
+ }
+ while ( bWhile );
+ ImplPostMessage( GetSalData()->mpFirstInstance->mhComWnd, SAL_MSG_DUMMY, 0, 0 );
+
+ // bring up a file choser if printing to file port but no file name given
+ OUString aOutFileName;
+ if( mpInfoPrinter->maPortName.EqualsIgnoreCaseAscii( "FILE:" ) && !(pFileName && pFileName->Len()) )
+ {
+
+ Reference< XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
+ if( xFactory.is() )
+ {
+ Reference< XFilePicker > xFilePicker( xFactory->createInstance(
+ OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.dialogs.FilePicker" ) ) ),
+ UNO_QUERY );
+ DBG_ASSERT( xFilePicker.is(), "could not get FilePicker service" );
+
+ Reference< XInitialization > xInit( xFilePicker, UNO_QUERY );
+ Reference< XFilterManager > xFilterMgr( xFilePicker, UNO_QUERY );
+ if( xInit.is() && xFilePicker.is() && xFilterMgr.is() )
+ {
+ Sequence< Any > aServiceType( 1 );
+ aServiceType[0] <<= TemplateDescription::FILESAVE_SIMPLE;
+ xInit->initialize( aServiceType );
+ if( xFilePicker->execute() == ExecutableDialogResults::OK )
+ {
+ Sequence< OUString > aPathSeq( xFilePicker->getFiles() );
+ INetURLObject aObj( aPathSeq[0] );
+ // we're using ansi calls (StartDocA) so convert the string
+ aOutFileName = aObj.PathToFileName();
+ }
+ else
+ {
+ mnError = SAL_PRINTER_ERROR_ABORT;
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ if( aSalShlData.mbWPrinter )
+ {
+ DOCINFOW aInfo;
+ memset( &aInfo, 0, sizeof( DOCINFOW ) );
+ aInfo.cbSize = sizeof( aInfo );
+ aInfo.lpszDocName = (LPWSTR)rJobName.GetBuffer();
+ if ( pFileName || aOutFileName.getLength() )
+ {
+ if ( (pFileName && pFileName->Len()) || aOutFileName.getLength() )
+ {
+ aInfo.lpszOutput = (LPWSTR)( (pFileName && pFileName->Len()) ? pFileName->GetBuffer() : aOutFileName.getStr());
+ }
+ else
+ aInfo.lpszOutput = L"FILE:";
+ }
+ else
+ aInfo.lpszOutput = NULL;
+
+ // start Job
+ int nRet = lcl_StartDocW( hDC, &aInfo, this );
+
+ if ( nRet <= 0 )
+ {
+ long nError = GetLastError();
+ if ( (nRet == SP_USERABORT) || (nRet == SP_APPABORT) || (nError == ERROR_PRINT_CANCELLED) || (nError == ERROR_CANCELLED) )
+ mnError = SAL_PRINTER_ERROR_ABORT;
+ else
+ mnError = SAL_PRINTER_ERROR_GENERALERROR;
+ return FALSE;
+ }
+ }
+ else
+ {
+ // Both strings must exist, if StartJob() is called
+ ByteString aJobName( ImplSalGetWinAnsiString( rJobName, TRUE ) );
+ ByteString aFileName;
+
+ DOCINFOA aInfo;
+ memset( &aInfo, 0, sizeof( DOCINFOA ) );
+ aInfo.cbSize = sizeof( aInfo );
+ aInfo.lpszDocName = (LPCSTR)aJobName.GetBuffer();
+ if ( pFileName || aOutFileName.getLength() )
+ {
+ if ( pFileName->Len() || aOutFileName.getLength() )
+ {
+ aFileName = ImplSalGetWinAnsiString( pFileName ? *pFileName : static_cast<const XubString>(aOutFileName), TRUE );
+ aInfo.lpszOutput = (LPCSTR)aFileName.GetBuffer();
+ }
+ else
+ aInfo.lpszOutput = "FILE:";
+ }
+ else
+ aInfo.lpszOutput = NULL;
+
+ // start Job
+ int nRet = lcl_StartDocA( hDC, &aInfo, this );
+ if ( nRet <= 0 )
+ {
+ long nError = GetLastError();
+ if ( (nRet == SP_USERABORT) || (nRet == SP_APPABORT) || (nError == ERROR_PRINT_CANCELLED) || (nError == ERROR_CANCELLED) )
+ mnError = SAL_PRINTER_ERROR_ABORT;
+ else
+ mnError = SAL_PRINTER_ERROR_GENERALERROR;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalPrinter::EndJob()
+{
+ DWORD err = 0;
+ HDC hDC = mhDC;
+ if ( isValid() && hDC )
+ {
+ if ( mpGraphics )
+ {
+ ImplSalDeInitGraphics( mpGraphics );
+ delete mpGraphics;
+ mpGraphics = NULL;
+ }
+
+ // #i54419# Windows fax printer brings up a dialog in EndDoc
+ // which text previously copied in soffice process can be
+ // pasted to -> deadlock due to mutex not released.
+ // it should be safe to release the yield mutex over the EndDoc
+ // call, however the real solution is supposed to be the threading
+ // framework yet to come.
+ SalData* pSalData = GetSalData();
+ ULONG nAcquire = pSalData->mpFirstInstance->ReleaseYieldMutex();
+ CATCH_DRIVER_EX_BEGIN;
+ if( ::EndDoc( hDC ) <= 0 )
+ err = GetLastError();
+ CATCH_DRIVER_EX_END( "exception in EndDoc", this );
+
+ pSalData->mpFirstInstance->AcquireYieldMutex( nAcquire );
+ DeleteDC( hDC );
+ mhDC = 0;
+ }
+
+ return TRUE;
+}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalPrinter::AbortJob()
+{
+ mbAbort = TRUE;
+
+ // Abort asyncron ausloesen
+ HDC hDC = mhDC;
+ if ( hDC )
+ {
+ SalData* pSalData = GetSalData();
+ ImplPostMessage( pSalData->mpFirstInstance->mhComWnd,
+ SAL_MSG_PRINTABORTJOB, (WPARAM)hDC, 0 );
+ }
+
+ return TRUE;
+}
+
+// -----------------------------------------------------------------------
+
+void ImplSalPrinterAbortJobAsync( HDC hPrnDC )
+{
+ SalData* pSalData = GetSalData();
+ WinSalPrinter* pPrinter = pSalData->mpFirstPrinter;
+
+ // Feststellen, ob Printer noch existiert
+ while ( pPrinter )
+ {
+ if ( pPrinter->mhDC == hPrnDC )
+ break;
+
+ pPrinter = pPrinter->mpNextPrinter;
+ }
+
+ // Wenn Printer noch existiert, dann den Job abbrechen
+ if ( pPrinter )
+ {
+ HDC hDC = pPrinter->mhDC;
+ if ( hDC )
+ {
+ if ( pPrinter->mpGraphics )
+ {
+ ImplSalDeInitGraphics( pPrinter->mpGraphics );
+ delete pPrinter->mpGraphics;
+ pPrinter->mpGraphics = NULL;
+ }
+
+ CATCH_DRIVER_EX_BEGIN;
+ ::AbortDoc( hDC );
+ CATCH_DRIVER_EX_END( "exception in AbortDoc", pPrinter );
+
+ DeleteDC( hDC );
+ pPrinter->mhDC = 0;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+SalGraphics* WinSalPrinter::StartPage( ImplJobSetup* pSetupData, BOOL bNewJobData )
+{
+ if( ! isValid() || mhDC == 0 )
+ return NULL;
+
+ HDC hDC = mhDC;
+ if ( pSetupData && pSetupData->mpDriverData && bNewJobData )
+ {
+ if( aSalShlData.mbWPrinter )
+ {
+ LPDEVMODEW pOrgDevModeW;
+ LPDEVMODEW pDevModeW;
+ pOrgDevModeW = SAL_DEVMODE_W( pSetupData );
+ pDevModeW = ImplSalSetCopies( pOrgDevModeW, mnCopies, mbCollate );
+ ResetDCW( hDC, pDevModeW );
+ if ( pDevModeW != pOrgDevModeW )
+ rtl_freeMemory( pDevModeW );
+ }
+ else
+ {
+ LPDEVMODEA pOrgDevModeA;
+ LPDEVMODEA pDevModeA;
+ pOrgDevModeA = SAL_DEVMODE_A( pSetupData );
+ pDevModeA = ImplSalSetCopies( pOrgDevModeA, mnCopies, mbCollate );
+ ResetDCA( hDC, pDevModeA );
+ if ( pDevModeA != pOrgDevModeA )
+ rtl_freeMemory( pDevModeA );
+ }
+ }
+ int nRet = 0;
+ CATCH_DRIVER_EX_BEGIN;
+ nRet = ::StartPage( hDC );
+ CATCH_DRIVER_EX_END( "exception in StartPage", this );
+
+ if ( nRet <= 0 )
+ {
+ GetLastError();
+ mnError = SAL_PRINTER_ERROR_GENERALERROR;
+ return NULL;
+ }
+
+ // Hack to work around old PostScript printer drivers optimizing away empty pages
+ // TODO: move into ImplCreateSalPrnGraphics()?
+ HPEN hTempPen = SelectPen( hDC, GetStockPen( NULL_PEN ) );
+ HBRUSH hTempBrush = SelectBrush( hDC, GetStockBrush( NULL_BRUSH ) );
+ WIN_Rectangle( hDC, -8000, -8000, -7999, -7999 );
+ SelectPen( hDC, hTempPen );
+ SelectBrush( hDC, hTempBrush );
+
+ mpGraphics = ImplCreateSalPrnGraphics( hDC );
+ return mpGraphics;
+}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalPrinter::EndPage()
+{
+ HDC hDC = mhDC;
+ if ( hDC && mpGraphics )
+ {
+ ImplSalDeInitGraphics( mpGraphics );
+ delete mpGraphics;
+ mpGraphics = NULL;
+ }
+
+ if( ! isValid() )
+ return FALSE;
+
+ int nRet = 0;
+ CATCH_DRIVER_EX_BEGIN;
+ nRet = ::EndPage( hDC );
+ CATCH_DRIVER_EX_END( "exception in EndPage", this );
+
+ if ( nRet > 0 )
+ return TRUE;
+ else
+ {
+ GetLastError();
+ mnError = SAL_PRINTER_ERROR_GENERALERROR;
+ return FALSE;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+ULONG WinSalPrinter::GetErrorCode()
+{
+ return mnError;
+}
diff --git a/vcl/win/source/gdi/salvd.cxx b/vcl/win/source/gdi/salvd.cxx
new file mode 100644
index 000000000000..816a8fef1141
--- /dev/null
+++ b/vcl/win/source/gdi/salvd.cxx
@@ -0,0 +1,256 @@
+/*************************************************************************
+ *
+ * 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/svwin.h>
+#include <wincomp.hxx>
+#include <saldata.hxx>
+#include <salinst.h>
+#include <salgdi.h>
+#include <salvd.h>
+#include <vcl/sysdata.hxx>
+
+// =======================================================================
+
+static HBITMAP ImplCreateVirDevBitmap( HDC hDC, long nDX, long nDY,
+ USHORT nBitCount )
+{
+ HBITMAP hBitmap;
+
+ if ( nBitCount == 1 )
+ {
+ hBitmap = CreateBitmap( (int)nDX, (int)nDY, 1, 1, NULL );
+ }
+ else
+ {
+ // #146839# Don't use CreateCompatibleBitmap() - there seem to
+ // be build-in limits for those HBITMAPs, at least this fails
+ // rather often on large displays/multi-monitor setups.
+ BITMAPINFO aBitmapInfo;
+ aBitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
+ aBitmapInfo.bmiHeader.biWidth = nDX;
+ aBitmapInfo.bmiHeader.biHeight = nDY;
+ aBitmapInfo.bmiHeader.biPlanes = 1;
+ aBitmapInfo.bmiHeader.biBitCount = (WORD)GetDeviceCaps( hDC,
+ BITSPIXEL );
+ aBitmapInfo.bmiHeader.biCompression = BI_RGB;
+ aBitmapInfo.bmiHeader.biSizeImage = 0;
+ aBitmapInfo.bmiHeader.biXPelsPerMeter = 0;
+ aBitmapInfo.bmiHeader.biYPelsPerMeter = 0;
+ aBitmapInfo.bmiHeader.biClrUsed = 0;
+ aBitmapInfo.bmiHeader.biClrImportant = 0;
+
+ void* pDummy;
+ hBitmap = CreateDIBSection( hDC, &aBitmapInfo,
+ DIB_RGB_COLORS, &pDummy, NULL,
+ 0 );
+ }
+
+ return hBitmap;
+}
+
+// =======================================================================
+
+SalVirtualDevice* WinSalInstance::CreateVirtualDevice( SalGraphics* pSGraphics,
+ long nDX, long nDY,
+ USHORT nBitCount,
+ const SystemGraphicsData* pData )
+{
+ WinSalGraphics* pGraphics = static_cast<WinSalGraphics*>(pSGraphics);
+
+ HDC hDC = NULL;
+ HBITMAP hBmp = NULL;
+ BOOL bOk = FALSE;
+
+ if( pData )
+ {
+ hDC = pData->hDC;
+ hBmp = NULL;
+ bOk = (hDC != NULL);
+ }
+ else
+ {
+ hDC = CreateCompatibleDC( pGraphics->mhDC );
+ if( !hDC )
+ ImplWriteLastError( GetLastError(), "CreateCompatibleDC in CreateVirtualDevice" );
+
+ hBmp = ImplCreateVirDevBitmap( pGraphics->mhDC,
+ nDX, nDY, nBitCount );
+ if( !hBmp )
+ ImplWriteLastError( GetLastError(), "ImplCreateVirDevBitmap in CreateVirtualDevice" );
+ // #124826# continue even if hBmp could not be created
+ // if we would return a failure in this case, the process
+ // would terminate which is not required
+
+ DBG_ASSERT( hBmp, "WinSalInstance::CreateVirtualDevice(), could not create Bitmap!" );
+
+ bOk = (hDC != NULL);
+ }
+
+ if ( bOk )
+ {
+ WinSalVirtualDevice* pVDev = new WinSalVirtualDevice;
+ SalData* pSalData = GetSalData();
+ WinSalGraphics* pVirGraphics = new WinSalGraphics;
+ pVirGraphics->SetLayout( 0 ); // by default no! mirroring for VirtualDevices, can be enabled with EnableRTL()
+ pVirGraphics->mhDC = hDC;
+ pVirGraphics->mhWnd = 0;
+ pVirGraphics->mbPrinter = FALSE;
+ pVirGraphics->mbVirDev = TRUE;
+ pVirGraphics->mbWindow = FALSE;
+ pVirGraphics->mbScreen = pGraphics->mbScreen;
+ if ( pSalData->mhDitherPal && pVirGraphics->mbScreen )
+ {
+ pVirGraphics->mhDefPal = SelectPalette( hDC, pSalData->mhDitherPal, TRUE );
+ RealizePalette( hDC );
+ }
+ ImplSalInitGraphics( pVirGraphics );
+
+ pVDev->mhDC = hDC;
+ pVDev->mhBmp = hBmp;
+ if( hBmp )
+ pVDev->mhDefBmp = SelectBitmap( hDC, hBmp );
+ else
+ pVDev->mhDefBmp = NULL;
+ pVDev->mpGraphics = pVirGraphics;
+ pVDev->mnBitCount = nBitCount;
+ pVDev->mbGraphics = FALSE;
+ pVDev->mbForeignDC = (pData != NULL);
+
+ // insert VirDev in VirDevList
+ pVDev->mpNext = pSalData->mpFirstVD;
+ pSalData->mpFirstVD = pVDev;
+
+ return pVDev;
+ }
+ else
+ {
+ if ( hDC && !pData )
+ DeleteDC( hDC );
+ if ( hBmp )
+ DeleteBitmap( hBmp );
+ return NULL;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalInstance::DestroyVirtualDevice( SalVirtualDevice* pDevice )
+{
+ delete pDevice;
+}
+
+// =======================================================================
+
+WinSalVirtualDevice::WinSalVirtualDevice()
+{
+ mhDC = (HDC) NULL; // HDC or 0 for Cache Device
+ mhBmp = (HBITMAP) NULL; // Memory Bitmap
+ mhDefBmp = (HBITMAP) NULL; // Default Bitmap
+ mpGraphics = NULL; // current VirDev graphics
+ mpNext = NULL; // next VirDev
+ mnBitCount = 0; // BitCount (0 or 1)
+ mbGraphics = FALSE; // is Graphics used
+ mbForeignDC = FALSE; // uses a foreign DC instead of a bitmap
+}
+
+// -----------------------------------------------------------------------
+
+WinSalVirtualDevice::~WinSalVirtualDevice()
+{
+ // remove VirDev from list of virtual devices
+ SalData* pSalData = GetSalData();
+ WinSalVirtualDevice** ppVirDev = &pSalData->mpFirstVD;
+ for(; (*ppVirDev != this) && *ppVirDev; ppVirDev = &(*ppVirDev)->mpNext );
+ if( *ppVirDev )
+ *ppVirDev = mpNext;
+
+ // destroy saved DC
+ if( mpGraphics->mhDefPal )
+ SelectPalette( mpGraphics->mhDC, mpGraphics->mhDefPal, TRUE );
+ ImplSalDeInitGraphics( mpGraphics );
+ if( mhDefBmp )
+ SelectBitmap( mpGraphics->mhDC, mhDefBmp );
+ if( !mbForeignDC )
+ DeleteDC( mpGraphics->mhDC );
+ if( mhBmp )
+ DeleteBitmap( mhBmp );
+ delete mpGraphics;
+ mpGraphics = NULL;
+}
+
+// -----------------------------------------------------------------------
+
+SalGraphics* WinSalVirtualDevice::GetGraphics()
+{
+ if ( mbGraphics )
+ return NULL;
+
+ if ( mpGraphics )
+ mbGraphics = TRUE;
+
+ return mpGraphics;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalVirtualDevice::ReleaseGraphics( SalGraphics* )
+{
+ mbGraphics = FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalVirtualDevice::SetSize( long nDX, long nDY )
+{
+ if( mbForeignDC || !mhBmp )
+ return TRUE; // ???
+ else
+ {
+ HBITMAP hNewBmp = ImplCreateVirDevBitmap( mhDC, nDX, nDY,
+ mnBitCount );
+ if ( hNewBmp )
+ {
+ SelectBitmap( mhDC, hNewBmp );
+ DeleteBitmap( mhBmp );
+ mhBmp = hNewBmp;
+ return TRUE;
+ }
+ else
+ {
+ ImplWriteLastError( GetLastError(), "ImplCreateVirDevBitmap in SetSize" );
+ return FALSE;
+ }
+ }
+}
+
+void WinSalVirtualDevice::GetSize( long& rWidth, long& rHeight )
+{
+ rWidth = GetDeviceCaps( mhDC, HORZRES );
+ rHeight= GetDeviceCaps( mhDC, VERTRES );
+}
diff --git a/vcl/win/source/gdi/winlayout.cxx b/vcl/win/source/gdi/winlayout.cxx
new file mode 100644
index 000000000000..82fa9bb4b5e1
--- /dev/null
+++ b/vcl/win/source/gdi/winlayout.cxx
@@ -0,0 +1,3225 @@
+/*************************************************************************
+ *
+ * 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/svwin.h"
+
+#include "salgdi.h"
+#include "saldata.hxx"
+// for GetMirroredChar
+#include "sft.hxx"
+
+#include "vcl/sallayout.hxx"
+#include "vcl/svapp.hxx"
+
+#include "rtl/ustring.hxx"
+
+#include "osl/module.h"
+#include "osl/file.h"
+
+
+#include <cstdio>
+#include <malloc.h>
+#ifndef __MINGW32__
+#define alloca _alloca
+#endif
+
+#ifdef GCP_KERN_HACK
+ #include <algorithm>
+#endif // GCP_KERN_HACK
+
+
+#define USE_UNISCRIBE
+#ifdef USE_UNISCRIBE
+#include <Usp10.h>
+#include <ShLwApi.h>
+#include <winver.h>
+#endif // USE_UNISCRIBE
+
+#include <hash_map>
+#include <set>
+
+typedef std::hash_map<int,int> IntMap;
+typedef std::set<int> IntSet;
+
+// Graphite headers
+#ifdef ENABLE_GRAPHITE
+#include <i18npool/mslangid.hxx>
+#include <graphite/GrClient.h>
+#include <graphite/WinFont.h>
+#include <graphite/Segment.h>
+#include <vcl/graphite_layout.hxx>
+#include <vcl/graphite_cache.hxx>
+#include <vcl/graphite_features.hxx>
+#endif
+
+#define DROPPED_OUTGLYPH 0xFFFF
+
+using namespace rtl;
+
+// =======================================================================
+
+// win32 specific physical font instance
+class ImplWinFontEntry : public ImplFontEntry
+{
+public:
+ ImplWinFontEntry( ImplFontSelectData& );
+ ~ImplWinFontEntry();
+
+private:
+ // TODO: also add HFONT??? Watch out for issues with too many active fonts...
+
+#ifdef GCP_KERN_HACK
+public:
+ bool HasKernData() const;
+ void SetKernData( int, const KERNINGPAIR* );
+ int GetKerning( sal_Unicode, sal_Unicode ) const;
+private:
+ KERNINGPAIR* mpKerningPairs;
+ int mnKerningPairs;
+#endif // GCP_KERN_HACK
+
+#ifdef USE_UNISCRIBE
+public:
+ SCRIPT_CACHE& GetScriptCache() const
+ { return maScriptCache; }
+private:
+ mutable SCRIPT_CACHE maScriptCache;
+#endif // USE_UNISCRIBE
+
+public:
+ int GetCachedGlyphWidth( int nCharCode ) const;
+ void CacheGlyphWidth( int nCharCode, int nCharWidth );
+
+ bool InitKashidaHandling( HDC );
+ int GetMinKashidaWidth() const { return mnMinKashidaWidth; }
+ int GetMinKashidaGlyph() const { return mnMinKashidaGlyph; }
+
+private:
+ IntMap maWidthMap;
+ mutable int mnMinKashidaWidth;
+ mutable int mnMinKashidaGlyph;
+};
+
+// -----------------------------------------------------------------------
+
+inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
+{
+ maWidthMap[ nCharCode ] = nCharWidth;
+}
+
+inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
+{
+ IntMap::const_iterator it = maWidthMap.find( nCharCode );
+ if( it == maWidthMap.end() )
+ return -1;
+ return it->second;
+}
+
+// =======================================================================
+
+class WinLayout : public SalLayout
+{
+public:
+ WinLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
+ virtual void InitFont() const;
+ void SetFontScale( float f ) { mfFontScale = f; }
+ float GetFontScale() const { return mfFontScale; }
+ HFONT DisableFontScaling( void) const;
+
+#ifdef USE_UNISCRIBE
+ SCRIPT_CACHE& GetScriptCache() const
+ { return mrWinFontEntry.GetScriptCache(); }
+#endif // USE_UNISCRIBE
+
+protected:
+ HDC mhDC; // WIN32 device handle
+ HFONT mhFont; // WIN32 font handle
+ int mnBaseAdv; // x-offset relative to Layout origin
+ float mfFontScale; // allows metrics emulation of huge font sizes
+
+ const ImplWinFontData& mrWinFontData;
+ ImplWinFontEntry& mrWinFontEntry;
+};
+
+// =======================================================================
+
+class SimpleWinLayout : public WinLayout
+{
+public:
+ SimpleWinLayout( HDC, BYTE nCharSet, const ImplWinFontData&, ImplWinFontEntry& );
+ virtual ~SimpleWinLayout();
+
+ virtual bool LayoutText( ImplLayoutArgs& );
+ virtual void AdjustLayout( ImplLayoutArgs& );
+ virtual void DrawText( SalGraphics& ) const;
+
+ virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
+ sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
+
+ virtual long FillDXArray( long* pDXArray ) const;
+ virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
+ virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
+
+ // for glyph+font+script fallback
+ virtual void MoveGlyph( int nStart, long nNewXPos );
+ virtual void DropGlyph( int nStart );
+ virtual void Simplify( bool bIsBase );
+
+protected:
+ void Justify( long nNewWidth );
+ void ApplyDXArray( const ImplLayoutArgs& );
+
+private:
+ int mnGlyphCount;
+ int mnCharCount;
+ WCHAR* mpOutGlyphs;
+ int* mpGlyphAdvances; // if possible this is shared with mpGlyphAdvances[]
+ int* mpGlyphOrigAdvs;
+ int* mpCharWidths; // map rel char pos to char width
+ int* mpChars2Glyphs; // map rel char pos to abs glyph pos
+ int* mpGlyphs2Chars; // map abs glyph pos to abs char pos
+ bool* mpGlyphRTLFlags; // BiDi status for glyphs: true=>RTL
+ mutable long mnWidth;
+ bool mbDisableGlyphs;
+
+ int mnNotdefWidth;
+ BYTE mnCharSet;
+};
+
+// =======================================================================
+
+WinLayout::WinLayout( HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE )
+: mhDC( hDC ),
+ mhFont( (HFONT)::GetCurrentObject(hDC,OBJ_FONT) ),
+ mnBaseAdv( 0 ),
+ mfFontScale( 1.0 ),
+ mrWinFontData( rWFD ),
+ mrWinFontEntry( rWFE )
+{}
+
+// -----------------------------------------------------------------------
+
+void WinLayout::InitFont() const
+{
+ ::SelectObject( mhDC, mhFont );
+}
+
+// -----------------------------------------------------------------------
+
+// Using reasonably sized fonts to emulate huge fonts works around
+// a lot of problems in printer and display drivers. Huge fonts are
+// mostly used by high resolution reference devices which are never
+// painted to anyway. In the rare case that a huge font needs to be
+// displayed somewhere then the workaround doesn't help anymore.
+// If the drivers fail silently for huge fonts, so be it...
+HFONT WinLayout::DisableFontScaling() const
+{
+ if( mfFontScale == 1.0 )
+ return 0;
+
+ HFONT hHugeFont = 0;
+ if( aSalShlData.mbWNT )
+ {
+ LOGFONTW aLogFont;
+ ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
+ aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
+ aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth);
+ hHugeFont = ::CreateFontIndirectW( &aLogFont);
+ }
+ else
+ {
+ LOGFONTA aLogFont;
+ ::GetObjectA( mhFont, sizeof(LOGFONTA), &aLogFont);
+ aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
+ aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth);
+ hHugeFont = ::CreateFontIndirectA( &aLogFont);
+ }
+
+ if( !hHugeFont )
+ return 0;
+
+ return SelectFont( mhDC, hHugeFont );
+}
+
+// =======================================================================
+
+SimpleWinLayout::SimpleWinLayout( HDC hDC, BYTE nCharSet,
+ const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
+: WinLayout( hDC, rWinFontData, rWinFontEntry ),
+ mnGlyphCount( 0 ),
+ mnCharCount( 0 ),
+ mpOutGlyphs( NULL ),
+ mpGlyphAdvances( NULL ),
+ mpGlyphOrigAdvs( NULL ),
+ mpCharWidths( NULL ),
+ mpChars2Glyphs( NULL ),
+ mpGlyphs2Chars( NULL ),
+ mpGlyphRTLFlags( NULL ),
+ mnWidth( 0 ),
+ mnNotdefWidth( -1 ),
+ mnCharSet( nCharSet ),
+ mbDisableGlyphs( false )
+{
+ mbDisableGlyphs = true;
+}
+
+// -----------------------------------------------------------------------
+
+SimpleWinLayout::~SimpleWinLayout()
+{
+ delete[] mpGlyphRTLFlags;
+ delete[] mpGlyphs2Chars;
+ delete[] mpChars2Glyphs;
+ if( mpCharWidths != mpGlyphAdvances )
+ delete[] mpCharWidths;
+ delete[] mpGlyphOrigAdvs;
+ delete[] mpGlyphAdvances;
+ delete[] mpOutGlyphs;
+}
+
+// -----------------------------------------------------------------------
+
+bool SimpleWinLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ // prepare layout
+ // TODO: fix case when recyclying old SimpleWinLayout object
+ mbDisableGlyphs |= ((rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) != 0);
+ mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
+
+ if( !mbDisableGlyphs )
+ {
+ // Win32 glyph APIs have serious problems with vertical layout
+ // => workaround is to use the unicode methods then
+ if( rArgs.mnFlags & SAL_LAYOUT_VERTICAL )
+ mbDisableGlyphs = true;
+ else
+ // use cached value from font face
+ mbDisableGlyphs = mrWinFontData.IsGlyphApiDisabled();
+ }
+
+ // TODO: use a cached value for bDisableAsianKern from upper layers
+ if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
+ {
+ TEXTMETRICA aTextMetricA;
+ if( ::GetTextMetricsA( mhDC, &aTextMetricA )
+ && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) && !(aTextMetricA.tmCharSet == 0x86) )
+ rArgs.mnFlags &= ~SAL_LAYOUT_KERNING_ASIAN;
+ }
+
+ // layout text
+ int i, j;
+
+ mnGlyphCount = 0;
+ bool bVertical = (rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0;
+
+ // count the number of chars to process if no RTL run
+ rArgs.ResetPos();
+ bool bHasRTL = false;
+ while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL )
+ mnGlyphCount += j - i;
+
+ // if there are RTL runs we need room to remember individual BiDi flags
+ if( bHasRTL )
+ {
+ mpGlyphRTLFlags = new bool[ mnCharCount ];
+ for( i = 0; i < mnCharCount; ++i )
+ mpGlyphRTLFlags[i] = false;
+ }
+
+ // rewrite the logical string if needed to prepare for the API calls
+ const sal_Unicode* pBidiStr = rArgs.mpStr + rArgs.mnMinCharPos;
+ if( (mnGlyphCount != mnCharCount) || bVertical )
+ {
+ // we need to rewrite the pBidiStr when any of
+ // - BiDirectional layout
+ // - vertical layout
+ // - partial runs (e.g. with control chars or for glyph fallback)
+ // are involved
+ sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) );
+ pBidiStr = pRewrittenStr;
+
+ // note: glyph to char mapping is relative to first character
+ mpChars2Glyphs = new int[ mnCharCount ];
+ mpGlyphs2Chars = new int[ mnCharCount ];
+ for( i = 0; i < mnCharCount; ++i )
+ mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1;
+
+ mnGlyphCount = 0;
+ rArgs.ResetPos();
+ bool bIsRTL = false;
+ while( rArgs.GetNextRun( &i, &j, &bIsRTL ) )
+ {
+ do
+ {
+ // get the next leftmost character in this run
+ int nCharPos = bIsRTL ? --j : i++;
+ sal_UCS4 cChar = rArgs.mpStr[ nCharPos ];
+
+ // in the RTL case mirror the character and remember its RTL status
+ if( bIsRTL )
+ {
+ cChar = ::GetMirroredChar( cChar );
+ mpGlyphRTLFlags[ mnGlyphCount ] = true;
+ }
+
+ // for vertical writing use vertical alternatives
+ if( bVertical )
+ {
+ sal_UCS4 cVert = ::GetVerticalChar( cChar );
+ if( cVert )
+ cChar = cVert;
+ }
+
+ // rewrite the original string
+ // update the mappings between original and rewritten string
+ // TODO: support surrogates in rewritten strings
+ pRewrittenStr[ mnGlyphCount ] = static_cast<sal_Unicode>(cChar);
+ mpGlyphs2Chars[ mnGlyphCount ] = nCharPos;
+ mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount;
+ ++mnGlyphCount;
+ } while( i < j );
+ }
+ }
+
+ mpOutGlyphs = new WCHAR[ mnGlyphCount ];
+ mpGlyphAdvances = new int[ mnGlyphCount ];
+
+ if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_PAIRS | SAL_LAYOUT_KERNING_ASIAN) )
+ mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+
+#ifndef GCP_KERN_HACK
+ DWORD nGcpOption = 0;
+ // enable kerning if requested
+ if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
+ nGcpOption |= GCP_USEKERNING;
+#endif // GCP_KERN_HACK
+
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpOutGlyphs[i] = pBidiStr[ i ];
+ mnWidth = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ // get the current UCS-4 code point, check for surrogate pairs
+ const WCHAR* pCodes = reinterpret_cast<LPCWSTR>(&pBidiStr[i]);
+ unsigned nCharCode = pCodes[0];
+ bool bSurrogate = ((nCharCode >= 0xD800) && (nCharCode <= 0xDFFF));
+ if( bSurrogate )
+ {
+ if( nCharCode >= 0xDC00 ) // this part of a surrogate pair was already processed
+ continue;
+ nCharCode = 0x10000 + ((pCodes[0] - 0xD800) << 10) + (pCodes[1] - 0xDC00);
+ }
+
+ // get the advance width for the current UCS-4 code point
+ int nGlyphWidth = mrWinFontEntry.GetCachedGlyphWidth( nCharCode );
+ if( nGlyphWidth == -1 )
+ {
+ ABC aABC;
+ SIZE aExtent;
+ if( ::GetTextExtentPoint32W( mhDC, &pCodes[0], bSurrogate ? 2 : 1, &aExtent) )
+ nGlyphWidth = aExtent.cx;
+ else if( ::GetCharABCWidthsW( mhDC, nCharCode, nCharCode, &aABC ) )
+ nGlyphWidth = aABC.abcA + aABC.abcB + aABC.abcC;
+ else if( !::GetCharWidth32W( mhDC, nCharCode, nCharCode, &nGlyphWidth )
+ && !::GetCharWidthW( mhDC, nCharCode, nCharCode, &nGlyphWidth ) )
+ nGlyphWidth = 0;
+ mrWinFontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth );
+ }
+ mpGlyphAdvances[ i ] = nGlyphWidth;
+ mnWidth += nGlyphWidth;
+
+ // remaining codes of surrogate pair get a zero width
+ if( bSurrogate && ((i+1) < mnGlyphCount) )
+ mpGlyphAdvances[ i+1 ] = 0;
+
+ // check with the font face if glyph fallback is needed
+ if( mrWinFontData.HasChar( nCharCode ) )
+ continue;
+
+ // request glyph fallback at this position in the string
+ bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false;
+ int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos;
+ rArgs.NeedFallback( nCharPos, bRTL );
+ if( bSurrogate && ((nCharPos+1) < rArgs.mnLength) )
+ rArgs.NeedFallback( nCharPos+1, bRTL );
+
+ // replace the current glyph shape with the NotDef glyph shape
+ if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
+ {
+ // when we already are layouting for glyph fallback
+ // then a new unresolved glyph is not interesting
+ mnNotdefWidth = 0;
+ mpOutGlyphs[i] = DROPPED_OUTGLYPH;
+ }
+ else
+ {
+ if( mnNotdefWidth < 0 )
+ {
+ // get the width of the NotDef glyph
+ SIZE aExtent;
+ WCHAR cNotDef = rArgs.mpStr[ nCharPos ];
+ mnNotdefWidth = 0;
+ if( ::GetTextExtentPoint32W( mhDC, &cNotDef, 1, &aExtent) )
+ mnNotdefWidth = aExtent.cx;
+ }
+ // use a better NotDef glyph
+ if( !mbDisableGlyphs && !bSurrogate )
+ mpOutGlyphs[i] = 0;
+ }
+ if( bSurrogate && ((i+1) < mnGlyphCount) )
+ mpOutGlyphs[i+1] = DROPPED_OUTGLYPH;
+
+ // adjust the current glyph width to the NotDef glyph width
+ mnWidth += mnNotdefWidth - mpGlyphAdvances[i];
+ mpGlyphAdvances[i] = mnNotdefWidth;
+ if( mpGlyphOrigAdvs )
+ mpGlyphOrigAdvs[i] = mnNotdefWidth;
+ }
+
+#ifdef GCP_KERN_HACK
+ // apply kerning if the layout engine has not yet done it
+ if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_ASIAN|SAL_LAYOUT_KERNING_PAIRS) )
+ {
+#else // GCP_KERN_HACK
+ // apply just asian kerning
+ if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
+ {
+ if( !(rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) )
+#endif // GCP_KERN_HACK
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphOrigAdvs[i] = mpGlyphAdvances[i];
+
+ // #99658# also apply asian kerning on the substring border
+ int nLen = mnGlyphCount;
+ if( rArgs.mnMinCharPos + nLen < rArgs.mnLength )
+ ++nLen;
+ for( i = 1; i < nLen; ++i )
+ {
+#ifdef GCP_KERN_HACK
+ if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
+ {
+ int nKernAmount = mrWinFontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] );
+ mpGlyphAdvances[ i-1 ] += nKernAmount;
+ mnWidth += nKernAmount;
+ }
+ else if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
+#endif // GCP_KERN_HACK
+
+ if( ( (0x3000 == (0xFF00 & pBidiStr[i-1])) || (0x2010 == (0xFFF0 & pBidiStr[i-1])) || (0xFF00 == (0xFF00 & pBidiStr[i-1])))
+ && ( (0x3000 == (0xFF00 & pBidiStr[i])) || (0x2010 == (0xFFF0 & pBidiStr[i])) || (0xFF00 == (0xFF00 & pBidiStr[i])) ) )
+ {
+ long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical );
+ long nKernNext = -CalcAsianKerning( pBidiStr[i], false, bVertical );
+
+ long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
+ if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
+ {
+ nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4;
+ mpGlyphAdvances[i-1] += nDelta;
+ mnWidth += nDelta;
+ }
+ }
+ }
+ }
+
+ // calculate virtual char widths
+ if( !mpGlyphs2Chars )
+ mpCharWidths = mpGlyphAdvances;
+ else
+ {
+ mpCharWidths = new int[ mnCharCount ];
+ for( i = 0; i < mnCharCount; ++i )
+ mpCharWidths[ i ] = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
+ if( j >= 0 )
+ mpCharWidths[ j ] += mpGlyphAdvances[ i ];
+ }
+ }
+
+ // scale layout metrics if needed
+ // TODO: does it make the code more simple if the metric scaling
+ // is moved to the methods that need metric scaling (e.g. FillDXArray())?
+ if( mfFontScale != 1.0 )
+ {
+ mnWidth = (long)(mnWidth * mfFontScale);
+ mnBaseAdv = (int)(mnBaseAdv * mfFontScale);
+ for( i = 0; i < mnCharCount; ++i )
+ mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
+ if( mpGlyphAdvances != mpCharWidths )
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
+ if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) )
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphOrigAdvs[i] = (int)(mpGlyphOrigAdvs[i] * mfFontScale);
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+int SimpleWinLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int& nStart,
+ long* pGlyphAdvances, int* pCharIndexes ) const
+{
+ // return zero if no more glyph found
+ if( nStart >= mnGlyphCount )
+ return 0;
+
+ // calculate glyph position relative to layout base
+ // TODO: avoid for nStart!=0 case by reusing rPos
+ long nXOffset = mnBaseAdv;
+ for( int i = 0; i < nStart; ++i )
+ nXOffset += mpGlyphAdvances[ i ];
+
+ // calculate absolute position in pixel units
+ Point aRelativePos( nXOffset, 0 );
+ rPos = GetDrawPosition( aRelativePos );
+
+ int nCount = 0;
+ while( nCount < nLen )
+ {
+ // update return values {nGlyphIndex,nCharPos,nGlyphAdvance}
+ sal_GlyphId nGlyphIndex = mpOutGlyphs[ nStart ];
+ if( mbDisableGlyphs )
+ {
+ if( mnLayoutFlags & SAL_LAYOUT_VERTICAL )
+ {
+ const sal_UCS4 cChar = static_cast<sal_UCS4>(nGlyphIndex & GF_IDXMASK);
+ if( mrWinFontData.HasGSUBstitutions( mhDC )
+ && mrWinFontData.IsGSUBstituted( cChar ) )
+ nGlyphIndex |= GF_GSUB | GF_ROTL;
+ else
+ {
+ nGlyphIndex |= GetVerticalFlags( cChar );
+ if( (nGlyphIndex & GF_ROTMASK) == 0 )
+ nGlyphIndex |= GF_VERT;
+ }
+ }
+ nGlyphIndex |= GF_ISCHAR;
+ }
+ ++nCount;
+ *(pGlyphs++) = nGlyphIndex;
+ if( pGlyphAdvances )
+ *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ];
+ if( pCharIndexes )
+ {
+ int nCharPos;
+ if( !mpGlyphs2Chars )
+ nCharPos = nStart + mnMinCharPos;
+ else
+ nCharPos = mpGlyphs2Chars[nStart];
+ *(pCharIndexes++) = nCharPos;
+ }
+
+ // stop at last glyph
+ if( ++nStart >= mnGlyphCount )
+ break;
+
+ // stop when next x-position is unexpected
+ if( !pGlyphAdvances && mpGlyphOrigAdvs )
+ if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
+ break;
+ }
+
+ return nCount;
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::DrawText( SalGraphics& rGraphics ) const
+{
+ if( mnGlyphCount <= 0 )
+ return;
+
+ WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
+ HDC aHDC = rWinGraphics.mhDC;
+
+ HFONT hOrigFont = DisableFontScaling();
+
+ UINT mnDrawOptions = ETO_GLYPH_INDEX;
+ if( mbDisableGlyphs )
+ mnDrawOptions = 0;
+
+ Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
+
+ // #108267#, limit the number of glyphs to avoid paint errors
+ UINT limitedGlyphCount = Min( 8192, mnGlyphCount );
+ if( mnDrawOptions || aSalShlData.mbWNT )
+ {
+ // #108267#, break up into glyph portions of a limited size required by Win32 API
+ const unsigned int maxGlyphCount = 8192;
+ UINT numGlyphPortions = mnGlyphCount / maxGlyphCount;
+ UINT remainingGlyphs = mnGlyphCount % maxGlyphCount;
+
+ if( numGlyphPortions )
+ {
+ // #108267#,#109387# break up string into smaller chunks
+ // the output positions will be updated by windows (SetTextAlign)
+ unsigned int i,n;
+ POINT oldPos;
+ UINT oldTa = ::GetTextAlign( aHDC );
+ ::SetTextAlign( aHDC, (oldTa & ~TA_NOUPDATECP) | TA_UPDATECP );
+ ::MoveToEx( aHDC, aPos.X(), aPos.Y(), &oldPos );
+ for( i=n=0; n<numGlyphPortions; n++, i+=maxGlyphCount )
+ ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
+ mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i );
+ ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
+ mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i );
+ ::MoveToEx( aHDC, oldPos.x, oldPos.y, (LPPOINT) NULL);
+ ::SetTextAlign( aHDC, oldTa );
+ }
+ else
+ ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), mnDrawOptions, NULL,
+ mpOutGlyphs, mnGlyphCount, mpGlyphAdvances );
+ }
+ else
+ {
+ // #108267#, On Win9x, we get paint errors when drawing huge strings, even when
+ // split into pieces (see above), seems to be a problem in the internal text clipping
+ // so we just cut off the string
+ if( !mpGlyphOrigAdvs )
+ ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), 0, NULL,
+ mpOutGlyphs, limitedGlyphCount, NULL );
+ else
+ {
+ // workaround for problem in #106259#
+ long nXPos = mnBaseAdv;
+ for( unsigned int i = 0; i < limitedGlyphCount; ++i )
+ {
+ ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), 0, NULL,
+ mpOutGlyphs+i, 1, NULL );
+ nXPos += mpGlyphAdvances[ i ];
+ aPos = GetDrawPosition( Point( nXPos, 0 ) );
+ }
+ }
+ }
+
+ if( hOrigFont )
+ DeleteFont( SelectFont( aHDC, hOrigFont ) );
+}
+
+// -----------------------------------------------------------------------
+
+long SimpleWinLayout::FillDXArray( long* pDXArray ) const
+{
+ if( !mnWidth )
+ {
+ long mnWidth = mnBaseAdv;
+ for( int i = 0; i < mnGlyphCount; ++i )
+ mnWidth += mpGlyphAdvances[ i ];
+ }
+
+ if( pDXArray != NULL )
+ {
+ for( int i = 0; i < mnCharCount; ++i )
+ pDXArray[ i ] = mpCharWidths[ i ];
+ }
+
+ return mnWidth;
+}
+
+// -----------------------------------------------------------------------
+
+int SimpleWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
+// NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values
+{
+ if( mnWidth )
+ if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth )
+ return STRING_LEN;
+
+ long nExtraWidth = mnBaseAdv * nFactor;
+ for( int n = 0; n < mnCharCount; ++n )
+ {
+ // skip unused characters
+ if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) )
+ continue;
+ // add char widths until max
+ nExtraWidth += mpCharWidths[ n ] * nFactor;
+ if( nExtraWidth >= nMaxWidth )
+ return (mnMinCharPos + n);
+ nExtraWidth += nCharExtra;
+ }
+
+ return STRING_LEN;
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
+{
+ long nXPos = mnBaseAdv;
+
+ if( !mpGlyphs2Chars )
+ {
+ for( int i = 0; i < nMaxIdx; i += 2 )
+ {
+ pCaretXArray[ i ] = nXPos;
+ nXPos += mpGlyphAdvances[ i>>1 ];
+ pCaretXArray[ i+1 ] = nXPos;
+ }
+ }
+ else
+ {
+ int i;
+ for( i = 0; i < nMaxIdx; ++i )
+ pCaretXArray[ i ] = -1;
+
+ // assign glyph positions to character positions
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos;
+ long nXRight = nXPos + mpCharWidths[ nCurrIdx ];
+ nCurrIdx *= 2;
+ if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) )
+ {
+ // 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;
+ }
+ nXPos += mpGlyphAdvances[ i ];
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::Justify( long nNewWidth )
+{
+ long nOldWidth = mnWidth;
+ mnWidth = nNewWidth;
+
+ if( mnGlyphCount <= 0 )
+ return;
+
+ if( nNewWidth == nOldWidth )
+ return;
+
+ // the rightmost glyph cannot be stretched
+ const int nRight = mnGlyphCount - 1;
+ nOldWidth -= mpGlyphAdvances[ nRight ];
+ nNewWidth -= mpGlyphAdvances[ nRight ];
+
+ // count stretchable glyphs
+ int nStretchable = 0, i;
+ for( i = 0; i < nRight; ++i )
+ if( mpGlyphAdvances[i] >= 0 )
+ ++nStretchable;
+
+ // stretch these glyphs
+ int nDiffWidth = nNewWidth - nOldWidth;
+ for( i = 0; (i < nRight) && (nStretchable > 0); ++i )
+ {
+ if( mpGlyphAdvances[i] <= 0 )
+ continue;
+ int nDeltaWidth = nDiffWidth / nStretchable;
+ mpGlyphAdvances[i] += nDeltaWidth;
+ --nStretchable;
+ nDiffWidth -= nDeltaWidth;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ SalLayout::AdjustLayout( rArgs );
+
+ // adjust positions if requested
+ if( rArgs.mpDXArray )
+ ApplyDXArray( rArgs );
+ else if( rArgs.mnLayoutWidth )
+ Justify( rArgs.mnLayoutWidth );
+ else
+ return;
+
+ // recalculate virtual char widths if they were changed
+ if( mpCharWidths != mpGlyphAdvances )
+ {
+ int i;
+ if( !mpGlyphs2Chars )
+ {
+ // standard LTR case
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpCharWidths[ i ] = mpGlyphAdvances[ i ];
+ }
+ else
+ {
+ // BiDi or complex case
+ for( i = 0; i < mnCharCount; ++i )
+ mpCharWidths[ i ] = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
+ if( j >= 0 )
+ mpCharWidths[ j ] += mpGlyphAdvances[ i ];
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
+{
+ // try to avoid disturbance of text flow for LSB rounding case;
+ const long* pDXArray = rArgs.mpDXArray;
+
+ int i = 0;
+ long nOldWidth = mnBaseAdv;
+ for(; i < mnCharCount; ++i )
+ {
+ int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
+ if( j >= 0 )
+ {
+ nOldWidth += mpGlyphAdvances[ j ];
+ int nDiff = nOldWidth - pDXArray[ i ];
+
+ // disabled because of #104768#
+ // works great for static text, but problems when typing
+ // if( nDiff>+1 || nDiff<-1 )
+ // only bother with changing anything when something moved
+ if( nDiff != 0 )
+ break;
+ }
+ }
+ if( i >= mnCharCount )
+ return;
+
+ if( !mpGlyphOrigAdvs )
+ {
+ mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ];
+ }
+
+ mnWidth = mnBaseAdv;
+ for( i = 0; i < mnCharCount; ++i )
+ {
+ int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
+ if( j >= 0 )
+ mpGlyphAdvances[j] = pDXArray[i] - mnWidth;
+ mnWidth = pDXArray[i];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::MoveGlyph( int nStart, long nNewXPos )
+{
+ if( nStart > mnGlyphCount )
+ return;
+
+ // calculate the current x-position of the requested glyph
+ // TODO: cache absolute positions
+ int nXPos = mnBaseAdv;
+ for( int i = 0; i < nStart; ++i )
+ nXPos += mpGlyphAdvances[i];
+
+ // calculate the difference to the current glyph position
+ int nDelta = nNewXPos - nXPos;
+
+ // adjust the width of the layout if it was already cached
+ if( mnWidth )
+ mnWidth += nDelta;
+
+ // depending on whether the requested glyph is leftmost in the layout
+ // adjust either the layout's or the requested glyph's relative position
+ if( nStart > 0 )
+ mpGlyphAdvances[ nStart-1 ] += nDelta;
+ else
+ mnBaseAdv += nDelta;
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::DropGlyph( int nStart )
+{
+ mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::Simplify( bool /*bIsBase*/ )
+{
+ // return early if no glyph has been dropped
+ int i = mnGlyphCount;
+ while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) );
+ if( i < 0 )
+ return;
+
+ // convert the layout to a sparse layout if it is not already
+ if( !mpGlyphs2Chars )
+ {
+ mpGlyphs2Chars = new int[ mnGlyphCount ];
+ mpCharWidths = new int[ mnCharCount ];
+ // assertion: mnGlyphCount == mnCharCount
+ for( int k = 0; k < mnGlyphCount; ++k )
+ {
+ mpGlyphs2Chars[ k ] = mnMinCharPos + k;
+ mpCharWidths[ k ] = mpGlyphAdvances[ k ];
+ }
+ }
+
+ // remove dropped glyphs that are rightmost in the layout
+ for( i = mnGlyphCount; --i >= 0; )
+ {
+ if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH )
+ break;
+ if( mnWidth )
+ mnWidth -= mpGlyphAdvances[ i ];
+ int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
+ if( nRelCharPos >= 0 )
+ mpCharWidths[ nRelCharPos ] = 0;
+ }
+ mnGlyphCount = i + 1;
+
+ // keep original glyph widths around
+ if( !mpGlyphOrigAdvs )
+ {
+ mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+ for( int k = 0; k < mnGlyphCount; ++k )
+ mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ];
+ }
+
+ // remove dropped glyphs inside the layout
+ int nNewGC = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH )
+ {
+ // adjust relative position to last valid glyph
+ int nDroppedWidth = mpGlyphAdvances[ i ];
+ mpGlyphAdvances[ i ] = 0;
+ if( nNewGC > 0 )
+ mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth;
+ else
+ mnBaseAdv += nDroppedWidth;
+
+ // zero the virtual char width for the char that has a fallback
+ int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
+ if( nRelCharPos >= 0 )
+ mpCharWidths[ nRelCharPos ] = 0;
+ }
+ else
+ {
+ if( nNewGC != i )
+ {
+ // rearrange the glyph array to get rid of the dropped glyph
+ mpOutGlyphs[ nNewGC ] = mpOutGlyphs[ i ];
+ mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ];
+ mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ];
+ mpGlyphs2Chars[ nNewGC ] = mpGlyphs2Chars[ i ];
+ }
+ ++nNewGC;
+ }
+ }
+
+ mnGlyphCount = nNewGC;
+ if( mnGlyphCount <= 0 )
+ mnWidth = mnBaseAdv = 0;
+}
+
+// =======================================================================
+
+#ifdef USE_UNISCRIBE
+
+struct VisualItem
+{
+public:
+ SCRIPT_ITEM* mpScriptItem;
+ int mnMinGlyphPos;
+ int mnEndGlyphPos;
+ int mnMinCharPos;
+ int mnEndCharPos;
+ //long mnPixelWidth;
+ int mnXOffset;
+ ABC maABCWidths;
+ bool mbHasKashidas;
+
+public:
+ bool IsEmpty() const { return (mnEndGlyphPos <= 0); }
+ bool IsRTL() const { return mpScriptItem->a.fRTL; }
+ bool HasKashidas() const { return mbHasKashidas; }
+};
+
+// -----------------------------------------------------------------------
+
+class UniscribeLayout : public WinLayout
+{
+public:
+ UniscribeLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
+
+ virtual bool LayoutText( ImplLayoutArgs& );
+ virtual void AdjustLayout( ImplLayoutArgs& );
+ virtual void DrawText( SalGraphics& ) const;
+ virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
+ sal_Int32* pGlyphAdvances, int* pCharPosAry ) const;
+
+ virtual long FillDXArray( long* pDXArray ) const;
+ virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
+ virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
+ virtual bool IsKashidaPosValid ( int nCharPos ) const;
+
+ // for glyph+font+script fallback
+ virtual void MoveGlyph( int nStart, long nNewXPos );
+ virtual void DropGlyph( int nStart );
+ virtual void Simplify( bool bIsBase );
+ virtual void DisableGlyphInjection( bool bDisable ) { mbDisableGlyphInjection = bDisable; }
+
+protected:
+ virtual ~UniscribeLayout();
+
+ void Justify( long nNewWidth );
+ void ApplyDXArray( const ImplLayoutArgs& );
+
+ bool GetItemSubrange( const VisualItem&,
+ int& rMinIndex, int& rEndIndex ) const;
+
+private:
+ // item specific info
+ SCRIPT_ITEM* mpScriptItems; // in logical order
+ VisualItem* mpVisualItems; // in visual order
+ int mnItemCount; // number of visual items
+
+ // string specific info
+ // everything is in logical order
+ int mnCharCapacity;
+ WORD* mpLogClusters; // map from absolute_char_pos to relative_glyph_pos
+ int* mpCharWidths; // map from absolute_char_pos to char_width
+ int mnSubStringMin; // char_pos of first char in context
+
+ // glyph specific info
+ // everything is in visual order
+ int mnGlyphCount;
+ int mnGlyphCapacity;
+ int* mpGlyphAdvances; // glyph advance width before justification
+ int* mpJustifications; // glyph advance width after justification
+ WORD* mpOutGlyphs; // glyphids in visual order
+ GOFFSET* mpGlyphOffsets; // glyph offsets to the "naive" layout
+ SCRIPT_VISATTR* mpVisualAttrs; // glyph visual attributes
+ mutable int* mpGlyphs2Chars; // map from absolute_glyph_pos to absolute_char_pos
+
+ // kashida stuff
+ void InitKashidaHandling();
+ void KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos );
+ bool KashidaWordFix( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos );
+
+ int mnMinKashidaWidth;
+ int mnMinKashidaGlyph;
+ bool mbDisableGlyphInjection;
+};
+
+// -----------------------------------------------------------------------
+// dynamic loading of usp library
+
+static oslModule aUspModule = NULL;
+static bool bUspEnabled = true;
+
+static HRESULT ((WINAPI *pScriptIsComplex)( const WCHAR*, int, DWORD ));
+static HRESULT ((WINAPI *pScriptItemize)( const WCHAR*, int, int,
+ const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*, int* ));
+static HRESULT ((WINAPI *pScriptShape)( HDC, SCRIPT_CACHE*, const WCHAR*,
+ int, int, SCRIPT_ANALYSIS*, WORD*, WORD*, SCRIPT_VISATTR*, int* ));
+static HRESULT ((WINAPI *pScriptPlace)( HDC, SCRIPT_CACHE*, const WORD*, int,
+ const SCRIPT_VISATTR*, SCRIPT_ANALYSIS*, int*, GOFFSET*, ABC* ));
+static HRESULT ((WINAPI *pScriptGetLogicalWidths)( const SCRIPT_ANALYSIS*,
+ int, int, const int*, const WORD*, const SCRIPT_VISATTR*, int* ));
+static HRESULT ((WINAPI *pScriptApplyLogicalWidth)( const int*, int, int, const WORD*,
+ const SCRIPT_VISATTR*, const int*, const SCRIPT_ANALYSIS*, ABC*, int* ));
+static HRESULT ((WINAPI *pScriptJustify)( const SCRIPT_VISATTR*,
+ const int*, int, int, int, int* ));
+static HRESULT ((WINAPI *pScriptTextOut)( const HDC, SCRIPT_CACHE*,
+ int, int, UINT, const RECT*, const SCRIPT_ANALYSIS*, const WCHAR*,
+ int, const WORD*, int, const int*, const int*, const GOFFSET* ));
+static HRESULT ((WINAPI *pScriptGetFontProperties)( HDC, SCRIPT_CACHE*, SCRIPT_FONTPROPERTIES* ));
+static HRESULT ((WINAPI *pScriptFreeCache)( SCRIPT_CACHE* ));
+
+static bool bManualCellAlign = true;
+
+// -----------------------------------------------------------------------
+
+static bool InitUSP()
+{
+ OUString aLibraryName( RTL_CONSTASCII_USTRINGPARAM( "usp10" ) );
+ aUspModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT );
+ if( !aUspModule )
+ return (bUspEnabled = false);
+
+ pScriptIsComplex = (HRESULT (WINAPI*)(const WCHAR*,int,DWORD))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptIsComplex" );
+ bUspEnabled &= (NULL != pScriptIsComplex);
+
+ pScriptItemize = (HRESULT (WINAPI*)(const WCHAR*,int,int,
+ const SCRIPT_CONTROL*,const SCRIPT_STATE*,SCRIPT_ITEM*,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptItemize" );
+ bUspEnabled &= (NULL != pScriptItemize);
+
+ pScriptShape = (HRESULT (WINAPI*)(HDC,SCRIPT_CACHE*,const WCHAR*,
+ int,int,SCRIPT_ANALYSIS*,WORD*,WORD*,SCRIPT_VISATTR*,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptShape" );
+ bUspEnabled &= (NULL != pScriptShape);
+
+ pScriptPlace = (HRESULT (WINAPI*)(HDC, SCRIPT_CACHE*, const WORD*, int,
+ const SCRIPT_VISATTR*,SCRIPT_ANALYSIS*,int*,GOFFSET*,ABC*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptPlace" );
+ bUspEnabled &= (NULL != pScriptPlace);
+
+ pScriptGetLogicalWidths = (HRESULT (WINAPI*)(const SCRIPT_ANALYSIS*,
+ int,int,const int*,const WORD*,const SCRIPT_VISATTR*,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetLogicalWidths" );
+ bUspEnabled &= (NULL != pScriptGetLogicalWidths);
+
+ pScriptApplyLogicalWidth = (HRESULT (WINAPI*)(const int*,int,int,const WORD*,
+ const SCRIPT_VISATTR*,const int*,const SCRIPT_ANALYSIS*,ABC*,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptApplyLogicalWidth" );
+ bUspEnabled &= (NULL != pScriptApplyLogicalWidth);
+
+ pScriptJustify = (HRESULT (WINAPI*)(const SCRIPT_VISATTR*,const int*,
+ int,int,int,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptJustify" );
+ bUspEnabled &= (NULL != pScriptJustify);
+
+ pScriptGetFontProperties = (HRESULT (WINAPI*)( HDC,SCRIPT_CACHE*,SCRIPT_FONTPROPERTIES*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetFontProperties" );
+ bUspEnabled &= (NULL != pScriptGetFontProperties);
+
+ pScriptTextOut = (HRESULT (WINAPI*)(const HDC,SCRIPT_CACHE*,
+ int,int,UINT,const RECT*,const SCRIPT_ANALYSIS*,const WCHAR*,
+ int,const WORD*,int,const int*,const int*,const GOFFSET*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptTextOut" );
+ bUspEnabled &= (NULL != pScriptTextOut);
+
+ pScriptFreeCache = (HRESULT (WINAPI*)(SCRIPT_CACHE*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptFreeCache" );
+ bUspEnabled &= (NULL != pScriptFreeCache);
+
+ if( !bUspEnabled )
+ {
+ osl_unloadModule( aUspModule );
+ aUspModule = NULL;
+ }
+
+ // get the DLL version info
+ int nUspVersion = 0;
+ // TODO: there must be a simpler way to get the friggin version info from OSL?
+ rtl_uString* pModuleURL = NULL;
+ osl_getModuleURLFromAddress( (void*)pScriptIsComplex, &pModuleURL );
+ rtl_uString* pModuleFileName = NULL;
+ if( pModuleURL )
+ osl_getSystemPathFromFileURL( pModuleURL, &pModuleFileName );
+ const sal_Unicode* pModuleFileCStr = NULL;
+ if( pModuleFileName )
+ pModuleFileCStr = rtl_uString_getStr( pModuleFileName );
+ if( pModuleFileCStr )
+ {
+ DWORD nHandle;
+ DWORD nBufSize = ::GetFileVersionInfoSizeW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), &nHandle );
+ char* pBuffer = (char*)alloca( nBufSize );
+ WIN_BOOL bRC = ::GetFileVersionInfoW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), nHandle, nBufSize, pBuffer );
+ VS_FIXEDFILEINFO* pFixedFileInfo = NULL;
+ UINT nFixedFileSize = 0;
+ if( bRC )
+ ::VerQueryValueW( pBuffer, const_cast<LPWSTR>(L"\\"), (void**)&pFixedFileInfo, &nFixedFileSize );
+ if( pFixedFileInfo && pFixedFileInfo->dwSignature == 0xFEEF04BD )
+ nUspVersion = HIWORD(pFixedFileInfo->dwProductVersionMS) * 10000
+ + LOWORD(pFixedFileInfo->dwProductVersionMS);
+ }
+
+ // #i77976# USP>=1.0600 changed the need to manually align glyphs in their cells
+ if( nUspVersion >= 10600 )
+ bManualCellAlign = false;
+
+ return bUspEnabled;
+}
+
+// -----------------------------------------------------------------------
+
+UniscribeLayout::UniscribeLayout( HDC hDC,
+ const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
+: WinLayout( hDC, rWinFontData, rWinFontEntry ),
+ mnItemCount( 0 ),
+ mpScriptItems( NULL ),
+ mpVisualItems( NULL ),
+ mpLogClusters( NULL ),
+ mpCharWidths( NULL ),
+ mnCharCapacity( 0 ),
+ mnSubStringMin( 0 ),
+ mnGlyphCapacity( 0 ),
+ mnGlyphCount( 0 ),
+ mpOutGlyphs( NULL ),
+ mpGlyphAdvances( NULL ),
+ mpJustifications( NULL ),
+ mpGlyphOffsets( NULL ),
+ mpVisualAttrs( NULL ),
+ mpGlyphs2Chars( NULL ),
+ mnMinKashidaGlyph( 0 ),
+ mbDisableGlyphInjection( false )
+{}
+
+// -----------------------------------------------------------------------
+
+UniscribeLayout::~UniscribeLayout()
+{
+ delete[] mpScriptItems;
+ delete[] mpVisualItems;
+ delete[] mpLogClusters;
+ delete[] mpCharWidths;
+ delete[] mpOutGlyphs;
+ delete[] mpGlyphAdvances;
+ delete[] mpJustifications;
+ delete[] mpGlyphOffsets;
+ delete[] mpVisualAttrs;
+ delete[] mpGlyphs2Chars;
+}
+
+// -----------------------------------------------------------------------
+
+bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ // for a base layout only the context glyphs have to be dropped
+ // => when the whole string is involved there is no extra context
+ typedef std::vector<int> TIntVector;
+ TIntVector aDropChars;
+ if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
+ {
+ // calculate superfluous context char positions
+ aDropChars.push_back( 0 );
+ aDropChars.push_back( rArgs.mnLength );
+ int nMin, nEnd;
+ bool bRTL;
+ for( rArgs.ResetPos(); rArgs.GetNextRun( &nMin, &nEnd, &bRTL ); )
+ {
+ aDropChars.push_back( nMin );
+ aDropChars.push_back( nEnd );
+ }
+ // prepare aDropChars for binary search which will allow to
+ // not bother with visual items that will be dropped anyway
+ std::sort( aDropChars.begin(), aDropChars.end() );
+ }
+
+ // prepare layout
+ // TODO: fix case when recyclying old UniscribeLayout object
+ mnMinCharPos = rArgs.mnMinCharPos;
+ mnEndCharPos = rArgs.mnEndCharPos;
+
+ // determine script items from string
+
+ // prepare itemization
+ // TODO: try to avoid itemization since it costs a lot of performance
+ SCRIPT_STATE aScriptState = {0,false,false,false,false,false,false,false,false,0,0};
+ aScriptState.uBidiLevel = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL));
+ aScriptState.fOverrideDirection = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG));
+ aScriptState.fDigitSubstitute = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
+ aScriptState.fArabicNumContext = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel;
+ DWORD nLangId = 0; // TODO: get language from font
+ SCRIPT_CONTROL aScriptControl = {nLangId,false,false,false,false,false,false,false,false,0};
+ aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection;
+ aScriptControl.fContextDigits = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
+ // determine relevant substring and work only on it
+ // when Bidi status is unknown we need to look at the whole string though
+ mnSubStringMin = 0;
+ int nSubStringEnd = rArgs.mnLength;
+ if( aScriptState.fOverrideDirection )
+ {
+ // TODO: limit substring to portion limits
+ mnSubStringMin = rArgs.mnMinCharPos - 8;
+ if( mnSubStringMin < 0 )
+ mnSubStringMin = 0;
+ nSubStringEnd = rArgs.mnEndCharPos + 8;
+ if( nSubStringEnd > rArgs.mnLength )
+ nSubStringEnd = rArgs.mnLength;
+
+ }
+
+ // now itemize the substring with its context
+ for( int nItemCapacity = 16;; nItemCapacity *= 8 )
+ {
+ mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ];
+ HRESULT nRC = (*pScriptItemize)(
+ reinterpret_cast<LPCWSTR>(rArgs.mpStr + mnSubStringMin), nSubStringEnd - mnSubStringMin,
+ nItemCapacity - 1, &aScriptControl, &aScriptState,
+ mpScriptItems, &mnItemCount );
+ if( !nRC ) // break loop when everything is correctly itemized
+ break;
+
+ // prepare bigger buffers for another itemization round
+ delete[] mpScriptItems;
+ mpScriptItems = NULL;
+ if( nRC != E_OUTOFMEMORY )
+ return false;
+ if( nItemCapacity > (nSubStringEnd - mnSubStringMin) + 16 )
+ return false;
+ }
+
+ // calculate the order of visual items
+ int nItem, i;
+
+ // adjust char positions by substring offset
+ for( nItem = 0; nItem <= mnItemCount; ++nItem )
+ mpScriptItems[ nItem ].iCharPos += mnSubStringMin;
+ // default visual item ordering
+ mpVisualItems = new VisualItem[ mnItemCount ];
+ for( nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ // initialize char specific item info
+ VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ SCRIPT_ITEM* pScriptItem = &mpScriptItems[ nItem ];
+ rVisualItem.mpScriptItem = pScriptItem;
+ rVisualItem.mnMinCharPos = pScriptItem[0].iCharPos;
+ rVisualItem.mnEndCharPos = pScriptItem[1].iCharPos;
+ }
+
+ // reorder visual item order if needed
+ if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
+ {
+ // force RTL item ordering if requested
+ if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL )
+ {
+ VisualItem* pVI0 = &mpVisualItems[ 0 ];
+ VisualItem* pVI1 = &mpVisualItems[ mnItemCount ];
+ while( pVI0 < --pVI1 )
+ {
+ VisualItem aVtmp = *pVI0;
+ *(pVI0++) = *pVI1;
+ *pVI1 = aVtmp;
+ }
+ }
+ }
+ else if( mnItemCount > 1 )
+ {
+ // apply bidi algorithm's rule L2 on item level
+ // TODO: use faster L2 algorithm
+ int nMaxBidiLevel = 0;
+ VisualItem* pVI = &mpVisualItems[0];
+ VisualItem* const pVIend = pVI + mnItemCount;
+ for(; pVI < pVIend; ++pVI )
+ if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
+ nMaxBidiLevel = pVI->mpScriptItem->a.s.uBidiLevel;
+
+ while( --nMaxBidiLevel >= 0 )
+ {
+ for( pVI = &mpVisualItems[0]; pVI < pVIend; )
+ {
+ // find item range that needs reordering
+ for(; pVI < pVIend; ++pVI )
+ if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
+ break;
+ VisualItem* pVImin = pVI++;
+ for(; pVI < pVIend; ++pVI )
+ if( nMaxBidiLevel >= pVI->mpScriptItem->a.s.uBidiLevel )
+ break;
+ VisualItem* pVImax = pVI++;
+
+ // reverse order of items in this range
+ while( pVImin < --pVImax )
+ {
+ VisualItem aVtmp = *pVImin;
+ *(pVImin++) = *pVImax;
+ *pVImax = aVtmp;
+ }
+ }
+ }
+ }
+
+ // allocate arrays
+ // TODO: when reusing object reuse old allocations or delete them
+ // TODO: use only [nSubStringMin..nSubStringEnd) instead of [0..nSubStringEnd)
+ mnCharCapacity = nSubStringEnd;
+ mpLogClusters = new WORD[ mnCharCapacity ];
+ mpCharWidths = new int[ mnCharCapacity ];
+
+ mnGlyphCount = 0;
+ mnGlyphCapacity = 16 + 4 * (nSubStringEnd - mnSubStringMin); // worst case assumption
+ mpGlyphAdvances = new int[ mnGlyphCapacity ];
+ mpOutGlyphs = new WORD[ mnGlyphCapacity ];
+ mpGlyphOffsets = new GOFFSET[ mnGlyphCapacity ];
+ mpVisualAttrs = new SCRIPT_VISATTR[ mnGlyphCapacity ];
+
+ long nXOffset = 0;
+ for( int j = mnSubStringMin; j < nSubStringEnd; ++j )
+ mpCharWidths[j] = 0;
+
+ // layout script items
+ SCRIPT_CACHE& rScriptCache = GetScriptCache();
+ for( nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ VisualItem& rVisualItem = mpVisualItems[ nItem ];
+
+ // initialize glyph specific item info
+ rVisualItem.mnMinGlyphPos = mnGlyphCount;
+ rVisualItem.mnEndGlyphPos = 0;
+ rVisualItem.mnXOffset = nXOffset;
+ //rVisualItem.mnPixelWidth = 0;
+
+ // shortcut ignorable items
+ if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos)
+ || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) )
+ {
+ for( int i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
+ mpLogClusters[i] = sal::static_int_cast<WORD>(~0U);
+ continue;
+ }
+
+ // override bidi analysis if requested
+ if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
+ {
+ // FIXME: is this intended ?
+ rVisualItem.mpScriptItem->a.fRTL = (aScriptState.uBidiLevel & 1);
+ rVisualItem.mpScriptItem->a.s.uBidiLevel = aScriptState.uBidiLevel;
+ rVisualItem.mpScriptItem->a.s.fOverrideDirection = aScriptState.fOverrideDirection;
+ }
+
+ // convert the unicodes to glyphs
+ int nGlyphCount = 0;
+ int nCharCount = rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos;
+ HRESULT nRC = (*pScriptShape)( mhDC, &rScriptCache,
+ reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
+ nCharCount,
+ mnGlyphCapacity - rVisualItem.mnMinGlyphPos, // problem when >0xFFFF
+ &rVisualItem.mpScriptItem->a,
+ mpOutGlyphs + rVisualItem.mnMinGlyphPos,
+ mpLogClusters + rVisualItem.mnMinCharPos,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ &nGlyphCount );
+
+ // find and handle problems in the unicode to glyph conversion
+ if( nRC == USP_E_SCRIPT_NOT_IN_FONT )
+ {
+ // the whole visual item needs a fallback, but make sure that the next
+ // fallback request is limited to the characters in the original request
+ // => this is handled in ImplLayoutArgs::PrepareFallback()
+ rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos,
+ rVisualItem.IsRTL() );
+
+ // don't bother to do a default layout in a fallback level
+ if( 0 != (rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
+ continue;
+
+ // the primitive layout engine is good enough for the default layout
+ rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED;
+ nRC = (*pScriptShape)( mhDC, &rScriptCache,
+ reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
+ nCharCount,
+ mnGlyphCapacity - rVisualItem.mnMinGlyphPos,
+ &rVisualItem.mpScriptItem->a,
+ mpOutGlyphs + rVisualItem.mnMinGlyphPos,
+ mpLogClusters + rVisualItem.mnMinCharPos,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ &nGlyphCount );
+
+ if( nRC != 0 )
+ continue;
+
+#if 0 // keep the glyphs for now because they are better than nothing
+ // mark as NotDef glyphs
+ for( i = 0; i < nGlyphCount; ++i )
+ mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 0;
+#endif
+ }
+ else if( nRC != 0 )
+ // something undefined happened => give up for this visual item
+ continue;
+ else // if( nRC == 0 )
+ {
+ // check if there are any NotDef glyphs
+ for( i = 0; i < nGlyphCount; ++i )
+ if( 0 == mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
+ break;
+ if( i < nGlyphCount )
+ {
+ // clip charpos limits to the layout string without context
+ int nMinCharPos = rVisualItem.mnMinCharPos;
+ if( nMinCharPos < rArgs.mnMinCharPos )
+ nMinCharPos = rArgs.mnMinCharPos;
+ int nEndCharPos = rVisualItem.mnEndCharPos;
+ if( nEndCharPos > rArgs.mnEndCharPos )
+ nEndCharPos = rArgs.mnEndCharPos;
+ // request fallback for individual NotDef glyphs
+ do
+ {
+ // ignore non-NotDef glyphs
+ if( 0 != mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
+ continue;
+ mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH;
+ // request fallback for the whole cell that resulted in a NotDef glyph
+ // TODO: optimize algorithm
+ const bool bRTL = rVisualItem.IsRTL();
+ if( !bRTL )
+ {
+ // request fallback for the left-to-right cell
+ for( int c = nMinCharPos; c < nEndCharPos; ++c )
+ {
+ if( mpLogClusters[ c ] == i )
+ {
+ // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER
+ if( rArgs.mpStr[ c ] == 0x2060 )
+ mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
+ else
+ // <--
+ rArgs.NeedFallback( c, false );
+ }
+ }
+ }
+ else
+ {
+ // request fallback for the right to left cell
+ for( int c = nEndCharPos; --c >= nMinCharPos; )
+ {
+ if( mpLogClusters[ c ] == i )
+ {
+ // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER
+ if( rArgs.mpStr[ c ] == 0x2060 )
+ mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
+ else
+ // <--
+ rArgs.NeedFallback( c, true );
+ }
+ }
+ }
+ } while( ++i < nGlyphCount );
+ }
+ }
+
+ // now place the glyphs
+ nRC = (*pScriptPlace)( mhDC, &rScriptCache,
+ mpOutGlyphs + rVisualItem.mnMinGlyphPos,
+ nGlyphCount,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ &rVisualItem.mpScriptItem->a,
+ mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
+ mpGlyphOffsets + rVisualItem.mnMinGlyphPos,
+ &rVisualItem.maABCWidths );
+
+ if( nRC != 0 )
+ continue;
+
+ // calculate the logical char widths from the glyph layout
+ nRC = (*pScriptGetLogicalWidths)(
+ &rVisualItem.mpScriptItem->a,
+ nCharCount, nGlyphCount,
+ mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
+ mpLogClusters + rVisualItem.mnMinCharPos,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ mpCharWidths + rVisualItem.mnMinCharPos );
+
+ // update the glyph counters
+ mnGlyphCount += nGlyphCount;
+ rVisualItem.mnEndGlyphPos = mnGlyphCount;
+
+ // update nXOffset
+ int nEndGlyphPos;
+ if( GetItemSubrange( rVisualItem, i, nEndGlyphPos ) )
+ for(; i < nEndGlyphPos; ++i )
+ nXOffset += mpGlyphAdvances[ i ];
+
+ // TODO: shrink glyphpos limits to match charpos/fallback limits
+ //pVI->mnMinGlyphPos = nMinGlyphPos;
+ //pVI->mnEndGlyphPos = nEndGlyphPos;
+
+ // drop the superfluous context glyphs
+ TIntVector::const_iterator it = aDropChars.begin();
+ while( it != aDropChars.end() )
+ {
+ // find matching "drop range"
+ int nMinDropPos = *(it++); // begin of drop range
+ if( nMinDropPos >= rVisualItem.mnEndCharPos )
+ break;
+ int nEndDropPos = *(it++); // end of drop range
+ if( nEndDropPos <= rVisualItem.mnMinCharPos )
+ continue;
+ // clip "drop range" to visual item's char range
+ if( nMinDropPos <= rVisualItem.mnMinCharPos )
+ {
+ nMinDropPos = rVisualItem.mnMinCharPos;
+ // drop the whole visual item if possible
+ if( nEndDropPos >= rVisualItem.mnEndCharPos )
+ {
+ rVisualItem.mnEndGlyphPos = 0;
+ break;
+ }
+ }
+ if( nEndDropPos > rVisualItem.mnEndCharPos )
+ nEndDropPos = rVisualItem.mnEndCharPos;
+
+ // drop the glyphs which correspond to the charpos range
+ // drop the corresponding glyphs in the cluster
+ for( int c = nMinDropPos; c < nEndDropPos; ++c )
+ {
+ int nGlyphPos = mpLogClusters[c] + rVisualItem.mnMinGlyphPos;
+ // no need to bother when the cluster was already dropped
+ if( mpOutGlyphs[ nGlyphPos ] != DROPPED_OUTGLYPH )
+ {
+ for(;;)
+ {
+ mpOutGlyphs[ nGlyphPos ] = DROPPED_OUTGLYPH;
+ // until the end of visual item
+ if( ++nGlyphPos >= rVisualItem.mnEndGlyphPos )
+ break;
+ // until the next cluster start
+ if( mpVisualAttrs[ nGlyphPos ].fClusterStart )
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // scale layout metrics if needed
+ // TODO: does it make the code more simple if the metric scaling
+ // is moved to the methods that need metric scaling (e.g. FillDXArray())?
+ if( mfFontScale != 1.0 )
+ {
+ mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
+
+ for( i = 0; i < mnItemCount; ++i )
+ mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale);
+
+ mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
+ mpGlyphOffsets[i].du = (LONG)(mpGlyphOffsets[i].du * mfFontScale);
+ mpGlyphOffsets[i].dv = (LONG)(mpGlyphOffsets[i].dv * mfFontScale);
+ // mpJustifications are still NULL
+ }
+
+ for( i = mnSubStringMin; i < nSubStringEnd; ++i )
+ mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+// calculate the range of relevant glyphs for this visual item
+bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem,
+ int& rMinGlyphPos, int& rEndGlyphPos ) const
+{
+ // return early when nothing of interest in this item
+ if( rVisualItem.IsEmpty()
+ || (rVisualItem.mnEndCharPos <= mnMinCharPos)
+ || (mnEndCharPos <= rVisualItem.mnMinCharPos) )
+ return false;
+
+ // default: subrange is complete range
+ rMinGlyphPos = rVisualItem.mnMinGlyphPos;
+ rEndGlyphPos = rVisualItem.mnEndGlyphPos;
+
+ // return early when the whole item is of interest
+ if( (mnMinCharPos <= rVisualItem.mnMinCharPos)
+ && (rVisualItem.mnEndCharPos <= mnEndCharPos ) )
+ return true;
+
+ // get glyph range from char range by looking at cluster boundries
+ // TODO: optimize for case that LTR/RTL correspond to monotonous glyph indexes
+ rMinGlyphPos = rVisualItem.mnEndGlyphPos;
+ int nMaxGlyphPos = 0;
+
+ int i = mnMinCharPos;
+ if( i < rVisualItem.mnMinCharPos )
+ i = rVisualItem.mnMinCharPos;
+ int nCharPosLimit = rVisualItem.mnEndCharPos;
+ if( nCharPosLimit > mnEndCharPos )
+ nCharPosLimit = mnEndCharPos;
+ for(; i < nCharPosLimit; ++i )
+ {
+ int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
+ if( rMinGlyphPos > n )
+ rMinGlyphPos = n;
+ if( nMaxGlyphPos < n )
+ nMaxGlyphPos = n;
+ }
+ if (nMaxGlyphPos > rVisualItem.mnEndGlyphPos)
+ nMaxGlyphPos = rVisualItem.mnEndGlyphPos - 1;
+
+ // extend the glyph range to account for all glyphs in referenced clusters
+ if( !rVisualItem.IsRTL() ) // LTR-item
+ {
+ // extend to rightmost glyph of rightmost referenced cluster
+ for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i )
+ if( mpVisualAttrs[i].fClusterStart )
+ break;
+ }
+ else // RTL-item
+ {
+ // extend to leftmost glyph of leftmost referenced cluster
+ for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i )
+ if( mpVisualAttrs[i].fClusterStart )
+ break;
+ }
+ rEndGlyphPos = nMaxGlyphPos + 1;
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
+ int& nStartx8, sal_Int32* pGlyphAdvances, int* pCharPosAry ) const
+{
+ // HACK to allow fake-glyph insertion (e.g. for kashidas)
+ // TODO: use iterator idiom instead of GetNextGlyphs(...)
+ // TODO: else make sure that the limit for glyph injection is sufficient (currently 256)
+ int nSubIter = nStartx8 & 0xff;
+ int nStart = nStartx8 >> 8;
+
+ // check the glyph iterator
+ if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs
+ return 0;
+
+ // find the visual item for the nStart glyph position
+ int nItem = 0;
+ const VisualItem* pVI = mpVisualItems;
+ if( nStart <= 0 ) // nStart<=0 requests the first visible glyph
+ {
+ // find first visible item
+ for(; nItem < mnItemCount; ++nItem, ++pVI )
+ if( !pVI->IsEmpty() )
+ break;
+ // it is possible that there are glyphs but no valid visual item
+ // TODO: get rid of these visual items more early
+ if( nItem < mnItemCount )
+ nStart = pVI->mnMinGlyphPos;
+ }
+ else //if( nStart > 0 ) // nStart>0 means absolute glyph pos +1
+ {
+ --nStart;
+
+ // find matching item
+ for(; nItem < mnItemCount; ++nItem, ++pVI )
+ if( (nStart >= pVI->mnMinGlyphPos)
+ && (nStart < pVI->mnEndGlyphPos) )
+ break;
+ }
+
+ // after the last visual item there are no more glyphs
+ if( (nItem >= mnItemCount) || (nStart < 0) )
+ {
+ nStartx8 = (mnGlyphCount + 1) << 8;
+ return 0;
+ }
+
+ // calculate the first glyph in the next visual item
+ int nNextItemStart = mnGlyphCount;
+ while( ++nItem < mnItemCount )
+ {
+ if( mpVisualItems[nItem].IsEmpty() )
+ continue;
+ nNextItemStart = mpVisualItems[nItem].mnMinGlyphPos;
+ break;
+ }
+
+ // get the range of relevant glyphs in this visual item
+ int nMinGlyphPos, nEndGlyphPos;
+ bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
+ DBG_ASSERT( bRC, "USPLayout::GNG GISR() returned false" );
+ if( !bRC )
+ {
+ nStartx8 = (mnGlyphCount + 1) << 8;
+ return 0;
+ }
+
+ // make sure nStart is inside the range of relevant glyphs
+ if( nStart < nMinGlyphPos )
+ nStart = nMinGlyphPos;
+
+ // calculate the start glyph xoffset relative to layout's base position,
+ // advance to next visual glyph position by using adjusted glyph widths
+ // TODO: speed up the calculation for nStart!=0 case by using rPos as a cache
+ long nXOffset = pVI->mnXOffset;
+ const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
+ for( int i = nMinGlyphPos; i < nStart; ++i )
+ nXOffset += pGlyphWidths[ i ];
+
+ // adjust the nXOffset relative to glyph cluster start
+ int c = mnMinCharPos;
+ if( !pVI->IsRTL() ) // LTR-case
+ {
+ // LTR case: subtract the remainder of the cell from xoffset
+ int nTmpIndex = mpLogClusters[c];
+ while( (--c >= pVI->mnMinCharPos)
+ && (nTmpIndex == mpLogClusters[c]) )
+ nXOffset -= mpCharWidths[c];
+ }
+ else // RTL-case
+ {
+ // RTL case: add the remainder of the cell from xoffset
+ int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ];
+ while( (--c >= pVI->mnMinCharPos)
+ && (nTmpIndex == mpLogClusters[c]) )
+ nXOffset += mpCharWidths[c];
+
+ // adjust the xoffset if justified glyphs are not positioned at their justified positions yet
+ if( mpJustifications && !bManualCellAlign )
+ nXOffset += mpJustifications[ nStart ] - mpGlyphAdvances[ nStart ];
+ }
+
+ // create mpGlyphs2Chars[] if it is needed later
+ if( pCharPosAry && !mpGlyphs2Chars )
+ {
+ // create and reset the new array
+ mpGlyphs2Chars = new int[ mnGlyphCapacity ];
+ for( int i = 0; i < mnGlyphCount; ++i )
+ mpGlyphs2Chars[i] = -1;
+ // calculate the char->glyph mapping
+ for( nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ // ignore invisible visual items
+ const VisualItem& rVI = mpVisualItems[ nItem ];
+ if( rVI.IsEmpty() )
+ continue;
+ // calculate the mapping by using mpLogClusters[]
+ // mpGlyphs2Chars[] should obey the logical order
+ // => reversing the loop does this by overwriting higher logicals
+ for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; )
+ {
+ int i = mpLogClusters[c] + rVI.mnMinGlyphPos;
+ mpGlyphs2Chars[i] = c;
+ }
+ }
+ }
+
+ // calculate the absolute position of the first result glyph in pixel units
+ const GOFFSET aGOffset = mpGlyphOffsets[ nStart ];
+ Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv );
+ rPos = GetDrawPosition( aRelativePos );
+
+ // fill the result arrays
+ int nCount = 0;
+ while( nCount < nLen )
+ {
+ // prepare return values
+ sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
+ int nGlyphWidth = pGlyphWidths[ nStart ];
+ int nCharPos = -1; // no need to determine charpos
+ if( mpGlyphs2Chars ) // unless explicitly requested+provided
+ nCharPos = mpGlyphs2Chars[ nStart ];
+
+ // inject kashida glyphs if needed
+ if( !mbDisableGlyphInjection
+ && mpJustifications
+ && mnMinKashidaWidth
+ && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL )
+ {
+ // prepare draw position adjustment
+ int nExtraOfs = (nSubIter++) * mnMinKashidaWidth;
+ // calculate space available for the injected glyphs
+ nGlyphWidth = mpGlyphAdvances[ nStart ];
+ const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth;
+ const int nToFillWidth = nExtraWidth - nExtraOfs;
+ if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room
+ || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others
+ {
+ // handle if there is not sufficient room for a full glyph
+ if( nToFillWidth < mnMinKashidaWidth )
+ {
+ // overlap it with the previously injected glyph if possible
+ int nOverlap = mnMinKashidaWidth - nToFillWidth;
+ // else overlap it with both neighboring glyphs
+ if( nSubIter <= 1 )
+ nOverlap /= 2;
+ nExtraOfs -= nOverlap;
+ }
+ nGlyphWidth = mnMinKashidaWidth;
+ aGlyphId = mnMinKashidaGlyph;
+ nCharPos = -1;
+ }
+ else
+ {
+ nExtraOfs += nToFillWidth; // at right of cell
+ nSubIter = 0; // done with glyph injection
+ }
+ if( !bManualCellAlign )
+ nExtraOfs -= nExtraWidth; // adjust for right-aligned cells
+
+ // adjust the draw position for the injected-glyphs case
+ if( nExtraOfs )
+ {
+ aRelativePos.X() += nExtraOfs;
+ rPos = GetDrawPosition( aRelativePos );
+ }
+ }
+
+ // update return values
+ *(pGlyphs++) = aGlyphId;
+ if( pGlyphAdvances )
+ *(pGlyphAdvances++) = nGlyphWidth;
+ if( pCharPosAry )
+ *(pCharPosAry++) = nCharPos;
+
+ // increment counter of returned glyphs
+ ++nCount;
+
+ // reduce code complexity by returning early in glyph-injection case
+ if( nSubIter != 0 )
+ break;
+
+ // stop after the last visible glyph in this visual item
+ if( ++nStart >= nEndGlyphPos )
+ {
+ nStart = nNextItemStart;
+ break;
+ }
+
+ // RTL-justified glyph positioning is not easy
+ // simplify the code by just returning only one glyph at a time
+ if( mpJustifications && pVI->IsRTL() )
+ break;
+
+ // stop when the x-position of the next glyph is unexpected
+ if( !pGlyphAdvances )
+ if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) )
+ || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) )
+ break;
+
+ // stop when the y-position of the next glyph is unexpected
+ if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) )
+ break;
+ }
+
+ ++nStart;
+ nStartx8 = (nStart << 8) + nSubIter;
+ return nCount;
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos )
+{
+ DBG_ASSERT( !(nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" );
+ int nStart = nStartx8 >> 8;
+ if( nStart > mnGlyphCount )
+ return;
+
+ VisualItem* pVI = mpVisualItems;
+ int nMinGlyphPos = 0, nEndGlyphPos;
+ if( nStart == 0 ) // nStart==0 for first visible glyph
+ {
+ for( int i = mnItemCount; --i >= 0; ++pVI )
+ if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) )
+ break;
+ nStart = nMinGlyphPos;
+ DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::MoveG overflow" );
+ }
+ else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1
+ {
+ --nStart;
+ for( int i = mnItemCount; --i >= 0; ++pVI )
+ if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) )
+ break;
+ bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
+ (void)bRC; // avoid var-not-used warning
+ DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" );
+ }
+
+ long nDelta = nNewXPos - pVI->mnXOffset;
+ if( nStart > nMinGlyphPos )
+ {
+ // move the glyph by expanding its left glyph but ignore dropped glyphs
+ int i, nLastUndropped = nMinGlyphPos - 1;
+ for( i = nMinGlyphPos; i < nStart; ++i )
+ {
+ if (mpOutGlyphs[i] != DROPPED_OUTGLYPH)
+ {
+ nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ];
+ nLastUndropped = i;
+ }
+ }
+ if (nLastUndropped >= nMinGlyphPos)
+ {
+ mpGlyphAdvances[ nLastUndropped ] += nDelta;
+ if (mpJustifications) mpJustifications[ nLastUndropped ] += nDelta;
+ }
+ else
+ {
+ pVI->mnXOffset += nDelta;
+ }
+ }
+ else
+ {
+ // move the visual item by having an offset
+ pVI->mnXOffset += nDelta;
+ }
+ // move subsequent items - this often isn't necessary because subsequent
+ // moves will correct subsequent items. However, if there is a contiguous
+ // range not involving fallback which spans items, this will be needed
+ while (++pVI - mpVisualItems < mnItemCount)
+ {
+ pVI->mnXOffset += nDelta;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::DropGlyph( int nStartx8 )
+{
+ DBG_ASSERT( !(nStartx8 & 0xff), "USP::DropGlyph(): glyph injection not disabled!" );
+ int nStart = nStartx8 >> 8;
+ DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" );
+
+ if( nStart > 0 ) // nStart>0 means absolute glyph pos + 1
+ --nStart;
+ else // nStart<=0 for first visible glyph
+ {
+ VisualItem* pVI = mpVisualItems;
+ for( int i = mnItemCount, nDummy; --i >= 0; ++pVI )
+ if( GetItemSubrange( *pVI, nStart, nDummy ) )
+ break;
+ DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::DropG overflow" );
+ int nOffset = 0;
+ int j = pVI->mnMinGlyphPos;
+ while (mpOutGlyphs[j] == DROPPED_OUTGLYPH) j++;
+ if (j == nStart)
+ {
+ pVI->mnXOffset += ((mpJustifications)? mpJustifications[nStart] : mpGlyphAdvances[nStart]);
+ }
+ }
+
+ mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::Simplify( bool /*bIsBase*/ )
+{
+ static const WCHAR cDroppedGlyph = DROPPED_OUTGLYPH;
+ int i;
+ // if there are no dropped glyphs don't bother
+ for( i = 0; i < mnGlyphCount; ++i )
+ if( mpOutGlyphs[ i ] == cDroppedGlyph )
+ break;
+ if( i >= mnGlyphCount )
+ return;
+
+ // prepare for sparse layout
+ // => make sure mpGlyphs2Chars[] exists
+ if( !mpGlyphs2Chars )
+ {
+ mpGlyphs2Chars = new int[ mnGlyphCapacity ];
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphs2Chars[ i ] = -1;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ // skip invisible items
+ VisualItem& rVI = mpVisualItems[ nItem ];
+ if( rVI.IsEmpty() )
+ continue;
+ for( i = rVI.mnEndCharPos; --i >= rVI.mnMinCharPos; )
+ {
+ int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
+ mpGlyphs2Chars[ j ] = i;
+ }
+ }
+ }
+
+ // remove the dropped glyphs
+ const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ VisualItem& rVI = mpVisualItems[ nItem ];
+ if( rVI.IsEmpty() )
+ continue;
+
+ // mark replaced character widths
+ for( i = rVI.mnMinCharPos; i < rVI.mnEndCharPos; ++i )
+ {
+ int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
+ if( mpOutGlyphs[ j ] == cDroppedGlyph )
+ mpCharWidths[ i ] = 0;
+ }
+
+ // handle dropped glyphs at start of visual item
+ int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos;
+ GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos );
+ i = nMinGlyphPos;
+ while( (mpOutGlyphs[i] == cDroppedGlyph) && (i < nEndGlyphPos) )
+ {
+ //rVI.mnXOffset += pGlyphWidths[ i ];
+ rVI.mnMinGlyphPos = ++i;
+ }
+
+ // when all glyphs in item got dropped mark it as empty
+ if( i >= nEndGlyphPos )
+ {
+ rVI.mnEndGlyphPos = 0;
+ continue;
+ }
+ // If there are still glyphs in the cluster and mnMinGlyphPos
+ // has changed then we need to remove the dropped glyphs at start
+ // to correct logClusters, which is unsigned and relative to the
+ // item start.
+ if (rVI.mnMinGlyphPos != nOrigMinGlyphPos)
+ {
+ // drop any glyphs in the visual item outside the range
+ for (i = nOrigMinGlyphPos; i < nMinGlyphPos; i++)
+ mpOutGlyphs[ i ] = cDroppedGlyph;
+ rVI.mnMinGlyphPos = i = nOrigMinGlyphPos;
+ }
+
+ // handle dropped glyphs in the middle of visual item
+ for(; i < nEndGlyphPos; ++i )
+ if( mpOutGlyphs[ i ] == cDroppedGlyph )
+ break;
+ int j = i;
+ while( ++i < nEndGlyphPos )
+ {
+ if( mpOutGlyphs[ i ] == cDroppedGlyph )
+ continue;
+ mpOutGlyphs[ j ] = mpOutGlyphs[ i ];
+ mpGlyphOffsets[ j ] = mpGlyphOffsets[ i ];
+ mpVisualAttrs[ j ] = mpVisualAttrs[ i ];
+ mpGlyphAdvances[ j ] = mpGlyphAdvances[ i ];
+ if( mpJustifications )
+ mpJustifications[ j ] = mpJustifications[ i ];
+ const int k = mpGlyphs2Chars[ i ];
+ mpGlyphs2Chars[ j ] = k;
+ const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos;
+ if( k < 0) // extra glyphs are already mapped
+ continue;
+ mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos);
+ }
+
+ rVI.mnEndGlyphPos = j;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::DrawText( SalGraphics& ) const
+{
+ HFONT hOrigFont = DisableFontScaling();
+
+ int nBaseClusterOffset = 0;
+ int nBaseGlyphPos = -1;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ const VisualItem& rVisualItem = mpVisualItems[ nItem ];
+
+ // skip if there is nothing to display
+ int nMinGlyphPos, nEndGlyphPos;
+ if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
+ continue;
+
+ if( nBaseGlyphPos < 0 )
+ {
+ // adjust draw position relative to cluster start
+ if( rVisualItem.IsRTL() )
+ nBaseGlyphPos = nEndGlyphPos - 1;
+ else
+ nBaseGlyphPos = nMinGlyphPos;
+
+ const int* pGlyphWidths;
+ if( mpJustifications )
+ pGlyphWidths = mpJustifications;
+ else
+ pGlyphWidths = mpGlyphAdvances;
+
+ int i = mnMinCharPos;
+ while( (--i >= rVisualItem.mnMinCharPos)
+ && (nBaseGlyphPos == mpLogClusters[i]) )
+ nBaseClusterOffset += mpCharWidths[i];
+
+ if( !rVisualItem.IsRTL() )
+ nBaseClusterOffset = -nBaseClusterOffset;
+ }
+
+ // now draw the matching glyphs in this item
+ Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 );
+ Point aPos = GetDrawPosition( aRelPos );
+ SCRIPT_CACHE& rScriptCache = GetScriptCache();
+ (*pScriptTextOut)( mhDC, &rScriptCache,
+ aPos.X(), aPos.Y(), 0, NULL,
+ &rVisualItem.mpScriptItem->a, NULL, 0,
+ mpOutGlyphs + nMinGlyphPos,
+ nEndGlyphPos - nMinGlyphPos,
+ mpGlyphAdvances + nMinGlyphPos,
+ mpJustifications ? mpJustifications + nMinGlyphPos : NULL,
+ mpGlyphOffsets + nMinGlyphPos );
+ }
+
+ if( hOrigFont )
+ DeleteFont( SelectFont( mhDC, hOrigFont ) );
+}
+
+// -----------------------------------------------------------------------
+
+long UniscribeLayout::FillDXArray( long* pDXArray ) const
+{
+ // calculate width of the complete layout
+ long nWidth = mnBaseAdv;
+ for( int nItem = mnItemCount; --nItem >= 0; )
+ {
+ const VisualItem& rVI = mpVisualItems[ nItem ];
+
+ // skip if there is nothing to display
+ int nMinGlyphPos, nEndGlyphPos;
+ if( !GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ) )
+ continue;
+
+ // width = xoffset + width of last item
+ nWidth = rVI.mnXOffset;
+ const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
+ for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
+ nWidth += pGlyphWidths[i];
+ break;
+ }
+
+ // copy the virtual char widths into pDXArray[]
+ if( pDXArray )
+ for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
+ pDXArray[ i - mnMinCharPos ] = mpCharWidths[ i ];
+
+ return nWidth;
+}
+
+// -----------------------------------------------------------------------
+
+int UniscribeLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
+{
+ long nWidth = 0;
+ for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
+ {
+ nWidth += mpCharWidths[ i ] * nFactor;
+
+ // check if the nMaxWidth still fits the current sub-layout
+ if( nWidth >= nMaxWidth )
+ {
+ // go back to cluster start
+ // we have to find the visual item first since the mpLogClusters[]
+ // needed to find the cluster start is relative to to the visual item
+ int nMinGlyphIndex = 0;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ const VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
+ if( (i >= rVisualItem.mnMinCharPos)
+ && (i < rVisualItem.mnEndCharPos) )
+ break;
+ }
+ // now go back to the matching cluster start
+ do
+ {
+ int nGlyphPos = mpLogClusters[i] + nMinGlyphIndex;
+ if( 0 != mpVisualAttrs[ nGlyphPos ].fClusterStart )
+ return i;
+ } while( --i >= mnMinCharPos );
+
+ // if the cluster starts before the start of the visual item
+ // then set the visual breakpoint before this item
+ return mnMinCharPos;
+ }
+
+ // the visual break also depends on the nCharExtra between the characters
+ nWidth += nCharExtra;
+ }
+
+ // the whole layout did fit inside the nMaxWidth
+ return STRING_LEN;
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
+{
+ int i;
+ for( i = 0; i < nMaxIdx; ++i )
+ pCaretXArray[ i ] = -1;
+ long* const pGlyphPos = (long*)alloca( (mnGlyphCount+1) * sizeof(long) );
+ for( i = 0; i <= mnGlyphCount; ++i )
+ pGlyphPos[ i ] = -1;
+
+ long nXPos = 0;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ const VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ if( rVisualItem.IsEmpty() )
+ continue;
+
+ if (mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK)
+ {
+ nXPos = rVisualItem.mnXOffset;
+ }
+ // get glyph positions
+ // TODO: handle when rVisualItem's glyph range is only partially used
+ for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
+ {
+ pGlyphPos[ i ] = nXPos;
+ nXPos += mpGlyphAdvances[ i ];
+ }
+ // rightmost position of this visualitem
+ pGlyphPos[ i ] = nXPos;
+
+ // convert glyph positions to character positions
+ i = rVisualItem.mnMinCharPos;
+ if( i < mnMinCharPos )
+ i = mnMinCharPos;
+ for(; (i < rVisualItem.mnEndCharPos) && (i < mnEndCharPos); ++i )
+ {
+ int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
+ int nCurrIdx = i * 2;
+ if( !rVisualItem.IsRTL() )
+ {
+ // normal positions for LTR case
+ pCaretXArray[ nCurrIdx ] = pGlyphPos[ j ];
+ pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j+1 ];
+ }
+ else
+ {
+ // reverse positions for RTL case
+ pCaretXArray[ nCurrIdx ] = pGlyphPos[ j+1 ];
+ pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j ];
+ }
+ }
+ }
+
+ if (!(mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK))
+ {
+ nXPos = 0;
+ // fixup unknown character positions to neighbor
+ for( i = 0; i < nMaxIdx; ++i )
+ {
+ if( pCaretXArray[ i ] >= 0 )
+ nXPos = pCaretXArray[ i ];
+ else
+ pCaretXArray[ i ] = nXPos;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ SalLayout::AdjustLayout( rArgs );
+
+ // adjust positions if requested
+ if( rArgs.mpDXArray )
+ ApplyDXArray( rArgs );
+ else if( rArgs.mnLayoutWidth )
+ Justify( rArgs.mnLayoutWidth );
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
+{
+ const long* pDXArray = rArgs.mpDXArray;
+
+ // increase char widths in string range to desired values
+ bool bModified = false;
+ int nOldWidth = 0;
+ DBG_ASSERT( mnUnitsPerPixel==1, "UniscribeLayout.mnUnitsPerPixel != 1" );
+ int i,j;
+ for( i = mnMinCharPos, j = 0; i < mnEndCharPos; ++i, ++j )
+ {
+ int nNewCharWidth = (pDXArray[j] - nOldWidth);
+ // TODO: nNewCharWidth *= mnUnitsPerPixel;
+ if( mpCharWidths[i] != nNewCharWidth )
+ {
+ mpCharWidths[i] = nNewCharWidth;
+ bModified = true;
+ }
+ nOldWidth = pDXArray[j];
+ }
+
+ if( !bModified )
+ return;
+
+ // initialize justifications array
+ mpJustifications = new int[ mnGlyphCapacity ];
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpJustifications[ i ] = mpGlyphAdvances[ i ];
+
+ // apply new widths to script items
+ long nXOffset = 0;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ VisualItem& rVisualItem = mpVisualItems[ nItem ];
+
+ // set the position of this visual item
+ rVisualItem.mnXOffset = nXOffset;
+
+ // ignore empty visual items
+ if( rVisualItem.IsEmpty() )
+ {
+ for (i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; i++)
+ nXOffset += mpCharWidths[i];
+ continue;
+ }
+ // ignore irrelevant visual items
+ if( (rVisualItem.mnMinCharPos >= mnEndCharPos)
+ || (rVisualItem.mnEndCharPos <= mnMinCharPos) )
+ continue;
+
+ // if needed prepare special handling for arabic justification
+ rVisualItem.mbHasKashidas = false;
+ if( rVisualItem.IsRTL() )
+ {
+ for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
+ if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF82 ) // any Arabic justification
+ { // excluding SCRIPT_JUSTIFY_NONE
+ // yes
+ rVisualItem.mbHasKashidas = true;
+ // so prepare for kashida handling
+ InitKashidaHandling();
+ break;
+ }
+
+ if( rVisualItem.HasKashidas() )
+ for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
+ {
+ // TODO: check if we still need this hack after correction of kashida placing?
+ // (i87688): apparently yes, we still need it!
+ if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE )
+ // usp decided that justification can't be applied here
+ // but maybe our Kashida algorithm thinks differently.
+ // To avoid trouble (gaps within words, last character of
+ // a word gets a Kashida appended) override this.
+
+ // I chose SCRIPT_JUSTIFY_ARABIC_KASHIDA to replace SCRIPT_JUSTIFY_NONE
+ // just because this previous hack (which I haven't understand, sorry) used
+ // the same value to replace. Don't know if this is really the best
+ // thing to do, but it seems to fix things
+ mpVisualAttrs[i].uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
+ }
+ }
+
+ // convert virtual charwidths to glyph justification values
+ HRESULT nRC = (*pScriptApplyLogicalWidth)(
+ mpCharWidths + rVisualItem.mnMinCharPos,
+ rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos,
+ rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
+ mpLogClusters + rVisualItem.mnMinCharPos,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
+ &rVisualItem.mpScriptItem->a,
+ &rVisualItem.maABCWidths,
+ mpJustifications + rVisualItem.mnMinGlyphPos );
+
+ if( nRC != 0 )
+ {
+ delete[] mpJustifications;
+ mpJustifications = NULL;
+ break;
+ }
+
+ // to prepare for the next visual item
+ // update nXOffset to the next items position
+ // before the mpJustifications[] array gets modified
+ int nMinGlyphPos, nEndGlyphPos;
+ if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
+ {
+ for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
+ nXOffset += mpJustifications[ i ];
+
+ if( rVisualItem.mbHasKashidas )
+ KashidaItemFix( nMinGlyphPos, nEndGlyphPos );
+ }
+
+ // workaround needed for older USP versions:
+ // right align the justification-adjusted glyphs in their cells for RTL-items
+ // unless the right alignment is done by inserting kashidas
+ if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() )
+ {
+ for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
+ {
+ const int nXOffsetAdjust = mpJustifications[i] - mpGlyphAdvances[i];
+ // #i99862# skip diacritics, we mustn't add extra justification to diacritics
+ int nIdxAdd = i - 1;
+ while( (nIdxAdd >= nMinGlyphPos) && !mpGlyphAdvances[nIdxAdd] )
+ --nIdxAdd;
+ if( nIdxAdd < nMinGlyphPos )
+ rVisualItem.mnXOffset += nXOffsetAdjust;
+ else
+ mpJustifications[nIdxAdd] += nXOffsetAdjust;
+ mpJustifications[i] -= nXOffsetAdjust;
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::InitKashidaHandling()
+{
+ if( mnMinKashidaGlyph != 0 ) // already initialized
+ return;
+
+ mrWinFontEntry.InitKashidaHandling( mhDC );
+ mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth());
+ mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph();
+}
+
+// adjust the kashida placement matching to the WriterEngine
+void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos )
+{
+ // workaround needed for all known USP versions:
+ // ApplyLogicalWidth does not match ScriptJustify behaviour
+ for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
+ {
+ // check for vowels
+ if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ])
+ && (1U << mpVisualAttrs[i].uJustification) & 0xFF83 ) // all Arabic justifiction types
+ { // including SCRIPT_JUSTIFY_NONE
+ // vowel, we do it like ScriptJustify does
+ // the vowel gets the extra width
+ long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
+ mpJustifications [ i ] = mpGlyphAdvances [ i ];
+ mpJustifications [ i - 1 ] += nSpaceAdded;
+ }
+ }
+
+ // redistribute the widths for kashidas
+ for( int i = nMinGlyphPos; i < nEndGlyphPos; )
+ KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i );
+}
+
+bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos )
+{
+ // doing pixel work within a word.
+ // sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth
+
+ // find the next kashida
+ int nMinPos = *pnCurrentPos;
+ int nMaxPos = *pnCurrentPos;
+ for( int i = nMaxPos; i < nEndGlyphPos; ++i )
+ {
+ if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK)
+ && (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) )
+ break;
+ nMaxPos = i;
+ }
+ *pnCurrentPos = nMaxPos + 1;
+ if( nMinPos == nMaxPos )
+ return false;
+
+ // calculate the available space for an extra kashida
+ long nMaxAdded = 0;
+ int nKashPos = -1;
+ for( int i = nMaxPos; i >= nMinPos; --i )
+ {
+ long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
+ if( nSpaceAdded > nMaxAdded )
+ {
+ nKashPos = i;
+ nMaxAdded = nSpaceAdded;
+ }
+ }
+
+ // return early if there is no need for an extra kashida
+ if ( nMaxAdded <= 0 )
+ return false;
+ // return early if there is not enough space for an extra kashida
+ if( 2*nMaxAdded < mnMinKashidaWidth )
+ return false;
+
+ // redistribute the extra spacing to the kashida position
+ for( int i = nMinPos; i <= nMaxPos; ++i )
+ {
+ if( i == nKashPos )
+ continue;
+ // everything else should not have extra spacing
+ long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
+ if( nSpaceAdded > 0 )
+ {
+ mpJustifications[ i ] -= nSpaceAdded;
+ mpJustifications[ nKashPos ] += nSpaceAdded;
+ }
+ }
+
+ // check if we fulfill minimal kashida width
+ long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ];
+ if( nSpaceAdded < mnMinKashidaWidth )
+ {
+ // ugly: steal some pixels
+ long nSteal = 1;
+ if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos)))
+ nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos);
+ for( int i = nMinPos; i <= nMaxPos; ++i )
+ {
+ if( i == nKashPos )
+ continue;
+ nSteal = Min( mnMinKashidaWidth - nSpaceAdded, nSteal );
+ if ( nSteal > 0 )
+ {
+ mpJustifications [ i ] -= nSteal;
+ mpJustifications [ nKashPos ] += nSteal;
+ nSpaceAdded += nSteal;
+ }
+ if( nSpaceAdded >= mnMinKashidaWidth )
+ return true;
+ }
+ }
+
+ // blank padding
+ long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded;
+ if( nSpaceMissing > 0 )
+ {
+ // inner glyph: distribute extra space evenly
+ if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) )
+ {
+ mpJustifications [ nKashPos ] += nSpaceMissing;
+ long nHalfSpace = nSpaceMissing / 2;
+ mpJustifications [ nMinPos - 1 ] -= nHalfSpace;
+ mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace;
+ }
+ // rightmost: left glyph gets extra space
+ else if( nMinPos > nMinGlyphPos )
+ {
+ mpJustifications [ nMinPos - 1 ] -= nSpaceMissing;
+ mpJustifications [ nKashPos ] += nSpaceMissing;
+ }
+ // leftmost: right glyph gets extra space
+ else if( nMaxPos < nEndGlyphPos - 1 )
+ {
+ mpJustifications [ nKashPos ] += nSpaceMissing;
+ mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing;
+ }
+ else
+ return false;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::Justify( long nNewWidth )
+{
+ long nOldWidth = 0;
+ int i;
+ for( i = mnMinCharPos; i < mnEndCharPos; ++i )
+ nOldWidth += mpCharWidths[ i ];
+ if( nOldWidth <= 0 )
+ return;
+
+ nNewWidth *= mnUnitsPerPixel; // convert into font units
+ if( nNewWidth == nOldWidth )
+ return;
+ // prepare to distribute the extra width evenly among the visual items
+ const double fStretch = (double)nNewWidth / nOldWidth;
+
+ // initialize justifications array
+ mpJustifications = new int[ mnGlyphCapacity ];
+ for( i = 0; i < mnGlyphCapacity; ++i )
+ mpJustifications[ i ] = mpGlyphAdvances[ i ];
+
+ // justify stretched script items
+ long nXOffset = 0;
+ SCRIPT_CACHE& rScriptCache = GetScriptCache();
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ if( rVisualItem.IsEmpty() )
+ continue;
+
+ if( (rVisualItem.mnMinCharPos < mnEndCharPos)
+ && (rVisualItem.mnEndCharPos > mnMinCharPos) )
+ {
+ long nItemWidth = 0;
+ for( i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
+ nItemWidth += mpCharWidths[ i ];
+ nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5);
+
+ HRESULT nRC = (*pScriptJustify) (
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
+ rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
+ nItemWidth,
+ mnMinKashidaWidth,
+ mpJustifications + rVisualItem.mnMinGlyphPos );
+
+ rVisualItem.mnXOffset = nXOffset;
+ nXOffset += nItemWidth;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const
+{
+ // we have to find the visual item first since the mpLogClusters[]
+ // needed to find the cluster start is relative to to the visual item
+ int nMinGlyphIndex = -1;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ const VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ if( (nCharPos >= rVisualItem.mnMinCharPos)
+ && (nCharPos < rVisualItem.mnEndCharPos) )
+ {
+ nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
+ break;
+ }
+ }
+ // Invalid char pos or leftmost glyph in visual item
+ if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] )
+ return false;
+
+// This test didn't give the expected results
+/* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ])
+ // two chars, one glyph
+ return false;*/
+
+ const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex;
+ if( nGlyphPos <= 0 )
+ return true;
+ // justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE
+ // and not SCRIPT_JUSTIFY_ARABIC_BLANK
+ // special case: glyph to the left is vowel (no advance width)
+ if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK
+ || ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE
+ && mpGlyphAdvances [ nGlyphPos-1 ] ))
+ return false;
+ return true;
+}
+
+#endif // USE_UNISCRIBE
+
+#ifdef ENABLE_GRAPHITE
+
+class GraphiteLayoutWinImpl : public GraphiteLayout
+{
+public:
+ GraphiteLayoutWinImpl(const gr::Font & font, ImplWinFontEntry & rFont)
+ throw()
+ : GraphiteLayout(font), mrFont(rFont) {};
+ virtual ~GraphiteLayoutWinImpl() throw() {};
+ virtual sal_GlyphId getKashidaGlyph(int & rWidth);
+private:
+ ImplWinFontEntry & mrFont;
+};
+
+sal_GlyphId GraphiteLayoutWinImpl::getKashidaGlyph(int & rWidth)
+{
+ rWidth = mrFont.GetMinKashidaWidth();
+ return mrFont.GetMinKashidaGlyph();
+}
+
+// This class uses the SIL Graphite engine to provide complex text layout services to the VCL
+// @author tse
+//
+class GraphiteWinLayout : public WinLayout
+{
+private:
+ mutable GraphiteWinFont mpFont;
+ grutils::GrFeatureParser * mpFeatures;
+ mutable GraphiteLayoutWinImpl maImpl;
+public:
+ GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE);
+
+ static bool IsGraphiteEnabledFont(HDC hDC) throw();
+
+ // used by upper layers
+ virtual bool LayoutText( ImplLayoutArgs& ); // first step of layout
+ virtual void AdjustLayout( ImplLayoutArgs& ); // adjusting after fallback etc.
+ // virtual void InitFont() const;
+ virtual void DrawText( SalGraphics& ) const;
+
+ // methods using string indexing
+ virtual int GetTextBreak( long nMaxWidth, long nCharExtra=0, int nFactor=1 ) const;
+ virtual long FillDXArray( long* pDXArray ) const;
+
+ virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
+
+ // methods using glyph indexing
+ virtual int GetNextGlyphs(int nLen, sal_GlyphId* pGlyphIdxAry, ::Point & rPos, int&,
+ long* pGlyphAdvAry = 0, int* pCharPosAry = 0 ) const;
+
+ // used by glyph+font+script fallback
+ virtual void MoveGlyph( int nStart, long nNewXPos );
+ virtual void DropGlyph( int nStart );
+ virtual void Simplify( bool bIsBase );
+ ~GraphiteWinLayout() { delete mpFeatures; mpFeatures = NULL; };
+protected:
+ virtual void ReplaceDC(gr::Segment & segment) const;
+ virtual void RestoreDC(gr::Segment & segment) const;
+};
+
+bool GraphiteWinLayout::IsGraphiteEnabledFont(HDC hDC) throw()
+{
+ return gr::WinFont::FontHasGraphiteTables(hDC);
+}
+
+GraphiteWinLayout::GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw()
+ : WinLayout(hDC, rWFD, rWFE), mpFont(hDC),
+ maImpl(mpFont, rWFE)
+{
+ const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( rWFE.maFontSelData.meLanguage );
+ rtl::OString name = rtl::OUStringToOString(
+ rWFE.maFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
+ sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
+ if (nFeat > 0)
+ {
+ rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
+ mpFeatures = new grutils::GrFeatureParser(mpFont, aFeat.getStr(), aLang.getStr());
+ }
+ else
+ {
+ mpFeatures = new grutils::GrFeatureParser(mpFont, aLang.getStr());
+ }
+ maImpl.SetFeatures(mpFeatures);
+}
+
+void GraphiteWinLayout::ReplaceDC(gr::Segment & segment) const
+{
+ COLORREF color = GetTextColor(mhDC);
+ dynamic_cast<gr::WinFont&>(segment.getFont()).replaceDC(mhDC);
+ SetTextColor(mhDC, color);
+}
+
+void GraphiteWinLayout::RestoreDC(gr::Segment & segment) const
+{
+ dynamic_cast<gr::WinFont&>(segment.getFont()).restoreDC();
+}
+
+bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args)
+{
+ if (args.mnMinCharPos >= args.mnEndCharPos)
+ {
+ maImpl.clear();
+ return true;
+ }
+ HFONT hUnRotatedFont;
+ if (args.mnOrientation)
+ {
+ // Graphite gets very confused if the font is rotated
+ LOGFONTW aLogFont;
+ ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
+ aLogFont.lfEscapement = 0;
+ aLogFont.lfOrientation = 0;
+ hUnRotatedFont = ::CreateFontIndirectW( &aLogFont);
+ ::SelectFont(mhDC, hUnRotatedFont);
+ }
+ WinLayout::AdjustLayout(args);
+ mpFont.replaceDC(mhDC);
+ maImpl.SetFontScale(WinLayout::mfFontScale);
+ //bool succeeded = maImpl.LayoutText(args);
+#ifdef GRCACHE
+ GrSegRecord * pSegRecord = NULL;
+ gr::Segment * pSegment = maImpl.CreateSegment(args, &pSegRecord);
+#else
+ gr::Segment * pSegment = maImpl.CreateSegment(args);
+#endif
+ bool bSucceeded = false;
+ if (pSegment)
+ {
+ // replace the DC on the font within the segment
+ ReplaceDC(*pSegment);
+ // create glyph vectors
+#ifdef GRCACHE
+ bSucceeded = maImpl.LayoutGlyphs(args, pSegment, pSegRecord);
+#else
+ bSucceeded = maImpl.LayoutGlyphs(args, pSegment);
+#endif
+ // restore original DC
+ RestoreDC(*pSegment);
+#ifdef GRCACHE
+ if (pSegRecord) pSegRecord->unlock();
+ else delete pSegment;
+#else
+ delete pSegment;
+#endif
+ }
+ mpFont.restoreDC();
+ if (args.mnOrientation)
+ {
+ // restore the rotated font
+ ::SelectFont(mhDC, mhFont);
+ ::DeleteObject(hUnRotatedFont);
+ }
+ return bSucceeded;
+}
+
+void GraphiteWinLayout::AdjustLayout(ImplLayoutArgs& rArgs)
+{
+ WinLayout::AdjustLayout(rArgs);
+ maImpl.DrawBase() = WinLayout::maDrawBase;
+ maImpl.DrawOffset() = WinLayout::maDrawOffset;
+ if ( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) && rArgs.mpDXArray)
+ {
+ mrWinFontEntry.InitKashidaHandling(mhDC);
+ }
+ maImpl.AdjustLayout(rArgs);
+}
+
+void GraphiteWinLayout::DrawText(SalGraphics &sal_graphics) const
+{
+ HFONT hOrigFont = DisableFontScaling();
+ HDC aHDC = static_cast<WinSalGraphics&>(sal_graphics).mhDC;
+ maImpl.DrawBase() = WinLayout::maDrawBase;
+ maImpl.DrawOffset() = WinLayout::maDrawOffset;
+ const int MAX_GLYPHS = 2;
+ sal_GlyphId glyphIntStr[MAX_GLYPHS];
+ WORD glyphWStr[MAX_GLYPHS];
+ int glyphIndex = 0;
+ Point aPos(0,0);
+ int nGlyphs = 0;
+ do
+ {
+ nGlyphs = maImpl.GetNextGlyphs(1, glyphIntStr, aPos, glyphIndex);
+ if (nGlyphs < 1)
+ break;
+ std::copy(glyphIntStr, glyphIntStr + nGlyphs, glyphWStr);
+ ::ExtTextOutW(aHDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX,
+ NULL, (LPCWSTR)&(glyphWStr), nGlyphs, NULL);
+ } while (nGlyphs);
+ if( hOrigFont )
+ DeleteFont( SelectFont( mhDC, hOrigFont ) );
+}
+
+int GraphiteWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
+{
+ mpFont.replaceDC(mhDC);
+ int nBreak = maImpl.GetTextBreak(nMaxWidth, nCharExtra, nFactor);
+ mpFont.restoreDC();
+ return nBreak;
+}
+
+long GraphiteWinLayout::FillDXArray( long* pDXArray ) const
+{
+ return maImpl.FillDXArray(pDXArray);
+}
+
+void GraphiteWinLayout::GetCaretPositions( int nArraySize, long* pCaretXArray ) const
+{
+ maImpl.GetCaretPositions(nArraySize, pCaretXArray);
+}
+
+int GraphiteWinLayout::GetNextGlyphs( int length, sal_GlyphId* glyph_out,
+ ::Point & pos_out, int &glyph_slot, long * glyph_adv, int *char_index) const
+{
+ maImpl.DrawBase() = WinLayout::maDrawBase;
+ maImpl.DrawOffset() = WinLayout::maDrawOffset;
+ return maImpl.GetNextGlyphs(length, glyph_out, pos_out, glyph_slot, glyph_adv, char_index);
+}
+
+void GraphiteWinLayout::MoveGlyph( int glyph_idx, long new_x_pos )
+{
+ maImpl.MoveGlyph(glyph_idx, new_x_pos);
+}
+
+void GraphiteWinLayout::DropGlyph( int glyph_idx )
+{
+ maImpl.DropGlyph(glyph_idx);
+}
+
+void GraphiteWinLayout::Simplify( bool is_base )
+{
+ maImpl.Simplify(is_base);
+}
+#endif // ENABLE_GRAPHITE
+// =======================================================================
+
+SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel )
+{
+ DBG_ASSERT( mpWinFontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL");
+
+ WinLayout* pWinLayout = NULL;
+
+ const ImplWinFontData& rFontFace = *mpWinFontData[ nFallbackLevel ];
+ ImplWinFontEntry& rFontInstance = *mpWinFontEntry[ nFallbackLevel ];
+
+#if defined( USE_UNISCRIBE )
+ if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED)
+ && (aUspModule || (bUspEnabled && InitUSP())) ) // CTL layout engine
+ {
+#ifdef ENABLE_GRAPHITE
+ if (rFontFace.SupportsGraphite())
+ pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance);
+ else
+#endif // ENABLE_GRAPHITE
+ // script complexity is determined in upper layers
+ pWinLayout = new UniscribeLayout( mhDC, rFontFace, rFontInstance );
+ // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
+ // the created UniscribeLayout, otherwise the data passed into the
+ // constructor might become invalid too early
+ }
+ else
+#endif // USE_UNISCRIBE
+ {
+#ifdef GCP_KERN_HACK
+ if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() )
+ {
+ // TODO: directly cache kerning info in the rFontInstance
+ // TODO: get rid of kerning methods+data in WinSalGraphics object
+ GetKernPairs( 0, NULL );
+ rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs );
+ }
+#endif // GCP_KERN_HACK
+
+ BYTE eCharSet = ANSI_CHARSET;
+ if( mpLogFont )
+ eCharSet = mpLogFont->lfCharSet;
+#ifdef ENABLE_GRAPHITE
+ if (rFontFace.SupportsGraphite())
+ pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance);
+ else
+#endif // ENABLE_GRAPHITE
+ pWinLayout = new SimpleWinLayout( mhDC, eCharSet, rFontFace, rFontInstance );
+ }
+
+ if( mfFontScale != 1.0 )
+ pWinLayout->SetFontScale( mfFontScale );
+
+ return pWinLayout;
+}
+
+// -----------------------------------------------------------------------
+
+int WinSalGraphics::GetMinKashidaWidth()
+{
+ if( !mpWinFontEntry[0] )
+ return 0;
+ mpWinFontEntry[0]->InitKashidaHandling( mhDC );
+ int nMinKashida = static_cast<int>(mfFontScale * mpWinFontEntry[0]->GetMinKashidaWidth());
+ return nMinKashida;
+}
+
+// =======================================================================
+
+ImplWinFontEntry::ImplWinFontEntry( ImplFontSelectData& rFSD )
+: ImplFontEntry( rFSD )
+, maWidthMap( 512 )
+, mpKerningPairs( NULL )
+, mnKerningPairs( -1 )
+, mnMinKashidaWidth( -1 )
+, mnMinKashidaGlyph( -1 )
+{
+#ifdef USE_UNISCRIBE
+ maScriptCache = NULL;
+#endif // USE_UNISCRIBE
+}
+
+// -----------------------------------------------------------------------
+
+ImplWinFontEntry::~ImplWinFontEntry()
+{
+#ifdef USE_UNISCRIBE
+ if( maScriptCache != NULL )
+ (*pScriptFreeCache)( &maScriptCache );
+#endif // USE_UNISCRIBE
+#ifdef GCP_KERN_HACK
+ delete[] mpKerningPairs;
+#endif // GCP_KERN_HACK
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplWinFontEntry::HasKernData() const
+{
+ return (mnKerningPairs >= 0);
+}
+
+// -----------------------------------------------------------------------
+
+void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData )
+{
+ mnKerningPairs = nPairCount;
+ mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ];
+ ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) );
+}
+
+// -----------------------------------------------------------------------
+
+int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const
+{
+ int nKernAmount = 0;
+ if( mpKerningPairs )
+ {
+ const KERNINGPAIR aRefPair = { cLeft, cRight, 0 };
+ const KERNINGPAIR* pFirstPair = mpKerningPairs;
+ const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs;
+ const KERNINGPAIR* pPair = std::lower_bound( pFirstPair,
+ pEndPair, aRefPair, ImplCmpKernData );
+ if( (pPair != pEndPair)
+ && (pPair->wFirst == aRefPair.wFirst)
+ && (pPair->wSecond == aRefPair.wSecond) )
+ nKernAmount = pPair->iKernAmount;
+ }
+
+ return nKernAmount;
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplWinFontEntry::InitKashidaHandling( HDC hDC )
+{
+ if( mnMinKashidaWidth >= 0 ) // already cached?
+ return mnMinKashidaWidth;
+
+ // initialize the kashida width
+ mnMinKashidaWidth = 0;
+ mnMinKashidaGlyph = 0;
+#ifdef USE_UNISCRIBE
+ if (aUspModule || (bUspEnabled && InitUSP()))
+ {
+ SCRIPT_FONTPROPERTIES aFontProperties;
+ aFontProperties.cBytes = sizeof (aFontProperties);
+ SCRIPT_CACHE& rScriptCache = GetScriptCache();
+ HRESULT nRC = (*pScriptGetFontProperties)( hDC, &rScriptCache, &aFontProperties );
+ if( nRC != 0 )
+ return false;
+ mnMinKashidaWidth = aFontProperties.iKashidaWidth;
+ mnMinKashidaGlyph = aFontProperties.wgKashida;
+ }
+#endif // USE_UNISCRIBE
+
+ return true;
+}
+
+// =======================================================================
+
+ImplFontData* ImplWinFontData::Clone() const
+{
+ if( mpUnicodeMap )
+ mpUnicodeMap->AddReference();
+ ImplFontData* pClone = new ImplWinFontData( *this );
+ return pClone;
+}
+
+// -----------------------------------------------------------------------
+
+ImplFontEntry* ImplWinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
+{
+ ImplFontEntry* pEntry = new ImplWinFontEntry( rFSD );
+ return pEntry;
+}
+
+// =======================================================================
diff --git a/vcl/win/source/gdi/wntgdi.cxx b/vcl/win/source/gdi/wntgdi.cxx
new file mode 100644
index 000000000000..eb53fb4d8699
--- /dev/null
+++ b/vcl/win/source/gdi/wntgdi.cxx
@@ -0,0 +1,67 @@
+/*************************************************************************
+ *
+ * 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"
+
+#if defined _MSC_VER
+#pragma warning(push, 1)
+#endif
+#include <windows.h>
+#if defined _MSC_VER
+#pragma warning(pop)
+#endif
+
+// -----------------------------------------------------------------------
+
+extern "C"
+{
+BOOL WINAPI WIN_Rectangle( HDC hDC, int X1, int Y1, int X2, int Y2 )
+{
+ return Rectangle( hDC, X1, Y1, X2, Y2 );
+}
+}
+
+// -----------------------------------------------------------------------
+
+extern "C"
+{
+BOOL WINAPI WIN_Polygon( HDC hDC, CONST POINT * ppt, int ncnt )
+{
+ return Polygon( hDC, ppt, ncnt );
+}
+}
+
+// -----------------------------------------------------------------------
+
+extern "C"
+{
+BOOL WINAPI WIN_PolyPolygon( HDC hDC, CONST POINT * ppt, LPINT npcnt, int ncnt )
+{
+ return PolyPolygon( hDC, ppt, npcnt, ncnt );
+}
+}