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