xref: /core/vcl/source/gdi/pdfwriter_impl2.cxx (revision c16eaa0629769362cc3edbf0ee951b430c0ae7dd)
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 #include <pdf/pdfwriter_impl.hxx>
20 #include <pdf/EncryptionHashTransporter.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 #include <pdf/IPDFEncryptor.hxx>
29 
30 #include <unotools/streamwrap.hxx>
31 
32 #include <tools/helpers.hxx>
33 #include <tools/fract.hxx>
34 #include <tools/stream.hxx>
35 
36 #include <comphelper/fileformat.h>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/propertyvalue.hxx>
39 
40 #include <com/sun/star/beans/PropertyValue.hpp>
41 #include <com/sun/star/io/XSeekable.hpp>
42 #include <com/sun/star/graphic/GraphicProvider.hpp>
43 #include <com/sun/star/graphic/XGraphicProvider.hpp>
44 #include <com/sun/star/beans/XMaterialHolder.hpp>
45 
46 #include <o3tl/unit_conversion.hxx>
47 #include <osl/diagnose.h>
48 #include <vcl/skia/SkiaHelper.hxx>
49 
50 #include <sal/log.hxx>
51 #include <memory>
52 
53 using namespace vcl;
54 using namespace com::sun::star;
55 using namespace com::sun::star::uno;
56 using namespace com::sun::star::beans;
57 
58 static bool lcl_canUsePDFAxialShading(const Gradient& rGradient);
59 
implWriteGradient(const tools::PolyPolygon & i_rPolyPoly,const Gradient & i_rGradient,VirtualDevice * i_pDummyVDev,const vcl::PDFWriter::PlayMetafileContext & i_rContext)60 void PDFWriterImpl::implWriteGradient( const tools::PolyPolygon& i_rPolyPoly, const Gradient& i_rGradient,
61                                        VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
62 {
63     GDIMetaFile        aTmpMtf;
64     Gradient aGradient(i_rGradient);
65 
66     aGradient.AddGradientActions( i_rPolyPoly.GetBoundRect(), aTmpMtf );
67 
68     m_rOuterFace.Push();
69     m_rOuterFace.IntersectClipRegion( i_rPolyPoly.getB2DPolyPolygon() );
70     playMetafile( aTmpMtf, nullptr, i_rContext, i_pDummyVDev );
71     m_rOuterFace.Pop();
72 }
73 
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)74 void PDFWriterImpl::implWriteBitmapEx( const Point& i_rPoint, const Size& i_rSize, const BitmapEx& i_rBitmapEx, const Graphic& i_Graphic,
75                                        VirtualDevice const * i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
76 {
77     if ( i_rBitmapEx.IsEmpty() || !i_rSize.Width() || !i_rSize.Height() )
78         return;
79 
80     BitmapEx        aBitmapEx( i_rBitmapEx );
81     Point           aPoint( i_rPoint );
82     Size            aSize( i_rSize );
83 
84     // #i19065# Negative sizes have mirror semantics on
85     // OutputDevice. BitmapEx and co. have no idea about that, so
86     // perform that _before_ doing anything with aBitmapEx.
87     BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE);
88     if( aSize.Width() < 0 )
89     {
90         aSize.setWidth( aSize.Width() * -1 );
91         aPoint.AdjustX( -(aSize.Width()) );
92         nMirrorFlags |= BmpMirrorFlags::Horizontal;
93     }
94     if( aSize.Height() < 0 )
95     {
96         aSize.setHeight( aSize.Height() * -1 );
97         aPoint.AdjustY( -(aSize.Height()) );
98         nMirrorFlags |= BmpMirrorFlags::Vertical;
99     }
100 
101     if( nMirrorFlags != BmpMirrorFlags::NONE )
102     {
103         aBitmapEx.Mirror( nMirrorFlags );
104     }
105 
106     bool bIsJpeg = false, bIsPng = false;
107     if( i_Graphic.GetType() != GraphicType::NONE && i_Graphic.GetBitmapEx() == aBitmapEx )
108     {
109         GfxLinkType eType = i_Graphic.GetGfxLink().GetType();
110         bIsJpeg = (eType == GfxLinkType::NativeJpg);
111         bIsPng = (eType == GfxLinkType::NativePng);
112     }
113 
114     // Do not downsample images smaller than 50x50px.
115     const Size aBmpSize(aBitmapEx.GetSizePixel());
116     if (i_rContext.m_nMaxImageResolution > 50 && aBmpSize.getWidth() > 50
117         && aBmpSize.getHeight() > 50)
118     {
119         // do downsampling if necessary
120         const Size      aDstSizeTwip( i_pDummyVDev->PixelToLogic(i_pDummyVDev->LogicToPixel(aSize), MapMode(MapUnit::MapTwip)) );
121         const double    fBmpPixelX = aBmpSize.Width();
122         const double    fBmpPixelY = aBmpSize.Height();
123         const double fMaxPixelX
124             = o3tl::convert<double>(aDstSizeTwip.Width(), o3tl::Length::twip, o3tl::Length::in)
125               * i_rContext.m_nMaxImageResolution;
126         const double fMaxPixelY
127             = o3tl::convert<double>(aDstSizeTwip.Height(), o3tl::Length::twip, o3tl::Length::in)
128               * i_rContext.m_nMaxImageResolution;
129 
130         // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
131         if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) ||
132             ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) &&
133             ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) )
134         {
135             // do scaling
136             Size            aNewBmpSize;
137             const double    fBmpWH = fBmpPixelX / fBmpPixelY;
138             const double    fMaxWH = fMaxPixelX / fMaxPixelY;
139 
140             if( fBmpWH < fMaxWH )
141             {
142                 aNewBmpSize.setWidth(basegfx::fround<tools::Long>(fMaxPixelY * fBmpWH));
143                 aNewBmpSize.setHeight(basegfx::fround<tools::Long>(fMaxPixelY));
144             }
145             else if( fBmpWH > 0.0 )
146             {
147                 aNewBmpSize.setWidth(basegfx::fround<tools::Long>(fMaxPixelX));
148                 aNewBmpSize.setHeight(basegfx::fround<tools::Long>(fMaxPixelX / fBmpWH));
149             }
150 
151             if( aNewBmpSize.Width() && aNewBmpSize.Height() )
152             {
153                 // #i121233# Use best quality for PDF exports
154                 aBitmapEx.Scale( aNewBmpSize, BmpScaleFlag::BestQuality );
155             }
156             else
157             {
158                 aBitmapEx.SetEmpty();
159             }
160         }
161     }
162 
163     const Size aSizePixel( aBitmapEx.GetSizePixel() );
164     if ( !(aSizePixel.Width() && aSizePixel.Height()) )
165         return;
166 
167     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
168         aBitmapEx.Convert(BmpConversion::N8BitGreys);
169     bool bUseJPGCompression = !i_rContext.m_bOnlyLosslessCompression;
170     if ( bIsPng || ( aSizePixel.Width() < 32 ) || ( aSizePixel.Height() < 32 ) )
171         bUseJPGCompression = false;
172 
173     auto   pStrm=std::make_shared<SvMemoryStream>();
174     AlphaMask aAlphaMask;
175 
176     bool bTrueColorJPG = true;
177     if ( bUseJPGCompression )
178     {
179         // TODO this checks could be done much earlier, saving us
180         // from trying conversion & stores before...
181         if ( !aBitmapEx.IsAlpha() )
182         {
183             const auto aCacheEntry=m_aPDFBmpCache.find(
184                 aBitmapEx.GetChecksum());
185             if ( aCacheEntry != m_aPDFBmpCache.end() )
186             {
187                 m_rOuterFace.DrawJPGBitmap( *aCacheEntry->second, true, aSizePixel,
188                                             tools::Rectangle( aPoint, aSize ), aAlphaMask, i_Graphic );
189                 return;
190             }
191         }
192         sal_uInt32 nZippedFileSize = 0; // sj: we will calculate the filesize of a zipped bitmap
193         if ( !bIsJpeg )                 // to determine if jpeg compression is useful
194         {
195             SvMemoryStream aTemp;
196             aTemp.SetCompressMode( aTemp.GetCompressMode() | SvStreamCompressFlags::ZBITMAP );
197             aTemp.SetVersion( SOFFICE_FILEFORMAT_40 );  // sj: up from version 40 our bitmap stream operator
198             WriteDIBBitmapEx(aBitmapEx, aTemp); // is capable of zlib stream compression
199             nZippedFileSize = aTemp.TellEnd();
200         }
201         if ( aBitmapEx.IsAlpha() )
202             aAlphaMask = aBitmapEx.GetAlphaMask();
203         Graphic aGraphic(BitmapEx(aBitmapEx.GetBitmap()));
204 
205         Sequence< PropertyValue > aFilterData{
206             comphelper::makePropertyValue(u"Quality"_ustr, sal_Int32(i_rContext.m_nJPEGQuality)),
207             comphelper::makePropertyValue(u"ColorMode"_ustr, sal_Int32(0))
208         };
209 
210         try
211         {
212             uno::Reference < io::XStream > xStream = new utl::OStreamWrapper( *pStrm );
213             uno::Reference< io::XSeekable > xSeekable( xStream, UNO_QUERY_THROW );
214             const uno::Reference< uno::XComponentContext >& xContext( comphelper::getProcessComponentContext() );
215             uno::Reference< graphic::XGraphicProvider > xGraphicProvider( graphic::GraphicProvider::create(xContext) );
216             uno::Reference< graphic::XGraphic > xGraphic( aGraphic.GetXGraphic() );
217             uno::Reference < io::XOutputStream > xOut( xStream->getOutputStream() );
218             uno::Sequence< beans::PropertyValue > aOutMediaProperties{
219                 comphelper::makePropertyValue(u"OutputStream"_ustr, xOut),
220                 comphelper::makePropertyValue(u"MimeType"_ustr, u"image/jpeg"_ustr),
221                 comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData)
222             };
223             xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties );
224             xOut->flush();
225             if ( !bIsJpeg && xSeekable->getLength() > nZippedFileSize )
226             {
227                 bUseJPGCompression = false;
228             }
229             else
230             {
231                 pStrm->Seek( STREAM_SEEK_TO_END );
232 
233                 xSeekable->seek( 0 );
234                 Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(u"InputStream"_ustr,
235                                                                                xStream) };
236                 uno::Reference< XPropertySet > xPropSet( xGraphicProvider->queryGraphicDescriptor( aArgs ) );
237                 if ( xPropSet.is() )
238                 {
239                     sal_Int16 nBitsPerPixel = 24;
240                     if ( xPropSet->getPropertyValue(u"BitsPerPixel"_ustr) >>= nBitsPerPixel )
241                     {
242                         bTrueColorJPG = nBitsPerPixel != 8;
243                     }
244                 }
245             }
246         }
247         catch( uno::Exception& )
248         {
249             bUseJPGCompression = false;
250         }
251     }
252     if ( bUseJPGCompression )
253     {
254         m_rOuterFace.DrawJPGBitmap( *pStrm, bTrueColorJPG, aSizePixel, tools::Rectangle( aPoint, aSize ), aAlphaMask, i_Graphic );
255         if (!aBitmapEx.IsAlpha() && bTrueColorJPG)
256         {
257             // Cache last jpeg export
258             m_aPDFBmpCache.insert(
259                 {aBitmapEx.GetChecksum(), pStrm});
260         }
261     }
262     else if ( aBitmapEx.IsAlpha() )
263         m_rOuterFace.DrawBitmapEx( aPoint, aSize, Bitmap(aBitmapEx) );
264     else
265         m_rOuterFace.DrawBitmap( aPoint, aSize, aBitmapEx.GetBitmap(), i_Graphic );
266 
267 }
268 
playMetafile(const GDIMetaFile & i_rMtf,vcl::PDFExtOutDevData * i_pOutDevData,const vcl::PDFWriter::PlayMetafileContext & i_rContext,VirtualDevice * pDummyVDev)269 void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevData* i_pOutDevData, const vcl::PDFWriter::PlayMetafileContext& i_rContext, VirtualDevice* pDummyVDev )
270 {
271     bool bAssertionFired( false );
272 
273     ScopedVclPtr<VirtualDevice> xPrivateDevice;
274     if( ! pDummyVDev )
275     {
276         xPrivateDevice.disposeAndReset(VclPtr<VirtualDevice>::Create());
277         pDummyVDev = xPrivateDevice.get();
278         pDummyVDev->EnableOutput( false );
279         pDummyVDev->SetMapMode( i_rMtf.GetPrefMapMode() );
280     }
281     const GDIMetaFile& aMtf( i_rMtf );
282 
283     for( sal_uInt32 i = 0, nCount = aMtf.GetActionSize(); i < nCount; )
284     {
285         if ( !i_pOutDevData || !i_pOutDevData->PlaySyncPageAct( m_rOuterFace, i, aMtf ) )
286         {
287             const MetaAction*    pAction = aMtf.GetAction( i );
288             const MetaActionType nType = pAction->GetType();
289 
290             switch( nType )
291             {
292                 case MetaActionType::PIXEL:
293                 {
294                     const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
295                     m_rOuterFace.DrawPixel( pA->GetPoint(), pA->GetColor() );
296                 }
297                 break;
298 
299                 case MetaActionType::POINT:
300                 {
301                     const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
302                     m_rOuterFace.DrawPixel( pA->GetPoint() );
303                 }
304                 break;
305 
306                 case MetaActionType::LINE:
307                 {
308                     const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
309                     if ( pA->GetLineInfo().IsDefault() )
310                         m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint() );
311                     else
312                         m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint(), pA->GetLineInfo() );
313                 }
314                 break;
315 
316                 case MetaActionType::RECT:
317                 {
318                     const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
319                     m_rOuterFace.DrawRect( pA->GetRect() );
320                 }
321                 break;
322 
323                 case MetaActionType::ROUNDRECT:
324                 {
325                     const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
326                     m_rOuterFace.DrawRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
327                 }
328                 break;
329 
330                 case MetaActionType::ELLIPSE:
331                 {
332                     const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
333                     m_rOuterFace.DrawEllipse( pA->GetRect() );
334                 }
335                 break;
336 
337                 case MetaActionType::ARC:
338                 {
339                     const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
340                     m_rOuterFace.DrawArc( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
341                 }
342                 break;
343 
344                 case MetaActionType::PIE:
345                 {
346                     const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
347                     m_rOuterFace.DrawPie( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
348                 }
349                 break;
350 
351                 case MetaActionType::CHORD:
352                 {
353                     const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
354                     m_rOuterFace.DrawChord( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
355                 }
356                 break;
357 
358                 case MetaActionType::POLYGON:
359                 {
360                     const MetaPolygonAction* pA = static_cast<const MetaPolygonAction*>(pAction);
361                     m_rOuterFace.DrawPolygon( pA->GetPolygon() );
362                 }
363                 break;
364 
365                 case MetaActionType::POLYLINE:
366                 {
367                     const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
368                     if ( pA->GetLineInfo().IsDefault() )
369                         m_rOuterFace.DrawPolyLine( pA->GetPolygon() );
370                     else
371                         m_rOuterFace.DrawPolyLine( pA->GetPolygon(), pA->GetLineInfo() );
372                 }
373                 break;
374 
375                 case MetaActionType::POLYPOLYGON:
376                 {
377                     const MetaPolyPolygonAction* pA = static_cast<const MetaPolyPolygonAction*>(pAction);
378                     m_rOuterFace.DrawPolyPolygon( pA->GetPolyPolygon() );
379                 }
380                 break;
381 
382                 case MetaActionType::GRADIENT:
383                 {
384                     const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
385                     const Gradient& rGradient = pA->GetGradient();
386                     if (lcl_canUsePDFAxialShading(rGradient))
387                     {
388                         m_rOuterFace.DrawGradient( pA->GetRect(), rGradient );
389                     }
390                     else
391                     {
392                         const tools::PolyPolygon aPolyPoly( pA->GetRect() );
393                         implWriteGradient( aPolyPoly, rGradient, pDummyVDev, i_rContext );
394                     }
395                 }
396                 break;
397 
398                 case MetaActionType::GRADIENTEX:
399                 {
400                     const MetaGradientExAction* pA = static_cast<const MetaGradientExAction*>(pAction);
401                     const Gradient& rGradient = pA->GetGradient();
402 
403                     if (lcl_canUsePDFAxialShading(rGradient))
404                         m_rOuterFace.DrawGradient( pA->GetPolyPolygon(), rGradient );
405                     else
406                         implWriteGradient( pA->GetPolyPolygon(), rGradient, pDummyVDev, i_rContext );
407                 }
408                 break;
409 
410                 case MetaActionType::HATCH:
411                 {
412                     const MetaHatchAction*  pA = static_cast<const MetaHatchAction*>(pAction);
413                     m_rOuterFace.DrawHatch( pA->GetPolyPolygon(), pA->GetHatch() );
414                 }
415                 break;
416 
417                 case MetaActionType::Transparent:
418                 {
419                     const MetaTransparentAction* pA = static_cast<const MetaTransparentAction*>(pAction);
420                     m_rOuterFace.DrawTransparent( pA->GetPolyPolygon(), pA->GetTransparence() );
421                 }
422                 break;
423 
424                 case MetaActionType::FLOATTRANSPARENT:
425                 {
426                     const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
427 
428                     GDIMetaFile     aTmpMtf( pA->GetGDIMetaFile() );
429                     const Point&    rPos = pA->GetPoint();
430                     const Size&     rSize= pA->GetSize();
431                     const Gradient& rTransparenceGradient = pA->GetGradient();
432 
433                     // special case constant alpha value
434                     if( rTransparenceGradient.GetStartColor() == rTransparenceGradient.GetEndColor() )
435                     {
436                         const Color aTransCol( rTransparenceGradient.GetStartColor() );
437                         const sal_uInt16 nTransPercent = aTransCol.GetLuminance() * 100 / 255;
438                         m_rOuterFace.BeginTransparencyGroup();
439 
440                         // tdf#138826 adjust the aTmpMtf to start at rPos (see also #i112076#)
441                         Point aMtfOrigin(aTmpMtf.GetPrefMapMode().GetOrigin());
442                         if (rPos != aMtfOrigin)
443                             aTmpMtf.Move(rPos.X() - aMtfOrigin.X(), rPos.Y() - aMtfOrigin.Y());
444 
445                         playMetafile( aTmpMtf, nullptr, i_rContext, pDummyVDev );
446                         m_rOuterFace.EndTransparencyGroup( tools::Rectangle( rPos, rSize ), nTransPercent );
447                     }
448                     else
449                     {
450                         const Size aDstSizeTwip( pDummyVDev->PixelToLogic(pDummyVDev->LogicToPixel(rSize), MapMode(MapUnit::MapTwip)) );
451 
452                         // i#115962# Always use at least 300 DPI for bitmap conversion of transparence gradients,
453                         // else the quality is not acceptable (see bugdoc as example)
454                         sal_Int32 nMaxBmpDPI(300);
455 
456                         if( i_rContext.m_nMaxImageResolution > 50 )
457                         {
458                             if ( nMaxBmpDPI > i_rContext.m_nMaxImageResolution )
459                                 nMaxBmpDPI = i_rContext.m_nMaxImageResolution;
460                         }
461                         const sal_Int32 nPixelX = o3tl::convert<double>(aDstSizeTwip.Width(), o3tl::Length::twip, o3tl::Length::in) * nMaxBmpDPI;
462                         const sal_Int32 nPixelY = o3tl::convert<double>(aDstSizeTwip.Height(), o3tl::Length::twip, o3tl::Length::in) * nMaxBmpDPI;
463                         if ( nPixelX && nPixelY )
464                         {
465                             Size aDstSizePixel( nPixelX, nPixelY );
466                             ScopedVclPtrInstance<VirtualDevice> xVDev(DeviceFormat::WITH_ALPHA);
467                             if( xVDev->SetOutputSizePixel( aDstSizePixel, true, true ) )
468                             {
469                                 Point           aPoint;
470 
471                                 MapMode aMapMode( pDummyVDev->GetMapMode() );
472                                 aMapMode.SetOrigin( aPoint );
473                                 xVDev->SetMapMode( aMapMode );
474                                 const bool bVDevOldMap = xVDev->IsMapModeEnabled();
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                                 xVDev->EnableMapMode( false );
491                                 BitmapEx aPaint(xVDev->GetBitmap(aPoint, xVDev->GetOutputSizePixel()));
492                                 xVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here!
493 
494                                 // create alpha mask from gradient
495                                 xVDev->SetDrawMode( DrawModeFlags::GrayGradient );
496                                 xVDev->DrawGradient( tools::Rectangle( aPoint, aDstSize ), rTransparenceGradient );
497                                 xVDev->SetDrawMode( DrawModeFlags::Default );
498                                 xVDev->EnableMapMode( false );
499 
500                                 AlphaMask aAlpha(xVDev->GetBitmap(Point(), xVDev->GetOutputSizePixel()));
501 #if HAVE_FEATURE_SKIA
502 #if OSL_DEBUG_LEVEL > 0
503                                 // In release builds, we always invert
504                                 // regardless of whether Skia is enabled or not.
505                                 // But in debug builds, we can't invert when
506                                 // Skia is enabled.
507                                 if ( !SkiaHelper::isVCLSkiaEnabled() )
508 #endif
509 #endif
510                                 {
511                                     // When Skia is disabled, the alpha mask
512                                     // must be inverted a second time. To test
513                                     // this code, export the following
514                                     // document to PDF:
515                                     //   https://bugs.documentfoundation.org/attachment.cgi?id=188084
516                                     aAlpha.Invert(); // convert to alpha
517                                 }
518                                 aAlpha.BlendWith(aPaint.GetAlphaMask());
519 
520                                 xVDev.disposeAndClear();
521 
522                                 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
523                                 implWriteBitmapEx( rPos, rSize, BitmapEx( aPaint.GetBitmap(), aAlpha ), aGraphic, pDummyVDev, i_rContext );
524                             }
525                         }
526                     }
527                 }
528                 break;
529 
530                 case MetaActionType::EPS:
531                 {
532                     const MetaEPSAction*    pA = static_cast<const MetaEPSAction*>(pAction);
533                     const GDIMetaFile&      aSubstitute( pA->GetSubstitute() );
534 
535                     m_rOuterFace.Push();
536                     pDummyVDev->Push();
537 
538                     MapMode aMapMode( aSubstitute.GetPrefMapMode() );
539                     Size aOutSize( OutputDevice::LogicToLogic( pA->GetSize(), pDummyVDev->GetMapMode(), aMapMode ) );
540                     aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) );
541                     aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) );
542                     aMapMode.SetOrigin( OutputDevice::LogicToLogic( pA->GetPoint(), pDummyVDev->GetMapMode(), aMapMode ) );
543 
544                     m_rOuterFace.SetMapMode( aMapMode );
545                     pDummyVDev->SetMapMode( aMapMode );
546                     playMetafile( aSubstitute, nullptr, i_rContext, pDummyVDev );
547                     pDummyVDev->Pop();
548                     m_rOuterFace.Pop();
549                 }
550                 break;
551 
552                 case MetaActionType::COMMENT:
553                 if( ! i_rContext.m_bTransparenciesWereRemoved )
554                 {
555                     const MetaCommentAction*    pA = static_cast<const MetaCommentAction*>(pAction);
556 
557                     if( pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
558                     {
559                         const MetaGradientExAction* pGradAction = nullptr;
560                         bool                        bDone = false;
561 
562                         while( !bDone && ( ++i < nCount ) )
563                         {
564                             pAction = aMtf.GetAction( i );
565 
566                             if( pAction->GetType() == MetaActionType::GRADIENTEX )
567                                 pGradAction = static_cast<const MetaGradientExAction*>(pAction);
568                             else if( ( pAction->GetType() == MetaActionType::COMMENT ) &&
569                                      ( static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END")) )
570                             {
571                                 bDone = true;
572                             }
573                         }
574 
575                         if( pGradAction )
576                         {
577                             if (lcl_canUsePDFAxialShading(pGradAction->GetGradient()))
578                             {
579                                 m_rOuterFace.DrawGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient() );
580                             }
581                             else
582                             {
583                                 implWriteGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), pDummyVDev, i_rContext );
584                             }
585                         }
586                     }
587                     else
588                     {
589                         const sal_uInt8* pData = pA->GetData();
590                         if ( pData )
591                         {
592                             SvMemoryStream  aMemStm( const_cast<sal_uInt8 *>(pData), pA->GetDataSize(), StreamMode::READ );
593                             bool            bSkipSequence = false;
594                             OString sSeqEnd;
595 
596                             if( pA->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
597                             {
598                                 sSeqEnd = "XPATHSTROKE_SEQ_END"_ostr;
599                                 SvtGraphicStroke aStroke;
600                                 ReadSvtGraphicStroke( aMemStm, aStroke );
601 
602                                 tools::Polygon aPath;
603                                 aStroke.getPath( aPath );
604 
605                                 tools::PolyPolygon aStartArrow;
606                                 tools::PolyPolygon aEndArrow;
607                                 double fTransparency( aStroke.getTransparency() );
608                                 double fStrokeWidth( aStroke.getStrokeWidth() );
609                                 SvtGraphicStroke::DashArray aDashArray;
610 
611                                 aStroke.getStartArrow( aStartArrow );
612                                 aStroke.getEndArrow( aEndArrow );
613                                 aStroke.getDashArray( aDashArray );
614 
615                                 bSkipSequence = true;
616                                 if ( aStartArrow.Count() || aEndArrow.Count() )
617                                     bSkipSequence = false;
618                                 if ( !aDashArray.empty() && ( fStrokeWidth != 0.0 ) && ( fTransparency == 0.0 ) )
619                                     bSkipSequence = false;
620                                 if ( bSkipSequence )
621                                 {
622                                     PDFWriter::ExtLineInfo aInfo;
623                                     aInfo.m_fLineWidth      = fStrokeWidth;
624                                     aInfo.m_fTransparency   = fTransparency;
625                                     aInfo.m_fMiterLimit     = aStroke.getMiterLimit();
626                                     switch( aStroke.getCapType() )
627                                     {
628                                         default:
629                                         case SvtGraphicStroke::capButt:   aInfo.m_eCap = PDFWriter::capButt;break;
630                                         case SvtGraphicStroke::capRound:  aInfo.m_eCap = PDFWriter::capRound;break;
631                                         case SvtGraphicStroke::capSquare: aInfo.m_eCap = PDFWriter::capSquare;break;
632                                     }
633                                     switch( aStroke.getJoinType() )
634                                     {
635                                         default:
636                                         case SvtGraphicStroke::joinMiter: aInfo.m_eJoin = PDFWriter::joinMiter;break;
637                                         case SvtGraphicStroke::joinRound: aInfo.m_eJoin = PDFWriter::joinRound;break;
638                                         case SvtGraphicStroke::joinBevel: aInfo.m_eJoin = PDFWriter::joinBevel;break;
639                                         case SvtGraphicStroke::joinNone:
640                                             aInfo.m_eJoin = PDFWriter::joinMiter;
641                                             aInfo.m_fMiterLimit = 0.0;
642                                             break;
643                                     }
644                                     aInfo.m_aDashArray = std::move(aDashArray);
645 
646                                     if(SvtGraphicStroke::joinNone == aStroke.getJoinType()
647                                         && fStrokeWidth > 0.0)
648                                     {
649                                         // emulate no edge rounding by handling single edges
650                                         const sal_uInt16 nPoints(aPath.GetSize());
651                                         const bool bCurve(aPath.HasFlags());
652 
653                                         for(sal_uInt16 a(0); a + 1 < nPoints; a++)
654                                         {
655                                             if(bCurve
656                                                 && PolyFlags::Normal != aPath.GetFlags(a + 1)
657                                                 && a + 2 < nPoints
658                                                 && PolyFlags::Normal != aPath.GetFlags(a + 2)
659                                                 && a + 3 < nPoints)
660                                             {
661                                                 const tools::Polygon aSnippet(4,
662                                                     aPath.GetConstPointAry() + a,
663                                                     aPath.GetConstFlagAry() + a);
664                                                 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
665                                                 a += 2;
666                                             }
667                                             else
668                                             {
669                                                 const tools::Polygon aSnippet(2,
670                                                     aPath.GetConstPointAry() + a);
671                                                 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
672                                             }
673                                         }
674                                     }
675                                     else
676                                     {
677                                         m_rOuterFace.DrawPolyLine( aPath, aInfo );
678                                     }
679                                 }
680                             }
681                             else if ( pA->GetComment() == "XPATHFILL_SEQ_BEGIN" )
682                             {
683                                 sSeqEnd = "XPATHFILL_SEQ_END"_ostr;
684                                 SvtGraphicFill aFill;
685                                 ReadSvtGraphicFill( aMemStm, aFill );
686 
687                                 if ( ( aFill.getFillType() == SvtGraphicFill::fillSolid ) && ( aFill.getFillRule() == SvtGraphicFill::fillEvenOdd ) )
688                                 {
689                                     double fTransparency = aFill.getTransparency();
690                                     if ( fTransparency == 0.0 )
691                                     {
692                                         tools::PolyPolygon aPath;
693                                         aFill.getPath( aPath );
694 
695                                         bSkipSequence = true;
696                                         m_rOuterFace.DrawPolyPolygon( aPath );
697                                     }
698                                     else if ( fTransparency == 1.0 )
699                                         bSkipSequence = true;
700                                 }
701                             }
702                             if ( bSkipSequence )
703                             {
704                                 while( ++i < nCount )
705                                 {
706                                     pAction = aMtf.GetAction( i );
707                                     if ( pAction->GetType() == MetaActionType::COMMENT )
708                                     {
709                                         OString sComment( static_cast<const MetaCommentAction*>(pAction)->GetComment() );
710                                         if (sComment == sSeqEnd)
711                                             break;
712                                     }
713                                     // #i44496#
714                                     // the replacement action for stroke is a filled rectangle
715                                     // the set fillcolor of the replacement is part of the graphics
716                                     // state and must not be skipped
717                                     else if( pAction->GetType() == MetaActionType::FILLCOLOR )
718                                     {
719                                         const MetaFillColorAction* pMA = static_cast<const MetaFillColorAction*>(pAction);
720                                         if( pMA->IsSetting() )
721                                             m_rOuterFace.SetFillColor( pMA->GetColor() );
722                                         else
723                                             m_rOuterFace.SetFillColor();
724                                     }
725                                 }
726                             }
727                         }
728                     }
729                 }
730                 break;
731 
732                 case MetaActionType::BMP:
733                 {
734                     const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
735                     BitmapEx aBitmapEx( pA->GetBitmap() );
736                     Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
737                                                             aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
738                     if( ! ( aSize.Width() && aSize.Height() ) )
739                         aSize = pDummyVDev->PixelToLogic( aBitmapEx.GetSizePixel() );
740 
741                     Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
742                     implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, aGraphic, pDummyVDev, i_rContext );
743                 }
744                 break;
745 
746                 case MetaActionType::BMPSCALE:
747                 {
748                     const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
749                     Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
750                     implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), BitmapEx( pA->GetBitmap() ), aGraphic, pDummyVDev, i_rContext );
751                 }
752                 break;
753 
754                 case MetaActionType::BMPSCALEPART:
755                 {
756                     const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
757                     BitmapEx aBitmapEx( pA->GetBitmap() );
758                     aBitmapEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
759                     Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
760                     implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
761                 }
762                 break;
763 
764                 case MetaActionType::BMPEX:
765                 {
766                     const MetaBmpExAction*  pA = static_cast<const MetaBmpExAction*>(pAction);
767                     BitmapEx aBitmapEx( pA->GetBitmapEx() );
768                     Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
769                             aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
770                     Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
771                     implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, aGraphic, pDummyVDev, i_rContext );
772                 }
773                 break;
774 
775                 case MetaActionType::BMPEXSCALE:
776                 {
777                     const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
778                     BitmapEx aBitmapEx( pA->GetBitmapEx() );
779                     Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
780                     implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
781                 }
782                 break;
783 
784                 case MetaActionType::BMPEXSCALEPART:
785                 {
786                     const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
787 
788                     BitmapEx aBitmapEx( pA->GetBitmapEx() );
789                     aBitmapEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
790                     Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
791                     implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
792                 }
793                 break;
794 
795                 case MetaActionType::MASK:
796                 case MetaActionType::MASKSCALE:
797                 case MetaActionType::MASKSCALEPART:
798                 {
799                     SAL_WARN( "vcl", "MetaMask...Action not supported yet" );
800                 }
801                 break;
802 
803                 case MetaActionType::TEXT:
804                 {
805                     const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
806                     m_rOuterFace.DrawText( pA->GetPoint(), pA->GetText().copy( pA->GetIndex(), std::min<sal_Int32>(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) ) );
807                 }
808                 break;
809 
810                 case MetaActionType::TEXTRECT:
811                 {
812                     const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
813                     m_rOuterFace.DrawText( pA->GetRect(), pA->GetText(), pA->GetStyle() );
814                 }
815                 break;
816 
817                 case MetaActionType::TEXTARRAY:
818                 {
819                     const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
820                     m_rOuterFace.DrawTextArray(pA->GetPoint(), pA->GetText(), pA->GetDXArray(),
821                                                pA->GetKashidaArray(), pA->GetIndex(), pA->GetLen(),
822                                                pA->GetLayoutContextIndex(),
823                                                pA->GetLayoutContextLen());
824                 }
825                 break;
826 
827                 case MetaActionType::STRETCHTEXT:
828                 {
829                     const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
830                     m_rOuterFace.DrawStretchText( pA->GetPoint(), pA->GetWidth(), pA->GetText(), pA->GetIndex(), pA->GetLen() );
831                 }
832                 break;
833 
834                 case MetaActionType::TEXTLINE:
835                 {
836                     const MetaTextLineAction* pA = static_cast<const MetaTextLineAction*>(pAction);
837                     m_rOuterFace.DrawTextLine( pA->GetStartPoint(), pA->GetWidth(), pA->GetStrikeout(), pA->GetUnderline(), pA->GetOverline() );
838 
839                 }
840                 break;
841 
842                 case MetaActionType::CLIPREGION:
843                 {
844                     const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
845 
846                     if( pA->IsClipping() )
847                     {
848                         if( pA->GetRegion().IsEmpty() )
849                             m_rOuterFace.SetClipRegion( basegfx::B2DPolyPolygon() );
850                         else
851                         {
852                             const vcl::Region& aReg( pA->GetRegion() );
853                             m_rOuterFace.SetClipRegion( aReg.GetAsB2DPolyPolygon() );
854                         }
855                     }
856                     else
857                         m_rOuterFace.SetClipRegion();
858                 }
859                 break;
860 
861                 case MetaActionType::ISECTRECTCLIPREGION:
862                 {
863                     const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction);
864                     m_rOuterFace.IntersectClipRegion( pA->GetRect() );
865                 }
866                 break;
867 
868                 case MetaActionType::ISECTREGIONCLIPREGION:
869                 {
870                     const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction);
871                     const vcl::Region& aReg( pA->GetRegion() );
872                     m_rOuterFace.IntersectClipRegion( aReg.GetAsB2DPolyPolygon() );
873                 }
874                 break;
875 
876                 case MetaActionType::MOVECLIPREGION:
877                 {
878                     const MetaMoveClipRegionAction* pA = static_cast<const MetaMoveClipRegionAction*>(pAction);
879                     m_rOuterFace.MoveClipRegion( pA->GetHorzMove(), pA->GetVertMove() );
880                 }
881                 break;
882 
883                 case MetaActionType::MAPMODE:
884                 {
885                     const_cast< MetaAction* >( pAction )->Execute( pDummyVDev );
886                     m_rOuterFace.SetMapMode( pDummyVDev->GetMapMode() );
887                 }
888                 break;
889 
890                 case MetaActionType::LINECOLOR:
891                 {
892                     const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction);
893 
894                     if( pA->IsSetting() )
895                         m_rOuterFace.SetLineColor( pA->GetColor() );
896                     else
897                         m_rOuterFace.SetLineColor();
898                 }
899                 break;
900 
901                 case MetaActionType::FILLCOLOR:
902                 {
903                     const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction);
904 
905                     if( pA->IsSetting() )
906                         m_rOuterFace.SetFillColor( pA->GetColor() );
907                     else
908                         m_rOuterFace.SetFillColor();
909                 }
910                 break;
911 
912                 case MetaActionType::TEXTLINECOLOR:
913                 {
914                     const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction);
915 
916                     if( pA->IsSetting() )
917                         m_rOuterFace.SetTextLineColor( pA->GetColor() );
918                     else
919                         m_rOuterFace.SetTextLineColor();
920                 }
921                 break;
922 
923                 case MetaActionType::OVERLINECOLOR:
924                 {
925                     const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction);
926 
927                     if( pA->IsSetting() )
928                         m_rOuterFace.SetOverlineColor( pA->GetColor() );
929                     else
930                         m_rOuterFace.SetOverlineColor();
931                 }
932                 break;
933 
934                 case MetaActionType::TEXTFILLCOLOR:
935                 {
936                     const MetaTextFillColorAction* pA = static_cast<const MetaTextFillColorAction*>(pAction);
937 
938                     if( pA->IsSetting() )
939                         m_rOuterFace.SetTextFillColor( pA->GetColor() );
940                     else
941                         m_rOuterFace.SetTextFillColor();
942                 }
943                 break;
944 
945                 case MetaActionType::TEXTCOLOR:
946                 {
947                     const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pAction);
948                     m_rOuterFace.SetTextColor( pA->GetColor() );
949                 }
950                 break;
951 
952                 case MetaActionType::TEXTALIGN:
953                 {
954                     const MetaTextAlignAction* pA = static_cast<const MetaTextAlignAction*>(pAction);
955                     m_rOuterFace.SetTextAlign( pA->GetTextAlign() );
956                 }
957                 break;
958 
959                 case MetaActionType::FONT:
960                 {
961                     const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
962                     m_rOuterFace.SetFont( pA->GetFont() );
963                 }
964                 break;
965 
966                 case MetaActionType::PUSH:
967                 {
968                     const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
969 
970                     pDummyVDev->Push( pA->GetFlags() );
971                     m_rOuterFace.Push( pA->GetFlags() );
972                 }
973                 break;
974 
975                 case MetaActionType::POP:
976                 {
977                     pDummyVDev->Pop();
978                     m_rOuterFace.Pop();
979                 }
980                 break;
981 
982                 case MetaActionType::LAYOUTMODE:
983                 {
984                     const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
985                     m_rOuterFace.SetLayoutMode( pA->GetLayoutMode() );
986                 }
987                 break;
988 
989                 case MetaActionType::TEXTLANGUAGE:
990                 {
991                     const  MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
992                     m_rOuterFace.SetDigitLanguage( pA->GetTextLanguage() );
993                 }
994                 break;
995 
996                 case MetaActionType::WALLPAPER:
997                 {
998                     const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
999                     m_rOuterFace.DrawWallpaper( pA->GetRect(), pA->GetWallpaper() );
1000                 }
1001                 break;
1002 
1003                 case MetaActionType::RASTEROP:
1004                 case MetaActionType::REFPOINT:
1005                 {
1006                     // !!! >>> we don't want to support this actions
1007                 }
1008                 break;
1009 
1010                 default:
1011                     // #i24604# Made assertion fire only once per
1012                     // metafile. The asserted actions here are all
1013                     // deprecated
1014                     if( !bAssertionFired )
1015                     {
1016                         bAssertionFired = true;
1017                         SAL_WARN( "vcl", "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered " << static_cast<int>(nType) );
1018                     }
1019                 break;
1020             }
1021             i++;
1022         }
1023     }
1024 }
1025 
1026 // Encryption methods
1027 
checkAndEnableStreamEncryption(sal_Int32 nObject)1028 void PDFWriterImpl::checkAndEnableStreamEncryption(sal_Int32 nObject)
1029 {
1030     if (!m_aContext.Encryption.canEncrypt() || !m_pPDFEncryptor)
1031         return;
1032 
1033     m_pPDFEncryptor->enableStreamEncryption();
1034     m_pPDFEncryptor->setupEncryption(m_aContext.Encryption.EncryptionKey, nObject);
1035 }
1036 
disableStreamEncryption()1037 void PDFWriterImpl::disableStreamEncryption()
1038 {
1039     if (m_pPDFEncryptor)
1040         m_pPDFEncryptor->disableStreamEncryption();
1041 }
1042 
enableStringEncryption(sal_Int32 nObject)1043 void PDFWriterImpl::enableStringEncryption(sal_Int32 nObject)
1044 {
1045     if (!m_aContext.Encryption.canEncrypt() || !m_pPDFEncryptor)
1046         return;
1047 
1048     m_pPDFEncryptor->setupEncryption(m_aContext.Encryption.EncryptionKey, nObject);
1049 }
1050 
1051 const tools::Long unsetRun[256] =
1052 {
1053     8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
1054     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
1055     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */
1056     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */
1057     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1058     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */
1059     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1060     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */
1061     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
1062     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
1063     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
1064     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
1065     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
1066     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
1067     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
1068     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */
1069 };
1070 
1071 const tools::Long setRun[256] =
1072 {
1073     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
1074     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1075     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
1076     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
1077     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
1078     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
1079     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */
1080     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
1081     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */
1082     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */
1083     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */
1084     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */
1085     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */
1086     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */
1087     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
1088     4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
1089 };
1090 
isSet(const Scanline i_pLine,tools::Long i_nIndex)1091 static bool isSet( const Scanline i_pLine, tools::Long i_nIndex )
1092 {
1093     return (i_pLine[ i_nIndex/8 ] & (0x80 >> (i_nIndex&7))) != 0;
1094 }
1095 
findBitRunImpl(const Scanline i_pLine,tools::Long i_nStartIndex,tools::Long i_nW,bool i_bSet)1096 static tools::Long findBitRunImpl( const Scanline i_pLine, tools::Long i_nStartIndex, tools::Long i_nW, bool i_bSet )
1097 {
1098     tools::Long nIndex = i_nStartIndex;
1099     if( nIndex < i_nW )
1100     {
1101         const sal_uInt8 * pByte = i_pLine + (nIndex/8);
1102         sal_uInt8 nByte = *pByte;
1103 
1104         // run up to byte boundary
1105         tools::Long nBitInByte = (nIndex & 7);
1106         if( nBitInByte )
1107         {
1108             sal_uInt8 nMask = 0x80 >> nBitInByte;
1109             while( nBitInByte != 8 )
1110             {
1111                 if( (nByte & nMask) != (i_bSet ? nMask : 0) )
1112                     return std::min(nIndex, i_nW);
1113                 nMask = nMask >> 1;
1114                 nBitInByte++;
1115                 nIndex++;
1116             }
1117             if( nIndex < i_nW )
1118             {
1119                 pByte++;
1120                 nByte = *pByte;
1121             }
1122         }
1123 
1124         sal_uInt8 nRunByte;
1125         const tools::Long* pRunTable;
1126         if( i_bSet )
1127         {
1128             nRunByte = 0xff;
1129             pRunTable = setRun;
1130         }
1131         else
1132         {
1133             nRunByte = 0;
1134             pRunTable = unsetRun;
1135         }
1136 
1137         if( nIndex < i_nW )
1138         {
1139             while( nByte == nRunByte )
1140             {
1141                 nIndex += 8;
1142 
1143                 if (nIndex >= i_nW)
1144                     break;
1145 
1146                 pByte++;
1147                 nByte = *pByte;
1148             }
1149         }
1150 
1151         if( nIndex < i_nW )
1152         {
1153             nIndex += pRunTable[nByte];
1154         }
1155     }
1156     return std::min(nIndex, i_nW);
1157 }
1158 
findBitRun(const Scanline i_pLine,tools::Long i_nStartIndex,tools::Long i_nW,bool i_bSet)1159 static tools::Long findBitRun(const Scanline i_pLine, tools::Long i_nStartIndex, tools::Long i_nW, bool i_bSet)
1160 {
1161     if (i_nStartIndex < 0)
1162         return i_nW;
1163 
1164     return findBitRunImpl(i_pLine, i_nStartIndex, i_nW, i_bSet);
1165 }
1166 
findBitRun(const Scanline i_pLine,tools::Long i_nStartIndex,tools::Long i_nW)1167 static tools::Long findBitRun(const Scanline i_pLine, tools::Long i_nStartIndex, tools::Long i_nW)
1168 {
1169     if (i_nStartIndex < 0)
1170         return i_nW;
1171 
1172     const bool bSet = i_nStartIndex < i_nW && isSet(i_pLine, i_nStartIndex);
1173 
1174     return findBitRunImpl(i_pLine, i_nStartIndex, i_nW, bSet);
1175 }
1176 
1177 struct BitStreamState
1178 {
1179     sal_uInt8       mnBuffer;
1180     sal_uInt32      mnNextBitPos;
1181 
BitStreamStateBitStreamState1182     BitStreamState()
1183     : mnBuffer( 0 )
1184     , mnNextBitPos( 8 )
1185     {
1186     }
1187 
getByteBitStreamState1188     const sal_uInt8& getByte() const { return mnBuffer; }
flushBitStreamState1189     void flush() { mnNextBitPos = 8; mnBuffer = 0; }
1190 };
1191 
putG4Bits(sal_uInt32 i_nLength,sal_uInt32 i_nCode,BitStreamState & io_rState)1192 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState )
1193 {
1194     while( i_nLength > io_rState.mnNextBitPos )
1195     {
1196         io_rState.mnBuffer |= static_cast<sal_uInt8>( i_nCode >> (i_nLength - io_rState.mnNextBitPos) );
1197         i_nLength -= io_rState.mnNextBitPos;
1198         (void)writeBufferBytes( &io_rState.getByte(), 1 );
1199         io_rState.flush();
1200     }
1201     assert(i_nLength < 9);
1202     static const unsigned int msbmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1203     io_rState.mnBuffer |= static_cast<sal_uInt8>( (i_nCode & msbmask[i_nLength]) << (io_rState.mnNextBitPos - i_nLength) );
1204     io_rState.mnNextBitPos -= i_nLength;
1205     if( io_rState.mnNextBitPos == 0 )
1206     {
1207         (void)writeBufferBytes( &io_rState.getByte(), 1 );
1208         io_rState.flush();
1209     }
1210 }
1211 
1212 namespace {
1213 
1214 struct PixelCode
1215 {
1216     sal_uInt32      mnEncodedPixels;
1217     sal_uInt32      mnCodeBits;
1218     sal_uInt32      mnCode;
1219 };
1220 
1221 }
1222 
1223 const PixelCode WhitePixelCodes[] =
1224 {
1225     { 0, 8, 0x35 },     // 0011 0101
1226     { 1, 6, 0x7 },      // 0001 11
1227     { 2, 4, 0x7 },      // 0111
1228     { 3, 4, 0x8 },      // 1000
1229     { 4, 4, 0xB },      // 1011
1230     { 5, 4, 0xC },      // 1100
1231     { 6, 4, 0xE },      // 1110
1232     { 7, 4, 0xF },      // 1111
1233     { 8, 5, 0x13 },     // 1001 1
1234     { 9, 5, 0x14 },     // 1010 0
1235     { 10, 5, 0x7 },     // 0011 1
1236     { 11, 5, 0x8 },     // 0100 0
1237     { 12, 6, 0x8 },     // 0010 00
1238     { 13, 6, 0x3 },     // 0000 11
1239     { 14, 6, 0x34 },    // 1101 00
1240     { 15, 6, 0x35 },    // 1101 01
1241     { 16, 6, 0x2A },    // 1010 10
1242     { 17, 6, 0x2B },    // 1010 11
1243     { 18, 7, 0x27 },    // 0100 111
1244     { 19, 7, 0xC },     // 0001 100
1245     { 20, 7, 0x8 },     // 0001 000
1246     { 21, 7, 0x17 },    // 0010 111
1247     { 22, 7, 0x3 },     // 0000 011
1248     { 23, 7, 0x4 },     // 0000 100
1249     { 24, 7, 0x28 },    // 0101 000
1250     { 25, 7, 0x2B },    // 0101 011
1251     { 26, 7, 0x13 },    // 0010 011
1252     { 27, 7, 0x24 },    // 0100 100
1253     { 28, 7, 0x18 },    // 0011 000
1254     { 29, 8, 0x2 },     // 0000 0010
1255     { 30, 8, 0x3 },     // 0000 0011
1256     { 31, 8, 0x1A },    // 0001 1010
1257     { 32, 8, 0x1B },    // 0001 1011
1258     { 33, 8, 0x12 },    // 0001 0010
1259     { 34, 8, 0x13 },    // 0001 0011
1260     { 35, 8, 0x14 },    // 0001 0100
1261     { 36, 8, 0x15 },    // 0001 0101
1262     { 37, 8, 0x16 },    // 0001 0110
1263     { 38, 8, 0x17 },    // 0001 0111
1264     { 39, 8, 0x28 },    // 0010 1000
1265     { 40, 8, 0x29 },    // 0010 1001
1266     { 41, 8, 0x2A },    // 0010 1010
1267     { 42, 8, 0x2B },    // 0010 1011
1268     { 43, 8, 0x2C },    // 0010 1100
1269     { 44, 8, 0x2D },    // 0010 1101
1270     { 45, 8, 0x4 },     // 0000 0100
1271     { 46, 8, 0x5 },     // 0000 0101
1272     { 47, 8, 0xA },     // 0000 1010
1273     { 48, 8, 0xB },     // 0000 1011
1274     { 49, 8, 0x52 },    // 0101 0010
1275     { 50, 8, 0x53 },    // 0101 0011
1276     { 51, 8, 0x54 },    // 0101 0100
1277     { 52, 8, 0x55 },    // 0101 0101
1278     { 53, 8, 0x24 },    // 0010 0100
1279     { 54, 8, 0x25 },    // 0010 0101
1280     { 55, 8, 0x58 },    // 0101 1000
1281     { 56, 8, 0x59 },    // 0101 1001
1282     { 57, 8, 0x5A },    // 0101 1010
1283     { 58, 8, 0x5B },    // 0101 1011
1284     { 59, 8, 0x4A },    // 0100 1010
1285     { 60, 8, 0x4B },    // 0100 1011
1286     { 61, 8, 0x32 },    // 0011 0010
1287     { 62, 8, 0x33 },    // 0011 0011
1288     { 63, 8, 0x34 },    // 0011 0100
1289     { 64, 5, 0x1B },    // 1101 1
1290     { 128, 5, 0x12 },   // 1001 0
1291     { 192, 6, 0x17 },   // 0101 11
1292     { 256, 7, 0x37 },   // 0110 111
1293     { 320, 8, 0x36 },   // 0011 0110
1294     { 384, 8, 0x37 },   // 0011 0111
1295     { 448, 8, 0x64 },   // 0110 0100
1296     { 512, 8, 0x65 },   // 0110 0101
1297     { 576, 8, 0x68 },   // 0110 1000
1298     { 640, 8, 0x67 },   // 0110 0111
1299     { 704, 9, 0xCC },   // 0110 0110 0
1300     { 768, 9, 0xCD },   // 0110 0110 1
1301     { 832, 9, 0xD2 },   // 0110 1001 0
1302     { 896, 9, 0xD3 },   // 0110 1001 1
1303     { 960, 9, 0xD4 },   // 0110 1010 0
1304     { 1024, 9, 0xD5 },  // 0110 1010 1
1305     { 1088, 9, 0xD6 },  // 0110 1011 0
1306     { 1152, 9, 0xD7 },  // 0110 1011 1
1307     { 1216, 9, 0xD8 },  // 0110 1100 0
1308     { 1280, 9, 0xD9 },  // 0110 1100 1
1309     { 1344, 9, 0xDA },  // 0110 1101 0
1310     { 1408, 9, 0xDB },  // 0110 1101 1
1311     { 1472, 9, 0x98 },  // 0100 1100 0
1312     { 1536, 9, 0x99 },  // 0100 1100 1
1313     { 1600, 9, 0x9A },  // 0100 1101 0
1314     { 1664, 6, 0x18 },  // 0110 00
1315     { 1728, 9, 0x9B },  // 0100 1101 1
1316     { 1792, 11, 0x8 },  // 0000 0001 000
1317     { 1856, 11, 0xC },  // 0000 0001 100
1318     { 1920, 11, 0xD },  // 0000 0001 101
1319     { 1984, 12, 0x12 }, // 0000 0001 0010
1320     { 2048, 12, 0x13 }, // 0000 0001 0011
1321     { 2112, 12, 0x14 }, // 0000 0001 0100
1322     { 2176, 12, 0x15 }, // 0000 0001 0101
1323     { 2240, 12, 0x16 }, // 0000 0001 0110
1324     { 2304, 12, 0x17 }, // 0000 0001 0111
1325     { 2368, 12, 0x1C }, // 0000 0001 1100
1326     { 2432, 12, 0x1D }, // 0000 0001 1101
1327     { 2496, 12, 0x1E }, // 0000 0001 1110
1328     { 2560, 12, 0x1F }  // 0000 0001 1111
1329 };
1330 
1331 const PixelCode BlackPixelCodes[] =
1332 {
1333     { 0, 10, 0x37 },    // 0000 1101 11
1334     { 1, 3, 0x2 },      // 010
1335     { 2, 2, 0x3 },      // 11
1336     { 3, 2, 0x2 },      // 10
1337     { 4, 3, 0x3 },      // 011
1338     { 5, 4, 0x3 },      // 0011
1339     { 6, 4, 0x2 },      // 0010
1340     { 7, 5, 0x3 },      // 0001 1
1341     { 8, 6, 0x5 },      // 0001 01
1342     { 9, 6, 0x4 },      // 0001 00
1343     { 10, 7, 0x4 },     // 0000 100
1344     { 11, 7, 0x5 },     // 0000 101
1345     { 12, 7, 0x7 },     // 0000 111
1346     { 13, 8, 0x4 },     // 0000 0100
1347     { 14, 8, 0x7 },     // 0000 0111
1348     { 15, 9, 0x18 },    // 0000 1100 0
1349     { 16, 10, 0x17 },   // 0000 0101 11
1350     { 17, 10, 0x18 },   // 0000 0110 00
1351     { 18, 10, 0x8 },    // 0000 0010 00
1352     { 19, 11, 0x67 },   // 0000 1100 111
1353     { 20, 11, 0x68 },   // 0000 1101 000
1354     { 21, 11, 0x6C },   // 0000 1101 100
1355     { 22, 11, 0x37 },   // 0000 0110 111
1356     { 23, 11, 0x28 },   // 0000 0101 000
1357     { 24, 11, 0x17 },   // 0000 0010 111
1358     { 25, 11, 0x18 },   // 0000 0011 000
1359     { 26, 12, 0xCA },   // 0000 1100 1010
1360     { 27, 12, 0xCB },   // 0000 1100 1011
1361     { 28, 12, 0xCC },   // 0000 1100 1100
1362     { 29, 12, 0xCD },   // 0000 1100 1101
1363     { 30, 12, 0x68 },   // 0000 0110 1000
1364     { 31, 12, 0x69 },   // 0000 0110 1001
1365     { 32, 12, 0x6A },   // 0000 0110 1010
1366     { 33, 12, 0x6B },   // 0000 0110 1011
1367     { 34, 12, 0xD2 },   // 0000 1101 0010
1368     { 35, 12, 0xD3 },   // 0000 1101 0011
1369     { 36, 12, 0xD4 },   // 0000 1101 0100
1370     { 37, 12, 0xD5 },   // 0000 1101 0101
1371     { 38, 12, 0xD6 },   // 0000 1101 0110
1372     { 39, 12, 0xD7 },   // 0000 1101 0111
1373     { 40, 12, 0x6C },   // 0000 0110 1100
1374     { 41, 12, 0x6D },   // 0000 0110 1101
1375     { 42, 12, 0xDA },   // 0000 1101 1010
1376     { 43, 12, 0xDB },   // 0000 1101 1011
1377     { 44, 12, 0x54 },   // 0000 0101 0100
1378     { 45, 12, 0x55 },   // 0000 0101 0101
1379     { 46, 12, 0x56 },   // 0000 0101 0110
1380     { 47, 12, 0x57 },   // 0000 0101 0111
1381     { 48, 12, 0x64 },   // 0000 0110 0100
1382     { 49, 12, 0x65 },   // 0000 0110 0101
1383     { 50, 12, 0x52 },   // 0000 0101 0010
1384     { 51, 12, 0x53 },   // 0000 0101 0011
1385     { 52, 12, 0x24 },   // 0000 0010 0100
1386     { 53, 12, 0x37 },   // 0000 0011 0111
1387     { 54, 12, 0x38 },   // 0000 0011 1000
1388     { 55, 12, 0x27 },   // 0000 0010 0111
1389     { 56, 12, 0x28 },   // 0000 0010 1000
1390     { 57, 12, 0x58 },   // 0000 0101 1000
1391     { 58, 12, 0x59 },   // 0000 0101 1001
1392     { 59, 12, 0x2B },   // 0000 0010 1011
1393     { 60, 12, 0x2C },   // 0000 0010 1100
1394     { 61, 12, 0x5A },   // 0000 0101 1010
1395     { 62, 12, 0x66 },   // 0000 0110 0110
1396     { 63, 12, 0x67 },   // 0000 0110 0111
1397     { 64, 10, 0xF },    // 0000 0011 11
1398     { 128, 12, 0xC8 },  // 0000 1100 1000
1399     { 192, 12, 0xC9 },  // 0000 1100 1001
1400     { 256, 12, 0x5B },  // 0000 0101 1011
1401     { 320, 12, 0x33 },  // 0000 0011 0011
1402     { 384, 12, 0x34 },  // 0000 0011 0100
1403     { 448, 12, 0x35 },  // 0000 0011 0101
1404     { 512, 13, 0x6C },  // 0000 0011 0110 0
1405     { 576, 13, 0x6D },  // 0000 0011 0110 1
1406     { 640, 13, 0x4A },  // 0000 0010 0101 0
1407     { 704, 13, 0x4B },  // 0000 0010 0101 1
1408     { 768, 13, 0x4C },  // 0000 0010 0110 0
1409     { 832, 13, 0x4D },  // 0000 0010 0110 1
1410     { 896, 13, 0x72 },  // 0000 0011 1001 0
1411     { 960, 13, 0x73 },  // 0000 0011 1001 1
1412     { 1024, 13, 0x74 }, // 0000 0011 1010 0
1413     { 1088, 13, 0x75 }, // 0000 0011 1010 1
1414     { 1152, 13, 0x76 }, // 0000 0011 1011 0
1415     { 1216, 13, 0x77 }, // 0000 0011 1011 1
1416     { 1280, 13, 0x52 }, // 0000 0010 1001 0
1417     { 1344, 13, 0x53 }, // 0000 0010 1001 1
1418     { 1408, 13, 0x54 }, // 0000 0010 1010 0
1419     { 1472, 13, 0x55 }, // 0000 0010 1010 1
1420     { 1536, 13, 0x5A }, // 0000 0010 1101 0
1421     { 1600, 13, 0x5B }, // 0000 0010 1101 1
1422     { 1664, 13, 0x64 }, // 0000 0011 0010 0
1423     { 1728, 13, 0x65 }, // 0000 0011 0010 1
1424     { 1792, 11, 0x8 },  // 0000 0001 000
1425     { 1856, 11, 0xC },  // 0000 0001 100
1426     { 1920, 11, 0xD },  // 0000 0001 101
1427     { 1984, 12, 0x12 }, // 0000 0001 0010
1428     { 2048, 12, 0x13 }, // 0000 0001 0011
1429     { 2112, 12, 0x14 }, // 0000 0001 0100
1430     { 2176, 12, 0x15 }, // 0000 0001 0101
1431     { 2240, 12, 0x16 }, // 0000 0001 0110
1432     { 2304, 12, 0x17 }, // 0000 0001 0111
1433     { 2368, 12, 0x1C }, // 0000 0001 1100
1434     { 2432, 12, 0x1D }, // 0000 0001 1101
1435     { 2496, 12, 0x1E }, // 0000 0001 1110
1436     { 2560, 12, 0x1F }  // 0000 0001 1111
1437 };
1438 
putG4Span(tools::Long i_nSpan,bool i_bWhitePixel,BitStreamState & io_rState)1439 void PDFWriterImpl::putG4Span( tools::Long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState )
1440 {
1441     const PixelCode* pTable = i_bWhitePixel ? WhitePixelCodes : BlackPixelCodes;
1442     // maximum encoded span is 2560 consecutive pixels
1443     while( i_nSpan > 2623 )
1444     {
1445         // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1446         putG4Bits( pTable[103].mnCodeBits, pTable[103].mnCode, io_rState );
1447         i_nSpan -= pTable[103].mnEncodedPixels;
1448     }
1449     // write multiples of 64 pixels up to 2560
1450     if( i_nSpan > 63 )
1451     {
1452         sal_uInt32 nTabIndex = 63 + (i_nSpan >> 6);
1453         OSL_ASSERT( pTable[nTabIndex].mnEncodedPixels == static_cast<sal_uInt32>(64*(i_nSpan >> 6)) );
1454         putG4Bits( pTable[nTabIndex].mnCodeBits, pTable[nTabIndex].mnCode, io_rState );
1455         i_nSpan -= pTable[nTabIndex].mnEncodedPixels;
1456     }
1457     putG4Bits( pTable[i_nSpan].mnCodeBits, pTable[i_nSpan].mnCode, io_rState );
1458 }
1459 
writeG4Stream(BitmapReadAccess const * i_pBitmap)1460 void PDFWriterImpl::writeG4Stream( BitmapReadAccess const * i_pBitmap )
1461 {
1462     tools::Long nW = i_pBitmap->Width();
1463     tools::Long nH = i_pBitmap->Height();
1464     if( nW <= 0 || nH <= 0 )
1465         return;
1466     if( i_pBitmap->GetBitCount() != 1 )
1467         return;
1468 
1469     BitStreamState aBitState;
1470 
1471     // the first reference line is virtual and completely empty
1472     std::unique_ptr<sal_uInt8[]> pFirstRefLine(new  sal_uInt8[nW/8 + 1]);
1473     memset(pFirstRefLine.get(), 0, nW/8 + 1);
1474     Scanline pRefLine = pFirstRefLine.get();
1475     for( tools::Long nY = 0; nY < nH; nY++ )
1476     {
1477         const Scanline pCurLine = i_pBitmap->GetScanline( nY );
1478         tools::Long nLineIndex = 0;
1479         bool bRunSet = (*pCurLine & 0x80) != 0;
1480         bool bRefSet = (*pRefLine & 0x80) != 0;
1481         tools::Long nRunIndex1 = bRunSet ? 0 : findBitRun( pCurLine, 0, nW, bRunSet );
1482         tools::Long nRefIndex1 = bRefSet ? 0 : findBitRun( pRefLine, 0, nW, bRefSet );
1483         for( ; nLineIndex < nW; )
1484         {
1485             tools::Long nRefIndex2 = findBitRun( pRefLine, nRefIndex1, nW );
1486             if( nRefIndex2 >= nRunIndex1 )
1487             {
1488                 tools::Long nDiff = nRefIndex1 - nRunIndex1;
1489                 if( -3 <= nDiff && nDiff <= 3 )
1490                 {   // vertical coding
1491                     static const struct
1492                     {
1493                         sal_uInt32 mnCodeBits;
1494                         sal_uInt32 mnCode;
1495                     } VerticalCodes[7] = {
1496                         { 7, 0x03 },    // 0000 011
1497                         { 6, 0x03 },    // 0000 11
1498                         { 3, 0x03 },    // 011
1499                         { 1, 0x1 },     // 1
1500                         { 3, 0x2 },     // 010
1501                         { 6, 0x02 },    // 0000 10
1502                         { 7, 0x02 }     // 0000 010
1503                     };
1504                     // convert to index
1505                     nDiff += 3;
1506 
1507                     // emit diff code
1508                     putG4Bits( VerticalCodes[nDiff].mnCodeBits, VerticalCodes[nDiff].mnCode, aBitState );
1509                     nLineIndex = nRunIndex1;
1510                 }
1511                 else
1512                 {   // difference too large, horizontal coding
1513                     // emit horz code 001
1514                     putG4Bits( 3, 0x1, aBitState );
1515                     tools::Long nRunIndex2 = findBitRun( pCurLine, nRunIndex1, nW );
1516                     bool bWhiteFirst = ( nLineIndex + nRunIndex1 == 0 || ! isSet( pCurLine, nLineIndex ) );
1517                     putG4Span( nRunIndex1 - nLineIndex, bWhiteFirst, aBitState );
1518                     putG4Span( nRunIndex2 - nRunIndex1, ! bWhiteFirst, aBitState );
1519                     nLineIndex = nRunIndex2;
1520                 }
1521             }
1522             else
1523             {   // emit pass code 0001
1524                 putG4Bits( 4, 0x1, aBitState );
1525                 nLineIndex = nRefIndex2;
1526             }
1527             if( nLineIndex < nW )
1528             {
1529                 bool bSet = isSet( pCurLine, nLineIndex );
1530                 nRunIndex1 = findBitRun( pCurLine, nLineIndex, nW, bSet );
1531                 nRefIndex1 = findBitRun( pRefLine, nLineIndex, nW, ! bSet );
1532                 nRefIndex1 = findBitRun( pRefLine, nRefIndex1, nW, bSet );
1533             }
1534         }
1535 
1536         // the current line is the reference for the next line
1537         pRefLine = pCurLine;
1538     }
1539     // terminate strip with EOFB
1540     putG4Bits( 12, 1, aBitState );
1541     putG4Bits( 12, 1, aBitState );
1542     if( aBitState.mnNextBitPos != 8 )
1543     {
1544         (void)writeBufferBytes( &aBitState.getByte(), 1 );
1545         aBitState.flush();
1546     }
1547 }
1548 
DrawHatchLine_DrawLine(const Point & rStartPoint,const Point & rEndPoint)1549 void PDFWriterImpl::DrawHatchLine_DrawLine(const Point& rStartPoint, const Point& rEndPoint)
1550 {
1551     drawLine(rStartPoint, rEndPoint);
1552 }
1553 
lcl_canUsePDFAxialShading(const Gradient & rGradient)1554 static bool lcl_canUsePDFAxialShading(const Gradient& rGradient) {
1555     switch (rGradient.GetStyle())
1556     {
1557         case css::awt::GradientStyle_LINEAR:
1558         case css::awt::GradientStyle_AXIAL:
1559             break;
1560         default:
1561             return false;
1562     }
1563 
1564     // TODO: handle step count
1565     return rGradient.GetSteps() <= 0;
1566 }
1567 
ImplClearFontData(bool bNewFontLists)1568 void PDFWriterImpl::ImplClearFontData(bool bNewFontLists)
1569 {
1570     VirtualDevice::ImplClearFontData(bNewFontLists);
1571     if (bNewFontLists && AcquireGraphics())
1572     {
1573         ReleaseFontCollection();
1574         ReleaseFontCache();
1575     }
1576 }
1577 
ImplRefreshFontData(bool bNewFontLists)1578 void PDFWriterImpl::ImplRefreshFontData(bool bNewFontLists)
1579 {
1580     if (bNewFontLists && AcquireGraphics())
1581     {
1582         SetFontCollectionFromSVData();
1583         ResetNewFontCache();
1584     }
1585 }
1586 
ClipToDeviceBounds(vcl::Region aRegion) const1587 vcl::Region PDFWriterImpl::ClipToDeviceBounds(vcl::Region aRegion) const
1588 {
1589     return aRegion;
1590 }
1591 
1592 
1593 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1594