xref: /aoo41x/main/vcl/source/gdi/pdfwriter_impl2.cxx (revision 7b6ba143)
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 
424                         // #115962# Always use at least 300 DPI for bitmap conversion of transparence gradients,
425                         // else the quality is not acceptable (see bugdoc as example)
426                         // sal_Int32	nMaxBmpDPI = i_rContext.m_bOnlyLosslessCompression ? 300 : 72;
427                         sal_Int32 nMaxBmpDPI(300);
428 
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 				default:
1052 					// #i24604# Made assertion fire only once per
1053 					// metafile. The asserted actions here are all
1054 					// deprecated
1055 					if( !bAssertionFired )
1056 					{
1057 						bAssertionFired = true;
1058 						DBG_ERROR( "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered" );
1059 					}
1060 				break;
1061 			}
1062 			i++;
1063 		}
1064 	}
1065 
1066     delete pPrivateDevice;
1067 }
1068 
1069 // Encryption methods
1070 
1071 /* a crutch to transport an rtlDigest safely though UNO API
1072    this is needed for the PDF export dialog, which otherwise would have to pass
1073    clear text passwords down till they can be used in PDFWriter. Unfortunately
1074    the MD5 sum of the password (which is needed to create the PDF encryption key)
1075    is not sufficient, since an rtl MD5 digest cannot be created in an arbitrary state
1076    which would be needed in PDFWriterImpl::computeEncryptionKey.
1077 */
1078 class EncHashTransporter : public cppu::WeakImplHelper1 < com::sun::star::beans::XMaterialHolder >
1079 {
1080     rtlDigest                   maUDigest;
1081     sal_IntPtr                  maID;
1082     std::vector< sal_uInt8 >    maOValue;
1083 
1084     static std::map< sal_IntPtr, EncHashTransporter* >      sTransporters;
1085 public:
1086     EncHashTransporter()
1087     : maUDigest( rtl_digest_createMD5() )
1088     {
1089         maID = reinterpret_cast< sal_IntPtr >(this);
1090         while( sTransporters.find( maID ) != sTransporters.end() ) // paranoia mode
1091             maID++;
1092         sTransporters[ maID ] = this;
1093     }
1094 
1095     virtual ~EncHashTransporter()
1096     {
1097         sTransporters.erase( maID );
1098         if( maUDigest )
1099             rtl_digest_destroyMD5( maUDigest );
1100         OSL_TRACE( "EncHashTransporter freed\n" );
1101     }
1102 
1103     rtlDigest getUDigest() const { return maUDigest; };
1104     std::vector< sal_uInt8 >& getOValue() { return maOValue; }
1105     void invalidate()
1106     {
1107         if( maUDigest )
1108         {
1109             rtl_digest_destroyMD5( maUDigest );
1110             maUDigest = NULL;
1111         }
1112     }
1113 
1114     // XMaterialHolder
1115     virtual uno::Any SAL_CALL getMaterial() throw()
1116     {
1117         return uno::makeAny( sal_Int64(maID) );
1118     }
1119 
1120     static EncHashTransporter* getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& );
1121 
1122 };
1123 
1124 std::map< sal_IntPtr, EncHashTransporter* > EncHashTransporter::sTransporters;
1125 
1126 EncHashTransporter* EncHashTransporter::getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& xRef )
1127 {
1128     EncHashTransporter* pResult = NULL;
1129     if( xRef.is() )
1130     {
1131         uno::Any aMat( xRef->getMaterial() );
1132         sal_Int64 nMat = 0;
1133         if( aMat >>= nMat )
1134         {
1135             std::map< sal_IntPtr, EncHashTransporter* >::iterator it = sTransporters.find( static_cast<sal_IntPtr>(nMat) );
1136             if( it != sTransporters.end() )
1137                 pResult = it->second;
1138         }
1139     }
1140     return pResult;
1141 }
1142 
1143 sal_Bool PDFWriterImpl::checkEncryptionBufferSize( register sal_Int32 newSize )
1144 {
1145     if( m_nEncryptionBufferSize < newSize )
1146     {
1147         /* reallocate the buffer, the used function allocate as rtl_allocateMemory
1148         if the pointer parameter is NULL */
1149         m_pEncryptionBuffer = (sal_uInt8*)rtl_reallocateMemory( m_pEncryptionBuffer, newSize );
1150         if( m_pEncryptionBuffer )
1151             m_nEncryptionBufferSize = newSize;
1152         else
1153             m_nEncryptionBufferSize = 0;
1154     }
1155     return ( m_nEncryptionBufferSize != 0 );
1156 }
1157 
1158 void PDFWriterImpl::checkAndEnableStreamEncryption( register sal_Int32 nObject )
1159 {
1160     if( m_aContext.Encryption.Encrypt() )
1161     {
1162         m_bEncryptThisStream = true;
1163         sal_Int32 i = m_nKeyLength;
1164         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject;
1165         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 );
1166         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 );
1167         //the other location of m_nEncryptionKey are already set to 0, our fixed generation number
1168         // do the MD5 hash
1169         sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1170         // the i+2 to take into account the generation number, always zero
1171         rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) );
1172         // initialize the RC4 with the key
1173         // key legth: see algoritm 3.1, step 4: (N+5) max 16
1174         rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 );
1175     }
1176 }
1177 
1178 void PDFWriterImpl::enableStringEncryption( register sal_Int32 nObject )
1179 {
1180     if( m_aContext.Encryption.Encrypt() )
1181     {
1182         sal_Int32 i = m_nKeyLength;
1183         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject;
1184         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 );
1185         m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 );
1186         //the other location of m_nEncryptionKey are already set to 0, our fixed generation number
1187         // do the MD5 hash
1188         sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1189         // the i+2 to take into account the generation number, always zero
1190         rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) );
1191         // initialize the RC4 with the key
1192         // key legth: see algoritm 3.1, step 4: (N+5) max 16
1193         rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 );
1194     }
1195 }
1196 
1197 /* init the encryption engine
1198 1. init the document id, used both for building the document id and for building the encryption key(s)
1199 2. build the encryption key following algorithms described in the PDF specification
1200  */
1201 uno::Reference< beans::XMaterialHolder > PDFWriterImpl::initEncryption( const rtl::OUString& i_rOwnerPassword,
1202                                                                         const rtl::OUString& i_rUserPassword,
1203                                                                         bool b128Bit
1204                                                                         )
1205 {
1206     uno::Reference< beans::XMaterialHolder > xResult;
1207     if( i_rOwnerPassword.getLength() || i_rUserPassword.getLength() )
1208     {
1209         EncHashTransporter* pTransporter = new EncHashTransporter;
1210         xResult = pTransporter;
1211 
1212         // get padded passwords
1213         sal_uInt8 aPadUPW[ENCRYPTED_PWD_SIZE], aPadOPW[ENCRYPTED_PWD_SIZE];
1214         padPassword( i_rOwnerPassword.getLength() ? i_rOwnerPassword : i_rUserPassword, aPadOPW );
1215         padPassword( i_rUserPassword, aPadUPW );
1216         sal_Int32 nKeyLength = SECUR_40BIT_KEY;
1217         if( b128Bit )
1218             nKeyLength = SECUR_128BIT_KEY;
1219 
1220         if( computeODictionaryValue( aPadOPW, aPadUPW, pTransporter->getOValue(), nKeyLength ) )
1221         {
1222             rtlDigest aDig = pTransporter->getUDigest();
1223             if( rtl_digest_updateMD5( aDig, aPadUPW, ENCRYPTED_PWD_SIZE ) != rtl_Digest_E_None )
1224                 xResult.clear();
1225         }
1226         else
1227             xResult.clear();
1228 
1229         // trash temporary padded cleartext PWDs
1230         rtl_zeroMemory( aPadOPW, sizeof(aPadOPW) );
1231         rtl_zeroMemory( aPadUPW, sizeof(aPadUPW) );
1232 
1233     }
1234     return xResult;
1235 }
1236 
1237 bool PDFWriterImpl::prepareEncryption( const uno::Reference< beans::XMaterialHolder >& xEnc )
1238 {
1239     bool bSuccess = false;
1240     EncHashTransporter* pTransporter = EncHashTransporter::getEncHashTransporter( xEnc );
1241     if( pTransporter )
1242     {
1243         sal_Int32 nKeyLength = 0, nRC4KeyLength = 0;
1244         sal_Int32 nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, nKeyLength, nRC4KeyLength );
1245         m_aContext.Encryption.OValue = pTransporter->getOValue();
1246         bSuccess = computeUDictionaryValue( pTransporter, m_aContext.Encryption, nKeyLength, nAccessPermissions );
1247     }
1248     if( ! bSuccess )
1249     {
1250         m_aContext.Encryption.OValue.clear();
1251         m_aContext.Encryption.UValue.clear();
1252         m_aContext.Encryption.EncryptionKey.clear();
1253     }
1254     return bSuccess;
1255 }
1256 
1257 sal_Int32 PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties,
1258 	                                               sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength )
1259 {
1260     /*
1261     2) compute the access permissions, in numerical form
1262 
1263     the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
1264     - for 40 bit security the unused bit must be set to 1, since they are not used
1265     - for 128 bit security the same bit must be preset to 0 and set later if needed
1266     according to the table 3.15, pdf v 1.4 */
1267     sal_Int32 nAccessPermissions = ( i_rProperties.Security128bit ) ? 0xfffff0c0 : 0xffffffc0 ;
1268 
1269     /* check permissions for 40 bit security case */
1270     nAccessPermissions |= ( i_rProperties.CanPrintTheDocument ) ?  1 << 2 : 0;
1271     nAccessPermissions |= ( i_rProperties.CanModifyTheContent ) ? 1 << 3 : 0;
1272     nAccessPermissions |= ( i_rProperties.CanCopyOrExtract ) ?   1 << 4 : 0;
1273     nAccessPermissions |= ( i_rProperties.CanAddOrModify ) ? 1 << 5 : 0;
1274     o_rKeyLength = SECUR_40BIT_KEY;
1275     o_rRC4KeyLength = SECUR_40BIT_KEY+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5
1276 
1277     if( i_rProperties.Security128bit )
1278     {
1279         o_rKeyLength = SECUR_128BIT_KEY;
1280         o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum
1281         // permitted value is 16
1282         nAccessPermissions |= ( i_rProperties.CanFillInteractive ) ?         1 << 8 : 0;
1283         nAccessPermissions |= ( i_rProperties.CanExtractForAccessibility ) ? 1 << 9 : 0;
1284         nAccessPermissions |= ( i_rProperties.CanAssemble ) ?                1 << 10 : 0;
1285         nAccessPermissions |= ( i_rProperties.CanPrintFull ) ?               1 << 11 : 0;
1286     }
1287     return nAccessPermissions;
1288 }
1289 
1290 /*************************************************************
1291 begin i12626 methods
1292 
1293 Implements Algorithm 3.2, step 1 only
1294 */
1295 void PDFWriterImpl::padPassword( const rtl::OUString& i_rPassword, sal_uInt8* o_pPaddedPW )
1296 {
1297     // get ansi-1252 version of the password string CHECKIT ! i12626
1298     rtl::OString aString( rtl::OUStringToOString( i_rPassword, RTL_TEXTENCODING_MS_1252 ) );
1299 
1300     //copy the string to the target
1301     sal_Int32 nToCopy = ( aString.getLength() < ENCRYPTED_PWD_SIZE ) ? aString.getLength() : ENCRYPTED_PWD_SIZE;
1302     sal_Int32 nCurrentChar;
1303 
1304     for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ )
1305         o_pPaddedPW[nCurrentChar] = (sal_uInt8)( aString.getStr()[nCurrentChar] );
1306 
1307     //pad it with standard byte string
1308     sal_Int32 i,y;
1309     for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ )
1310         o_pPaddedPW[i] = s_nPadString[y];
1311 
1312     // trash memory of temporary clear text password
1313     rtl_zeroMemory( (sal_Char*)aString.getStr(), aString.getLength() );
1314 }
1315 
1316 /**********************************
1317 Algorithm 3.2  Compute the encryption key used
1318 
1319 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
1320 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
1321 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
1322 
1323 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
1324 
1325 */
1326 bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter* i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, sal_Int32 i_nAccessPermissions )
1327 {
1328     bool bSuccess = true;
1329     sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1330 
1331     // transporter contains an MD5 digest with the padded user password already
1332     rtlDigest aDigest = i_pTransporter->getUDigest();
1333     rtlDigestError nError = rtl_Digest_E_None;
1334     if( aDigest )
1335     {
1336         //step 3
1337         if( ! io_rProperties.OValue.empty() )
1338             nError = rtl_digest_updateMD5( aDigest, &io_rProperties.OValue[0] , sal_Int32(io_rProperties.OValue.size()) );
1339         else
1340             bSuccess = false;
1341         //Step 4
1342         sal_uInt8 nPerm[4];
1343 
1344         nPerm[0] = (sal_uInt8)i_nAccessPermissions;
1345         nPerm[1] = (sal_uInt8)( i_nAccessPermissions >> 8 );
1346         nPerm[2] = (sal_uInt8)( i_nAccessPermissions >> 16 );
1347         nPerm[3] = (sal_uInt8)( i_nAccessPermissions >> 24 );
1348 
1349         if( nError == rtl_Digest_E_None )
1350             nError = rtl_digest_updateMD5( aDigest, nPerm , sizeof( nPerm ) );
1351 
1352         //step 5, get the document ID, binary form
1353         if( nError == rtl_Digest_E_None )
1354             nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) );
1355         //get the digest
1356         if( nError == rtl_Digest_E_None )
1357         {
1358             rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1359 
1360             //step 6, only if 128 bit
1361             if( io_rProperties.Security128bit )
1362             {
1363                 for( sal_Int32 i = 0; i < 50; i++ )
1364                 {
1365                     nError = rtl_digest_updateMD5( aDigest, &nMD5Sum, sizeof( nMD5Sum ) );
1366                     if( nError != rtl_Digest_E_None )
1367                     {
1368                         bSuccess =  false;
1369                         break;
1370                     }
1371                     rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1372                 }
1373             }
1374         }
1375     }
1376     else
1377         bSuccess = false;
1378 
1379     i_pTransporter->invalidate();
1380 
1381     //Step 7
1382     if( bSuccess )
1383     {
1384         io_rProperties.EncryptionKey.resize( MAXIMUM_RC4_KEY_LENGTH );
1385         for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ )
1386             io_rProperties.EncryptionKey[i] = nMD5Sum[i];
1387     }
1388     else
1389         io_rProperties.EncryptionKey.clear();
1390 
1391     return bSuccess;
1392 }
1393 
1394 /**********************************
1395 Algorithm 3.3  Compute the encryption dictionary /O value, save into the class data member
1396 the step numbers down here correspond to the ones in PDF v.1.4 specfication
1397 */
1398 bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword,
1399                                              const sal_uInt8* i_pPaddedUserPassword,
1400                                              std::vector< sal_uInt8 >& io_rOValue,
1401                                              sal_Int32 i_nKeyLength
1402                                              )
1403 {
1404     bool bSuccess = true;
1405 
1406     io_rOValue.resize( ENCRYPTED_PWD_SIZE );
1407 
1408     rtlDigest aDigest = rtl_digest_createMD5();
1409     rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1410     if( aDigest && aCipher)
1411     {
1412         //step 1 already done, data is in i_pPaddedOwnerPassword
1413         //step 2
1414 
1415         rtlDigestError nError = rtl_digest_updateMD5( aDigest, i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE );
1416         if( nError == rtl_Digest_E_None )
1417         {
1418             sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1419 
1420             rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) );
1421 //step 3, only if 128 bit
1422             if( i_nKeyLength == SECUR_128BIT_KEY )
1423             {
1424                 sal_Int32 i;
1425                 for( i = 0; i < 50; i++ )
1426                 {
1427                     nError = rtl_digest_updateMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1428                     if( nError != rtl_Digest_E_None )
1429                     {
1430                         bSuccess = false;
1431                         break;
1432                     }
1433                     rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1434                 }
1435             }
1436             //Step 4, the key is in nMD5Sum
1437             //step 5 already done, data is in i_pPaddedUserPassword
1438             //step 6
1439             rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1440                                      nMD5Sum, i_nKeyLength , NULL, 0 );
1441             // encrypt the user password using the key set above
1442             rtl_cipher_encodeARCFOUR( aCipher, i_pPaddedUserPassword, ENCRYPTED_PWD_SIZE, // the data to be encrypted
1443                                       &io_rOValue[0], sal_Int32(io_rOValue.size()) ); //encrypted data
1444             //Step 7, only if 128 bit
1445             if( i_nKeyLength == SECUR_128BIT_KEY )
1446             {
1447                 sal_uInt32 i, y;
1448                 sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key
1449 
1450                 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1451                 {
1452                     for( y = 0; y < sizeof( nLocalKey ); y++ )
1453                         nLocalKey[y] = (sal_uInt8)( nMD5Sum[y] ^ i );
1454 
1455                     rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1456                                             nLocalKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area, on init can be NULL
1457                     rtl_cipher_encodeARCFOUR( aCipher, &io_rOValue[0], sal_Int32(io_rOValue.size()), // the data to be encrypted
1458                                               &io_rOValue[0], sal_Int32(io_rOValue.size()) ); // encrypted data, can be the same as the input, encrypt "in place"
1459                     //step 8, store in class data member
1460                 }
1461             }
1462         }
1463         else
1464             bSuccess = false;
1465     }
1466     else
1467         bSuccess = false;
1468 
1469     if( aDigest )
1470         rtl_digest_destroyMD5( aDigest );
1471     if( aCipher )
1472         rtl_cipher_destroyARCFOUR( aCipher );
1473 
1474     if( ! bSuccess )
1475         io_rOValue.clear();
1476     return bSuccess;
1477 }
1478 
1479 /**********************************
1480 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)
1481 */
1482 bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter* i_pTransporter,
1483                                              vcl::PDFWriter::PDFEncryptionProperties& io_rProperties,
1484                                              sal_Int32 i_nKeyLength,
1485                                              sal_Int32 i_nAccessPermissions
1486                                              )
1487 {
1488     bool bSuccess = true;
1489 
1490     io_rProperties.UValue.resize( ENCRYPTED_PWD_SIZE );
1491 
1492     rtlDigest aDigest = rtl_digest_createMD5();
1493     rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1494     if( aDigest && aCipher )
1495     {
1496         //step 1, common to both 3.4 and 3.5
1497         if( computeEncryptionKey( i_pTransporter, io_rProperties, i_nAccessPermissions ) )
1498         {
1499             // prepare encryption key for object
1500             for( sal_Int32 i = i_nKeyLength, y = 0; y < 5 ; y++ )
1501                 io_rProperties.EncryptionKey[i++] = 0;
1502 
1503             if( io_rProperties.Security128bit == false )
1504             {
1505                 //3.4
1506                 //step 2 and 3
1507                 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1508                                         &io_rProperties.EncryptionKey[0], 5 , // key and key length
1509                                         NULL, 0 ); //destination data area
1510                 // encrypt the user password using the key set above, save for later use
1511                 rtl_cipher_encodeARCFOUR( aCipher, s_nPadString, sizeof( s_nPadString ), // the data to be encrypted
1512                                           &io_rProperties.UValue[0], sal_Int32(io_rProperties.UValue.size()) ); //encrypted data, stored in class data member
1513             }
1514             else
1515             {
1516                 //or 3.5, for 128 bit security
1517                 //step6, initilize the last 16 bytes of the encrypted user password to 0
1518                 for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sal_uInt32(io_rProperties.UValue.size()); i++)
1519                     io_rProperties.UValue[i] = 0;
1520                 //step 2
1521                 rtlDigestError nError = rtl_digest_updateMD5( aDigest, s_nPadString, sizeof( s_nPadString ) );
1522                 //step 3
1523                 if( nError == rtl_Digest_E_None )
1524                     nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) );
1525                 else
1526                     bSuccess = false;
1527 
1528                 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1529                 rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) );
1530                 //Step 4
1531                 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1532                                         &io_rProperties.EncryptionKey[0], SECUR_128BIT_KEY, NULL, 0 ); //destination data area
1533                 rtl_cipher_encodeARCFOUR( aCipher, nMD5Sum, sizeof( nMD5Sum ), // the data to be encrypted
1534                                           &io_rProperties.UValue[0], sizeof( nMD5Sum ) ); //encrypted data, stored in class data member
1535                 //step 5
1536                 sal_uInt32 i, y;
1537                 sal_uInt8 nLocalKey[SECUR_128BIT_KEY];
1538 
1539                 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1540                 {
1541                     for( y = 0; y < sizeof( nLocalKey ) ; y++ )
1542                         nLocalKey[y] = (sal_uInt8)( io_rProperties.EncryptionKey[y] ^ i );
1543 
1544                     rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1545                                             nLocalKey, SECUR_128BIT_KEY, // key and key length
1546                                             NULL, 0 ); //destination data area, on init can be NULL
1547                     rtl_cipher_encodeARCFOUR( aCipher, &io_rProperties.UValue[0], SECUR_128BIT_KEY, // the data to be encrypted
1548                                               &io_rProperties.UValue[0], SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place"
1549                 }
1550             }
1551         }
1552         else
1553             bSuccess = false;
1554     }
1555     else
1556         bSuccess = false;
1557 
1558     if( aDigest )
1559         rtl_digest_destroyMD5( aDigest );
1560     if( aCipher )
1561         rtl_cipher_destroyARCFOUR( aCipher );
1562 
1563     if( ! bSuccess )
1564         io_rProperties.UValue.clear();
1565     return bSuccess;
1566 }
1567 
1568 /* end i12626 methods */
1569 
1570 static const long unsetRun[256] =
1571 {
1572     8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,	/* 0x00 - 0x0f */
1573     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 0x10 - 0x1f */
1574     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0x20 - 0x2f */
1575     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0x30 - 0x3f */
1576     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x40 - 0x4f */
1577     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x50 - 0x5f */
1578     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x60 - 0x6f */
1579     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x70 - 0x7f */
1580     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x80 - 0x8f */
1581     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x90 - 0x9f */
1582     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xa0 - 0xaf */
1583     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xb0 - 0xbf */
1584     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xc0 - 0xcf */
1585     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xd0 - 0xdf */
1586     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xe0 - 0xef */
1587     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0xf0 - 0xff */
1588 };
1589 
1590 static const long setRun[256] =
1591 {
1592     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x00 - 0x0f */
1593     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x10 - 0x1f */
1594     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x20 - 0x2f */
1595     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x30 - 0x3f */
1596     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x40 - 0x4f */
1597     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x50 - 0x5f */
1598     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x60 - 0x6f */
1599     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 0x70 - 0x7f */
1600     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x80 - 0x8f */
1601     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0x90 - 0x9f */
1602     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0xa0 - 0xaf */
1603     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,	/* 0xb0 - 0xbf */
1604     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0xc0 - 0xcf */
1605     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,	/* 0xd0 - 0xdf */
1606     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 0xe0 - 0xef */
1607     4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8,	/* 0xf0 - 0xff */
1608 };
1609 
1610 inline bool isSet( const Scanline i_pLine, long i_nIndex )
1611 {
1612     return (i_pLine[ i_nIndex/8 ] & (0x80 >> (i_nIndex&7))) != 0;
1613 }
1614 
1615 long findBitRun( const Scanline i_pLine, long i_nStartIndex, long i_nW, bool i_bSet )
1616 {
1617     if( i_nStartIndex < 0 )
1618         return i_nW;
1619 
1620     long nIndex = i_nStartIndex;
1621     if( nIndex < i_nW )
1622     {
1623         const sal_uInt8 * pByte = static_cast<sal_uInt8*>(i_pLine) + (nIndex/8);
1624         sal_uInt8 nByte = *pByte;
1625 
1626         // run up to byte boundary
1627         long nBitInByte = (nIndex & 7);
1628         if( nBitInByte )
1629         {
1630             sal_uInt8 nMask = 0x80 >> nBitInByte;
1631             while( nBitInByte != 8 )
1632             {
1633                 if( (nByte & nMask) != (i_bSet ? nMask : 0) )
1634                     return nIndex < i_nW ? nIndex : i_nW;
1635                 nMask = nMask >> 1;
1636                 nBitInByte++;
1637                 nIndex++;
1638             }
1639             if( nIndex < i_nW )
1640             {
1641                 pByte++;
1642                 nByte = *pByte;
1643             }
1644         }
1645 
1646         sal_uInt8 nRunByte;
1647         const long* pRunTable;
1648         if( i_bSet )
1649         {
1650             nRunByte = 0xff;
1651             pRunTable = setRun;
1652         }
1653         else
1654         {
1655             nRunByte = 0;
1656             pRunTable = unsetRun;
1657         }
1658 
1659         while( nByte == nRunByte && nIndex < i_nW )
1660         {
1661             nIndex += 8;
1662             pByte++;
1663             nByte = *pByte;
1664         }
1665         if( nIndex < i_nW )
1666         {
1667             nIndex += pRunTable[nByte];
1668         }
1669     }
1670     return nIndex < i_nW ? nIndex : i_nW;
1671 }
1672 
1673 struct BitStreamState
1674 {
1675     sal_uInt8       mnBuffer;
1676     sal_uInt32      mnNextBitPos;
1677 
1678     BitStreamState()
1679     : mnBuffer( 0 )
1680     , mnNextBitPos( 8 )
1681     {
1682     }
1683 
1684     const sal_uInt8* getByte() const { return &mnBuffer; }
1685     void flush() { mnNextBitPos = 8; mnBuffer = 0; }
1686 };
1687 
1688 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState )
1689 {
1690     while( i_nLength > io_rState.mnNextBitPos )
1691     {
1692         io_rState.mnBuffer |= static_cast<sal_uInt8>( i_nCode >> (i_nLength - io_rState.mnNextBitPos) );
1693         i_nLength -= io_rState.mnNextBitPos;
1694         writeBuffer( io_rState.getByte(), 1 );
1695         io_rState.flush();
1696     }
1697     OSL_ASSERT( i_nLength < 9 );
1698     static const unsigned int msbmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1699     io_rState.mnBuffer |= static_cast<sal_uInt8>( (i_nCode & msbmask[i_nLength]) << (io_rState.mnNextBitPos - i_nLength) );
1700     io_rState.mnNextBitPos -= i_nLength;
1701     if( io_rState.mnNextBitPos == 0 )
1702     {
1703         writeBuffer( io_rState.getByte(), 1 );
1704         io_rState.flush();
1705     }
1706 }
1707 
1708 struct PixelCode
1709 {
1710     sal_uInt32      mnEncodedPixels;
1711     sal_uInt32      mnCodeBits;
1712     sal_uInt32      mnCode;
1713 };
1714 
1715 static const PixelCode WhitePixelCodes[] =
1716 {
1717     { 0, 8, 0x35 },     // 0011 0101
1718     { 1, 6, 0x7 },      // 0001 11
1719     { 2, 4, 0x7 },      // 0111
1720     { 3, 4, 0x8 },      // 1000
1721     { 4, 4, 0xB },      // 1011
1722     { 5, 4, 0xC },      // 1100
1723     { 6, 4, 0xE },      // 1110
1724     { 7, 4, 0xF },      // 1111
1725     { 8, 5, 0x13 },     // 1001 1
1726     { 9, 5, 0x14 },     // 1010 0
1727     { 10, 5, 0x7 },     // 0011 1
1728     { 11, 5, 0x8 },     // 0100 0
1729     { 12, 6, 0x8 },     // 0010 00
1730     { 13, 6, 0x3 },     // 0000 11
1731     { 14, 6, 0x34 },    // 1101 00
1732     { 15, 6, 0x35 },    // 1101 01
1733     { 16, 6, 0x2A },    // 1010 10
1734     { 17, 6, 0x2B },    // 1010 11
1735     { 18, 7, 0x27 },    // 0100 111
1736     { 19, 7, 0xC },     // 0001 100
1737     { 20, 7, 0x8 },     // 0001 000
1738     { 21, 7, 0x17 },    // 0010 111
1739     { 22, 7, 0x3 },     // 0000 011
1740     { 23, 7, 0x4 },     // 0000 100
1741     { 24, 7, 0x28 },    // 0101 000
1742     { 25, 7, 0x2B },    // 0101 011
1743     { 26, 7, 0x13 },    // 0010 011
1744     { 27, 7, 0x24 },    // 0100 100
1745     { 28, 7, 0x18 },    // 0011 000
1746     { 29, 8, 0x2 },     // 0000 0010
1747     { 30, 8, 0x3 },     // 0000 0011
1748     { 31, 8, 0x1A },    // 0001 1010
1749     { 32, 8, 0x1B },    // 0001 1011
1750     { 33, 8, 0x12 },    // 0001 0010
1751     { 34, 8, 0x13 },    // 0001 0011
1752     { 35, 8, 0x14 },    // 0001 0100
1753     { 36, 8, 0x15 },    // 0001 0101
1754     { 37, 8, 0x16 },    // 0001 0110
1755     { 38, 8, 0x17 },    // 0001 0111
1756     { 39, 8, 0x28 },    // 0010 1000
1757     { 40, 8, 0x29 },    // 0010 1001
1758     { 41, 8, 0x2A },    // 0010 1010
1759     { 42, 8, 0x2B },    // 0010 1011
1760     { 43, 8, 0x2C },    // 0010 1100
1761     { 44, 8, 0x2D },    // 0010 1101
1762     { 45, 8, 0x4 },     // 0000 0100
1763     { 46, 8, 0x5 },     // 0000 0101
1764     { 47, 8, 0xA },     // 0000 1010
1765     { 48, 8, 0xB },     // 0000 1011
1766     { 49, 8, 0x52 },    // 0101 0010
1767     { 50, 8, 0x53 },    // 0101 0011
1768     { 51, 8, 0x54 },    // 0101 0100
1769     { 52, 8, 0x55 },    // 0101 0101
1770     { 53, 8, 0x24 },    // 0010 0100
1771     { 54, 8, 0x25 },    // 0010 0101
1772     { 55, 8, 0x58 },    // 0101 1000
1773     { 56, 8, 0x59 },    // 0101 1001
1774     { 57, 8, 0x5A },    // 0101 1010
1775     { 58, 8, 0x5B },    // 0101 1011
1776     { 59, 8, 0x4A },    // 0100 1010
1777     { 60, 8, 0x4B },    // 0100 1011
1778     { 61, 8, 0x32 },    // 0011 0010
1779     { 62, 8, 0x33 },    // 0011 0011
1780     { 63, 8, 0x34 },    // 0011 0100
1781     { 64, 5, 0x1B },    // 1101 1
1782     { 128, 5, 0x12 },   // 1001 0
1783     { 192, 6, 0x17 },   // 0101 11
1784     { 256, 7, 0x37 },   // 0110 111
1785     { 320, 8, 0x36 },   // 0011 0110
1786     { 384, 8, 0x37 },   // 0011 0111
1787     { 448, 8, 0x64 },   // 0110 0100
1788     { 512, 8, 0x65 },   // 0110 0101
1789     { 576, 8, 0x68 },   // 0110 1000
1790     { 640, 8, 0x67 },   // 0110 0111
1791     { 704, 9, 0xCC },   // 0110 0110 0
1792     { 768, 9, 0xCD },   // 0110 0110 1
1793     { 832, 9, 0xD2 },   // 0110 1001 0
1794     { 896, 9, 0xD3 },   // 0110 1001 1
1795     { 960, 9, 0xD4 },   // 0110 1010 0
1796     { 1024, 9, 0xD5 },  // 0110 1010 1
1797     { 1088, 9, 0xD6 },  // 0110 1011 0
1798     { 1152, 9, 0xD7 },  // 0110 1011 1
1799     { 1216, 9, 0xD8 },  // 0110 1100 0
1800     { 1280, 9, 0xD9 },  // 0110 1100 1
1801     { 1344, 9, 0xDA },  // 0110 1101 0
1802     { 1408, 9, 0xDB },  // 0110 1101 1
1803     { 1472, 9, 0x98 },  // 0100 1100 0
1804     { 1536, 9, 0x99 },  // 0100 1100 1
1805     { 1600, 9, 0x9A },  // 0100 1101 0
1806     { 1664, 6, 0x18 },  // 0110 00
1807     { 1728, 9, 0x9B },  // 0100 1101 1
1808     { 1792, 11, 0x8 },  // 0000 0001 000
1809     { 1856, 11, 0xC },  // 0000 0001 100
1810     { 1920, 11, 0xD },  // 0000 0001 101
1811     { 1984, 12, 0x12 }, // 0000 0001 0010
1812     { 2048, 12, 0x13 }, // 0000 0001 0011
1813     { 2112, 12, 0x14 }, // 0000 0001 0100
1814     { 2176, 12, 0x15 }, // 0000 0001 0101
1815     { 2240, 12, 0x16 }, // 0000 0001 0110
1816     { 2304, 12, 0x17 }, // 0000 0001 0111
1817     { 2368, 12, 0x1C }, // 0000 0001 1100
1818     { 2432, 12, 0x1D }, // 0000 0001 1101
1819     { 2496, 12, 0x1E }, // 0000 0001 1110
1820     { 2560, 12, 0x1F }  // 0000 0001 1111
1821 };
1822 
1823 static const PixelCode BlackPixelCodes[] =
1824 {
1825     { 0, 10, 0x37 },    // 0000 1101 11
1826     { 1, 3, 0x2 },      // 010
1827     { 2, 2, 0x3 },      // 11
1828     { 3, 2, 0x2 },      // 10
1829     { 4, 3, 0x3 },      // 011
1830     { 5, 4, 0x3 },      // 0011
1831     { 6, 4, 0x2 },      // 0010
1832     { 7, 5, 0x3 },      // 0001 1
1833     { 8, 6, 0x5 },      // 0001 01
1834     { 9, 6, 0x4 },      // 0001 00
1835     { 10, 7, 0x4 },     // 0000 100
1836     { 11, 7, 0x5 },     // 0000 101
1837     { 12, 7, 0x7 },     // 0000 111
1838     { 13, 8, 0x4 },     // 0000 0100
1839     { 14, 8, 0x7 },     // 0000 0111
1840     { 15, 9, 0x18 },    // 0000 1100 0
1841     { 16, 10, 0x17 },   // 0000 0101 11
1842     { 17, 10, 0x18 },   // 0000 0110 00
1843     { 18, 10, 0x8 },    // 0000 0010 00
1844     { 19, 11, 0x67 },   // 0000 1100 111
1845     { 20, 11, 0x68 },   // 0000 1101 000
1846     { 21, 11, 0x6C },   // 0000 1101 100
1847     { 22, 11, 0x37 },   // 0000 0110 111
1848     { 23, 11, 0x28 },   // 0000 0101 000
1849     { 24, 11, 0x17 },   // 0000 0010 111
1850     { 25, 11, 0x18 },   // 0000 0011 000
1851     { 26, 12, 0xCA },   // 0000 1100 1010
1852     { 27, 12, 0xCB },   // 0000 1100 1011
1853     { 28, 12, 0xCC },   // 0000 1100 1100
1854     { 29, 12, 0xCD },   // 0000 1100 1101
1855     { 30, 12, 0x68 },   // 0000 0110 1000
1856     { 31, 12, 0x69 },   // 0000 0110 1001
1857     { 32, 12, 0x6A },   // 0000 0110 1010
1858     { 33, 12, 0x6B },   // 0000 0110 1011
1859     { 34, 12, 0xD2 },   // 0000 1101 0010
1860     { 35, 12, 0xD3 },   // 0000 1101 0011
1861     { 36, 12, 0xD4 },   // 0000 1101 0100
1862     { 37, 12, 0xD5 },   // 0000 1101 0101
1863     { 38, 12, 0xD6 },   // 0000 1101 0110
1864     { 39, 12, 0xD7 },   // 0000 1101 0111
1865     { 40, 12, 0x6C },   // 0000 0110 1100
1866     { 41, 12, 0x6D },   // 0000 0110 1101
1867     { 42, 12, 0xDA },   // 0000 1101 1010
1868     { 43, 12, 0xDB },   // 0000 1101 1011
1869     { 44, 12, 0x54 },   // 0000 0101 0100
1870     { 45, 12, 0x55 },   // 0000 0101 0101
1871     { 46, 12, 0x56 },   // 0000 0101 0110
1872     { 47, 12, 0x57 },   // 0000 0101 0111
1873     { 48, 12, 0x64 },   // 0000 0110 0100
1874     { 49, 12, 0x65 },   // 0000 0110 0101
1875     { 50, 12, 0x52 },   // 0000 0101 0010
1876     { 51, 12, 0x53 },   // 0000 0101 0011
1877     { 52, 12, 0x24 },   // 0000 0010 0100
1878     { 53, 12, 0x37 },   // 0000 0011 0111
1879     { 54, 12, 0x38 },   // 0000 0011 1000
1880     { 55, 12, 0x27 },   // 0000 0010 0111
1881     { 56, 12, 0x28 },   // 0000 0010 1000
1882     { 57, 12, 0x58 },   // 0000 0101 1000
1883     { 58, 12, 0x59 },   // 0000 0101 1001
1884     { 59, 12, 0x2B },   // 0000 0010 1011
1885     { 60, 12, 0x2C },   // 0000 0010 1100
1886     { 61, 12, 0x5A },   // 0000 0101 1010
1887     { 62, 12, 0x66 },   // 0000 0110 0110
1888     { 63, 12, 0x67 },   // 0000 0110 0111
1889     { 64, 10, 0xF },    // 0000 0011 11
1890     { 128, 12, 0xC8 },  // 0000 1100 1000
1891     { 192, 12, 0xC9 },  // 0000 1100 1001
1892     { 256, 12, 0x5B },  // 0000 0101 1011
1893     { 320, 12, 0x33 },  // 0000 0011 0011
1894     { 384, 12, 0x34 },  // 0000 0011 0100
1895     { 448, 12, 0x35 },  // 0000 0011 0101
1896     { 512, 13, 0x6C },  // 0000 0011 0110 0
1897     { 576, 13, 0x6D },  // 0000 0011 0110 1
1898     { 640, 13, 0x4A },  // 0000 0010 0101 0
1899     { 704, 13, 0x4B },  // 0000 0010 0101 1
1900     { 768, 13, 0x4C },  // 0000 0010 0110 0
1901     { 832, 13, 0x4D },  // 0000 0010 0110 1
1902     { 896, 13, 0x72 },  // 0000 0011 1001 0
1903     { 960, 13, 0x73 },  // 0000 0011 1001 1
1904     { 1024, 13, 0x74 }, // 0000 0011 1010 0
1905     { 1088, 13, 0x75 }, // 0000 0011 1010 1
1906     { 1152, 13, 0x76 }, // 0000 0011 1011 0
1907     { 1216, 13, 0x77 }, // 0000 0011 1011 1
1908     { 1280, 13, 0x52 }, // 0000 0010 1001 0
1909     { 1344, 13, 0x53 }, // 0000 0010 1001 1
1910     { 1408, 13, 0x54 }, // 0000 0010 1010 0
1911     { 1472, 13, 0x55 }, // 0000 0010 1010 1
1912     { 1536, 13, 0x5A }, // 0000 0010 1101 0
1913     { 1600, 13, 0x5B }, // 0000 0010 1101 1
1914     { 1664, 13, 0x64 }, // 0000 0011 0010 0
1915     { 1728, 13, 0x65 }, // 0000 0011 0010 1
1916     { 1792, 11, 0x8 },  // 0000 0001 000
1917     { 1856, 11, 0xC },  // 0000 0001 100
1918     { 1920, 11, 0xD },  // 0000 0001 101
1919     { 1984, 12, 0x12 }, // 0000 0001 0010
1920     { 2048, 12, 0x13 }, // 0000 0001 0011
1921     { 2112, 12, 0x14 }, // 0000 0001 0100
1922     { 2176, 12, 0x15 }, // 0000 0001 0101
1923     { 2240, 12, 0x16 }, // 0000 0001 0110
1924     { 2304, 12, 0x17 }, // 0000 0001 0111
1925     { 2368, 12, 0x1C }, // 0000 0001 1100
1926     { 2432, 12, 0x1D }, // 0000 0001 1101
1927     { 2496, 12, 0x1E }, // 0000 0001 1110
1928     { 2560, 12, 0x1F }  // 0000 0001 1111
1929 };
1930 
1931 
1932 void PDFWriterImpl::putG4Span( long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState )
1933 {
1934     const PixelCode* pTable = i_bWhitePixel ? WhitePixelCodes : BlackPixelCodes;
1935     // maximum encoded span is 2560 consecutive pixels
1936     while( i_nSpan > 2623 )
1937     {
1938         // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1939         putG4Bits( pTable[103].mnCodeBits, pTable[103].mnCode, io_rState );
1940         i_nSpan -= pTable[103].mnEncodedPixels;
1941     }
1942     // write multiples of 64 pixels up to 2560
1943     if( i_nSpan > 63 )
1944     {
1945         sal_uInt32 nTabIndex = 63 + (i_nSpan >> 6);
1946         OSL_ASSERT( pTable[nTabIndex].mnEncodedPixels == static_cast<sal_uInt32>(64*(i_nSpan >> 6)) );
1947         putG4Bits( pTable[nTabIndex].mnCodeBits, pTable[nTabIndex].mnCode, io_rState );
1948         i_nSpan -= pTable[nTabIndex].mnEncodedPixels;
1949     }
1950     putG4Bits( pTable[i_nSpan].mnCodeBits, pTable[i_nSpan].mnCode, io_rState );
1951 }
1952 
1953 void PDFWriterImpl::writeG4Stream( BitmapReadAccess* i_pBitmap )
1954 {
1955     long nW = i_pBitmap->Width();
1956     long nH = i_pBitmap->Height();
1957     if( nW <= 0 || nH <= 0 )
1958         return;
1959     if( i_pBitmap->GetBitCount() != 1 )
1960         return;
1961 
1962     BitStreamState aBitState;
1963 
1964     // the first reference line is virtual and completely empty
1965     const Scanline pFirstRefLine = (Scanline)rtl_allocateZeroMemory( nW/8 + 1 );
1966     Scanline pRefLine = pFirstRefLine;
1967     for( long nY = 0; nY < nH; nY++ )
1968     {
1969         const Scanline pCurLine = i_pBitmap->GetScanline( nY );
1970         long nLineIndex = 0;
1971         bool bRunSet = (*pCurLine & 0x80) ? true : false;
1972         bool bRefSet = (*pRefLine & 0x80) ? true : false;
1973         long nRunIndex1 = bRunSet ? 0 : findBitRun( pCurLine, 0, nW, bRunSet );
1974         long nRefIndex1 = bRefSet ? 0 : findBitRun( pRefLine, 0, nW, bRefSet );
1975         for( ; nLineIndex < nW; )
1976         {
1977             long nRefIndex2 = findBitRun( pRefLine, nRefIndex1, nW, isSet( pRefLine, nRefIndex1 ) );
1978             if( nRefIndex2 >= nRunIndex1 )
1979             {
1980                 long nDiff = nRefIndex1 - nRunIndex1;
1981                 if( -3 <= nDiff && nDiff <= 3 )
1982                 {   // vertical coding
1983                     static const struct
1984                     {
1985                         sal_uInt32 mnCodeBits;
1986                         sal_uInt32 mnCode;
1987                     } VerticalCodes[7] = {
1988                         { 7, 0x03 },    // 0000 011
1989                         { 6, 0x03 },    // 0000 11
1990                         { 3, 0x03 },    // 011
1991                         { 1, 0x1 },     // 1
1992                         { 3, 0x2 },     // 010
1993                         { 6, 0x02 },    // 0000 10
1994                         { 7, 0x02 }     // 0000 010
1995                     };
1996                     // convert to index
1997                     nDiff += 3;
1998 
1999                     // emit diff code
2000                     putG4Bits( VerticalCodes[nDiff].mnCodeBits, VerticalCodes[nDiff].mnCode, aBitState );
2001                     nLineIndex = nRunIndex1;
2002                 }
2003                 else
2004                 {   // difference too large, horizontal coding
2005                     // emit horz code 001
2006                     putG4Bits( 3, 0x1, aBitState );
2007                     long nRunIndex2 = findBitRun( pCurLine, nRunIndex1, nW, isSet( pCurLine, nRunIndex1 ) );
2008                     bool bWhiteFirst = ( nLineIndex + nRunIndex1 == 0 || ! isSet( pCurLine, nLineIndex ) );
2009                     putG4Span( nRunIndex1 - nLineIndex, bWhiteFirst, aBitState );
2010                     putG4Span( nRunIndex2 - nRunIndex1, ! bWhiteFirst, aBitState );
2011                     nLineIndex = nRunIndex2;
2012                 }
2013             }
2014             else
2015             {   // emit pass code 0001
2016                 putG4Bits( 4, 0x1, aBitState );
2017                 nLineIndex = nRefIndex2;
2018             }
2019             if( nLineIndex < nW )
2020             {
2021                 bool bSet = isSet( pCurLine, nLineIndex );
2022                 nRunIndex1 = findBitRun( pCurLine, nLineIndex, nW, bSet );
2023                 nRefIndex1 = findBitRun( pRefLine, nLineIndex, nW, ! bSet );
2024                 nRefIndex1 = findBitRun( pRefLine, nRefIndex1, nW, bSet );
2025             }
2026         }
2027 
2028         // the current line is the reference for the next line
2029         pRefLine = pCurLine;
2030     }
2031     // terminate strip with EOFB
2032     putG4Bits( 12, 1, aBitState );
2033     putG4Bits( 12, 1, aBitState );
2034     if( aBitState.mnNextBitPos != 8 )
2035     {
2036         writeBuffer( aBitState.getByte(), 1 );
2037         aBitState.flush();
2038     }
2039 
2040     rtl_freeMemory( pFirstRefLine );
2041 }
2042