/************************************************************************* * * $RCSfile: pdfwriter_impl.cxx,v $ * * $Revision: 1.54 $ * * last change: $Author: hr $ $Date: 2003-06-30 14:29:34 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses * * - GNU Lesser General Public License Version 2.1 * - Sun Industry Standards Source License Version 1.1 * * Sun Microsystems Inc., October, 2000 * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2000 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * Sun Industry Standards Source License Version 1.1 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.1 (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.openoffice.org/license.html. * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2000 by Sun Microsystems, Inc. * * All Rights Reserved. * * Contributor(s): _______________________________________ * * ************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef REMOTE_APPSERVER #include #include #else #include #endif #include #include #include #include "implncvt.hxx" #include #ifdef WNT // Aaarrgh #define M_PI 3.14159265 #endif #define ENABLE_COMPRESSION #if OSL_DEBUG_LEVEL < 2 #define COMPRESS_PAGES #endif using namespace vcl; using namespace rtl; #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL #define MARK( x ) emitComment( x ) #else #define MARK( x ) #endif static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer ) { static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] ); rBuffer.append( pHexDigits[ nInt & 15 ] ); } static void appendUnicodeTextString( const String& rString, OStringBuffer& rBuffer ) { rBuffer.append( "> 8), rBuffer ); appendHex( (sal_Int8)(aChar & 255 ), rBuffer ); } rBuffer.append( ">" ); } // appends a double. PDF does not accept exponential format, only fixed point static void appendDouble( double fValue, OStringBuffer& rBuffer, int nPrecision = 5 ) { bool bNeg = false; if( fValue < 0.0 ) { bNeg = true; fValue=-fValue; } sal_Int64 nInt = (sal_Int64)fValue; fValue -= (double)nInt; // optimizing hardware may lead to a value of 1.0 after the subtraction if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision ) { nInt++; fValue = 0.0; } sal_Int64 nFrac = 0; if( fValue ) { fValue *= pow( 10.0, (double)nPrecision ); nFrac = (sal_Int64)fValue; } if( bNeg && ( nInt || nFrac ) ) rBuffer.append( '-' ); rBuffer.append( nInt ); if( nFrac ) { int i; rBuffer.append( '.' ); sal_Int64 nBound = (sal_Int64)(pow( 10, nPrecision - 1 )+0.5); for ( i = 0; ( i < nPrecision ) && nFrac; i++ ) { sal_Int64 nNumb = nFrac / nBound; nFrac -= nNumb * nBound; rBuffer.append( nNumb ); nBound /= 10; } } } static void appendColor( const Color& rColor, OStringBuffer& rBuffer ) { if( rColor != Color( COL_TRANSPARENT ) ) { appendDouble( (double)rColor.GetRed() / 255.0, rBuffer ); rBuffer.append( ' ' ); appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer ); rBuffer.append( ' ' ); appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer ); } } static void appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) { if( rColor != Color( COL_TRANSPARENT ) ) { appendColor( rColor, rBuffer ); rBuffer.append( " RG" ); } } static void appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) { if( rColor != Color( COL_TRANSPARENT ) ) { appendColor( rColor, rBuffer ); rBuffer.append( " rg" ); } } PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ) : m_pWriter( pWriter ), m_nPageWidth( nPageWidth ), m_nPageHeight( nPageHeight ), m_eOrientation( eOrientation ), m_nPageObject( 0 ), // invalid object number m_nStreamObject( 0 ), m_nStreamLengthObject( 0 ), m_nBeginStreamPos( 0 ) { } PDFWriterImpl::PDFPage::~PDFPage() { } void PDFWriterImpl::PDFPage::beginStream() { m_nStreamObject = m_pWriter->createObject(); if( ! m_pWriter->updateObject( m_nStreamObject ) ) return; m_nStreamLengthObject = m_pWriter->createObject(); // write content stream header OStringBuffer aLine; aLine.append( m_nStreamObject ); aLine.append( " 0 obj\r\n<< /Length " ); aLine.append( m_nStreamLengthObject ); aLine.append( " 0 R\r\n" ); #if defined COMPRESS_PAGES && defined ENABLE_COMPRESSION aLine.append( " /Filter /FlateDecode\r\n" ); #endif aLine.append( ">>\r\nstream\r\n" ); if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) ) return; if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) ) { osl_closeFile( m_pWriter->m_aFile ); m_pWriter->m_bOpen = false; } #if defined COMPRESS_PAGES && defined ENABLE_COMPRESSION m_pWriter->beginCompression(); #endif } void PDFWriterImpl::PDFPage::endStream() { sal_uInt64 nEndStreamPos; if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) ) { osl_closeFile( m_pWriter->m_aFile ); m_pWriter->m_bOpen = false; return; } if( ! m_pWriter->writeBuffer( "endstream\r\nendobj\r\n\r\n", 21 ) ) return; // emit stream length object if( ! m_pWriter->updateObject( m_nStreamLengthObject ) ) return; OStringBuffer aLine; aLine.append( m_nStreamLengthObject ); aLine.append( " 0 obj\r\n " ); aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) ); aLine.append( "\r\nendobj\r\n\r\n" ); m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); } bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject ) { // emit page object m_nPageObject = m_pWriter->createObject(); if( ! m_pWriter->updateObject( m_nPageObject ) ) return false; OStringBuffer aLine; aLine.append( m_nPageObject ); aLine.append( " 0 obj\r\n" "<< /Type /Page\r\n" " /Parent " ); aLine.append( nParentObject ); aLine.append( " 0 R\r\n" ); if( m_nPageWidth && m_nPageHeight ) { aLine.append( " /MediaBox [ 0 0 " ); aLine.append( m_nPageWidth ); aLine.append( ' ' ); aLine.append( m_nPageHeight ); aLine.append( " ]\r\n" ); } switch( m_eOrientation ) { case PDFWriter::Landscape: aLine.append( " /Rotate 90\r\n" );break; case PDFWriter::Seascape: aLine.append( " /Rotate -90\r\n" );break; case PDFWriter::Portrait: aLine.append( " /Rotate 0\r\n" );break; case PDFWriter::Inherit: default: break; } aLine.append( " /Contents " ); aLine.append( m_nStreamObject ); aLine.append( " 0 R\r\n>>\r\nendobj\r\n\r\n" ); return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); } namespace vcl { template < class GEOMETRY > GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject ) { GEOMETRY aPoint; if ( MAP_PIXEL == _rSource.GetMapUnit() ) { aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest ); } else { aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest ); } return aPoint; } } void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg ) { Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, m_pWriter->m_aMapMode, m_pWriter->getReferenceDevice(), rPoint ) ); sal_Int32 nValue = aPoint.X(); if( bNeg ) nValue = -nValue; if( nValue < 0 ) { rBuffer.append( '-' ); nValue = -nValue; } sal_Int32 nInt = nValue / 10; sal_Int32 nDecimal = nValue % 10; rBuffer.append( nInt ); if( nDecimal ) { rBuffer.append( '.' ); rBuffer.append( nDecimal ); } rBuffer.append( ' ' ); nValue = 10*(m_nPageHeight ? m_nPageHeight : m_pWriter->m_nInheritedPageHeight) - aPoint.Y(); if( bNeg ) nValue = -nValue; if( nValue < 0 ) { rBuffer.append( '-' ); nValue = -nValue; } nInt = nValue / 10; nDecimal = nValue % 10; rBuffer.append( nInt ); if( nDecimal ) { rBuffer.append( '.' ); rBuffer.append( nDecimal ); } } void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) { appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer ); rBuffer.append( ' ' ); appendMappedLength( rRect.GetWidth(), rBuffer, false ); rBuffer.append( ' ' ); appendMappedLength( rRect.GetHeight(), rBuffer, true ); rBuffer.append( " re" ); } void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) { Rectangle aConvertRect( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, m_pWriter->m_aMapMode, m_pWriter->getReferenceDevice(), rRect ) ); sal_Int32 nMirror = m_nPageHeight ? m_nPageHeight : m_pWriter->m_nInheritedPageHeight; rRect = Rectangle( Point( aConvertRect.BottomLeft().X(), 10*nMirror-aConvertRect.BottomLeft().Y() ), aConvertRect.GetSize() ); } void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) { int nPoints = rPoly.GetSize(); /* * #108582# applications do weird things */ if( nPoints > 0 ) { appendPoint( rPoly[0], rBuffer ); rBuffer.append( " m\r\n" ); for( int i = 1; i < nPoints; i++ ) { appendPoint( rPoly[i], rBuffer ); rBuffer.append( " l" ); rBuffer.append( (i & 3) ? " " : "\r\n" ); } if( bClose ) rBuffer.append( "h\r\n" ); } } void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) { int nPolygons = rPolyPoly.Count(); for( int n = 0; n < nPolygons; n++ ) appendPolygon( rPolyPoly[n], rBuffer, bClose ); } void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical ) { if ( nLength < 0 ) { rBuffer.append( '-' ); nLength = -nLength; } Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, m_pWriter->m_aMapMode, m_pWriter->getReferenceDevice(), Size( nLength, nLength ) ) ); sal_Int32 nInt = ( bVertical ? aSize.Height() : aSize.Width() ) / 10; sal_Int32 nDecimal = ( bVertical ? aSize.Height() : aSize.Width() ) % 10; rBuffer.append( nInt ); if( nDecimal ) { rBuffer.append( '.' ); rBuffer.append( nDecimal ); } } void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical ) { Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, m_pWriter->m_aMapMode, m_pWriter->getReferenceDevice(), Size( 1000, 1000 ) ) ); fLength *= (double)(bVertical ? aSize.Height() : aSize.Width()) / 10000.0; appendDouble( fLength, rBuffer ); } void PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) { if( rInfo.GetStyle() == LINE_DASH ) { rBuffer.append( "[ " ); for( int n = 0; n < rInfo.GetDashCount(); n++ ) { appendMappedLength( rInfo.GetDashLen(), rBuffer ); rBuffer.append( ' ' ); appendMappedLength( rInfo.GetDistance(), rBuffer ); rBuffer.append( ' ' ); } for( int m = 0; m < rInfo.GetDotCount(); m++ ) { appendMappedLength( rInfo.GetDotLen(), rBuffer ); rBuffer.append( ' ' ); appendMappedLength( rInfo.GetDistance(), rBuffer ); rBuffer.append( ' ' ); } rBuffer.append( "] 0 d\r\n" ); } if( rInfo.GetWidth() > 1 ) { appendMappedLength( rInfo.GetWidth(), rBuffer ); rBuffer.append( " w\r\n" ); } else if( rInfo.GetWidth() == 0 ) rBuffer.append( "0 w\r\n" ); } void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) { if( nWidth <= 0 ) return; if( nDelta < 1 ) nDelta = 1; rBuffer.append( "0 " ); appendMappedLength( nY, rBuffer, true ); rBuffer.append( " m\r\n" ); for( sal_Int32 n = 0; n < nWidth; ) { n += nDelta; appendMappedLength( n, rBuffer, false ); rBuffer.append( ' ' ); appendMappedLength( nDelta+nY, rBuffer, true ); rBuffer.append( ' ' ); n += nDelta; appendMappedLength( n, rBuffer, false ); rBuffer.append( ' ' ); appendMappedLength( nY, rBuffer, true ); rBuffer.append( " v " ); if( n < nWidth ) { n += nDelta; appendMappedLength( n, rBuffer, false ); rBuffer.append( ' ' ); appendMappedLength( nY-nDelta, rBuffer, true ); rBuffer.append( ' ' ); n += nDelta; appendMappedLength( n, rBuffer, false ); rBuffer.append( ' ' ); appendMappedLength( nY, rBuffer, true ); rBuffer.append( " v\r\n" ); } } rBuffer.append( "S\r\n" ); } /* * class PDFWriterImpl */ PDFWriterImpl::PDFWriterImpl( const OUString& rFilename, PDFWriter::PDFVersion eVersion, PDFWriter::Compression eCompression ) : m_pReferenceDevice( NULL ), m_aMapMode( MAP_POINT, Point(), Fraction( 1L, 10L ), Fraction( 1L, 10L ) ), m_nNextFID( 1 ), m_nInheritedPageWidth( 595 ), // default A4 m_nInheritedPageHeight( 842 ), // default A4 m_eInheritedOrientation( PDFWriter::Portrait ), m_nCurrentPage( -1 ), m_eVersion( eVersion ), m_eCompression( eCompression ), m_aFileName( rFilename ), m_pCodec( NULL ) { Font aFont; aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) ); aFont.SetSize( Size( 0, 12 ) ); GraphicsState aState; aState.m_aMapMode = m_aMapMode; aState.m_aFont = aFont; m_aGraphicsStack.push_front( aState ); oslFileError aError = osl_openFile( m_aFileName.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ); if( aError != osl_File_E_None ) { if( aError == osl_File_E_EXIST ) { aError = osl_openFile( m_aFileName.pData, &m_aFile, osl_File_OpenFlag_Write ); if( aError == osl_File_E_None ) aError = osl_setFileSize( m_aFile, 0 ); } } if( aError != osl_File_E_None ) return; m_bOpen = true; // write header OStringBuffer aBuffer( 20 ); aBuffer.append( "%PDF-" ); switch( m_eVersion ) { case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break; case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break; default: case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break; } // append something binary as comment (suggested in PDF Reference) aBuffer.append( "\r\n%äöÜß\r\n" ); if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) ) { osl_closeFile( m_aFile ); m_bOpen = false; return; } } PDFWriterImpl::~PDFWriterImpl() { delete static_cast(m_pReferenceDevice); } void PDFWriterImpl::setDocInfo( const PDFDocInfo& rInfo ) { m_aDocInfo.Title = rInfo.Title; m_aDocInfo.Author = rInfo.Author; m_aDocInfo.Subject = rInfo.Subject; m_aDocInfo.Keywords = rInfo.Keywords; m_aDocInfo.Creator = rInfo.Creator; m_aDocInfo.Producer = rInfo.Producer; } void PDFWriterImpl::emitComment( const OString& rComment ) { OStringBuffer aLine( rComment.getLength()+5 ); aLine.append( "% " ); aLine.append( rComment ); aLine.append( "\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::beginCompression() { #ifdef ENABLE_COMPRESSION m_pCodec = new ZCodec( 0x4000, 0x4000 ); m_pMemStream = new SvMemoryStream(); m_pCodec->BeginCompression(); #endif } void PDFWriterImpl::endCompression() { #ifdef ENABLE_COMPRESSION if( m_pCodec ) { m_pCodec->EndCompression(); delete m_pCodec; m_pCodec = NULL; sal_uInt64 nLen = m_pMemStream->Tell(); m_pMemStream->Seek( 0 ); writeBuffer( m_pMemStream->GetData(), nLen ); delete m_pMemStream; m_pMemStream = NULL; } #endif } bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes ) { if( ! m_bOpen ) // we are already down the drain return false; if( ! nBytes ) // huh ? return true; sal_uInt64 nWritten; if( m_pCodec ) { m_pCodec->Write( *m_pMemStream, static_cast(pBuffer), (ULONG)nBytes ); nWritten = nBytes; } else { if( osl_writeFile( m_aFile, pBuffer, nBytes, &nWritten ) != osl_File_E_None ) nWritten = 0; if( nWritten != nBytes ) { osl_closeFile( m_aFile ); m_bOpen = false; } } return nWritten == nBytes; } OutputDevice* PDFWriterImpl::getReferenceDevice() { if( ! m_pReferenceDevice ) { VirtualDevice* pVDev = new VirtualDevice( 0 ); sal_Int32 nDPI; m_pReferenceDevice = pVDev; switch( m_eCompression ) { case( PDFWriter::Print ): nDPI = 1200; break; case( PDFWriter::Press ): nDPI = 2400; break; default: nDPI = 600; break; } pVDev->SetOutputSizePixel( Size( 640, 480 ) ); pVDev->SetMapMode( MAP_MM ); pVDev->mnDPIX = pVDev->mnDPIY = nDPI; m_pReferenceDevice->mpPDFWriter = this; m_pReferenceDevice->ImplUpdateFontData( TRUE ); } return m_pReferenceDevice; } ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList ) { DBG_ASSERT( m_aSubsets.size() == 0, "Fonts changing during PDF generation, document will be invalid" ); ImplDevFontList* pFiltered = new ImplDevFontList(); ImplDevFontListData* pData = pFontList->First(); while( pData ) { ImplFontData* pEntry = pData->mpFirst; while( pEntry ) { if( pEntry->mbSubsettable || pEntry->mbEmbeddable ) { ImplFontData* pNewData = new ImplFontData(); *pNewData = *pEntry; pNewData->mbDevice = FALSE; // obviously pFiltered->Add( pNewData ); } pEntry = pEntry->mpNext; } pData = pFontList->Next(); } // append the 14 PDF builtin fonts for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ ) { ImplFontData* pNewData = new ImplFontData(); pNewData->mpSysData = (void*)&m_aBuiltinFonts[i]; pNewData->maName = String::CreateFromAscii( m_aBuiltinFonts[i].m_pName ); pNewData->maStyleName = String::CreateFromAscii( m_aBuiltinFonts[i].m_pStyleName ); pNewData->mnWidth = 0; pNewData->mnHeight = 0; pNewData->meFamily = m_aBuiltinFonts[i].m_eFamily; pNewData->meCharSet = m_aBuiltinFonts[i].m_eCharSet; pNewData->mePitch = m_aBuiltinFonts[i].m_ePitch; pNewData->meWeight = m_aBuiltinFonts[i].m_eWeight; pNewData->meItalic = m_aBuiltinFonts[i].m_eItalic; pNewData->meWidthType = m_aBuiltinFonts[i].m_eWidthType; pNewData->meType = TYPE_SCALABLE; pNewData->mnVerticalOrientation = 0; pNewData->mbOrientation = TRUE; pNewData->mbDevice = TRUE; pNewData->mnQuality = 50000; pNewData->mbSubsettable = FALSE; pNewData->mbEmbeddable = FALSE; pFiltered->Add( pNewData ); } return pFiltered; } bool PDFWriterImpl::isBuiltinFont( ImplFontData* pFont ) const { for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ ) { if( pFont->mpSysData == (void*)&m_aBuiltinFonts[i] ) return true; } return false; } void PDFWriterImpl::getFontMetric( ImplFontSelectData* pSelect, ImplFontMetricData* pMetric ) const { for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ ) { if( pSelect->mpFontData->mpSysData == (void*)&m_aBuiltinFonts[i] ) { pMetric->mnWidth = pSelect->mnHeight; pMetric->mnAscent = ( pSelect->mnHeight * m_aBuiltinFonts[i].m_nAscent + 500 ) / 1000; pMetric->mnDescent = ( pSelect->mnHeight * (-m_aBuiltinFonts[i].m_nDescent) + 500 ) / 1000; pMetric->mnLeading = 0; pMetric->mnSlant = 0; pMetric->mnFirstChar = 32; pMetric->mnLastChar = 255; pMetric->meFamily = m_aBuiltinFonts[i].m_eFamily; pMetric->meCharSet = m_aBuiltinFonts[i].m_eCharSet; pMetric->mePitch = m_aBuiltinFonts[i].m_ePitch; pMetric->meWeight = m_aBuiltinFonts[i].m_eWeight; pMetric->meItalic = m_aBuiltinFonts[i].m_eItalic; pMetric->meType = TYPE_SCALABLE; pMetric->mbDevice = TRUE; break; } } } // ----------------------------------------------------------------------- namespace vcl { class PDFSalLayout : public GenericSalLayout { PDFWriterImpl& mrPDFWriterImpl; const PDFWriterImpl::BuiltinFont& mrBuiltinFont; bool mbIsSymbolFont; long mnPixelPerEM; String maOrigText; public: PDFSalLayout( PDFWriterImpl&, const PDFWriterImpl::BuiltinFont&, long nPixelPerEM, int nOrientation ); void SetText( const String& rText ) { maOrigText = rText; } virtual bool LayoutText( ImplLayoutArgs& ); virtual void InitFont() const; virtual void DrawText( SalGraphics& ) const; }; } // ----------------------------------------------------------------------- PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl, const PDFWriterImpl::BuiltinFont& rBuiltinFont, long nPixelPerEM, int nOrientation ) : mrPDFWriterImpl( rPDFWriterImpl ), mrBuiltinFont( rBuiltinFont ), mnPixelPerEM( nPixelPerEM ) { mbIsSymbolFont = (rBuiltinFont.m_eCharSet == RTL_TEXTENCODING_SYMBOL); SetOrientation( nOrientation ); } // ----------------------------------------------------------------------- bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs ) { const String aText( rArgs.mpStr+rArgs.mnMinCharPos, rArgs.mnEndCharPos-rArgs.mnMinCharPos ); SetText( aText ); SetUnitsPerPixel( 1000 ); Point aNewPos( 0, 0 ); bool bRightToLeft; for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); ) { sal_Unicode cChar = rArgs.mpStr[ nCharPos ]; if( cChar & 0xff00 ) { // some characters can be used by conversion if( (cChar >= 0xf000) && mbIsSymbolFont ) cChar -= 0xf000; else { String aString( cChar); ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 ); cChar = ((sal_Unicode)aChar.GetChar( 0 )) & 0x00ff; } } DBG_ASSERT( cChar < 256, "invalid character index requested for builtin font" ); if( cChar & 0xff00 ) { cChar = 0; // NotDef glyph rArgs.NeedFallback( nCharPos, bRightToLeft ); } long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM; long nGlyphFlags = (nGlyphWidth > 0) ? 0 : GlyphItem::IS_IN_CLUSTER; if( bRightToLeft ) nGlyphFlags |= GlyphItem::IS_RTL_GLYPH; // TODO: get kerning from builtin fonts GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth ); AppendGlyph( aGI ); aNewPos.X() += nGlyphWidth; } return true; } // ----------------------------------------------------------------------- void PDFSalLayout::InitFont() const { // TODO: recreate font with all its attributes } // ----------------------------------------------------------------------- void PDFSalLayout::DrawText( SalGraphics& rSalGraphics ) const { mrPDFWriterImpl.drawLayout( *const_cast(this), maOrigText, true ); } // ----------------------------------------------------------------------- SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pSelect ) { DBG_ASSERT( (pSelect->mpFontData != NULL), "PDFWriterImpl::GetTextLayout mpFontData is NULL" ); for( unsigned int n = 0; n < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); n++ ) { if( pSelect->mpFontData->mpSysData != (void*)&m_aBuiltinFonts[n] ) continue; long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight; int nOrientation = pSelect->mnOrientation; PDFSalLayout* pLayout = new PDFSalLayout( *this, m_aBuiltinFonts[n], nPixelPerEM, nOrientation ); pLayout->SetText( rArgs.mpStr ); return pLayout; } return NULL; } sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ) { endPage(); m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) ); m_aPages.back().beginStream(); return ++m_nCurrentPage; } void PDFWriterImpl::endPage() { if( m_aPages.begin() != m_aPages.end() ) { m_aGraphicsStack.clear(); m_aGraphicsStack.push_back( GraphicsState() ); // this should pop the PDF graphics stack if necessary updateGraphicsState(); if( m_pCodec ) endCompression(); m_aPages.back().endStream(); // reset the default font Font aFont; aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) ); aFont.SetSize( Size( 0, 12 ) ); m_aCurrentPDFState = m_aGraphicsStack.front(); m_aGraphicsStack.front().m_aFont = aFont; for( std::list::iterator it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it ) { if( ! it->m_aBitmap.IsEmpty() ) { writeBitmapObject( *it ); it->m_aBitmap = BitmapEx(); } } for( std::list::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg ) { if( jpeg->m_pStream ) { writeJPG( *jpeg ); delete jpeg->m_pStream; jpeg->m_pStream = NULL; jpeg->m_aMask = Bitmap(); } } for( std::list::iterator t = m_aTransparentObjects.begin(); t != m_aTransparentObjects.end(); ++t ) { if( t->m_aContentStream.getLength() ) { writeTransparentObject( *t ); t->m_aContentStream = OStringBuffer(); } } } } sal_Int32 PDFWriterImpl::createObject() { m_aObjects.push_back( ~0 ); return m_aObjects.size(); } bool PDFWriterImpl::updateObject( sal_Int32 n ) { if( ! m_bOpen ) return false; sal_uInt64 nOffset = ~0; oslFileError aError = osl_getFilePos( m_aFile, &nOffset ); DBG_ASSERT( aError == osl_File_E_None, "could not register object" ); if( aError != osl_File_E_None ) { osl_closeFile( m_aFile ); m_bOpen = false; } m_aObjects[ n-1 ] = nOffset; return aError == osl_File_E_None; } #define CHECK_RETURN( x ) if( !(x) ) return 0 bool PDFWriterImpl::emitGradients() { for( std::list::iterator it = m_aGradients.begin(); it != m_aGradients.end(); ++it ) { CHECK_RETURN( writeGradientFunction( *it ) ); } return true; } bool PDFWriterImpl::emitTilings() { OStringBuffer aTilingStream( 1024 ); OStringBuffer aTilingObj( 1024 ); for( std::list::const_iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it ) { aTilingStream.setLength( 0 ); aTilingObj.setLength( 0 ); sal_Int32 nX = (sal_Int32)it->m_aRectangle.BottomLeft().X(); sal_Int32 nY = (sal_Int32)it->m_aRectangle.BottomLeft().Y()+1; sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth(); sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight(); appendDouble( (double)nW/10.0, aTilingStream, 1 ); aTilingStream.append( " 0 0 " ); appendDouble( (double)nH/10.0, aTilingStream, 1 ); aTilingStream.append( ' ' ); appendDouble( (double)nX/10.0, aTilingStream, 1 ); aTilingStream.append( ' ' ); appendDouble( (double)nY/10.0, aTilingStream, 1 ); aTilingStream.append( " cm\r\n /Im" ); aTilingStream.append( it->m_nBitmapObject ); aTilingStream.append( " Do\r\n" ); // write pattern object aTilingObj.append( it->m_nObject ); aTilingObj.append( " 0 obj\r\n" ); aTilingObj.append( "<< /Type /Pattern\r\n" " /PatternType 1\r\n" " /PaintType 1\r\n" " /TilingType 1\r\n" " /BBox [ " ); appendDouble( (double)nX/10.0, aTilingObj, 1 ); aTilingObj.append( ' ' ); appendDouble( (double)nY/10.0, aTilingObj, 1 ); aTilingObj.append( ' ' ); appendDouble( (double)(nX+nW)/10.0, aTilingObj, 1 ); aTilingObj.append( ' ' ); appendDouble( (double)(nY+nH)/10.0, aTilingObj, 1 ); aTilingObj.append( " ]\r\n" " /XStep " ); appendDouble( (double)nW/10.0, aTilingObj, 1 ); aTilingObj.append( "\r\n" " /YStep " ); appendDouble( (double)nH/10.0, aTilingObj, 1 ); aTilingObj.append( "\r\n" " /Resources <<\r\n" " /XObject << /Im" ); aTilingObj.append( it->m_nBitmapObject ); aTilingObj.append( ' ' ); aTilingObj.append( it->m_nBitmapObject ); aTilingObj.append( " 0 R >> >>\r\n" " /Length " ); aTilingObj.append( (sal_Int32)aTilingStream.getLength() ); aTilingObj.append( "\r\n" ">>\r\nstream\r\n" ); CHECK_RETURN( updateObject( it->m_nObject ) ); CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ); CHECK_RETURN( writeBuffer( aTilingStream.getStr(), aTilingStream.getLength() ) ); aTilingObj.setLength( 0 ); aTilingObj.append( "\r\nendstream\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ); } return true; } bool PDFWriterImpl::emitHatches() { OStringBuffer aHatchStream( 1024 ); OStringBuffer aHatchObj( 1024 ); // get maximum page rectangle sal_Int32 nWidth, nHeight, nMax = 0; for( std::list::const_iterator pg = m_aPages.begin(); pg != m_aPages.end(); ++pg ) { if( pg->m_nPageWidth && pg->m_nPageHeight ) { nWidth = pg->m_nPageWidth; nHeight = pg->m_nPageHeight; } else { nWidth = m_nInheritedPageWidth; nHeight = m_nInheritedPageHeight; } nMax = (nMax > nWidth ? nMax : nWidth); nMax = (nMax > nHeight ? nMax : nHeight); } sal_Int32 nSingleHatch = 0, nDoubleHatch = 0, nTripleHatch = 0; for( std::list::iterator it = m_aHatches.begin(); it != m_aHatches.end(); ++it ) { aHatchStream.setLength( 0 ); aHatchObj.setLength( 0 ); const Hatch& rHatch = it->m_aHatch; // draw hatch sal_Int32 nBaseHatch = 0; switch( rHatch.GetStyle() ) { case HATCH_SINGLE: if( ! nSingleHatch ) nSingleHatch = createObject(); nBaseHatch = nSingleHatch; break; case HATCH_DOUBLE: if( ! nDoubleHatch ) nDoubleHatch = createObject(); nBaseHatch = nDoubleHatch; break; case HATCH_TRIPLE: if( ! nTripleHatch ) nTripleHatch = createObject(); nBaseHatch = nTripleHatch; break; } aHatchStream.append( "/HCS cs " ); aHatchStream.append( (double)rHatch.GetColor().GetRed()/255.0 ); aHatchStream.append( ' ' ); aHatchStream.append( (double)rHatch.GetColor().GetGreen()/255.0 ); aHatchStream.append( ' ' ); aHatchStream.append( (double)rHatch.GetColor().GetBlue()/255.0 ); aHatchStream.append( " /P" ); aHatchStream.append( nBaseHatch ); aHatchStream.append( " scn\r\n" ); aHatchStream.append( -nMax ); aHatchStream.append( ' ' ); aHatchStream.append( -nMax ); aHatchStream.append( ' ' ); aHatchStream.append( 2*nMax ); aHatchStream.append( ' ' ); aHatchStream.append( 2*nMax ); aHatchStream.append( " re f\r\n" ); // write pattern object aHatchObj.append( it->m_nObject ); aHatchObj.append( " 0 obj\r\n" ); aHatchObj.append( "<< /Type /Pattern\r\n" " /PatternType 1\r\n" " /PaintType 1\r\n" " /TilingType 1\r\n" " /BBox [ " ); aHatchObj.append( -nMax ); aHatchObj.append( ' ' ); aHatchObj.append( -nMax ); aHatchObj.append( ' ' ); aHatchObj.append( nMax ); aHatchObj.append( ' ' ); aHatchObj.append( nMax ); aHatchObj.append( " ]\r\n" " /XStep " ); aHatchObj.append( 2*nMax ); aHatchObj.append( "\r\n" " /YStep " ); aHatchObj.append( 2*nMax ); aHatchObj.append( "\r\n" " /Matrix [ " ); // prepare matrix const double theta = (double)rHatch.GetAngle() * M_PI / 1800.0; Size aSize( rHatch.GetDistance(), 0 ); aSize = lcl_convert( it->m_aMapMode, MapMode( MAP_POINT ), getReferenceDevice(), aSize ); const double scale = (double)aSize.Width(); appendDouble( scale*cos( theta ), aHatchObj ); aHatchObj.append( ' ' ); appendDouble( scale*sin( theta ), aHatchObj ); aHatchObj.append( ' ' ); appendDouble( scale*(-sin( theta )), aHatchObj ); aHatchObj.append( ' ' ); appendDouble( scale*cos( theta ), aHatchObj ); aHatchObj.append( " 0 0 ]\r\n" ); aHatchObj.append( " /Resources <<\r\n" " /ColorSpace << /HCS [ /Pattern /DeviceRGB ] >>\r\n" " /Pattern << /P" ); aHatchObj.append( nBaseHatch ); aHatchObj.append( ' ' ); aHatchObj.append( nBaseHatch ); aHatchObj.append( " 0 R >>\r\n" " >>\r\n" " /Length " ); aHatchObj.append( (sal_Int32)aHatchStream.getLength() ); aHatchObj.append( "\r\n" ">>\r\n" "stream\r\n" ); CHECK_RETURN( updateObject( it->m_nObject ) ); CHECK_RETURN( writeBuffer( aHatchObj.getStr(), aHatchObj.getLength() ) ); CHECK_RETURN( writeBuffer( aHatchStream.getStr(), aHatchStream.getLength() ) ); aHatchObj.setLength( 0 ); aHatchObj.append( "\r\nendstream\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aHatchObj.getStr(), aHatchObj.getLength() ) ); } // emit needed base hatches for( int i = 0; i < 3; i++ ) { if( ( i == 0 && nSingleHatch ) || ( i == 1 && nDoubleHatch ) || ( i == 2 && nTripleHatch ) ) { sal_Int32 nObject = 0; aHatchStream.setLength( 0 ); switch( i ) { case 0: nObject = nSingleHatch; aHatchStream.append( "0 0.5 m 1 0.5 l S\r\n" ); break; case 1: nObject = nDoubleHatch; aHatchStream.append( "0 0.5 m 1 0.5 l S\r\n" ); aHatchStream.append( "0.5 0 m 0.5 1 l S\r\n" ); break; case 2: nObject = nTripleHatch; aHatchStream.append( "0 0.5 m 1 0.5 l S\r\n" ); aHatchStream.append( "0.5 0 m 0.5 1 l S\r\n" ); aHatchStream.append( "0 1 m 1 0 l S\r\n" ); break; } CHECK_RETURN( updateObject( nObject ) ); aHatchObj.setLength( 0 ); aHatchObj.append( nObject ); aHatchObj.append( " 0 obj\r\n" ); aHatchObj.append( "<< /Type /Pattern\r\n" " /PatternType 1\r\n" " /PaintType 2\r\n" " /TilingType 1\r\n" " /BBox [ 0 0 1 1 ]\r\n" " /XStep 1\r\n" " /YStep 1\r\n" " /Resources << >>\r\n" " /Length " ); aHatchObj.append( (sal_Int32)aHatchStream.getLength() ); aHatchObj.append( "\r\n" ">>\r\n" "stream\r\n" ); CHECK_RETURN( writeBuffer( aHatchObj.getStr(), aHatchObj.getLength() ) ); CHECK_RETURN( writeBuffer( aHatchStream.getStr(), aHatchStream.getLength() ) ); aHatchObj.setLength( 0 ); aHatchObj.append( "\r\nendstream\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aHatchObj.getStr(), aHatchObj.getLength() ) ); } } return true; } sal_Int32 PDFWriterImpl::emitBuiltinFont( ImplFontData* pFont ) { sal_Int32 nFontObject = 0; for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ ) { if( pFont->mpSysData == (void*)&m_aBuiltinFonts[i] ) { OStringBuffer aLine( 1024 ); nFontObject = createObject(); CHECK_RETURN( updateObject( nFontObject ) ); aLine.append( nFontObject ); aLine.append( " 0 obj\r\n" "<< /Type /Font\r\n" " /Subtype /Type1\r\n" " /BaseFont /" ); aLine.append( m_aBuiltinFonts[i].m_pPSName ); aLine.append( "\r\n" ); if( m_aBuiltinFonts[i].m_eCharSet != RTL_TEXTENCODING_SYMBOL ) aLine.append( " /Encoding /WinAnsiEncoding\r\n" ); aLine.append( ">>\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); break; } } return nFontObject; } std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( ImplFontData* pFont, EmbedFont& rEmbed ) { std::map< sal_Int32, sal_Int32 > aRet; if( isBuiltinFont( pFont ) ) { aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont ); return aRet; } sal_Int32 nFontObject = 0; sal_Int32 nStreamObject = 0; sal_Int32 nFontDescriptor = 0; FontSubsetInfo aInfo; sal_Int32 pWidths[256]; const unsigned char* pFontData = NULL; long nFontLen = 0; sal_Int32 nLength1, nLength2; if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, pWidths, aInfo, &nFontLen )) ) { if( aInfo.m_nFontType != SAL_FONTSUBSETINFO_TYPE_TYPE1 ) goto streamend; // see whether it is pfb or pfa; if it is a pfb, fill ranges // of 6 bytes that are not part of the font program std::list< int > aSections; std::list< int >::const_iterator it; int nIndex = 0; while( pFontData[nIndex] == 0x80 ) { aSections.push_back( nIndex ); if( pFontData[nIndex+1] == 0x03 ) break; sal_Int32 nBytes = ((sal_Int32)pFontData[nIndex+2]) | ((sal_Int32)pFontData[nIndex+3]) << 8 | ((sal_Int32)pFontData[nIndex+4]) << 16 | ((sal_Int32)pFontData[nIndex+5]) << 24; nIndex += nBytes+6; } // search for eexec nIndex = 0; int nEndAsciiIndex; int nBeginBinaryIndex; int nEndBinaryIndex; do { while( nIndex < nFontLen-4 && ( pFontData[nIndex] != 'e' || pFontData[nIndex+1] != 'e' || pFontData[nIndex+2] != 'x' || pFontData[nIndex+3] != 'e' || pFontData[nIndex+4] != 'c' ) ) nIndex++; // check whether we are in a excluded section for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) ; } while( it != aSections.end() && nIndex < nFontLen-4 ); // this should end the ascii part if( nIndex > nFontLen-5 ) goto streamend; nEndAsciiIndex = nIndex+4; // now count backwards until we can account for 512 '0' // which is the endmarker of the (hopefully) binary data // do not count the pfb header sections int nFound = 0; nIndex = nFontLen-1; while( nIndex > 0 && nFound < 512 ) { for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) ; if( it == aSections.end() ) { // inside the 512 '0' block there may only be whitespace // according to T1 spec; probably it would be to simple // if all fonts complied if( pFontData[nIndex] == '0' ) nFound++; else if( nFound > 0 && pFontData[nIndex] != '\r' && pFontData[nIndex] != '\t' && pFontData[nIndex] != '\n' && pFontData[nIndex] != ' ' ) break; } nIndex--; } if( nIndex < 1 || nIndex <= nEndAsciiIndex ) goto streamend; // there may be whitespace to ignore before the 512 '0' while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' ) { nIndex--; for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) ; if( it != aSections.end() ) { nIndex = (*it)-1; break; // this is surely a binary boundary, in ascii case it wouldn't matter } } nEndBinaryIndex = nIndex; // search for beginning of binary section nBeginBinaryIndex = nEndAsciiIndex; do { nBeginBinaryIndex++; for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it ) ; } while( nBeginBinaryIndex < nEndBinaryIndex && ( pFontData[nBeginBinaryIndex] == '\r' || pFontData[nBeginBinaryIndex] == '\n' || it != aSections.end() ) ); // it seems to be vital to copy the exact whitespace between binary data // and eexec, else a invalid font results. so make nEndAsciiIndex // always immediate in front of nBeginBinaryIndex nEndAsciiIndex = nBeginBinaryIndex-1; for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it ) ; if( it != aSections.end() ) nEndAsciiIndex = (*it)-1; nLength1 = nEndAsciiIndex+1; // including the last character for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it ) nLength1 -= 6; // decrease by pfb section size // if the first four bytes are all ascii hex characters, then binary data // has to be converted to real binary data for( nIndex = 0; nIndex < 4 && ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) || ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) || ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' ) ); ++nIndex ) ; bool bConvertHexData = true; if( nIndex < 4 ) { bConvertHexData = false; nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte for( it = aSections.begin(); it != aSections.end(); ++it ) if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex ) nLength2 -= 6; } else { // count the hex ascii characters to get nLength2 nLength2 = 0; int nNextSectionIndex = 0; for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) ; if( it != aSections.end() ) nNextSectionIndex = *it; for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) { if( nIndex == nNextSectionIndex ) { nIndex += 6; ++it; nNextSectionIndex = (it == aSections.end() ? 0 : *it ); } if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) || ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) || ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) ) nLength2++; } DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" ); nLength2 /= 2; } // now we can actually write the font stream ! OStringBuffer aLine( 512 ); nStreamObject = createObject(); if( !updateObject(nStreamObject)) goto streamend; sal_Int32 nStreamLengthObject = createObject(); aLine.append( nStreamObject ); aLine.append( " 0 obj\r\n" "<< /Length " ); aLine.append( nStreamLengthObject ); aLine.append( " 0 R\r\n" #ifdef ENABLE_COMPRESSION " /Filter /FlateDecode\r\n" #endif " /Length1 " ); aLine.append( nLength1 ); aLine.append( "\r\n" " /Length2 " ); aLine.append( nLength2 ); aLine.append( "\r\n" " /Length3 0\r\n" ">>\r\n" "stream\r\n" ); if( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) goto streamend; sal_uInt64 nBeginStreamPos = 0; osl_getFilePos( m_aFile, &nBeginStreamPos ); beginCompression(); // write ascii section if( aSections.begin() == aSections.end() ) { if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) ) goto streamend; } else { // first section always starts at 0 it = aSections.begin(); nIndex = (*it)+6; ++it; while( *it < nEndAsciiIndex ) { if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) ) goto streamend; nIndex = (*it)+6; ++it; } // write partial last section if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) ) goto streamend; } // write binary section if( ! bConvertHexData ) { if( aSections.begin() == aSections.end() ) { if( ! writeBuffer( pFontData+nBeginBinaryIndex, nEndBinaryIndex-nBeginBinaryIndex+1 ) ) goto streamend; } else { for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it ) ; if( *it > nEndBinaryIndex ) { if( ! writeBuffer( pFontData+nBeginBinaryIndex, nEndBinaryIndex-nBeginBinaryIndex+1 ) ) goto streamend; } else { // write first partial section if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) ) goto streamend; nIndex = (*it)+6; ++it; while( *it < nEndBinaryIndex ) { if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) ) goto streamend; nIndex = (*it)+6; ++it; } // write partial last section if( ! writeBuffer( pFontData+nIndex, nEndBinaryIndex-nIndex+1 ) ) goto streamend; } } } else { unsigned char* pWriteBuffer = (unsigned char*)rtl_allocateMemory( nLength2 ); memset( pWriteBuffer, 0, nLength2 ); int nWriteIndex = 0; int nNextSectionIndex = 0; for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) ; if( it != aSections.end() ) nNextSectionIndex = *it; for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) { if( nIndex == nNextSectionIndex ) { nIndex += 6; ++it; nNextSectionIndex = (it == aSections.end() ? 0 : *it ); } unsigned char cNibble = 0x80; if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) cNibble = pFontData[nIndex] - '0'; else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) cNibble = pFontData[nIndex] - 'a' + 10; else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) cNibble = pFontData[nIndex] - 'A' + 10; if( cNibble != 0x80 ) { if( !(nWriteIndex & 1 ) ) cNibble <<= 4; pWriteBuffer[ nWriteIndex/2 ] |= cNibble; nWriteIndex++; } } if( ! writeBuffer( pWriteBuffer, nLength2 ) ) goto streamend; rtl_freeMemory( pWriteBuffer ); } endCompression(); sal_uInt64 nEndStreamPos = 0; osl_getFilePos( m_aFile, &nEndStreamPos ); // and finally close the stream aLine.setLength( 0 ); aLine.append( "\r\nendstream\r\nendobj\r\n\r\n" ); if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) goto streamend; // write stream length object aLine.setLength( 0 ); if( ! updateObject( nStreamLengthObject ) ) goto streamend; aLine.append( nStreamLengthObject ); aLine.append( " 0 obj\r\n" ); aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) ); aLine.append( "\r\nendobj\r\n\r\n" ); if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) goto streamend; } if( nStreamObject ) // write font descriptor nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject ); if( nFontDescriptor ) { const std::map< sal_Unicode, sal_Int32 >* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL ); sal_Int32 nToUnicodeStream = 0; sal_uInt8 nEncoding[256]; sal_Unicode nEncodedCodes[256]; if( pEncoding ) { memset( nEncodedCodes, 0, sizeof(nEncodedCodes) ); memset( nEncoding, 0, sizeof(nEncoding) ); for( std::map< sal_Unicode, sal_Int32 >::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it ) { if( it->second != -1 ) { sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff); nEncoding[ nCode ] = nCode; nEncodedCodes[ nCode ] = it->first; } } nToUnicodeStream = createToUnicodeCMap( nEncoding, nEncodedCodes, sizeof(nEncoding)/sizeof(nEncoding[0]) ); } // write font object sal_Int32 nObject = createObject(); if( ! updateObject( nObject ) ) goto streamend; OStringBuffer aLine( 1024 ); aLine.append( nObject ); aLine.append( " 0 obj\r\n" "<< /Type /Font\r\n" " /Subtype /Type1\r\n" " /BaseFont /" ); aLine.append( OUStringToOString( aInfo.m_aPSName, osl_getThreadTextEncoding() ) ); aLine.append( "\r\n" ); if( pFont->meCharSet != RTL_TEXTENCODING_SYMBOL && pEncoding == 0 ) aLine.append( " /Encoding /WinAnsiEncoding\r\n" ); if( nToUnicodeStream ) { aLine.append( " /ToUnicode " ); aLine.append( nToUnicodeStream ); aLine.append( " 0 R\r\n" ); } aLine.append( " /FirstChar 0\r\n" " /LastChar 255\r\n" " /Widths [ " ); for( int i = 0; i < 256; i++ ) { aLine.append( pWidths[i] ); aLine.append( ((i&7) == 7) ? "\r\n " : " " ); } aLine.append( " ]\r\n" " /FontDescriptor " ); aLine.append( nFontDescriptor ); aLine.append( " 0 R\r\n" ">>\r\n" "endobj\r\n\r\n" ); if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) goto streamend; nFontObject = nObject; aRet[ rEmbed.m_nNormalFontID ] = nObject; // write additional encodings for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it ) { sal_Int32 aEncWidths[ 256 ]; // emit encoding dict sal_Int32 nEncObject = createObject(); if( ! updateObject( nEncObject ) ) goto streamend; OutputDevice* pRef = getReferenceDevice(); pRef->Push( PUSH_FONT | PUSH_MAPMODE ); pRef->SetMapMode( MapMode( MAP_POINT ) ); Font aFont( pFont->maName, pFont->maStyleName, Size( 0, 100 ) ); pRef->SetFont( aFont ); pRef->ImplNewFont(); aLine.setLength( 0 ); aLine.append( nEncObject ); aLine.append( " 0 obj\r\n" "<< /Type /Encoding\r\n" " /Differences [ 0\r\n" ); int nEncoded = 0; for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it ) { String aStr( str_it->m_aUnicode ); aEncWidths[nEncoded] = pRef->GetTextWidth( aStr ) * 10; nEncodedCodes[nEncoded] = str_it->m_aUnicode; nEncoding[nEncoded] = nEncoded; aLine.append( " /" ); aLine.append( str_it->m_aName ); if( !((++nEncoded) & 7) ) aLine.append( "\r\n" ); } aLine.append( " ]\r\n" ">>\r\n" "endobj\r\n\r\n" ); pRef->Pop(); if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) goto streamend; nToUnicodeStream = createToUnicodeCMap( nEncoding, nEncodedCodes, nEncoded ); nObject = createObject(); if( ! updateObject( nObject ) ) goto streamend; aLine.setLength( 0 ); aLine.append( nObject ); aLine.append( " 0 obj\r\n" "<< /Type /Font\r\n" " /Subtype /Type1\r\n" " /BaseFont /" ); aLine.append( OUStringToOString( aInfo.m_aPSName, osl_getThreadTextEncoding() ) ); aLine.append( "\r\n" ); aLine.append( " /Encoding " ); aLine.append( nEncObject ); aLine.append( " 0 R\r\n" ); if( nToUnicodeStream ) { aLine.append( " /ToUnicode " ); aLine.append( nToUnicodeStream ); aLine.append( " 0 R\r\n" ); } aLine.append( " /FirstChar 0\r\n" " /LastChar " ); aLine.append( (sal_Int32)(nEncoded-1) ); aLine.append( "\r\n" " /Widths [ " ); for( int i = 0; i < nEncoded; i++ ) { aLine.append( aEncWidths[i] ); aLine.append( ((i&7) == 7) ? "\r\n " : " " ); } aLine.append( " ]\r\n" " /FontDescriptor " ); aLine.append( nFontDescriptor ); aLine.append( " 0 R\r\n" ">>\r\n" "endobj\r\n\r\n" ); if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) goto streamend; aRet[ enc_it->m_nFontID ] = nObject; } } streamend: if( pFontData ) m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen ); return aRet; } static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer ) { if( nSubsetID ) { for( int i = 0; i < 6; i++ ) { int nOffset = (nSubsetID % 26); nSubsetID /= 26; rBuffer.append( (sal_Char)('A'+nOffset) ); } rBuffer.append( '+' ); } rBuffer.append( OUStringToOString( rPSName, osl_getThreadTextEncoding() ) ); } sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, sal_Unicode* pUnicodes, int nGlyphs ) { int nMapped = 0, n = 0; for( n = 0; n < nGlyphs; n++ ) if( pUnicodes[n] ) nMapped++; if( nMapped == 0 ) return 0; sal_Int32 nStream = createObject(); CHECK_RETURN( updateObject( nStream ) ); OStringBuffer aContents( 1024 ); aContents.append( "/CIDInit /ProcSet findresource begin\r\n" "12 dict begin\r\n" "begincmap\r\n" "/CIDSystemInfo <<\r\n" " /Registry (Adobe)\r\n" " /Ordering (UCS)\r\n" " /Supplement 0\r\n" ">> def\r\n" "/CMapName /Adobe-Identity-UCS def\r\n" "/CMapType 2 def\r\n" "1 begincodespacerange\r\n" "<00> \r\n" "endcodespacerange\r\n" ); int nCount = 0; for( n = 0; n < nGlyphs; n++ ) { if( pUnicodes[n] ) { if( (nCount % 100) == 0 ) { if( nCount ) aContents.append( "endbfchar\r\n" ); aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) ); aContents.append( " beginbfchar\r\n" ); } aContents.append( '<' ); appendHex( (sal_Int8)pEncoding[n], aContents ); aContents.append( "> <" ); appendHex( (sal_Int8)(pUnicodes[n] / 256), aContents ); appendHex( (sal_Int8)(pUnicodes[n] & 255), aContents ); aContents.append( ">\r\n" ); nCount++; } } aContents.append( "endbfchar\r\n" "endcmap\r\n" "CMapName currentdict /CMap defineresource pop\r\n" "end\r\n" "end\r\n" ); #if defined COMPRESS_PAGES && defined ENABLE_COMPRESSION ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 ); SvMemoryStream aStream; pCodec->BeginCompression(); pCodec->Write( aStream, (const BYTE*)aContents.getStr(), aContents.getLength() ); pCodec->EndCompression(); delete pCodec; #endif OStringBuffer aLine( 40 ); aLine.append( nStream ); aLine.append( " 0 obj\r\n<< /Length " ); #if defined COMPRESS_PAGES && defined ENABLE_COMPRESSION sal_Int32 nLen = (sal_Int32)aStream.Tell(); aStream.Seek( 0 ); aLine.append( nLen ); aLine.append( "\r\n /Filter /FlateDecode" ); #else aLine.append( aContents.getLength() ); #endif aLine.append( " >>\r\nstream\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); #if defined COMPRESS_PAGES && defined ENABLE_COMPRESSION CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) ); #else CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) ); #endif aLine.setLength( 0 ); aLine.append( "endstream\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); return nStream; } sal_Int32 PDFWriterImpl::emitFontDescriptor( ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream ) { OStringBuffer aLine( 1024 ); // get font flags, see PDF reference 1.4 p. 358 // possibly characters outside Adobe standard encoding // so set Symbolic flag sal_Int32 nFontFlags = (1<<2); if( pFont->meItalic == ITALIC_NORMAL || pFont->meItalic == ITALIC_OBLIQUE ) nFontFlags |= (1 << 6); if( pFont->mePitch == PITCH_FIXED ) nFontFlags |= 1; if( pFont->meFamily == FAMILY_SCRIPT ) nFontFlags |= (1 << 3); if( pFont->meFamily == FAMILY_ROMAN ) nFontFlags |= (1 << 1); sal_Int32 nFontDescriptor = createObject(); CHECK_RETURN( updateObject( nFontDescriptor ) ); aLine.setLength( 0 ); aLine.append( nFontDescriptor ); aLine.append( " 0 obj\r\n" "<< /Type /FontDescriptor\r\n" " /FontName /" ); appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine ); aLine.append( "\r\n" " /Flags " ); aLine.append( nFontFlags ); aLine.append( "\r\n" " /FontBBox [ " ); // note: Top and Bottom are reversed in VCL and PDF rectangles aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() ); aLine.append( ' ' ); aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() ); aLine.append( ' ' ); aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() ); aLine.append( ' ' ); aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) ); aLine.append( " ]\r\n" " /ItalicAngle " ); if( pFont->meItalic == ITALIC_OBLIQUE || pFont->meItalic == ITALIC_NORMAL ) aLine.append( "-30" ); else aLine.append( "0" ); aLine.append( "\r\n" " /Ascent " ); aLine.append( (sal_Int32)rInfo.m_nAscent ); aLine.append( "\r\n" " /Descent " ); aLine.append( (sal_Int32)-rInfo.m_nDescent ); aLine.append( "\r\n" " /CapHeight " ); aLine.append( (sal_Int32)rInfo.m_nCapHeight ); // According to PDF reference 1.4 StemV is required // seems a tad strange to me, but well ... aLine.append( "\r\n" " /StemV 80\r\n" " /FontFile" ); switch( rInfo.m_nFontType ) { case SAL_FONTSUBSETINFO_TYPE_TRUETYPE: aLine.append( '2' ); break; case SAL_FONTSUBSETINFO_TYPE_TYPE1: break; default: DBG_ERROR( "unknown fonttype in PDF font descriptor" ); return 0; } aLine.append( ' ' ); aLine.append( nFontStream ); aLine.append( " 0 R\r\n" ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); return nFontDescriptor; } sal_Int32 PDFWriterImpl::emitFonts() { sal_Int32 nFontDict = 0; if( ! m_aSubsets.size() && ! m_aEmbeddedFonts.size() ) // no fonts return 0; #ifndef REMOTE_APPSERVER if( ! m_pReferenceDevice->ImplGetGraphics() ) #else if( ! m_pReferenceDevice->ImplGetServerGraphics() ) #endif return 0; OStringBuffer aLine( 1024 ); char buf[8192]; std::map< sal_Int32, sal_Int32 > aFontIDToObject; OUString aTmpName; osl_createTempFile( NULL, NULL, &aTmpName.pData ); for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it ) { for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit ) { long pGlyphIDs[ 256 ]; sal_Int32 pWidths[ 256 ]; sal_uInt8 pEncoding[ 256 ]; sal_Unicode pUnicodes[ 256 ]; int nGlyphs = 1; // fill arrays and prepare encoding index map sal_Int32 nToUnicodeStream = 0; memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) ); memset( pEncoding, 0, sizeof( pEncoding ) ); memset( pUnicodes, 0, sizeof( pUnicodes ) ); for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit ) { sal_uInt8 nEnc = fit->second.m_nSubsetGlyphID; DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" ); DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" ); pGlyphIDs[ nEnc ] = fit->first; pEncoding[ nEnc ] = nEnc; pUnicodes[ nEnc ] = fit->second.m_aUnicode; if( pUnicodes[ nEnc ] ) nToUnicodeStream = 1; if( nGlyphs < 256 ) nGlyphs++; else { DBG_ERROR( "too many glyphs for subset" ); } } FontSubsetInfo aSubsetInfo; if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) ) { DBG_ASSERT( aSubsetInfo.m_nFontType == SAL_FONTSUBSETINFO_TYPE_TRUETYPE, "wrong font type in font subset" ); // create font stream oslFileHandle aFontFile; CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) ); // get file size sal_uInt64 nLength; CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) ); CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength ) ) ); CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); sal_Int32 nFontStream = createObject(); sal_Int32 nStreamLengthObject = createObject(); CHECK_RETURN( updateObject( nFontStream ) ); aLine.setLength( 0 ); aLine.append( nFontStream ); aLine.append( " 0 obj\r\n" "<< /Length " ); aLine.append( (sal_Int32)nStreamLengthObject ); aLine.append( " 0 R\r\n" #ifdef ENABLE_COMPRESSION " /Filter /FlateDecode\r\n" #endif " /Length1 " ); aLine.append( (sal_Int32)nLength ); aLine.append( "\r\n" ">>\r\n" "stream\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); sal_uInt64 nStartPos = 0; CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); // copy font file beginCompression(); sal_uInt64 nRead; sal_Bool bEOF = sal_False; do { CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) ); CHECK_RETURN( writeBuffer( buf, nRead ) ); CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) ); } while( ! bEOF ); endCompression(); // close the file osl_closeFile( aFontFile ); sal_uInt64 nEndPos = 0; CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) ); // end the stream aLine.setLength( 0 ); aLine.append( "\r\nendstream\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); // emit stream length object CHECK_RETURN( updateObject( nStreamLengthObject ) ); aLine.setLength( 0 ); aLine.append( nStreamLengthObject ); aLine.append( " 0 obj\r\n" ); aLine.append( (sal_Int64)(nEndPos-nStartPos) ); aLine.append( "\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); // write font descriptor sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream ); if( nToUnicodeStream ) nToUnicodeStream = createToUnicodeCMap( pEncoding, pUnicodes, nGlyphs ); sal_Int32 nFontObject = createObject(); CHECK_RETURN( updateObject( nFontObject ) ); aLine.setLength( 0 ); aLine.append( nFontObject ); aLine.append( " 0 obj\r\n" "<< /Type /Font\r\n" " /Subtype /TrueType\r\n" " /BaseFont /" ); appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine ); aLine.append( "\r\n" " /FirstChar 0\r\n" " /LastChar " ); aLine.append( (sal_Int32)(nGlyphs-1) ); aLine.append( "\r\n" " /Widths [ " ); for( int i = 0; i < nGlyphs; i++ ) { aLine.append( pWidths[ i ] ); aLine.append( ((i & 7) == 7) ? "\r\n " : " " ); } aLine.append( "]\r\n" " /FontDescriptor " ); aLine.append( nFontDescriptor ); aLine.append( " 0 R\r\n" ); if( nToUnicodeStream ) { aLine.append( " /ToUnicode " ); aLine.append( nToUnicodeStream ); aLine.append( " 0 R\r\n" ); } aLine.append( ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); aFontIDToObject[ lit->m_nFontID ] = nFontObject; } } } osl_removeFile( aTmpName.pData ); // emit embedded fonts for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit ) { std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second ); for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) { CHECK_RETURN( fit->second ); aFontIDToObject[ fit->first ] = fit->second; } } nFontDict = createObject(); CHECK_RETURN( updateObject( nFontDict ) ); aLine.setLength( 0 ); aLine.append( nFontDict ); aLine.append( " 0 obj\r\n" "<< " ); for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit ) { aLine.append( "/F" ); aLine.append( mit->first ); aLine.append( ' ' ); aLine.append( mit->second ); aLine.append( " 0 R\r\n" " " ); } aLine.append( ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); return nFontDict; } sal_Int32 PDFWriterImpl::emitResources() { OStringBuffer aLine( 512 ); // emit shadings sal_Int32 nShadingDict = 0; if( m_aGradients.begin() != m_aGradients.end() ) { CHECK_RETURN( emitGradients() ); aLine.setLength( 0 ); aLine.append( nShadingDict = createObject() ); aLine.append( " 0 obj\r\n" "<< " ); for( std::list::iterator it = m_aGradients.begin(); it != m_aGradients.end(); ++it ) { aLine.append( "/P" ); aLine.append( it->m_nObject ); aLine.append( ' ' ); aLine.append( it->m_nObject ); aLine.append( " 0 R\r\n " ); } aLine.append( ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( updateObject( nShadingDict ) ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); } // emit patterns sal_Int32 nPatternDict = 0; if( m_aHatches.begin() != m_aHatches.end() || m_aTilings.begin() != m_aTilings.end() ) { if( m_aHatches.begin() != m_aHatches.end() ) CHECK_RETURN( emitHatches() ); if( m_aTilings.begin() != m_aTilings.end() ) CHECK_RETURN( emitTilings() ); aLine.setLength( 0 ); aLine.append( nPatternDict = createObject() ); aLine.append( " 0 obj\r\n<< " ); for( std::list::const_iterator hatch = m_aHatches.begin(); hatch != m_aHatches.end(); ++hatch ) { aLine.append( "/P" ); aLine.append( hatch->m_nObject ); aLine.append( ' ' ); aLine.append( hatch->m_nObject ); aLine.append( " 0 R\r\n " ); } for( std::list::const_iterator tile = m_aTilings.begin(); tile != m_aTilings.end(); ++tile ) { aLine.append( "/P" ); aLine.append( tile->m_nObject ); aLine.append( ' ' ); aLine.append( tile->m_nObject ); aLine.append( " 0 R\r\n " ); } aLine.append( ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( updateObject( nPatternDict ) ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); } // emit font dict sal_Int32 nFontDict = emitFonts(); // emit xobject dict sal_Int32 nXObjectDict = 0; if( m_aBitmaps.begin() != m_aBitmaps.end() || m_aJPGs.begin() != m_aJPGs.end() || m_aTransparentObjects.begin() != m_aTransparentObjects.end() ) { aLine.setLength( 0 ); nXObjectDict = createObject(); aLine.append( nXObjectDict ); aLine.append( " 0 obj\r\n" "<< " ); for( std::list::const_iterator it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it ) { aLine.append( "/Im" ); aLine.append( it->m_nObject ); aLine.append( ' ' ); aLine.append( it->m_nObject ); aLine.append( " 0 R\r\n " ); } for( std::list::const_iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg ) { aLine.append( "/Im" ); aLine.append( jpeg->m_nObject ); aLine.append( ' ' ); aLine.append( jpeg->m_nObject ); aLine.append( " 0 R\r\n " ); } for( std::list::const_iterator t = m_aTransparentObjects.begin(); t != m_aTransparentObjects.end(); ++t ) { aLine.append( "/Tr" ); aLine.append( t->m_nObject ); aLine.append( ' ' ); aLine.append( t->m_nObject ); aLine.append( " 0 R\r\n " ); } aLine.append( ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( updateObject( nXObjectDict ) ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); } // emit Resource dict sal_Int32 nResourceDict = createObject(); CHECK_RETURN( updateObject( nResourceDict ) ); aLine.setLength( 0 ); aLine.append( nResourceDict ); aLine.append( " 0 obj\r\n<<\r\n" ); if( nFontDict ) { aLine.append( " /Font " ); aLine.append( nFontDict ); aLine.append( " 0 R\r\n" ); } if( nXObjectDict ) { aLine.append( " /XObject " ); aLine.append( nXObjectDict ); aLine.append( " 0 R\r\n" ); } if( nShadingDict ) { aLine.append( " /Shading " ); aLine.append( nShadingDict ); aLine.append( " 0 R\r\n" ); } if( nPatternDict ) { aLine.append( " /Pattern " ); aLine.append( nPatternDict ); aLine.append( " 0 R\r\n" ); } aLine.append( " /ProcSet [ /PDF " ); if( nXObjectDict ) aLine.append( "/ImageC /ImageI " ); aLine.append( "]\r\n" ); aLine.append( ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); return nResourceDict; } #undef CHECK_RETURN #define CHECK_RETURN( x ) if( !x ) return false bool PDFWriterImpl::emitCatalog() { // build page tree // currently there is only one node that contains all leaves // first create a page tree node id sal_Int32 nTreeNode = createObject(); // emit all pages for( std::list::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it ) if( ! it->emit( nTreeNode ) ) return false; sal_Int32 nResourceDict = emitResources(); // adjust tree node file offset if( ! updateObject( nTreeNode ) ) return false; // emit tree node OStringBuffer aLine( 1024 ); aLine.append( nTreeNode ); aLine.append( " 0 obj\r\n" ); aLine.append( "<< /Type /Pages\r\n" ); aLine.append( " /Resources " ); aLine.append( nResourceDict ); aLine.append( " 0 R\r\n" ); switch( m_eInheritedOrientation ) { case PDFWriter::Landscape: aLine.append( " /Rotate 90\r\n" );break; case PDFWriter::Seascape: aLine.append( " /Rotate -90\r\n" );break; case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant case PDFWriter::Portrait: default: break; } aLine.append( " /MediaBox [ 0 0 " ); aLine.append( m_nInheritedPageWidth ); aLine.append( ' ' ); aLine.append( m_nInheritedPageHeight ); aLine.append( " ]\r\n" " /Kids [ " ); for( std::list::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter ) { aLine.append( iter->m_nPageObject ); aLine.append( " 0 R\r\n" " " ); } aLine.append( "]\r\n" " /Count " ); aLine.append( (sal_Int32)m_aPages.size() ); aLine.append( "\r\n" ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); // emit Catalog m_nCatalogObject = createObject(); if( ! updateObject( m_nCatalogObject ) ) return false; aLine.setLength( 0 ); aLine.append( m_nCatalogObject ); aLine.append( " 0 obj\r\n" "<< /Type /Catalog\r\n" " /Pages " ); aLine.append( nTreeNode ); aLine.append( " 0 R\r\n" ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); return true; } sal_Int32 PDFWriterImpl::emitInfoDict() { sal_Int32 nObject = createObject(); if( updateObject( nObject ) ) { OStringBuffer aLine( 1024 ); aLine.append( nObject ); aLine.append( " 0 obj\r\n" "<< " ); if( m_aDocInfo.Title.Len() ) { aLine.append( "/Title " ); appendUnicodeTextString( m_aDocInfo.Title, aLine ); aLine.append( "\r\n" ); } if( m_aDocInfo.Author.Len() ) { aLine.append( "/Author " ); appendUnicodeTextString( m_aDocInfo.Author, aLine ); aLine.append( "\r\n" ); } if( m_aDocInfo.Subject.Len() ) { aLine.append( "/Subject " ); appendUnicodeTextString( m_aDocInfo.Subject, aLine ); aLine.append( "\r\n" ); } if( m_aDocInfo.Keywords.Len() ) { aLine.append( "/Keywords " ); appendUnicodeTextString( m_aDocInfo.Keywords, aLine ); aLine.append( "\r\n" ); } if( m_aDocInfo.Creator.Len() ) { aLine.append( "/Creator " ); appendUnicodeTextString( m_aDocInfo.Creator, aLine ); aLine.append( "\r\n" ); } if( m_aDocInfo.Producer.Len() ) { aLine.append( "/Producer " ); appendUnicodeTextString( m_aDocInfo.Producer, aLine ); aLine.append( "\r\n" ); } TimeValue aTVal, aGMT; oslDateTime aDT; osl_getSystemTime( &aGMT ); osl_getLocalTimeFromSystemTime( &aGMT, &aTVal ); osl_getDateTimeFromTimeValue( &aTVal, &aDT ); aLine.append( "/CreationDate (D:" ); aLine.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Year)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Month)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Day)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); aLine.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); sal_uInt32 nDelta = 0; if( aGMT.Seconds > aTVal.Seconds ) { aLine.append( "-" ); nDelta = aGMT.Seconds-aTVal.Seconds; } else if( aGMT.Seconds < aTVal.Seconds ) { aLine.append( "+" ); nDelta = aTVal.Seconds-aGMT.Seconds; } else aLine.append( "Z" ); if( nDelta ) { aLine.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); aLine.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); aLine.append( "'" ); aLine.append( (sal_Char)('0' + ((nDelta/600)%6)) ); aLine.append( (sal_Char)('0' + ((nDelta/60)%10)) ); } aLine.append( "')\r\n" ); aLine.append( ">>\r\nendobj\r\n\r\n" ); if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) nObject = 0; } else nObject = 0; return nObject; } bool PDFWriterImpl::emitTrailer() { // emit doc info sal_Int32 nDocInfoObject = emitInfoDict(); // emit xref table // remember start sal_uInt64 nXRefOffset = 0; CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) ); CHECK_RETURN( writeBuffer( "xref\r\n", 6 ) ); sal_Int32 nObjects = m_aObjects.size(); OStringBuffer aLine; aLine.append( "0 " ); aLine.append( (sal_Int32)(nObjects+1) ); aLine.append( "\r\n" ); aLine.append( "0000000000 65535 f\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); for( sal_Int32 i = 0; i < nObjects; i++ ) { aLine.setLength( 0 ); OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] ); for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ ) aLine.append( '0' ); aLine.append( aOffset ); aLine.append( " 00000 n\r\n" ); DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); } // emit trailer aLine.setLength( 0 ); aLine.append( "trailer\r\n" "<< /Size " ); aLine.append( (sal_Int32)(nObjects+1) ); aLine.append( "\r\n" " /Root " ); aLine.append( m_nCatalogObject ); aLine.append( " 0 R\r\n" ); if( nDocInfoObject ) { aLine.append( " /Info " ); aLine.append( nDocInfoObject ); aLine.append( " 0 R\r\n" ); } aLine.append( ">>\r\n" "startxref\r\n" ); aLine.append( (sal_Int64)nXRefOffset ); aLine.append( "\r\n" "%%EOF\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); return true; } bool PDFWriterImpl::emit() { endPage(); // emit catalog CHECK_RETURN( emitCatalog() ); // emit trailer CHECK_RETURN( emitTrailer() ); osl_closeFile( m_aFile ); m_bOpen = false; return true; } void PDFWriterImpl::registerGlyphs( int nGlyphs, long* pGlyphs, sal_Unicode* pUnicodes, sal_uInt8* pMappedGlyphs, sal_Int32* pMappedFontObjects, ImplFontData* pFallbackFonts[] ) { ImplFontData* pCurrentFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; FontSubset* pCurrentSubset = &m_aSubsets[ pCurrentFont ]; if( pCurrentFont->mbSubsettable ) { for( int i = 0; i < nGlyphs; i++ ) { if( ! pGlyphs[i] ) continue; FontSubset& rSubset = pFallbackFonts[i] ? m_aSubsets[pFallbackFonts[i]] : *pCurrentSubset; // search for glyphID FontMapping::iterator it = rSubset.m_aMapping.find( pGlyphs[i] ); if( it != rSubset.m_aMapping.end() ) { pMappedFontObjects[i] = it->second.m_nFontID; pMappedGlyphs[i] = it->second.m_nSubsetGlyphID; } else { // create new subset if necessary if( rSubset.m_aSubsets.begin() == rSubset.m_aSubsets.end() || rSubset.m_aSubsets.back().m_aMapping.size() > 254 ) { rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) ); } // copy font id pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID; // create new glyph in subset sal_uInt8 nNewId = rSubset.m_aSubsets.back().m_aMapping.size()+1; pMappedGlyphs[i] = nNewId; // add new glyph to emitted font subset GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ pGlyphs[i] ]; rNewGlyphEmit.m_nSubsetGlyphID = nNewId; rNewGlyphEmit.m_aUnicode = (pUnicodes ? pUnicodes[i] : 0); // add new glyph to font mapping Glyph& rNewGlyph = rSubset.m_aMapping[ pGlyphs[i] ]; rNewGlyph.m_nFontID = pMappedFontObjects[i]; rNewGlyph.m_nSubsetGlyphID = nNewId; } } } else { sal_Int32 nFontID = 0; FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont ); if( it != m_aEmbeddedFonts.end() ) nFontID = it->second.m_nNormalFontID; else { nFontID = m_nNextFID++; m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont(); m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID; } EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont]; const std::map< sal_Unicode, sal_Int32 >* pEncoding = NULL; const std::map< sal_Unicode, rtl::OString >* pNonEncoded = NULL; #ifndef REMOTE_APPSERVER getReferenceDevice()->ImplGetGraphics(); pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded ); #endif std::map< sal_Unicode, sal_Int32 >::const_iterator enc_it; std::map< sal_Unicode, rtl::OString >::const_iterator nonenc_it; for( int i = 0; i < nGlyphs; i++ ) { sal_Int32 nCurFontID = nFontID; sal_Unicode cChar = pUnicodes[i]; if( pEncoding ) { enc_it = pEncoding->find( cChar ); if( enc_it != pEncoding->end() && enc_it->second > 0 ) { DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" ); cChar = (sal_Unicode)enc_it->second; } else if( (enc_it == pEncoding->end() || enc_it->second == -1) && pNonEncoded && (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() ) { nCurFontID = 0; // find non encoded glyph for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it ) { if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() ) { nCurFontID = nec_it->m_nFontID; cChar = (sal_Unicode)nec_it->m_aCMap[ cChar ]; break; } } if( nCurFontID == 0 ) // new nonencoded glyph { if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 ) { rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() ); rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++; } EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back(); rEncoding.m_aEncVector.push_back( EmbedCode() ); rEncoding.m_aEncVector.back().m_aUnicode = cChar; rEncoding.m_aEncVector.back().m_aName = nonenc_it->second; rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1); nCurFontID = rEncoding.m_nFontID; cChar = (sal_Unicode)rEncoding.m_aCMap[ cChar ]; } } else cChar = 0; } else { if( cChar & 0xff00 ) { // some characters can be used by conversion if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area cChar -= 0xf000; else { String aString( cChar); ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 ); cChar = ((sal_Unicode)aChar.GetChar( 0 )) & 0x00ff; } } } pMappedGlyphs[ i ] = (sal_Int8)cChar; pMappedFontObjects[ i ] = nCurFontID; } } } void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines ) { FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief(); // relief takes precedence over shadow (see outdev3.cxx) if( eRelief != RELIEF_NONE ) { push( PUSH_ALL ); Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor(); Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor; Color aReliefColor( COL_LIGHTGRAY ); if( aTextColor == COL_BLACK ) aTextColor = Color( COL_WHITE ); if( aTextLineColor == COL_BLACK ) aTextLineColor = Color( COL_WHITE ); if( aTextColor == COL_WHITE ) aReliefColor = Color( COL_BLACK ); Font aSetFont = m_aCurrentPDFState.m_aFont; aSetFont.SetRelief( RELIEF_NONE ); aSetFont.SetShadow( FALSE ); aSetFont.SetColor( aReliefColor ); setTextLineColor( aTextLineColor ); setFont( aSetFont ); long nOff = 1 + getReferenceDevice()->mnDPIX/300; if( eRelief == RELIEF_ENGRAVED ) nOff = -nOff; rLayout.DrawOffset() += Point( nOff, nOff ); updateGraphicsState(); drawLayout( rLayout, rText, bTextLines ); rLayout.DrawOffset() -= Point( nOff, nOff ); setTextLineColor( aTextLineColor ); aSetFont.SetColor( aTextColor ); setFont( aSetFont ); updateGraphicsState(); drawLayout( rLayout, rText, bTextLines ); // clean up the mess pop(); return; } else if( m_aCurrentPDFState.m_aFont.IsShadow() ) { Font aSaveFont = m_aCurrentPDFState.m_aFont; Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor; Font& rFont = m_aCurrentPDFState.m_aFont; if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 ) rFont.SetColor( Color( COL_LIGHTGRAY ) ); else rFont.SetColor( Color( COL_BLACK ) ); rFont.SetShadow( FALSE ); setFont( rFont ); setTextLineColor( rFont.GetColor() ); updateGraphicsState(); long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24); if( rFont.IsOutline() ) nOff++; rLayout.DrawBase() += Point( nOff, nOff ); drawLayout( rLayout, rText, bTextLines ); rLayout.DrawBase() -= Point( nOff, nOff ); setFont( aSaveFont ); setTextLineColor( aSaveTextLineColor ); updateGraphicsState(); } OStringBuffer aLine( 512 ); // setup text colors (if necessary) bool bPop = false; if( m_aCurrentPDFState.m_aFont.IsOutline() && m_aCurrentPDFState.m_aLineColor != m_aCurrentPDFState.m_aFont.GetColor() ) { bPop = true; aLine.append( "q " ); appendStrokingColor( m_aCurrentPDFState.m_aFont.GetColor(), aLine ); aLine.append( "\r\n" ); } else if( m_aCurrentPDFState.m_aFillColor != m_aCurrentPDFState.m_aFont.GetColor() ) { bPop = true; aLine.append( "q " ); appendNonStrokingColor( m_aCurrentPDFState.m_aFont.GetColor(), aLine ); aLine.append( "\r\n" ); } // begin text object aLine.append( "BT\r\n" ); // outline attribute ? if( m_aCurrentPDFState.m_aFont.IsOutline() ) { aLine.append( "1 Tr " ); double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0; m_aPages.back().appendMappedLength( fW, aLine ); aLine.append ( " w\r\n" ); } const int nMaxGlyphs = 256; // note: the layout calculates in outdevs device pixel !! long pGlyphs[nMaxGlyphs]; sal_uInt8 pMappedGlyphs[nMaxGlyphs]; sal_Int32 pMappedFontObjects[nMaxGlyphs]; sal_Unicode pUnicodes[nMaxGlyphs]; int pCharPosAry[nMaxGlyphs]; long nAdvanceWidths[nMaxGlyphs]; ImplFontData* pFallbackFonts[nMaxGlyphs]; long *pAdvanceWidths = m_aCurrentPDFState.m_aFont.IsVertical() ? nAdvanceWidths : NULL; long nGlyphFlags[nMaxGlyphs]; int nGlyphs; int nIndex = 0; Point aPos, aLastPos(0, 0); bool bFirst = true; int nMinCharPos = 0, nMaxCharPos = rText.Len()-1; double fXScale = 1.0; sal_Int32 nFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight; TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); // transform font height back to current units nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nFontHeight ); if( m_aCurrentPDFState.m_aFont.GetWidth() ) { Font aFont( m_aCurrentPDFState.m_aFont ); aFont.SetWidth( 0 ); FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont ); if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() ) { fXScale = (double)m_aCurrentPDFState.m_aFont.GetWidth() / (double)aMetric.GetWidth(); } // force state before GetFontMetric m_pReferenceDevice->ImplNewFont(); } // if the mapmode is distorted we need to adjust for that also if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() ) { fXScale *= (double)(m_aCurrentPDFState.m_aMapMode.GetScaleX() / m_aCurrentPDFState.m_aMapMode.GetScaleY()); } double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0; double fSin = sin( fAngle ); double fCos = cos( fAngle ); sal_Int32 nLastMappedFont = -1; while( (nGlyphs = rLayout.GetNextGlyphs( nMaxGlyphs, pGlyphs, aPos, nIndex, pAdvanceWidths, pCharPosAry )) ) { // back transformation to current coordinate system aPos = m_pReferenceDevice->PixelToLogic( aPos ); Point aDiff; if ( eAlign == ALIGN_BOTTOM ) aDiff.Y() -= m_pReferenceDevice->GetFontMetric().GetDescent(); else if ( eAlign == ALIGN_TOP ) aDiff.Y() += m_pReferenceDevice->GetFontMetric().GetAscent(); if( aDiff.X() || aDiff.Y() ) { aDiff = Point( (int)(fXScale * fCos * (double)aDiff.X() + fSin * (double)aDiff.Y()), -(int)(fXScale * fSin * (double)aDiff.X() - fCos * (double)aDiff.Y()) ); aPos += aDiff; } for( int i = 0; i < nGlyphs; i++ ) { if( pGlyphs[i] & GF_FONTMASK ) pFallbackFonts[i] = ((MultiSalLayout&)rLayout).GetFallbackFontData((pGlyphs[i] & GF_FONTMASK) >> GF_FONTSHIFT); else pFallbackFonts[i] = NULL; nGlyphFlags[i] = (pGlyphs[i] & GF_FLAGMASK); #ifndef WNT // #104930# workaround for Win32 bug: the glyph ids are actually // Unicodes for vertical fonts because Win32 does not return // the correct glyph ids; this is indicated by GF_ISCHAR which is // needed in SalGraphics::CreateFontSubset to convert the Unicodes // to vertical glyph ids. Doing this here on a per character // basis would be a major performance hit. pGlyphs[i] &= GF_IDXMASK; #endif if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos ) pUnicodes[i] = rText.GetChar( pCharPosAry[i] ); else pUnicodes[i] = 0; // note: in case of ctl one character may result // in multiple glyphs. The current SalLayout // implementations set -1 then to indicate that no direct // mapping is possible } registerGlyphs( nGlyphs, pGlyphs, pUnicodes, pMappedGlyphs, pMappedFontObjects, pFallbackFonts ); if( pAdvanceWidths ) { // have to emit each glyph on its own long nXOffset = 0; for( int n = 0; n < nGlyphs; n++ ) { double fDeltaAngle = 0.0; double fYScale = 1.0; double fTempXScale = fXScale; Point aDeltaPos; if( ( nGlyphFlags[n] & GF_ROTMASK ) == GF_ROTL ) { fDeltaAngle = M_PI/2.0; aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent() - m_pReferenceDevice->GetFontMetric().GetDescent(); aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale); fYScale = fXScale; fTempXScale = 1.0; } else if( ( nGlyphFlags[n] & GF_ROTMASK ) == GF_ROTR ) { fDeltaAngle = -M_PI/2.0; aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale); aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent() + m_pReferenceDevice->GetFontMetric().GetDescent(); fYScale = fXScale; fTempXScale = 1.0; } aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale)/rLayout.GetUnitsPerPixel(), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) ); nXOffset += pAdvanceWidths[n]; if( ! pGlyphs[n] ) continue; aDeltaPos = Point( (int)(fXScale * fCos * (double)aDeltaPos.X() + fSin * (double)aDeltaPos.Y()), -(int)(fXScale * fSin * (double)aDeltaPos.X() - fCos * (double)aDeltaPos.Y()) ); double fDSin = sin( fAngle+fDeltaAngle ); double fDCos = cos( fAngle+fDeltaAngle ); appendDouble( fTempXScale*fDCos, aLine ); aLine.append( ' ' ); appendDouble( fDSin*fTempXScale, aLine ); aLine.append( ' ' ); appendDouble( -fDSin*fYScale, aLine ); aLine.append( ' ' ); appendDouble( fDCos*fYScale, aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPos+aDeltaPos, aLine ); aLine.append( " Tm" ); if( nLastMappedFont != pMappedFontObjects[n] ) { nLastMappedFont = pMappedFontObjects[n]; aLine.append( " /F" ); aLine.append( pMappedFontObjects[n] ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nFontHeight, aLine, true ); aLine.append( " Tf" ); } aLine.append( " <" ); appendHex( (sal_Int8)pMappedGlyphs[n], aLine ); aLine.append( "> Tj\r\n" ); } } else // normal case { // optimize use of Td vs. Tm if( fXScale == 1.0 && fCos == 1.0 && fSin == 0.0 ) { if( bFirst ) { m_aPages.back().appendPoint( aPos, aLine ); bFirst = false; } else { Point aDiff = aPos - aLastPos; m_aPages.back().appendMappedLength( aDiff.X(), aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( aDiff.Y(), aLine, true ); } aLine.append( " Td " ); aLastPos = aPos; } else { appendDouble( fXScale*fCos, aLine ); aLine.append( ' ' ); appendDouble( fSin*fXScale, aLine ); aLine.append( ' ' ); appendDouble( -fSin, aLine ); aLine.append( ' ' ); appendDouble( fCos, aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPos, aLine ); aLine.append( " Tm\r\n" ); } int nLast = 0; while( nLast < nGlyphs ) { while( ! pGlyphs[nLast] && nLast < nGlyphs ) nLast++; if( nLast >= nGlyphs ) break; int nNext = nLast+1; while( nNext < nGlyphs && pMappedFontObjects[ nNext ] == pMappedFontObjects[nLast] && pGlyphs[nNext] ) nNext++; if( nLastMappedFont != pMappedFontObjects[nLast] ) { aLine.append( "/F" ); aLine.append( pMappedFontObjects[nLast] ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nFontHeight, aLine, true ); aLine.append( " Tf " ); nLastMappedFont = pMappedFontObjects[nLast]; } aLine.append( "<" ); for( int i = nLast; i < nNext; i++ ) { appendHex( (sal_Int8)pMappedGlyphs[i], aLine ); if( i && (i % 35) == 0 ) aLine.append( "\r\n" ); } aLine.append( "> Tj\r\n" ); nLast = nNext; } } } // end textobject aLine.append( "ET\r\n" ); if( bPop ) aLine.append( "Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); // draw eventual textlines FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout(); FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline(); if( bTextLines && ( ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) || ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW ) ) ) { BOOL bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont ); if( m_aCurrentPDFState.m_aFont.IsWordLineMode() ) { Point aPos, aStartPt; long nWidth = 0, nAdvance=0; for( int nStart = 0;;) { long nGlyphIndex; if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) break; if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) { if( !nWidth ) aStartPt = aPos; nWidth += nAdvance; } else if( nWidth > 0 ) { drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), eStrikeout, eUnderline, bUnderlineAbove ); nWidth = 0; } } if( nWidth > 0 ) { drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), eStrikeout, eUnderline, bUnderlineAbove ); } } else { Point aStartPt = rLayout.GetDrawPosition(); int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel(); drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), eStrikeout, eUnderline, bUnderlineAbove ); } } // write eventual emphasis marks if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) { PolyPolygon aEmphPoly; Rectangle aEmphRect1; Rectangle aEmphRect2; long nEmphYOff; long nEmphWidth; long nEmphHeight; BOOL bEmphPolyLine; FontEmphasisMark nEmphMark; push( PUSH_ALL ); aLine.setLength( 0 ); aLine.append( "q\r\n" ); nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont ); if ( nEmphMark & EMPHASISMARK_POS_BELOW ) nEmphHeight = m_pReferenceDevice->mnEmphasisDescent; else nEmphHeight = m_pReferenceDevice->mnEmphasisAscent; m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly, bEmphPolyLine, aEmphRect1, aEmphRect2, nEmphYOff, nEmphWidth, nEmphMark, nEmphHeight, m_pReferenceDevice->mpFontEntry->mnOrientation ); if ( bEmphPolyLine ) { setLineColor( m_aCurrentPDFState.m_aFont.GetColor() ); setFillColor( Color( COL_TRANSPARENT ) ); } else { setFillColor( m_aCurrentPDFState.m_aFont.GetColor() ); setLineColor( Color( COL_TRANSPARENT ) ); } writeBuffer( aLine.getStr(), aLine.getLength() ); Point aOffset = Point(0,0); if ( nEmphMark & EMPHASISMARK_POS_BELOW ) aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff; else aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff; long nEmphWidth2 = nEmphWidth / 2; long nEmphHeight2 = nEmphHeight / 2; aOffset += Point( nEmphWidth2, nEmphHeight2 ); if ( eAlign == ALIGN_BOTTOM ) aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent; else if ( eAlign == ALIGN_TOP ) aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent; for( int nStart = 0;;) { Point aPos; long nGlyphIndex, nAdvance; if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) break; if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) { Point aAdjOffset = aOffset; aAdjOffset.X() += (nAdvance - nEmphWidth) / 2; aAdjOffset = Point( (int)(fXScale * fCos * (double)aAdjOffset.X() + fSin * (double)aAdjOffset.Y()), -(int)(fXScale * fSin * (double)aAdjOffset.X() - fCos * (double)aAdjOffset.Y()) ); aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 ); aPos += aAdjOffset; aPos = m_pReferenceDevice->PixelToLogic( aPos ); drawEmphasisMark( aPos.X(), aPos.Y(), aEmphPoly, bEmphPolyLine, aEmphRect1, aEmphRect2 ); } } writeBuffer( "Q\r\n", 3 ); pop(); } } void PDFWriterImpl::drawEmphasisMark( long nX, long nY, const PolyPolygon& rPolyPoly, BOOL bPolyLine, const Rectangle& rRect1, const Rectangle& rRect2 ) { // TODO: pass nWidth as width of this mark long nWidth = 0; if ( rPolyPoly.Count() ) { if ( bPolyLine ) { Polygon aPoly = rPolyPoly.GetObject( 0 ); aPoly.Move( nX, nY ); drawPolyLine( aPoly ); } else { PolyPolygon aPolyPoly = rPolyPoly; aPolyPoly.Move( nX, nY ); drawPolyPolygon( aPolyPoly ); } } if ( !rRect1.IsEmpty() ) { Rectangle aRect( Point( nX+rRect1.Left(), nY+rRect1.Top() ), rRect1.GetSize() ); drawRectangle( aRect ); } if ( !rRect2.IsEmpty() ) { Rectangle aRect( Point( nX+rRect2.Left(), nY+rRect2.Top() ), rRect2.GetSize() ); drawRectangle( aRect ); } } void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) { MARK( "drawText" ); updateGraphicsState(); // get a layout from the OuputDevice's SalGraphics // this also enforces font substitution and sets the font on SalGraphics SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos ); if( pLayout ) { drawLayout( *pLayout, rText, bTextLines ); pLayout->Release(); } } void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const long* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) { MARK( "drawText with array" ); updateGraphicsState(); // get a layout from the OuputDevice's SalGraphics // this also enforces font substitution and sets the font on SalGraphics SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray ); if( pLayout ) { drawLayout( *pLayout, rText, bTextLines ); pLayout->Release(); } } void PDFWriterImpl::drawStretchText( const Point& rPos, ULONG nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) { MARK( "drawStretchText" ); updateGraphicsState(); // get a layout from the OuputDevice's SalGraphics // this also enforces font substitution and sets the font on SalGraphics SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth ); if( pLayout ) { drawLayout( *pLayout, rText, bTextLines ); pLayout->Release(); } } void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, USHORT nStyle, bool bTextLines ) { long nWidth = rRect.GetWidth(); long nHeight = rRect.GetHeight(); if ( nWidth <= 0 || nHeight <= 0 ) return; MARK( "drawText with rectangle" ); updateGraphicsState(); // clip with rectangle OStringBuffer aLine; aLine.append( "q " ); m_aPages.back().appendRect( rRect, aLine ); aLine.append( " W* n\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); // if disabled text is needed, put in here Point aPos = rRect.TopLeft(); long nTextHeight = m_pReferenceDevice->GetTextHeight(); TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); xub_StrLen nMnemonicPos = STRING_NOTFOUND; String aStr = rOrigStr; if ( nStyle & TEXT_DRAW_MNEMONIC ) aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos ); // multiline text if ( nStyle & TEXT_DRAW_MULTILINE ) { XubString aLastLine; ImplMultiTextLineInfo aMultiLineInfo; ImplTextLineInfo* pLineInfo; long nMaxTextWidth; xub_StrLen i; xub_StrLen nLines; xub_StrLen nFormatLines; if ( nTextHeight ) { nMaxTextWidth = m_pReferenceDevice->ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle ); nLines = (xub_StrLen)(nHeight/nTextHeight); nFormatLines = aMultiLineInfo.Count(); if ( !nLines ) nLines = 1; if ( nFormatLines > nLines ) { if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) { // handle last line nFormatLines = nLines-1; pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); aLastLine = aStr.Copy( pLineInfo->GetIndex() ); aLastLine.ConvertLineEnd( LINEEND_LF ); // replace line feed by space xub_StrLen nLastLineLen = aLastLine.Len(); for ( i = 0; i < nLastLineLen; i++ ) { if ( aLastLine.GetChar( i ) == _LF ) aLastLine.SetChar( i, ' ' ); } aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle ); nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM); nStyle |= TEXT_DRAW_TOP; } } // vertical alignment if ( nStyle & TEXT_DRAW_BOTTOM ) aPos.Y() += nHeight-(nFormatLines*nTextHeight); else if ( nStyle & TEXT_DRAW_VCENTER ) aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; // draw all lines excluding the last for ( i = 0; i < nFormatLines; i++ ) { pLineInfo = aMultiLineInfo.GetLine( i ); if ( nStyle & TEXT_DRAW_RIGHT ) aPos.X() += nWidth-pLineInfo->GetWidth(); else if ( nStyle & TEXT_DRAW_CENTER ) aPos.X() += (nWidth-pLineInfo->GetWidth())/2; xub_StrLen nIndex = pLineInfo->GetIndex(); xub_StrLen nLineLen = pLineInfo->GetLen(); drawText( aPos, aStr, nIndex, nLineLen, bTextLines ); // mnemonics should not appear in documents, // if the need arises, put them in here aPos.Y() += nTextHeight; aPos.X() = rRect.Left(); } // output last line left adjusted since it was shortened if ( aLastLine.Len() ) drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines ); } } else { long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); // Evt. Text kuerzen if ( nTextWidth > nWidth ) { if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) ) { aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle ); nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT); nStyle |= TEXT_DRAW_LEFT; nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); } } // vertical alignment if ( nStyle & TEXT_DRAW_RIGHT ) aPos.X() += nWidth-nTextWidth; else if ( nStyle & TEXT_DRAW_CENTER ) aPos.X() += (nWidth-nTextWidth)/2; if ( nStyle & TEXT_DRAW_BOTTOM ) aPos.Y() += nHeight-nTextHeight; else if ( nStyle & TEXT_DRAW_VCENTER ) aPos.Y() += (nHeight-nTextHeight)/2; // mnemonics should be inserted here if the need arises // draw the actual text drawText( aPos, aStr, 0, STRING_LEN, bTextLines ); } // reset clip region to original value aLine.setLength( 0 ); aLine.append( "Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop ) { MARK( "drawLine" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) return; OStringBuffer aLine; aLine.append( "q 0 w " ); m_aPages.back().appendPoint( rStart, aLine ); aLine.append( " m " ); m_aPages.back().appendPoint( rStop, aLine ); aLine.append( " l S Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo ) { MARK( "drawLine with LineInfo" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) return; #if 1 if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 ) { drawLine( rStart, rStop ); return; } OStringBuffer aLine; aLine.append( "q " ); m_aPages.back().appendLineInfo( rInfo, aLine ); m_aPages.back().appendPoint( rStart, aLine ); aLine.append( " m " ); m_aPages.back().appendPoint( rStop, aLine ); aLine.append( " l S Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); #else Polygon aPoly( 2 ); aPoly[ 0 ] = rStart; aPoly[ 1 ] = rStop; ImplLineConverter aLineCvt( aPoly, rInfo, NULL ); if( rInfo.GetWidth() > 1 ) { Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor; Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; setFillColor( m_aGraphicsStack.front().m_aLineColor ); setLineColor( Color( COL_TRANSPARENT ) ); for( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() ) drawPolygon( *pPoly ); setLineColor( aOldLineColor ); setFillColor( aOldFillColor ); } else { for( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() ) drawLine( (*pPoly)[0], (*pPoly)[1] ); } #endif } void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth ) { Point aDiff( rStop-rStart ); double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) ); if( fLen < 1.0 ) return; MARK( "drawWaveLine" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) return; OStringBuffer aLine( 512 ); aLine.append( "q " ); m_aPages.back().appendMappedLength( nLineWidth, aLine, true ); aLine.append( " w " ); appendDouble( (double)aDiff.X()/fLen, aLine ); aLine.append( ' ' ); appendDouble( -(double)aDiff.Y()/fLen, aLine ); aLine.append( ' ' ); appendDouble( (double)aDiff.Y()/fLen, aLine ); aLine.append( ' ' ); appendDouble( (double)aDiff.X()/fLen, aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( rStart, aLine ); aLine.append( " cm " ); m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine ); aLine.append( "Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x ) #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x ) void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, bool bUnderlineAbove ) { if ( !nWidth || ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) && ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) ) ) return; MARK( "drawTextLine" ); updateGraphicsState(); // note: units in pFontEntry are ref device pixel ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor; Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor(); long nLineHeight = 0; long nLinePos = 0; long nLinePos2 = 0; bool bNormalLines = true; if ( bNormalLines && ((eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X)) ) { String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" ); String aStrikeout = aStrikeoutChar; while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth ) aStrikeout.Append( aStrikeout ); // do not get broader than nWidth modulo 1 character while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth ) aStrikeout.Erase( 0, 1 ); aStrikeout.Append( aStrikeoutChar ); BOOL bShadow = m_aCurrentPDFState.m_aFont.IsShadow(); if( bShadow ) { Font aFont = m_aCurrentPDFState.m_aFont; aFont.SetShadow( FALSE ); setFont( aFont ); updateGraphicsState(); } drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false ); if( bShadow ) { Font aFont = m_aCurrentPDFState.m_aFont; aFont.SetShadow( TRUE ); setFont( aFont ); updateGraphicsState(); } switch( eUnderline ) { case UNDERLINE_NONE: case UNDERLINE_DONTKNOW: case UNDERLINE_SMALLWAVE: case UNDERLINE_WAVE: case UNDERLINE_DOUBLEWAVE: case UNDERLINE_BOLDWAVE: bNormalLines = false; } } Point aPos( rPos ); TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); if( eAlign == ALIGN_TOP ) aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent ); else if( eAlign == ALIGN_BOTTOM ) aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent ); OStringBuffer aLine( 512 ); // save GS aLine.append( "q " ); // rotate and translate matrix double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0; double fSin = sin( fAngle ); double fCos = cos( fAngle ); appendDouble( fCos, aLine ); aLine.append( ' ' ); appendDouble( fSin, aLine ); aLine.append( ' ' ); appendDouble( -fSin, aLine ); aLine.append( ' ' ); appendDouble( fCos, aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPos, aLine ); aLine.append( " cm\r\n" ); if ( aUnderlineColor.GetTransparency() != 0 ) aUnderlineColor = aStrikeoutColor; if ( (eUnderline == UNDERLINE_SMALLWAVE) || (eUnderline == UNDERLINE_WAVE) || (eUnderline == UNDERLINE_DOUBLEWAVE) || (eUnderline == UNDERLINE_BOLDWAVE) ) { appendStrokingColor( aUnderlineColor, aLine ); aLine.append( "\r\n" ); if ( bUnderlineAbove ) { if ( !pFontEntry->maMetric.mnAboveWUnderlineSize ) m_pReferenceDevice->ImplInitAboveTextLineSize(); nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset ); nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize ); } else { if ( !pFontEntry->maMetric.mnWUnderlineSize ) m_pReferenceDevice->ImplInitTextLineSize(); nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset ); nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize ); } if ( (eUnderline == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) ) nLineHeight = 3; long nLineWidth = nLineHeight; if ( eUnderline == UNDERLINE_BOLDWAVE ) nLineWidth = 3*nLineWidth/2; m_aPages.back().appendMappedLength( nLineWidth, aLine ); aLine.append( " w " ); if ( eUnderline == UNDERLINE_DOUBLEWAVE ) { long nOrgLineHeight = nLineHeight; nLineHeight /= 3; if ( nLineHeight < 2 ) { if ( nOrgLineHeight > 1 ) nLineHeight = 2; else nLineHeight = 1; } long nLineDY = nOrgLineHeight-(nLineHeight*2); if ( nLineDY < nLineWidth ) nLineDY = nLineWidth; long nLineDY2 = nLineDY/2; if ( !nLineDY2 ) nLineDY2 = 1; nLinePos -= nLineWidth-nLineDY2; m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); nLinePos += nLineWidth+nLineDY; m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); } else { if( eUnderline != UNDERLINE_BOLDWAVE ) nLinePos -= nLineWidth/2; m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine ); } if ( (eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW) ) bNormalLines = false; } if ( bNormalLines ) { if ( eUnderline > UNDERLINE_BOLDWAVE ) eUnderline = UNDERLINE_SINGLE; if ( (eUnderline == UNDERLINE_SINGLE) || (eUnderline == UNDERLINE_DOTTED) || (eUnderline == UNDERLINE_DASH) || (eUnderline == UNDERLINE_LONGDASH) || (eUnderline == UNDERLINE_DASHDOT) || (eUnderline == UNDERLINE_DASHDOTDOT) ) { if ( bUnderlineAbove ) { if ( !pFontEntry->maMetric.mnAboveUnderlineSize ) m_pReferenceDevice->ImplInitAboveTextLineSize(); nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize ); nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset ); } else { if ( !pFontEntry->maMetric.mnUnderlineSize ) m_pReferenceDevice->ImplInitTextLineSize(); nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize ); nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset ); } } else if ( (eUnderline == UNDERLINE_BOLD) || (eUnderline == UNDERLINE_BOLDDOTTED) || (eUnderline == UNDERLINE_BOLDDASH) || (eUnderline == UNDERLINE_BOLDLONGDASH) || (eUnderline == UNDERLINE_BOLDDASHDOT) || (eUnderline == UNDERLINE_BOLDDASHDOTDOT) ) { if ( bUnderlineAbove ) { if ( !pFontEntry->maMetric.mnAboveBUnderlineSize ) m_pReferenceDevice->ImplInitAboveTextLineSize(); nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize ); nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset ); } else { if ( !pFontEntry->maMetric.mnBUnderlineSize ) m_pReferenceDevice->ImplInitTextLineSize(); nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize ); nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset ); nLinePos += nLineHeight/2; } } else if ( eUnderline == UNDERLINE_DOUBLE ) { if ( bUnderlineAbove ) { if ( !pFontEntry->maMetric.mnAboveDUnderlineSize ) m_pReferenceDevice->ImplInitAboveTextLineSize(); nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize ); nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 ); nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 ); } else { if ( !pFontEntry->maMetric.mnDUnderlineSize ) m_pReferenceDevice->ImplInitTextLineSize(); nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize ); nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 ); nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 ); } } else nLineHeight = 0; if ( nLineHeight ) { m_aPages.back().appendMappedLength( nLineHeight, aLine, true ); aLine.append( " w " ); appendStrokingColor( aUnderlineColor, aLine ); aLine.append( "\r\n" ); if ( (eUnderline == UNDERLINE_DOTTED) || (eUnderline == UNDERLINE_BOLDDOTTED) ) { aLine.append( "[ " ); m_aPages.back().appendMappedLength( nLineHeight, aLine, false ); aLine.append( " ] 0 d\r\n" ); } else if ( (eUnderline == UNDERLINE_DASH) || (eUnderline == UNDERLINE_LONGDASH) || (eUnderline == UNDERLINE_BOLDDASH) || (eUnderline == UNDERLINE_BOLDLONGDASH) ) { sal_Int32 nDashLength = 4*nLineHeight; sal_Int32 nVoidLength = 2*nLineHeight; switch( eUnderline ) { case UNDERLINE_LONGDASH: case UNDERLINE_BOLDLONGDASH: nDashLength = 8*nLineHeight; } aLine.append( "[ " ); m_aPages.back().appendMappedLength( nDashLength, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); aLine.append( " ] 0 d\r\n" ); } else if ( (eUnderline == UNDERLINE_DASHDOT) || (eUnderline == UNDERLINE_BOLDDASHDOT) ) { sal_Int32 nDashLength = 4*nLineHeight; sal_Int32 nVoidLength = 2*nLineHeight; aLine.append( "[ " ); m_aPages.back().appendMappedLength( nDashLength, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nLineHeight, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); aLine.append( " ] 0 d\r\n" ); } else if ( (eUnderline == UNDERLINE_DASHDOTDOT) || (eUnderline == UNDERLINE_BOLDDASHDOTDOT) ) { sal_Int32 nDashLength = 4*nLineHeight; sal_Int32 nVoidLength = 2*nLineHeight; aLine.append( "[ " ); m_aPages.back().appendMappedLength( nDashLength, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nLineHeight, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nLineHeight, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); aLine.append( " ] 0 d\r\n" ); } aLine.append( "0 " ); m_aPages.back().appendMappedLength( -nLinePos, aLine, true ); aLine.append( " m " ); m_aPages.back().appendMappedLength( nWidth, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( -nLinePos, aLine, true ); aLine.append( " l S\r\n" ); if ( eUnderline == UNDERLINE_DOUBLE ) { aLine.append( "0 " ); m_aPages.back().appendMappedLength( -nLinePos2-nLineHeight, aLine, true ); aLine.append( " m " ); m_aPages.back().appendMappedLength( nWidth, aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( -nLinePos2-nLineHeight, aLine, true ); aLine.append( " l S\r\n" ); } } if ( eStrikeout > STRIKEOUT_X ) eStrikeout = STRIKEOUT_SINGLE; if ( eStrikeout == STRIKEOUT_SINGLE ) { if ( !pFontEntry->maMetric.mnStrikeoutSize ) m_pReferenceDevice->ImplInitTextLineSize(); nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize ); nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset ); } else if ( eStrikeout == STRIKEOUT_BOLD ) { if ( !pFontEntry->maMetric.mnBStrikeoutSize ) m_pReferenceDevice->ImplInitTextLineSize(); nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize ); nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset ); } else if ( eStrikeout == STRIKEOUT_DOUBLE ) { if ( !pFontEntry->maMetric.mnDStrikeoutSize ) m_pReferenceDevice->ImplInitTextLineSize(); nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize ); nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 ); nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 ); } else nLineHeight = 0; if ( nLineHeight ) { m_aPages.back().appendMappedLength( nLineHeight, aLine, true ); aLine.append( " w " ); appendStrokingColor( aStrikeoutColor, aLine ); aLine.append( "\r\n" ); aLine.append( "0 " ); m_aPages.back().appendMappedLength( -nLinePos, aLine, true ); aLine.append( " m " ); m_aPages.back().appendMappedLength( nWidth, aLine, true ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( -nLinePos, aLine, true ); aLine.append( " l S\r\n" ); if ( eStrikeout == STRIKEOUT_DOUBLE ) { aLine.append( "0 " ); m_aPages.back().appendMappedLength( -nLinePos2-nLineHeight, aLine, true ); aLine.append( " m " ); m_aPages.back().appendMappedLength( nWidth, aLine, true ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( -nLinePos2-nLineHeight, aLine, true ); aLine.append( " l S\r\n" ); } } } aLine.append( "Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawPolygon( const Polygon& rPoly ) { MARK( "drawPolygon" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) return; int nPoints = rPoly.GetSize(); OStringBuffer aLine( 20 * nPoints ); m_aPages.back().appendPolygon( rPoly, aLine ); if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) aLine.append( "B*\r\n" ); else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) aLine.append( "S\r\n" ); else aLine.append( "f*\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly ) { MARK( "drawPolyPolygon" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) return; int nPolygons = rPolyPoly.Count(); OStringBuffer aLine( 40 * nPolygons ); m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) aLine.append( "B*\r\n" ); else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) aLine.append( "S\r\n" ); else aLine.append( "f*\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent ) { MARK( "drawTransparent" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) return; if( m_eVersion < PDFWriter::PDF_1_4 ) { drawPolyPolygon( rPolyPoly ); return; } // create XObject m_aTransparentObjects.push_back( TransparencyEmit() ); m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect(); // convert rectangle to default user space m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); m_aTransparentObjects.back().m_nObject = createObject(); m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; // create XObject's content stream m_aPages.back().appendPolyPolygon( rPolyPoly, m_aTransparentObjects.back().m_aContentStream ); if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) && m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) ) m_aTransparentObjects.back().m_aContentStream.append( " B*\r\n" ); else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) ) m_aTransparentObjects.back().m_aContentStream.append( " S\r\n" ); else m_aTransparentObjects.back().m_aContentStream.append( " f*\r\n" ); OStringBuffer aLine( 80 ); // insert XObject aLine.append( "/Tr" ); aLine.append( m_aTransparentObjects.back().m_nObject ); aLine.append( " Do\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawRectangle( const Rectangle& rRect ) { MARK( "drawRectangle" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) return; OStringBuffer aLine( 40 ); m_aPages.back().appendRect( rRect, aLine ); if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) aLine.append( " B*\r\n" ); else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) aLine.append( " S\r\n" ); else aLine.append( " f*\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound ) { MARK( "drawRectangle with rounded edges" ); if( !nHorzRound && !nVertRound ) drawRectangle( rRect ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) return; if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 ) nHorzRound = rRect.GetWidth()/2; if( nVertRound > (sal_uInt32)rRect.GetWidth()/2 ) nVertRound = rRect.GetWidth()/2; Point aPoints[16]; const double kappa = 0.5522847498; const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5); const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5); aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() ); aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() ); aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() ); aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound ); aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky ); aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound ); aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky ); aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 ); aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() ); aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() ); aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() ); aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound ); aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky ); aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound ); aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky ); OStringBuffer aLine( 80 ); m_aPages.back().appendPoint( aPoints[1], aLine ); aLine.append( " m " ); m_aPages.back().appendPoint( aPoints[2], aLine ); aLine.append( " l " ); m_aPages.back().appendPoint( aPoints[3], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[4], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[5], aLine ); aLine.append( " c\r\n" ); m_aPages.back().appendPoint( aPoints[6], aLine ); aLine.append( " l " ); m_aPages.back().appendPoint( aPoints[7], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[8], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[9], aLine ); aLine.append( " c\r\n" ); m_aPages.back().appendPoint( aPoints[10], aLine ); aLine.append( " l " ); m_aPages.back().appendPoint( aPoints[11], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[12], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[13], aLine ); aLine.append( " c\r\n" ); m_aPages.back().appendPoint( aPoints[14], aLine ); aLine.append( " l " ); m_aPages.back().appendPoint( aPoints[15], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[0], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[1], aLine ); aLine.append( " c " ); if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) aLine.append( "b*\r\n" ); else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) aLine.append( "s\r\n" ); else aLine.append( "f*\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawEllipse( const Rectangle& rRect ) { MARK( "drawEllipse" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) return; Point aPoints[12]; const double kappa = 0.5522847498; const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5); const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5); aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() ); aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() ); aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 ); aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky ); aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky ); aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 ); aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() ); aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() ); aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 ); aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky ); aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky ); OStringBuffer aLine( 80 ); m_aPages.back().appendPoint( aPoints[1], aLine ); aLine.append( " m " ); m_aPages.back().appendPoint( aPoints[2], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[3], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[4], aLine ); aLine.append( " c\r\n" ); m_aPages.back().appendPoint( aPoints[5], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[6], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[7], aLine ); aLine.append( " c\r\n" ); m_aPages.back().appendPoint( aPoints[8], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[9], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[10], aLine ); aLine.append( " c\r\n" ); m_aPages.back().appendPoint( aPoints[11], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[0], aLine ); aLine.append( ' ' ); m_aPages.back().appendPoint( aPoints[1], aLine ); aLine.append( " c " ); if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) aLine.append( "b*\r\n" ); else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) aLine.append( "s\r\n" ); else aLine.append( "f*\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } static double calcAngle( const Rectangle& rRect, const Point& rPoint ) { Point aOrigin((rRect.TopLeft().X()+rRect.BottomRight().X()+1)/2, (rRect.TopLeft().Y()+rRect.BottomRight().Y()+1)/2); Point aPoint = rPoint - aOrigin; aPoint.Y() = -aPoint.Y(); if( rRect.GetWidth() > rRect.GetHeight() ) aPoint.Y() = aPoint.Y()*rRect.GetWidth()/rRect.GetHeight(); else if( rRect.GetHeight() > rRect.GetWidth() ) aPoint.X() = aPoint.X()*rRect.GetHeight()/rRect.GetWidth(); return atan2( (double)aPoint.Y(), (double)aPoint.X() ); } void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord ) { MARK( "drawArc" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) return; // calculate start and stop angles double fStartAngle = calcAngle( rRect, rStart ); double fStopAngle = calcAngle( rRect, rStop ); while( fStopAngle < fStartAngle ) fStopAngle += 2.0*M_PI; int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1; double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments; double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0); double halfWidth = (double)rRect.GetWidth()/2.0; double halfHeight = (double)rRect.GetHeight()/2.0; Point aCenter( (rRect.TopLeft().X()+rRect.BottomRight().X()+1)/2, (rRect.TopLeft().Y()+rRect.BottomRight().Y()+1)/2 ); OStringBuffer aLine( 30*nFragments ); Point aPoint( (int)(halfWidth * cos(fStartAngle) ), -(int)(halfHeight * sin(fStartAngle) ) ); aPoint += aCenter; m_aPages.back().appendPoint( aPoint, aLine ); aLine.append( " m " ); for( int i = 0; i < nFragments; i++ ) { double fStartFragment = fStartAngle + (double)i*fFragmentDelta; double fStopFragment = fStartFragment + fFragmentDelta; aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ), -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) ); aPoint += aCenter; m_aPages.back().appendPoint( aPoint, aLine ); aLine.append( ' ' ); aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ), -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) ); aPoint += aCenter; m_aPages.back().appendPoint( aPoint, aLine ); aLine.append( ' ' ); aPoint = Point( (int)(halfWidth * cos(fStopFragment) ), -(int)(halfHeight * sin(fStopFragment) ) ); aPoint += aCenter; m_aPages.back().appendPoint( aPoint, aLine ); aLine.append( " c\r\n" ); } if( bWithPie ) { m_aPages.back().appendPoint( aCenter, aLine ); aLine.append( " l " ); } if( ! bWithChord && ! bWithPie ) aLine.append( "S\r\n" ); else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) aLine.append( "b*\r\n" ); else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) aLine.append( "s\r\n" ); else aLine.append( "f*\r\n" ); #ifdef DEBUG_ARC aLine.append( "0 1 1 RG " ); m_aPages.back().appendPoint( rStart, aLine ); aLine.append( " m " ); m_aPages.back().appendPoint( aCenter, aLine ); aLine.append( " l S 1 0 0 RG " ); m_aPages.back().appendPoint( aCenter, aLine ); aLine.append( " m " ); m_aPages.back().appendPoint( rStop, aLine ); aLine.append( " l S\r\n" ); aLine.append( "0 0 0 RG " ); m_aPages.back().appendRect( rRect ); aLine.append( " S\r\n" ); #endif writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawPolyLine( const Polygon& rPoly ) { MARK( "drawPolyLine" ); int nPoints = rPoly.GetSize(); if( nPoints < 2 ) return; updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) return; OStringBuffer aLine( 20 * nPoints ); aLine.append( "q 0 w " ); m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] ); aLine.append( "S Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo ) { MARK( "drawPolyLine with LineInfo" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) return; OStringBuffer aLine; aLine.append( "q " ); m_aPages.back().appendLineInfo( rInfo,aLine ); writeBuffer( aLine.getStr(), aLine.getLength() ); drawPolyLine( rPoly ); writeBuffer( "Q\r\n", 3 ); } void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor ) { MARK( "drawPixel" ); Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor ); if( aColor == Color( COL_TRANSPARENT ) ) return; // pixels are drawn in line color, so have to set // the nonstroking color to line color Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; setFillColor( aColor ); updateGraphicsState(); OStringBuffer aLine( 20 ); m_aPages.back().appendPoint( rPoint, aLine ); aLine.append( " 1 1 re f\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); setFillColor( aOldFillColor ); } void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors ) { MARK( "drawPixel with Polygon" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors ) return; int nPoints = rPoints.GetSize(); OStringBuffer aLine( nPoints*40 ); aLine.append( "q " ); if( ! pColors ) { appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine ); aLine.append( ' ' ); } for( int i = 0; i < nPoints; i++ ) { if( pColors ) { if( pColors[i] == Color( COL_TRANSPARENT ) ) continue; appendNonStrokingColor( pColors[i], aLine ); aLine.append( ' ' ); } m_aPages.back().appendPoint( rPoints[i], aLine ); aLine.append( " 1 1 re f\r\n" ); } aLine.append( "Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } class AccessReleaser { BitmapReadAccess* m_pAccess; public: AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){} ~AccessReleaser() { delete m_pAccess; } }; bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject ) { CHECK_RETURN( updateObject( rObject.m_nObject ) ); OStringBuffer aProlog; sal_Int32 nStateObject = createObject(); aProlog.append( "/EGS" ); aProlog.append( nStateObject ); aProlog.append( " gs\r\n" ); OStringBuffer aLine( 512 ); CHECK_RETURN( updateObject( rObject.m_nObject ) ); aLine.append( rObject.m_nObject ); aLine.append( " 0 obj\r\n" "<< /Type /XObject\r\n" " /Subtype /Form\r\n" " /BBox [ " ); appendDouble( ((double)rObject.m_aBoundRect.TopLeft().X())/10.0, aLine ); aLine.append( ' ' ); appendDouble( ((double)rObject.m_aBoundRect.TopLeft().Y())/10.0, aLine ); aLine.append( ' ' ); appendDouble( ((double)rObject.m_aBoundRect.BottomRight().X())/10.0, aLine ); aLine.append( ' ' ); appendDouble( ((double)rObject.m_aBoundRect.BottomRight().Y()+1)/10.0, aLine ); aLine.append( " ]\r\n" " /Resources << /ExtGState << /EGS" ); aLine.append( nStateObject ); aLine.append( ' ' ); aLine.append( nStateObject ); aLine.append( " 0 R >> >>\r\n" ); aLine.append( " /Group << /S /Transparency /CS /DeviceRGB >>\r\n" ); aLine.append( " /Length " ); aLine.append( (sal_Int32)(rObject.m_aContentStream.getLength()+aProlog.getLength()) ); aLine.append( "\r\n" ">>\r\n" "stream\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); CHECK_RETURN( writeBuffer( aProlog.getStr(), aProlog.getLength() ) ); CHECK_RETURN( writeBuffer( rObject.m_aContentStream.getStr(), rObject.m_aContentStream.getLength() ) ); aLine.setLength( 0 ); aLine.append( "endstream\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); // write ExtGState dict for this XObject aLine.setLength( 0 ); CHECK_RETURN( updateObject( nStateObject ) ); aLine.append( nStateObject ); aLine.append( " 0 obj\r\n" "<< /CA " ); appendDouble( rObject.m_fAlpha, aLine ); aLine.append( "\r\n" " /ca " ); appendDouble( rObject.m_fAlpha, aLine ); aLine.append( "\r\n" ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); return true; } bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject ) { sal_Int32 nFunctionObject = createObject(); CHECK_RETURN( updateObject( nFunctionObject ) ); OutputDevice* pRefDevice = getReferenceDevice(); pRefDevice->Push( PUSH_ALL ); if( rObject.m_aSize.Width() > pRefDevice->GetOutputSizePixel().Width() ) rObject.m_aSize.Width() = pRefDevice->GetOutputSizePixel().Width(); if( rObject.m_aSize.Height() > pRefDevice->GetOutputSizePixel().Height() ) rObject.m_aSize.Height() = pRefDevice->GetOutputSizePixel().Height(); pRefDevice->SetMapMode( MapMode( MAP_PIXEL ) ); pRefDevice->DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient ); Bitmap aSample = pRefDevice->GetBitmap( Point( 0, 0 ), rObject.m_aSize ); BitmapReadAccess* pAccess = aSample.AcquireReadAccess(); AccessReleaser aReleaser( pAccess ); Size aSize = aSample.GetSizePixel(); sal_Int32 nStreamLengthObject = createObject(); OStringBuffer aLine( 120 ); aLine.append( nFunctionObject ); aLine.append( " 0 obj\r\n" "<< /FunctionType 0\r\n" " /Domain [ 0 1 0 1 ]\r\n" " /Size [ " ); aLine.append( (sal_Int32)aSize.Width() ); aLine.append( ' ' ); aLine.append( (sal_Int32)aSize.Height() ); aLine.append( " ]\r\n" " /BitsPerSample 8\r\n" " /Range [ 0 1 0 1 0 1 ]\r\n" " /Length " ); aLine.append( nStreamLengthObject ); aLine.append( " 0 R\r\n" #ifdef ENABLE_COMPRESSION " /Filter /FlateDecode\r\n" #endif ">>\r\n" "stream\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); sal_uInt64 nStartStreamPos = 0; CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) ); beginCompression(); for( int y = 0; y < aSize.Height(); y++ ) { for( int x = 0; x < aSize.Width(); x++ ) { sal_uInt8 aCol[3]; BitmapColor aColor = pAccess->GetColor( y, x ); aCol[0] = aColor.GetRed(); aCol[1] = aColor.GetGreen(); aCol[2] = aColor.GetBlue(); CHECK_RETURN( writeBuffer( aCol, 3 ) ); } } endCompression(); sal_uInt64 nEndStreamPos = 0; CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) ); aLine.setLength( 0 ); aLine.append( "\r\nendstream\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); // write stream length CHECK_RETURN( updateObject( nStreamLengthObject ) ); aLine.setLength( 0 ); aLine.append( nStreamLengthObject ); aLine.append( " 0 obj\r\n" ); aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) ); aLine.append( "\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); CHECK_RETURN( updateObject( rObject.m_nObject ) ); aLine.setLength( 0 ); aLine.append( rObject.m_nObject ); aLine.append( " 0 obj\r\n" "<< /ShadingType 1\r\n" " /ColorSpace /DeviceRGB\r\n" " /AntiAlias true\r\n" " /Domain [ 0 1 0 1 ]\r\n" " /Matrix [ " ); aLine.append( (sal_Int32)aSize.Width() ); aLine.append( " 0 0 " ); aLine.append( (sal_Int32)aSize.Height() ); aLine.append( " 0 0 ]\r\n" " /Function " ); aLine.append( nFunctionObject ); aLine.append( " 0 R\r\n" ">>\r\n" "endobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); pRefDevice->Pop(); return true; } bool PDFWriterImpl::writeJPG( JPGEmit& rObject ) { CHECK_RETURN( rObject.m_pStream ); CHECK_RETURN( updateObject( rObject.m_nObject ) ); sal_Int32 nLength = 0; rObject.m_pStream->Seek( STREAM_SEEK_TO_END ); nLength = rObject.m_pStream->Tell(); rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN ); sal_Int32 nMaskObject = 0; if( !!rObject.m_aMask ) { if( rObject.m_aMask.GetBitCount() == 1 || ( rObject.m_aMask.GetBitCount() == 8 && m_eVersion >= PDFWriter::PDF_1_4 ) ) nMaskObject = createObject(); } OStringBuffer aLine(80); aLine.append( rObject.m_nObject ); aLine.append( " 0 obj\r\n" "<< /Type /XObject\r\n" " /Subtype /Image\r\n" " /Width " ); aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() ); aLine.append( "\r\n" " /Height " ); aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() ); aLine.append( "\r\n" " /BitsPerComponent 8\r\n" " /ColorSpace /DeviceRGB\r\n" " /Filter /DCTDecode\r\n" " /Length " ); aLine.append( nLength ); if( nMaskObject ) { aLine.append( rObject.m_aMask.GetBitCount() == 1 ? "\r\n /Mask " : "\r\n /SMask " ); aLine.append( nMaskObject ); aLine.append( " 0 R" ); } aLine.append( "\r\n>>\r\nstream\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) ); aLine.setLength( 0 ); aLine.append( "\r\nendstream\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); if( nMaskObject ) { BitmapEmit aEmit; aEmit.m_nObject = nMaskObject; if( rObject.m_aMask.GetBitCount() == 1 ) aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask ); else if( rObject.m_aMask.GetBitCount() == 8 ) aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) ); writeBitmapObject( aEmit, true ); } return true; } bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) { CHECK_RETURN( updateObject( rObject.m_nObject ) ); Bitmap aBitmap; Color aTransparentColor( COL_TRANSPARENT ); bool bWriteMask = false; if( ! bMask ) { aBitmap = rObject.m_aBitmap.GetBitmap(); if( rObject.m_aBitmap.IsAlpha() ) { if( m_eVersion >= PDFWriter::PDF_1_4 ) bWriteMask = true; // else draw without alpha channel } else { switch( rObject.m_aBitmap.GetTransparentType() ) { case TRANSPARENT_NONE: // comes from drawMask function if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask ) bMask = true; break; case TRANSPARENT_COLOR: aTransparentColor = rObject.m_aBitmap.GetTransparentColor(); break; case TRANSPARENT_BITMAP: bWriteMask = true; break; } } } else { if( m_eVersion < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() ) { aBitmap = rObject.m_aBitmap.GetMask(); aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); } else if( aBitmap.GetBitCount() != 8 ) { aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap(); aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS ); DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" ); } } BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess(); AccessReleaser aReleaser( pAccess ); bool bTrueColor; sal_Int32 nBitsPerComponent; switch( aBitmap.GetBitCount() ) { case 1: case 2: case 4: case 8: bTrueColor = false; nBitsPerComponent = aBitmap.GetBitCount(); break; default: bTrueColor = true; nBitsPerComponent = 8; break; } sal_Int32 nStreamLengthObject = createObject(); sal_Int32 nMaskObject = 0; OStringBuffer aLine(80); aLine.append( rObject.m_nObject ); aLine.append( " 0 obj\r\n" "<< /Type /XObject\r\n" " /Subtype /Image\r\n" " /Width " ); aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); aLine.append( "\r\n" " /Height " ); aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() ); aLine.append( "\r\n" " /BitsPerComponent " ); aLine.append( nBitsPerComponent ); aLine.append( "\r\n" " /Length " ); aLine.append( nStreamLengthObject ); aLine.append( " 0 R\r\n" ); #ifdef ENABLE_COMPRESSION aLine.append( " /Filter /FlateDecode\r\n" ); #endif if( ! bMask ) { aLine.append( " /ColorSpace " ); if( bTrueColor ) aLine.append( "/DeviceRGB\r\n" ); else { aLine.append( "[ /Indexed /DeviceRGB " ); aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) ); aLine.append( " <\r\n" ); for( int i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) { const BitmapColor& rColor = pAccess->GetPaletteColor( i ); appendHex( rColor.GetRed(), aLine ); appendHex( rColor.GetGreen(), aLine ); appendHex( rColor.GetBlue(), aLine ); if( (i+1) & 7 ) aLine.append( ' ' ); else aLine.append( "\r\n" ); } aLine.append( "> ]\r\n" ); } } else { if( aBitmap.GetBitCount() == 1 ) { aLine.append( " /ImageMask true\r\n" ); sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); if( nBlackIndex ) aLine.append( " /Decode [ 1 0 ]\r\n" ); else aLine.append( " /Decode [ 0 1 ]\r\n" ); } else if( aBitmap.GetBitCount() == 8 ) { aLine.append( " /ColorSpace /DeviceGray\r\n" " /Decode [ 1 0 ]\r\n" ); } } if( ! bMask && m_eVersion > PDFWriter::PDF_1_2 ) { if( bWriteMask ) { nMaskObject = createObject(); if( rObject.m_aBitmap.IsAlpha() && m_eVersion > PDFWriter::PDF_1_3 ) aLine.append( " /SMask " ); else aLine.append( " /Mask " ); aLine.append( nMaskObject ); aLine.append( " 0 R\r\n" ); } else if( aTransparentColor != Color( COL_TRANSPARENT ) ) { aLine.append( " /Mask [ " ); if( bTrueColor ) { aLine.append( (sal_Int32)aTransparentColor.GetRed() ); aLine.append( ' ' ); aLine.append( (sal_Int32)aTransparentColor.GetRed() ); aLine.append( ' ' ); aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); aLine.append( ' ' ); aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); aLine.append( ' ' ); aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); aLine.append( ' ' ); aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); } else { sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) ); aLine.append( nIndex ); } aLine.append( " ]\r\n" ); } } aLine.append( ">>\r\n" "stream\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); sal_uInt64 nStartPos = 0; CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) ); beginCompression(); if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ) { const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U ); for( int i = 0; i < pAccess->Height(); i++ ) { CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) ); } } else { const int nScanLineBytes = pAccess->Width()*3; sal_uInt8 *pCol = (sal_uInt8*)rtl_allocateMemory( nScanLineBytes ); for( int y = 0; y < pAccess->Height(); y++ ) { for( int x = 0; x < pAccess->Width(); x++ ) { BitmapColor aColor = pAccess->GetColor( y, x ); pCol[3*x+0] = aColor.GetRed(); pCol[3*x+1] = aColor.GetGreen(); pCol[3*x+2] = aColor.GetBlue(); } CHECK_RETURN( writeBuffer( pCol, nScanLineBytes ) ); } rtl_freeMemory( pCol ); } endCompression(); sal_uInt64 nEndPos = 0; CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) ); aLine.setLength( 0 ); aLine.append( "\r\nendstream\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); CHECK_RETURN( updateObject( nStreamLengthObject ) ); aLine.setLength( 0 ); aLine.append( nStreamLengthObject ); aLine.append( " 0 obj\r\n" ); aLine.append( (sal_Int64)(nEndPos-nStartPos) ); aLine.append( "\r\nendobj\r\n\r\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); if( nMaskObject ) { BitmapEmit aEmit; aEmit.m_nObject = nMaskObject; aEmit.m_aBitmap = rObject.m_aBitmap; return writeBitmapObject( aEmit, true ); } return true; } void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask ) { MARK( "drawJPGBitmap" ); OStringBuffer aLine( 80 ); updateGraphicsState(); SvMemoryStream* pStream = new SvMemoryStream; rDCTData.Seek( 0 ); *pStream << rDCTData; pStream->Seek( STREAM_SEEK_TO_END ); BitmapID aID; aID.m_aPixelSize = rSizePixel; aID.m_nSize = pStream->Tell(); pStream->Seek( STREAM_SEEK_TO_BEGIN ); aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize ); if( ! rMask.IsEmpty() ) aID.m_nMaskChecksum = rMask.GetChecksum(); std::list< JPGEmit >::const_iterator it; for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it ) ; if( it == m_aJPGs.end() ) { m_aJPGs.push_front( JPGEmit() ); JPGEmit& rEmit = m_aJPGs.front(); rEmit.m_nObject = createObject(); rEmit.m_aID = aID; rEmit.m_pStream = pStream; if( !! rMask && rMask.GetSizePixel() == rSizePixel ) rEmit.m_aMask = rMask; it = m_aJPGs.begin(); } else delete pStream; aLine.append( "q " ); m_aPages.back().appendMappedLength( rTargetArea.GetWidth(), aLine, false ); aLine.append( " 0 0 " ); m_aPages.back().appendMappedLength( rTargetArea.GetHeight(), aLine, true ); aLine.append( ' ' ); m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine ); aLine.append( " cm\r\n /Im" ); aLine.append( it->m_nObject ); aLine.append( " Do Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor ) { OStringBuffer aLine( 80 ); updateGraphicsState(); aLine.append( "q " ); if( rFillColor != Color( COL_TRANSPARENT ) ) { appendNonStrokingColor( rFillColor, aLine ); aLine.append( ' ' ); } m_aPages.back().appendMappedLength( rDestSize.Width(), aLine, false ); aLine.append( " 0 0 " ); m_aPages.back().appendMappedLength( rDestSize.Height(), aLine, true ); aLine.append( ' ' ); m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine ); aLine.append( " cm\r\n /Im" ); aLine.append( rBitmap.m_nObject ); aLine.append( " Do Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& rBitmap, bool bDrawMask ) { BitmapID aID; aID.m_aPixelSize = rBitmap.GetSizePixel(); aID.m_nSize = rBitmap.GetBitCount(); aID.m_nChecksum = rBitmap.GetBitmap().GetChecksum(); aID.m_nMaskChecksum = 0; if( rBitmap.IsAlpha() ) aID.m_nMaskChecksum = rBitmap.GetAlpha().GetChecksum(); else { Bitmap aMask = rBitmap.GetMask(); if( ! aMask.IsEmpty() ) aID.m_nMaskChecksum = aMask.GetChecksum(); } for( std::list< BitmapEmit >::const_iterator it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it ) { if( aID == it->m_aID ) return *it; } m_aBitmaps.push_front( BitmapEmit() ); m_aBitmaps.front().m_aID = aID; m_aBitmaps.front().m_aBitmap = rBitmap; m_aBitmaps.front().m_nObject = createObject(); m_aBitmaps.front().m_bDrawMask = bDrawMask; return m_aBitmaps.front(); } void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap ) { MARK( "drawBitmap (Bitmap)" ); const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) ); drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); } void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap ) { MARK( "drawBitmap (BitmapEx)" ); const BitmapEmit& rEmit = createBitmapEmit( rBitmap ); drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); } void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor ) { MARK( "drawMask" ); Bitmap aBitmap( rBitmap ); if( aBitmap.GetBitCount() > 1 ) aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true ); drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor ); } sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize ) { Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode, MapMode( MAP_POINT ), getReferenceDevice(), rSize ) ); // check if we already have this gradient for( std::list::iterator it = m_aGradients.begin(); it != m_aGradients.end(); ++it ) { if( it->m_aGradient == rGradient ) { if( it->m_aSize.Width() < aPtSize.Width() ) it->m_aSize.Width() = aPtSize.Width(); if( it->m_aSize.Height() <= aPtSize.Height() ) it->m_aSize.Height() = aPtSize.Height(); return it->m_nObject; } } m_aGradients.push_back( GradientEmit() ); m_aGradients.back().m_aGradient = rGradient; m_aGradients.back().m_nObject = createObject(); m_aGradients.back().m_aSize = aPtSize; return m_aGradients.back().m_nObject; } void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient ) { MARK( "drawGradient (Rectangle)" ); if( m_eVersion == PDFWriter::PDF_1_2 ) { drawRectangle( rRect ); return; } sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() ); Point aTranslate( rRect.BottomLeft() ); aTranslate += Point( 0, 1 ); updateGraphicsState(); OStringBuffer aLine( 80 ); aLine.append( "q 1 0 0 1 " ); m_aPages.back().appendPoint( aTranslate, aLine ); aLine.append( " cm " ); // if a stroke is appended reset the clip region before stroke if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) aLine.append( "q " ); aLine.append( "0 0 " ); m_aPages.back().appendMappedLength( rRect.GetWidth(), aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( rRect.GetHeight(), aLine, true ); aLine.append( " re W n\r\n" ); aLine.append( "/P" ); aLine.append( nGradient ); aLine.append( " sh " ); if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) { aLine.append( "Q 0 0 " ); m_aPages.back().appendMappedLength( rRect.GetWidth(), aLine, false ); aLine.append( ' ' ); m_aPages.back().appendMappedLength( rRect.GetHeight(), aLine, true ); aLine.append( " re S " ); } aLine.append( "Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient ) { MARK( "drawGradient (PolyPolygon)" ); if( m_eVersion == PDFWriter::PDF_1_2 ) { drawPolyPolygon( rPolyPoly ); return; } sal_Int32 nGradient = createGradient( rGradient, rPolyPoly.GetBoundRect().GetSize() ); updateGraphicsState(); Rectangle aBoundRect = rPolyPoly.GetBoundRect(); Point aTranslate = aBoundRect.BottomLeft() + Point( 0, 1 ); int nPolygons = rPolyPoly.Count(); OStringBuffer aLine( 80*nPolygons ); aLine.append( "q " ); // set PolyPolygon as clip path m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); aLine.append( "W* n\r\n" ); aLine.append( "1 0 0 1 " ); m_aPages.back().appendPoint( aTranslate, aLine ); aLine.append( " cm\r\n" ); aLine.append( "/P" ); aLine.append( nGradient ); aLine.append( " sh Q\r\n" ); if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) { // and draw the surrounding path m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); aLine.append( "S\r\n" ); } writeBuffer( aLine.getStr(), aLine.getLength() ); } sal_Int32 PDFWriterImpl::createHatch( const Hatch& rHatch ) { // check if we already have this gradient for( std::list::iterator it = m_aHatches.begin(); it != m_aHatches.end(); ++it ) { if( it->m_aHatch == rHatch && it->m_aMapMode == m_aCurrentPDFState.m_aMapMode ) return it->m_nObject; } m_aHatches.push_back( HatchEmit() ); m_aHatches.back().m_aHatch = rHatch; m_aHatches.back().m_nObject = createObject(); m_aHatches.back().m_aMapMode = m_aCurrentPDFState.m_aMapMode; return m_aHatches.back().m_nObject; } void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch ) { MARK( "drawHatch" ); updateGraphicsState(); if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) return; sal_Int32 nHatch = createHatch( rHatch ); OStringBuffer aLine( 256 ); aLine.append( "q /Pattern cs /P" ); aLine.append( nHatch ); aLine.append( " scn\r\n" ); m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) aLine.append( " B* " ); else aLine.append( " f* " ); aLine.append( "Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall ) { MARK( "drawWallpaper" ); bool bDrawColor = false; bool bDrawGradient = false; bool bDrawBitmap = false; BitmapEx aBitmap; Point aBmpPos = rRect.TopLeft(); Size aBmpSize; if( rWall.IsBitmap() ) { aBitmap = rWall.GetBitmap(); aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(), getMapMode(), getReferenceDevice(), aBitmap.GetPrefSize() ); Rectangle aRect( rRect ); if( rWall.IsRect() ) { aRect = rWall.GetRect(); aBmpPos = aRect.TopLeft(); aBmpSize = aRect.GetSize(); } if( rWall.GetStyle() != WALLPAPER_SCALE ) { if( rWall.GetStyle() != WALLPAPER_TILE ) { bDrawBitmap = true; if( rWall.IsGradient() ) bDrawGradient = true; else bDrawColor = true; switch( rWall.GetStyle() ) { case WALLPAPER_TOPLEFT: break; case WALLPAPER_TOP: aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; break; case WALLPAPER_LEFT: aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; break; case WALLPAPER_TOPRIGHT: aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); break; case WALLPAPER_CENTER: aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; break; case WALLPAPER_RIGHT: aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; break; case WALLPAPER_BOTTOMLEFT: aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); break; case WALLPAPER_BOTTOM: aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); break; case WALLPAPER_BOTTOMRIGHT: aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); break; } } else { // push the bitmap const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) ); // convert to page coordinates; this needs to be done here // since the emit does not know the page anymore Rectangle aConvertRect( aBmpPos, aBmpSize ); m_aPages.back().convertRect( aConvertRect ); // push the pattern m_aTilings.push_back( BitmapPatternEmit() ); m_aTilings.back().m_nObject = createObject(); m_aTilings.back().m_nBitmapObject = rEmit.m_nObject; m_aTilings.back().m_aRectangle = aConvertRect; updateGraphicsState(); // fill a rRect with the pattern OStringBuffer aLine( 100 ); aLine.append( "q /Pattern cs /P" ); aLine.append( m_aTilings.back().m_nObject ); aLine.append( " scn " ); m_aPages.back().appendRect( rRect, aLine ); aLine.append( " f Q\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); } } else { aBmpPos = aRect.TopLeft(); aBmpSize = aRect.GetSize(); bDrawBitmap = true; } if( aBitmap.IsTransparent() ) { if( rWall.IsGradient() ) bDrawGradient = true; else bDrawColor = true; } } else if( rWall.IsGradient() ) bDrawGradient = true; else bDrawColor = true; if( bDrawGradient ) { drawGradient( rRect, rWall.GetGradient() ); } if( bDrawColor ) { Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor; Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; setLineColor( Color( COL_TRANSPARENT ) ); setFillColor( rWall.GetColor() ); drawRectangle( rRect ); setLineColor( aOldLineColor ); setFillColor( aOldFillColor ); } if( bDrawBitmap ) { // set temporary clip region since aBmpPos and aBmpSize // may be outside rRect OStringBuffer aLine( 20 ); aLine.append( "q " ); m_aPages.back().appendRect( rRect, aLine ); aLine.append( " W n\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); drawBitmap( aBmpPos, aBmpSize, aBitmap ); writeBuffer( "Q\r\n", 3 ); } } void PDFWriterImpl::updateGraphicsState() { OStringBuffer aLine( 256 ); GraphicsState& rNewState = m_aGraphicsStack.front(); // first set clip region since it might invalidate everything else Region& rNewClip = rNewState.m_aClipRegion; /* #103137# equality operator is not implemented * const as API promises but may change Region * from Polygon to rectangles. Arrrgghh !!!! */ Region aLeft = m_aCurrentPDFState.m_aClipRegion; Region aRight = rNewClip; if( aLeft != aRight ) { if( ! m_aCurrentPDFState.m_aClipRegion.IsEmpty() && ! m_aCurrentPDFState.m_aClipRegion.IsNull() ) { aLine.append( "Q " ); // invalidate everything m_aCurrentPDFState = GraphicsState(); } if( ! rNewClip.IsEmpty() && ! rNewClip.IsNull() ) { // clip region is always stored in private PDF mapmode MapMode aNewMapMode = rNewState.m_aMapMode; rNewState.m_aMapMode = m_aMapMode; getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; aLine.append( "q " ); if( rNewClip.HasPolyPolygon() ) { m_aPages.back().appendPolyPolygon( rNewClip.GetPolyPolygon(), aLine ); aLine.append( "W* n\r\n" ); } else { // need to clip all rectangles RegionHandle aHandle = rNewClip.BeginEnumRects(); Rectangle aRect; while( rNewClip.GetNextEnumRect( aHandle, aRect ) ) { m_aPages.back().appendRect( aRect, aLine ); if( aLine.getLength() > 80 ) { aLine.append( "\r\n" ); writeBuffer( aLine.getStr(), aLine.getLength() ); aLine.setLength( 0 ); } else aLine.append( ' ' ); } rNewClip.EndEnumRects( aHandle ); aLine.append( "W* n\r\n" ); } rNewState.m_aMapMode = aNewMapMode; getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; } } if( m_aCurrentPDFState.m_aMapMode != rNewState.m_aMapMode ) { getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); } if( m_aCurrentPDFState.m_aFont != rNewState.m_aFont ) { getReferenceDevice()->SetFont( rNewState.m_aFont ); getReferenceDevice()->ImplNewFont(); } if( m_aCurrentPDFState.m_nLayoutMode != rNewState.m_nLayoutMode ) { getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode ); } if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor && rNewState.m_aLineColor != Color( COL_TRANSPARENT ) ) { appendStrokingColor( rNewState.m_aLineColor, aLine ); aLine.append( "\r\n" ); } if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor && rNewState.m_aFillColor != Color( COL_TRANSPARENT ) ) { appendNonStrokingColor( rNewState.m_aFillColor, aLine ); aLine.append( "\r\n" ); } if( m_eVersion >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent ) { // TODO: switch extended graphicsstate } // everything is up to date now m_aCurrentPDFState = m_aGraphicsStack.front(); if( aLine.getLength() ) writeBuffer( aLine.getStr(), aLine.getLength() ); } void PDFWriterImpl::push( sal_uInt16 nFlags ) { m_aGraphicsStack.push_front( m_aGraphicsStack.front() ); m_aGraphicsStack.front().m_nFlags = nFlags; } void PDFWriterImpl::pop() { GraphicsState aState = m_aGraphicsStack.front(); m_aGraphicsStack.pop_front(); // move those parameters back that were not pushed // in the first place if( ! (aState.m_nFlags & PUSH_LINECOLOR) ) m_aGraphicsStack.front().m_aLineColor = aState.m_aLineColor; if( ! (aState.m_nFlags & PUSH_FILLCOLOR) ) m_aGraphicsStack.front().m_aFillColor = aState.m_aFillColor; if( ! (aState.m_nFlags & PUSH_FONT) ) m_aGraphicsStack.front().m_aFont = aState.m_aFont; if( ! (aState.m_nFlags & PUSH_MAPMODE) ) setMapMode( aState.m_aMapMode ); if( ! (aState.m_nFlags & PUSH_CLIPREGION) ) m_aGraphicsStack.front().m_aClipRegion = aState.m_aClipRegion; if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) ) m_aGraphicsStack.front().m_aTextLineColor = aState.m_aTextLineColor; if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) ) m_aGraphicsStack.front().m_aFont.SetAlign( aState.m_aFont.GetAlign() ); if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) ) m_aGraphicsStack.front().m_aFont.SetFillColor( aState.m_aFont.GetFillColor() ); if( ! (aState.m_nFlags & PUSH_REFPOINT) ) { // what ? } } void PDFWriterImpl::setMapMode( const MapMode& rMapMode ) { m_aGraphicsStack.front().m_aMapMode = rMapMode; getReferenceDevice()->SetMapMode( rMapMode ); m_aCurrentPDFState.m_aMapMode = rMapMode; } void PDFWriterImpl::setClipRegion( const Region& rRegion ) { Region aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ); aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); m_aGraphicsStack.front().m_aClipRegion = aRegion; } void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY ) { Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Point( nX, nY ) ) ); aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Point() ); m_aGraphicsStack.front().m_aClipRegion.Move( nX, nY ); } bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect ) { Rectangle aRect( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), rRect ) ); return m_aGraphicsStack.front().m_aClipRegion.Intersect( aRect ); } bool PDFWriterImpl::intersectClipRegion( const Region& rRegion ) { Region aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ); aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); return m_aGraphicsStack.front().m_aClipRegion.Intersect( aRegion ); }