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