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