1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <pdf/pdfwriter_impl.hxx>
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>
29 #include <unotools/streamwrap.hxx>
31 #include <tools/helpers.hxx>
32 #include <tools/fract.hxx>
33 #include <tools/stream.hxx>
35 #include <comphelper/fileformat.h>
36 #include <comphelper/hash.hxx>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/propertyvalue.hxx>
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>
46 #include <cppuhelper/implbase.hxx>
47 #include <o3tl/unit_conversion.hxx>
48 #include <vcl/skia/SkiaHelper.hxx>
50 #include <sal/log.hxx>
54 using namespace com::sun::star
;
55 using namespace com::sun::star::uno
;
56 using namespace com::sun::star::beans
;
58 static bool lcl_canUsePDFAxialShading(const Gradient
& rGradient
);
60 void PDFWriterImpl::implWriteGradient( const tools::PolyPolygon
& i_rPolyPoly
, const Gradient
& i_rGradient
,
61 VirtualDevice
* i_pDummyVDev
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
)
64 Gradient
aGradient(i_rGradient
);
66 aGradient
.AddGradientActions( i_rPolyPoly
.GetBoundRect(), aTmpMtf
);
69 m_rOuterFace
.IntersectClipRegion( i_rPolyPoly
.getB2DPolyPolygon() );
70 playMetafile( aTmpMtf
, nullptr, i_rContext
, i_pDummyVDev
);
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
)
77 if ( i_rBitmapEx
.IsEmpty() || !i_rSize
.Width() || !i_rSize
.Height() )
80 BitmapEx
aBitmapEx( i_rBitmapEx
);
81 Point
aPoint( i_rPoint
);
82 Size
aSize( i_rSize
);
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 )
90 aSize
.setWidth( aSize
.Width() * -1 );
91 aPoint
.AdjustX( -(aSize
.Width()) );
92 nMirrorFlags
|= BmpMirrorFlags::Horizontal
;
94 if( aSize
.Height() < 0 )
96 aSize
.setHeight( aSize
.Height() * -1 );
97 aPoint
.AdjustY( -(aSize
.Height()) );
98 nMirrorFlags
|= BmpMirrorFlags::Vertical
;
101 if( nMirrorFlags
!= BmpMirrorFlags::NONE
)
103 aBitmapEx
.Mirror( nMirrorFlags
);
106 bool bIsJpeg
= false, bIsPng
= false;
107 if( i_Graphic
.GetType() != GraphicType::NONE
&& i_Graphic
.GetBitmapEx() == aBitmapEx
)
109 GfxLinkType eType
= i_Graphic
.GetGfxLink().GetType();
110 bIsJpeg
= (eType
== GfxLinkType::NativeJpg
);
111 bIsPng
= (eType
== GfxLinkType::NativePng
);
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)
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
;
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 ) )
137 const double fBmpWH
= fBmpPixelX
/ fBmpPixelY
;
138 const double fMaxWH
= fMaxPixelX
/ fMaxPixelY
;
140 if( fBmpWH
< fMaxWH
)
142 aNewBmpSize
.setWidth( FRound( fMaxPixelY
* fBmpWH
) );
143 aNewBmpSize
.setHeight( FRound( fMaxPixelY
) );
145 else if( fBmpWH
> 0.0 )
147 aNewBmpSize
.setWidth( FRound( fMaxPixelX
) );
148 aNewBmpSize
.setHeight( FRound( fMaxPixelX
/ fBmpWH
) );
151 if( aNewBmpSize
.Width() && aNewBmpSize
.Height() )
153 // #i121233# Use best quality for PDF exports
154 aBitmapEx
.Scale( aNewBmpSize
, BmpScaleFlag::BestQuality
);
158 aBitmapEx
.SetEmpty();
163 const Size
aSizePixel( aBitmapEx
.GetSizePixel() );
164 if ( !(aSizePixel
.Width() && aSizePixel
.Height()) )
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;
173 auto pStrm
=std::make_shared
<SvMemoryStream
>();
174 AlphaMask aAlphaMask
;
176 bool bTrueColorJPG
= true;
177 if ( bUseJPGCompression
)
179 // TODO this checks could be done much earlier, saving us
180 // from trying conversion & stores before...
181 if ( !aBitmapEx
.IsAlpha() )
183 const auto& rCacheEntry
=m_aPDFBmpCache
.find(
184 aBitmapEx
.GetChecksum());
185 if ( rCacheEntry
!= m_aPDFBmpCache
.end() )
187 m_rOuterFace
.DrawJPGBitmap( *rCacheEntry
->second
, true, aSizePixel
,
188 tools::Rectangle( aPoint
, aSize
), aAlphaMask
, i_Graphic
);
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
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();
201 if ( aBitmapEx
.IsAlpha() )
202 aAlphaMask
= aBitmapEx
.GetAlphaMask();
203 Graphic
aGraphic(BitmapEx(aBitmapEx
.GetBitmap()));
205 Sequence
< PropertyValue
> aFilterData
{
206 comphelper::makePropertyValue("Quality", sal_Int32(i_rContext
.m_nJPEGQuality
)),
207 comphelper::makePropertyValue("ColorMode", sal_Int32(0))
212 uno::Reference
< io::XStream
> xStream
= new utl::OStreamWrapper( *pStrm
);
213 uno::Reference
< io::XSeekable
> xSeekable( xStream
, UNO_QUERY_THROW
);
214 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("OutputStream", xOut
),
220 comphelper::makePropertyValue("MimeType", OUString("image/jpeg")),
221 comphelper::makePropertyValue("FilterData", aFilterData
)
223 xGraphicProvider
->storeGraphic( xGraphic
, aOutMediaProperties
);
225 if ( !bIsJpeg
&& xSeekable
->getLength() > nZippedFileSize
)
227 bUseJPGCompression
= false;
231 pStrm
->Seek( STREAM_SEEK_TO_END
);
233 xSeekable
->seek( 0 );
234 Sequence
< PropertyValue
> aArgs
{ comphelper::makePropertyValue("InputStream",
236 uno::Reference
< XPropertySet
> xPropSet( xGraphicProvider
->queryGraphicDescriptor( aArgs
) );
239 sal_Int16 nBitsPerPixel
= 24;
240 if ( xPropSet
->getPropertyValue("BitsPerPixel") >>= nBitsPerPixel
)
242 bTrueColorJPG
= nBitsPerPixel
!= 8;
247 catch( uno::Exception
& )
249 bUseJPGCompression
= false;
252 if ( bUseJPGCompression
)
254 m_rOuterFace
.DrawJPGBitmap( *pStrm
, bTrueColorJPG
, aSizePixel
, tools::Rectangle( aPoint
, aSize
), aAlphaMask
, i_Graphic
);
255 if (!aBitmapEx
.IsAlpha() && bTrueColorJPG
)
257 // Cache last jpeg export
258 m_aPDFBmpCache
.insert(
259 {aBitmapEx
.GetChecksum(), pStrm
});
262 else if ( aBitmapEx
.IsAlpha() )
263 m_rOuterFace
.DrawBitmapEx( aPoint
, aSize
, aBitmapEx
);
265 m_rOuterFace
.DrawBitmap( aPoint
, aSize
, aBitmapEx
.GetBitmap(), i_Graphic
);
269 void PDFWriterImpl::playMetafile( const GDIMetaFile
& i_rMtf
, vcl::PDFExtOutDevData
* i_pOutDevData
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
, VirtualDevice
* pDummyVDev
)
271 bool bAssertionFired( false );
273 ScopedVclPtr
<VirtualDevice
> xPrivateDevice
;
276 xPrivateDevice
.disposeAndReset(VclPtr
<VirtualDevice
>::Create());
277 pDummyVDev
= xPrivateDevice
.get();
278 pDummyVDev
->EnableOutput( false );
279 pDummyVDev
->SetMapMode( i_rMtf
.GetPrefMapMode() );
281 const GDIMetaFile
& aMtf( i_rMtf
);
283 for( sal_uInt32 i
= 0, nCount
= aMtf
.GetActionSize(); i
< nCount
; )
285 if ( !i_pOutDevData
|| !i_pOutDevData
->PlaySyncPageAct( m_rOuterFace
, i
, aMtf
) )
287 const MetaAction
* pAction
= aMtf
.GetAction( i
);
288 const MetaActionType nType
= pAction
->GetType();
292 case MetaActionType::PIXEL
:
294 const MetaPixelAction
* pA
= static_cast<const MetaPixelAction
*>(pAction
);
295 m_rOuterFace
.DrawPixel( pA
->GetPoint(), pA
->GetColor() );
299 case MetaActionType::POINT
:
301 const MetaPointAction
* pA
= static_cast<const MetaPointAction
*>(pAction
);
302 m_rOuterFace
.DrawPixel( pA
->GetPoint() );
306 case MetaActionType::LINE
:
308 const MetaLineAction
* pA
= static_cast<const MetaLineAction
*>(pAction
);
309 if ( pA
->GetLineInfo().IsDefault() )
310 m_rOuterFace
.DrawLine( pA
->GetStartPoint(), pA
->GetEndPoint() );
312 m_rOuterFace
.DrawLine( pA
->GetStartPoint(), pA
->GetEndPoint(), pA
->GetLineInfo() );
316 case MetaActionType::RECT
:
318 const MetaRectAction
* pA
= static_cast<const MetaRectAction
*>(pAction
);
319 m_rOuterFace
.DrawRect( pA
->GetRect() );
323 case MetaActionType::ROUNDRECT
:
325 const MetaRoundRectAction
* pA
= static_cast<const MetaRoundRectAction
*>(pAction
);
326 m_rOuterFace
.DrawRect( pA
->GetRect(), pA
->GetHorzRound(), pA
->GetVertRound() );
330 case MetaActionType::ELLIPSE
:
332 const MetaEllipseAction
* pA
= static_cast<const MetaEllipseAction
*>(pAction
);
333 m_rOuterFace
.DrawEllipse( pA
->GetRect() );
337 case MetaActionType::ARC
:
339 const MetaArcAction
* pA
= static_cast<const MetaArcAction
*>(pAction
);
340 m_rOuterFace
.DrawArc( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
344 case MetaActionType::PIE
:
346 const MetaArcAction
* pA
= static_cast<const MetaArcAction
*>(pAction
);
347 m_rOuterFace
.DrawPie( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
351 case MetaActionType::CHORD
:
353 const MetaChordAction
* pA
= static_cast<const MetaChordAction
*>(pAction
);
354 m_rOuterFace
.DrawChord( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
358 case MetaActionType::POLYGON
:
360 const MetaPolygonAction
* pA
= static_cast<const MetaPolygonAction
*>(pAction
);
361 m_rOuterFace
.DrawPolygon( pA
->GetPolygon() );
365 case MetaActionType::POLYLINE
:
367 const MetaPolyLineAction
* pA
= static_cast<const MetaPolyLineAction
*>(pAction
);
368 if ( pA
->GetLineInfo().IsDefault() )
369 m_rOuterFace
.DrawPolyLine( pA
->GetPolygon() );
371 m_rOuterFace
.DrawPolyLine( pA
->GetPolygon(), pA
->GetLineInfo() );
375 case MetaActionType::POLYPOLYGON
:
377 const MetaPolyPolygonAction
* pA
= static_cast<const MetaPolyPolygonAction
*>(pAction
);
378 m_rOuterFace
.DrawPolyPolygon( pA
->GetPolyPolygon() );
382 case MetaActionType::GRADIENT
:
384 const MetaGradientAction
* pA
= static_cast<const MetaGradientAction
*>(pAction
);
385 const Gradient
& rGradient
= pA
->GetGradient();
386 if (lcl_canUsePDFAxialShading(rGradient
))
388 m_rOuterFace
.DrawGradient( pA
->GetRect(), rGradient
);
392 const tools::PolyPolygon
aPolyPoly( pA
->GetRect() );
393 implWriteGradient( aPolyPoly
, rGradient
, pDummyVDev
, i_rContext
);
398 case MetaActionType::GRADIENTEX
:
400 const MetaGradientExAction
* pA
= static_cast<const MetaGradientExAction
*>(pAction
);
401 const Gradient
& rGradient
= pA
->GetGradient();
403 if (lcl_canUsePDFAxialShading(rGradient
))
404 m_rOuterFace
.DrawGradient( pA
->GetPolyPolygon(), rGradient
);
406 implWriteGradient( pA
->GetPolyPolygon(), rGradient
, pDummyVDev
, i_rContext
);
410 case MetaActionType::HATCH
:
412 const MetaHatchAction
* pA
= static_cast<const MetaHatchAction
*>(pAction
);
413 m_rOuterFace
.DrawHatch( pA
->GetPolyPolygon(), pA
->GetHatch() );
417 case MetaActionType::Transparent
:
419 const MetaTransparentAction
* pA
= static_cast<const MetaTransparentAction
*>(pAction
);
420 m_rOuterFace
.DrawTransparent( pA
->GetPolyPolygon(), pA
->GetTransparence() );
424 case MetaActionType::FLOATTRANSPARENT
:
426 const MetaFloatTransparentAction
* pA
= static_cast<const MetaFloatTransparentAction
*>(pAction
);
428 GDIMetaFile
aTmpMtf( pA
->GetGDIMetaFile() );
429 const Point
& rPos
= pA
->GetPoint();
430 const Size
& rSize
= pA
->GetSize();
431 const Gradient
& rTransparenceGradient
= pA
->GetGradient();
433 // special case constant alpha value
434 if( rTransparenceGradient
.GetStartColor() == rTransparenceGradient
.GetEndColor() )
436 const Color
aTransCol( rTransparenceGradient
.GetStartColor() );
437 const sal_uInt16 nTransPercent
= aTransCol
.GetLuminance() * 100 / 255;
438 m_rOuterFace
.BeginTransparencyGroup();
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());
445 playMetafile( aTmpMtf
, nullptr, i_rContext
, pDummyVDev
);
446 m_rOuterFace
.EndTransparencyGroup( tools::Rectangle( rPos
, rSize
), nTransPercent
);
450 const Size
aDstSizeTwip( pDummyVDev
->PixelToLogic(pDummyVDev
->LogicToPixel(rSize
), MapMode(MapUnit::MapTwip
)) );
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);
456 if( i_rContext
.m_nMaxImageResolution
> 50 )
458 if ( nMaxBmpDPI
> i_rContext
.m_nMaxImageResolution
)
459 nMaxBmpDPI
= i_rContext
.m_nMaxImageResolution
;
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
)
465 Size
aDstSizePixel( nPixelX
, nPixelY
);
466 ScopedVclPtrInstance
<VirtualDevice
> xVDev(DeviceFormat::WITH_ALPHA
);
467 if( xVDev
->SetOutputSizePixel( aDstSizePixel
, true, true ) )
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
) );
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
);
486 // create paint bitmap
488 aTmpMtf
.Play(*xVDev
, aPoint
, aDstSize
);
490 xVDev
->EnableMapMode( false );
491 BitmapEx aPaint
= xVDev
->GetBitmapEx(aPoint
, xVDev
->GetOutputSizePixel());
492 xVDev
->EnableMapMode( bVDevOldMap
); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here!
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 );
500 AlphaMask
aAlpha(xVDev
->GetBitmap(Point(), xVDev
->GetOutputSizePixel()));
501 AlphaMask
aPaintAlpha(aPaint
.GetAlphaMask());
502 // The alpha mask is inverted from what is
503 // expected so invert it again. To test this
504 // code, export to PDF the transparent shapes,
505 // gradients, and images in the documents
506 // attached to the following bug reports:
507 // https://bugs.documentfoundation.org/show_bug.cgi?id=155912
508 // https://bugs.documentfoundation.org/show_bug.cgi?id=156630
509 aAlpha
.Invert(); // convert to alpha
510 aAlpha
.BlendWith(aPaintAlpha
);
511 #if HAVE_FEATURE_SKIA
512 #if OSL_DEBUG_LEVEL > 0
513 // In release builds, we always invert
514 // regardless of whether Skia is enabled or not.
515 // But in debug builds, we can't invert when
517 if ( !SkiaHelper::isVCLSkiaEnabled() )
521 // When Skia is disabled, the alpha mask
522 // must be inverted a second time. To test
523 // this code, export the following
525 // https://bugs.documentfoundation.org/attachment.cgi?id=188084
526 aAlpha
.Invert(); // convert to alpha
529 xVDev
.disposeAndClear();
531 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
532 implWriteBitmapEx( rPos
, rSize
, BitmapEx( aPaint
.GetBitmap(), aAlpha
), aGraphic
, pDummyVDev
, i_rContext
);
539 case MetaActionType::EPS
:
541 const MetaEPSAction
* pA
= static_cast<const MetaEPSAction
*>(pAction
);
542 const GDIMetaFile
& aSubstitute( pA
->GetSubstitute() );
547 MapMode
aMapMode( aSubstitute
.GetPrefMapMode() );
548 Size
aOutSize( OutputDevice::LogicToLogic( pA
->GetSize(), pDummyVDev
->GetMapMode(), aMapMode
) );
549 aMapMode
.SetScaleX( Fraction( aOutSize
.Width(), aSubstitute
.GetPrefSize().Width() ) );
550 aMapMode
.SetScaleY( Fraction( aOutSize
.Height(), aSubstitute
.GetPrefSize().Height() ) );
551 aMapMode
.SetOrigin( OutputDevice::LogicToLogic( pA
->GetPoint(), pDummyVDev
->GetMapMode(), aMapMode
) );
553 m_rOuterFace
.SetMapMode( aMapMode
);
554 pDummyVDev
->SetMapMode( aMapMode
);
555 playMetafile( aSubstitute
, nullptr, i_rContext
, pDummyVDev
);
561 case MetaActionType::COMMENT
:
562 if( ! i_rContext
.m_bTransparenciesWereRemoved
)
564 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
566 if( pA
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
568 const MetaGradientExAction
* pGradAction
= nullptr;
571 while( !bDone
&& ( ++i
< nCount
) )
573 pAction
= aMtf
.GetAction( i
);
575 if( pAction
->GetType() == MetaActionType::GRADIENTEX
)
576 pGradAction
= static_cast<const MetaGradientExAction
*>(pAction
);
577 else if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
578 ( static_cast<const MetaCommentAction
*>(pAction
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END")) )
586 if (lcl_canUsePDFAxialShading(pGradAction
->GetGradient()))
588 m_rOuterFace
.DrawGradient( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient() );
592 implWriteGradient( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient(), pDummyVDev
, i_rContext
);
598 const sal_uInt8
* pData
= pA
->GetData();
601 SvMemoryStream
aMemStm( const_cast<sal_uInt8
*>(pData
), pA
->GetDataSize(), StreamMode::READ
);
602 bool bSkipSequence
= false;
605 if( pA
->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
607 sSeqEnd
= "XPATHSTROKE_SEQ_END"_ostr
;
608 SvtGraphicStroke aStroke
;
609 ReadSvtGraphicStroke( aMemStm
, aStroke
);
611 tools::Polygon aPath
;
612 aStroke
.getPath( aPath
);
614 tools::PolyPolygon aStartArrow
;
615 tools::PolyPolygon aEndArrow
;
616 double fTransparency( aStroke
.getTransparency() );
617 double fStrokeWidth( aStroke
.getStrokeWidth() );
618 SvtGraphicStroke::DashArray aDashArray
;
620 aStroke
.getStartArrow( aStartArrow
);
621 aStroke
.getEndArrow( aEndArrow
);
622 aStroke
.getDashArray( aDashArray
);
624 bSkipSequence
= true;
625 if ( aStartArrow
.Count() || aEndArrow
.Count() )
626 bSkipSequence
= false;
627 if ( !aDashArray
.empty() && ( fStrokeWidth
!= 0.0 ) && ( fTransparency
== 0.0 ) )
628 bSkipSequence
= false;
631 PDFWriter::ExtLineInfo aInfo
;
632 aInfo
.m_fLineWidth
= fStrokeWidth
;
633 aInfo
.m_fTransparency
= fTransparency
;
634 aInfo
.m_fMiterLimit
= aStroke
.getMiterLimit();
635 switch( aStroke
.getCapType() )
638 case SvtGraphicStroke::capButt
: aInfo
.m_eCap
= PDFWriter::capButt
;break;
639 case SvtGraphicStroke::capRound
: aInfo
.m_eCap
= PDFWriter::capRound
;break;
640 case SvtGraphicStroke::capSquare
: aInfo
.m_eCap
= PDFWriter::capSquare
;break;
642 switch( aStroke
.getJoinType() )
645 case SvtGraphicStroke::joinMiter
: aInfo
.m_eJoin
= PDFWriter::joinMiter
;break;
646 case SvtGraphicStroke::joinRound
: aInfo
.m_eJoin
= PDFWriter::joinRound
;break;
647 case SvtGraphicStroke::joinBevel
: aInfo
.m_eJoin
= PDFWriter::joinBevel
;break;
648 case SvtGraphicStroke::joinNone
:
649 aInfo
.m_eJoin
= PDFWriter::joinMiter
;
650 aInfo
.m_fMiterLimit
= 0.0;
653 aInfo
.m_aDashArray
= aDashArray
;
655 if(SvtGraphicStroke::joinNone
== aStroke
.getJoinType()
656 && fStrokeWidth
> 0.0)
658 // emulate no edge rounding by handling single edges
659 const sal_uInt16
nPoints(aPath
.GetSize());
660 const bool bCurve(aPath
.HasFlags());
662 for(sal_uInt16
a(0); a
+ 1 < nPoints
; a
++)
665 && PolyFlags::Normal
!= aPath
.GetFlags(a
+ 1)
667 && PolyFlags::Normal
!= aPath
.GetFlags(a
+ 2)
670 const tools::Polygon
aSnippet(4,
671 aPath
.GetConstPointAry() + a
,
672 aPath
.GetConstFlagAry() + a
);
673 m_rOuterFace
.DrawPolyLine( aSnippet
, aInfo
);
678 const tools::Polygon
aSnippet(2,
679 aPath
.GetConstPointAry() + a
);
680 m_rOuterFace
.DrawPolyLine( aSnippet
, aInfo
);
686 m_rOuterFace
.DrawPolyLine( aPath
, aInfo
);
690 else if ( pA
->GetComment() == "XPATHFILL_SEQ_BEGIN" )
692 sSeqEnd
= "XPATHFILL_SEQ_END"_ostr
;
693 SvtGraphicFill aFill
;
694 ReadSvtGraphicFill( aMemStm
, aFill
);
696 if ( ( aFill
.getFillType() == SvtGraphicFill::fillSolid
) && ( aFill
.getFillRule() == SvtGraphicFill::fillEvenOdd
) )
698 double fTransparency
= aFill
.getTransparency();
699 if ( fTransparency
== 0.0 )
701 tools::PolyPolygon aPath
;
702 aFill
.getPath( aPath
);
704 bSkipSequence
= true;
705 m_rOuterFace
.DrawPolyPolygon( aPath
);
707 else if ( fTransparency
== 1.0 )
708 bSkipSequence
= true;
713 while( ++i
< nCount
)
715 pAction
= aMtf
.GetAction( i
);
716 if ( pAction
->GetType() == MetaActionType::COMMENT
)
718 OString
sComment( static_cast<const MetaCommentAction
*>(pAction
)->GetComment() );
719 if (sComment
== sSeqEnd
)
723 // the replacement action for stroke is a filled rectangle
724 // the set fillcolor of the replacement is part of the graphics
725 // state and must not be skipped
726 else if( pAction
->GetType() == MetaActionType::FILLCOLOR
)
728 const MetaFillColorAction
* pMA
= static_cast<const MetaFillColorAction
*>(pAction
);
729 if( pMA
->IsSetting() )
730 m_rOuterFace
.SetFillColor( pMA
->GetColor() );
732 m_rOuterFace
.SetFillColor();
741 case MetaActionType::BMP
:
743 const MetaBmpAction
* pA
= static_cast<const MetaBmpAction
*>(pAction
);
744 BitmapEx
aBitmapEx( pA
->GetBitmap() );
745 Size
aSize( OutputDevice::LogicToLogic( aBitmapEx
.GetPrefSize(),
746 aBitmapEx
.GetPrefMapMode(), pDummyVDev
->GetMapMode() ) );
747 if( ! ( aSize
.Width() && aSize
.Height() ) )
748 aSize
= pDummyVDev
->PixelToLogic( aBitmapEx
.GetSizePixel() );
750 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
751 implWriteBitmapEx( pA
->GetPoint(), aSize
, aBitmapEx
, aGraphic
, pDummyVDev
, i_rContext
);
755 case MetaActionType::BMPSCALE
:
757 const MetaBmpScaleAction
* pA
= static_cast<const MetaBmpScaleAction
*>(pAction
);
758 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
759 implWriteBitmapEx( pA
->GetPoint(), pA
->GetSize(), BitmapEx( pA
->GetBitmap() ), aGraphic
, pDummyVDev
, i_rContext
);
763 case MetaActionType::BMPSCALEPART
:
765 const MetaBmpScalePartAction
* pA
= static_cast<const MetaBmpScalePartAction
*>(pAction
);
766 BitmapEx
aBitmapEx( pA
->GetBitmap() );
767 aBitmapEx
.Crop( tools::Rectangle( pA
->GetSrcPoint(), pA
->GetSrcSize() ) );
768 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
769 implWriteBitmapEx( pA
->GetDestPoint(), pA
->GetDestSize(), aBitmapEx
, aGraphic
, pDummyVDev
, i_rContext
);
773 case MetaActionType::BMPEX
:
775 const MetaBmpExAction
* pA
= static_cast<const MetaBmpExAction
*>(pAction
);
777 // The alpha mask is inverted from what is
778 // expected so invert it again. To test this
779 // code, export to PDF the transparent shapes,
780 // gradients, and images in the documents
781 // attached to the following bug reports:
782 // https://bugs.documentfoundation.org/show_bug.cgi?id=155912
783 // https://bugs.documentfoundation.org/show_bug.cgi?id=156630
784 BitmapEx
aBitmapEx( pA
->GetBitmapEx() );
785 if ( aBitmapEx
.IsAlpha())
787 AlphaMask aAlpha
= aBitmapEx
.GetAlphaMask();
789 aBitmapEx
= BitmapEx(aBitmapEx
.GetBitmap(), aAlpha
);
792 Size
aSize( OutputDevice::LogicToLogic( aBitmapEx
.GetPrefSize(),
793 aBitmapEx
.GetPrefMapMode(), pDummyVDev
->GetMapMode() ) );
794 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
795 implWriteBitmapEx( pA
->GetPoint(), aSize
, aBitmapEx
, aGraphic
, pDummyVDev
, i_rContext
);
799 case MetaActionType::BMPEXSCALE
:
801 const MetaBmpExScaleAction
* pA
= static_cast<const MetaBmpExScaleAction
*>(pAction
);
803 // The alpha mask is inverted from what is
804 // expected so invert it again. To test this
805 // code, export to PDF the transparent shapes,
806 // gradients, and images in the documents
807 // attached to the following bug reports:
808 // https://bugs.documentfoundation.org/show_bug.cgi?id=155912
809 // https://bugs.documentfoundation.org/show_bug.cgi?id=156630
810 BitmapEx
aBitmapEx( pA
->GetBitmapEx() );
811 if ( aBitmapEx
.IsAlpha())
813 AlphaMask aAlpha
= aBitmapEx
.GetAlphaMask();
815 aBitmapEx
= BitmapEx(aBitmapEx
.GetBitmap(), aAlpha
);
818 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
819 implWriteBitmapEx( pA
->GetPoint(), pA
->GetSize(), aBitmapEx
, aGraphic
, pDummyVDev
, i_rContext
);
823 case MetaActionType::BMPEXSCALEPART
:
825 const MetaBmpExScalePartAction
* pA
= static_cast<const MetaBmpExScalePartAction
*>(pAction
);
827 // The alpha mask is inverted from what is
828 // expected so invert it again. To test this
829 // code, export to PDF the transparent shapes,
830 // gradients, and images in the documents
831 // attached to the following bug reports:
832 // https://bugs.documentfoundation.org/show_bug.cgi?id=155912
833 // https://bugs.documentfoundation.org/show_bug.cgi?id=156630
834 BitmapEx
aBitmapEx( pA
->GetBitmapEx() );
835 if ( aBitmapEx
.IsAlpha())
837 AlphaMask aAlpha
= aBitmapEx
.GetAlphaMask();
839 aBitmapEx
= BitmapEx(aBitmapEx
.GetBitmap(), aAlpha
);
842 aBitmapEx
.Crop( tools::Rectangle( pA
->GetSrcPoint(), pA
->GetSrcSize() ) );
843 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
844 implWriteBitmapEx( pA
->GetDestPoint(), pA
->GetDestSize(), aBitmapEx
, aGraphic
, pDummyVDev
, i_rContext
);
848 case MetaActionType::MASK
:
849 case MetaActionType::MASKSCALE
:
850 case MetaActionType::MASKSCALEPART
:
852 SAL_WARN( "vcl", "MetaMask...Action not supported yet" );
856 case MetaActionType::TEXT
:
858 const MetaTextAction
* pA
= static_cast<const MetaTextAction
*>(pAction
);
859 m_rOuterFace
.DrawText( pA
->GetPoint(), pA
->GetText().copy( pA
->GetIndex(), std::min
<sal_Int32
>(pA
->GetText().getLength() - pA
->GetIndex(), pA
->GetLen()) ) );
863 case MetaActionType::TEXTRECT
:
865 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
866 m_rOuterFace
.DrawText( pA
->GetRect(), pA
->GetText(), pA
->GetStyle() );
870 case MetaActionType::TEXTARRAY
:
872 const MetaTextArrayAction
* pA
= static_cast<const MetaTextArrayAction
*>(pAction
);
873 m_rOuterFace
.DrawTextArray( pA
->GetPoint(), pA
->GetText(), pA
->GetDXArray(), pA
->GetKashidaArray(), pA
->GetIndex(), pA
->GetLen() );
877 case MetaActionType::STRETCHTEXT
:
879 const MetaStretchTextAction
* pA
= static_cast<const MetaStretchTextAction
*>(pAction
);
880 m_rOuterFace
.DrawStretchText( pA
->GetPoint(), pA
->GetWidth(), pA
->GetText(), pA
->GetIndex(), pA
->GetLen() );
884 case MetaActionType::TEXTLINE
:
886 const MetaTextLineAction
* pA
= static_cast<const MetaTextLineAction
*>(pAction
);
887 m_rOuterFace
.DrawTextLine( pA
->GetStartPoint(), pA
->GetWidth(), pA
->GetStrikeout(), pA
->GetUnderline(), pA
->GetOverline() );
892 case MetaActionType::CLIPREGION
:
894 const MetaClipRegionAction
* pA
= static_cast<const MetaClipRegionAction
*>(pAction
);
896 if( pA
->IsClipping() )
898 if( pA
->GetRegion().IsEmpty() )
899 m_rOuterFace
.SetClipRegion( basegfx::B2DPolyPolygon() );
902 const vcl::Region
& aReg( pA
->GetRegion() );
903 m_rOuterFace
.SetClipRegion( aReg
.GetAsB2DPolyPolygon() );
907 m_rOuterFace
.SetClipRegion();
911 case MetaActionType::ISECTRECTCLIPREGION
:
913 const MetaISectRectClipRegionAction
* pA
= static_cast<const MetaISectRectClipRegionAction
*>(pAction
);
914 m_rOuterFace
.IntersectClipRegion( pA
->GetRect() );
918 case MetaActionType::ISECTREGIONCLIPREGION
:
920 const MetaISectRegionClipRegionAction
* pA
= static_cast<const MetaISectRegionClipRegionAction
*>(pAction
);
921 const vcl::Region
& aReg( pA
->GetRegion() );
922 m_rOuterFace
.IntersectClipRegion( aReg
.GetAsB2DPolyPolygon() );
926 case MetaActionType::MOVECLIPREGION
:
928 const MetaMoveClipRegionAction
* pA
= static_cast<const MetaMoveClipRegionAction
*>(pAction
);
929 m_rOuterFace
.MoveClipRegion( pA
->GetHorzMove(), pA
->GetVertMove() );
933 case MetaActionType::MAPMODE
:
935 const_cast< MetaAction
* >( pAction
)->Execute( pDummyVDev
);
936 m_rOuterFace
.SetMapMode( pDummyVDev
->GetMapMode() );
940 case MetaActionType::LINECOLOR
:
942 const MetaLineColorAction
* pA
= static_cast<const MetaLineColorAction
*>(pAction
);
944 if( pA
->IsSetting() )
945 m_rOuterFace
.SetLineColor( pA
->GetColor() );
947 m_rOuterFace
.SetLineColor();
951 case MetaActionType::FILLCOLOR
:
953 const MetaFillColorAction
* pA
= static_cast<const MetaFillColorAction
*>(pAction
);
955 if( pA
->IsSetting() )
956 m_rOuterFace
.SetFillColor( pA
->GetColor() );
958 m_rOuterFace
.SetFillColor();
962 case MetaActionType::TEXTLINECOLOR
:
964 const MetaTextLineColorAction
* pA
= static_cast<const MetaTextLineColorAction
*>(pAction
);
966 if( pA
->IsSetting() )
967 m_rOuterFace
.SetTextLineColor( pA
->GetColor() );
969 m_rOuterFace
.SetTextLineColor();
973 case MetaActionType::OVERLINECOLOR
:
975 const MetaOverlineColorAction
* pA
= static_cast<const MetaOverlineColorAction
*>(pAction
);
977 if( pA
->IsSetting() )
978 m_rOuterFace
.SetOverlineColor( pA
->GetColor() );
980 m_rOuterFace
.SetOverlineColor();
984 case MetaActionType::TEXTFILLCOLOR
:
986 const MetaTextFillColorAction
* pA
= static_cast<const MetaTextFillColorAction
*>(pAction
);
988 if( pA
->IsSetting() )
989 m_rOuterFace
.SetTextFillColor( pA
->GetColor() );
991 m_rOuterFace
.SetTextFillColor();
995 case MetaActionType::TEXTCOLOR
:
997 const MetaTextColorAction
* pA
= static_cast<const MetaTextColorAction
*>(pAction
);
998 m_rOuterFace
.SetTextColor( pA
->GetColor() );
1002 case MetaActionType::TEXTALIGN
:
1004 const MetaTextAlignAction
* pA
= static_cast<const MetaTextAlignAction
*>(pAction
);
1005 m_rOuterFace
.SetTextAlign( pA
->GetTextAlign() );
1009 case MetaActionType::FONT
:
1011 const MetaFontAction
* pA
= static_cast<const MetaFontAction
*>(pAction
);
1012 m_rOuterFace
.SetFont( pA
->GetFont() );
1016 case MetaActionType::PUSH
:
1018 const MetaPushAction
* pA
= static_cast<const MetaPushAction
*>(pAction
);
1020 pDummyVDev
->Push( pA
->GetFlags() );
1021 m_rOuterFace
.Push( pA
->GetFlags() );
1025 case MetaActionType::POP
:
1032 case MetaActionType::LAYOUTMODE
:
1034 const MetaLayoutModeAction
* pA
= static_cast<const MetaLayoutModeAction
*>(pAction
);
1035 m_rOuterFace
.SetLayoutMode( pA
->GetLayoutMode() );
1039 case MetaActionType::TEXTLANGUAGE
:
1041 const MetaTextLanguageAction
* pA
= static_cast<const MetaTextLanguageAction
*>(pAction
);
1042 m_rOuterFace
.SetDigitLanguage( pA
->GetTextLanguage() );
1046 case MetaActionType::WALLPAPER
:
1048 const MetaWallpaperAction
* pA
= static_cast<const MetaWallpaperAction
*>(pAction
);
1049 m_rOuterFace
.DrawWallpaper( pA
->GetRect(), pA
->GetWallpaper() );
1053 case MetaActionType::RASTEROP
:
1055 // !!! >>> we don't want to support this actions
1059 case MetaActionType::REFPOINT
:
1061 // !!! >>> we don't want to support this actions
1066 // #i24604# Made assertion fire only once per
1067 // metafile. The asserted actions here are all
1069 if( !bAssertionFired
)
1071 bAssertionFired
= true;
1072 SAL_WARN( "vcl", "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered " << static_cast<int>(nType
) );
1081 // Encryption methods
1083 /* a crutch to transport a ::comphelper::Hash safely though UNO API
1084 this is needed for the PDF export dialog, which otherwise would have to pass
1085 clear text passwords down till they can be used in PDFWriter. Unfortunately
1086 the MD5 sum of the password (which is needed to create the PDF encryption key)
1087 is not sufficient, since an MD5 digest cannot be created in an arbitrary state
1088 which would be needed in PDFWriterImpl::computeEncryptionKey.
1090 class EncHashTransporter
: public cppu::WeakImplHelper
< css::beans::XMaterialHolder
>
1092 ::std::unique_ptr
<::comphelper::Hash
> m_pDigest
;
1094 std::vector
< sal_uInt8
> maOValue
;
1096 static std::map
< sal_IntPtr
, EncHashTransporter
* > sTransporters
;
1098 EncHashTransporter()
1099 : m_pDigest(new ::comphelper::Hash(::comphelper::HashType::MD5
))
1101 maID
= reinterpret_cast< sal_IntPtr
>(this);
1102 while( sTransporters
.find( maID
) != sTransporters
.end() ) // paranoia mode
1104 sTransporters
[ maID
] = this;
1107 virtual ~EncHashTransporter() override
1109 sTransporters
.erase( maID
);
1110 SAL_INFO( "vcl", "EncHashTransporter freed" );
1113 ::comphelper::Hash
* getUDigest() { return m_pDigest
.get(); };
1114 std::vector
< sal_uInt8
>& getOValue() { return maOValue
; }
1121 virtual uno::Any SAL_CALL
getMaterial() override
1123 return uno::Any( sal_Int64(maID
) );
1126 static EncHashTransporter
* getEncHashTransporter( const uno::Reference
< beans::XMaterialHolder
>& );
1130 std::map
< sal_IntPtr
, EncHashTransporter
* > EncHashTransporter::sTransporters
;
1132 EncHashTransporter
* EncHashTransporter::getEncHashTransporter( const uno::Reference
< beans::XMaterialHolder
>& xRef
)
1134 EncHashTransporter
* pResult
= nullptr;
1137 uno::Any
aMat( xRef
->getMaterial() );
1141 std::map
< sal_IntPtr
, EncHashTransporter
* >::iterator it
= sTransporters
.find( static_cast<sal_IntPtr
>(nMat
) );
1142 if( it
!= sTransporters
.end() )
1143 pResult
= it
->second
;
1149 void PDFWriterImpl::checkAndEnableStreamEncryption( sal_Int32 nObject
)
1151 if( !m_aContext
.Encryption
.Encrypt() )
1154 m_bEncryptThisStream
= true;
1155 sal_Int32 i
= m_nKeyLength
;
1156 m_aContext
.Encryption
.EncryptionKey
[i
++] = static_cast<sal_uInt8
>(nObject
);
1157 m_aContext
.Encryption
.EncryptionKey
[i
++] = static_cast<sal_uInt8
>( nObject
>> 8 );
1158 m_aContext
.Encryption
.EncryptionKey
[i
++] = static_cast<sal_uInt8
>( nObject
>> 16 );
1159 // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1161 ::std::vector
<unsigned char> const nMD5Sum(::comphelper::Hash::calculateHash(
1162 m_aContext
.Encryption
.EncryptionKey
.data(), i
+2, ::comphelper::HashType::MD5
));
1163 // the i+2 to take into account the generation number, always zero
1164 // initialize the RC4 with the key
1165 // key length: see algorithm 3.1, step 4: (N+5) max 16
1166 rtl_cipher_initARCFOUR( m_aCipher
, rtl_Cipher_DirectionEncode
, nMD5Sum
.data(), m_nRC4KeyLength
, nullptr, 0 );
1169 void PDFWriterImpl::enableStringEncryption( sal_Int32 nObject
)
1171 if( !m_aContext
.Encryption
.Encrypt() )
1174 sal_Int32 i
= m_nKeyLength
;
1175 m_aContext
.Encryption
.EncryptionKey
[i
++] = static_cast<sal_uInt8
>(nObject
);
1176 m_aContext
.Encryption
.EncryptionKey
[i
++] = static_cast<sal_uInt8
>( nObject
>> 8 );
1177 m_aContext
.Encryption
.EncryptionKey
[i
++] = static_cast<sal_uInt8
>( nObject
>> 16 );
1178 // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1180 // the i+2 to take into account the generation number, always zero
1181 ::std::vector
<unsigned char> const nMD5Sum(::comphelper::Hash::calculateHash(
1182 m_aContext
.Encryption
.EncryptionKey
.data(), i
+2, ::comphelper::HashType::MD5
));
1183 // initialize the RC4 with the key
1184 // key length: see algorithm 3.1, step 4: (N+5) max 16
1185 rtl_cipher_initARCFOUR( m_aCipher
, rtl_Cipher_DirectionEncode
, nMD5Sum
.data(), m_nRC4KeyLength
, nullptr, 0 );
1188 /* init the encryption engine
1189 1. init the document id, used both for building the document id and for building the encryption key(s)
1190 2. build the encryption key following algorithms described in the PDF specification
1192 uno::Reference
< beans::XMaterialHolder
> PDFWriterImpl::initEncryption( const OUString
& i_rOwnerPassword
,
1193 const OUString
& i_rUserPassword
1196 uno::Reference
< beans::XMaterialHolder
> xResult
;
1197 if( !i_rOwnerPassword
.isEmpty() || !i_rUserPassword
.isEmpty() )
1199 rtl::Reference
<EncHashTransporter
> pTransporter
= new EncHashTransporter
;
1200 xResult
= pTransporter
;
1202 // get padded passwords
1203 sal_uInt8 aPadUPW
[ENCRYPTED_PWD_SIZE
], aPadOPW
[ENCRYPTED_PWD_SIZE
];
1204 padPassword( i_rOwnerPassword
.isEmpty() ? i_rUserPassword
: i_rOwnerPassword
, aPadOPW
);
1205 padPassword( i_rUserPassword
, aPadUPW
);
1207 if( computeODictionaryValue( aPadOPW
, aPadUPW
, pTransporter
->getOValue(), SECUR_128BIT_KEY
) )
1209 pTransporter
->getUDigest()->update(aPadUPW
, ENCRYPTED_PWD_SIZE
);
1214 // trash temporary padded cleartext PWDs
1215 rtl_secureZeroMemory (aPadOPW
, sizeof(aPadOPW
));
1216 rtl_secureZeroMemory (aPadUPW
, sizeof(aPadUPW
));
1221 bool PDFWriterImpl::prepareEncryption( const uno::Reference
< beans::XMaterialHolder
>& xEnc
)
1223 bool bSuccess
= false;
1224 EncHashTransporter
* pTransporter
= EncHashTransporter::getEncHashTransporter( xEnc
);
1227 sal_Int32 nKeyLength
= 0, nRC4KeyLength
= 0;
1228 sal_Int32 nAccessPermissions
= computeAccessPermissions( m_aContext
.Encryption
, nKeyLength
, nRC4KeyLength
);
1229 m_aContext
.Encryption
.OValue
= pTransporter
->getOValue();
1230 bSuccess
= computeUDictionaryValue( pTransporter
, m_aContext
.Encryption
, nKeyLength
, nAccessPermissions
);
1234 m_aContext
.Encryption
.OValue
.clear();
1235 m_aContext
.Encryption
.UValue
.clear();
1236 m_aContext
.Encryption
.EncryptionKey
.clear();
1241 sal_Int32
PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties
& i_rProperties
,
1242 sal_Int32
& o_rKeyLength
, sal_Int32
& o_rRC4KeyLength
)
1245 2) compute the access permissions, in numerical form
1247 the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
1248 - for 40 bit security the unused bit must be set to 1, since they are not used
1249 - for 128 bit security the same bit must be preset to 0 and set later if needed
1250 according to the table 3.15, pdf v 1.4 */
1251 sal_Int32 nAccessPermissions
= 0xfffff0c0;
1253 o_rKeyLength
= SECUR_128BIT_KEY
;
1254 o_rRC4KeyLength
= 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16,
1255 // thus maximum permitted value is 16
1257 nAccessPermissions
|= ( i_rProperties
.CanPrintTheDocument
) ? 1 << 2 : 0;
1258 nAccessPermissions
|= ( i_rProperties
.CanModifyTheContent
) ? 1 << 3 : 0;
1259 nAccessPermissions
|= ( i_rProperties
.CanCopyOrExtract
) ? 1 << 4 : 0;
1260 nAccessPermissions
|= ( i_rProperties
.CanAddOrModify
) ? 1 << 5 : 0;
1261 nAccessPermissions
|= ( i_rProperties
.CanFillInteractive
) ? 1 << 8 : 0;
1262 nAccessPermissions
|= ( i_rProperties
.CanExtractForAccessibility
) ? 1 << 9 : 0;
1263 nAccessPermissions
|= ( i_rProperties
.CanAssemble
) ? 1 << 10 : 0;
1264 nAccessPermissions
|= ( i_rProperties
.CanPrintFull
) ? 1 << 11 : 0;
1265 return nAccessPermissions
;
1268 /*************************************************************
1269 begin i12626 methods
1271 Implements Algorithm 3.2, step 1 only
1273 void PDFWriterImpl::padPassword( std::u16string_view i_rPassword
, sal_uInt8
* o_pPaddedPW
)
1275 // get ansi-1252 version of the password string CHECKIT ! i12626
1276 OString
aString( OUStringToOString( i_rPassword
, RTL_TEXTENCODING_MS_1252
) );
1278 //copy the string to the target
1279 sal_Int32 nToCopy
= ( aString
.getLength() < ENCRYPTED_PWD_SIZE
) ? aString
.getLength() : ENCRYPTED_PWD_SIZE
;
1280 sal_Int32 nCurrentChar
;
1282 for( nCurrentChar
= 0; nCurrentChar
< nToCopy
; nCurrentChar
++ )
1283 o_pPaddedPW
[nCurrentChar
] = static_cast<sal_uInt8
>( aString
[nCurrentChar
] );
1285 //pad it with standard byte string
1287 for( i
= nCurrentChar
, y
= 0 ; i
< ENCRYPTED_PWD_SIZE
; i
++, y
++ )
1288 o_pPaddedPW
[i
] = s_nPadString
[y
];
1291 /**********************************
1292 Algorithm 3.2 Compute the encryption key used
1294 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
1295 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
1296 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
1298 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
1301 bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter
* i_pTransporter
, vcl::PDFWriter::PDFEncryptionProperties
& io_rProperties
, sal_Int32 i_nAccessPermissions
)
1303 bool bSuccess
= true;
1304 ::std::vector
<unsigned char> nMD5Sum
;
1306 // transporter contains an MD5 digest with the padded user password already
1307 ::comphelper::Hash
*const pDigest
= i_pTransporter
->getUDigest();
1311 if( ! io_rProperties
.OValue
.empty() )
1312 pDigest
->update(io_rProperties
.OValue
.data(), io_rProperties
.OValue
.size());
1318 nPerm
[0] = static_cast<sal_uInt8
>(i_nAccessPermissions
);
1319 nPerm
[1] = static_cast<sal_uInt8
>( i_nAccessPermissions
>> 8 );
1320 nPerm
[2] = static_cast<sal_uInt8
>( i_nAccessPermissions
>> 16 );
1321 nPerm
[3] = static_cast<sal_uInt8
>( i_nAccessPermissions
>> 24 );
1323 pDigest
->update(nPerm
, sizeof(nPerm
));
1325 //step 5, get the document ID, binary form
1326 pDigest
->update(io_rProperties
.DocumentIdentifier
.data(), io_rProperties
.DocumentIdentifier
.size());
1328 nMD5Sum
= pDigest
->finalize();
1330 //step 6, only if 128 bit
1331 for (sal_Int32 i
= 0; i
< 50; i
++)
1333 nMD5Sum
= ::comphelper::Hash::calculateHash(nMD5Sum
.data(), nMD5Sum
.size(), ::comphelper::HashType::MD5
);
1339 i_pTransporter
->invalidate();
1344 io_rProperties
.EncryptionKey
.resize( MAXIMUM_RC4_KEY_LENGTH
);
1345 for( sal_Int32 i
= 0; i
< MD5_DIGEST_SIZE
; i
++ )
1346 io_rProperties
.EncryptionKey
[i
] = nMD5Sum
[i
];
1349 io_rProperties
.EncryptionKey
.clear();
1354 /**********************************
1355 Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member
1356 the step numbers down here correspond to the ones in PDF v.1.4 specification
1358 bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8
* i_pPaddedOwnerPassword
,
1359 const sal_uInt8
* i_pPaddedUserPassword
,
1360 std::vector
< sal_uInt8
>& io_rOValue
,
1361 sal_Int32 i_nKeyLength
1364 bool bSuccess
= true;
1366 io_rOValue
.resize( ENCRYPTED_PWD_SIZE
);
1368 rtlCipher aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1371 //step 1 already done, data is in i_pPaddedOwnerPassword
1374 ::std::vector
<unsigned char> nMD5Sum(::comphelper::Hash::calculateHash(
1375 i_pPaddedOwnerPassword
, ENCRYPTED_PWD_SIZE
, ::comphelper::HashType::MD5
));
1376 //step 3, only if 128 bit
1377 if (i_nKeyLength
== SECUR_128BIT_KEY
)
1380 for (i
= 0; i
< 50; i
++)
1382 nMD5Sum
= ::comphelper::Hash::calculateHash(nMD5Sum
.data(), nMD5Sum
.size(), ::comphelper::HashType::MD5
);
1385 //Step 4, the key is in nMD5Sum
1386 //step 5 already done, data is in i_pPaddedUserPassword
1388 if (rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1389 nMD5Sum
.data(), i_nKeyLength
, nullptr, 0 )
1390 == rtl_Cipher_E_None
)
1392 // encrypt the user password using the key set above
1393 rtl_cipher_encodeARCFOUR( aCipher
, i_pPaddedUserPassword
, ENCRYPTED_PWD_SIZE
, // the data to be encrypted
1394 io_rOValue
.data(), sal_Int32(io_rOValue
.size()) ); //encrypted data
1395 //Step 7, only if 128 bit
1396 if( i_nKeyLength
== SECUR_128BIT_KEY
)
1400 sal_uInt8 nLocalKey
[ SECUR_128BIT_KEY
]; // 16 = 128 bit key
1402 for( i
= 1; i
<= 19; i
++ ) // do it 19 times, start with 1
1404 for( y
= 0; y
< sizeof( nLocalKey
); y
++ )
1405 nLocalKey
[y
] = static_cast<sal_uInt8
>( nMD5Sum
[y
] ^ i
);
1407 if (rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1408 nLocalKey
, SECUR_128BIT_KEY
, nullptr, 0 ) //destination data area, on init can be NULL
1409 != rtl_Cipher_E_None
)
1414 rtl_cipher_encodeARCFOUR( aCipher
, io_rOValue
.data(), sal_Int32(io_rOValue
.size()), // the data to be encrypted
1415 io_rOValue
.data(), sal_Int32(io_rOValue
.size()) ); // encrypted data, can be the same as the input, encrypt "in place"
1416 //step 8, store in class data member
1427 rtl_cipher_destroyARCFOUR( aCipher
);
1434 /**********************************
1435 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)
1437 bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter
* i_pTransporter
,
1438 vcl::PDFWriter::PDFEncryptionProperties
& io_rProperties
,
1439 sal_Int32 i_nKeyLength
,
1440 sal_Int32 i_nAccessPermissions
1443 bool bSuccess
= true;
1445 io_rProperties
.UValue
.resize( ENCRYPTED_PWD_SIZE
);
1447 ::comphelper::Hash
aDigest(::comphelper::HashType::MD5
);
1448 rtlCipher aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1451 //step 1, common to both 3.4 and 3.5
1452 if( computeEncryptionKey( i_pTransporter
, io_rProperties
, i_nAccessPermissions
) )
1454 // prepare encryption key for object
1455 for( sal_Int32 i
= i_nKeyLength
, y
= 0; y
< 5 ; y
++ )
1456 io_rProperties
.EncryptionKey
[i
++] = 0;
1458 //or 3.5, for 128 bit security
1459 //step6, initialize the last 16 bytes of the encrypted user password to 0
1460 for(sal_uInt32 i
= MD5_DIGEST_SIZE
; i
< sal_uInt32(io_rProperties
.UValue
.size()); i
++)
1461 io_rProperties
.UValue
[i
] = 0;
1463 aDigest
.update(s_nPadString
, sizeof(s_nPadString
));
1464 aDigest
.update(io_rProperties
.DocumentIdentifier
.data(), io_rProperties
.DocumentIdentifier
.size());
1466 ::std::vector
<unsigned char> const nMD5Sum(aDigest
.finalize());
1468 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1469 io_rProperties
.EncryptionKey
.data(), SECUR_128BIT_KEY
, nullptr, 0 ); //destination data area
1470 rtl_cipher_encodeARCFOUR( aCipher
, nMD5Sum
.data(), nMD5Sum
.size(), // the data to be encrypted
1471 io_rProperties
.UValue
.data(), SECUR_128BIT_KEY
); //encrypted data, stored in class data member
1475 sal_uInt8 nLocalKey
[SECUR_128BIT_KEY
];
1477 for( i
= 1; i
<= 19; i
++ ) // do it 19 times, start with 1
1479 for( y
= 0; y
< sizeof( nLocalKey
) ; y
++ )
1480 nLocalKey
[y
] = static_cast<sal_uInt8
>( io_rProperties
.EncryptionKey
[y
] ^ i
);
1482 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1483 nLocalKey
, SECUR_128BIT_KEY
, // key and key length
1484 nullptr, 0 ); //destination data area, on init can be NULL
1485 rtl_cipher_encodeARCFOUR( aCipher
, io_rProperties
.UValue
.data(), SECUR_128BIT_KEY
, // the data to be encrypted
1486 io_rProperties
.UValue
.data(), SECUR_128BIT_KEY
); // encrypted data, can be the same as the input, encrypt "in place"
1496 rtl_cipher_destroyARCFOUR( aCipher
);
1499 io_rProperties
.UValue
.clear();
1503 /* end i12626 methods */
1505 const tools::Long unsetRun
[256] =
1507 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
1508 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
1509 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */
1510 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */
1511 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1512 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */
1513 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1514 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */
1515 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
1516 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
1517 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
1518 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
1519 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
1520 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
1521 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
1522 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */
1525 const tools::Long setRun
[256] =
1527 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
1528 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1529 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
1530 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
1531 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
1532 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
1533 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */
1534 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
1535 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */
1536 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */
1537 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */
1538 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */
1539 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */
1540 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */
1541 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
1542 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
1545 static bool isSet( const Scanline i_pLine
, tools::Long i_nIndex
)
1547 return (i_pLine
[ i_nIndex
/8 ] & (0x80 >> (i_nIndex
&7))) != 0;
1550 static tools::Long
findBitRunImpl( const Scanline i_pLine
, tools::Long i_nStartIndex
, tools::Long i_nW
, bool i_bSet
)
1552 tools::Long nIndex
= i_nStartIndex
;
1555 const sal_uInt8
* pByte
= i_pLine
+ (nIndex
/8);
1556 sal_uInt8 nByte
= *pByte
;
1558 // run up to byte boundary
1559 tools::Long nBitInByte
= (nIndex
& 7);
1562 sal_uInt8 nMask
= 0x80 >> nBitInByte
;
1563 while( nBitInByte
!= 8 )
1565 if( (nByte
& nMask
) != (i_bSet
? nMask
: 0) )
1566 return std::min(nIndex
, i_nW
);
1579 const tools::Long
* pRunTable
;
1588 pRunTable
= unsetRun
;
1593 while( nByte
== nRunByte
)
1607 nIndex
+= pRunTable
[nByte
];
1610 return std::min(nIndex
, i_nW
);
1613 static tools::Long
findBitRun(const Scanline i_pLine
, tools::Long i_nStartIndex
, tools::Long i_nW
, bool i_bSet
)
1615 if (i_nStartIndex
< 0)
1618 return findBitRunImpl(i_pLine
, i_nStartIndex
, i_nW
, i_bSet
);
1621 static tools::Long
findBitRun(const Scanline i_pLine
, tools::Long i_nStartIndex
, tools::Long i_nW
)
1623 if (i_nStartIndex
< 0)
1626 const bool bSet
= i_nStartIndex
< i_nW
&& isSet(i_pLine
, i_nStartIndex
);
1628 return findBitRunImpl(i_pLine
, i_nStartIndex
, i_nW
, bSet
);
1631 struct BitStreamState
1634 sal_uInt32 mnNextBitPos
;
1642 const sal_uInt8
& getByte() const { return mnBuffer
; }
1643 void flush() { mnNextBitPos
= 8; mnBuffer
= 0; }
1646 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength
, sal_uInt32 i_nCode
, BitStreamState
& io_rState
)
1648 while( i_nLength
> io_rState
.mnNextBitPos
)
1650 io_rState
.mnBuffer
|= static_cast<sal_uInt8
>( i_nCode
>> (i_nLength
- io_rState
.mnNextBitPos
) );
1651 i_nLength
-= io_rState
.mnNextBitPos
;
1652 writeBufferBytes( &io_rState
.getByte(), 1 );
1655 assert(i_nLength
< 9);
1656 static const unsigned int msbmask
[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1657 io_rState
.mnBuffer
|= static_cast<sal_uInt8
>( (i_nCode
& msbmask
[i_nLength
]) << (io_rState
.mnNextBitPos
- i_nLength
) );
1658 io_rState
.mnNextBitPos
-= i_nLength
;
1659 if( io_rState
.mnNextBitPos
== 0 )
1661 writeBufferBytes( &io_rState
.getByte(), 1 );
1670 sal_uInt32 mnEncodedPixels
;
1671 sal_uInt32 mnCodeBits
;
1677 const PixelCode WhitePixelCodes
[] =
1679 { 0, 8, 0x35 }, // 0011 0101
1680 { 1, 6, 0x7 }, // 0001 11
1681 { 2, 4, 0x7 }, // 0111
1682 { 3, 4, 0x8 }, // 1000
1683 { 4, 4, 0xB }, // 1011
1684 { 5, 4, 0xC }, // 1100
1685 { 6, 4, 0xE }, // 1110
1686 { 7, 4, 0xF }, // 1111
1687 { 8, 5, 0x13 }, // 1001 1
1688 { 9, 5, 0x14 }, // 1010 0
1689 { 10, 5, 0x7 }, // 0011 1
1690 { 11, 5, 0x8 }, // 0100 0
1691 { 12, 6, 0x8 }, // 0010 00
1692 { 13, 6, 0x3 }, // 0000 11
1693 { 14, 6, 0x34 }, // 1101 00
1694 { 15, 6, 0x35 }, // 1101 01
1695 { 16, 6, 0x2A }, // 1010 10
1696 { 17, 6, 0x2B }, // 1010 11
1697 { 18, 7, 0x27 }, // 0100 111
1698 { 19, 7, 0xC }, // 0001 100
1699 { 20, 7, 0x8 }, // 0001 000
1700 { 21, 7, 0x17 }, // 0010 111
1701 { 22, 7, 0x3 }, // 0000 011
1702 { 23, 7, 0x4 }, // 0000 100
1703 { 24, 7, 0x28 }, // 0101 000
1704 { 25, 7, 0x2B }, // 0101 011
1705 { 26, 7, 0x13 }, // 0010 011
1706 { 27, 7, 0x24 }, // 0100 100
1707 { 28, 7, 0x18 }, // 0011 000
1708 { 29, 8, 0x2 }, // 0000 0010
1709 { 30, 8, 0x3 }, // 0000 0011
1710 { 31, 8, 0x1A }, // 0001 1010
1711 { 32, 8, 0x1B }, // 0001 1011
1712 { 33, 8, 0x12 }, // 0001 0010
1713 { 34, 8, 0x13 }, // 0001 0011
1714 { 35, 8, 0x14 }, // 0001 0100
1715 { 36, 8, 0x15 }, // 0001 0101
1716 { 37, 8, 0x16 }, // 0001 0110
1717 { 38, 8, 0x17 }, // 0001 0111
1718 { 39, 8, 0x28 }, // 0010 1000
1719 { 40, 8, 0x29 }, // 0010 1001
1720 { 41, 8, 0x2A }, // 0010 1010
1721 { 42, 8, 0x2B }, // 0010 1011
1722 { 43, 8, 0x2C }, // 0010 1100
1723 { 44, 8, 0x2D }, // 0010 1101
1724 { 45, 8, 0x4 }, // 0000 0100
1725 { 46, 8, 0x5 }, // 0000 0101
1726 { 47, 8, 0xA }, // 0000 1010
1727 { 48, 8, 0xB }, // 0000 1011
1728 { 49, 8, 0x52 }, // 0101 0010
1729 { 50, 8, 0x53 }, // 0101 0011
1730 { 51, 8, 0x54 }, // 0101 0100
1731 { 52, 8, 0x55 }, // 0101 0101
1732 { 53, 8, 0x24 }, // 0010 0100
1733 { 54, 8, 0x25 }, // 0010 0101
1734 { 55, 8, 0x58 }, // 0101 1000
1735 { 56, 8, 0x59 }, // 0101 1001
1736 { 57, 8, 0x5A }, // 0101 1010
1737 { 58, 8, 0x5B }, // 0101 1011
1738 { 59, 8, 0x4A }, // 0100 1010
1739 { 60, 8, 0x4B }, // 0100 1011
1740 { 61, 8, 0x32 }, // 0011 0010
1741 { 62, 8, 0x33 }, // 0011 0011
1742 { 63, 8, 0x34 }, // 0011 0100
1743 { 64, 5, 0x1B }, // 1101 1
1744 { 128, 5, 0x12 }, // 1001 0
1745 { 192, 6, 0x17 }, // 0101 11
1746 { 256, 7, 0x37 }, // 0110 111
1747 { 320, 8, 0x36 }, // 0011 0110
1748 { 384, 8, 0x37 }, // 0011 0111
1749 { 448, 8, 0x64 }, // 0110 0100
1750 { 512, 8, 0x65 }, // 0110 0101
1751 { 576, 8, 0x68 }, // 0110 1000
1752 { 640, 8, 0x67 }, // 0110 0111
1753 { 704, 9, 0xCC }, // 0110 0110 0
1754 { 768, 9, 0xCD }, // 0110 0110 1
1755 { 832, 9, 0xD2 }, // 0110 1001 0
1756 { 896, 9, 0xD3 }, // 0110 1001 1
1757 { 960, 9, 0xD4 }, // 0110 1010 0
1758 { 1024, 9, 0xD5 }, // 0110 1010 1
1759 { 1088, 9, 0xD6 }, // 0110 1011 0
1760 { 1152, 9, 0xD7 }, // 0110 1011 1
1761 { 1216, 9, 0xD8 }, // 0110 1100 0
1762 { 1280, 9, 0xD9 }, // 0110 1100 1
1763 { 1344, 9, 0xDA }, // 0110 1101 0
1764 { 1408, 9, 0xDB }, // 0110 1101 1
1765 { 1472, 9, 0x98 }, // 0100 1100 0
1766 { 1536, 9, 0x99 }, // 0100 1100 1
1767 { 1600, 9, 0x9A }, // 0100 1101 0
1768 { 1664, 6, 0x18 }, // 0110 00
1769 { 1728, 9, 0x9B }, // 0100 1101 1
1770 { 1792, 11, 0x8 }, // 0000 0001 000
1771 { 1856, 11, 0xC }, // 0000 0001 100
1772 { 1920, 11, 0xD }, // 0000 0001 101
1773 { 1984, 12, 0x12 }, // 0000 0001 0010
1774 { 2048, 12, 0x13 }, // 0000 0001 0011
1775 { 2112, 12, 0x14 }, // 0000 0001 0100
1776 { 2176, 12, 0x15 }, // 0000 0001 0101
1777 { 2240, 12, 0x16 }, // 0000 0001 0110
1778 { 2304, 12, 0x17 }, // 0000 0001 0111
1779 { 2368, 12, 0x1C }, // 0000 0001 1100
1780 { 2432, 12, 0x1D }, // 0000 0001 1101
1781 { 2496, 12, 0x1E }, // 0000 0001 1110
1782 { 2560, 12, 0x1F } // 0000 0001 1111
1785 const PixelCode BlackPixelCodes
[] =
1787 { 0, 10, 0x37 }, // 0000 1101 11
1788 { 1, 3, 0x2 }, // 010
1789 { 2, 2, 0x3 }, // 11
1790 { 3, 2, 0x2 }, // 10
1791 { 4, 3, 0x3 }, // 011
1792 { 5, 4, 0x3 }, // 0011
1793 { 6, 4, 0x2 }, // 0010
1794 { 7, 5, 0x3 }, // 0001 1
1795 { 8, 6, 0x5 }, // 0001 01
1796 { 9, 6, 0x4 }, // 0001 00
1797 { 10, 7, 0x4 }, // 0000 100
1798 { 11, 7, 0x5 }, // 0000 101
1799 { 12, 7, 0x7 }, // 0000 111
1800 { 13, 8, 0x4 }, // 0000 0100
1801 { 14, 8, 0x7 }, // 0000 0111
1802 { 15, 9, 0x18 }, // 0000 1100 0
1803 { 16, 10, 0x17 }, // 0000 0101 11
1804 { 17, 10, 0x18 }, // 0000 0110 00
1805 { 18, 10, 0x8 }, // 0000 0010 00
1806 { 19, 11, 0x67 }, // 0000 1100 111
1807 { 20, 11, 0x68 }, // 0000 1101 000
1808 { 21, 11, 0x6C }, // 0000 1101 100
1809 { 22, 11, 0x37 }, // 0000 0110 111
1810 { 23, 11, 0x28 }, // 0000 0101 000
1811 { 24, 11, 0x17 }, // 0000 0010 111
1812 { 25, 11, 0x18 }, // 0000 0011 000
1813 { 26, 12, 0xCA }, // 0000 1100 1010
1814 { 27, 12, 0xCB }, // 0000 1100 1011
1815 { 28, 12, 0xCC }, // 0000 1100 1100
1816 { 29, 12, 0xCD }, // 0000 1100 1101
1817 { 30, 12, 0x68 }, // 0000 0110 1000
1818 { 31, 12, 0x69 }, // 0000 0110 1001
1819 { 32, 12, 0x6A }, // 0000 0110 1010
1820 { 33, 12, 0x6B }, // 0000 0110 1011
1821 { 34, 12, 0xD2 }, // 0000 1101 0010
1822 { 35, 12, 0xD3 }, // 0000 1101 0011
1823 { 36, 12, 0xD4 }, // 0000 1101 0100
1824 { 37, 12, 0xD5 }, // 0000 1101 0101
1825 { 38, 12, 0xD6 }, // 0000 1101 0110
1826 { 39, 12, 0xD7 }, // 0000 1101 0111
1827 { 40, 12, 0x6C }, // 0000 0110 1100
1828 { 41, 12, 0x6D }, // 0000 0110 1101
1829 { 42, 12, 0xDA }, // 0000 1101 1010
1830 { 43, 12, 0xDB }, // 0000 1101 1011
1831 { 44, 12, 0x54 }, // 0000 0101 0100
1832 { 45, 12, 0x55 }, // 0000 0101 0101
1833 { 46, 12, 0x56 }, // 0000 0101 0110
1834 { 47, 12, 0x57 }, // 0000 0101 0111
1835 { 48, 12, 0x64 }, // 0000 0110 0100
1836 { 49, 12, 0x65 }, // 0000 0110 0101
1837 { 50, 12, 0x52 }, // 0000 0101 0010
1838 { 51, 12, 0x53 }, // 0000 0101 0011
1839 { 52, 12, 0x24 }, // 0000 0010 0100
1840 { 53, 12, 0x37 }, // 0000 0011 0111
1841 { 54, 12, 0x38 }, // 0000 0011 1000
1842 { 55, 12, 0x27 }, // 0000 0010 0111
1843 { 56, 12, 0x28 }, // 0000 0010 1000
1844 { 57, 12, 0x58 }, // 0000 0101 1000
1845 { 58, 12, 0x59 }, // 0000 0101 1001
1846 { 59, 12, 0x2B }, // 0000 0010 1011
1847 { 60, 12, 0x2C }, // 0000 0010 1100
1848 { 61, 12, 0x5A }, // 0000 0101 1010
1849 { 62, 12, 0x66 }, // 0000 0110 0110
1850 { 63, 12, 0x67 }, // 0000 0110 0111
1851 { 64, 10, 0xF }, // 0000 0011 11
1852 { 128, 12, 0xC8 }, // 0000 1100 1000
1853 { 192, 12, 0xC9 }, // 0000 1100 1001
1854 { 256, 12, 0x5B }, // 0000 0101 1011
1855 { 320, 12, 0x33 }, // 0000 0011 0011
1856 { 384, 12, 0x34 }, // 0000 0011 0100
1857 { 448, 12, 0x35 }, // 0000 0011 0101
1858 { 512, 13, 0x6C }, // 0000 0011 0110 0
1859 { 576, 13, 0x6D }, // 0000 0011 0110 1
1860 { 640, 13, 0x4A }, // 0000 0010 0101 0
1861 { 704, 13, 0x4B }, // 0000 0010 0101 1
1862 { 768, 13, 0x4C }, // 0000 0010 0110 0
1863 { 832, 13, 0x4D }, // 0000 0010 0110 1
1864 { 896, 13, 0x72 }, // 0000 0011 1001 0
1865 { 960, 13, 0x73 }, // 0000 0011 1001 1
1866 { 1024, 13, 0x74 }, // 0000 0011 1010 0
1867 { 1088, 13, 0x75 }, // 0000 0011 1010 1
1868 { 1152, 13, 0x76 }, // 0000 0011 1011 0
1869 { 1216, 13, 0x77 }, // 0000 0011 1011 1
1870 { 1280, 13, 0x52 }, // 0000 0010 1001 0
1871 { 1344, 13, 0x53 }, // 0000 0010 1001 1
1872 { 1408, 13, 0x54 }, // 0000 0010 1010 0
1873 { 1472, 13, 0x55 }, // 0000 0010 1010 1
1874 { 1536, 13, 0x5A }, // 0000 0010 1101 0
1875 { 1600, 13, 0x5B }, // 0000 0010 1101 1
1876 { 1664, 13, 0x64 }, // 0000 0011 0010 0
1877 { 1728, 13, 0x65 }, // 0000 0011 0010 1
1878 { 1792, 11, 0x8 }, // 0000 0001 000
1879 { 1856, 11, 0xC }, // 0000 0001 100
1880 { 1920, 11, 0xD }, // 0000 0001 101
1881 { 1984, 12, 0x12 }, // 0000 0001 0010
1882 { 2048, 12, 0x13 }, // 0000 0001 0011
1883 { 2112, 12, 0x14 }, // 0000 0001 0100
1884 { 2176, 12, 0x15 }, // 0000 0001 0101
1885 { 2240, 12, 0x16 }, // 0000 0001 0110
1886 { 2304, 12, 0x17 }, // 0000 0001 0111
1887 { 2368, 12, 0x1C }, // 0000 0001 1100
1888 { 2432, 12, 0x1D }, // 0000 0001 1101
1889 { 2496, 12, 0x1E }, // 0000 0001 1110
1890 { 2560, 12, 0x1F } // 0000 0001 1111
1893 void PDFWriterImpl::putG4Span( tools::Long i_nSpan
, bool i_bWhitePixel
, BitStreamState
& io_rState
)
1895 const PixelCode
* pTable
= i_bWhitePixel
? WhitePixelCodes
: BlackPixelCodes
;
1896 // maximum encoded span is 2560 consecutive pixels
1897 while( i_nSpan
> 2623 )
1899 // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1900 putG4Bits( pTable
[103].mnCodeBits
, pTable
[103].mnCode
, io_rState
);
1901 i_nSpan
-= pTable
[103].mnEncodedPixels
;
1903 // write multiples of 64 pixels up to 2560
1906 sal_uInt32 nTabIndex
= 63 + (i_nSpan
>> 6);
1907 OSL_ASSERT( pTable
[nTabIndex
].mnEncodedPixels
== static_cast<sal_uInt32
>(64*(i_nSpan
>> 6)) );
1908 putG4Bits( pTable
[nTabIndex
].mnCodeBits
, pTable
[nTabIndex
].mnCode
, io_rState
);
1909 i_nSpan
-= pTable
[nTabIndex
].mnEncodedPixels
;
1911 putG4Bits( pTable
[i_nSpan
].mnCodeBits
, pTable
[i_nSpan
].mnCode
, io_rState
);
1914 void PDFWriterImpl::writeG4Stream( BitmapReadAccess
const * i_pBitmap
)
1916 tools::Long nW
= i_pBitmap
->Width();
1917 tools::Long nH
= i_pBitmap
->Height();
1918 if( nW
<= 0 || nH
<= 0 )
1920 if( i_pBitmap
->GetBitCount() != 1 )
1923 BitStreamState aBitState
;
1925 // the first reference line is virtual and completely empty
1926 std::unique_ptr
<sal_uInt8
[]> pFirstRefLine(new sal_uInt8
[nW
/8 + 1]);
1927 memset(pFirstRefLine
.get(), 0, nW
/8 + 1);
1928 Scanline pRefLine
= pFirstRefLine
.get();
1929 for( tools::Long nY
= 0; nY
< nH
; nY
++ )
1931 const Scanline pCurLine
= i_pBitmap
->GetScanline( nY
);
1932 tools::Long nLineIndex
= 0;
1933 bool bRunSet
= (*pCurLine
& 0x80) != 0;
1934 bool bRefSet
= (*pRefLine
& 0x80) != 0;
1935 tools::Long nRunIndex1
= bRunSet
? 0 : findBitRun( pCurLine
, 0, nW
, bRunSet
);
1936 tools::Long nRefIndex1
= bRefSet
? 0 : findBitRun( pRefLine
, 0, nW
, bRefSet
);
1937 for( ; nLineIndex
< nW
; )
1939 tools::Long nRefIndex2
= findBitRun( pRefLine
, nRefIndex1
, nW
);
1940 if( nRefIndex2
>= nRunIndex1
)
1942 tools::Long nDiff
= nRefIndex1
- nRunIndex1
;
1943 if( -3 <= nDiff
&& nDiff
<= 3 )
1944 { // vertical coding
1947 sal_uInt32 mnCodeBits
;
1949 } VerticalCodes
[7] = {
1950 { 7, 0x03 }, // 0000 011
1951 { 6, 0x03 }, // 0000 11
1955 { 6, 0x02 }, // 0000 10
1956 { 7, 0x02 } // 0000 010
1962 putG4Bits( VerticalCodes
[nDiff
].mnCodeBits
, VerticalCodes
[nDiff
].mnCode
, aBitState
);
1963 nLineIndex
= nRunIndex1
;
1966 { // difference too large, horizontal coding
1967 // emit horz code 001
1968 putG4Bits( 3, 0x1, aBitState
);
1969 tools::Long nRunIndex2
= findBitRun( pCurLine
, nRunIndex1
, nW
);
1970 bool bWhiteFirst
= ( nLineIndex
+ nRunIndex1
== 0 || ! isSet( pCurLine
, nLineIndex
) );
1971 putG4Span( nRunIndex1
- nLineIndex
, bWhiteFirst
, aBitState
);
1972 putG4Span( nRunIndex2
- nRunIndex1
, ! bWhiteFirst
, aBitState
);
1973 nLineIndex
= nRunIndex2
;
1977 { // emit pass code 0001
1978 putG4Bits( 4, 0x1, aBitState
);
1979 nLineIndex
= nRefIndex2
;
1981 if( nLineIndex
< nW
)
1983 bool bSet
= isSet( pCurLine
, nLineIndex
);
1984 nRunIndex1
= findBitRun( pCurLine
, nLineIndex
, nW
, bSet
);
1985 nRefIndex1
= findBitRun( pRefLine
, nLineIndex
, nW
, ! bSet
);
1986 nRefIndex1
= findBitRun( pRefLine
, nRefIndex1
, nW
, bSet
);
1990 // the current line is the reference for the next line
1991 pRefLine
= pCurLine
;
1993 // terminate strip with EOFB
1994 putG4Bits( 12, 1, aBitState
);
1995 putG4Bits( 12, 1, aBitState
);
1996 if( aBitState
.mnNextBitPos
!= 8 )
1998 writeBufferBytes( &aBitState
.getByte(), 1 );
2003 void PDFWriterImpl::DrawHatchLine_DrawLine(const Point
& rStartPoint
, const Point
& rEndPoint
)
2005 drawLine(rStartPoint
, rEndPoint
);
2008 static bool lcl_canUsePDFAxialShading(const Gradient
& rGradient
) {
2009 switch (rGradient
.GetStyle())
2011 case css::awt::GradientStyle_LINEAR
:
2012 case css::awt::GradientStyle_AXIAL
:
2018 // TODO: handle step count
2019 return rGradient
.GetSteps() <= 0;
2022 void PDFWriterImpl::ImplClearFontData(bool bNewFontLists
)
2024 VirtualDevice::ImplClearFontData(bNewFontLists
);
2025 if (bNewFontLists
&& AcquireGraphics())
2027 ReleaseFontCollection();
2032 void PDFWriterImpl::ImplRefreshFontData(bool bNewFontLists
)
2034 if (bNewFontLists
&& AcquireGraphics())
2036 SetFontCollectionFromSVData();
2037 ResetNewFontCache();
2041 vcl::Region
PDFWriterImpl::ClipToDeviceBounds(vcl::Region aRegion
) const
2047 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */