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 "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/bitmapaccess.hxx>
27 #include <vcl/graph.hxx>
31 #include "unotools/streamwrap.hxx"
33 #include <tools/fract.hxx>
34 #include <tools/stream.hxx>
36 #include "comphelper/processfactory.hxx"
38 #include "com/sun/star/beans/PropertyValue.hpp"
39 #include "com/sun/star/io/XSeekable.hpp"
40 #include "com/sun/star/graphic/GraphicProvider.hpp"
41 #include "com/sun/star/graphic/XGraphicProvider.hpp"
43 #include <cppuhelper/implbase.hxx>
45 #include <rtl/digest.h>
49 using namespace com::sun::star
;
50 using namespace com::sun::star::uno
;
51 using namespace com::sun::star::beans
;
53 static bool lcl_canUsePDFAxialShading(const Gradient
& rGradient
);
55 void PDFWriterImpl::implWriteGradient( const tools::PolyPolygon
& i_rPolyPoly
, const Gradient
& i_rGradient
,
56 VirtualDevice
* i_pDummyVDev
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
)
60 i_pDummyVDev
->AddGradientActions( i_rPolyPoly
.GetBoundRect(), i_rGradient
, aTmpMtf
);
63 m_rOuterFace
.IntersectClipRegion( i_rPolyPoly
.getB2DPolyPolygon() );
64 playMetafile( aTmpMtf
, nullptr, i_rContext
, i_pDummyVDev
);
68 void PDFWriterImpl::implWriteBitmapEx( const Point
& i_rPoint
, const Size
& i_rSize
, const BitmapEx
& i_rBitmapEx
, const Graphic
& i_Graphic
,
69 VirtualDevice
* i_pDummyVDev
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
)
71 if ( !i_rBitmapEx
.IsEmpty() && i_rSize
.Width() && i_rSize
.Height() )
73 BitmapEx
aBitmapEx( i_rBitmapEx
);
74 Point
aPoint( i_rPoint
);
75 Size
aSize( i_rSize
);
77 // #i19065# Negative sizes have mirror semantics on
78 // OutputDevice. BitmapEx and co. have no idea about that, so
79 // perform that _before_ doing anything with aBitmapEx.
80 BmpMirrorFlags
nMirrorFlags(BmpMirrorFlags::NONE
);
81 if( aSize
.Width() < 0 )
84 aPoint
.X() -= aSize
.Width();
85 nMirrorFlags
|= BmpMirrorFlags::Horizontal
;
87 if( aSize
.Height() < 0 )
90 aPoint
.Y() -= aSize
.Height();
91 nMirrorFlags
|= BmpMirrorFlags::Vertical
;
94 if( nMirrorFlags
!= BmpMirrorFlags::NONE
)
96 aBitmapEx
.Mirror( nMirrorFlags
);
99 bool bIsJpeg
= false, bIsPng
= false;
100 if( i_Graphic
.GetType() != GraphicType::NONE
&& i_Graphic
.GetBitmapEx() == aBitmapEx
)
102 GfxLinkType eType
= i_Graphic
.GetLink().GetType();
103 bIsJpeg
= (eType
== GfxLinkType::NativeJpg
);
104 bIsPng
= (eType
== GfxLinkType::NativePng
);
107 if( i_rContext
.m_nMaxImageResolution
> 50 )
109 // do downsampling if necessary
110 const Size
aDstSizeTwip( i_pDummyVDev
->PixelToLogic( i_pDummyVDev
->LogicToPixel( aSize
), MapUnit::MapTwip
) );
111 const Size
aBmpSize( aBitmapEx
.GetSizePixel() );
112 const double fBmpPixelX
= aBmpSize
.Width();
113 const double fBmpPixelY
= aBmpSize
.Height();
114 const double fMaxPixelX
= aDstSizeTwip
.Width() * i_rContext
.m_nMaxImageResolution
/ 1440.0;
115 const double fMaxPixelY
= aDstSizeTwip
.Height() * i_rContext
.m_nMaxImageResolution
/ 1440.0;
117 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
118 if( ( ( fBmpPixelX
> ( fMaxPixelX
+ 4 ) ) ||
119 ( fBmpPixelY
> ( fMaxPixelY
+ 4 ) ) ) &&
120 ( fBmpPixelY
> 0.0 ) && ( fMaxPixelY
> 0.0 ) )
124 const double fBmpWH
= fBmpPixelX
/ fBmpPixelY
;
125 const double fMaxWH
= fMaxPixelX
/ fMaxPixelY
;
127 if( fBmpWH
< fMaxWH
)
129 aNewBmpSize
.Width() = FRound( fMaxPixelY
* fBmpWH
);
130 aNewBmpSize
.Height() = FRound( fMaxPixelY
);
132 else if( fBmpWH
> 0.0 )
134 aNewBmpSize
.Width() = FRound( fMaxPixelX
);
135 aNewBmpSize
.Height() = FRound( fMaxPixelX
/ fBmpWH
);
138 if( aNewBmpSize
.Width() && aNewBmpSize
.Height() )
140 // #i121233# Use best quality for PDF exports
141 aBitmapEx
.Scale( aNewBmpSize
, BmpScaleFlag::BestQuality
);
145 aBitmapEx
.SetEmpty();
150 const Size
aSizePixel( aBitmapEx
.GetSizePixel() );
151 if ( aSizePixel
.Width() && aSizePixel
.Height() )
153 if( m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
)
155 BmpConversion eConv
= BmpConversion::N8BitGreys
;
156 int nDepth
= aBitmapEx
.GetBitmap().GetBitCount();
158 eConv
= BmpConversion::N4BitGreys
;
160 aBitmapEx
.Convert( eConv
);
162 bool bUseJPGCompression
= !i_rContext
.m_bOnlyLosslessCompression
;
163 if ( bIsPng
|| ( aSizePixel
.Width() < 32 ) || ( aSizePixel
.Height() < 32 ) )
164 bUseJPGCompression
= false;
166 SvMemoryStream aStrm
;
169 bool bTrueColorJPG
= true;
170 if ( bUseJPGCompression
)
173 sal_uInt32 nZippedFileSize
= 0; // sj: we will calculate the filesize of a zipped bitmap
174 if ( !bIsJpeg
) // to determine if jpeg compression is useful
176 SvMemoryStream aTemp
;
177 aTemp
.SetCompressMode( aTemp
.GetCompressMode() | SvStreamCompressFlags::ZBITMAP
);
178 aTemp
.SetVersion( SOFFICE_FILEFORMAT_40
); // sj: up from version 40 our bitmap stream operator
179 WriteDIBBitmapEx(aBitmapEx
, aTemp
); // is capable of zlib stream compression
180 aTemp
.Seek( STREAM_SEEK_TO_END
);
181 nZippedFileSize
= aTemp
.Tell();
183 if ( aBitmapEx
.IsTransparent() )
185 if ( aBitmapEx
.IsAlpha() )
186 aMask
= aBitmapEx
.GetAlpha().GetBitmap();
188 aMask
= aBitmapEx
.GetMask();
190 Graphic
aGraphic( aBitmapEx
.GetBitmap() );
191 sal_Int32 nColorMode
= 0;
193 Sequence
< PropertyValue
> aFilterData( 2 );
194 aFilterData
[ 0 ].Name
= "Quality";
195 aFilterData
[ 0 ].Value
<<= sal_Int32(i_rContext
.m_nJPEGQuality
);
196 aFilterData
[ 1 ].Name
= "ColorMode";
197 aFilterData
[ 1 ].Value
<<= nColorMode
;
201 uno::Reference
< io::XStream
> xStream
= new utl::OStreamWrapper( aStrm
);
202 uno::Reference
< io::XSeekable
> xSeekable( xStream
, UNO_QUERY_THROW
);
203 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
204 uno::Reference
< graphic::XGraphicProvider
> xGraphicProvider( graphic::GraphicProvider::create(xContext
) );
205 uno::Reference
< graphic::XGraphic
> xGraphic( aGraphic
.GetXGraphic() );
206 uno::Reference
< io::XOutputStream
> xOut( xStream
->getOutputStream() );
207 OUString
aMimeType("image/jpeg");
208 uno::Sequence
< beans::PropertyValue
> aOutMediaProperties( 3 );
209 aOutMediaProperties
[0].Name
= "OutputStream";
210 aOutMediaProperties
[0].Value
<<= xOut
;
211 aOutMediaProperties
[1].Name
= "MimeType";
212 aOutMediaProperties
[1].Value
<<= aMimeType
;
213 aOutMediaProperties
[2].Name
= "FilterData";
214 aOutMediaProperties
[2].Value
<<= aFilterData
;
215 xGraphicProvider
->storeGraphic( xGraphic
, aOutMediaProperties
);
217 if ( !bIsJpeg
&& xSeekable
->getLength() > nZippedFileSize
)
219 bUseJPGCompression
= false;
223 aStrm
.Seek( STREAM_SEEK_TO_END
);
225 xSeekable
->seek( 0 );
226 Sequence
< PropertyValue
> aArgs( 1 );
227 aArgs
[ 0 ].Name
= "InputStream";
228 aArgs
[ 0 ].Value
<<= xStream
;
229 uno::Reference
< XPropertySet
> xPropSet( xGraphicProvider
->queryGraphicDescriptor( aArgs
) );
232 sal_Int16 nBitsPerPixel
= 24;
233 if ( xPropSet
->getPropertyValue("BitsPerPixel") >>= nBitsPerPixel
)
235 bTrueColorJPG
= nBitsPerPixel
!= 8;
240 catch( uno::Exception
& )
242 bUseJPGCompression
= false;
245 if ( bUseJPGCompression
)
246 m_rOuterFace
.DrawJPGBitmap( aStrm
, bTrueColorJPG
, aSizePixel
, tools::Rectangle( aPoint
, aSize
), aMask
, i_Graphic
);
247 else if ( aBitmapEx
.IsTransparent() )
248 m_rOuterFace
.DrawBitmapEx( aPoint
, aSize
, aBitmapEx
);
250 m_rOuterFace
.DrawBitmap( aPoint
, aSize
, aBitmapEx
.GetBitmap(), i_Graphic
);
255 void PDFWriterImpl::playMetafile( const GDIMetaFile
& i_rMtf
, vcl::PDFExtOutDevData
* i_pOutDevData
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
, VirtualDevice
* pDummyVDev
)
257 bool bAssertionFired( false );
259 ScopedVclPtr
<VirtualDevice
> xPrivateDevice
;
262 xPrivateDevice
.disposeAndReset(VclPtr
<VirtualDevice
>::Create());
263 pDummyVDev
= xPrivateDevice
.get();
264 pDummyVDev
->EnableOutput( false );
265 pDummyVDev
->SetMapMode( i_rMtf
.GetPrefMapMode() );
267 GDIMetaFile
aMtf( i_rMtf
);
269 for( sal_uInt32 i
= 0, nCount
= aMtf
.GetActionSize(); i
< nCount
; )
271 if ( !i_pOutDevData
|| !i_pOutDevData
->PlaySyncPageAct( m_rOuterFace
, i
) )
273 const MetaAction
* pAction
= aMtf
.GetAction( i
);
274 const MetaActionType nType
= pAction
->GetType();
278 case( MetaActionType::PIXEL
):
280 const MetaPixelAction
* pA
= static_cast<const MetaPixelAction
*>(pAction
);
281 m_rOuterFace
.DrawPixel( pA
->GetPoint(), pA
->GetColor() );
285 case( MetaActionType::POINT
):
287 const MetaPointAction
* pA
= static_cast<const MetaPointAction
*>(pAction
);
288 m_rOuterFace
.DrawPixel( pA
->GetPoint() );
292 case( MetaActionType::LINE
):
294 const MetaLineAction
* pA
= static_cast<const MetaLineAction
*>(pAction
);
295 if ( pA
->GetLineInfo().IsDefault() )
296 m_rOuterFace
.DrawLine( pA
->GetStartPoint(), pA
->GetEndPoint() );
298 m_rOuterFace
.DrawLine( pA
->GetStartPoint(), pA
->GetEndPoint(), pA
->GetLineInfo() );
302 case( MetaActionType::RECT
):
304 const MetaRectAction
* pA
= static_cast<const MetaRectAction
*>(pAction
);
305 m_rOuterFace
.DrawRect( pA
->GetRect() );
309 case( MetaActionType::ROUNDRECT
):
311 const MetaRoundRectAction
* pA
= static_cast<const MetaRoundRectAction
*>(pAction
);
312 m_rOuterFace
.DrawRect( pA
->GetRect(), pA
->GetHorzRound(), pA
->GetVertRound() );
316 case( MetaActionType::ELLIPSE
):
318 const MetaEllipseAction
* pA
= static_cast<const MetaEllipseAction
*>(pAction
);
319 m_rOuterFace
.DrawEllipse( pA
->GetRect() );
323 case( MetaActionType::ARC
):
325 const MetaArcAction
* pA
= static_cast<const MetaArcAction
*>(pAction
);
326 m_rOuterFace
.DrawArc( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
330 case( MetaActionType::PIE
):
332 const MetaArcAction
* pA
= static_cast<const MetaArcAction
*>(pAction
);
333 m_rOuterFace
.DrawPie( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
337 case( MetaActionType::CHORD
):
339 const MetaChordAction
* pA
= static_cast<const MetaChordAction
*>(pAction
);
340 m_rOuterFace
.DrawChord( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
344 case( MetaActionType::POLYGON
):
346 const MetaPolygonAction
* pA
= static_cast<const MetaPolygonAction
*>(pAction
);
347 m_rOuterFace
.DrawPolygon( pA
->GetPolygon() );
351 case( MetaActionType::POLYLINE
):
353 const MetaPolyLineAction
* pA
= static_cast<const MetaPolyLineAction
*>(pAction
);
354 if ( pA
->GetLineInfo().IsDefault() )
355 m_rOuterFace
.DrawPolyLine( pA
->GetPolygon() );
357 m_rOuterFace
.DrawPolyLine( pA
->GetPolygon(), pA
->GetLineInfo() );
361 case( MetaActionType::POLYPOLYGON
):
363 const MetaPolyPolygonAction
* pA
= static_cast<const MetaPolyPolygonAction
*>(pAction
);
364 m_rOuterFace
.DrawPolyPolygon( pA
->GetPolyPolygon() );
368 case( MetaActionType::GRADIENT
):
370 const MetaGradientAction
* pA
= static_cast<const MetaGradientAction
*>(pAction
);
371 const Gradient
& rGradient
= pA
->GetGradient();
372 if (lcl_canUsePDFAxialShading(rGradient
))
374 m_rOuterFace
.DrawGradient( pA
->GetRect(), rGradient
);
378 const tools::PolyPolygon
aPolyPoly( pA
->GetRect() );
379 implWriteGradient( aPolyPoly
, rGradient
, pDummyVDev
, i_rContext
);
384 case( MetaActionType::GRADIENTEX
):
386 const MetaGradientExAction
* pA
= static_cast<const MetaGradientExAction
*>(pAction
);
387 const Gradient
& rGradient
= pA
->GetGradient();
389 if (lcl_canUsePDFAxialShading(rGradient
))
390 m_rOuterFace
.DrawGradient( pA
->GetPolyPolygon(), rGradient
);
392 implWriteGradient( pA
->GetPolyPolygon(), rGradient
, pDummyVDev
, i_rContext
);
396 case MetaActionType::HATCH
:
398 const MetaHatchAction
* pA
= static_cast<const MetaHatchAction
*>(pAction
);
399 m_rOuterFace
.DrawHatch( pA
->GetPolyPolygon(), pA
->GetHatch() );
403 case( MetaActionType::Transparent
):
405 const MetaTransparentAction
* pA
= static_cast<const MetaTransparentAction
*>(pAction
);
406 m_rOuterFace
.DrawTransparent( pA
->GetPolyPolygon(), pA
->GetTransparence() );
410 case( MetaActionType::FLOATTRANSPARENT
):
412 const MetaFloatTransparentAction
* pA
= static_cast<const MetaFloatTransparentAction
*>(pAction
);
414 GDIMetaFile
aTmpMtf( pA
->GetGDIMetaFile() );
415 const Point
& rPos
= pA
->GetPoint();
416 const Size
& rSize
= pA
->GetSize();
417 const Gradient
& rTransparenceGradient
= pA
->GetGradient();
419 // special case constant alpha value
420 if( rTransparenceGradient
.GetStartColor() == rTransparenceGradient
.GetEndColor() )
422 const Color
aTransCol( rTransparenceGradient
.GetStartColor() );
423 const sal_uInt16 nTransPercent
= aTransCol
.GetLuminance() * 100 / 255;
424 m_rOuterFace
.BeginTransparencyGroup();
425 playMetafile( aTmpMtf
, nullptr, i_rContext
, pDummyVDev
);
426 m_rOuterFace
.EndTransparencyGroup( tools::Rectangle( rPos
, rSize
), nTransPercent
);
430 const Size
aDstSizeTwip( pDummyVDev
->PixelToLogic( pDummyVDev
->LogicToPixel( rSize
), MapUnit::MapTwip
) );
432 // i#115962# Always use at least 300 DPI for bitmap conversion of transparence gradients,
433 // else the quality is not acceptable (see bugdoc as example)
434 sal_Int32
nMaxBmpDPI(300);
436 if( i_rContext
.m_nMaxImageResolution
> 50 )
438 if ( nMaxBmpDPI
> i_rContext
.m_nMaxImageResolution
)
439 nMaxBmpDPI
= i_rContext
.m_nMaxImageResolution
;
441 const sal_Int32 nPixelX
= (sal_Int32
)((double)aDstSizeTwip
.Width() * (double)nMaxBmpDPI
/ 1440.0);
442 const sal_Int32 nPixelY
= (sal_Int32
)((double)aDstSizeTwip
.Height() * (double)nMaxBmpDPI
/ 1440.0);
443 if ( nPixelX
&& nPixelY
)
445 Size
aDstSizePixel( nPixelX
, nPixelY
);
446 ScopedVclPtrInstance
<VirtualDevice
> xVDev
;
447 if( xVDev
->SetOutputSizePixel( aDstSizePixel
) )
449 Bitmap aPaint
, aMask
;
453 MapMode
aMapMode( pDummyVDev
->GetMapMode() );
454 aMapMode
.SetOrigin( aPoint
);
455 xVDev
->SetMapMode( aMapMode
);
456 Size
aDstSize( xVDev
->PixelToLogic( aDstSizePixel
) );
458 Point
aMtfOrigin( aTmpMtf
.GetPrefMapMode().GetOrigin() );
459 if ( aMtfOrigin
.X() || aMtfOrigin
.Y() )
460 aTmpMtf
.Move( -aMtfOrigin
.X(), -aMtfOrigin
.Y() );
461 double fScaleX
= (double)aDstSize
.Width() / (double)aTmpMtf
.GetPrefSize().Width();
462 double fScaleY
= (double)aDstSize
.Height() / (double)aTmpMtf
.GetPrefSize().Height();
463 if( fScaleX
!= 1.0 || fScaleY
!= 1.0 )
464 aTmpMtf
.Scale( fScaleX
, fScaleY
);
465 aTmpMtf
.SetPrefMapMode( aMapMode
);
467 // create paint bitmap
469 aTmpMtf
.Play( xVDev
.get(), aPoint
, aDstSize
);
472 xVDev
->EnableMapMode( false );
473 aPaint
= xVDev
->GetBitmap( aPoint
, aDstSizePixel
);
474 xVDev
->EnableMapMode();
476 // create mask bitmap
477 xVDev
->SetLineColor( COL_BLACK
);
478 xVDev
->SetFillColor( COL_BLACK
);
479 xVDev
->DrawRect( tools::Rectangle( aPoint
, aDstSize
) );
480 xVDev
->SetDrawMode( DrawModeFlags::WhiteLine
| DrawModeFlags::WhiteFill
| DrawModeFlags::WhiteText
|
481 DrawModeFlags::WhiteBitmap
| DrawModeFlags::WhiteGradient
);
483 aTmpMtf
.Play( xVDev
.get(), aPoint
, aDstSize
);
485 xVDev
->EnableMapMode( false );
486 aMask
= xVDev
->GetBitmap( aPoint
, aDstSizePixel
);
487 xVDev
->EnableMapMode();
489 // create alpha mask from gradient
490 xVDev
->SetDrawMode( DrawModeFlags::GrayGradient
);
491 xVDev
->DrawGradient( tools::Rectangle( aPoint
, aDstSize
), rTransparenceGradient
);
492 xVDev
->SetDrawMode( DrawModeFlags::Default
);
493 xVDev
->EnableMapMode( false );
494 xVDev
->DrawMask( aPoint
, aDstSizePixel
, aMask
, Color( COL_WHITE
) );
495 aAlpha
= xVDev
->GetBitmap( aPoint
, aDstSizePixel
);
497 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
498 implWriteBitmapEx( rPos
, rSize
, BitmapEx( aPaint
, aAlpha
), aGraphic
, pDummyVDev
, i_rContext
);
505 case( MetaActionType::EPS
):
507 const MetaEPSAction
* pA
= static_cast<const MetaEPSAction
*>(pAction
);
508 const GDIMetaFile
aSubstitute( pA
->GetSubstitute() );
513 MapMode
aMapMode( aSubstitute
.GetPrefMapMode() );
514 Size
aOutSize( OutputDevice::LogicToLogic( pA
->GetSize(), pDummyVDev
->GetMapMode(), aMapMode
) );
515 aMapMode
.SetScaleX( Fraction( aOutSize
.Width(), aSubstitute
.GetPrefSize().Width() ) );
516 aMapMode
.SetScaleY( Fraction( aOutSize
.Height(), aSubstitute
.GetPrefSize().Height() ) );
517 aMapMode
.SetOrigin( OutputDevice::LogicToLogic( pA
->GetPoint(), pDummyVDev
->GetMapMode(), aMapMode
) );
519 m_rOuterFace
.SetMapMode( aMapMode
);
520 pDummyVDev
->SetMapMode( aMapMode
);
521 playMetafile( aSubstitute
, nullptr, i_rContext
, pDummyVDev
);
527 case( MetaActionType::COMMENT
):
528 if( ! i_rContext
.m_bTransparenciesWereRemoved
)
530 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
532 if( pA
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
534 const MetaGradientExAction
* pGradAction
= nullptr;
537 while( !bDone
&& ( ++i
< nCount
) )
539 pAction
= aMtf
.GetAction( i
);
541 if( pAction
->GetType() == MetaActionType::GRADIENTEX
)
542 pGradAction
= static_cast<const MetaGradientExAction
*>(pAction
);
543 else if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
544 ( static_cast<const MetaCommentAction
*>(pAction
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END")) )
552 if (lcl_canUsePDFAxialShading(pGradAction
->GetGradient()))
554 m_rOuterFace
.DrawGradient( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient() );
558 implWriteGradient( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient(), pDummyVDev
, i_rContext
);
564 const sal_uInt8
* pData
= pA
->GetData();
567 SvMemoryStream
aMemStm( const_cast<sal_uInt8
*>(pData
), pA
->GetDataSize(), StreamMode::READ
);
568 bool bSkipSequence
= false;
571 if( pA
->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
573 sSeqEnd
= OString("XPATHSTROKE_SEQ_END");
574 SvtGraphicStroke aStroke
;
575 ReadSvtGraphicStroke( aMemStm
, aStroke
);
577 tools::Polygon aPath
;
578 aStroke
.getPath( aPath
);
580 tools::PolyPolygon aStartArrow
;
581 tools::PolyPolygon aEndArrow
;
582 double fTransparency( aStroke
.getTransparency() );
583 double fStrokeWidth( aStroke
.getStrokeWidth() );
584 SvtGraphicStroke::DashArray aDashArray
;
586 aStroke
.getStartArrow( aStartArrow
);
587 aStroke
.getEndArrow( aEndArrow
);
588 aStroke
.getDashArray( aDashArray
);
590 bSkipSequence
= true;
591 if ( aStartArrow
.Count() || aEndArrow
.Count() )
592 bSkipSequence
= false;
593 if ( aDashArray
.size() && ( fStrokeWidth
!= 0.0 ) && ( fTransparency
== 0.0 ) )
594 bSkipSequence
= false;
597 PDFWriter::ExtLineInfo aInfo
;
598 aInfo
.m_fLineWidth
= fStrokeWidth
;
599 aInfo
.m_fTransparency
= fTransparency
;
600 aInfo
.m_fMiterLimit
= aStroke
.getMiterLimit();
601 switch( aStroke
.getCapType() )
604 case SvtGraphicStroke::capButt
: aInfo
.m_eCap
= PDFWriter::capButt
;break;
605 case SvtGraphicStroke::capRound
: aInfo
.m_eCap
= PDFWriter::capRound
;break;
606 case SvtGraphicStroke::capSquare
: aInfo
.m_eCap
= PDFWriter::capSquare
;break;
608 switch( aStroke
.getJoinType() )
611 case SvtGraphicStroke::joinMiter
: aInfo
.m_eJoin
= PDFWriter::joinMiter
;break;
612 case SvtGraphicStroke::joinRound
: aInfo
.m_eJoin
= PDFWriter::joinRound
;break;
613 case SvtGraphicStroke::joinBevel
: aInfo
.m_eJoin
= PDFWriter::joinBevel
;break;
614 case SvtGraphicStroke::joinNone
:
615 aInfo
.m_eJoin
= PDFWriter::joinMiter
;
616 aInfo
.m_fMiterLimit
= 0.0;
619 aInfo
.m_aDashArray
= aDashArray
;
621 if(SvtGraphicStroke::joinNone
== aStroke
.getJoinType()
622 && fStrokeWidth
> 0.0)
624 // emulate no edge rounding by handling single edges
625 const sal_uInt16
nPoints(aPath
.GetSize());
626 const bool bCurve(aPath
.HasFlags());
628 for(sal_uInt16
a(0); a
+ 1 < nPoints
; a
++)
631 && PolyFlags::Normal
!= aPath
.GetFlags(a
+ 1)
633 && PolyFlags::Normal
!= aPath
.GetFlags(a
+ 2)
636 const tools::Polygon
aSnippet(4,
637 aPath
.GetConstPointAry() + a
,
638 aPath
.GetConstFlagAry() + a
);
639 m_rOuterFace
.DrawPolyLine( aSnippet
, aInfo
);
644 const tools::Polygon
aSnippet(2,
645 aPath
.GetConstPointAry() + a
);
646 m_rOuterFace
.DrawPolyLine( aSnippet
, aInfo
);
652 m_rOuterFace
.DrawPolyLine( aPath
, aInfo
);
656 else if ( pA
->GetComment() == "XPATHFILL_SEQ_BEGIN" )
658 sSeqEnd
= OString("XPATHFILL_SEQ_END");
659 SvtGraphicFill aFill
;
660 ReadSvtGraphicFill( aMemStm
, aFill
);
662 if ( ( aFill
.getFillType() == SvtGraphicFill::fillSolid
) && ( aFill
.getFillRule() == SvtGraphicFill::fillEvenOdd
) )
664 double fTransparency
= aFill
.getTransparency();
665 if ( fTransparency
== 0.0 )
667 tools::PolyPolygon aPath
;
668 aFill
.getPath( aPath
);
670 bSkipSequence
= true;
671 m_rOuterFace
.DrawPolyPolygon( aPath
);
673 else if ( fTransparency
== 1.0 )
674 bSkipSequence
= true;
676 /* #i81548# removing optimization for fill textures, because most of the texture settings are not
677 exported properly. In OpenOffice 3.1 the drawing layer will support graphic primitives, then it
678 will not be a problem to optimize the filltexture export. But for wysiwyg is more important than
680 else if( aFill.getFillType() == SvtGraphicFill::fillTexture && aFill.isTiling() )
682 sal_Int32 nPattern = mnCachePatternId;
683 Graphic aPatternGraphic;
684 aFill.getGraphic( aPatternGraphic );
685 bool bUseCache = false;
686 SvtGraphicFill::Transform aPatTransform;
687 aFill.getTransform( aPatTransform );
689 if( mnCachePatternId >= 0 )
691 SvtGraphicFill::Transform aCacheTransform;
692 maCacheFill.getTransform( aCacheTransform );
693 if( aCacheTransform.matrix[0] == aPatTransform.matrix[0] &&
694 aCacheTransform.matrix[1] == aPatTransform.matrix[1] &&
695 aCacheTransform.matrix[2] == aPatTransform.matrix[2] &&
696 aCacheTransform.matrix[3] == aPatTransform.matrix[3] &&
697 aCacheTransform.matrix[4] == aPatTransform.matrix[4] &&
698 aCacheTransform.matrix[5] == aPatTransform.matrix[5]
701 Graphic aCacheGraphic;
702 maCacheFill.getGraphic( aCacheGraphic );
703 if( aCacheGraphic == aPatternGraphic )
711 // paint graphic to metafile
712 GDIMetaFile aPattern;
713 pDummyVDev->SetConnectMetaFile( &aPattern );
715 pDummyVDev->SetMapMode( aPatternGraphic.GetPrefMapMode() );
717 aPatternGraphic.Draw( &rDummyVDev, Point( 0, 0 ) );
719 pDummyVDev->SetConnectMetaFile( NULL );
720 aPattern.WindStart();
722 MapMode aPatternMapMode( aPatternGraphic.GetPrefMapMode() );
723 // prepare pattern from metafile
724 Size aPrefSize( aPatternGraphic.GetPrefSize() );
725 // FIXME: this magic -1 shouldn't be necessary
726 aPrefSize.Width() -= 1;
727 aPrefSize.Height() -= 1;
728 aPrefSize = m_rOuterFace.GetReferenceDevice()->
729 LogicToLogic( aPrefSize,
731 &m_rOuterFace.GetReferenceDevice()->GetMapMode() );
732 // build bounding rectangle of pattern
733 Rectangle aBound( Point( 0, 0 ), aPrefSize );
734 m_rOuterFace.BeginPattern( aBound );
737 m_rOuterFace.SetMapMode( aPatternMapMode );
738 pDummyVDev->SetMapMode( aPatternMapMode );
739 ImplWriteActions( m_rOuterFace, NULL, aPattern, rDummyVDev );
743 nPattern = m_rOuterFace.EndPattern( aPatTransform );
745 // try some caching and reuse pattern
746 mnCachePatternId = nPattern;
750 // draw polypolygon with pattern fill
751 tools::PolyPolygon aPath;
752 aFill.getPath( aPath );
753 m_rOuterFace.DrawPolyPolygon( aPath, nPattern, aFill.getFillRule() == SvtGraphicFill::fillEvenOdd );
755 bSkipSequence = true;
761 while( ++i
< nCount
)
763 pAction
= aMtf
.GetAction( i
);
764 if ( pAction
->GetType() == MetaActionType::COMMENT
)
766 OString
sComment( static_cast<const MetaCommentAction
*>(pAction
)->GetComment() );
767 if (sComment
== sSeqEnd
)
771 // the replacement action for stroke is a filled rectangle
772 // the set fillcolor of the replacement is part of the graphics
773 // state and must not be skipped
774 else if( pAction
->GetType() == MetaActionType::FILLCOLOR
)
776 const MetaFillColorAction
* pMA
= static_cast<const MetaFillColorAction
*>(pAction
);
777 if( pMA
->IsSetting() )
778 m_rOuterFace
.SetFillColor( pMA
->GetColor() );
780 m_rOuterFace
.SetFillColor();
789 case( MetaActionType::BMP
):
791 const MetaBmpAction
* pA
= static_cast<const MetaBmpAction
*>(pAction
);
792 BitmapEx
aBitmapEx( pA
->GetBitmap() );
793 Size
aSize( OutputDevice::LogicToLogic( aBitmapEx
.GetPrefSize(),
794 aBitmapEx
.GetPrefMapMode(), pDummyVDev
->GetMapMode() ) );
795 if( ! ( aSize
.Width() && aSize
.Height() ) )
796 aSize
= pDummyVDev
->PixelToLogic( aBitmapEx
.GetSizePixel() );
798 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
799 implWriteBitmapEx( pA
->GetPoint(), aSize
, aBitmapEx
, aGraphic
, pDummyVDev
, i_rContext
);
803 case( MetaActionType::BMPSCALE
):
805 const MetaBmpScaleAction
* pA
= static_cast<const MetaBmpScaleAction
*>(pAction
);
806 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
807 implWriteBitmapEx( pA
->GetPoint(), pA
->GetSize(), BitmapEx( pA
->GetBitmap() ), aGraphic
, pDummyVDev
, i_rContext
);
811 case( MetaActionType::BMPSCALEPART
):
813 const MetaBmpScalePartAction
* pA
= static_cast<const MetaBmpScalePartAction
*>(pAction
);
814 BitmapEx
aBitmapEx( pA
->GetBitmap() );
815 aBitmapEx
.Crop( tools::Rectangle( pA
->GetSrcPoint(), pA
->GetSrcSize() ) );
816 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
817 implWriteBitmapEx( pA
->GetDestPoint(), pA
->GetDestSize(), aBitmapEx
, aGraphic
, pDummyVDev
, i_rContext
);
821 case( MetaActionType::BMPEX
):
823 const MetaBmpExAction
* pA
= static_cast<const MetaBmpExAction
*>(pAction
);
824 BitmapEx
aBitmapEx( pA
->GetBitmapEx() );
825 Size
aSize( OutputDevice::LogicToLogic( aBitmapEx
.GetPrefSize(),
826 aBitmapEx
.GetPrefMapMode(), pDummyVDev
->GetMapMode() ) );
827 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
828 implWriteBitmapEx( pA
->GetPoint(), aSize
, aBitmapEx
, aGraphic
, pDummyVDev
, i_rContext
);
832 case( MetaActionType::BMPEXSCALE
):
834 const MetaBmpExScaleAction
* pA
= static_cast<const MetaBmpExScaleAction
*>(pAction
);
835 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
836 implWriteBitmapEx( pA
->GetPoint(), pA
->GetSize(), pA
->GetBitmapEx(), aGraphic
, pDummyVDev
, i_rContext
);
840 case( MetaActionType::BMPEXSCALEPART
):
842 const MetaBmpExScalePartAction
* pA
= static_cast<const MetaBmpExScalePartAction
*>(pAction
);
843 BitmapEx
aBitmapEx( pA
->GetBitmapEx() );
844 aBitmapEx
.Crop( tools::Rectangle( pA
->GetSrcPoint(), pA
->GetSrcSize() ) );
845 Graphic aGraphic
= i_pOutDevData
? i_pOutDevData
->GetCurrentGraphic() : Graphic();
846 implWriteBitmapEx( pA
->GetDestPoint(), pA
->GetDestSize(), aBitmapEx
, aGraphic
, pDummyVDev
, i_rContext
);
850 case( MetaActionType::MASK
):
851 case( MetaActionType::MASKSCALE
):
852 case( MetaActionType::MASKSCALEPART
):
854 SAL_WARN( "vcl", "MetaMask...Action not supported yet" );
858 case( MetaActionType::TEXT
):
860 const MetaTextAction
* pA
= static_cast<const MetaTextAction
*>(pAction
);
861 m_rOuterFace
.DrawText( pA
->GetPoint(), pA
->GetText().copy( pA
->GetIndex(), std::min
<sal_Int32
>(pA
->GetText().getLength() - pA
->GetIndex(), pA
->GetLen()) ) );
865 case( MetaActionType::TEXTRECT
):
867 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
868 m_rOuterFace
.DrawText( pA
->GetRect(), pA
->GetText(), pA
->GetStyle() );
872 case( MetaActionType::TEXTARRAY
):
874 const MetaTextArrayAction
* pA
= static_cast<const MetaTextArrayAction
*>(pAction
);
875 m_rOuterFace
.DrawTextArray( pA
->GetPoint(), pA
->GetText(), pA
->GetDXArray(), pA
->GetIndex(), pA
->GetLen() );
879 case( MetaActionType::STRETCHTEXT
):
881 const MetaStretchTextAction
* pA
= static_cast<const MetaStretchTextAction
*>(pAction
);
882 m_rOuterFace
.DrawStretchText( pA
->GetPoint(), pA
->GetWidth(), pA
->GetText(), pA
->GetIndex(), pA
->GetLen() );
886 case( MetaActionType::TEXTLINE
):
888 const MetaTextLineAction
* pA
= static_cast<const MetaTextLineAction
*>(pAction
);
889 m_rOuterFace
.DrawTextLine( pA
->GetStartPoint(), pA
->GetWidth(), pA
->GetStrikeout(), pA
->GetUnderline(), pA
->GetOverline() );
894 case( MetaActionType::CLIPREGION
):
896 const MetaClipRegionAction
* pA
= static_cast<const MetaClipRegionAction
*>(pAction
);
898 if( pA
->IsClipping() )
900 if( pA
->GetRegion().IsEmpty() )
901 m_rOuterFace
.SetClipRegion( basegfx::B2DPolyPolygon() );
904 vcl::Region
aReg( pA
->GetRegion() );
905 m_rOuterFace
.SetClipRegion( aReg
.GetAsB2DPolyPolygon() );
909 m_rOuterFace
.SetClipRegion();
913 case( MetaActionType::ISECTRECTCLIPREGION
):
915 const MetaISectRectClipRegionAction
* pA
= static_cast<const MetaISectRectClipRegionAction
*>(pAction
);
916 m_rOuterFace
.IntersectClipRegion( pA
->GetRect() );
920 case( MetaActionType::ISECTREGIONCLIPREGION
):
922 const MetaISectRegionClipRegionAction
* pA
= static_cast<const MetaISectRegionClipRegionAction
*>(pAction
);
923 vcl::Region
aReg( pA
->GetRegion() );
924 m_rOuterFace
.IntersectClipRegion( aReg
.GetAsB2DPolyPolygon() );
928 case( MetaActionType::MOVECLIPREGION
):
930 const MetaMoveClipRegionAction
* pA
= static_cast<const MetaMoveClipRegionAction
*>(pAction
);
931 m_rOuterFace
.MoveClipRegion( pA
->GetHorzMove(), pA
->GetVertMove() );
935 case( MetaActionType::MAPMODE
):
937 const_cast< MetaAction
* >( pAction
)->Execute( pDummyVDev
);
938 m_rOuterFace
.SetMapMode( pDummyVDev
->GetMapMode() );
942 case( MetaActionType::LINECOLOR
):
944 const MetaLineColorAction
* pA
= static_cast<const MetaLineColorAction
*>(pAction
);
946 if( pA
->IsSetting() )
947 m_rOuterFace
.SetLineColor( pA
->GetColor() );
949 m_rOuterFace
.SetLineColor();
953 case( MetaActionType::FILLCOLOR
):
955 const MetaFillColorAction
* pA
= static_cast<const MetaFillColorAction
*>(pAction
);
957 if( pA
->IsSetting() )
958 m_rOuterFace
.SetFillColor( pA
->GetColor() );
960 m_rOuterFace
.SetFillColor();
964 case( MetaActionType::TEXTLINECOLOR
):
966 const MetaTextLineColorAction
* pA
= static_cast<const MetaTextLineColorAction
*>(pAction
);
968 if( pA
->IsSetting() )
969 m_rOuterFace
.SetTextLineColor( pA
->GetColor() );
971 m_rOuterFace
.SetTextLineColor();
975 case( MetaActionType::OVERLINECOLOR
):
977 const MetaOverlineColorAction
* pA
= static_cast<const MetaOverlineColorAction
*>(pAction
);
979 if( pA
->IsSetting() )
980 m_rOuterFace
.SetOverlineColor( pA
->GetColor() );
982 m_rOuterFace
.SetOverlineColor();
986 case( MetaActionType::TEXTFILLCOLOR
):
988 const MetaTextFillColorAction
* pA
= static_cast<const MetaTextFillColorAction
*>(pAction
);
990 if( pA
->IsSetting() )
991 m_rOuterFace
.SetTextFillColor( pA
->GetColor() );
993 m_rOuterFace
.SetTextFillColor();
997 case( MetaActionType::TEXTCOLOR
):
999 const MetaTextColorAction
* pA
= static_cast<const MetaTextColorAction
*>(pAction
);
1000 m_rOuterFace
.SetTextColor( pA
->GetColor() );
1004 case( MetaActionType::TEXTALIGN
):
1006 const MetaTextAlignAction
* pA
= static_cast<const MetaTextAlignAction
*>(pAction
);
1007 m_rOuterFace
.SetTextAlign( pA
->GetTextAlign() );
1011 case( MetaActionType::FONT
):
1013 const MetaFontAction
* pA
= static_cast<const MetaFontAction
*>(pAction
);
1014 m_rOuterFace
.SetFont( pA
->GetFont() );
1018 case( MetaActionType::PUSH
):
1020 const MetaPushAction
* pA
= static_cast<const MetaPushAction
*>(pAction
);
1022 pDummyVDev
->Push( pA
->GetFlags() );
1023 m_rOuterFace
.Push( pA
->GetFlags() );
1027 case( MetaActionType::POP
):
1034 case( MetaActionType::LAYOUTMODE
):
1036 const MetaLayoutModeAction
* pA
= static_cast<const MetaLayoutModeAction
*>(pAction
);
1037 m_rOuterFace
.SetLayoutMode( pA
->GetLayoutMode() );
1041 case MetaActionType::TEXTLANGUAGE
:
1043 const MetaTextLanguageAction
* pA
= static_cast<const MetaTextLanguageAction
*>(pAction
);
1044 m_rOuterFace
.SetDigitLanguage( pA
->GetTextLanguage() );
1048 case( MetaActionType::WALLPAPER
):
1050 const MetaWallpaperAction
* pA
= static_cast<const MetaWallpaperAction
*>(pAction
);
1051 m_rOuterFace
.DrawWallpaper( pA
->GetRect(), pA
->GetWallpaper() );
1055 case( MetaActionType::RASTEROP
):
1057 // !!! >>> we don't want to support this actions
1061 case( MetaActionType::REFPOINT
):
1063 // !!! >>> we don't want to support this actions
1068 // #i24604# Made assertion fire only once per
1069 // metafile. The asserted actions here are all
1071 if( !bAssertionFired
)
1073 bAssertionFired
= true;
1074 SAL_WARN( "vcl", "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered " << (int)nType
);
1083 // Encryption methods
1085 /* a crutch to transport an rtlDigest safely though UNO API
1086 this is needed for the PDF export dialog, which otherwise would have to pass
1087 clear text passwords down till they can be used in PDFWriter. Unfortunately
1088 the MD5 sum of the password (which is needed to create the PDF encryption key)
1089 is not sufficient, since an rtl MD5 digest cannot be created in an arbitrary state
1090 which would be needed in PDFWriterImpl::computeEncryptionKey.
1092 class EncHashTransporter
: public cppu::WeakImplHelper
< css::beans::XMaterialHolder
>
1094 rtlDigest maUDigest
;
1096 std::vector
< sal_uInt8
> maOValue
;
1098 static std::map
< sal_IntPtr
, EncHashTransporter
* > sTransporters
;
1100 EncHashTransporter()
1101 : maUDigest( rtl_digest_createMD5() )
1103 maID
= reinterpret_cast< sal_IntPtr
>(this);
1104 while( sTransporters
.find( maID
) != sTransporters
.end() ) // paranoia mode
1106 sTransporters
[ maID
] = this;
1109 virtual ~EncHashTransporter() override
1111 sTransporters
.erase( maID
);
1113 rtl_digest_destroyMD5( maUDigest
);
1114 SAL_INFO( "vcl", "EncHashTransporter freed" );
1117 rtlDigest
getUDigest() const { return maUDigest
; };
1118 std::vector
< sal_uInt8
>& getOValue() { return maOValue
; }
1123 rtl_digest_destroyMD5( maUDigest
);
1124 maUDigest
= nullptr;
1129 virtual uno::Any SAL_CALL
getMaterial() override
1131 return uno::makeAny( sal_Int64(maID
) );
1134 static EncHashTransporter
* getEncHashTransporter( const uno::Reference
< beans::XMaterialHolder
>& );
1138 std::map
< sal_IntPtr
, EncHashTransporter
* > EncHashTransporter::sTransporters
;
1140 EncHashTransporter
* EncHashTransporter::getEncHashTransporter( const uno::Reference
< beans::XMaterialHolder
>& xRef
)
1142 EncHashTransporter
* pResult
= nullptr;
1145 uno::Any
aMat( xRef
->getMaterial() );
1149 std::map
< sal_IntPtr
, EncHashTransporter
* >::iterator it
= sTransporters
.find( static_cast<sal_IntPtr
>(nMat
) );
1150 if( it
!= sTransporters
.end() )
1151 pResult
= it
->second
;
1157 bool PDFWriterImpl::checkEncryptionBufferSize( sal_Int32 newSize
)
1159 if( m_nEncryptionBufferSize
< newSize
)
1161 /* reallocate the buffer, the used function allocate as rtl_allocateMemory
1162 if the pointer parameter is NULL */
1163 m_pEncryptionBuffer
= static_cast<sal_uInt8
*>(rtl_reallocateMemory( m_pEncryptionBuffer
, newSize
));
1164 if( m_pEncryptionBuffer
)
1165 m_nEncryptionBufferSize
= newSize
;
1167 m_nEncryptionBufferSize
= 0;
1169 return ( m_nEncryptionBufferSize
!= 0 );
1172 void PDFWriterImpl::checkAndEnableStreamEncryption( sal_Int32 nObject
)
1174 if( m_aContext
.Encryption
.Encrypt() )
1176 m_bEncryptThisStream
= true;
1177 sal_Int32 i
= m_nKeyLength
;
1178 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)nObject
;
1179 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)( nObject
>> 8 );
1180 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)( nObject
>> 16 );
1181 // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1183 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1184 // the i+2 to take into account the generation number, always zero
1185 rtl_digest_MD5( &m_aContext
.Encryption
.EncryptionKey
[0], i
+2, nMD5Sum
, sizeof(nMD5Sum
) );
1186 // initialize the RC4 with the key
1187 // key length: see algorithm 3.1, step 4: (N+5) max 16
1188 rtl_cipher_initARCFOUR( m_aCipher
, rtl_Cipher_DirectionEncode
, nMD5Sum
, m_nRC4KeyLength
, nullptr, 0 );
1192 void PDFWriterImpl::enableStringEncryption( sal_Int32 nObject
)
1194 if( m_aContext
.Encryption
.Encrypt() )
1196 sal_Int32 i
= m_nKeyLength
;
1197 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)nObject
;
1198 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)( nObject
>> 8 );
1199 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)( nObject
>> 16 );
1200 // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1202 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1203 // the i+2 to take into account the generation number, always zero
1204 rtl_digest_MD5( &m_aContext
.Encryption
.EncryptionKey
[0], i
+2, nMD5Sum
, sizeof(nMD5Sum
) );
1205 // initialize the RC4 with the key
1206 // key length: see algorithm 3.1, step 4: (N+5) max 16
1207 rtl_cipher_initARCFOUR( m_aCipher
, rtl_Cipher_DirectionEncode
, nMD5Sum
, m_nRC4KeyLength
, nullptr, 0 );
1211 /* init the encryption engine
1212 1. init the document id, used both for building the document id and for building the encryption key(s)
1213 2. build the encryption key following algorithms described in the PDF specification
1215 uno::Reference
< beans::XMaterialHolder
> PDFWriterImpl::initEncryption( const OUString
& i_rOwnerPassword
,
1216 const OUString
& i_rUserPassword
,
1220 uno::Reference
< beans::XMaterialHolder
> xResult
;
1221 if( !i_rOwnerPassword
.isEmpty() || !i_rUserPassword
.isEmpty() )
1223 EncHashTransporter
* pTransporter
= new EncHashTransporter
;
1224 xResult
= pTransporter
;
1226 // get padded passwords
1227 sal_uInt8 aPadUPW
[ENCRYPTED_PWD_SIZE
], aPadOPW
[ENCRYPTED_PWD_SIZE
];
1228 padPassword( i_rOwnerPassword
.isEmpty() ? i_rUserPassword
: i_rOwnerPassword
, aPadOPW
);
1229 padPassword( i_rUserPassword
, aPadUPW
);
1230 sal_Int32 nKeyLength
= SECUR_40BIT_KEY
;
1232 nKeyLength
= SECUR_128BIT_KEY
;
1234 if( computeODictionaryValue( aPadOPW
, aPadUPW
, pTransporter
->getOValue(), nKeyLength
) )
1236 rtlDigest aDig
= pTransporter
->getUDigest();
1237 if( rtl_digest_updateMD5( aDig
, aPadUPW
, ENCRYPTED_PWD_SIZE
) != rtl_Digest_E_None
)
1243 // trash temporary padded cleartext PWDs
1244 rtl_secureZeroMemory (aPadOPW
, sizeof(aPadOPW
));
1245 rtl_secureZeroMemory (aPadUPW
, sizeof(aPadUPW
));
1250 bool PDFWriterImpl::prepareEncryption( const uno::Reference
< beans::XMaterialHolder
>& xEnc
)
1252 bool bSuccess
= false;
1253 EncHashTransporter
* pTransporter
= EncHashTransporter::getEncHashTransporter( xEnc
);
1256 sal_Int32 nKeyLength
= 0, nRC4KeyLength
= 0;
1257 sal_Int32 nAccessPermissions
= computeAccessPermissions( m_aContext
.Encryption
, nKeyLength
, nRC4KeyLength
);
1258 m_aContext
.Encryption
.OValue
= pTransporter
->getOValue();
1259 bSuccess
= computeUDictionaryValue( pTransporter
, m_aContext
.Encryption
, nKeyLength
, nAccessPermissions
);
1263 m_aContext
.Encryption
.OValue
.clear();
1264 m_aContext
.Encryption
.UValue
.clear();
1265 m_aContext
.Encryption
.EncryptionKey
.clear();
1270 sal_Int32
PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties
& i_rProperties
,
1271 sal_Int32
& o_rKeyLength
, sal_Int32
& o_rRC4KeyLength
)
1274 2) compute the access permissions, in numerical form
1276 the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
1277 - for 40 bit security the unused bit must be set to 1, since they are not used
1278 - for 128 bit security the same bit must be preset to 0 and set later if needed
1279 according to the table 3.15, pdf v 1.4 */
1280 sal_Int32 nAccessPermissions
= 0xfffff0c0;
1282 /* check permissions for 40 bit security case */
1283 nAccessPermissions
|= ( i_rProperties
.CanPrintTheDocument
) ? 1 << 2 : 0;
1284 nAccessPermissions
|= ( i_rProperties
.CanModifyTheContent
) ? 1 << 3 : 0;
1285 nAccessPermissions
|= ( i_rProperties
.CanCopyOrExtract
) ? 1 << 4 : 0;
1286 nAccessPermissions
|= ( i_rProperties
.CanAddOrModify
) ? 1 << 5 : 0;
1287 o_rKeyLength
= SECUR_40BIT_KEY
;
1288 o_rRC4KeyLength
= SECUR_40BIT_KEY
+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5
1290 o_rKeyLength
= SECUR_128BIT_KEY
;
1291 o_rRC4KeyLength
= 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum
1292 // permitted value is 16
1293 nAccessPermissions
|= ( i_rProperties
.CanFillInteractive
) ? 1 << 8 : 0;
1294 nAccessPermissions
|= ( i_rProperties
.CanExtractForAccessibility
) ? 1 << 9 : 0;
1295 nAccessPermissions
|= ( i_rProperties
.CanAssemble
) ? 1 << 10 : 0;
1296 nAccessPermissions
|= ( i_rProperties
.CanPrintFull
) ? 1 << 11 : 0;
1297 return nAccessPermissions
;
1300 /*************************************************************
1301 begin i12626 methods
1303 Implements Algorithm 3.2, step 1 only
1305 void PDFWriterImpl::padPassword( const OUString
& i_rPassword
, sal_uInt8
* o_pPaddedPW
)
1307 // get ansi-1252 version of the password string CHECKIT ! i12626
1308 OString
aString( OUStringToOString( i_rPassword
, RTL_TEXTENCODING_MS_1252
) );
1310 //copy the string to the target
1311 sal_Int32 nToCopy
= ( aString
.getLength() < ENCRYPTED_PWD_SIZE
) ? aString
.getLength() : ENCRYPTED_PWD_SIZE
;
1312 sal_Int32 nCurrentChar
;
1314 for( nCurrentChar
= 0; nCurrentChar
< nToCopy
; nCurrentChar
++ )
1315 o_pPaddedPW
[nCurrentChar
] = (sal_uInt8
)( aString
[nCurrentChar
] );
1317 //pad it with standard byte string
1319 for( i
= nCurrentChar
, y
= 0 ; i
< ENCRYPTED_PWD_SIZE
; i
++, y
++ )
1320 o_pPaddedPW
[i
] = s_nPadString
[y
];
1323 /**********************************
1324 Algorithm 3.2 Compute the encryption key used
1326 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
1327 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
1328 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
1330 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
1333 bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter
* i_pTransporter
, vcl::PDFWriter::PDFEncryptionProperties
& io_rProperties
, sal_Int32 i_nAccessPermissions
)
1335 bool bSuccess
= true;
1336 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1338 // transporter contains an MD5 digest with the padded user password already
1339 rtlDigest aDigest
= i_pTransporter
->getUDigest();
1340 rtlDigestError nError
= rtl_Digest_E_None
;
1344 if( ! io_rProperties
.OValue
.empty() )
1345 nError
= rtl_digest_updateMD5( aDigest
, &io_rProperties
.OValue
[0] , sal_Int32(io_rProperties
.OValue
.size()) );
1351 nPerm
[0] = (sal_uInt8
)i_nAccessPermissions
;
1352 nPerm
[1] = (sal_uInt8
)( i_nAccessPermissions
>> 8 );
1353 nPerm
[2] = (sal_uInt8
)( i_nAccessPermissions
>> 16 );
1354 nPerm
[3] = (sal_uInt8
)( i_nAccessPermissions
>> 24 );
1356 if( nError
== rtl_Digest_E_None
)
1357 nError
= rtl_digest_updateMD5( aDigest
, nPerm
, sizeof( nPerm
) );
1359 //step 5, get the document ID, binary form
1360 if( nError
== rtl_Digest_E_None
)
1361 nError
= rtl_digest_updateMD5( aDigest
, &io_rProperties
.DocumentIdentifier
[0], sal_Int32(io_rProperties
.DocumentIdentifier
.size()) );
1363 if( nError
== rtl_Digest_E_None
)
1365 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1367 //step 6, only if 128 bit
1368 for( sal_Int32 i
= 0; i
< 50; i
++ )
1370 nError
= rtl_digest_updateMD5( aDigest
, &nMD5Sum
, sizeof( nMD5Sum
) );
1371 if( nError
!= rtl_Digest_E_None
)
1376 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1387 i_pTransporter
->invalidate();
1392 io_rProperties
.EncryptionKey
.resize( MAXIMUM_RC4_KEY_LENGTH
);
1393 for( sal_Int32 i
= 0; i
< MD5_DIGEST_SIZE
; i
++ )
1394 io_rProperties
.EncryptionKey
[i
] = nMD5Sum
[i
];
1397 io_rProperties
.EncryptionKey
.clear();
1402 /**********************************
1403 Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member
1404 the step numbers down here correspond to the ones in PDF v.1.4 specification
1406 bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8
* i_pPaddedOwnerPassword
,
1407 const sal_uInt8
* i_pPaddedUserPassword
,
1408 std::vector
< sal_uInt8
>& io_rOValue
,
1409 sal_Int32 i_nKeyLength
1412 bool bSuccess
= true;
1414 io_rOValue
.resize( ENCRYPTED_PWD_SIZE
);
1416 rtlDigest aDigest
= rtl_digest_createMD5();
1417 rtlCipher aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1418 if( aDigest
&& aCipher
)
1420 //step 1 already done, data is in i_pPaddedOwnerPassword
1423 rtlDigestError nError
= rtl_digest_updateMD5( aDigest
, i_pPaddedOwnerPassword
, ENCRYPTED_PWD_SIZE
);
1424 if( nError
== rtl_Digest_E_None
)
1426 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1428 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof(nMD5Sum
) );
1429 //step 3, only if 128 bit
1430 if( i_nKeyLength
== SECUR_128BIT_KEY
)
1433 for( i
= 0; i
< 50; i
++ )
1435 nError
= rtl_digest_updateMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1436 if( nError
!= rtl_Digest_E_None
)
1441 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1444 //Step 4, the key is in nMD5Sum
1445 //step 5 already done, data is in i_pPaddedUserPassword
1447 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1448 nMD5Sum
, i_nKeyLength
, nullptr, 0 );
1449 // encrypt the user password using the key set above
1450 rtl_cipher_encodeARCFOUR( aCipher
, i_pPaddedUserPassword
, ENCRYPTED_PWD_SIZE
, // the data to be encrypted
1451 &io_rOValue
[0], sal_Int32(io_rOValue
.size()) ); //encrypted data
1452 //Step 7, only if 128 bit
1453 if( i_nKeyLength
== SECUR_128BIT_KEY
)
1456 sal_uInt8 nLocalKey
[ SECUR_128BIT_KEY
]; // 16 = 128 bit key
1458 for( i
= 1; i
<= 19; i
++ ) // do it 19 times, start with 1
1460 for( y
= 0; y
< sizeof( nLocalKey
); y
++ )
1461 nLocalKey
[y
] = (sal_uInt8
)( nMD5Sum
[y
] ^ i
);
1463 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1464 nLocalKey
, SECUR_128BIT_KEY
, nullptr, 0 ); //destination data area, on init can be NULL
1465 rtl_cipher_encodeARCFOUR( aCipher
, &io_rOValue
[0], sal_Int32(io_rOValue
.size()), // the data to be encrypted
1466 &io_rOValue
[0], sal_Int32(io_rOValue
.size()) ); // encrypted data, can be the same as the input, encrypt "in place"
1467 //step 8, store in class data member
1478 rtl_digest_destroyMD5( aDigest
);
1480 rtl_cipher_destroyARCFOUR( aCipher
);
1487 /**********************************
1488 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)
1490 bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter
* i_pTransporter
,
1491 vcl::PDFWriter::PDFEncryptionProperties
& io_rProperties
,
1492 sal_Int32 i_nKeyLength
,
1493 sal_Int32 i_nAccessPermissions
1496 bool bSuccess
= true;
1498 io_rProperties
.UValue
.resize( ENCRYPTED_PWD_SIZE
);
1500 rtlDigest aDigest
= rtl_digest_createMD5();
1501 rtlCipher aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1502 if( aDigest
&& aCipher
)
1504 //step 1, common to both 3.4 and 3.5
1505 if( computeEncryptionKey( i_pTransporter
, io_rProperties
, i_nAccessPermissions
) )
1507 // prepare encryption key for object
1508 for( sal_Int32 i
= i_nKeyLength
, y
= 0; y
< 5 ; y
++ )
1509 io_rProperties
.EncryptionKey
[i
++] = 0;
1511 //or 3.5, for 128 bit security
1512 //step6, initialize the last 16 bytes of the encrypted user password to 0
1513 for(sal_uInt32 i
= MD5_DIGEST_SIZE
; i
< sal_uInt32(io_rProperties
.UValue
.size()); i
++)
1514 io_rProperties
.UValue
[i
] = 0;
1516 if (rtl_digest_updateMD5( aDigest
, s_nPadString
, sizeof( s_nPadString
) ) != rtl_Digest_E_None
1517 || rtl_digest_updateMD5( aDigest
, &io_rProperties
.DocumentIdentifier
[0], sal_Int32(io_rProperties
.DocumentIdentifier
.size()) ) != rtl_Digest_E_None
)
1522 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1523 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof(nMD5Sum
) );
1525 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1526 &io_rProperties
.EncryptionKey
[0], SECUR_128BIT_KEY
, nullptr, 0 ); //destination data area
1527 rtl_cipher_encodeARCFOUR( aCipher
, nMD5Sum
, sizeof( nMD5Sum
), // the data to be encrypted
1528 &io_rProperties
.UValue
[0], sizeof( nMD5Sum
) ); //encrypted data, stored in class data member
1531 sal_uInt8 nLocalKey
[SECUR_128BIT_KEY
];
1533 for( i
= 1; i
<= 19; i
++ ) // do it 19 times, start with 1
1535 for( y
= 0; y
< sizeof( nLocalKey
) ; y
++ )
1536 nLocalKey
[y
] = (sal_uInt8
)( io_rProperties
.EncryptionKey
[y
] ^ i
);
1538 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1539 nLocalKey
, SECUR_128BIT_KEY
, // key and key length
1540 nullptr, 0 ); //destination data area, on init can be NULL
1541 rtl_cipher_encodeARCFOUR( aCipher
, &io_rProperties
.UValue
[0], SECUR_128BIT_KEY
, // the data to be encrypted
1542 &io_rProperties
.UValue
[0], SECUR_128BIT_KEY
); // encrypted data, can be the same as the input, encrypt "in place"
1552 rtl_digest_destroyMD5( aDigest
);
1554 rtl_cipher_destroyARCFOUR( aCipher
);
1557 io_rProperties
.UValue
.clear();
1561 /* end i12626 methods */
1563 static const long unsetRun
[256] =
1565 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
1566 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
1567 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */
1568 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */
1569 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1570 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */
1571 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1572 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */
1573 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
1574 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
1575 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
1576 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
1577 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
1578 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
1579 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
1580 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */
1583 static const long setRun
[256] =
1585 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
1586 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1587 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
1588 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
1589 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
1590 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
1591 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */
1592 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
1593 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */
1594 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */
1595 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */
1596 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */
1597 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */
1598 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */
1599 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
1600 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
1603 inline bool isSet( const Scanline i_pLine
, long i_nIndex
)
1605 return (i_pLine
[ i_nIndex
/8 ] & (0x80 >> (i_nIndex
&7))) != 0;
1608 long findBitRun( const Scanline i_pLine
, long i_nStartIndex
, long i_nW
, bool i_bSet
)
1610 if( i_nStartIndex
< 0 )
1613 long nIndex
= i_nStartIndex
;
1616 const sal_uInt8
* pByte
= static_cast<sal_uInt8
*>(i_pLine
) + (nIndex
/8);
1617 sal_uInt8 nByte
= *pByte
;
1619 // run up to byte boundary
1620 long nBitInByte
= (nIndex
& 7);
1623 sal_uInt8 nMask
= 0x80 >> nBitInByte
;
1624 while( nBitInByte
!= 8 )
1626 if( (nByte
& nMask
) != (i_bSet
? nMask
: 0) )
1627 return nIndex
< i_nW
? nIndex
: i_nW
;
1640 const long* pRunTable
;
1649 pRunTable
= unsetRun
;
1654 while( nByte
== nRunByte
)
1668 nIndex
+= pRunTable
[nByte
];
1671 return nIndex
< i_nW
? nIndex
: i_nW
;
1674 struct BitStreamState
1677 sal_uInt32 mnNextBitPos
;
1685 const sal_uInt8
& getByte() const { return mnBuffer
; }
1686 void flush() { mnNextBitPos
= 8; mnBuffer
= 0; }
1689 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength
, sal_uInt32 i_nCode
, BitStreamState
& io_rState
)
1691 while( i_nLength
> io_rState
.mnNextBitPos
)
1693 io_rState
.mnBuffer
|= static_cast<sal_uInt8
>( i_nCode
>> (i_nLength
- io_rState
.mnNextBitPos
) );
1694 i_nLength
-= io_rState
.mnNextBitPos
;
1695 writeBuffer( &io_rState
.getByte(), 1 );
1698 OSL_ASSERT( i_nLength
< 9 );
1699 static const unsigned int msbmask
[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1700 io_rState
.mnBuffer
|= static_cast<sal_uInt8
>( (i_nCode
& msbmask
[i_nLength
]) << (io_rState
.mnNextBitPos
- i_nLength
) );
1701 io_rState
.mnNextBitPos
-= i_nLength
;
1702 if( io_rState
.mnNextBitPos
== 0 )
1704 writeBuffer( &io_rState
.getByte(), 1 );
1711 sal_uInt32 mnEncodedPixels
;
1712 sal_uInt32 mnCodeBits
;
1716 static const PixelCode WhitePixelCodes
[] =
1718 { 0, 8, 0x35 }, // 0011 0101
1719 { 1, 6, 0x7 }, // 0001 11
1720 { 2, 4, 0x7 }, // 0111
1721 { 3, 4, 0x8 }, // 1000
1722 { 4, 4, 0xB }, // 1011
1723 { 5, 4, 0xC }, // 1100
1724 { 6, 4, 0xE }, // 1110
1725 { 7, 4, 0xF }, // 1111
1726 { 8, 5, 0x13 }, // 1001 1
1727 { 9, 5, 0x14 }, // 1010 0
1728 { 10, 5, 0x7 }, // 0011 1
1729 { 11, 5, 0x8 }, // 0100 0
1730 { 12, 6, 0x8 }, // 0010 00
1731 { 13, 6, 0x3 }, // 0000 11
1732 { 14, 6, 0x34 }, // 1101 00
1733 { 15, 6, 0x35 }, // 1101 01
1734 { 16, 6, 0x2A }, // 1010 10
1735 { 17, 6, 0x2B }, // 1010 11
1736 { 18, 7, 0x27 }, // 0100 111
1737 { 19, 7, 0xC }, // 0001 100
1738 { 20, 7, 0x8 }, // 0001 000
1739 { 21, 7, 0x17 }, // 0010 111
1740 { 22, 7, 0x3 }, // 0000 011
1741 { 23, 7, 0x4 }, // 0000 100
1742 { 24, 7, 0x28 }, // 0101 000
1743 { 25, 7, 0x2B }, // 0101 011
1744 { 26, 7, 0x13 }, // 0010 011
1745 { 27, 7, 0x24 }, // 0100 100
1746 { 28, 7, 0x18 }, // 0011 000
1747 { 29, 8, 0x2 }, // 0000 0010
1748 { 30, 8, 0x3 }, // 0000 0011
1749 { 31, 8, 0x1A }, // 0001 1010
1750 { 32, 8, 0x1B }, // 0001 1011
1751 { 33, 8, 0x12 }, // 0001 0010
1752 { 34, 8, 0x13 }, // 0001 0011
1753 { 35, 8, 0x14 }, // 0001 0100
1754 { 36, 8, 0x15 }, // 0001 0101
1755 { 37, 8, 0x16 }, // 0001 0110
1756 { 38, 8, 0x17 }, // 0001 0111
1757 { 39, 8, 0x28 }, // 0010 1000
1758 { 40, 8, 0x29 }, // 0010 1001
1759 { 41, 8, 0x2A }, // 0010 1010
1760 { 42, 8, 0x2B }, // 0010 1011
1761 { 43, 8, 0x2C }, // 0010 1100
1762 { 44, 8, 0x2D }, // 0010 1101
1763 { 45, 8, 0x4 }, // 0000 0100
1764 { 46, 8, 0x5 }, // 0000 0101
1765 { 47, 8, 0xA }, // 0000 1010
1766 { 48, 8, 0xB }, // 0000 1011
1767 { 49, 8, 0x52 }, // 0101 0010
1768 { 50, 8, 0x53 }, // 0101 0011
1769 { 51, 8, 0x54 }, // 0101 0100
1770 { 52, 8, 0x55 }, // 0101 0101
1771 { 53, 8, 0x24 }, // 0010 0100
1772 { 54, 8, 0x25 }, // 0010 0101
1773 { 55, 8, 0x58 }, // 0101 1000
1774 { 56, 8, 0x59 }, // 0101 1001
1775 { 57, 8, 0x5A }, // 0101 1010
1776 { 58, 8, 0x5B }, // 0101 1011
1777 { 59, 8, 0x4A }, // 0100 1010
1778 { 60, 8, 0x4B }, // 0100 1011
1779 { 61, 8, 0x32 }, // 0011 0010
1780 { 62, 8, 0x33 }, // 0011 0011
1781 { 63, 8, 0x34 }, // 0011 0100
1782 { 64, 5, 0x1B }, // 1101 1
1783 { 128, 5, 0x12 }, // 1001 0
1784 { 192, 6, 0x17 }, // 0101 11
1785 { 256, 7, 0x37 }, // 0110 111
1786 { 320, 8, 0x36 }, // 0011 0110
1787 { 384, 8, 0x37 }, // 0011 0111
1788 { 448, 8, 0x64 }, // 0110 0100
1789 { 512, 8, 0x65 }, // 0110 0101
1790 { 576, 8, 0x68 }, // 0110 1000
1791 { 640, 8, 0x67 }, // 0110 0111
1792 { 704, 9, 0xCC }, // 0110 0110 0
1793 { 768, 9, 0xCD }, // 0110 0110 1
1794 { 832, 9, 0xD2 }, // 0110 1001 0
1795 { 896, 9, 0xD3 }, // 0110 1001 1
1796 { 960, 9, 0xD4 }, // 0110 1010 0
1797 { 1024, 9, 0xD5 }, // 0110 1010 1
1798 { 1088, 9, 0xD6 }, // 0110 1011 0
1799 { 1152, 9, 0xD7 }, // 0110 1011 1
1800 { 1216, 9, 0xD8 }, // 0110 1100 0
1801 { 1280, 9, 0xD9 }, // 0110 1100 1
1802 { 1344, 9, 0xDA }, // 0110 1101 0
1803 { 1408, 9, 0xDB }, // 0110 1101 1
1804 { 1472, 9, 0x98 }, // 0100 1100 0
1805 { 1536, 9, 0x99 }, // 0100 1100 1
1806 { 1600, 9, 0x9A }, // 0100 1101 0
1807 { 1664, 6, 0x18 }, // 0110 00
1808 { 1728, 9, 0x9B }, // 0100 1101 1
1809 { 1792, 11, 0x8 }, // 0000 0001 000
1810 { 1856, 11, 0xC }, // 0000 0001 100
1811 { 1920, 11, 0xD }, // 0000 0001 101
1812 { 1984, 12, 0x12 }, // 0000 0001 0010
1813 { 2048, 12, 0x13 }, // 0000 0001 0011
1814 { 2112, 12, 0x14 }, // 0000 0001 0100
1815 { 2176, 12, 0x15 }, // 0000 0001 0101
1816 { 2240, 12, 0x16 }, // 0000 0001 0110
1817 { 2304, 12, 0x17 }, // 0000 0001 0111
1818 { 2368, 12, 0x1C }, // 0000 0001 1100
1819 { 2432, 12, 0x1D }, // 0000 0001 1101
1820 { 2496, 12, 0x1E }, // 0000 0001 1110
1821 { 2560, 12, 0x1F } // 0000 0001 1111
1824 static const PixelCode BlackPixelCodes
[] =
1826 { 0, 10, 0x37 }, // 0000 1101 11
1827 { 1, 3, 0x2 }, // 010
1828 { 2, 2, 0x3 }, // 11
1829 { 3, 2, 0x2 }, // 10
1830 { 4, 3, 0x3 }, // 011
1831 { 5, 4, 0x3 }, // 0011
1832 { 6, 4, 0x2 }, // 0010
1833 { 7, 5, 0x3 }, // 0001 1
1834 { 8, 6, 0x5 }, // 0001 01
1835 { 9, 6, 0x4 }, // 0001 00
1836 { 10, 7, 0x4 }, // 0000 100
1837 { 11, 7, 0x5 }, // 0000 101
1838 { 12, 7, 0x7 }, // 0000 111
1839 { 13, 8, 0x4 }, // 0000 0100
1840 { 14, 8, 0x7 }, // 0000 0111
1841 { 15, 9, 0x18 }, // 0000 1100 0
1842 { 16, 10, 0x17 }, // 0000 0101 11
1843 { 17, 10, 0x18 }, // 0000 0110 00
1844 { 18, 10, 0x8 }, // 0000 0010 00
1845 { 19, 11, 0x67 }, // 0000 1100 111
1846 { 20, 11, 0x68 }, // 0000 1101 000
1847 { 21, 11, 0x6C }, // 0000 1101 100
1848 { 22, 11, 0x37 }, // 0000 0110 111
1849 { 23, 11, 0x28 }, // 0000 0101 000
1850 { 24, 11, 0x17 }, // 0000 0010 111
1851 { 25, 11, 0x18 }, // 0000 0011 000
1852 { 26, 12, 0xCA }, // 0000 1100 1010
1853 { 27, 12, 0xCB }, // 0000 1100 1011
1854 { 28, 12, 0xCC }, // 0000 1100 1100
1855 { 29, 12, 0xCD }, // 0000 1100 1101
1856 { 30, 12, 0x68 }, // 0000 0110 1000
1857 { 31, 12, 0x69 }, // 0000 0110 1001
1858 { 32, 12, 0x6A }, // 0000 0110 1010
1859 { 33, 12, 0x6B }, // 0000 0110 1011
1860 { 34, 12, 0xD2 }, // 0000 1101 0010
1861 { 35, 12, 0xD3 }, // 0000 1101 0011
1862 { 36, 12, 0xD4 }, // 0000 1101 0100
1863 { 37, 12, 0xD5 }, // 0000 1101 0101
1864 { 38, 12, 0xD6 }, // 0000 1101 0110
1865 { 39, 12, 0xD7 }, // 0000 1101 0111
1866 { 40, 12, 0x6C }, // 0000 0110 1100
1867 { 41, 12, 0x6D }, // 0000 0110 1101
1868 { 42, 12, 0xDA }, // 0000 1101 1010
1869 { 43, 12, 0xDB }, // 0000 1101 1011
1870 { 44, 12, 0x54 }, // 0000 0101 0100
1871 { 45, 12, 0x55 }, // 0000 0101 0101
1872 { 46, 12, 0x56 }, // 0000 0101 0110
1873 { 47, 12, 0x57 }, // 0000 0101 0111
1874 { 48, 12, 0x64 }, // 0000 0110 0100
1875 { 49, 12, 0x65 }, // 0000 0110 0101
1876 { 50, 12, 0x52 }, // 0000 0101 0010
1877 { 51, 12, 0x53 }, // 0000 0101 0011
1878 { 52, 12, 0x24 }, // 0000 0010 0100
1879 { 53, 12, 0x37 }, // 0000 0011 0111
1880 { 54, 12, 0x38 }, // 0000 0011 1000
1881 { 55, 12, 0x27 }, // 0000 0010 0111
1882 { 56, 12, 0x28 }, // 0000 0010 1000
1883 { 57, 12, 0x58 }, // 0000 0101 1000
1884 { 58, 12, 0x59 }, // 0000 0101 1001
1885 { 59, 12, 0x2B }, // 0000 0010 1011
1886 { 60, 12, 0x2C }, // 0000 0010 1100
1887 { 61, 12, 0x5A }, // 0000 0101 1010
1888 { 62, 12, 0x66 }, // 0000 0110 0110
1889 { 63, 12, 0x67 }, // 0000 0110 0111
1890 { 64, 10, 0xF }, // 0000 0011 11
1891 { 128, 12, 0xC8 }, // 0000 1100 1000
1892 { 192, 12, 0xC9 }, // 0000 1100 1001
1893 { 256, 12, 0x5B }, // 0000 0101 1011
1894 { 320, 12, 0x33 }, // 0000 0011 0011
1895 { 384, 12, 0x34 }, // 0000 0011 0100
1896 { 448, 12, 0x35 }, // 0000 0011 0101
1897 { 512, 13, 0x6C }, // 0000 0011 0110 0
1898 { 576, 13, 0x6D }, // 0000 0011 0110 1
1899 { 640, 13, 0x4A }, // 0000 0010 0101 0
1900 { 704, 13, 0x4B }, // 0000 0010 0101 1
1901 { 768, 13, 0x4C }, // 0000 0010 0110 0
1902 { 832, 13, 0x4D }, // 0000 0010 0110 1
1903 { 896, 13, 0x72 }, // 0000 0011 1001 0
1904 { 960, 13, 0x73 }, // 0000 0011 1001 1
1905 { 1024, 13, 0x74 }, // 0000 0011 1010 0
1906 { 1088, 13, 0x75 }, // 0000 0011 1010 1
1907 { 1152, 13, 0x76 }, // 0000 0011 1011 0
1908 { 1216, 13, 0x77 }, // 0000 0011 1011 1
1909 { 1280, 13, 0x52 }, // 0000 0010 1001 0
1910 { 1344, 13, 0x53 }, // 0000 0010 1001 1
1911 { 1408, 13, 0x54 }, // 0000 0010 1010 0
1912 { 1472, 13, 0x55 }, // 0000 0010 1010 1
1913 { 1536, 13, 0x5A }, // 0000 0010 1101 0
1914 { 1600, 13, 0x5B }, // 0000 0010 1101 1
1915 { 1664, 13, 0x64 }, // 0000 0011 0010 0
1916 { 1728, 13, 0x65 }, // 0000 0011 0010 1
1917 { 1792, 11, 0x8 }, // 0000 0001 000
1918 { 1856, 11, 0xC }, // 0000 0001 100
1919 { 1920, 11, 0xD }, // 0000 0001 101
1920 { 1984, 12, 0x12 }, // 0000 0001 0010
1921 { 2048, 12, 0x13 }, // 0000 0001 0011
1922 { 2112, 12, 0x14 }, // 0000 0001 0100
1923 { 2176, 12, 0x15 }, // 0000 0001 0101
1924 { 2240, 12, 0x16 }, // 0000 0001 0110
1925 { 2304, 12, 0x17 }, // 0000 0001 0111
1926 { 2368, 12, 0x1C }, // 0000 0001 1100
1927 { 2432, 12, 0x1D }, // 0000 0001 1101
1928 { 2496, 12, 0x1E }, // 0000 0001 1110
1929 { 2560, 12, 0x1F } // 0000 0001 1111
1932 void PDFWriterImpl::putG4Span( long i_nSpan
, bool i_bWhitePixel
, BitStreamState
& io_rState
)
1934 const PixelCode
* pTable
= i_bWhitePixel
? WhitePixelCodes
: BlackPixelCodes
;
1935 // maximum encoded span is 2560 consecutive pixels
1936 while( i_nSpan
> 2623 )
1938 // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1939 putG4Bits( pTable
[103].mnCodeBits
, pTable
[103].mnCode
, io_rState
);
1940 i_nSpan
-= pTable
[103].mnEncodedPixels
;
1942 // write multiples of 64 pixels up to 2560
1945 sal_uInt32 nTabIndex
= 63 + (i_nSpan
>> 6);
1946 OSL_ASSERT( pTable
[nTabIndex
].mnEncodedPixels
== static_cast<sal_uInt32
>(64*(i_nSpan
>> 6)) );
1947 putG4Bits( pTable
[nTabIndex
].mnCodeBits
, pTable
[nTabIndex
].mnCode
, io_rState
);
1948 i_nSpan
-= pTable
[nTabIndex
].mnEncodedPixels
;
1950 putG4Bits( pTable
[i_nSpan
].mnCodeBits
, pTable
[i_nSpan
].mnCode
, io_rState
);
1953 void PDFWriterImpl::writeG4Stream( BitmapReadAccess
* i_pBitmap
)
1955 long nW
= i_pBitmap
->Width();
1956 long nH
= i_pBitmap
->Height();
1957 if( nW
<= 0 || nH
<= 0 )
1959 if( i_pBitmap
->GetBitCount() != 1 )
1962 BitStreamState aBitState
;
1964 // the first reference line is virtual and completely empty
1965 const Scanline pFirstRefLine
= static_cast<Scanline
>(rtl_allocateZeroMemory( nW
/8 + 1 ));
1966 Scanline pRefLine
= pFirstRefLine
;
1967 for( long nY
= 0; nY
< nH
; nY
++ )
1969 const Scanline pCurLine
= i_pBitmap
->GetScanline( nY
);
1970 long nLineIndex
= 0;
1971 bool bRunSet
= (*pCurLine
& 0x80) != 0;
1972 bool bRefSet
= (*pRefLine
& 0x80) != 0;
1973 long nRunIndex1
= bRunSet
? 0 : findBitRun( pCurLine
, 0, nW
, bRunSet
);
1974 long nRefIndex1
= bRefSet
? 0 : findBitRun( pRefLine
, 0, nW
, bRefSet
);
1975 for( ; nLineIndex
< nW
; )
1977 long nRefIndex2
= findBitRun( pRefLine
, nRefIndex1
, nW
, isSet( pRefLine
, nRefIndex1
) );
1978 if( nRefIndex2
>= nRunIndex1
)
1980 long nDiff
= nRefIndex1
- nRunIndex1
;
1981 if( -3 <= nDiff
&& nDiff
<= 3 )
1982 { // vertical coding
1985 sal_uInt32 mnCodeBits
;
1987 } VerticalCodes
[7] = {
1988 { 7, 0x03 }, // 0000 011
1989 { 6, 0x03 }, // 0000 11
1993 { 6, 0x02 }, // 0000 10
1994 { 7, 0x02 } // 0000 010
2000 putG4Bits( VerticalCodes
[nDiff
].mnCodeBits
, VerticalCodes
[nDiff
].mnCode
, aBitState
);
2001 nLineIndex
= nRunIndex1
;
2004 { // difference too large, horizontal coding
2005 // emit horz code 001
2006 putG4Bits( 3, 0x1, aBitState
);
2007 long nRunIndex2
= findBitRun( pCurLine
, nRunIndex1
, nW
, isSet( pCurLine
, nRunIndex1
) );
2008 bool bWhiteFirst
= ( nLineIndex
+ nRunIndex1
== 0 || ! isSet( pCurLine
, nLineIndex
) );
2009 putG4Span( nRunIndex1
- nLineIndex
, bWhiteFirst
, aBitState
);
2010 putG4Span( nRunIndex2
- nRunIndex1
, ! bWhiteFirst
, aBitState
);
2011 nLineIndex
= nRunIndex2
;
2015 { // emit pass code 0001
2016 putG4Bits( 4, 0x1, aBitState
);
2017 nLineIndex
= nRefIndex2
;
2019 if( nLineIndex
< nW
)
2021 bool bSet
= isSet( pCurLine
, nLineIndex
);
2022 nRunIndex1
= findBitRun( pCurLine
, nLineIndex
, nW
, bSet
);
2023 nRefIndex1
= findBitRun( pRefLine
, nLineIndex
, nW
, ! bSet
);
2024 nRefIndex1
= findBitRun( pRefLine
, nRefIndex1
, nW
, bSet
);
2028 // the current line is the reference for the next line
2029 pRefLine
= pCurLine
;
2031 // terminate strip with EOFB
2032 putG4Bits( 12, 1, aBitState
);
2033 putG4Bits( 12, 1, aBitState
);
2034 if( aBitState
.mnNextBitPos
!= 8 )
2036 writeBuffer( &aBitState
.getByte(), 1 );
2040 rtl_freeMemory( pFirstRefLine
);
2043 static bool lcl_canUsePDFAxialShading(const Gradient
& rGradient
) {
2044 switch (rGradient
.GetStyle())
2046 case GradientStyle::Linear
:
2047 case GradientStyle::Axial
:
2053 // TODO: handle step count
2054 return rGradient
.GetSteps() <= 0;
2057 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */