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