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