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