xref: /aoo41x/main/vcl/source/gdi/pdfwriter_impl2.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "precompiled_vcl.hxx"
29 
30 #include "pdfwriter_impl.hxx"
31 
32 #include "vcl/pdfextoutdevdata.hxx"
33 #include "vcl/virdev.hxx"
34 #include "vcl/gdimtf.hxx"
35 #include "vcl/metaact.hxx"
36 #include "vcl/bmpacc.hxx"
37 #include "vcl/graph.hxx"
38 #include "vcl/rendergraphicrasterizer.hxx"
39 
40 #include "svdata.hxx"
41 
42 #include "unotools/streamwrap.hxx"
43 #include "unotools/processfactory.hxx"
44 
45 #include "comphelper/processfactory.hxx"
46 
47 #include "com/sun/star/beans/PropertyValue.hpp"
48 #include "com/sun/star/io/XSeekable.hpp"
49 #include "com/sun/star/graphic/XGraphicProvider.hpp"
50 
51 #include "cppuhelper/implbase1.hxx"
52 
53 #include <rtl/digest.h>
54 
55 #undef USE_PDFGRADIENTS
56 
57 using namespace vcl;
58 using namespace rtl;
59 using namespace com::sun::star;
60 using namespace com::sun::star::uno;
61 using namespace com::sun::star::beans;
62 
63 // -----------------------------------------------------------------------------
64 
65 void PDFWriterImpl::implWriteGradient( const PolyPolygon& i_rPolyPoly, const Gradient& i_rGradient,
66                                        VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
67 {
68     GDIMetaFile        aTmpMtf;
69 
70     i_pDummyVDev->AddGradientActions( i_rPolyPoly.GetBoundRect(), i_rGradient, aTmpMtf );
71 
72     m_rOuterFace.Push();
73     m_rOuterFace.IntersectClipRegion( i_rPolyPoly.getB2DPolyPolygon() );
74     playMetafile( aTmpMtf, NULL, i_rContext, i_pDummyVDev );
75     m_rOuterFace.Pop();
76 }
77 
78 // -----------------------------------------------------------------------------
79 
80 void PDFWriterImpl::implWriteBitmapEx( const Point& i_rPoint, const Size& i_rSize, const BitmapEx& i_rBitmapEx,
81                                        VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
82 {
83 	if ( !i_rBitmapEx.IsEmpty() && i_rSize.Width() && i_rSize.Height() )
84 	{
85 		BitmapEx		aBitmapEx( i_rBitmapEx );
86         Point			aPoint( i_rPoint );
87         Size			aSize( i_rSize );
88 
89         // #i19065# Negative sizes have mirror semantics on
90         // OutputDevice. BitmapEx and co. have no idea about that, so
91         // perform that _before_ doing anything with aBitmapEx.
92         sal_uLong nMirrorFlags(BMP_MIRROR_NONE);
93         if( aSize.Width() < 0 )
94         {
95             aSize.Width() *= -1;
96             aPoint.X() -= aSize.Width();
97             nMirrorFlags |= BMP_MIRROR_HORZ;
98         }
99         if( aSize.Height() < 0 )
100         {
101             aSize.Height() *= -1;
102             aPoint.Y() -= aSize.Height();
103             nMirrorFlags |= BMP_MIRROR_VERT;
104         }
105 
106         if( nMirrorFlags != BMP_MIRROR_NONE )
107         {
108             aBitmapEx.Mirror( nMirrorFlags );
109         }
110 		if( i_rContext.m_nMaxImageResolution > 50 )
111 		{
112 			// do downsampling if neccessary
113 			const Size      aDstSizeTwip( i_pDummyVDev->PixelToLogic( i_pDummyVDev->LogicToPixel( aSize ), MAP_TWIP ) );
114 			const Size      aBmpSize( aBitmapEx.GetSizePixel() );
115 			const double    fBmpPixelX = aBmpSize.Width();
116 			const double    fBmpPixelY = aBmpSize.Height();
117 			const double    fMaxPixelX = aDstSizeTwip.Width() * i_rContext.m_nMaxImageResolution / 1440.0;
118 			const double    fMaxPixelY = aDstSizeTwip.Height() * i_rContext.m_nMaxImageResolution / 1440.0;
119 
120 			// check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
121 			if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) ||
122 				( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) &&
123 				( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) )
124 			{
125 				// do scaling
126 				Size            aNewBmpSize;
127 				const double    fBmpWH = fBmpPixelX / fBmpPixelY;
128 				const double    fMaxWH = fMaxPixelX / fMaxPixelY;
129 
130 				if( fBmpWH < fMaxWH )
131 				{
132 					aNewBmpSize.Width() = FRound( fMaxPixelY * fBmpWH );
133 					aNewBmpSize.Height() = FRound( fMaxPixelY );
134 				}
135 				else if( fBmpWH > 0.0 )
136 				{
137 					aNewBmpSize.Width() = FRound( fMaxPixelX );
138 					aNewBmpSize.Height() = FRound( fMaxPixelX / fBmpWH);
139 				}
140 				if( aNewBmpSize.Width() && aNewBmpSize.Height() )
141 					aBitmapEx.Scale( aNewBmpSize );
142 				else
143 					aBitmapEx.SetEmpty();
144 			}
145 		}
146 
147 		const Size aSizePixel( aBitmapEx.GetSizePixel() );
148 		if ( aSizePixel.Width() && aSizePixel.Height() )
149 		{
150             if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
151             {
152                 BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
153                 int nDepth = aBitmapEx.GetBitmap().GetBitCount();
154                 if( nDepth <= 4 )
155                     eConv = BMP_CONVERSION_4BIT_GREYS;
156                 if( nDepth > 1 )
157                     aBitmapEx.Convert( eConv );
158             }
159 			sal_Bool bUseJPGCompression = !i_rContext.m_bOnlyLosslessCompression;
160 			if ( ( aSizePixel.Width() < 32 ) || ( aSizePixel.Height() < 32 ) )
161 				bUseJPGCompression = sal_False;
162 
163 			SvMemoryStream	aStrm;
164 			Bitmap			aMask;
165 
166 			bool bTrueColorJPG = true;
167 			if ( bUseJPGCompression )
168 			{
169 				sal_uInt32 nZippedFileSize;		// sj: we will calculate the filesize of a zipped bitmap
170 				{								// to determine if jpeg compression is usefull
171 					SvMemoryStream aTemp;
172 					aTemp.SetCompressMode( aTemp.GetCompressMode() | COMPRESSMODE_ZBITMAP );
173 					aTemp.SetVersion( SOFFICE_FILEFORMAT_40 );	// sj: up from version 40 our bitmap stream operator
174 					aTemp << aBitmapEx;							// is capable of zlib stream compression
175 					aTemp.Seek( STREAM_SEEK_TO_END );
176 					nZippedFileSize = aTemp.Tell();
177 				}
178 				if ( aBitmapEx.IsTransparent() )
179 				{
180 					if ( aBitmapEx.IsAlpha() )
181 						aMask = aBitmapEx.GetAlpha().GetBitmap();
182 					else
183 						aMask = aBitmapEx.GetMask();
184 				}
185 				Graphic			aGraphic( aBitmapEx.GetBitmap() );
186 				sal_Int32		nColorMode = 0;
187 
188 				Sequence< PropertyValue > aFilterData( 2 );
189 				aFilterData[ 0 ].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "Quality" ) );
190 				aFilterData[ 0 ].Value <<= sal_Int32(i_rContext.m_nJPEGQuality);
191 				aFilterData[ 1 ].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "ColorMode" ) );
192 				aFilterData[ 1 ].Value <<= nColorMode;
193 
194 				try
195 				{
196 					uno::Reference < io::XStream > xStream = new utl::OStreamWrapper( aStrm );
197 					uno::Reference< io::XSeekable > xSeekable( xStream, UNO_QUERY_THROW );
198 					uno::Reference< graphic::XGraphicProvider > xGraphicProvider( ImplGetSVData()->maAppData.mxMSF->createInstance(
199 						OUString::createFromAscii( "com.sun.star.graphic.GraphicProvider" ) ), UNO_QUERY );
200 					if ( xGraphicProvider.is() )
201 					{
202 						uno::Reference< graphic::XGraphic > xGraphic( aGraphic.GetXGraphic() );
203 						uno::Reference < io::XOutputStream > xOut( xStream->getOutputStream() );
204 						rtl::OUString aMimeType( ::rtl::OUString::createFromAscii( "image/jpeg" ) );
205 						uno::Sequence< beans::PropertyValue > aOutMediaProperties( 3 );
206 						aOutMediaProperties[0].Name = ::rtl::OUString::createFromAscii( "OutputStream" );
207 						aOutMediaProperties[0].Value <<= xOut;
208 						aOutMediaProperties[1].Name = ::rtl::OUString::createFromAscii( "MimeType" );
209 						aOutMediaProperties[1].Value <<= aMimeType;
210 						aOutMediaProperties[2].Name = ::rtl::OUString::createFromAscii( "FilterData" );
211 						aOutMediaProperties[2].Value <<= aFilterData;
212 						xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties );
213 						xOut->flush();
214 						if ( xSeekable->getLength() > nZippedFileSize )
215 						{
216 							bUseJPGCompression = sal_False;
217 						}
218 						else
219 						{
220                             aStrm.Seek( STREAM_SEEK_TO_END );
221 
222                             xSeekable->seek( 0 );
223                             Sequence< PropertyValue > aArgs( 1 );
224                             aArgs[ 0 ].Name = ::rtl::OUString::createFromAscii( "InputStream" );
225                             aArgs[ 0 ].Value <<= xStream;
226                             uno::Reference< XPropertySet > xPropSet( xGraphicProvider->queryGraphicDescriptor( aArgs ) );
227                             if ( xPropSet.is() )
228                             {
229                                 sal_Int16 nBitsPerPixel = 24;
230                                 if ( xPropSet->getPropertyValue( ::rtl::OUString::createFromAscii( "BitsPerPixel" ) ) >>= nBitsPerPixel )
231                                 {
232                                     bTrueColorJPG = nBitsPerPixel != 8;
233                                 }
234                             }
235                         }
236 					}
237 					else
238 					    bUseJPGCompression = sal_False;
239 				}
240 				catch( uno::Exception& )
241 				{
242 				    bUseJPGCompression = sal_False;
243 				}
244 			}
245 			if ( bUseJPGCompression )
246 				m_rOuterFace.DrawJPGBitmap( aStrm, bTrueColorJPG, aSizePixel, Rectangle( aPoint, aSize ), aMask );
247 			else if ( aBitmapEx.IsTransparent() )
248 				m_rOuterFace.DrawBitmapEx( aPoint, aSize, aBitmapEx );
249 			else
250 				m_rOuterFace.DrawBitmap( aPoint, aSize, aBitmapEx.GetBitmap() );
251 		}
252 	}
253 }
254 
255 
256 // -----------------------------------------------------------------------------
257 
258 void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevData* i_pOutDevData, const vcl::PDFWriter::PlayMetafileContext& i_rContext, VirtualDevice* pDummyVDev )
259 {
260     bool bAssertionFired( false );
261 
262     VirtualDevice* pPrivateDevice = NULL;
263     if( ! pDummyVDev )
264     {
265         pPrivateDevice = pDummyVDev = new VirtualDevice();
266         pDummyVDev->EnableOutput( sal_False );
267         pDummyVDev->SetMapMode( i_rMtf.GetPrefMapMode() );
268     }
269     GDIMetaFile aMtf( i_rMtf );
270 
271 	for( sal_uInt32 i = 0, nCount = aMtf.GetActionCount(); i < nCount; )
272 	{
273 		if ( !i_pOutDevData || !i_pOutDevData->PlaySyncPageAct( m_rOuterFace, i ) )
274 		{
275 			const MetaAction*	pAction = aMtf.GetAction( i );
276 			const sal_uInt16		nType = pAction->GetType();
277 
278 			switch( nType )
279 			{
280 				case( META_PIXEL_ACTION	):
281 				{
282 					const MetaPixelAction* pA = (const MetaPixelAction*) pAction;
283 					m_rOuterFace.DrawPixel( pA->GetPoint(), pA->GetColor() );
284 				}
285 				break;
286 
287 				case( META_POINT_ACTION	):
288 				{
289 					const MetaPointAction* pA = (const MetaPointAction*) pAction;
290 					m_rOuterFace.DrawPixel( pA->GetPoint() );
291 				}
292 				break;
293 
294 				case( META_LINE_ACTION ):
295 				{
296 					const MetaLineAction* pA = (const MetaLineAction*) pAction;
297 					if ( pA->GetLineInfo().IsDefault() )
298 						m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint() );
299 					else
300 						m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint(), pA->GetLineInfo() );
301 				}
302 				break;
303 
304 				case( META_RECT_ACTION ):
305 				{
306 					const MetaRectAction* pA = (const MetaRectAction*) pAction;
307 					m_rOuterFace.DrawRect( pA->GetRect() );
308 				}
309 				break;
310 
311 				case( META_ROUNDRECT_ACTION	):
312 				{
313 					const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pAction;
314 					m_rOuterFace.DrawRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
315 				}
316 				break;
317 
318 				case( META_ELLIPSE_ACTION ):
319 				{
320 					const MetaEllipseAction* pA = (const MetaEllipseAction*) pAction;
321 					m_rOuterFace.DrawEllipse( pA->GetRect() );
322 				}
323 				break;
324 
325 				case( META_ARC_ACTION ):
326 				{
327 					const MetaArcAction* pA = (const MetaArcAction*) pAction;
328 					m_rOuterFace.DrawArc( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
329 				}
330 				break;
331 
332 				case( META_PIE_ACTION ):
333 				{
334 					const MetaArcAction* pA = (const MetaArcAction*) pAction;
335 					m_rOuterFace.DrawPie( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
336 				}
337 				break;
338 
339 				case( META_CHORD_ACTION	):
340 				{
341 					const MetaChordAction* pA = (const MetaChordAction*) pAction;
342 					m_rOuterFace.DrawChord( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
343 				}
344 				break;
345 
346 				case( META_POLYGON_ACTION ):
347 				{
348 					const MetaPolygonAction* pA = (const MetaPolygonAction*) pAction;
349 					m_rOuterFace.DrawPolygon( pA->GetPolygon() );
350 				}
351 				break;
352 
353 				case( META_POLYLINE_ACTION ):
354     			{
355 					const MetaPolyLineAction* pA = (const MetaPolyLineAction*) pAction;
356 					if ( pA->GetLineInfo().IsDefault() )
357 						m_rOuterFace.DrawPolyLine( pA->GetPolygon() );
358 					else
359 						m_rOuterFace.DrawPolyLine( pA->GetPolygon(), pA->GetLineInfo() );
360 				}
361 				break;
362 
363 				case( META_POLYPOLYGON_ACTION ):
364 				{
365 					const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*) pAction;
366 					m_rOuterFace.DrawPolyPolygon( pA->GetPolyPolygon() );
367 				}
368 				break;
369 
370 				case( META_GRADIENT_ACTION ):
371 				{
372 					const MetaGradientAction* pA = (const MetaGradientAction*) pAction;
373 					#ifdef USE_PDFGRADIENTS
374 					m_rOuterFace.DrawGradient( pA->GetRect(), pA->GetGradient() );
375 					#else
376 					const PolyPolygon         aPolyPoly( pA->GetRect() );
377 					implWriteGradient( aPolyPoly, pA->GetGradient(), pDummyVDev, i_rContext );
378 					#endif
379 				}
380 				break;
381 
382 				case( META_GRADIENTEX_ACTION ):
383 				{
384 					const MetaGradientExAction*	pA = (const MetaGradientExAction*) pAction;
385 					#ifdef USE_PDFGRADIENTS
386 					m_rOuterFace.DrawGradient( pA->GetPolyPolygon(), pA->GetGradient() );
387 					#else
388 					implWriteGradient( pA->GetPolyPolygon(), pA->GetGradient(), pDummyVDev, i_rContext );
389 					#endif
390 				}
391 				break;
392 
393 				case META_HATCH_ACTION:
394 				{
395 					const MetaHatchAction*	pA = (const MetaHatchAction*) pAction;
396 					m_rOuterFace.DrawHatch( pA->GetPolyPolygon(), pA->GetHatch() );
397 				}
398 				break;
399 
400 				case( META_TRANSPARENT_ACTION ):
401 				{
402 					const MetaTransparentAction* pA = (const MetaTransparentAction*) pAction;
403 					m_rOuterFace.DrawTransparent( pA->GetPolyPolygon(), pA->GetTransparence() );
404 				}
405 				break;
406 
407 				case( META_FLOATTRANSPARENT_ACTION ):
408 				{
409 					const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*) pAction;
410 
411 					GDIMetaFile		aTmpMtf( pA->GetGDIMetaFile() );
412 					const Point&	rPos = pA->GetPoint();
413 					const Size&		rSize= pA->GetSize();
414 					const Gradient&	rTransparenceGradient = pA->GetGradient();
415 
416                     // special case constant alpha value
417                     if( rTransparenceGradient.GetStartColor() == rTransparenceGradient.GetEndColor() )
418                     {
419                         const Color aTransCol( rTransparenceGradient.GetStartColor() );
420                         const sal_uInt16 nTransPercent = aTransCol.GetLuminance() * 100 / 255;
421                         m_rOuterFace.BeginTransparencyGroup();
422                         playMetafile( aTmpMtf, NULL, i_rContext, pDummyVDev );
423                         m_rOuterFace.EndTransparencyGroup( Rectangle( rPos, rSize ), nTransPercent );
424                     }
425                     else
426                     {
427                         const Size	aDstSizeTwip( pDummyVDev->PixelToLogic( pDummyVDev->LogicToPixel( rSize ), MAP_TWIP ) );
428                         sal_Int32	nMaxBmpDPI = i_rContext.m_bOnlyLosslessCompression ? 300 : 72;
429                         if( i_rContext.m_nMaxImageResolution > 50 )
430                         {
431                             if ( nMaxBmpDPI > i_rContext.m_nMaxImageResolution )
432                                 nMaxBmpDPI = i_rContext.m_nMaxImageResolution;
433                         }
434                         const sal_Int32	nPixelX = (sal_Int32)((double)aDstSizeTwip.Width() * (double)nMaxBmpDPI / 1440.0);
435                         const sal_Int32 nPixelY = (sal_Int32)((double)aDstSizeTwip.Height() * (double)nMaxBmpDPI / 1440.0);
436                         if ( nPixelX && nPixelY )
437                         {
438                             Size aDstSizePixel( nPixelX, nPixelY );
439                             VirtualDevice* pVDev = new VirtualDevice;
440                             if( pVDev->SetOutputSizePixel( aDstSizePixel ) )
441                             {
442                                 Bitmap			aPaint, aMask;
443                                 AlphaMask		aAlpha;
444                                 Point			aPoint;
445 
446                                 MapMode aMapMode( pDummyVDev->GetMapMode() );
447                                 aMapMode.SetOrigin( aPoint );
448                                 pVDev->SetMapMode( aMapMode );
449                                 Size aDstSize( pVDev->PixelToLogic( aDstSizePixel ) );
450 
451                                 Point	aMtfOrigin( aTmpMtf.GetPrefMapMode().GetOrigin() );
452                                 if ( aMtfOrigin.X() || aMtfOrigin.Y() )
453                                     aTmpMtf.Move( -aMtfOrigin.X(), -aMtfOrigin.Y() );
454                                 double	fScaleX = (double)aDstSize.Width() / (double)aTmpMtf.GetPrefSize().Width();
455                                 double	fScaleY = (double)aDstSize.Height() / (double)aTmpMtf.GetPrefSize().Height();
456                                 if( fScaleX != 1.0 || fScaleY != 1.0 )
457                                     aTmpMtf.Scale( fScaleX, fScaleY );
458                                 aTmpMtf.SetPrefMapMode( aMapMode );
459 
460                                 // create paint bitmap
461                                 aTmpMtf.WindStart();
462                                 aTmpMtf.Play( pVDev, aPoint, aDstSize );
463                                 aTmpMtf.WindStart();
464 
465                                 pVDev->EnableMapMode( sal_False );
466                                 aPaint = pVDev->GetBitmap( aPoint, aDstSizePixel );
467                                 pVDev->EnableMapMode( sal_True );
468 
469                                 // create mask bitmap
470                                 pVDev->SetLineColor( COL_BLACK );
471                                 pVDev->SetFillColor( COL_BLACK );
472                                 pVDev->DrawRect( Rectangle( aPoint, aDstSize ) );
473                                 pVDev->SetDrawMode( DRAWMODE_WHITELINE | DRAWMODE_WHITEFILL | DRAWMODE_WHITETEXT |
474                                                     DRAWMODE_WHITEBITMAP | DRAWMODE_WHITEGRADIENT );
475                                 aTmpMtf.WindStart();
476                                 aTmpMtf.Play( pVDev, aPoint, aDstSize );
477                                 aTmpMtf.WindStart();
478                                 pVDev->EnableMapMode( sal_False );
479                                 aMask = pVDev->GetBitmap( aPoint, aDstSizePixel );
480                                 pVDev->EnableMapMode( sal_True );
481 
482                                 // create alpha mask from gradient
483                                 pVDev->SetDrawMode( DRAWMODE_GRAYGRADIENT );
484                                 pVDev->DrawGradient( Rectangle( aPoint, aDstSize ), rTransparenceGradient );
485                                 pVDev->SetDrawMode( DRAWMODE_DEFAULT );
486                                 pVDev->EnableMapMode( sal_False );
487                                 pVDev->DrawMask( aPoint, aDstSizePixel, aMask, Color( COL_WHITE ) );
488                                 aAlpha = pVDev->GetBitmap( aPoint, aDstSizePixel );
489                                 implWriteBitmapEx( rPos, rSize, BitmapEx( aPaint, aAlpha ), pDummyVDev, i_rContext );
490                             }
491                             delete pVDev;
492                         }
493                     }
494 				}
495 				break;
496 
497 				case( META_EPS_ACTION ):
498 				{
499 					const MetaEPSAction*	pA = (const MetaEPSAction*) pAction;
500 					const GDIMetaFile		aSubstitute( pA->GetSubstitute() );
501 
502 					m_rOuterFace.Push();
503 					pDummyVDev->Push();
504 
505 					MapMode	aMapMode( aSubstitute.GetPrefMapMode() );
506 					Size aOutSize( pDummyVDev->LogicToLogic( pA->GetSize(), pDummyVDev->GetMapMode(), aMapMode ) );
507 					aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) );
508 					aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) );
509 					aMapMode.SetOrigin( pDummyVDev->LogicToLogic( pA->GetPoint(), pDummyVDev->GetMapMode(), aMapMode ) );
510 
511 					m_rOuterFace.SetMapMode( aMapMode );
512 					pDummyVDev->SetMapMode( aMapMode );
513 					playMetafile( aSubstitute, NULL, i_rContext, pDummyVDev );
514 					pDummyVDev->Pop();
515 					m_rOuterFace.Pop();
516 				}
517 				break;
518 
519 				case( META_COMMENT_ACTION ):
520                 if( ! i_rContext.m_bTransparenciesWereRemoved )
521 				{
522 					const MetaCommentAction*	pA = (const MetaCommentAction*) pAction;
523 					String						aSkipComment;
524 
525 					if( pA->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL )
526 					{
527 						const MetaGradientExAction*	pGradAction = NULL;
528 						sal_Bool					bDone = sal_False;
529 
530 						while( !bDone && ( ++i < nCount ) )
531 						{
532 							pAction = aMtf.GetAction( i );
533 
534 							if( pAction->GetType() == META_GRADIENTEX_ACTION )
535 								pGradAction = (const MetaGradientExAction*) pAction;
536 							else if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
537 									( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) )
538 							{
539 								bDone = sal_True;
540 							}
541 						}
542 
543 						if( pGradAction )
544 						{
545 						    #if USE_PDFGRADIENTS
546 							m_rOuterFace.DrawGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient() );
547 							#else
548 							implWriteGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), pDummyVDev, i_rContext );
549 							#endif
550 						}
551 					}
552 					else
553 					{
554 						const sal_uInt8* pData = pA->GetData();
555 						if ( pData )
556 						{
557 							SvMemoryStream	aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ );
558 							sal_Bool		bSkipSequence = sal_False;
559 							ByteString		sSeqEnd;
560 
561 							if( pA->GetComment().Equals( "XPATHSTROKE_SEQ_BEGIN" ) )
562 							{
563 								sSeqEnd = ByteString( "XPATHSTROKE_SEQ_END" );
564 								SvtGraphicStroke aStroke;
565 								aMemStm >> aStroke;
566 
567 								Polygon aPath;
568 								aStroke.getPath( aPath );
569 
570 								PolyPolygon aStartArrow;
571 								PolyPolygon aEndArrow;
572 								double fTransparency( aStroke.getTransparency() );
573 								double fStrokeWidth( aStroke.getStrokeWidth() );
574 								SvtGraphicStroke::DashArray aDashArray;
575 
576 								aStroke.getStartArrow( aStartArrow );
577 								aStroke.getEndArrow( aEndArrow );
578 								aStroke.getDashArray( aDashArray );
579 
580 								bSkipSequence = sal_True;
581 								if ( aStartArrow.Count() || aEndArrow.Count() )
582 									bSkipSequence = sal_False;
583 								if ( aDashArray.size() && ( fStrokeWidth != 0.0 ) && ( fTransparency == 0.0 ) )
584 									bSkipSequence = sal_False;
585 								if ( bSkipSequence )
586 								{
587 									PDFWriter::ExtLineInfo aInfo;
588                                     aInfo.m_fLineWidth      = fStrokeWidth;
589                                     aInfo.m_fTransparency   = fTransparency;
590                                     aInfo.m_fMiterLimit     = aStroke.getMiterLimit();
591                                     switch( aStroke.getCapType() )
592                                     {
593                                         default:
594                                         case SvtGraphicStroke::capButt:   aInfo.m_eCap = PDFWriter::capButt;break;
595                                         case SvtGraphicStroke::capRound:  aInfo.m_eCap = PDFWriter::capRound;break;
596                                         case SvtGraphicStroke::capSquare: aInfo.m_eCap = PDFWriter::capSquare;break;
597                                     }
598                                     switch( aStroke.getJoinType() )
599                                     {
600                                         default:
601                                         case SvtGraphicStroke::joinMiter: aInfo.m_eJoin = PDFWriter::joinMiter;break;
602                                         case SvtGraphicStroke::joinRound: aInfo.m_eJoin = PDFWriter::joinRound;break;
603                                         case SvtGraphicStroke::joinBevel: aInfo.m_eJoin = PDFWriter::joinBevel;break;
604                                         case SvtGraphicStroke::joinNone:
605                                             aInfo.m_eJoin = PDFWriter::joinMiter;
606                                             aInfo.m_fMiterLimit = 0.0;
607                                             break;
608                                     }
609                                     aInfo.m_aDashArray = aDashArray;
610 
611                                     if(SvtGraphicStroke::joinNone == aStroke.getJoinType()
612                                         && fStrokeWidth > 0.0)
613                                     {
614                                         // emulate no edge rounding by handling single edges
615                                         const sal_uInt16 nPoints(aPath.GetSize());
616                                         const bool bCurve(aPath.HasFlags());
617 
618                                         for(sal_uInt16 a(0); a + 1 < nPoints; a++)
619                                         {
620                                             if(bCurve
621                                                 && POLY_NORMAL != aPath.GetFlags(a + 1)
622                                                 && a + 2 < nPoints
623                                                 && POLY_NORMAL != aPath.GetFlags(a + 2)
624                                                 && a + 3 < nPoints)
625                                             {
626                 								const Polygon aSnippet(4,
627                                                     aPath.GetConstPointAry() + a,
628                                                     aPath.GetConstFlagAry() + a);
629                                                 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
630                                                 a += 2;
631                                             }
632                                             else
633                                             {
634                 								const Polygon aSnippet(2,
635                                                     aPath.GetConstPointAry() + a);
636                                                 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
637                                             }
638                                         }
639                                     }
640                                     else
641                                     {
642                                         m_rOuterFace.DrawPolyLine( aPath, aInfo );
643                                     }
644 								}
645 							}
646 							else if ( pA->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) )
647 							{
648 								sSeqEnd = ByteString( "XPATHFILL_SEQ_END" );
649 								SvtGraphicFill aFill;
650 								aMemStm >> aFill;
651 
652 								if ( ( aFill.getFillType() == SvtGraphicFill::fillSolid ) && ( aFill.getFillRule() == SvtGraphicFill::fillEvenOdd ) )
653 								{
654 									double fTransparency = aFill.getTransparency();
655 									if ( fTransparency == 0.0 )
656 									{
657 										PolyPolygon aPath;
658 										aFill.getPath( aPath );
659 
660 										bSkipSequence = sal_True;
661 										m_rOuterFace.DrawPolyPolygon( aPath );
662 									}
663 									else if ( fTransparency == 1.0 )
664 										bSkipSequence = sal_True;
665 								}
666 /* #i81548# removing optimization for fill textures, because most of the texture settings are not
667    exported properly. In OpenOffice 3.1 the drawing layer will support graphic primitives, then it
668    will not be a problem to optimize the filltexture export. But for wysiwyg is more important than
669    filesize.
670                                 else if( aFill.getFillType() == SvtGraphicFill::fillTexture && aFill.isTiling() )
671                                 {
672                                     sal_Int32 nPattern = mnCachePatternId;
673                                     Graphic aPatternGraphic;
674                                     aFill.getGraphic( aPatternGraphic );
675                                     bool bUseCache = false;
676                                     SvtGraphicFill::Transform aPatTransform;
677                                     aFill.getTransform( aPatTransform );
678 
679                                     if(  mnCachePatternId >= 0 )
680                                     {
681                                         SvtGraphicFill::Transform aCacheTransform;
682                                         maCacheFill.getTransform( aCacheTransform );
683                                         if( aCacheTransform.matrix[0] == aPatTransform.matrix[0] &&
684                                             aCacheTransform.matrix[1] == aPatTransform.matrix[1] &&
685                                             aCacheTransform.matrix[2] == aPatTransform.matrix[2] &&
686                                             aCacheTransform.matrix[3] == aPatTransform.matrix[3] &&
687                                             aCacheTransform.matrix[4] == aPatTransform.matrix[4] &&
688                                             aCacheTransform.matrix[5] == aPatTransform.matrix[5]
689                                             )
690                                         {
691                                             Graphic aCacheGraphic;
692                                             maCacheFill.getGraphic( aCacheGraphic );
693                                             if( aCacheGraphic == aPatternGraphic )
694                                                 bUseCache = true;
695                                         }
696                                     }
697 
698                                     if( ! bUseCache )
699                                     {
700 
701                                         // paint graphic to metafile
702                                         GDIMetaFile aPattern;
703                                         pDummyVDev->SetConnectMetaFile( &aPattern );
704                                         pDummyVDev->Push();
705                                         pDummyVDev->SetMapMode( aPatternGraphic.GetPrefMapMode() );
706 
707                                         aPatternGraphic.Draw( &rDummyVDev, Point( 0, 0 ) );
708                                         pDummyVDev->Pop();
709                                         pDummyVDev->SetConnectMetaFile( NULL );
710                                         aPattern.WindStart();
711 
712                                         MapMode	aPatternMapMode( aPatternGraphic.GetPrefMapMode() );
713                                         // prepare pattern from metafile
714                                         Size aPrefSize( aPatternGraphic.GetPrefSize() );
715                                         // FIXME: this magic -1 shouldn't be necessary
716                                         aPrefSize.Width() -= 1;
717                                         aPrefSize.Height() -= 1;
718                                         aPrefSize = m_rOuterFace.GetReferenceDevice()->
719                                             LogicToLogic( aPrefSize,
720                                                           &aPatternMapMode,
721                                                           &m_rOuterFace.GetReferenceDevice()->GetMapMode() );
722                                         // build bounding rectangle of pattern
723                                         Rectangle aBound( Point( 0, 0 ), aPrefSize );
724                                         m_rOuterFace.BeginPattern( aBound );
725                                         m_rOuterFace.Push();
726                                         pDummyVDev->Push();
727                                         m_rOuterFace.SetMapMode( aPatternMapMode );
728                                         pDummyVDev->SetMapMode( aPatternMapMode );
729                                         ImplWriteActions( m_rOuterFace, NULL, aPattern, rDummyVDev );
730                                         pDummyVDev->Pop();
731                                         m_rOuterFace.Pop();
732 
733                                         nPattern = m_rOuterFace.EndPattern( aPatTransform );
734 
735                                         // try some caching and reuse pattern
736                                         mnCachePatternId = nPattern;
737                                         maCacheFill = aFill;
738                                     }
739 
740                                     // draw polypolygon with pattern fill
741                                     PolyPolygon aPath;
742                                     aFill.getPath( aPath );
743                                     m_rOuterFace.DrawPolyPolygon( aPath, nPattern, aFill.getFillRule() == SvtGraphicFill::fillEvenOdd );
744 
745                                     bSkipSequence = sal_True;
746                                 }
747 */
748 							}
749 							if ( bSkipSequence )
750 							{
751 								while( ++i < nCount )
752 								{
753 									pAction = aMtf.GetAction( i );
754 									if ( pAction->GetType() == META_COMMENT_ACTION )
755 									{
756 										ByteString sComment( ((MetaCommentAction*)pAction)->GetComment() );
757 										if ( sComment.Equals( sSeqEnd ) )
758 											break;
759 									}
760                                     // #i44496#
761                                     // the replacement action for stroke is a filled rectangle
762                                     // the set fillcolor of the replacement is part of the graphics
763                                     // state and must not be skipped
764                                     else if( pAction->GetType() == META_FILLCOLOR_ACTION )
765                                     {
766                                         const MetaFillColorAction* pMA = (const MetaFillColorAction*) pAction;
767                                         if( pMA->IsSetting() )
768                                             m_rOuterFace.SetFillColor( pMA->GetColor() );
769                                         else
770                                             m_rOuterFace.SetFillColor();
771                                     }
772 								}
773 							}
774 						}
775 					}
776 				}
777 				break;
778 
779 				case( META_BMP_ACTION ):
780 				{
781 					const MetaBmpAction* pA = (const MetaBmpAction*) pAction;
782 					BitmapEx aBitmapEx( pA->GetBitmap() );
783 					Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
784 							aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
785                     if( ! ( aSize.Width() && aSize.Height() ) )
786                         aSize = pDummyVDev->PixelToLogic( aBitmapEx.GetSizePixel() );
787 					implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, pDummyVDev, i_rContext );
788 				}
789 				break;
790 
791 				case( META_BMPSCALE_ACTION ):
792 				{
793 					const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*) pAction;
794 					implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), BitmapEx( pA->GetBitmap() ), pDummyVDev, i_rContext );
795 				}
796 				break;
797 
798 				case( META_BMPSCALEPART_ACTION ):
799 				{
800 					const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*) pAction;
801 					BitmapEx aBitmapEx( pA->GetBitmap() );
802 					aBitmapEx.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
803 					implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, pDummyVDev, i_rContext );
804 				}
805 				break;
806 
807 				case( META_BMPEX_ACTION	):
808 				{
809 					const MetaBmpExAction*	pA = (const MetaBmpExAction*) pAction;
810 					BitmapEx aBitmapEx( pA->GetBitmapEx() );
811 					Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
812 							aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
813 					implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, pDummyVDev, i_rContext );
814 				}
815 				break;
816 
817 				case( META_BMPEXSCALE_ACTION ):
818 				{
819 					const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pAction;
820 					implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), pA->GetBitmapEx(), pDummyVDev, i_rContext );
821 				}
822 				break;
823 
824 				case( META_BMPEXSCALEPART_ACTION ):
825 				{
826 					const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*) pAction;
827 					BitmapEx aBitmapEx( pA->GetBitmapEx() );
828 					aBitmapEx.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
829 					implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, pDummyVDev, i_rContext );
830 				}
831 				break;
832 
833 				case( META_MASK_ACTION ):
834 				case( META_MASKSCALE_ACTION	):
835 				case( META_MASKSCALEPART_ACTION	):
836 				{
837 					DBG_ERROR( "MetaMask...Action not supported yet" );
838 				}
839 				break;
840 
841 				case( META_TEXT_ACTION ):
842 				{
843 					const MetaTextAction* pA = (const MetaTextAction*) pAction;
844 					m_rOuterFace.DrawText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ) );
845 				}
846 				break;
847 
848 				case( META_TEXTRECT_ACTION ):
849 				{
850 					const MetaTextRectAction* pA = (const MetaTextRectAction*) pAction;
851 					m_rOuterFace.DrawText( pA->GetRect(), String( pA->GetText() ), pA->GetStyle() );
852 				}
853 				break;
854 
855 				case( META_TEXTARRAY_ACTION	):
856 				{
857 					const MetaTextArrayAction* pA = (const MetaTextArrayAction*) pAction;
858 					m_rOuterFace.DrawTextArray( pA->GetPoint(), pA->GetText(), pA->GetDXArray(), pA->GetIndex(), pA->GetLen() );
859 				}
860 				break;
861 
862 				case( META_STRETCHTEXT_ACTION ):
863 				{
864 					const MetaStretchTextAction* pA = (const MetaStretchTextAction*) pAction;
865 					m_rOuterFace.DrawStretchText( pA->GetPoint(), pA->GetWidth(), pA->GetText(), pA->GetIndex(), pA->GetLen() );
866 				}
867 				break;
868 
869 
870 				case( META_TEXTLINE_ACTION ):
871 				{
872 					const MetaTextLineAction* pA = (const MetaTextLineAction*) pAction;
873 					m_rOuterFace.DrawTextLine( pA->GetStartPoint(), pA->GetWidth(), pA->GetStrikeout(), pA->GetUnderline(), pA->GetOverline() );
874 
875 				}
876 				break;
877 
878 				case( META_CLIPREGION_ACTION ):
879 				{
880 					const MetaClipRegionAction* pA = (const MetaClipRegionAction*) pAction;
881 
882 					if( pA->IsClipping() )
883 					{
884 					    if( pA->GetRegion().IsEmpty() )
885 					        m_rOuterFace.SetClipRegion( basegfx::B2DPolyPolygon() );
886 					    else
887 					    {
888 					        Region aReg( pA->GetRegion() );
889 					        m_rOuterFace.SetClipRegion( aReg.ConvertToB2DPolyPolygon() );
890 					    }
891 					}
892 					else
893 						m_rOuterFace.SetClipRegion();
894 				}
895 				break;
896 
897 				case( META_ISECTRECTCLIPREGION_ACTION ):
898 				{
899 					const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*) pAction;
900 					m_rOuterFace.IntersectClipRegion( pA->GetRect() );
901 				}
902 				break;
903 
904 				case( META_ISECTREGIONCLIPREGION_ACTION	):
905 				{
906 				    const MetaISectRegionClipRegionAction* pA = (const MetaISectRegionClipRegionAction*) pAction;
907 				    Region aReg( pA->GetRegion() );
908 				    m_rOuterFace.IntersectClipRegion( aReg.ConvertToB2DPolyPolygon() );
909 				}
910 				break;
911 
912 				case( META_MOVECLIPREGION_ACTION ):
913 				{
914 					const MetaMoveClipRegionAction* pA = (const MetaMoveClipRegionAction*) pAction;
915 					m_rOuterFace.MoveClipRegion( pA->GetHorzMove(), pA->GetVertMove() );
916 				}
917 				break;
918 
919 				case( META_MAPMODE_ACTION ):
920 				{
921 					const_cast< MetaAction* >( pAction )->Execute( pDummyVDev );
922 					m_rOuterFace.SetMapMode( pDummyVDev->GetMapMode() );
923 				}
924 				break;
925 
926 				case( META_LINECOLOR_ACTION	):
927 				{
928 					const MetaLineColorAction* pA = (const MetaLineColorAction*) pAction;
929 
930 					if( pA->IsSetting() )
931 						m_rOuterFace.SetLineColor( pA->GetColor() );
932 					else
933 						m_rOuterFace.SetLineColor();
934 				}
935 				break;
936 
937 				case( META_FILLCOLOR_ACTION	):
938 				{
939 					const MetaFillColorAction* pA = (const MetaFillColorAction*) pAction;
940 
941 					if( pA->IsSetting() )
942 						m_rOuterFace.SetFillColor( pA->GetColor() );
943 					else
944 						m_rOuterFace.SetFillColor();
945 				}
946 				break;
947 
948 				case( META_TEXTLINECOLOR_ACTION ):
949 				{
950 					const MetaTextLineColorAction* pA = (const MetaTextLineColorAction*) pAction;
951 
952 					if( pA->IsSetting() )
953 						m_rOuterFace.SetTextLineColor( pA->GetColor() );
954 					else
955 						m_rOuterFace.SetTextLineColor();
956 				}
957 				break;
958 
959 				case( META_OVERLINECOLOR_ACTION ):
960 				{
961 					const MetaOverlineColorAction* pA = (const MetaOverlineColorAction*) pAction;
962 
963 					if( pA->IsSetting() )
964 						m_rOuterFace.SetOverlineColor( pA->GetColor() );
965 					else
966 						m_rOuterFace.SetOverlineColor();
967 				}
968 				break;
969 
970 				case( META_TEXTFILLCOLOR_ACTION	):
971 				{
972 					const MetaTextFillColorAction* pA = (const MetaTextFillColorAction*) pAction;
973 
974 					if( pA->IsSetting() )
975 						m_rOuterFace.SetTextFillColor( pA->GetColor() );
976 					else
977 						m_rOuterFace.SetTextFillColor();
978 				}
979 				break;
980 
981 				case( META_TEXTCOLOR_ACTION	):
982 				{
983 					const MetaTextColorAction* pA = (const MetaTextColorAction*) pAction;
984 					m_rOuterFace.SetTextColor( pA->GetColor() );
985 				}
986 				break;
987 
988 				case( META_TEXTALIGN_ACTION	):
989 				{
990 					const MetaTextAlignAction* pA = (const MetaTextAlignAction*) pAction;
991 					m_rOuterFace.SetTextAlign( pA->GetTextAlign() );
992 				}
993 				break;
994 
995 				case( META_FONT_ACTION ):
996 				{
997 					const MetaFontAction* pA = (const MetaFontAction*) pAction;
998 					m_rOuterFace.SetFont( pA->GetFont() );
999 				}
1000 				break;
1001 
1002 				case( META_PUSH_ACTION ):
1003 				{
1004 					const MetaPushAction* pA = (const MetaPushAction*) pAction;
1005 
1006 					pDummyVDev->Push( pA->GetFlags() );
1007 					m_rOuterFace.Push( pA->GetFlags() );
1008 				}
1009 				break;
1010 
1011 				case( META_POP_ACTION ):
1012 				{
1013 					pDummyVDev->Pop();
1014 					m_rOuterFace.Pop();
1015 				}
1016 				break;
1017 
1018 				case( META_LAYOUTMODE_ACTION ):
1019 				{
1020 					const MetaLayoutModeAction* pA = (const MetaLayoutModeAction*) pAction;
1021 					m_rOuterFace.SetLayoutMode( pA->GetLayoutMode() );
1022 				}
1023 				break;
1024 
1025 				case META_TEXTLANGUAGE_ACTION:
1026 				{
1027 					const  MetaTextLanguageAction* pA = (const MetaTextLanguageAction*) pAction;
1028                     m_rOuterFace.SetDigitLanguage( pA->GetTextLanguage() );
1029 				}
1030 				break;
1031 
1032 				case( META_WALLPAPER_ACTION	):
1033 				{
1034 					const MetaWallpaperAction* pA = (const MetaWallpaperAction*) pAction;
1035 					m_rOuterFace.DrawWallpaper( pA->GetRect(), pA->GetWallpaper() );
1036 				}
1037 				break;
1038 
1039 				case( META_RASTEROP_ACTION ):
1040 				{
1041 					// !!! >>> we don't want to support this actions
1042 				}
1043 				break;
1044 
1045 				case( META_REFPOINT_ACTION ):
1046 				{
1047 					// !!! >>> we don't want to support this actions
1048 				}
1049 				break;
1050 
1051 				case( META_RENDERGRAPHIC_ACTION ):
1052 				{
1053 					const MetaRenderGraphicAction* pA = static_cast< const MetaRenderGraphicAction* >( pAction );
1054                     const ::vcl::RenderGraphicRasterizer aRasterizer( pA->GetRenderGraphic() );
1055 
1056 					implWriteBitmapEx( pA->GetPoint(), pA->GetSize(),
1057                                        aRasterizer.Rasterize( pDummyVDev->LogicToPixel( pA->GetSize() ) ),
1058                                        pDummyVDev, i_rContext );
1059 				}
1060 				break;
1061 
1062 				default:
1063 					// #i24604# Made assertion fire only once per
1064 					// metafile. The asserted actions here are all
1065 					// deprecated
1066 					if( !bAssertionFired )
1067 					{
1068 						bAssertionFired = true;
1069 						DBG_ERROR( "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered" );
1070 					}
1071 				break;
1072 			}
1073 			i++;
1074 		}
1075 	}
1076 
1077     delete pPrivateDevice;
1078 }
1079 
1080 // Encryption methods
1081 
1082 /* a crutch to transport an rtlDigest safely though UNO API
1083    this is needed for the PDF export dialog, which otherwise would have to pass
1084    clear text passwords down till they can be used in PDFWriter. Unfortunately
1085    the MD5 sum of the password (which is needed to create the PDF encryption key)
1086    is not sufficient, since an rtl MD5 digest cannot be created in an arbitrary state
1087    which would be needed in PDFWriterImpl::computeEncryptionKey.
1088 */
1089 class EncHashTransporter : public cppu::WeakImplHelper1 < com::sun::star::beans::XMaterialHolder >
1090 {
1091     rtlDigest                   maUDigest;
1092     sal_IntPtr                  maID;
1093     std::vector< sal_uInt8 >    maOValue;
1094 
1095     static std::map< sal_IntPtr, EncHashTransporter* >      sTransporters;
1096 public:
1097     EncHashTransporter()
1098     : maUDigest( rtl_digest_createMD5() )
1099     {
1100         maID = reinterpret_cast< sal_IntPtr >(this);
1101         while( sTransporters.find( maID ) != sTransporters.end() ) // paranoia mode
1102             maID++;
1103         sTransporters[ maID ] = this;
1104     }
1105 
1106     virtual ~EncHashTransporter()
1107     {
1108         sTransporters.erase( maID );
1109         if( maUDigest )
1110             rtl_digest_destroyMD5( maUDigest );
1111         OSL_TRACE( "EncHashTransporter freed\n" );
1112     }
1113 
1114     rtlDigest getUDigest() const { return maUDigest; };
1115     std::vector< sal_uInt8 >& getOValue() { return maOValue; }
1116     void invalidate()
1117     {
1118         if( maUDigest )
1119         {
1120             rtl_digest_destroyMD5( maUDigest );
1121             maUDigest = NULL;
1122         }
1123     }
1124 
1125     // XMaterialHolder
1126     virtual uno::Any SAL_CALL getMaterial() throw()
1127     {
1128         return uno::makeAny( sal_Int64(maID) );
1129     }
1130 
1131     static EncHashTransporter* getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& );
1132 
1133 };
1134 
1135 std::map< sal_IntPtr, EncHashTransporter* > EncHashTransporter::sTransporters;
1136 
1137 EncHashTransporter* EncHashTransporter::getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& xRef )
1138 {
1139     EncHashTransporter* pResult = NULL;
1140     if( xRef.is() )
1141     {
1142         uno::Any aMat( xRef->getMaterial() );
1143         sal_Int64 nMat = 0;
1144         if( aMat >>= nMat )
1145         {
1146             std::map< sal_IntPtr, EncHashTransporter* >::iterator it = sTransporters.find( static_cast<sal_IntPtr>(nMat) );
1147             if( it != sTransporters.end() )
1148                 pResult = it->second;
1149         }
1150     }
1151     return pResult;
1152 }
1153 
1154 sal_Bool PDFWriterImpl::checkEncryptionBufferSize( register sal_Int32 newSize )
1155 {
1156     if( m_nEncryptionBufferSize < newSize )
1157     {
1158         /* reallocate the buffer, the used function allocate as rtl_allocateMemory
1159         if the pointer parameter is NULL */
1160         m_pEncryptionBuffer = (sal_uInt8*)rtl_reallocateMemory( m_pEncryptionBuffer, newSize );
1161         if( m_pEncryptionBuffer )
1162             m_nEncryptionBufferSize = newSize;
1163         else
1164             m_nEncryptionBufferSize = 0;
1165     }
1166     return ( m_nEncryptionBufferSize != 0 );
1167 }
1168 
1169 void PDFWriterImpl::checkAndEnableStreamEncryption( register sal_Int32 nObject )
1170 {
1171     if( m_aContext.Encryption.Encrypt() )
1172     {
1173         m_bEncryptThisStream = true;
1174         sal_Int32 i = m_nKeyLength;
1175         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject;
1176         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 );
1177         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 );
1178         //the other location of m_nEncryptionKey are already set to 0, our fixed generation number
1179         // do the MD5 hash
1180         sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1181         // the i+2 to take into account the generation number, always zero
1182         rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) );
1183         // initialize the RC4 with the key
1184         // key legth: see algoritm 3.1, step 4: (N+5) max 16
1185         rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 );
1186     }
1187 }
1188 
1189 void PDFWriterImpl::enableStringEncryption( register sal_Int32 nObject )
1190 {
1191     if( m_aContext.Encryption.Encrypt() )
1192     {
1193         sal_Int32 i = m_nKeyLength;
1194         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject;
1195         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 );
1196         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 );
1197         //the other location of m_nEncryptionKey are already set to 0, our fixed generation number
1198         // do the MD5 hash
1199         sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1200         // the i+2 to take into account the generation number, always zero
1201         rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) );
1202         // initialize the RC4 with the key
1203         // key legth: see algoritm 3.1, step 4: (N+5) max 16
1204         rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 );
1205     }
1206 }
1207 
1208 /* init the encryption engine
1209 1. init the document id, used both for building the document id and for building the encryption key(s)
1210 2. build the encryption key following algorithms described in the PDF specification
1211  */
1212 uno::Reference< beans::XMaterialHolder > PDFWriterImpl::initEncryption( const rtl::OUString& i_rOwnerPassword,
1213                                                                         const rtl::OUString& i_rUserPassword,
1214                                                                         bool b128Bit
1215                                                                         )
1216 {
1217     uno::Reference< beans::XMaterialHolder > xResult;
1218     if( i_rOwnerPassword.getLength() || i_rUserPassword.getLength() )
1219     {
1220         EncHashTransporter* pTransporter = new EncHashTransporter;
1221         xResult = pTransporter;
1222 
1223         // get padded passwords
1224         sal_uInt8 aPadUPW[ENCRYPTED_PWD_SIZE], aPadOPW[ENCRYPTED_PWD_SIZE];
1225         padPassword( i_rOwnerPassword.getLength() ? i_rOwnerPassword : i_rUserPassword, aPadOPW );
1226         padPassword( i_rUserPassword, aPadUPW );
1227         sal_Int32 nKeyLength = SECUR_40BIT_KEY;
1228         if( b128Bit )
1229             nKeyLength = SECUR_128BIT_KEY;
1230 
1231         if( computeODictionaryValue( aPadOPW, aPadUPW, pTransporter->getOValue(), nKeyLength ) )
1232         {
1233             rtlDigest aDig = pTransporter->getUDigest();
1234             if( rtl_digest_updateMD5( aDig, aPadUPW, ENCRYPTED_PWD_SIZE ) != rtl_Digest_E_None )
1235                 xResult.clear();
1236         }
1237         else
1238             xResult.clear();
1239 
1240         // trash temporary padded cleartext PWDs
1241         rtl_zeroMemory( aPadOPW, sizeof(aPadOPW) );
1242         rtl_zeroMemory( aPadUPW, sizeof(aPadUPW) );
1243 
1244     }
1245     return xResult;
1246 }
1247 
1248 bool PDFWriterImpl::prepareEncryption( const uno::Reference< beans::XMaterialHolder >& xEnc )
1249 {
1250     bool bSuccess = false;
1251     EncHashTransporter* pTransporter = EncHashTransporter::getEncHashTransporter( xEnc );
1252     if( pTransporter )
1253     {
1254         sal_Int32 nKeyLength = 0, nRC4KeyLength = 0;
1255         sal_Int32 nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, nKeyLength, nRC4KeyLength );
1256         m_aContext.Encryption.OValue = pTransporter->getOValue();
1257         bSuccess = computeUDictionaryValue( pTransporter, m_aContext.Encryption, nKeyLength, nAccessPermissions );
1258     }
1259     if( ! bSuccess )
1260     {
1261         m_aContext.Encryption.OValue.clear();
1262         m_aContext.Encryption.UValue.clear();
1263         m_aContext.Encryption.EncryptionKey.clear();
1264     }
1265     return bSuccess;
1266 }
1267 
1268 sal_Int32 PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties,
1269 	                                               sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength )
1270 {
1271     /*
1272     2) compute the access permissions, in numerical form
1273 
1274     the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
1275     - for 40 bit security the unused bit must be set to 1, since they are not used
1276     - for 128 bit security the same bit must be preset to 0 and set later if needed
1277     according to the table 3.15, pdf v 1.4 */
1278     sal_Int32 nAccessPermissions = ( i_rProperties.Security128bit ) ? 0xfffff0c0 : 0xffffffc0 ;
1279 
1280     /* check permissions for 40 bit security case */
1281     nAccessPermissions |= ( i_rProperties.CanPrintTheDocument ) ?  1 << 2 : 0;
1282     nAccessPermissions |= ( i_rProperties.CanModifyTheContent ) ? 1 << 3 : 0;
1283     nAccessPermissions |= ( i_rProperties.CanCopyOrExtract ) ?   1 << 4 : 0;
1284     nAccessPermissions |= ( i_rProperties.CanAddOrModify ) ? 1 << 5 : 0;
1285     o_rKeyLength = SECUR_40BIT_KEY;
1286     o_rRC4KeyLength = SECUR_40BIT_KEY+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5
1287 
1288     if( i_rProperties.Security128bit )
1289     {
1290         o_rKeyLength = SECUR_128BIT_KEY;
1291         o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum
1292         // permitted value is 16
1293         nAccessPermissions |= ( i_rProperties.CanFillInteractive ) ?         1 << 8 : 0;
1294         nAccessPermissions |= ( i_rProperties.CanExtractForAccessibility ) ? 1 << 9 : 0;
1295         nAccessPermissions |= ( i_rProperties.CanAssemble ) ?                1 << 10 : 0;
1296         nAccessPermissions |= ( i_rProperties.CanPrintFull ) ?               1 << 11 : 0;
1297     }
1298     return nAccessPermissions;
1299 }
1300 
1301 /*************************************************************
1302 begin i12626 methods
1303 
1304 Implements Algorithm 3.2, step 1 only
1305 */
1306 void PDFWriterImpl::padPassword( const rtl::OUString& i_rPassword, sal_uInt8* o_pPaddedPW )
1307 {
1308     // get ansi-1252 version of the password string CHECKIT ! i12626
1309     rtl::OString aString( rtl::OUStringToOString( i_rPassword, RTL_TEXTENCODING_MS_1252 ) );
1310 
1311     //copy the string to the target
1312     sal_Int32 nToCopy = ( aString.getLength() < ENCRYPTED_PWD_SIZE ) ? aString.getLength() : ENCRYPTED_PWD_SIZE;
1313     sal_Int32 nCurrentChar;
1314 
1315     for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ )
1316         o_pPaddedPW[nCurrentChar] = (sal_uInt8)( aString.getStr()[nCurrentChar] );
1317 
1318     //pad it with standard byte string
1319     sal_Int32 i,y;
1320     for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ )
1321         o_pPaddedPW[i] = s_nPadString[y];
1322 
1323     // trash memory of temporary clear text password
1324     rtl_zeroMemory( (sal_Char*)aString.getStr(), aString.getLength() );
1325 }
1326 
1327 /**********************************
1328 Algorithm 3.2  Compute the encryption key used
1329 
1330 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
1331 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
1332 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
1333 
1334 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
1335 
1336 */
1337 bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter* i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, sal_Int32 i_nAccessPermissions )
1338 {
1339     bool bSuccess = true;
1340     sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1341 
1342     // transporter contains an MD5 digest with the padded user password already
1343     rtlDigest aDigest = i_pTransporter->getUDigest();
1344     rtlDigestError nError = rtl_Digest_E_None;
1345     if( aDigest )
1346     {
1347         //step 3
1348         if( ! io_rProperties.OValue.empty() )
1349             nError = rtl_digest_updateMD5( aDigest, &io_rProperties.OValue[0] , sal_Int32(io_rProperties.OValue.size()) );
1350         else
1351             bSuccess = false;
1352         //Step 4
1353         sal_uInt8 nPerm[4];
1354 
1355         nPerm[0] = (sal_uInt8)i_nAccessPermissions;
1356         nPerm[1] = (sal_uInt8)( i_nAccessPermissions >> 8 );
1357         nPerm[2] = (sal_uInt8)( i_nAccessPermissions >> 16 );
1358         nPerm[3] = (sal_uInt8)( i_nAccessPermissions >> 24 );
1359 
1360         if( nError == rtl_Digest_E_None )
1361             nError = rtl_digest_updateMD5( aDigest, nPerm , sizeof( nPerm ) );
1362 
1363         //step 5, get the document ID, binary form
1364         if( nError == rtl_Digest_E_None )
1365             nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) );
1366         //get the digest
1367         if( nError == rtl_Digest_E_None )
1368         {
1369             rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1370 
1371             //step 6, only if 128 bit
1372             if( io_rProperties.Security128bit )
1373             {
1374                 for( sal_Int32 i = 0; i < 50; i++ )
1375                 {
1376                     nError = rtl_digest_updateMD5( aDigest, &nMD5Sum, sizeof( nMD5Sum ) );
1377                     if( nError != rtl_Digest_E_None )
1378                     {
1379                         bSuccess =  false;
1380                         break;
1381                     }
1382                     rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1383                 }
1384             }
1385         }
1386     }
1387     else
1388         bSuccess = false;
1389 
1390     i_pTransporter->invalidate();
1391 
1392     //Step 7
1393     if( bSuccess )
1394     {
1395         io_rProperties.EncryptionKey.resize( MAXIMUM_RC4_KEY_LENGTH );
1396         for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ )
1397             io_rProperties.EncryptionKey[i] = nMD5Sum[i];
1398     }
1399     else
1400         io_rProperties.EncryptionKey.clear();
1401 
1402     return bSuccess;
1403 }
1404 
1405 /**********************************
1406 Algorithm 3.3  Compute the encryption dictionary /O value, save into the class data member
1407 the step numbers down here correspond to the ones in PDF v.1.4 specfication
1408 */
1409 bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword,
1410                                              const sal_uInt8* i_pPaddedUserPassword,
1411                                              std::vector< sal_uInt8 >& io_rOValue,
1412                                              sal_Int32 i_nKeyLength
1413                                              )
1414 {
1415     bool bSuccess = true;
1416 
1417     io_rOValue.resize( ENCRYPTED_PWD_SIZE );
1418 
1419     rtlDigest aDigest = rtl_digest_createMD5();
1420     rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1421     if( aDigest && aCipher)
1422     {
1423         //step 1 already done, data is in i_pPaddedOwnerPassword
1424         //step 2
1425 
1426         rtlDigestError nError = rtl_digest_updateMD5( aDigest, i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE );
1427         if( nError == rtl_Digest_E_None )
1428         {
1429             sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1430 
1431             rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) );
1432 //step 3, only if 128 bit
1433             if( i_nKeyLength == SECUR_128BIT_KEY )
1434             {
1435                 sal_Int32 i;
1436                 for( i = 0; i < 50; i++ )
1437                 {
1438                     nError = rtl_digest_updateMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1439                     if( nError != rtl_Digest_E_None )
1440                     {
1441                         bSuccess = false;
1442                         break;
1443                     }
1444                     rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1445                 }
1446             }
1447             //Step 4, the key is in nMD5Sum
1448             //step 5 already done, data is in i_pPaddedUserPassword
1449             //step 6
1450             rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1451                                      nMD5Sum, i_nKeyLength , NULL, 0 );
1452             // encrypt the user password using the key set above
1453             rtl_cipher_encodeARCFOUR( aCipher, i_pPaddedUserPassword, ENCRYPTED_PWD_SIZE, // the data to be encrypted
1454                                       &io_rOValue[0], sal_Int32(io_rOValue.size()) ); //encrypted data
1455             //Step 7, only if 128 bit
1456             if( i_nKeyLength == SECUR_128BIT_KEY )
1457             {
1458                 sal_uInt32 i, y;
1459                 sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key
1460 
1461                 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1462                 {
1463                     for( y = 0; y < sizeof( nLocalKey ); y++ )
1464                         nLocalKey[y] = (sal_uInt8)( nMD5Sum[y] ^ i );
1465 
1466                     rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1467                                             nLocalKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area, on init can be NULL
1468                     rtl_cipher_encodeARCFOUR( aCipher, &io_rOValue[0], sal_Int32(io_rOValue.size()), // the data to be encrypted
1469                                               &io_rOValue[0], sal_Int32(io_rOValue.size()) ); // encrypted data, can be the same as the input, encrypt "in place"
1470                     //step 8, store in class data member
1471                 }
1472             }
1473         }
1474         else
1475             bSuccess = false;
1476     }
1477     else
1478         bSuccess = false;
1479 
1480     if( aDigest )
1481         rtl_digest_destroyMD5( aDigest );
1482     if( aCipher )
1483         rtl_cipher_destroyARCFOUR( aCipher );
1484 
1485     if( ! bSuccess )
1486         io_rOValue.clear();
1487     return bSuccess;
1488 }
1489 
1490 /**********************************
1491 Algorithms 3.4 and 3.5  Compute the encryption dictionary /U value, save into the class data member, revision 2 (40 bit) or 3 (128 bit)
1492 */
1493 bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter* i_pTransporter,
1494                                              vcl::PDFWriter::PDFEncryptionProperties& io_rProperties,
1495                                              sal_Int32 i_nKeyLength,
1496                                              sal_Int32 i_nAccessPermissions
1497                                              )
1498 {
1499     bool bSuccess = true;
1500 
1501     io_rProperties.UValue.resize( ENCRYPTED_PWD_SIZE );
1502 
1503     rtlDigest aDigest = rtl_digest_createMD5();
1504     rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1505     if( aDigest && aCipher )
1506     {
1507         //step 1, common to both 3.4 and 3.5
1508         if( computeEncryptionKey( i_pTransporter, io_rProperties, i_nAccessPermissions ) )
1509         {
1510             // prepare encryption key for object
1511             for( sal_Int32 i = i_nKeyLength, y = 0; y < 5 ; y++ )
1512                 io_rProperties.EncryptionKey[i++] = 0;
1513 
1514             if( io_rProperties.Security128bit == false )
1515             {
1516                 //3.4
1517                 //step 2 and 3
1518                 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1519                                         &io_rProperties.EncryptionKey[0], 5 , // key and key length
1520                                         NULL, 0 ); //destination data area
1521                 // encrypt the user password using the key set above, save for later use
1522                 rtl_cipher_encodeARCFOUR( aCipher, s_nPadString, sizeof( s_nPadString ), // the data to be encrypted
1523                                           &io_rProperties.UValue[0], sal_Int32(io_rProperties.UValue.size()) ); //encrypted data, stored in class data member
1524             }
1525             else
1526             {
1527                 //or 3.5, for 128 bit security
1528                 //step6, initilize the last 16 bytes of the encrypted user password to 0
1529                 for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sal_uInt32(io_rProperties.UValue.size()); i++)
1530                     io_rProperties.UValue[i] = 0;
1531                 //step 2
1532                 rtlDigestError nError = rtl_digest_updateMD5( aDigest, s_nPadString, sizeof( s_nPadString ) );
1533                 //step 3
1534                 if( nError == rtl_Digest_E_None )
1535                     nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) );
1536                 else
1537                     bSuccess = false;
1538 
1539                 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1540                 rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) );
1541                 //Step 4
1542                 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1543                                         &io_rProperties.EncryptionKey[0], SECUR_128BIT_KEY, NULL, 0 ); //destination data area
1544                 rtl_cipher_encodeARCFOUR( aCipher, nMD5Sum, sizeof( nMD5Sum ), // the data to be encrypted
1545                                           &io_rProperties.UValue[0], sizeof( nMD5Sum ) ); //encrypted data, stored in class data member
1546                 //step 5
1547                 sal_uInt32 i, y;
1548                 sal_uInt8 nLocalKey[SECUR_128BIT_KEY];
1549 
1550                 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1551                 {
1552                     for( y = 0; y < sizeof( nLocalKey ) ; y++ )
1553                         nLocalKey[y] = (sal_uInt8)( io_rProperties.EncryptionKey[y] ^ i );
1554 
1555                     rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1556                                             nLocalKey, SECUR_128BIT_KEY, // key and key length
1557                                             NULL, 0 ); //destination data area, on init can be NULL
1558                     rtl_cipher_encodeARCFOUR( aCipher, &io_rProperties.UValue[0], SECUR_128BIT_KEY, // the data to be encrypted
1559                                               &io_rProperties.UValue[0], SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place"
1560                 }
1561             }
1562         }
1563         else
1564             bSuccess = false;
1565     }
1566     else
1567         bSuccess = false;
1568 
1569     if( aDigest )
1570         rtl_digest_destroyMD5( aDigest );
1571     if( aCipher )
1572         rtl_cipher_destroyARCFOUR( aCipher );
1573 
1574     if( ! bSuccess )
1575         io_rProperties.UValue.clear();
1576     return bSuccess;
1577 }
1578 
1579 /* end i12626 methods */
1580 
1581 static const long unsetRun[256] =
1582 {
1583     8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,	/* 0x00 - 0x0f */
1584     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 0x10 - 0x1f */
1585     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0x20 - 0x2f */
1586     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0x30 - 0x3f */
1587     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x40 - 0x4f */
1588     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x50 - 0x5f */
1589     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x60 - 0x6f */
1590     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x70 - 0x7f */
1591     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x80 - 0x8f */
1592     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x90 - 0x9f */
1593     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xa0 - 0xaf */
1594     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xb0 - 0xbf */
1595     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xc0 - 0xcf */
1596     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xd0 - 0xdf */
1597     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xe0 - 0xef */
1598     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xf0 - 0xff */
1599 };
1600 
1601 static const long setRun[256] =
1602 {
1603     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x00 - 0x0f */
1604     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x10 - 0x1f */
1605     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x20 - 0x2f */
1606     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x30 - 0x3f */
1607     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x40 - 0x4f */
1608     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x50 - 0x5f */
1609     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x60 - 0x6f */
1610     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x70 - 0x7f */
1611     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x80 - 0x8f */
1612     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x90 - 0x9f */
1613     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0xa0 - 0xaf */
1614     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0xb0 - 0xbf */
1615     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0xc0 - 0xcf */
1616     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0xd0 - 0xdf */
1617     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 0xe0 - 0xef */
1618     4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8,	/* 0xf0 - 0xff */
1619 };
1620 
1621 inline bool isSet( const Scanline i_pLine, long i_nIndex )
1622 {
1623     return (i_pLine[ i_nIndex/8 ] & (0x80 >> (i_nIndex&7))) != 0;
1624 }
1625 
1626 long findBitRun( const Scanline i_pLine, long i_nStartIndex, long i_nW, bool i_bSet )
1627 {
1628     if( i_nStartIndex < 0 )
1629         return i_nW;
1630 
1631     long nIndex = i_nStartIndex;
1632     if( nIndex < i_nW )
1633     {
1634         const sal_uInt8 * pByte = static_cast<sal_uInt8*>(i_pLine) + (nIndex/8);
1635         sal_uInt8 nByte = *pByte;
1636 
1637         // run up to byte boundary
1638         long nBitInByte = (nIndex & 7);
1639         if( nBitInByte )
1640         {
1641             sal_uInt8 nMask = 0x80 >> nBitInByte;
1642             while( nBitInByte != 8 )
1643             {
1644                 if( (nByte & nMask) != (i_bSet ? nMask : 0) )
1645                     return nIndex < i_nW ? nIndex : i_nW;
1646                 nMask = nMask >> 1;
1647                 nBitInByte++;
1648                 nIndex++;
1649             }
1650             if( nIndex < i_nW )
1651             {
1652                 pByte++;
1653                 nByte = *pByte;
1654             }
1655         }
1656 
1657         sal_uInt8 nRunByte;
1658         const long* pRunTable;
1659         if( i_bSet )
1660         {
1661             nRunByte = 0xff;
1662             pRunTable = setRun;
1663         }
1664         else
1665         {
1666             nRunByte = 0;
1667             pRunTable = unsetRun;
1668         }
1669 
1670         while( nByte == nRunByte && nIndex < i_nW )
1671         {
1672             nIndex += 8;
1673             pByte++;
1674             nByte = *pByte;
1675         }
1676         if( nIndex < i_nW )
1677         {
1678             nIndex += pRunTable[nByte];
1679         }
1680     }
1681     return nIndex < i_nW ? nIndex : i_nW;
1682 }
1683 
1684 struct BitStreamState
1685 {
1686     sal_uInt8       mnBuffer;
1687     sal_uInt32      mnNextBitPos;
1688 
1689     BitStreamState()
1690     : mnBuffer( 0 )
1691     , mnNextBitPos( 8 )
1692     {
1693     }
1694 
1695     const sal_uInt8* getByte() const { return &mnBuffer; }
1696     void flush() { mnNextBitPos = 8; mnBuffer = 0; }
1697 };
1698 
1699 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState )
1700 {
1701     while( i_nLength > io_rState.mnNextBitPos )
1702     {
1703         io_rState.mnBuffer |= static_cast<sal_uInt8>( i_nCode >> (i_nLength - io_rState.mnNextBitPos) );
1704         i_nLength -= io_rState.mnNextBitPos;
1705         writeBuffer( io_rState.getByte(), 1 );
1706         io_rState.flush();
1707     }
1708     OSL_ASSERT( i_nLength < 9 );
1709     static const unsigned int msbmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1710     io_rState.mnBuffer |= static_cast<sal_uInt8>( (i_nCode & msbmask[i_nLength]) << (io_rState.mnNextBitPos - i_nLength) );
1711     io_rState.mnNextBitPos -= i_nLength;
1712     if( io_rState.mnNextBitPos == 0 )
1713     {
1714         writeBuffer( io_rState.getByte(), 1 );
1715         io_rState.flush();
1716     }
1717 }
1718 
1719 struct PixelCode
1720 {
1721     sal_uInt32      mnEncodedPixels;
1722     sal_uInt32      mnCodeBits;
1723     sal_uInt32      mnCode;
1724 };
1725 
1726 static const PixelCode WhitePixelCodes[] =
1727 {
1728     { 0, 8, 0x35 },     // 0011 0101
1729     { 1, 6, 0x7 },      // 0001 11
1730     { 2, 4, 0x7 },      // 0111
1731     { 3, 4, 0x8 },      // 1000
1732     { 4, 4, 0xB },      // 1011
1733     { 5, 4, 0xC },      // 1100
1734     { 6, 4, 0xE },      // 1110
1735     { 7, 4, 0xF },      // 1111
1736     { 8, 5, 0x13 },     // 1001 1
1737     { 9, 5, 0x14 },     // 1010 0
1738     { 10, 5, 0x7 },     // 0011 1
1739     { 11, 5, 0x8 },     // 0100 0
1740     { 12, 6, 0x8 },     // 0010 00
1741     { 13, 6, 0x3 },     // 0000 11
1742     { 14, 6, 0x34 },    // 1101 00
1743     { 15, 6, 0x35 },    // 1101 01
1744     { 16, 6, 0x2A },    // 1010 10
1745     { 17, 6, 0x2B },    // 1010 11
1746     { 18, 7, 0x27 },    // 0100 111
1747     { 19, 7, 0xC },     // 0001 100
1748     { 20, 7, 0x8 },     // 0001 000
1749     { 21, 7, 0x17 },    // 0010 111
1750     { 22, 7, 0x3 },     // 0000 011
1751     { 23, 7, 0x4 },     // 0000 100
1752     { 24, 7, 0x28 },    // 0101 000
1753     { 25, 7, 0x2B },    // 0101 011
1754     { 26, 7, 0x13 },    // 0010 011
1755     { 27, 7, 0x24 },    // 0100 100
1756     { 28, 7, 0x18 },    // 0011 000
1757     { 29, 8, 0x2 },     // 0000 0010
1758     { 30, 8, 0x3 },     // 0000 0011
1759     { 31, 8, 0x1A },    // 0001 1010
1760     { 32, 8, 0x1B },    // 0001 1011
1761     { 33, 8, 0x12 },    // 0001 0010
1762     { 34, 8, 0x13 },    // 0001 0011
1763     { 35, 8, 0x14 },    // 0001 0100
1764     { 36, 8, 0x15 },    // 0001 0101
1765     { 37, 8, 0x16 },    // 0001 0110
1766     { 38, 8, 0x17 },    // 0001 0111
1767     { 39, 8, 0x28 },    // 0010 1000
1768     { 40, 8, 0x29 },    // 0010 1001
1769     { 41, 8, 0x2A },    // 0010 1010
1770     { 42, 8, 0x2B },    // 0010 1011
1771     { 43, 8, 0x2C },    // 0010 1100
1772     { 44, 8, 0x2D },    // 0010 1101
1773     { 45, 8, 0x4 },     // 0000 0100
1774     { 46, 8, 0x5 },     // 0000 0101
1775     { 47, 8, 0xA },     // 0000 1010
1776     { 48, 8, 0xB },     // 0000 1011
1777     { 49, 8, 0x52 },    // 0101 0010
1778     { 50, 8, 0x53 },    // 0101 0011
1779     { 51, 8, 0x54 },    // 0101 0100
1780     { 52, 8, 0x55 },    // 0101 0101
1781     { 53, 8, 0x24 },    // 0010 0100
1782     { 54, 8, 0x25 },    // 0010 0101
1783     { 55, 8, 0x58 },    // 0101 1000
1784     { 56, 8, 0x59 },    // 0101 1001
1785     { 57, 8, 0x5A },    // 0101 1010
1786     { 58, 8, 0x5B },    // 0101 1011
1787     { 59, 8, 0x4A },    // 0100 1010
1788     { 60, 8, 0x4B },    // 0100 1011
1789     { 61, 8, 0x32 },    // 0011 0010
1790     { 62, 8, 0x33 },    // 0011 0011
1791     { 63, 8, 0x34 },    // 0011 0100
1792     { 64, 5, 0x1B },    // 1101 1
1793     { 128, 5, 0x12 },   // 1001 0
1794     { 192, 6, 0x17 },   // 0101 11
1795     { 256, 7, 0x37 },   // 0110 111
1796     { 320, 8, 0x36 },   // 0011 0110
1797     { 384, 8, 0x37 },   // 0011 0111
1798     { 448, 8, 0x64 },   // 0110 0100
1799     { 512, 8, 0x65 },   // 0110 0101
1800     { 576, 8, 0x68 },   // 0110 1000
1801     { 640, 8, 0x67 },   // 0110 0111
1802     { 704, 9, 0xCC },   // 0110 0110 0
1803     { 768, 9, 0xCD },   // 0110 0110 1
1804     { 832, 9, 0xD2 },   // 0110 1001 0
1805     { 896, 9, 0xD3 },   // 0110 1001 1
1806     { 960, 9, 0xD4 },   // 0110 1010 0
1807     { 1024, 9, 0xD5 },  // 0110 1010 1
1808     { 1088, 9, 0xD6 },  // 0110 1011 0
1809     { 1152, 9, 0xD7 },  // 0110 1011 1
1810     { 1216, 9, 0xD8 },  // 0110 1100 0
1811     { 1280, 9, 0xD9 },  // 0110 1100 1
1812     { 1344, 9, 0xDA },  // 0110 1101 0
1813     { 1408, 9, 0xDB },  // 0110 1101 1
1814     { 1472, 9, 0x98 },  // 0100 1100 0
1815     { 1536, 9, 0x99 },  // 0100 1100 1
1816     { 1600, 9, 0x9A },  // 0100 1101 0
1817     { 1664, 6, 0x18 },  // 0110 00
1818     { 1728, 9, 0x9B },  // 0100 1101 1
1819     { 1792, 11, 0x8 },  // 0000 0001 000
1820     { 1856, 11, 0xC },  // 0000 0001 100
1821     { 1920, 11, 0xD },  // 0000 0001 101
1822     { 1984, 12, 0x12 }, // 0000 0001 0010
1823     { 2048, 12, 0x13 }, // 0000 0001 0011
1824     { 2112, 12, 0x14 }, // 0000 0001 0100
1825     { 2176, 12, 0x15 }, // 0000 0001 0101
1826     { 2240, 12, 0x16 }, // 0000 0001 0110
1827     { 2304, 12, 0x17 }, // 0000 0001 0111
1828     { 2368, 12, 0x1C }, // 0000 0001 1100
1829     { 2432, 12, 0x1D }, // 0000 0001 1101
1830     { 2496, 12, 0x1E }, // 0000 0001 1110
1831     { 2560, 12, 0x1F }  // 0000 0001 1111
1832 };
1833 
1834 static const PixelCode BlackPixelCodes[] =
1835 {
1836     { 0, 10, 0x37 },    // 0000 1101 11
1837     { 1, 3, 0x2 },      // 010
1838     { 2, 2, 0x3 },      // 11
1839     { 3, 2, 0x2 },      // 10
1840     { 4, 3, 0x3 },      // 011
1841     { 5, 4, 0x3 },      // 0011
1842     { 6, 4, 0x2 },      // 0010
1843     { 7, 5, 0x3 },      // 0001 1
1844     { 8, 6, 0x5 },      // 0001 01
1845     { 9, 6, 0x4 },      // 0001 00
1846     { 10, 7, 0x4 },     // 0000 100
1847     { 11, 7, 0x5 },     // 0000 101
1848     { 12, 7, 0x7 },     // 0000 111
1849     { 13, 8, 0x4 },     // 0000 0100
1850     { 14, 8, 0x7 },     // 0000 0111
1851     { 15, 9, 0x18 },    // 0000 1100 0
1852     { 16, 10, 0x17 },   // 0000 0101 11
1853     { 17, 10, 0x18 },   // 0000 0110 00
1854     { 18, 10, 0x8 },    // 0000 0010 00
1855     { 19, 11, 0x67 },   // 0000 1100 111
1856     { 20, 11, 0x68 },   // 0000 1101 000
1857     { 21, 11, 0x6C },   // 0000 1101 100
1858     { 22, 11, 0x37 },   // 0000 0110 111
1859     { 23, 11, 0x28 },   // 0000 0101 000
1860     { 24, 11, 0x17 },   // 0000 0010 111
1861     { 25, 11, 0x18 },   // 0000 0011 000
1862     { 26, 12, 0xCA },   // 0000 1100 1010
1863     { 27, 12, 0xCB },   // 0000 1100 1011
1864     { 28, 12, 0xCC },   // 0000 1100 1100
1865     { 29, 12, 0xCD },   // 0000 1100 1101
1866     { 30, 12, 0x68 },   // 0000 0110 1000
1867     { 31, 12, 0x69 },   // 0000 0110 1001
1868     { 32, 12, 0x6A },   // 0000 0110 1010
1869     { 33, 12, 0x6B },   // 0000 0110 1011
1870     { 34, 12, 0xD2 },   // 0000 1101 0010
1871     { 35, 12, 0xD3 },   // 0000 1101 0011
1872     { 36, 12, 0xD4 },   // 0000 1101 0100
1873     { 37, 12, 0xD5 },   // 0000 1101 0101
1874     { 38, 12, 0xD6 },   // 0000 1101 0110
1875     { 39, 12, 0xD7 },   // 0000 1101 0111
1876     { 40, 12, 0x6C },   // 0000 0110 1100
1877     { 41, 12, 0x6D },   // 0000 0110 1101
1878     { 42, 12, 0xDA },   // 0000 1101 1010
1879     { 43, 12, 0xDB },   // 0000 1101 1011
1880     { 44, 12, 0x54 },   // 0000 0101 0100
1881     { 45, 12, 0x55 },   // 0000 0101 0101
1882     { 46, 12, 0x56 },   // 0000 0101 0110
1883     { 47, 12, 0x57 },   // 0000 0101 0111
1884     { 48, 12, 0x64 },   // 0000 0110 0100
1885     { 49, 12, 0x65 },   // 0000 0110 0101
1886     { 50, 12, 0x52 },   // 0000 0101 0010
1887     { 51, 12, 0x53 },   // 0000 0101 0011
1888     { 52, 12, 0x24 },   // 0000 0010 0100
1889     { 53, 12, 0x37 },   // 0000 0011 0111
1890     { 54, 12, 0x38 },   // 0000 0011 1000
1891     { 55, 12, 0x27 },   // 0000 0010 0111
1892     { 56, 12, 0x28 },   // 0000 0010 1000
1893     { 57, 12, 0x58 },   // 0000 0101 1000
1894     { 58, 12, 0x59 },   // 0000 0101 1001
1895     { 59, 12, 0x2B },   // 0000 0010 1011
1896     { 60, 12, 0x2C },   // 0000 0010 1100
1897     { 61, 12, 0x5A },   // 0000 0101 1010
1898     { 62, 12, 0x66 },   // 0000 0110 0110
1899     { 63, 12, 0x67 },   // 0000 0110 0111
1900     { 64, 10, 0xF },    // 0000 0011 11
1901     { 128, 12, 0xC8 },  // 0000 1100 1000
1902     { 192, 12, 0xC9 },  // 0000 1100 1001
1903     { 256, 12, 0x5B },  // 0000 0101 1011
1904     { 320, 12, 0x33 },  // 0000 0011 0011
1905     { 384, 12, 0x34 },  // 0000 0011 0100
1906     { 448, 12, 0x35 },  // 0000 0011 0101
1907     { 512, 13, 0x6C },  // 0000 0011 0110 0
1908     { 576, 13, 0x6D },  // 0000 0011 0110 1
1909     { 640, 13, 0x4A },  // 0000 0010 0101 0
1910     { 704, 13, 0x4B },  // 0000 0010 0101 1
1911     { 768, 13, 0x4C },  // 0000 0010 0110 0
1912     { 832, 13, 0x4D },  // 0000 0010 0110 1
1913     { 896, 13, 0x72 },  // 0000 0011 1001 0
1914     { 960, 13, 0x73 },  // 0000 0011 1001 1
1915     { 1024, 13, 0x74 }, // 0000 0011 1010 0
1916     { 1088, 13, 0x75 }, // 0000 0011 1010 1
1917     { 1152, 13, 0x76 }, // 0000 0011 1011 0
1918     { 1216, 13, 0x77 }, // 0000 0011 1011 1
1919     { 1280, 13, 0x52 }, // 0000 0010 1001 0
1920     { 1344, 13, 0x53 }, // 0000 0010 1001 1
1921     { 1408, 13, 0x54 }, // 0000 0010 1010 0
1922     { 1472, 13, 0x55 }, // 0000 0010 1010 1
1923     { 1536, 13, 0x5A }, // 0000 0010 1101 0
1924     { 1600, 13, 0x5B }, // 0000 0010 1101 1
1925     { 1664, 13, 0x64 }, // 0000 0011 0010 0
1926     { 1728, 13, 0x65 }, // 0000 0011 0010 1
1927     { 1792, 11, 0x8 },  // 0000 0001 000
1928     { 1856, 11, 0xC },  // 0000 0001 100
1929     { 1920, 11, 0xD },  // 0000 0001 101
1930     { 1984, 12, 0x12 }, // 0000 0001 0010
1931     { 2048, 12, 0x13 }, // 0000 0001 0011
1932     { 2112, 12, 0x14 }, // 0000 0001 0100
1933     { 2176, 12, 0x15 }, // 0000 0001 0101
1934     { 2240, 12, 0x16 }, // 0000 0001 0110
1935     { 2304, 12, 0x17 }, // 0000 0001 0111
1936     { 2368, 12, 0x1C }, // 0000 0001 1100
1937     { 2432, 12, 0x1D }, // 0000 0001 1101
1938     { 2496, 12, 0x1E }, // 0000 0001 1110
1939     { 2560, 12, 0x1F }  // 0000 0001 1111
1940 };
1941 
1942 
1943 void PDFWriterImpl::putG4Span( long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState )
1944 {
1945     const PixelCode* pTable = i_bWhitePixel ? WhitePixelCodes : BlackPixelCodes;
1946     // maximum encoded span is 2560 consecutive pixels
1947     while( i_nSpan > 2623 )
1948     {
1949         // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1950         putG4Bits( pTable[103].mnCodeBits, pTable[103].mnCode, io_rState );
1951         i_nSpan -= pTable[103].mnEncodedPixels;
1952     }
1953     // write multiples of 64 pixels up to 2560
1954     if( i_nSpan > 63 )
1955     {
1956         sal_uInt32 nTabIndex = 63 + (i_nSpan >> 6);
1957         OSL_ASSERT( pTable[nTabIndex].mnEncodedPixels == static_cast<sal_uInt32>(64*(i_nSpan >> 6)) );
1958         putG4Bits( pTable[nTabIndex].mnCodeBits, pTable[nTabIndex].mnCode, io_rState );
1959         i_nSpan -= pTable[nTabIndex].mnEncodedPixels;
1960     }
1961     putG4Bits( pTable[i_nSpan].mnCodeBits, pTable[i_nSpan].mnCode, io_rState );
1962 }
1963 
1964 void PDFWriterImpl::writeG4Stream( BitmapReadAccess* i_pBitmap )
1965 {
1966     long nW = i_pBitmap->Width();
1967     long nH = i_pBitmap->Height();
1968     if( nW <= 0 || nH <= 0 )
1969         return;
1970     if( i_pBitmap->GetBitCount() != 1 )
1971         return;
1972 
1973     BitStreamState aBitState;
1974 
1975     // the first reference line is virtual and completely empty
1976     const Scanline pFirstRefLine = (Scanline)rtl_allocateZeroMemory( nW/8 + 1 );
1977     Scanline pRefLine = pFirstRefLine;
1978     for( long nY = 0; nY < nH; nY++ )
1979     {
1980         const Scanline pCurLine = i_pBitmap->GetScanline( nY );
1981         long nLineIndex = 0;
1982         bool bRunSet = (*pCurLine & 0x80) ? true : false;
1983         bool bRefSet = (*pRefLine & 0x80) ? true : false;
1984         long nRunIndex1 = bRunSet ? 0 : findBitRun( pCurLine, 0, nW, bRunSet );
1985         long nRefIndex1 = bRefSet ? 0 : findBitRun( pRefLine, 0, nW, bRefSet );
1986         for( ; nLineIndex < nW; )
1987         {
1988             long nRefIndex2 = findBitRun( pRefLine, nRefIndex1, nW, isSet( pRefLine, nRefIndex1 ) );
1989             if( nRefIndex2 >= nRunIndex1 )
1990             {
1991                 long nDiff = nRefIndex1 - nRunIndex1;
1992                 if( -3 <= nDiff && nDiff <= 3 )
1993                 {   // vertical coding
1994                     static const struct
1995                     {
1996                         sal_uInt32 mnCodeBits;
1997                         sal_uInt32 mnCode;
1998                     } VerticalCodes[7] = {
1999                         { 7, 0x03 },    // 0000 011
2000                         { 6, 0x03 },    // 0000 11
2001                         { 3, 0x03 },    // 011
2002                         { 1, 0x1 },     // 1
2003                         { 3, 0x2 },     // 010
2004                         { 6, 0x02 },    // 0000 10
2005                         { 7, 0x02 }     // 0000 010
2006                     };
2007                     // convert to index
2008                     nDiff += 3;
2009 
2010                     // emit diff code
2011                     putG4Bits( VerticalCodes[nDiff].mnCodeBits, VerticalCodes[nDiff].mnCode, aBitState );
2012                     nLineIndex = nRunIndex1;
2013                 }
2014                 else
2015                 {   // difference too large, horizontal coding
2016                     // emit horz code 001
2017                     putG4Bits( 3, 0x1, aBitState );
2018                     long nRunIndex2 = findBitRun( pCurLine, nRunIndex1, nW, isSet( pCurLine, nRunIndex1 ) );
2019                     bool bWhiteFirst = ( nLineIndex + nRunIndex1 == 0 || ! isSet( pCurLine, nLineIndex ) );
2020                     putG4Span( nRunIndex1 - nLineIndex, bWhiteFirst, aBitState );
2021                     putG4Span( nRunIndex2 - nRunIndex1, ! bWhiteFirst, aBitState );
2022                     nLineIndex = nRunIndex2;
2023                 }
2024             }
2025             else
2026             {   // emit pass code 0001
2027                 putG4Bits( 4, 0x1, aBitState );
2028                 nLineIndex = nRefIndex2;
2029             }
2030             if( nLineIndex < nW )
2031             {
2032                 bool bSet = isSet( pCurLine, nLineIndex );
2033                 nRunIndex1 = findBitRun( pCurLine, nLineIndex, nW, bSet );
2034                 nRefIndex1 = findBitRun( pRefLine, nLineIndex, nW, ! bSet );
2035                 nRefIndex1 = findBitRun( pRefLine, nRefIndex1, nW, bSet );
2036             }
2037         }
2038 
2039         // the current line is the reference for the next line
2040         pRefLine = pCurLine;
2041     }
2042     // terminate strip with EOFB
2043     putG4Bits( 12, 1, aBitState );
2044     putG4Bits( 12, 1, aBitState );
2045     if( aBitState.mnNextBitPos != 8 )
2046     {
2047         writeBuffer( aBitState.getByte(), 1 );
2048         aBitState.flush();
2049     }
2050 
2051     rtl_freeMemory( pFirstRefLine );
2052 }
2053