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