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