/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include "emfwr.hxx" #include #include #include #include #include #include #include #include #include #define WIN_EMR_POLYGON 3 #define WIN_EMR_POLYLINE 4 #define WIN_EMR_POLYBEZIERTO 5 #define WIN_EMR_POLYLINETO 6 #define WIN_EMR_POLYPOLYGON 8 #define WIN_EMR_SETWINDOWEXTEX 9 #define WIN_EMR_SETWINDOWORGEX 10 #define WIN_EMR_SETVIEWPORTEXTEX 11 #define WIN_EMR_SETVIEWPORTORGEX 12 #define WIN_EMR_EOF 14 #define WIN_EMR_SETPIXELV 15 #define WIN_EMR_SETMAPMODE 17 #define WIN_EMR_SETBKMODE 18 #define WIN_EMR_SETROP2 20 #define WIN_EMR_SETTEXTALIGN 22 #define WIN_EMR_SETTEXTCOLOR 24 #define WIN_EMR_MOVETOEX 27 #define WIN_EMR_INTERSECTCLIPRECT 30 #define WIN_EMR_SAVEDC 33 #define WIN_EMR_RESTOREDC 34 #define WIN_EMR_SELECTOBJECT 37 #define WIN_EMR_CREATEPEN 38 #define WIN_EMR_CREATEBRUSHINDIRECT 39 #define WIN_EMR_DELETEOBJECT 40 #define WIN_EMR_ELLIPSE 42 #define WIN_EMR_RECTANGLE 43 #define WIN_EMR_ROUNDRECT 44 #define WIN_EMR_LINETO 54 #define WIN_EMR_BEGINPATH 59 #define WIN_EMR_ENDPATH 60 #define WIN_EMR_CLOSEFIGURE 61 #define WIN_EMR_FILLPATH 62 #define WIN_EMR_STROKEPATH 64 #define WIN_EMR_GDICOMMENT 70 #define WIN_EMR_STRETCHDIBITS 81 #define WIN_EMR_EXTCREATEFONTINDIRECTW 82 #define WIN_EMR_EXTTEXTOUTW 84 #define WIN_SRCCOPY 0x00CC0020L #define WIN_SRCPAINT 0x00EE0086L #define WIN_SRCAND 0x008800C6L #define WIN_SRCINVERT 0x00660046L #define WIN_EMR_COMMENT_EMFPLUS 0x2B464D45L #define HANDLE_INVALID 0xffffffff #define MAXHANDLES 65000 #define LINE_SELECT 0x00000001 #define FILL_SELECT 0x00000002 #define TEXT_SELECT 0x00000004 /* Text Alignment Options */ #define TA_RIGHT 2 #define TA_TOP 0 #define TA_BOTTOM 8 #define TA_BASELINE 24 #define TA_RTLREADING 256 #define MM_ANISOTROPIC 8 enum class EmfPlusRecordType { Header = 0x4001, EndOfFile = 0x4002, GetDC = 0x4004, FillPolygon = 0x400C, SetAntiAliasMode = 0x401E, SetInterpolationMode = 0x4021, SetPixelOffsetMode = 0x4022, SetCompositingQuality = 0x4024 }; void EMFWriter::ImplBeginCommentRecord( sal_Int32 nCommentType ) { ImplBeginRecord( WIN_EMR_GDICOMMENT ); m_rStm.SeekRel( 4 ); m_rStm.WriteInt32( nCommentType ); } void EMFWriter::ImplEndCommentRecord() { if( mbRecordOpen ) { sal_Int32 nActPos = m_rStm.Tell(); m_rStm.Seek( mnRecordPos + 8 ); m_rStm.WriteUInt32( nActPos - mnRecordPos - 0xc ); m_rStm.Seek( nActPos ); } ImplEndRecord(); } void EMFWriter::ImplBeginPlusRecord( EmfPlusRecordType nType, sal_uInt16 nFlags ) { SAL_WARN_IF( mbRecordPlusOpen, "vcl", "Another EMF+ record is already opened!" ); if( !mbRecordPlusOpen ) { mbRecordPlusOpen = true; mnRecordPlusPos = m_rStm.Tell(); m_rStm.WriteUInt16( static_cast(nType) ).WriteUInt16( nFlags ); m_rStm.SeekRel( 8 ); } } void EMFWriter::ImplEndPlusRecord() { SAL_WARN_IF( !mbRecordPlusOpen, "vcl", "EMF+ Record was not opened!" ); if( mbRecordPlusOpen ) { sal_Int32 nActPos = m_rStm.Tell(); sal_Int32 nSize = nActPos - mnRecordPlusPos; m_rStm.Seek( mnRecordPlusPos + 4 ); m_rStm.WriteUInt32( nSize ) // Size .WriteUInt32( nSize - 0xc ); // Data Size m_rStm.Seek( nActPos ); mbRecordPlusOpen = false; } } void EMFWriter::ImplPlusRecord( EmfPlusRecordType nType, sal_uInt16 nFlags ) { ImplBeginPlusRecord( nType, nFlags ); ImplEndPlusRecord(); } void EMFWriter::WriteEMFPlusHeader( const Size &rMtfSizePix, const Size &rMtfSizeLog ) { ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS ); sal_Int32 nDPIX = rMtfSizePix.Width()*25; sal_Int32 nDivX = rMtfSizeLog.Width()/100; if (nDivX) nDPIX /= nDivX; // DPI X sal_Int32 nDPIY = rMtfSizePix.Height()*25; sal_Int32 nDivY = rMtfSizeLog.Height()/100; if (nDivY) nDPIY /= nDivY; // DPI Y m_rStm.WriteInt16( sal_Int16(EmfPlusRecordType::Header) ); m_rStm.WriteInt16( 0x01 ) // Flags - Dual Mode // TODO: Check this .WriteInt32( 0x1C ) // Size .WriteInt32( 0x10 ) // Data Size .WriteInt32( 0xdbc01002 ) // (lower 12bits) 1-> v1 2-> v1.1 // TODO: Check this .WriteInt32( 0x01 ) // Video display .WriteInt32( nDPIX ) .WriteInt32( nDPIY ); ImplEndCommentRecord(); // Write more properties ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS ); ImplPlusRecord( EmfPlusRecordType::SetPixelOffsetMode, 0x0 ); ImplPlusRecord( EmfPlusRecordType::SetAntiAliasMode, 0x09 ); // TODO: Check actual values for AntiAlias ImplPlusRecord( EmfPlusRecordType::SetCompositingQuality, 0x0100 ); // Default Quality ImplPlusRecord( EmfPlusRecordType::SetInterpolationMode, 0x00 ); // Default ImplPlusRecord( EmfPlusRecordType::GetDC, 0x00 ); ImplEndCommentRecord(); } void EMFWriter::ImplWritePlusEOF() { ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS ); ImplPlusRecord( EmfPlusRecordType::EndOfFile, 0x0 ); ImplEndCommentRecord(); } void EMFWriter::ImplWritePlusColor( const Color& rColor, sal_uInt32 nTrans ) { sal_uInt32 nAlpha = ((100-nTrans)*0xFF)/100; sal_uInt32 nCol = rColor.GetBlue(); nCol |= static_cast(rColor.GetGreen()) << 8; nCol |= static_cast(rColor.GetRed()) << 16; nCol |= ( nAlpha << 24 ); m_rStm.WriteUInt32( nCol ); } void EMFWriter::ImplWritePlusPoint( const Point& rPoint ) { // Convert to pixels const Point aPoint(maVDev->LogicToPixel( rPoint, maDestMapMode )); m_rStm.WriteUInt16( aPoint.X() ).WriteUInt16( aPoint.Y() ); } void EMFWriter::ImplWritePlusFillPolygonRecord( const tools::Polygon& rPoly, sal_uInt32 nTrans ) { ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS ); if( rPoly.GetSize() ) { ImplBeginPlusRecord( EmfPlusRecordType::FillPolygon, 0xC000 ); // Sets the color as well ImplWritePlusColor( maVDev->GetFillColor(), nTrans ); m_rStm.WriteUInt32( rPoly.GetSize() ); for( sal_uInt16 i = 0; i < rPoly.GetSize(); i++ ) ImplWritePlusPoint( rPoly[ i ] ); ImplEndPlusRecord(); } ImplEndCommentRecord(); } bool EMFWriter::WriteEMF(const GDIMetaFile& rMtf) { const sal_uLong nHeaderPos = m_rStm.Tell(); maVDev->EnableOutput( false ); maVDev->SetMapMode( rMtf.GetPrefMapMode() ); // don't work with pixel as destination map mode -> higher resolution preferable maDestMapMode.SetMapUnit( MapUnit::Map100thMM ); mHandlesUsed = std::vector(MAXHANDLES, false); mnHandleCount = mnRecordCount = mnRecordPos = mnRecordPlusPos = 0; mbRecordOpen = mbRecordPlusOpen = false; mbLineChanged = mbFillChanged = mbTextChanged = false; mnLineHandle = mnFillHandle = mnTextHandle = HANDLE_INVALID; mnHorTextAlign = 0; const Size aMtfSizePix( maVDev->LogicToPixel( rMtf.GetPrefSize(), rMtf.GetPrefMapMode() ) ); const Size aMtfSizeLog( OutputDevice::LogicToLogic(rMtf.GetPrefSize(), rMtf.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)) ); // seek over header // use [MS-EMF 2.2.11] HeaderExtension2 Object, otherwise resulting EMF cannot be converted with GetWinMetaFileBits() m_rStm.SeekRel( 108 ); // Write EMF+ Header WriteEMFPlusHeader( aMtfSizePix, aMtfSizeLog ); // write initial values // set 100th mm map mode in EMF ImplBeginRecord( WIN_EMR_SETMAPMODE ); m_rStm.WriteInt32( MM_ANISOTROPIC ); ImplEndRecord(); ImplBeginRecord( WIN_EMR_SETVIEWPORTEXTEX ); m_rStm.WriteInt32( maVDev->GetDPIX() ).WriteInt32( maVDev->GetDPIY() ); ImplEndRecord(); ImplBeginRecord( WIN_EMR_SETWINDOWEXTEX ); m_rStm.WriteInt32( 2540 ).WriteInt32( 2540 ); ImplEndRecord(); ImplBeginRecord( WIN_EMR_SETVIEWPORTORGEX ); m_rStm.WriteInt32( 0 ).WriteInt32( 0 ); ImplEndRecord(); ImplBeginRecord( WIN_EMR_SETWINDOWORGEX ); m_rStm.WriteInt32( 0 ).WriteInt32( 0 ); ImplEndRecord(); ImplWriteRasterOp( RasterOp::OverPaint ); ImplBeginRecord( WIN_EMR_SETBKMODE ); m_rStm.WriteUInt32( 1 ); // TRANSPARENT ImplEndRecord(); // write emf data ImplWrite( rMtf ); ImplWritePlusEOF(); ImplBeginRecord( WIN_EMR_EOF ); m_rStm.WriteUInt32( 0 ) // nPalEntries .WriteUInt32( 0x10 ) // offPalEntries .WriteUInt32( 0x14 ); // nSizeLast ImplEndRecord(); // write header const sal_uLong nEndPos = m_rStm.Tell(); m_rStm.Seek( nHeaderPos ); m_rStm.WriteUInt32( 0x00000001 ).WriteUInt32( 108 ) //use [MS-EMF 2.2.11] HeaderExtension2 Object .WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( aMtfSizePix.Width() - 1 ).WriteInt32( aMtfSizePix.Height() - 1 ) .WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( aMtfSizeLog.Width() - 1 ).WriteInt32( aMtfSizeLog.Height() - 1 ) .WriteUInt32( 0x464d4520 ).WriteUInt32( 0x10000 ).WriteUInt32( nEndPos - nHeaderPos ) .WriteUInt32( mnRecordCount ).WriteUInt16( mnHandleCount + 1 ).WriteUInt16( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ) .WriteInt32( aMtfSizePix.Width() ).WriteInt32( aMtfSizePix.Height() ) .WriteInt32( aMtfSizeLog.Width() / 100 ).WriteInt32( aMtfSizeLog.Height() / 100 ) .WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ) .WriteInt32( aMtfSizeLog.Width() * 10 ).WriteInt32( aMtfSizeLog.Height() * 10 ); //use [MS-EMF 2.2.11] HeaderExtension2 Object m_rStm.Seek( nEndPos ); mHandlesUsed.clear(); return( m_rStm.GetError() == ERRCODE_NONE ); } sal_uLong EMFWriter::ImplAcquireHandle() { sal_uLong nHandle = HANDLE_INVALID; for( sal_uLong i = 0; i < mHandlesUsed.size() && ( HANDLE_INVALID == nHandle ); i++ ) { if( !mHandlesUsed[ i ] ) { mHandlesUsed[ i ] = true; if( ( nHandle = i ) == mnHandleCount ) mnHandleCount++; } } SAL_WARN_IF( nHandle == HANDLE_INVALID, "vcl", "No more handles available" ); return( nHandle != HANDLE_INVALID ? nHandle + 1 : HANDLE_INVALID ); } void EMFWriter::ImplReleaseHandle( sal_uLong nHandle ) { SAL_WARN_IF( !nHandle || ( nHandle >= mHandlesUsed.size() ), "vcl", "Handle out of range" ); mHandlesUsed[ nHandle - 1 ] = false; } void EMFWriter::ImplBeginRecord( sal_uInt32 nType ) { SAL_WARN_IF( mbRecordOpen, "vcl", "Another record is already opened!" ); if( !mbRecordOpen ) { mbRecordOpen = true; mnRecordPos = m_rStm.Tell(); m_rStm.WriteUInt32( nType ); m_rStm.SeekRel( 4 ); } } void EMFWriter::ImplEndRecord() { SAL_WARN_IF( !mbRecordOpen, "vcl", "Record was not opened!" ); if( !mbRecordOpen ) return; sal_Int32 nFillBytes, nActPos = m_rStm.Tell(); m_rStm.Seek( mnRecordPos + 4 ); nFillBytes = nActPos - mnRecordPos; nFillBytes += 3; // each record has to be dword aligned nFillBytes ^= 3; nFillBytes &= 3; m_rStm.WriteUInt32( ( nActPos - mnRecordPos ) + nFillBytes ); m_rStm.Seek( nActPos ); while( nFillBytes-- ) m_rStm.WriteUChar( 0 ); mnRecordCount++; mbRecordOpen = false; } bool EMFWriter::ImplPrepareHandleSelect( sal_uInt32& rHandle, sal_uLong nSelectType ) { if( rHandle != HANDLE_INVALID ) { sal_uInt32 nStockObject = 0x80000000; if( LINE_SELECT == nSelectType ) nStockObject |= 0x00000007; else if( FILL_SELECT == nSelectType ) nStockObject |= 0x00000001; else if( TEXT_SELECT == nSelectType ) nStockObject |= 0x0000000a; // select stock object first ImplBeginRecord( WIN_EMR_SELECTOBJECT ); m_rStm.WriteUInt32( nStockObject ); ImplEndRecord(); // destroy handle of created object ImplBeginRecord( WIN_EMR_DELETEOBJECT ); m_rStm.WriteUInt32( rHandle ); ImplEndRecord(); // mark handle as free ImplReleaseHandle( rHandle ); } rHandle = ImplAcquireHandle(); return( HANDLE_INVALID != rHandle ); } void EMFWriter::ImplCheckLineAttr() { if( mbLineChanged && ImplPrepareHandleSelect( mnLineHandle, LINE_SELECT ) ) { sal_uInt32 nStyle = maVDev->IsLineColor() ? 0 : 5; ImplBeginRecord( WIN_EMR_CREATEPEN ); m_rStm.WriteUInt32( mnLineHandle ).WriteUInt32( nStyle ).WriteUInt32( 0/*nWidth*/ ).WriteUInt32( 0/*nHeight*/ ); ImplWriteColor( maVDev->GetLineColor() ); ImplEndRecord(); ImplBeginRecord( WIN_EMR_SELECTOBJECT ); m_rStm.WriteUInt32( mnLineHandle ); ImplEndRecord(); } } void EMFWriter::ImplCheckFillAttr() { if( mbFillChanged && ImplPrepareHandleSelect( mnFillHandle, FILL_SELECT ) ) { sal_uInt32 nStyle = maVDev->IsFillColor() ? 0 : 1; ImplBeginRecord( WIN_EMR_CREATEBRUSHINDIRECT ); m_rStm.WriteUInt32( mnFillHandle ).WriteUInt32( nStyle ); ImplWriteColor( maVDev->GetFillColor() ); m_rStm.WriteUInt32( 0/*nPatternStyle*/ ); ImplEndRecord(); ImplBeginRecord( WIN_EMR_SELECTOBJECT ); m_rStm.WriteUInt32( mnFillHandle ); ImplEndRecord(); } } void EMFWriter::ImplCheckTextAttr() { if( !(mbTextChanged && ImplPrepareHandleSelect( mnTextHandle, TEXT_SELECT )) ) return; const vcl::Font& rFont = maVDev->GetFont(); const OUString& aFontName( rFont.GetFamilyName() ); sal_Int32 nWeight; sal_uInt16 i; sal_uInt8 nPitchAndFamily; // tdf#127471 adapt nFontWidth from NormedFontScaling to // Windows-like notation if used for text scaling const long nFontHeight(rFont.GetFontSize().Height()); long nFontWidth(rFont.GetFontSize().Width()); #ifndef _WIN32 const bool bFontScaledHorizontally(nFontWidth != 0 && nFontWidth != nFontHeight); if(bFontScaledHorizontally) { // tdf#127471 nFontWidth is the non-Windows NormedFontScaling, need to convert to // Windows-like notation with pre-multiplied AvgFontWidth since EMF/WMF are Windows // specific formats. const long nAverageFontWidth(rFont.GetOrCalculateAverageFontWidth()); if(nAverageFontWidth > 0) { const double fScaleFactor(static_cast(nAverageFontWidth) / static_cast(nFontHeight)); nFontWidth = static_cast(static_cast(nFontWidth) * fScaleFactor); } } #endif ImplBeginRecord( WIN_EMR_EXTCREATEFONTINDIRECTW ); m_rStm.WriteUInt32( mnTextHandle ); ImplWriteExtent( -nFontHeight ); ImplWriteExtent( nFontWidth ); m_rStm.WriteInt32( rFont.GetOrientation() ).WriteInt32( rFont.GetOrientation() ); switch( rFont.GetWeight() ) { case WEIGHT_THIN: nWeight = 100; break; case WEIGHT_ULTRALIGHT: nWeight = 200; break; case WEIGHT_LIGHT: nWeight = 300; break; case WEIGHT_SEMILIGHT: nWeight = 300; break; case WEIGHT_NORMAL: nWeight = 400; break; case WEIGHT_MEDIUM: nWeight = 500; break; case WEIGHT_SEMIBOLD: nWeight = 600; break; case WEIGHT_BOLD: nWeight = 700; break; case WEIGHT_ULTRABOLD: nWeight = 800; break; case WEIGHT_BLACK: nWeight = 900; break; default: nWeight = 0; break; } m_rStm.WriteInt32( nWeight ); m_rStm.WriteUChar( ( ITALIC_NONE == rFont.GetItalic() ) ? 0 : 1 ); m_rStm.WriteUChar( ( LINESTYLE_NONE == rFont.GetUnderline() ) ? 0 : 1 ); m_rStm.WriteUChar( ( STRIKEOUT_NONE == rFont.GetStrikeout() ) ? 0 : 1 ); m_rStm.WriteUChar( ( RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet() ) ? 2 : 0 ); m_rStm.WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ); switch( rFont.GetPitch() ) { case PITCH_FIXED: nPitchAndFamily = 0x01; break; case PITCH_VARIABLE: nPitchAndFamily = 0x02; break; default: nPitchAndFamily = 0x00; break; } switch( rFont.GetFamilyType() ) { case FAMILY_DECORATIVE: nPitchAndFamily |= 0x50; break; case FAMILY_MODERN: nPitchAndFamily |= 0x30; break; case FAMILY_ROMAN: nPitchAndFamily |= 0x10; break; case FAMILY_SCRIPT: nPitchAndFamily |= 0x40; break; case FAMILY_SWISS: nPitchAndFamily |= 0x20; break; default: break; } m_rStm.WriteUChar( nPitchAndFamily ); for( i = 0; i < 32; i++ ) m_rStm.WriteUInt16( ( i < aFontName.getLength() ) ? aFontName[ i ] : 0 ); // dummy elfFullName for( i = 0; i < 64; i++ ) m_rStm.WriteUInt16( 0 ); // dummy elfStyle for( i = 0; i < 32; i++ ) m_rStm.WriteUInt16( 0 ); // dummy elfVersion, elfStyleSize, elfMatch, elfReserved m_rStm.WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ) ; // dummy elfVendorId m_rStm.WriteUInt32( 0 ); // dummy elfCulture m_rStm.WriteUInt32( 0 ); // dummy elfPanose m_rStm.WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ); // fill record to get a record size divideable by 4 m_rStm.WriteUInt16( 0 ); ImplEndRecord(); // TextAlign sal_uInt32 nTextAlign; switch( rFont.GetAlignment() ) { case ALIGN_TOP: nTextAlign = TA_TOP; break; case ALIGN_BOTTOM: nTextAlign = TA_BOTTOM; break; default: nTextAlign = TA_BASELINE; break; } nTextAlign |= mnHorTextAlign; ImplBeginRecord( WIN_EMR_SETTEXTALIGN ); m_rStm.WriteUInt32( nTextAlign ); ImplEndRecord(); // Text color ImplBeginRecord( WIN_EMR_SETTEXTCOLOR ); ImplWriteColor( maVDev->GetTextColor() ); ImplEndRecord(); ImplBeginRecord( WIN_EMR_SELECTOBJECT ); m_rStm.WriteUInt32( mnTextHandle ); ImplEndRecord(); } void EMFWriter::ImplWriteColor( const Color& rColor ) { sal_uInt32 nCol = rColor.GetRed(); nCol |= static_cast(rColor.GetGreen()) << 8; nCol |= static_cast(rColor.GetBlue()) << 16; m_rStm.WriteUInt32( nCol ); } void EMFWriter::ImplWriteRasterOp( RasterOp eRop ) { sal_uInt32 nROP2; switch( eRop ) { case RasterOp::Invert: nROP2 = 6; break; case RasterOp::Xor: nROP2 = 7; break; default: nROP2 = 13;break; } ImplBeginRecord( WIN_EMR_SETROP2 ); m_rStm.WriteUInt32( nROP2 ); ImplEndRecord(); } void EMFWriter::ImplWriteExtent( long nExtent ) { nExtent = OutputDevice::LogicToLogic( Size( nExtent, 0 ), maVDev->GetMapMode(), maDestMapMode ).Width(); m_rStm.WriteInt32( nExtent ); } void EMFWriter::ImplWritePoint( const Point& rPoint ) { const Point aPoint( OutputDevice::LogicToLogic( rPoint, maVDev->GetMapMode(), maDestMapMode )); m_rStm.WriteInt32( aPoint.X() ).WriteInt32( aPoint.Y() ); } void EMFWriter::ImplWriteSize( const Size& rSize) { const Size aSize( OutputDevice::LogicToLogic( rSize, maVDev->GetMapMode(), maDestMapMode )); m_rStm.WriteInt32( aSize.Width() ).WriteInt32( aSize.Height() ); } void EMFWriter::ImplWriteRect( const tools::Rectangle& rRect ) { const tools::Rectangle aRect( OutputDevice::LogicToLogic ( rRect, maVDev->GetMapMode(), maDestMapMode )); auto right = aRect.IsWidthEmpty() ? aRect.Left() : aRect.Right(); auto bottom = aRect.IsHeightEmpty() ? aRect.Top() : aRect.Bottom(); m_rStm .WriteInt32( aRect.Left() ) .WriteInt32( aRect.Top() ) .WriteInt32( right ) .WriteInt32( bottom ); } void EMFWriter::ImplWritePolygonRecord( const tools::Polygon& rPoly, bool bClose ) { if( rPoly.GetSize() ) { if( rPoly.HasFlags() ) ImplWritePath( rPoly, bClose ); else { if( bClose ) ImplCheckFillAttr(); ImplCheckLineAttr(); ImplBeginRecord( bClose ? WIN_EMR_POLYGON : WIN_EMR_POLYLINE ); ImplWriteRect( rPoly.GetBoundRect() ); m_rStm.WriteUInt32( rPoly.GetSize() ); for( sal_uInt16 i = 0; i < rPoly.GetSize(); i++ ) ImplWritePoint( rPoly[ i ] ); ImplEndRecord(); } } } void EMFWriter::ImplWritePolyPolygonRecord( const tools::PolyPolygon& rPolyPoly ) { sal_uInt16 n, i, nPolyCount = rPolyPoly.Count(); if( nPolyCount ) { if( 1 == nPolyCount ) ImplWritePolygonRecord( rPolyPoly[ 0 ], true ); else { bool bHasFlags = false; sal_uInt32 nTotalPoints = 0; for( i = 0; i < nPolyCount; i++ ) { nTotalPoints += rPolyPoly[ i ].GetSize(); if ( rPolyPoly[ i ].HasFlags() ) bHasFlags = true; } if( nTotalPoints ) { if ( bHasFlags ) ImplWritePath( rPolyPoly, true ); else { ImplCheckFillAttr(); ImplCheckLineAttr(); ImplBeginRecord( WIN_EMR_POLYPOLYGON ); ImplWriteRect( rPolyPoly.GetBoundRect() ); m_rStm.WriteUInt32( nPolyCount ).WriteUInt32( nTotalPoints ); for( i = 0; i < nPolyCount; i++ ) m_rStm.WriteUInt32( rPolyPoly[ i ].GetSize() ); for( i = 0; i < nPolyCount; i++ ) { const tools::Polygon& rPoly = rPolyPoly[ i ]; for( n = 0; n < rPoly.GetSize(); n++ ) ImplWritePoint( rPoly[ n ] ); } ImplEndRecord(); } } } } } void EMFWriter::ImplWritePath( const tools::PolyPolygon& rPolyPoly, bool bClosed ) { if ( bClosed ) ImplCheckFillAttr(); ImplCheckLineAttr(); ImplBeginRecord( WIN_EMR_BEGINPATH ); ImplEndRecord(); sal_uInt16 i, n, o, nPolyCount = rPolyPoly.Count(); for ( i = 0; i < nPolyCount; i++ ) { n = 0; const tools::Polygon& rPoly = rPolyPoly[ i ]; while ( n < rPoly.GetSize() ) { if( n == 0 ) { ImplBeginRecord( WIN_EMR_MOVETOEX ); ImplWritePoint( rPoly[ 0 ] ); ImplEndRecord(); n++; continue; } sal_uInt16 nBezPoints = 0; while ( ( ( nBezPoints + n + 2 ) < rPoly.GetSize() ) && ( rPoly.GetFlags( nBezPoints + n ) == PolyFlags::Control ) ) nBezPoints += 3; if ( nBezPoints ) { ImplBeginRecord( WIN_EMR_POLYBEZIERTO ); tools::Polygon aNewPoly( nBezPoints + 1 ); aNewPoly[ 0 ] = rPoly[ n - 1 ]; for ( o = 0; o < nBezPoints; o++ ) aNewPoly[ o + 1 ] = rPoly[ n + o ]; ImplWriteRect( aNewPoly.GetBoundRect() ); m_rStm.WriteUInt32( nBezPoints ); for( o = 1; o < aNewPoly.GetSize(); o++ ) ImplWritePoint( aNewPoly[ o ] ); ImplEndRecord(); n = n + nBezPoints; } else { sal_uInt16 nPoints = 1; while( ( nPoints + n ) < rPoly.GetSize() && ( rPoly.GetFlags( nPoints + n ) != PolyFlags::Control ) ) nPoints++; if ( nPoints > 1 ) { ImplBeginRecord( WIN_EMR_POLYLINETO ); tools::Polygon aNewPoly( nPoints + 1 ); aNewPoly[ 0 ] = rPoly[ n - 1]; for ( o = 1; o <= nPoints; o++ ) aNewPoly[ o ] = rPoly[ n - 1 + o ]; ImplWriteRect( aNewPoly.GetBoundRect() ); m_rStm.WriteUInt32( nPoints ); for( o = 1; o < aNewPoly.GetSize(); o++ ) ImplWritePoint( aNewPoly[ o ] ); ImplEndRecord(); } else { ImplBeginRecord( WIN_EMR_LINETO ); ImplWritePoint( rPoly[ n ] ); ImplEndRecord(); } n = n + nPoints; } if ( bClosed && ( n == rPoly.GetSize() ) ) { ImplBeginRecord( WIN_EMR_CLOSEFIGURE ); ImplEndRecord(); } } } ImplBeginRecord( WIN_EMR_ENDPATH ); ImplEndRecord(); ImplBeginRecord( bClosed ? WIN_EMR_FILLPATH : WIN_EMR_STROKEPATH ); ImplWriteRect( rPolyPoly.GetBoundRect() ); ImplEndRecord(); } void EMFWriter::ImplWriteBmpRecord( const Bitmap& rBmp, const Point& rPt, const Size& rSz, sal_uInt32 nROP ) { if( !rBmp ) return; SvMemoryStream aMemStm( 65535, 65535 ); const Size aBmpSizePixel( rBmp.GetSizePixel() ); ImplBeginRecord( WIN_EMR_STRETCHDIBITS ); ImplWriteRect( tools::Rectangle( rPt, rSz ) ); ImplWritePoint( rPt ); m_rStm.WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( aBmpSizePixel.Width() ).WriteInt32( aBmpSizePixel.Height() ); // write offset positions and sizes later const sal_uLong nOffPos = m_rStm.Tell(); m_rStm.SeekRel( 16 ); m_rStm.WriteUInt32( 0 ).WriteInt32( ( RasterOp::Xor == maVDev->GetRasterOp() && WIN_SRCCOPY == nROP ) ? WIN_SRCINVERT : nROP ); ImplWriteSize( rSz ); WriteDIB(rBmp, aMemStm, true, false); sal_uInt32 nDIBSize = aMemStm.Tell(), nHeaderSize, nCompression, nColsUsed, nPalCount, nImageSize; sal_uInt16 nBitCount; // get DIB parameters aMemStm.Seek( 0 ); aMemStm.ReadUInt32( nHeaderSize ); aMemStm.SeekRel( 10 ); aMemStm.ReadUInt16( nBitCount ).ReadUInt32( nCompression ).ReadUInt32( nImageSize ); aMemStm.SeekRel( 8 ); aMemStm.ReadUInt32( nColsUsed ); if (nBitCount <= 8) { if (nColsUsed) nPalCount = nColsUsed; else nPalCount = 1 << static_cast(nBitCount); } else { if (nCompression == BITFIELDS) nPalCount = 3; else nPalCount = 0; } sal_uInt32 nPalSize = nPalCount * 4; m_rStm.WriteBytes( aMemStm.GetData(), nDIBSize ); const sal_uLong nEndPos = m_rStm.Tell(); m_rStm.Seek( nOffPos ); m_rStm.WriteUInt32( 80 ).WriteUInt32( nHeaderSize + nPalSize ); m_rStm.WriteUInt32( 80 + nHeaderSize + nPalSize ).WriteUInt32( nImageSize ); m_rStm.Seek( nEndPos ); ImplEndRecord(); } void EMFWriter::ImplWriteTextRecord( const Point& rPos, const OUString& rText, const long* pDXArray, sal_uInt32 nWidth ) { sal_Int32 nLen = rText.getLength(), i; if( !nLen ) return; sal_uInt32 nNormWidth; std::unique_ptr pOwnArray; long* pDX; // get text sizes if( pDXArray ) { nNormWidth = maVDev->GetTextWidth( rText ); pDX = const_cast(pDXArray); } else { pOwnArray.reset(new long[ nLen ]); nNormWidth = maVDev->GetTextArray( rText, pOwnArray.get() ); pDX = pOwnArray.get(); } if( nLen > 1 ) { nNormWidth = pDX[ nLen - 2 ] + maVDev->GetTextWidth( OUString(rText[ nLen - 1 ]) ); if( nWidth && nNormWidth && ( nWidth != nNormWidth ) ) { const double fFactor = static_cast(nWidth) / nNormWidth; for( i = 0; i < ( nLen - 1 ); i++ ) pDX[ i ] = FRound( pDX[ i ] * fFactor ); } } // write text record ImplBeginRecord( WIN_EMR_EXTTEXTOUTW ); ImplWriteRect( tools::Rectangle( rPos, Size( nNormWidth, maVDev->GetTextHeight() ) ) ); m_rStm.WriteUInt32( 1 ); m_rStm.WriteInt32( 0 ).WriteInt32( 0 ); ImplWritePoint( rPos ); m_rStm.WriteUInt32( nLen ).WriteUInt32( 76 ).WriteUInt32( 2 ); m_rStm.WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( 0 ); m_rStm.WriteUInt32( 76 + ( nLen << 1 ) + ( (nLen & 1 ) ? 2 : 0 ) ); // write text for( i = 0; i < nLen; i++ ) m_rStm.WriteUInt16( rText[ i ] ); // padding word if( nLen & 1 ) m_rStm.WriteUInt16( 0 ); // write DX array ImplWriteExtent( pDX[ 0 ] ); if( nLen > 1 ) { for( i = 1; i < ( nLen - 1 ); i++ ) ImplWriteExtent( pDX[ i ] - pDX[ i - 1 ] ); ImplWriteExtent( pDX[ nLen - 2 ] / ( nLen - 1 ) ); } ImplEndRecord(); } void EMFWriter::Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon) { if(rLinePolygon.count()) { basegfx::B2DPolyPolygon aLinePolyPolygon(rLinePolygon); basegfx::B2DPolyPolygon aFillPolyPolygon; rInfo.applyToB2DPolyPolygon(aLinePolyPolygon, aFillPolyPolygon); if(aLinePolyPolygon.count()) { for(auto const& rB2DPolygon : aLinePolyPolygon) { ImplWritePolygonRecord( tools::Polygon(rB2DPolygon), false ); } } if(aFillPolyPolygon.count()) { const Color aOldLineColor(maVDev->GetLineColor()); const Color aOldFillColor(maVDev->GetFillColor()); maVDev->SetLineColor(); maVDev->SetFillColor(aOldLineColor); for(auto const& rB2DPolygon : aFillPolyPolygon) { ImplWritePolyPolygonRecord(tools::PolyPolygon( tools::Polygon(rB2DPolygon) )); } maVDev->SetLineColor(aOldLineColor); maVDev->SetFillColor(aOldFillColor); } } } void EMFWriter::ImplWrite( const GDIMetaFile& rMtf ) { for( size_t j = 0, nActionCount = rMtf.GetActionSize(); j < nActionCount; j++ ) { const MetaAction* pAction = rMtf.GetAction( j ); const MetaActionType nType = pAction->GetType(); switch( nType ) { case MetaActionType::PIXEL: { const MetaPixelAction* pA = static_cast(pAction); ImplCheckLineAttr(); ImplBeginRecord( WIN_EMR_SETPIXELV ); ImplWritePoint( pA->GetPoint() ); ImplWriteColor( pA->GetColor() ); ImplEndRecord(); } break; case MetaActionType::POINT: { if( maVDev->IsLineColor() ) { const MetaPointAction* pA = static_cast(pAction); ImplCheckLineAttr(); ImplBeginRecord( WIN_EMR_SETPIXELV ); ImplWritePoint( pA->GetPoint() ); ImplWriteColor( maVDev->GetLineColor() ); ImplEndRecord(); } } break; case MetaActionType::LINE: { if( maVDev->IsLineColor() ) { const MetaLineAction* pA = static_cast(pAction); if(pA->GetLineInfo().IsDefault()) { ImplCheckLineAttr(); ImplBeginRecord( WIN_EMR_MOVETOEX ); ImplWritePoint( pA->GetStartPoint() ); ImplEndRecord(); ImplBeginRecord( WIN_EMR_LINETO ); ImplWritePoint( pA->GetEndPoint() ); ImplEndRecord(); ImplBeginRecord( WIN_EMR_SETPIXELV ); ImplWritePoint( pA->GetEndPoint() ); ImplWriteColor( maVDev->GetLineColor() ); ImplEndRecord(); } else { // LineInfo used; handle Dash/Dot and fat lines basegfx::B2DPolygon aPolygon; aPolygon.append(basegfx::B2DPoint(pA->GetStartPoint().X(), pA->GetStartPoint().Y())); aPolygon.append(basegfx::B2DPoint(pA->GetEndPoint().X(), pA->GetEndPoint().Y())); Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon); } } } break; case MetaActionType::RECT: { if( maVDev->IsLineColor() || maVDev->IsFillColor() ) { const MetaRectAction* pA = static_cast(pAction); ImplCheckFillAttr(); ImplCheckLineAttr(); ImplBeginRecord( WIN_EMR_RECTANGLE ); ImplWriteRect( pA->GetRect() ); ImplEndRecord(); } } break; case MetaActionType::ROUNDRECT: { if( maVDev->IsLineColor() || maVDev->IsFillColor() ) { const MetaRoundRectAction* pA = static_cast(pAction); ImplCheckFillAttr(); ImplCheckLineAttr(); ImplBeginRecord( WIN_EMR_ROUNDRECT ); ImplWriteRect( pA->GetRect() ); ImplWriteSize( Size( pA->GetHorzRound(), pA->GetVertRound() ) ); ImplEndRecord(); } } break; case MetaActionType::ELLIPSE: { if( maVDev->IsLineColor() || maVDev->IsFillColor() ) { const MetaEllipseAction* pA = static_cast(pAction); ImplCheckFillAttr(); ImplCheckLineAttr(); ImplBeginRecord( WIN_EMR_ELLIPSE ); ImplWriteRect( pA->GetRect() ); ImplEndRecord(); } } break; case MetaActionType::ARC: case MetaActionType::PIE: case MetaActionType::CHORD: case MetaActionType::POLYGON: { if( maVDev->IsLineColor() || maVDev->IsFillColor() ) { tools::Polygon aPoly; switch( nType ) { case MetaActionType::ARC: { const MetaArcAction* pA = static_cast(pAction); aPoly = tools::Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Arc ); } break; case MetaActionType::PIE: { const MetaPieAction* pA = static_cast(pAction); aPoly = tools::Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Pie ); } break; case MetaActionType::CHORD: { const MetaChordAction* pA = static_cast(pAction); aPoly = tools::Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Chord ); } break; case MetaActionType::POLYGON: aPoly = static_cast(pAction)->GetPolygon(); break; default: break; } ImplWritePolygonRecord( aPoly, nType != MetaActionType::ARC ); } } break; case MetaActionType::POLYLINE: { if( maVDev->IsLineColor() ) { const MetaPolyLineAction* pA = static_cast(pAction); const tools::Polygon& rPoly = pA->GetPolygon(); if( rPoly.GetSize() ) { if(pA->GetLineInfo().IsDefault()) { ImplWritePolygonRecord( rPoly, false ); } else { // LineInfo used; handle Dash/Dot and fat lines Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon()); } } } } break; case MetaActionType::POLYPOLYGON: { if( maVDev->IsLineColor() || maVDev->IsFillColor() ) ImplWritePolyPolygonRecord( static_cast(pAction)->GetPolyPolygon() ); } break; case MetaActionType::GRADIENT: { const MetaGradientAction* pA = static_cast(pAction); GDIMetaFile aTmpMtf; maVDev->AddGradientActions( pA->GetRect(), pA->GetGradient(), aTmpMtf ); ImplWrite( aTmpMtf ); } break; case MetaActionType::HATCH: { const MetaHatchAction* pA = static_cast(pAction); GDIMetaFile aTmpMtf; maVDev->AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf ); ImplWrite( aTmpMtf ); } break; case MetaActionType::Transparent: { const tools::PolyPolygon& rPolyPoly = static_cast(pAction)->GetPolyPolygon(); if( rPolyPoly.Count() ) ImplWritePlusFillPolygonRecord( rPolyPoly[0], static_cast(pAction)->GetTransparence() ); ImplCheckFillAttr(); ImplCheckLineAttr(); ImplWritePolyPolygonRecord( rPolyPoly ); ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS ); ImplPlusRecord( EmfPlusRecordType::GetDC, 0x00 ); ImplEndCommentRecord(); } break; case MetaActionType::FLOATTRANSPARENT: { const MetaFloatTransparentAction* pA = static_cast(pAction); GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() ); Point aSrcPt( aTmpMtf.GetPrefMapMode().GetOrigin() ); const Size aSrcSize( aTmpMtf.GetPrefSize() ); const Point aDestPt( pA->GetPoint() ); const Size aDestSize( pA->GetSize() ); const double fScaleX = aSrcSize.Width() ? static_cast(aDestSize.Width()) / aSrcSize.Width() : 1.0; const double fScaleY = aSrcSize.Height() ? static_cast(aDestSize.Height()) / aSrcSize.Height() : 1.0; long nMoveX, nMoveY; if( fScaleX != 1.0 || fScaleY != 1.0 ) { aTmpMtf.Scale( fScaleX, fScaleY ); aSrcPt.setX( FRound( aSrcPt.X() * fScaleX ) ); aSrcPt.setY( FRound( aSrcPt.Y() * fScaleY ) ); } nMoveX = aDestPt.X() - aSrcPt.X(); nMoveY = aDestPt.Y() - aSrcPt.Y(); if( nMoveX || nMoveY ) aTmpMtf.Move( nMoveX, nMoveY ); ImplCheckFillAttr(); ImplCheckLineAttr(); ImplCheckTextAttr(); ImplWrite( aTmpMtf ); } break; case MetaActionType::EPS: { const MetaEPSAction* pA = static_cast(pAction); const GDIMetaFile& aSubstitute( pA->GetSubstitute() ); for( size_t i = 0, nCount = aSubstitute.GetActionSize(); i < nCount; i++ ) { const MetaAction* pSubstAct = aSubstitute.GetAction( i ); if( pSubstAct->GetType() == MetaActionType::BMPSCALE ) { maVDev->Push(); ImplBeginRecord( WIN_EMR_SAVEDC ); ImplEndRecord(); MapMode aMapMode( aSubstitute.GetPrefMapMode() ); Size aOutSize( OutputDevice::LogicToLogic( pA->GetSize(), maVDev->GetMapMode(), aMapMode ) ); aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) ); aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) ); aMapMode.SetOrigin( OutputDevice::LogicToLogic( pA->GetPoint(), maVDev->GetMapMode(), aMapMode ) ); maVDev->SetMapMode( aMapMode ); ImplWrite( aSubstitute ); maVDev->Pop(); ImplBeginRecord( WIN_EMR_RESTOREDC ); m_rStm.WriteInt32( -1 ); ImplEndRecord(); break; } } } break; case MetaActionType::BMP: { const MetaBmpAction* pA = static_cast(pAction); ImplWriteBmpRecord( pA->GetBitmap(), pA->GetPoint(), maVDev->PixelToLogic( pA->GetBitmap().GetSizePixel() ), WIN_SRCCOPY ); } break; case MetaActionType::BMPSCALE: { const MetaBmpScaleAction* pA = static_cast(pAction); ImplWriteBmpRecord( pA->GetBitmap(), pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY ); } break; case MetaActionType::BMPSCALEPART: { const MetaBmpScalePartAction* pA = static_cast(pAction); Bitmap aTmp( pA->GetBitmap() ); if( aTmp.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) ) ) ImplWriteBmpRecord( aTmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCCOPY ); } break; case MetaActionType::BMPEX: { const MetaBmpExAction* pA = static_cast(pAction); Bitmap aBmp( pA->GetBitmapEx().GetBitmap() ); Bitmap aMsk( pA->GetBitmapEx().GetMask() ); if( !!aMsk ) { aBmp.Replace( aMsk, COL_WHITE ); aMsk.Invert(); ImplWriteBmpRecord( aMsk, pA->GetPoint(), maVDev->PixelToLogic( aMsk.GetSizePixel() ), WIN_SRCPAINT ); ImplWriteBmpRecord( aBmp, pA->GetPoint(), maVDev->PixelToLogic( aBmp.GetSizePixel() ), WIN_SRCAND ); } else ImplWriteBmpRecord( aBmp, pA->GetPoint(), aBmp.GetSizePixel(), WIN_SRCCOPY ); } break; case MetaActionType::BMPEXSCALE: { const MetaBmpExScaleAction* pA = static_cast(pAction); Bitmap aBmp( pA->GetBitmapEx().GetBitmap() ); Bitmap aMsk( pA->GetBitmapEx().GetMask() ); if( !!aMsk ) { aBmp.Replace( aMsk, COL_WHITE ); aMsk.Invert(); ImplWriteBmpRecord( aMsk, pA->GetPoint(), pA->GetSize(), WIN_SRCPAINT ); ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCAND ); } else ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY ); } break; case MetaActionType::BMPEXSCALEPART: { const MetaBmpExScalePartAction* pA = static_cast(pAction); BitmapEx aBmpEx( pA->GetBitmapEx() ); aBmpEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) ); Bitmap aBmp( aBmpEx.GetBitmap() ); Bitmap aMsk( aBmpEx.GetMask() ); if( !!aMsk ) { aBmp.Replace( aMsk, COL_WHITE ); aMsk.Invert(); ImplWriteBmpRecord( aMsk, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCPAINT ); ImplWriteBmpRecord( aBmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCAND ); } else ImplWriteBmpRecord( aBmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCCOPY ); } break; case MetaActionType::TEXT: { const MetaTextAction* pA = static_cast(pAction); const OUString aText = pA->GetText().copy( pA->GetIndex(), std::min(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) ); ImplCheckTextAttr(); ImplWriteTextRecord( pA->GetPoint(), aText, nullptr, 0 ); } break; case MetaActionType::TEXTRECT: { const MetaTextRectAction* pA = static_cast(pAction); const OUString& aText( pA->GetText() ); ImplCheckTextAttr(); ImplWriteTextRecord( pA->GetRect().TopLeft(), aText, nullptr, 0 ); } break; case MetaActionType::TEXTARRAY: { const MetaTextArrayAction* pA = static_cast(pAction); const OUString aText = pA->GetText().copy( pA->GetIndex(), std::min(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) ); ImplCheckTextAttr(); ImplWriteTextRecord( pA->GetPoint(), aText, pA->GetDXArray(), 0 ); } break; case MetaActionType::STRETCHTEXT: { const MetaStretchTextAction* pA = static_cast(pAction); const OUString aText = pA->GetText().copy( pA->GetIndex(), std::min(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) ); ImplCheckTextAttr(); ImplWriteTextRecord( pA->GetPoint(), aText, nullptr, pA->GetWidth() ); } break; case MetaActionType::LINECOLOR: { const_cast(pAction)->Execute( maVDev ); mbLineChanged = true; } break; case MetaActionType::FILLCOLOR: { const_cast(pAction)->Execute( maVDev ); mbFillChanged = true; } break; case MetaActionType::TEXTCOLOR: case MetaActionType::TEXTLINECOLOR: case MetaActionType::TEXTFILLCOLOR: case MetaActionType::TEXTALIGN: case MetaActionType::FONT: { const_cast(pAction)->Execute( maVDev ); mbTextChanged = true; } break; case MetaActionType::ISECTRECTCLIPREGION: { const_cast(pAction)->Execute( maVDev ); ImplBeginRecord( WIN_EMR_INTERSECTCLIPRECT ); ImplWriteRect( static_cast(pAction)->GetRect() ); ImplEndRecord(); } break; case MetaActionType::CLIPREGION: case MetaActionType::ISECTREGIONCLIPREGION: case MetaActionType::MOVECLIPREGION: { const_cast(pAction)->Execute( maVDev ); } break; case MetaActionType::REFPOINT: case MetaActionType::MAPMODE: const_cast(pAction)->Execute( maVDev ); break; case MetaActionType::PUSH: { const_cast(pAction)->Execute( maVDev ); ImplBeginRecord( WIN_EMR_SAVEDC ); ImplEndRecord(); } break; case MetaActionType::POP: { const_cast(pAction)->Execute( maVDev ); ImplBeginRecord( WIN_EMR_RESTOREDC ); m_rStm.WriteInt32( -1 ); ImplEndRecord(); ImplWriteRasterOp( maVDev->GetRasterOp() ); mbLineChanged = mbFillChanged = mbTextChanged = true; } break; case MetaActionType::RASTEROP: { const_cast(pAction)->Execute( maVDev ); ImplWriteRasterOp( static_cast(pAction)->GetRasterOp() ); } break; case MetaActionType::LAYOUTMODE: { ComplexTextLayoutFlags nLayoutMode = static_cast(pAction)->GetLayoutMode(); mnHorTextAlign = 0; if ((nLayoutMode & ComplexTextLayoutFlags::BiDiRtl) != ComplexTextLayoutFlags::Default) { mnHorTextAlign = TA_RIGHT | TA_RTLREADING; } if ((nLayoutMode & ComplexTextLayoutFlags::TextOriginRight) != ComplexTextLayoutFlags::Default) mnHorTextAlign |= TA_RIGHT; else if ((nLayoutMode & ComplexTextLayoutFlags::TextOriginLeft) != ComplexTextLayoutFlags::Default) mnHorTextAlign &= ~TA_RIGHT; break; } case MetaActionType::COMMENT: { MetaCommentAction const*const pCommentAction( static_cast(pAction)); if (pCommentAction->GetComment() == "EMF_PLUS") { ImplBeginCommentRecord(WIN_EMR_COMMENT_EMFPLUS); m_rStm.WriteBytes(pCommentAction->GetData(), pCommentAction->GetDataSize()); ImplEndCommentRecord(); } } break; case MetaActionType::MASK: case MetaActionType::MASKSCALE: case MetaActionType::MASKSCALEPART: case MetaActionType::WALLPAPER: case MetaActionType::TEXTLINE: case MetaActionType::GRADIENTEX: // Explicitly ignored cases break; default: // TODO: Implement more cases as necessary. Let's not bother with a warning. break; } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */