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/bmpacc.hxx"
27 #include "vcl/graph.hxx"
31 #include "unotools/streamwrap.hxx"
33 #include <tools/fract.hxx>
35 #include "comphelper/processfactory.hxx"
37 #include "com/sun/star/beans/PropertyValue.hpp"
38 #include "com/sun/star/io/XSeekable.hpp"
39 #include "com/sun/star/graphic/GraphicProvider.hpp"
40 #include "com/sun/star/graphic/XGraphicProvider.hpp"
42 #include "cppuhelper/implbase1.hxx"
44 #include <rtl/digest.h>
48 using namespace com::sun::star
;
49 using namespace com::sun::star::uno
;
50 using namespace com::sun::star::beans
;
52 static bool lcl_canUsePDFAxialShading(const Gradient
& rGradient
);
54 void PDFWriterImpl::implWriteGradient( const tools::PolyPolygon
& i_rPolyPoly
, const Gradient
& i_rGradient
,
55 VirtualDevice
* i_pDummyVDev
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
)
59 i_pDummyVDev
->AddGradientActions( i_rPolyPoly
.GetBoundRect(), i_rGradient
, aTmpMtf
);
62 m_rOuterFace
.IntersectClipRegion( i_rPolyPoly
.getB2DPolyPolygon() );
63 playMetafile( aTmpMtf
, NULL
, i_rContext
, i_pDummyVDev
);
67 void PDFWriterImpl::implWriteBitmapEx( const Point
& i_rPoint
, const Size
& i_rSize
, const BitmapEx
& i_rBitmapEx
,
68 VirtualDevice
* i_pDummyVDev
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
)
70 if ( !i_rBitmapEx
.IsEmpty() && i_rSize
.Width() && i_rSize
.Height() )
72 BitmapEx
aBitmapEx( i_rBitmapEx
);
73 Point
aPoint( i_rPoint
);
74 Size
aSize( i_rSize
);
76 // #i19065# Negative sizes have mirror semantics on
77 // OutputDevice. BitmapEx and co. have no idea about that, so
78 // perform that _before_ doing anything with aBitmapEx.
79 BmpMirrorFlags
nMirrorFlags(BmpMirrorFlags::NONE
);
80 if( aSize
.Width() < 0 )
83 aPoint
.X() -= aSize
.Width();
84 nMirrorFlags
|= BmpMirrorFlags::Horizontal
;
86 if( aSize
.Height() < 0 )
89 aPoint
.Y() -= aSize
.Height();
90 nMirrorFlags
|= BmpMirrorFlags::Vertical
;
93 if( nMirrorFlags
!= BmpMirrorFlags::NONE
)
95 aBitmapEx
.Mirror( nMirrorFlags
);
97 if( i_rContext
.m_nMaxImageResolution
> 50 )
99 // do downsampling if necessary
100 const Size
aDstSizeTwip( i_pDummyVDev
->PixelToLogic( i_pDummyVDev
->LogicToPixel( aSize
), MAP_TWIP
) );
101 const Size
aBmpSize( aBitmapEx
.GetSizePixel() );
102 const double fBmpPixelX
= aBmpSize
.Width();
103 const double fBmpPixelY
= aBmpSize
.Height();
104 const double fMaxPixelX
= aDstSizeTwip
.Width() * i_rContext
.m_nMaxImageResolution
/ 1440.0;
105 const double fMaxPixelY
= aDstSizeTwip
.Height() * i_rContext
.m_nMaxImageResolution
/ 1440.0;
107 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
108 if( ( ( fBmpPixelX
> ( fMaxPixelX
+ 4 ) ) ||
109 ( fBmpPixelY
> ( fMaxPixelY
+ 4 ) ) ) &&
110 ( fBmpPixelY
> 0.0 ) && ( fMaxPixelY
> 0.0 ) )
114 const double fBmpWH
= fBmpPixelX
/ fBmpPixelY
;
115 const double fMaxWH
= fMaxPixelX
/ fMaxPixelY
;
117 if( fBmpWH
< fMaxWH
)
119 aNewBmpSize
.Width() = FRound( fMaxPixelY
* fBmpWH
);
120 aNewBmpSize
.Height() = FRound( fMaxPixelY
);
122 else if( fBmpWH
> 0.0 )
124 aNewBmpSize
.Width() = FRound( fMaxPixelX
);
125 aNewBmpSize
.Height() = FRound( fMaxPixelX
/ fBmpWH
);
128 if( aNewBmpSize
.Width() && aNewBmpSize
.Height() )
130 // #i121233# Use best quality for PDF exports
131 aBitmapEx
.Scale( aNewBmpSize
, BmpScaleFlag::BestQuality
);
135 aBitmapEx
.SetEmpty();
140 const Size
aSizePixel( aBitmapEx
.GetSizePixel() );
141 if ( aSizePixel
.Width() && aSizePixel
.Height() )
143 if( m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
)
145 BmpConversion eConv
= BMP_CONVERSION_8BIT_GREYS
;
146 int nDepth
= aBitmapEx
.GetBitmap().GetBitCount();
148 eConv
= BMP_CONVERSION_4BIT_GREYS
;
150 aBitmapEx
.Convert( eConv
);
152 bool bUseJPGCompression
= !i_rContext
.m_bOnlyLosslessCompression
;
153 if ( ( aSizePixel
.Width() < 32 ) || ( aSizePixel
.Height() < 32 ) )
154 bUseJPGCompression
= false;
156 SvMemoryStream aStrm
;
159 bool bTrueColorJPG
= true;
160 if ( bUseJPGCompression
)
162 sal_uInt32 nZippedFileSize
; // sj: we will calculate the filesize of a zipped bitmap
163 { // to determine if jpeg compression is useful
164 SvMemoryStream aTemp
;
165 aTemp
.SetCompressMode( aTemp
.GetCompressMode() | SvStreamCompressFlags::ZBITMAP
);
166 aTemp
.SetVersion( SOFFICE_FILEFORMAT_40
); // sj: up from version 40 our bitmap stream operator
167 WriteDIBBitmapEx(aBitmapEx
, aTemp
); // is capable of zlib stream compression
168 aTemp
.Seek( STREAM_SEEK_TO_END
);
169 nZippedFileSize
= aTemp
.Tell();
171 if ( aBitmapEx
.IsTransparent() )
173 if ( aBitmapEx
.IsAlpha() )
174 aMask
= aBitmapEx
.GetAlpha().GetBitmap();
176 aMask
= aBitmapEx
.GetMask();
178 Graphic
aGraphic( aBitmapEx
.GetBitmap() );
179 sal_Int32 nColorMode
= 0;
181 Sequence
< PropertyValue
> aFilterData( 2 );
182 aFilterData
[ 0 ].Name
= "Quality";
183 aFilterData
[ 0 ].Value
<<= sal_Int32(i_rContext
.m_nJPEGQuality
);
184 aFilterData
[ 1 ].Name
= "ColorMode";
185 aFilterData
[ 1 ].Value
<<= nColorMode
;
189 uno::Reference
< io::XStream
> xStream
= new utl::OStreamWrapper( aStrm
);
190 uno::Reference
< io::XSeekable
> xSeekable( xStream
, UNO_QUERY_THROW
);
191 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
192 uno::Reference
< graphic::XGraphicProvider
> xGraphicProvider( graphic::GraphicProvider::create(xContext
) );
193 uno::Reference
< graphic::XGraphic
> xGraphic( aGraphic
.GetXGraphic() );
194 uno::Reference
< io::XOutputStream
> xOut( xStream
->getOutputStream() );
195 OUString
aMimeType("image/jpeg");
196 uno::Sequence
< beans::PropertyValue
> aOutMediaProperties( 3 );
197 aOutMediaProperties
[0].Name
= "OutputStream";
198 aOutMediaProperties
[0].Value
<<= xOut
;
199 aOutMediaProperties
[1].Name
= "MimeType";
200 aOutMediaProperties
[1].Value
<<= aMimeType
;
201 aOutMediaProperties
[2].Name
= "FilterData";
202 aOutMediaProperties
[2].Value
<<= aFilterData
;
203 xGraphicProvider
->storeGraphic( xGraphic
, aOutMediaProperties
);
205 if ( xSeekable
->getLength() > nZippedFileSize
)
207 bUseJPGCompression
= false;
211 aStrm
.Seek( STREAM_SEEK_TO_END
);
213 xSeekable
->seek( 0 );
214 Sequence
< PropertyValue
> aArgs( 1 );
215 aArgs
[ 0 ].Name
= "InputStream";
216 aArgs
[ 0 ].Value
<<= xStream
;
217 uno::Reference
< XPropertySet
> xPropSet( xGraphicProvider
->queryGraphicDescriptor( aArgs
) );
220 sal_Int16 nBitsPerPixel
= 24;
221 if ( xPropSet
->getPropertyValue("BitsPerPixel") >>= nBitsPerPixel
)
223 bTrueColorJPG
= nBitsPerPixel
!= 8;
228 catch( uno::Exception
& )
230 bUseJPGCompression
= false;
233 if ( bUseJPGCompression
)
234 m_rOuterFace
.DrawJPGBitmap( aStrm
, bTrueColorJPG
, aSizePixel
, Rectangle( aPoint
, aSize
), aMask
);
235 else if ( aBitmapEx
.IsTransparent() )
236 m_rOuterFace
.DrawBitmapEx( aPoint
, aSize
, aBitmapEx
);
238 m_rOuterFace
.DrawBitmap( aPoint
, aSize
, aBitmapEx
.GetBitmap() );
243 void PDFWriterImpl::playMetafile( const GDIMetaFile
& i_rMtf
, vcl::PDFExtOutDevData
* i_pOutDevData
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
, VirtualDevice
* pDummyVDev
)
245 bool bAssertionFired( false );
247 ScopedVclPtr
<VirtualDevice
> xPrivateDevice
;
250 xPrivateDevice
.reset(VclPtr
<VirtualDevice
>::Create());
251 pDummyVDev
= xPrivateDevice
.get();
252 pDummyVDev
->EnableOutput( false );
253 pDummyVDev
->SetMapMode( i_rMtf
.GetPrefMapMode() );
255 GDIMetaFile
aMtf( i_rMtf
);
257 for( sal_uInt32 i
= 0, nCount
= aMtf
.GetActionSize(); i
< (sal_uInt32
)nCount
; )
259 if ( !i_pOutDevData
|| !i_pOutDevData
->PlaySyncPageAct( m_rOuterFace
, i
) )
261 const MetaAction
* pAction
= aMtf
.GetAction( i
);
262 const MetaActionType nType
= pAction
->GetType();
266 case( MetaActionType::PIXEL
):
268 const MetaPixelAction
* pA
= static_cast<const MetaPixelAction
*>(pAction
);
269 m_rOuterFace
.DrawPixel( pA
->GetPoint(), pA
->GetColor() );
273 case( MetaActionType::POINT
):
275 const MetaPointAction
* pA
= static_cast<const MetaPointAction
*>(pAction
);
276 m_rOuterFace
.DrawPixel( pA
->GetPoint() );
280 case( MetaActionType::LINE
):
282 const MetaLineAction
* pA
= static_cast<const MetaLineAction
*>(pAction
);
283 if ( pA
->GetLineInfo().IsDefault() )
284 m_rOuterFace
.DrawLine( pA
->GetStartPoint(), pA
->GetEndPoint() );
286 m_rOuterFace
.DrawLine( pA
->GetStartPoint(), pA
->GetEndPoint(), pA
->GetLineInfo() );
290 case( MetaActionType::RECT
):
292 const MetaRectAction
* pA
= static_cast<const MetaRectAction
*>(pAction
);
293 m_rOuterFace
.DrawRect( pA
->GetRect() );
297 case( MetaActionType::ROUNDRECT
):
299 const MetaRoundRectAction
* pA
= static_cast<const MetaRoundRectAction
*>(pAction
);
300 m_rOuterFace
.DrawRect( pA
->GetRect(), pA
->GetHorzRound(), pA
->GetVertRound() );
304 case( MetaActionType::ELLIPSE
):
306 const MetaEllipseAction
* pA
= static_cast<const MetaEllipseAction
*>(pAction
);
307 m_rOuterFace
.DrawEllipse( pA
->GetRect() );
311 case( MetaActionType::ARC
):
313 const MetaArcAction
* pA
= static_cast<const MetaArcAction
*>(pAction
);
314 m_rOuterFace
.DrawArc( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
318 case( MetaActionType::PIE
):
320 const MetaArcAction
* pA
= static_cast<const MetaArcAction
*>(pAction
);
321 m_rOuterFace
.DrawPie( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
325 case( MetaActionType::CHORD
):
327 const MetaChordAction
* pA
= static_cast<const MetaChordAction
*>(pAction
);
328 m_rOuterFace
.DrawChord( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
332 case( MetaActionType::POLYGON
):
334 const MetaPolygonAction
* pA
= static_cast<const MetaPolygonAction
*>(pAction
);
335 m_rOuterFace
.DrawPolygon( pA
->GetPolygon() );
339 case( MetaActionType::POLYLINE
):
341 const MetaPolyLineAction
* pA
= static_cast<const MetaPolyLineAction
*>(pAction
);
342 if ( pA
->GetLineInfo().IsDefault() )
343 m_rOuterFace
.DrawPolyLine( pA
->GetPolygon() );
345 m_rOuterFace
.DrawPolyLine( pA
->GetPolygon(), pA
->GetLineInfo() );
349 case( MetaActionType::POLYPOLYGON
):
351 const MetaPolyPolygonAction
* pA
= static_cast<const MetaPolyPolygonAction
*>(pAction
);
352 m_rOuterFace
.DrawPolyPolygon( pA
->GetPolyPolygon() );
356 case( MetaActionType::GRADIENT
):
358 const MetaGradientAction
* pA
= static_cast<const MetaGradientAction
*>(pAction
);
359 const Gradient
& rGradient
= pA
->GetGradient();
360 if (lcl_canUsePDFAxialShading(rGradient
))
362 m_rOuterFace
.DrawGradient( pA
->GetRect(), rGradient
);
366 const tools::PolyPolygon
aPolyPoly( pA
->GetRect() );
367 implWriteGradient( aPolyPoly
, rGradient
, pDummyVDev
, i_rContext
);
372 case( MetaActionType::GRADIENTEX
):
374 const MetaGradientExAction
* pA
= static_cast<const MetaGradientExAction
*>(pAction
);
375 const Gradient
& rGradient
= pA
->GetGradient();
377 if (lcl_canUsePDFAxialShading(rGradient
))
378 m_rOuterFace
.DrawGradient( pA
->GetPolyPolygon(), rGradient
);
380 implWriteGradient( pA
->GetPolyPolygon(), rGradient
, pDummyVDev
, i_rContext
);
384 case MetaActionType::HATCH
:
386 const MetaHatchAction
* pA
= static_cast<const MetaHatchAction
*>(pAction
);
387 m_rOuterFace
.DrawHatch( pA
->GetPolyPolygon(), pA
->GetHatch() );
391 case( MetaActionType::Transparent
):
393 const MetaTransparentAction
* pA
= static_cast<const MetaTransparentAction
*>(pAction
);
394 m_rOuterFace
.DrawTransparent( pA
->GetPolyPolygon(), pA
->GetTransparence() );
398 case( MetaActionType::FLOATTRANSPARENT
):
400 const MetaFloatTransparentAction
* pA
= static_cast<const MetaFloatTransparentAction
*>(pAction
);
402 GDIMetaFile
aTmpMtf( pA
->GetGDIMetaFile() );
403 const Point
& rPos
= pA
->GetPoint();
404 const Size
& rSize
= pA
->GetSize();
405 const Gradient
& rTransparenceGradient
= pA
->GetGradient();
407 // special case constant alpha value
408 if( rTransparenceGradient
.GetStartColor() == rTransparenceGradient
.GetEndColor() )
410 const Color
aTransCol( rTransparenceGradient
.GetStartColor() );
411 const sal_uInt16 nTransPercent
= aTransCol
.GetLuminance() * 100 / 255;
412 m_rOuterFace
.BeginTransparencyGroup();
413 playMetafile( aTmpMtf
, NULL
, i_rContext
, pDummyVDev
);
414 m_rOuterFace
.EndTransparencyGroup( Rectangle( rPos
, rSize
), nTransPercent
);
418 const Size
aDstSizeTwip( pDummyVDev
->PixelToLogic( pDummyVDev
->LogicToPixel( rSize
), MAP_TWIP
) );
420 // i#115962# Always use at least 300 DPI for bitmap conversion of transparence gradients,
421 // else the quality is not acceptable (see bugdoc as example)
422 sal_Int32
nMaxBmpDPI(300);
424 if( i_rContext
.m_nMaxImageResolution
> 50 )
426 if ( nMaxBmpDPI
> i_rContext
.m_nMaxImageResolution
)
427 nMaxBmpDPI
= i_rContext
.m_nMaxImageResolution
;
429 const sal_Int32 nPixelX
= (sal_Int32
)((double)aDstSizeTwip
.Width() * (double)nMaxBmpDPI
/ 1440.0);
430 const sal_Int32 nPixelY
= (sal_Int32
)((double)aDstSizeTwip
.Height() * (double)nMaxBmpDPI
/ 1440.0);
431 if ( nPixelX
&& nPixelY
)
433 Size
aDstSizePixel( nPixelX
, nPixelY
);
434 ScopedVclPtrInstance
<VirtualDevice
> xVDev
;
435 if( xVDev
->SetOutputSizePixel( aDstSizePixel
) )
437 Bitmap aPaint
, aMask
;
441 MapMode
aMapMode( pDummyVDev
->GetMapMode() );
442 aMapMode
.SetOrigin( aPoint
);
443 xVDev
->SetMapMode( aMapMode
);
444 Size
aDstSize( xVDev
->PixelToLogic( aDstSizePixel
) );
446 Point
aMtfOrigin( aTmpMtf
.GetPrefMapMode().GetOrigin() );
447 if ( aMtfOrigin
.X() || aMtfOrigin
.Y() )
448 aTmpMtf
.Move( -aMtfOrigin
.X(), -aMtfOrigin
.Y() );
449 double fScaleX
= (double)aDstSize
.Width() / (double)aTmpMtf
.GetPrefSize().Width();
450 double fScaleY
= (double)aDstSize
.Height() / (double)aTmpMtf
.GetPrefSize().Height();
451 if( fScaleX
!= 1.0 || fScaleY
!= 1.0 )
452 aTmpMtf
.Scale( fScaleX
, fScaleY
);
453 aTmpMtf
.SetPrefMapMode( aMapMode
);
455 // create paint bitmap
457 aTmpMtf
.Play( xVDev
.get(), aPoint
, aDstSize
);
460 xVDev
->EnableMapMode( false );
461 aPaint
= xVDev
->GetBitmap( aPoint
, aDstSizePixel
);
462 xVDev
->EnableMapMode( true );
464 // create mask bitmap
465 xVDev
->SetLineColor( COL_BLACK
);
466 xVDev
->SetFillColor( COL_BLACK
);
467 xVDev
->DrawRect( Rectangle( aPoint
, aDstSize
) );
468 xVDev
->SetDrawMode( DrawModeFlags::WhiteLine
| DrawModeFlags::WhiteFill
| DrawModeFlags::WhiteText
|
469 DrawModeFlags::WhiteBitmap
| DrawModeFlags::WhiteGradient
);
471 aTmpMtf
.Play( xVDev
.get(), aPoint
, aDstSize
);
473 xVDev
->EnableMapMode( false );
474 aMask
= xVDev
->GetBitmap( aPoint
, aDstSizePixel
);
475 xVDev
->EnableMapMode( true );
477 // create alpha mask from gradient
478 xVDev
->SetDrawMode( DrawModeFlags::GrayGradient
);
479 xVDev
->DrawGradient( Rectangle( aPoint
, aDstSize
), rTransparenceGradient
);
480 xVDev
->SetDrawMode( DrawModeFlags::Default
);
481 xVDev
->EnableMapMode( false );
482 xVDev
->DrawMask( aPoint
, aDstSizePixel
, aMask
, Color( COL_WHITE
) );
483 aAlpha
= xVDev
->GetBitmap( aPoint
, aDstSizePixel
);
484 implWriteBitmapEx( rPos
, rSize
, BitmapEx( aPaint
, aAlpha
), pDummyVDev
, i_rContext
);
491 case( MetaActionType::EPS
):
493 const MetaEPSAction
* pA
= static_cast<const MetaEPSAction
*>(pAction
);
494 const GDIMetaFile
aSubstitute( pA
->GetSubstitute() );
499 MapMode
aMapMode( aSubstitute
.GetPrefMapMode() );
500 Size
aOutSize( OutputDevice::LogicToLogic( pA
->GetSize(), pDummyVDev
->GetMapMode(), aMapMode
) );
501 aMapMode
.SetScaleX( Fraction( aOutSize
.Width(), aSubstitute
.GetPrefSize().Width() ) );
502 aMapMode
.SetScaleY( Fraction( aOutSize
.Height(), aSubstitute
.GetPrefSize().Height() ) );
503 aMapMode
.SetOrigin( OutputDevice::LogicToLogic( pA
->GetPoint(), pDummyVDev
->GetMapMode(), aMapMode
) );
505 m_rOuterFace
.SetMapMode( aMapMode
);
506 pDummyVDev
->SetMapMode( aMapMode
);
507 playMetafile( aSubstitute
, NULL
, i_rContext
, pDummyVDev
);
513 case( MetaActionType::COMMENT
):
514 if( ! i_rContext
.m_bTransparenciesWereRemoved
)
516 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
518 if( pA
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
520 const MetaGradientExAction
* pGradAction
= NULL
;
523 while( !bDone
&& ( ++i
< nCount
) )
525 pAction
= aMtf
.GetAction( i
);
527 if( pAction
->GetType() == MetaActionType::GRADIENTEX
)
528 pGradAction
= static_cast<const MetaGradientExAction
*>(pAction
);
529 else if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
530 ( static_cast<const MetaCommentAction
*>(pAction
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END")) )
538 if (lcl_canUsePDFAxialShading(pGradAction
->GetGradient()))
540 m_rOuterFace
.DrawGradient( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient() );
544 implWriteGradient( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient(), pDummyVDev
, i_rContext
);
550 const sal_uInt8
* pData
= pA
->GetData();
553 SvMemoryStream
aMemStm( (void*)pData
, pA
->GetDataSize(), StreamMode::READ
);
554 bool bSkipSequence
= false;
557 if( pA
->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
559 sSeqEnd
= OString("XPATHSTROKE_SEQ_END");
560 SvtGraphicStroke aStroke
;
561 ReadSvtGraphicStroke( aMemStm
, aStroke
);
564 aStroke
.getPath( aPath
);
566 tools::PolyPolygon aStartArrow
;
567 tools::PolyPolygon aEndArrow
;
568 double fTransparency( aStroke
.getTransparency() );
569 double fStrokeWidth( aStroke
.getStrokeWidth() );
570 SvtGraphicStroke::DashArray aDashArray
;
572 aStroke
.getStartArrow( aStartArrow
);
573 aStroke
.getEndArrow( aEndArrow
);
574 aStroke
.getDashArray( aDashArray
);
576 bSkipSequence
= true;
577 if ( aStartArrow
.Count() || aEndArrow
.Count() )
578 bSkipSequence
= false;
579 if ( aDashArray
.size() && ( fStrokeWidth
!= 0.0 ) && ( fTransparency
== 0.0 ) )
580 bSkipSequence
= false;
583 PDFWriter::ExtLineInfo aInfo
;
584 aInfo
.m_fLineWidth
= fStrokeWidth
;
585 aInfo
.m_fTransparency
= fTransparency
;
586 aInfo
.m_fMiterLimit
= aStroke
.getMiterLimit();
587 switch( aStroke
.getCapType() )
590 case SvtGraphicStroke::capButt
: aInfo
.m_eCap
= PDFWriter::capButt
;break;
591 case SvtGraphicStroke::capRound
: aInfo
.m_eCap
= PDFWriter::capRound
;break;
592 case SvtGraphicStroke::capSquare
: aInfo
.m_eCap
= PDFWriter::capSquare
;break;
594 switch( aStroke
.getJoinType() )
597 case SvtGraphicStroke::joinMiter
: aInfo
.m_eJoin
= PDFWriter::joinMiter
;break;
598 case SvtGraphicStroke::joinRound
: aInfo
.m_eJoin
= PDFWriter::joinRound
;break;
599 case SvtGraphicStroke::joinBevel
: aInfo
.m_eJoin
= PDFWriter::joinBevel
;break;
600 case SvtGraphicStroke::joinNone
:
601 aInfo
.m_eJoin
= PDFWriter::joinMiter
;
602 aInfo
.m_fMiterLimit
= 0.0;
605 aInfo
.m_aDashArray
= aDashArray
;
607 if(SvtGraphicStroke::joinNone
== aStroke
.getJoinType()
608 && fStrokeWidth
> 0.0)
610 // emulate no edge rounding by handling single edges
611 const sal_uInt16
nPoints(aPath
.GetSize());
612 const bool bCurve(aPath
.HasFlags());
614 for(sal_uInt16
a(0); a
+ 1 < nPoints
; a
++)
617 && POLY_NORMAL
!= aPath
.GetFlags(a
+ 1)
619 && POLY_NORMAL
!= aPath
.GetFlags(a
+ 2)
622 const Polygon
aSnippet(4,
623 aPath
.GetConstPointAry() + a
,
624 aPath
.GetConstFlagAry() + a
);
625 m_rOuterFace
.DrawPolyLine( aSnippet
, aInfo
);
630 const Polygon
aSnippet(2,
631 aPath
.GetConstPointAry() + a
);
632 m_rOuterFace
.DrawPolyLine( aSnippet
, aInfo
);
638 m_rOuterFace
.DrawPolyLine( aPath
, aInfo
);
642 else if ( pA
->GetComment() == "XPATHFILL_SEQ_BEGIN" )
644 sSeqEnd
= OString("XPATHFILL_SEQ_END");
645 SvtGraphicFill aFill
;
646 ReadSvtGraphicFill( aMemStm
, aFill
);
648 if ( ( aFill
.getFillType() == SvtGraphicFill::fillSolid
) && ( aFill
.getFillRule() == SvtGraphicFill::fillEvenOdd
) )
650 double fTransparency
= aFill
.getTransparency();
651 if ( fTransparency
== 0.0 )
653 tools::PolyPolygon aPath
;
654 aFill
.getPath( aPath
);
656 bSkipSequence
= true;
657 m_rOuterFace
.DrawPolyPolygon( aPath
);
659 else if ( fTransparency
== 1.0 )
660 bSkipSequence
= true;
662 /* #i81548# removing optimization for fill textures, because most of the texture settings are not
663 exported properly. In OpenOffice 3.1 the drawing layer will support graphic primitives, then it
664 will not be a problem to optimize the filltexture export. But for wysiwyg is more important than
666 else if( aFill.getFillType() == SvtGraphicFill::fillTexture && aFill.isTiling() )
668 sal_Int32 nPattern = mnCachePatternId;
669 Graphic aPatternGraphic;
670 aFill.getGraphic( aPatternGraphic );
671 bool bUseCache = false;
672 SvtGraphicFill::Transform aPatTransform;
673 aFill.getTransform( aPatTransform );
675 if( mnCachePatternId >= 0 )
677 SvtGraphicFill::Transform aCacheTransform;
678 maCacheFill.getTransform( aCacheTransform );
679 if( aCacheTransform.matrix[0] == aPatTransform.matrix[0] &&
680 aCacheTransform.matrix[1] == aPatTransform.matrix[1] &&
681 aCacheTransform.matrix[2] == aPatTransform.matrix[2] &&
682 aCacheTransform.matrix[3] == aPatTransform.matrix[3] &&
683 aCacheTransform.matrix[4] == aPatTransform.matrix[4] &&
684 aCacheTransform.matrix[5] == aPatTransform.matrix[5]
687 Graphic aCacheGraphic;
688 maCacheFill.getGraphic( aCacheGraphic );
689 if( aCacheGraphic == aPatternGraphic )
697 // paint graphic to metafile
698 GDIMetaFile aPattern;
699 pDummyVDev->SetConnectMetaFile( &aPattern );
701 pDummyVDev->SetMapMode( aPatternGraphic.GetPrefMapMode() );
703 aPatternGraphic.Draw( &rDummyVDev, Point( 0, 0 ) );
705 pDummyVDev->SetConnectMetaFile( NULL );
706 aPattern.WindStart();
708 MapMode aPatternMapMode( aPatternGraphic.GetPrefMapMode() );
709 // prepare pattern from metafile
710 Size aPrefSize( aPatternGraphic.GetPrefSize() );
711 // FIXME: this magic -1 shouldn't be necessary
712 aPrefSize.Width() -= 1;
713 aPrefSize.Height() -= 1;
714 aPrefSize = m_rOuterFace.GetReferenceDevice()->
715 LogicToLogic( aPrefSize,
717 &m_rOuterFace.GetReferenceDevice()->GetMapMode() );
718 // build bounding rectangle of pattern
719 Rectangle aBound( Point( 0, 0 ), aPrefSize );
720 m_rOuterFace.BeginPattern( aBound );
723 m_rOuterFace.SetMapMode( aPatternMapMode );
724 pDummyVDev->SetMapMode( aPatternMapMode );
725 ImplWriteActions( m_rOuterFace, NULL, aPattern, rDummyVDev );
729 nPattern = m_rOuterFace.EndPattern( aPatTransform );
731 // try some caching and reuse pattern
732 mnCachePatternId = nPattern;
736 // draw polypolygon with pattern fill
737 tools::PolyPolygon aPath;
738 aFill.getPath( aPath );
739 m_rOuterFace.DrawPolyPolygon( aPath, nPattern, aFill.getFillRule() == SvtGraphicFill::fillEvenOdd );
741 bSkipSequence = true;
747 while( ++i
< nCount
)
749 pAction
= aMtf
.GetAction( i
);
750 if ( pAction
->GetType() == MetaActionType::COMMENT
)
752 OString
sComment( static_cast<const MetaCommentAction
*>(pAction
)->GetComment() );
753 if (sComment
== sSeqEnd
)
757 // the replacement action for stroke is a filled rectangle
758 // the set fillcolor of the replacement is part of the graphics
759 // state and must not be skipped
760 else if( pAction
->GetType() == MetaActionType::FILLCOLOR
)
762 const MetaFillColorAction
* pMA
= static_cast<const MetaFillColorAction
*>(pAction
);
763 if( pMA
->IsSetting() )
764 m_rOuterFace
.SetFillColor( pMA
->GetColor() );
766 m_rOuterFace
.SetFillColor();
775 case( MetaActionType::BMP
):
777 const MetaBmpAction
* pA
= static_cast<const MetaBmpAction
*>(pAction
);
778 BitmapEx
aBitmapEx( pA
->GetBitmap() );
779 Size
aSize( OutputDevice::LogicToLogic( aBitmapEx
.GetPrefSize(),
780 aBitmapEx
.GetPrefMapMode(), pDummyVDev
->GetMapMode() ) );
781 if( ! ( aSize
.Width() && aSize
.Height() ) )
782 aSize
= pDummyVDev
->PixelToLogic( aBitmapEx
.GetSizePixel() );
783 implWriteBitmapEx( pA
->GetPoint(), aSize
, aBitmapEx
, pDummyVDev
, i_rContext
);
787 case( MetaActionType::BMPSCALE
):
789 const MetaBmpScaleAction
* pA
= static_cast<const MetaBmpScaleAction
*>(pAction
);
790 implWriteBitmapEx( pA
->GetPoint(), pA
->GetSize(), BitmapEx( pA
->GetBitmap() ), pDummyVDev
, i_rContext
);
794 case( MetaActionType::BMPSCALEPART
):
796 const MetaBmpScalePartAction
* pA
= static_cast<const MetaBmpScalePartAction
*>(pAction
);
797 BitmapEx
aBitmapEx( pA
->GetBitmap() );
798 aBitmapEx
.Crop( Rectangle( pA
->GetSrcPoint(), pA
->GetSrcSize() ) );
799 implWriteBitmapEx( pA
->GetDestPoint(), pA
->GetDestSize(), aBitmapEx
, pDummyVDev
, i_rContext
);
803 case( MetaActionType::BMPEX
):
805 const MetaBmpExAction
* pA
= static_cast<const MetaBmpExAction
*>(pAction
);
806 BitmapEx
aBitmapEx( pA
->GetBitmapEx() );
807 Size
aSize( OutputDevice::LogicToLogic( aBitmapEx
.GetPrefSize(),
808 aBitmapEx
.GetPrefMapMode(), pDummyVDev
->GetMapMode() ) );
809 implWriteBitmapEx( pA
->GetPoint(), aSize
, aBitmapEx
, pDummyVDev
, i_rContext
);
813 case( MetaActionType::BMPEXSCALE
):
815 const MetaBmpExScaleAction
* pA
= static_cast<const MetaBmpExScaleAction
*>(pAction
);
816 implWriteBitmapEx( pA
->GetPoint(), pA
->GetSize(), pA
->GetBitmapEx(), pDummyVDev
, i_rContext
);
820 case( MetaActionType::BMPEXSCALEPART
):
822 const MetaBmpExScalePartAction
* pA
= static_cast<const MetaBmpExScalePartAction
*>(pAction
);
823 BitmapEx
aBitmapEx( pA
->GetBitmapEx() );
824 aBitmapEx
.Crop( Rectangle( pA
->GetSrcPoint(), pA
->GetSrcSize() ) );
825 implWriteBitmapEx( pA
->GetDestPoint(), pA
->GetDestSize(), aBitmapEx
, pDummyVDev
, i_rContext
);
829 case( MetaActionType::MASK
):
830 case( MetaActionType::MASKSCALE
):
831 case( MetaActionType::MASKSCALEPART
):
833 OSL_TRACE( "MetaMask...Action not supported yet" );
837 case( MetaActionType::TEXT
):
839 const MetaTextAction
* pA
= static_cast<const MetaTextAction
*>(pAction
);
840 m_rOuterFace
.DrawText( pA
->GetPoint(), pA
->GetText().copy( pA
->GetIndex(), std::min
<sal_Int32
>(pA
->GetText().getLength() - pA
->GetIndex(), pA
->GetLen()) ) );
844 case( MetaActionType::TEXTRECT
):
846 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
847 m_rOuterFace
.DrawText( pA
->GetRect(), pA
->GetText(), pA
->GetStyle() );
851 case( MetaActionType::TEXTARRAY
):
853 const MetaTextArrayAction
* pA
= static_cast<const MetaTextArrayAction
*>(pAction
);
854 m_rOuterFace
.DrawTextArray( pA
->GetPoint(), pA
->GetText(), pA
->GetDXArray(), pA
->GetIndex(), pA
->GetLen() );
858 case( MetaActionType::STRETCHTEXT
):
860 const MetaStretchTextAction
* pA
= static_cast<const MetaStretchTextAction
*>(pAction
);
861 m_rOuterFace
.DrawStretchText( pA
->GetPoint(), pA
->GetWidth(), pA
->GetText(), pA
->GetIndex(), pA
->GetLen() );
865 case( MetaActionType::TEXTLINE
):
867 const MetaTextLineAction
* pA
= static_cast<const MetaTextLineAction
*>(pAction
);
868 m_rOuterFace
.DrawTextLine( pA
->GetStartPoint(), pA
->GetWidth(), pA
->GetStrikeout(), pA
->GetUnderline(), pA
->GetOverline() );
873 case( MetaActionType::CLIPREGION
):
875 const MetaClipRegionAction
* pA
= static_cast<const MetaClipRegionAction
*>(pAction
);
877 if( pA
->IsClipping() )
879 if( pA
->GetRegion().IsEmpty() )
880 m_rOuterFace
.SetClipRegion( basegfx::B2DPolyPolygon() );
883 vcl::Region
aReg( pA
->GetRegion() );
884 m_rOuterFace
.SetClipRegion( aReg
.GetAsB2DPolyPolygon() );
888 m_rOuterFace
.SetClipRegion();
892 case( MetaActionType::ISECTRECTCLIPREGION
):
894 const MetaISectRectClipRegionAction
* pA
= static_cast<const MetaISectRectClipRegionAction
*>(pAction
);
895 m_rOuterFace
.IntersectClipRegion( pA
->GetRect() );
899 case( MetaActionType::ISECTREGIONCLIPREGION
):
901 const MetaISectRegionClipRegionAction
* pA
= static_cast<const MetaISectRegionClipRegionAction
*>(pAction
);
902 vcl::Region
aReg( pA
->GetRegion() );
903 m_rOuterFace
.IntersectClipRegion( aReg
.GetAsB2DPolyPolygon() );
907 case( MetaActionType::MOVECLIPREGION
):
909 const MetaMoveClipRegionAction
* pA
= static_cast<const MetaMoveClipRegionAction
*>(pAction
);
910 m_rOuterFace
.MoveClipRegion( pA
->GetHorzMove(), pA
->GetVertMove() );
914 case( MetaActionType::MAPMODE
):
916 const_cast< MetaAction
* >( pAction
)->Execute( pDummyVDev
);
917 m_rOuterFace
.SetMapMode( pDummyVDev
->GetMapMode() );
921 case( MetaActionType::LINECOLOR
):
923 const MetaLineColorAction
* pA
= static_cast<const MetaLineColorAction
*>(pAction
);
925 if( pA
->IsSetting() )
926 m_rOuterFace
.SetLineColor( pA
->GetColor() );
928 m_rOuterFace
.SetLineColor();
932 case( MetaActionType::FILLCOLOR
):
934 const MetaFillColorAction
* pA
= static_cast<const MetaFillColorAction
*>(pAction
);
936 if( pA
->IsSetting() )
937 m_rOuterFace
.SetFillColor( pA
->GetColor() );
939 m_rOuterFace
.SetFillColor();
943 case( MetaActionType::TEXTLINECOLOR
):
945 const MetaTextLineColorAction
* pA
= static_cast<const MetaTextLineColorAction
*>(pAction
);
947 if( pA
->IsSetting() )
948 m_rOuterFace
.SetTextLineColor( pA
->GetColor() );
950 m_rOuterFace
.SetTextLineColor();
954 case( MetaActionType::OVERLINECOLOR
):
956 const MetaOverlineColorAction
* pA
= static_cast<const MetaOverlineColorAction
*>(pAction
);
958 if( pA
->IsSetting() )
959 m_rOuterFace
.SetOverlineColor( pA
->GetColor() );
961 m_rOuterFace
.SetOverlineColor();
965 case( MetaActionType::TEXTFILLCOLOR
):
967 const MetaTextFillColorAction
* pA
= static_cast<const MetaTextFillColorAction
*>(pAction
);
969 if( pA
->IsSetting() )
970 m_rOuterFace
.SetTextFillColor( pA
->GetColor() );
972 m_rOuterFace
.SetTextFillColor();
976 case( MetaActionType::TEXTCOLOR
):
978 const MetaTextColorAction
* pA
= static_cast<const MetaTextColorAction
*>(pAction
);
979 m_rOuterFace
.SetTextColor( pA
->GetColor() );
983 case( MetaActionType::TEXTALIGN
):
985 const MetaTextAlignAction
* pA
= static_cast<const MetaTextAlignAction
*>(pAction
);
986 m_rOuterFace
.SetTextAlign( pA
->GetTextAlign() );
990 case( MetaActionType::FONT
):
992 const MetaFontAction
* pA
= static_cast<const MetaFontAction
*>(pAction
);
993 m_rOuterFace
.SetFont( pA
->GetFont() );
997 case( MetaActionType::PUSH
):
999 const MetaPushAction
* pA
= static_cast<const MetaPushAction
*>(pAction
);
1001 pDummyVDev
->Push( pA
->GetFlags() );
1002 m_rOuterFace
.Push( pA
->GetFlags() );
1006 case( MetaActionType::POP
):
1013 case( MetaActionType::LAYOUTMODE
):
1015 const MetaLayoutModeAction
* pA
= static_cast<const MetaLayoutModeAction
*>(pAction
);
1016 m_rOuterFace
.SetLayoutMode( pA
->GetLayoutMode() );
1020 case MetaActionType::TEXTLANGUAGE
:
1022 const MetaTextLanguageAction
* pA
= static_cast<const MetaTextLanguageAction
*>(pAction
);
1023 m_rOuterFace
.SetDigitLanguage( pA
->GetTextLanguage() );
1027 case( MetaActionType::WALLPAPER
):
1029 const MetaWallpaperAction
* pA
= static_cast<const MetaWallpaperAction
*>(pAction
);
1030 m_rOuterFace
.DrawWallpaper( pA
->GetRect(), pA
->GetWallpaper() );
1034 case( MetaActionType::RASTEROP
):
1036 // !!! >>> we don't want to support this actions
1040 case( MetaActionType::REFPOINT
):
1042 // !!! >>> we don't want to support this actions
1047 // #i24604# Made assertion fire only once per
1048 // metafile. The asserted actions here are all
1050 if( !bAssertionFired
)
1052 bAssertionFired
= true;
1053 OSL_TRACE( "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered" );
1062 // Encryption methods
1064 /* a crutch to transport an rtlDigest safely though UNO API
1065 this is needed for the PDF export dialog, which otherwise would have to pass
1066 clear text passwords down till they can be used in PDFWriter. Unfortunately
1067 the MD5 sum of the password (which is needed to create the PDF encryption key)
1068 is not sufficient, since an rtl MD5 digest cannot be created in an arbitrary state
1069 which would be needed in PDFWriterImpl::computeEncryptionKey.
1071 class EncHashTransporter
: public cppu::WeakImplHelper1
< com::sun::star::beans::XMaterialHolder
>
1073 rtlDigest maUDigest
;
1075 std::vector
< sal_uInt8
> maOValue
;
1077 static std::map
< sal_IntPtr
, EncHashTransporter
* > sTransporters
;
1079 EncHashTransporter()
1080 : maUDigest( rtl_digest_createMD5() )
1082 maID
= reinterpret_cast< sal_IntPtr
>(this);
1083 while( sTransporters
.find( maID
) != sTransporters
.end() ) // paranoia mode
1085 sTransporters
[ maID
] = this;
1088 virtual ~EncHashTransporter()
1090 sTransporters
.erase( maID
);
1092 rtl_digest_destroyMD5( maUDigest
);
1093 OSL_TRACE( "EncHashTransporter freed" );
1096 rtlDigest
getUDigest() const { return maUDigest
; };
1097 std::vector
< sal_uInt8
>& getOValue() { return maOValue
; }
1102 rtl_digest_destroyMD5( maUDigest
);
1108 virtual uno::Any SAL_CALL
getMaterial() throw(std::exception
) SAL_OVERRIDE
1110 return uno::makeAny( sal_Int64(maID
) );
1113 static EncHashTransporter
* getEncHashTransporter( const uno::Reference
< beans::XMaterialHolder
>& );
1117 std::map
< sal_IntPtr
, EncHashTransporter
* > EncHashTransporter::sTransporters
;
1119 EncHashTransporter
* EncHashTransporter::getEncHashTransporter( const uno::Reference
< beans::XMaterialHolder
>& xRef
)
1121 EncHashTransporter
* pResult
= NULL
;
1124 uno::Any
aMat( xRef
->getMaterial() );
1128 std::map
< sal_IntPtr
, EncHashTransporter
* >::iterator it
= sTransporters
.find( static_cast<sal_IntPtr
>(nMat
) );
1129 if( it
!= sTransporters
.end() )
1130 pResult
= it
->second
;
1136 bool PDFWriterImpl::checkEncryptionBufferSize( register sal_Int32 newSize
)
1138 if( m_nEncryptionBufferSize
< newSize
)
1140 /* reallocate the buffer, the used function allocate as rtl_allocateMemory
1141 if the pointer parameter is NULL */
1142 m_pEncryptionBuffer
= static_cast<sal_uInt8
*>(rtl_reallocateMemory( m_pEncryptionBuffer
, newSize
));
1143 if( m_pEncryptionBuffer
)
1144 m_nEncryptionBufferSize
= newSize
;
1146 m_nEncryptionBufferSize
= 0;
1148 return ( m_nEncryptionBufferSize
!= 0 );
1151 void PDFWriterImpl::checkAndEnableStreamEncryption( register sal_Int32 nObject
)
1153 if( m_aContext
.Encryption
.Encrypt() )
1155 m_bEncryptThisStream
= true;
1156 sal_Int32 i
= m_nKeyLength
;
1157 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)nObject
;
1158 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)( nObject
>> 8 );
1159 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)( nObject
>> 16 );
1160 // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1162 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1163 // the i+2 to take into account the generation number, always zero
1164 rtl_digest_MD5( &m_aContext
.Encryption
.EncryptionKey
[0], i
+2, nMD5Sum
, sizeof(nMD5Sum
) );
1165 // initialize the RC4 with the key
1166 // key length: see algorithm 3.1, step 4: (N+5) max 16
1167 rtl_cipher_initARCFOUR( m_aCipher
, rtl_Cipher_DirectionEncode
, nMD5Sum
, m_nRC4KeyLength
, NULL
, 0 );
1171 void PDFWriterImpl::enableStringEncryption( register sal_Int32 nObject
)
1173 if( m_aContext
.Encryption
.Encrypt() )
1175 sal_Int32 i
= m_nKeyLength
;
1176 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)nObject
;
1177 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)( nObject
>> 8 );
1178 m_aContext
.Encryption
.EncryptionKey
[i
++] = (sal_uInt8
)( nObject
>> 16 );
1179 // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1181 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1182 // the i+2 to take into account the generation number, always zero
1183 rtl_digest_MD5( &m_aContext
.Encryption
.EncryptionKey
[0], i
+2, nMD5Sum
, sizeof(nMD5Sum
) );
1184 // initialize the RC4 with the key
1185 // key length: see algorithm 3.1, step 4: (N+5) max 16
1186 rtl_cipher_initARCFOUR( m_aCipher
, rtl_Cipher_DirectionEncode
, nMD5Sum
, m_nRC4KeyLength
, NULL
, 0 );
1190 /* init the encryption engine
1191 1. init the document id, used both for building the document id and for building the encryption key(s)
1192 2. build the encryption key following algorithms described in the PDF specification
1194 uno::Reference
< beans::XMaterialHolder
> PDFWriterImpl::initEncryption( const OUString
& i_rOwnerPassword
,
1195 const OUString
& i_rUserPassword
,
1199 uno::Reference
< beans::XMaterialHolder
> xResult
;
1200 if( !i_rOwnerPassword
.isEmpty() || !i_rUserPassword
.isEmpty() )
1202 EncHashTransporter
* pTransporter
= new EncHashTransporter
;
1203 xResult
= pTransporter
;
1205 // get padded passwords
1206 sal_uInt8 aPadUPW
[ENCRYPTED_PWD_SIZE
], aPadOPW
[ENCRYPTED_PWD_SIZE
];
1207 padPassword( i_rOwnerPassword
.isEmpty() ? i_rUserPassword
: i_rOwnerPassword
, aPadOPW
);
1208 padPassword( i_rUserPassword
, aPadUPW
);
1209 sal_Int32 nKeyLength
= SECUR_40BIT_KEY
;
1211 nKeyLength
= SECUR_128BIT_KEY
;
1213 if( computeODictionaryValue( aPadOPW
, aPadUPW
, pTransporter
->getOValue(), nKeyLength
) )
1215 rtlDigest aDig
= pTransporter
->getUDigest();
1216 if( rtl_digest_updateMD5( aDig
, aPadUPW
, ENCRYPTED_PWD_SIZE
) != rtl_Digest_E_None
)
1222 // trash temporary padded cleartext PWDs
1223 rtl_secureZeroMemory (aPadOPW
, sizeof(aPadOPW
));
1224 rtl_secureZeroMemory (aPadUPW
, sizeof(aPadUPW
));
1229 bool PDFWriterImpl::prepareEncryption( const uno::Reference
< beans::XMaterialHolder
>& xEnc
)
1231 bool bSuccess
= false;
1232 EncHashTransporter
* pTransporter
= EncHashTransporter::getEncHashTransporter( xEnc
);
1235 sal_Int32 nKeyLength
= 0, nRC4KeyLength
= 0;
1236 sal_Int32 nAccessPermissions
= computeAccessPermissions( m_aContext
.Encryption
, nKeyLength
, nRC4KeyLength
);
1237 m_aContext
.Encryption
.OValue
= pTransporter
->getOValue();
1238 bSuccess
= computeUDictionaryValue( pTransporter
, m_aContext
.Encryption
, nKeyLength
, nAccessPermissions
);
1242 m_aContext
.Encryption
.OValue
.clear();
1243 m_aContext
.Encryption
.UValue
.clear();
1244 m_aContext
.Encryption
.EncryptionKey
.clear();
1249 sal_Int32
PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties
& i_rProperties
,
1250 sal_Int32
& o_rKeyLength
, sal_Int32
& o_rRC4KeyLength
)
1253 2) compute the access permissions, in numerical form
1255 the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
1256 - for 40 bit security the unused bit must be set to 1, since they are not used
1257 - for 128 bit security the same bit must be preset to 0 and set later if needed
1258 according to the table 3.15, pdf v 1.4 */
1259 sal_Int32 nAccessPermissions
= ( i_rProperties
.Security128bit
) ? 0xfffff0c0 : 0xffffffc0 ;
1261 /* check permissions for 40 bit security case */
1262 nAccessPermissions
|= ( i_rProperties
.CanPrintTheDocument
) ? 1 << 2 : 0;
1263 nAccessPermissions
|= ( i_rProperties
.CanModifyTheContent
) ? 1 << 3 : 0;
1264 nAccessPermissions
|= ( i_rProperties
.CanCopyOrExtract
) ? 1 << 4 : 0;
1265 nAccessPermissions
|= ( i_rProperties
.CanAddOrModify
) ? 1 << 5 : 0;
1266 o_rKeyLength
= SECUR_40BIT_KEY
;
1267 o_rRC4KeyLength
= SECUR_40BIT_KEY
+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5
1269 if( i_rProperties
.Security128bit
)
1271 o_rKeyLength
= SECUR_128BIT_KEY
;
1272 o_rRC4KeyLength
= 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum
1273 // permitted value is 16
1274 nAccessPermissions
|= ( i_rProperties
.CanFillInteractive
) ? 1 << 8 : 0;
1275 nAccessPermissions
|= ( i_rProperties
.CanExtractForAccessibility
) ? 1 << 9 : 0;
1276 nAccessPermissions
|= ( i_rProperties
.CanAssemble
) ? 1 << 10 : 0;
1277 nAccessPermissions
|= ( i_rProperties
.CanPrintFull
) ? 1 << 11 : 0;
1279 return nAccessPermissions
;
1282 /*************************************************************
1283 begin i12626 methods
1285 Implements Algorithm 3.2, step 1 only
1287 void PDFWriterImpl::padPassword( const OUString
& i_rPassword
, sal_uInt8
* o_pPaddedPW
)
1289 // get ansi-1252 version of the password string CHECKIT ! i12626
1290 OString
aString( OUStringToOString( i_rPassword
, RTL_TEXTENCODING_MS_1252
) );
1292 //copy the string to the target
1293 sal_Int32 nToCopy
= ( aString
.getLength() < ENCRYPTED_PWD_SIZE
) ? aString
.getLength() : ENCRYPTED_PWD_SIZE
;
1294 sal_Int32 nCurrentChar
;
1296 for( nCurrentChar
= 0; nCurrentChar
< nToCopy
; nCurrentChar
++ )
1297 o_pPaddedPW
[nCurrentChar
] = (sal_uInt8
)( aString
[nCurrentChar
] );
1299 //pad it with standard byte string
1301 for( i
= nCurrentChar
, y
= 0 ; i
< ENCRYPTED_PWD_SIZE
; i
++, y
++ )
1302 o_pPaddedPW
[i
] = s_nPadString
[y
];
1305 /**********************************
1306 Algorithm 3.2 Compute the encryption key used
1308 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
1309 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
1310 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
1312 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
1315 bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter
* i_pTransporter
, vcl::PDFWriter::PDFEncryptionProperties
& io_rProperties
, sal_Int32 i_nAccessPermissions
)
1317 bool bSuccess
= true;
1318 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1320 // transporter contains an MD5 digest with the padded user password already
1321 rtlDigest aDigest
= i_pTransporter
->getUDigest();
1322 rtlDigestError nError
= rtl_Digest_E_None
;
1326 if( ! io_rProperties
.OValue
.empty() )
1327 nError
= rtl_digest_updateMD5( aDigest
, &io_rProperties
.OValue
[0] , sal_Int32(io_rProperties
.OValue
.size()) );
1333 nPerm
[0] = (sal_uInt8
)i_nAccessPermissions
;
1334 nPerm
[1] = (sal_uInt8
)( i_nAccessPermissions
>> 8 );
1335 nPerm
[2] = (sal_uInt8
)( i_nAccessPermissions
>> 16 );
1336 nPerm
[3] = (sal_uInt8
)( i_nAccessPermissions
>> 24 );
1338 if( nError
== rtl_Digest_E_None
)
1339 nError
= rtl_digest_updateMD5( aDigest
, nPerm
, sizeof( nPerm
) );
1341 //step 5, get the document ID, binary form
1342 if( nError
== rtl_Digest_E_None
)
1343 nError
= rtl_digest_updateMD5( aDigest
, &io_rProperties
.DocumentIdentifier
[0], sal_Int32(io_rProperties
.DocumentIdentifier
.size()) );
1345 if( nError
== rtl_Digest_E_None
)
1347 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1349 //step 6, only if 128 bit
1350 if( io_rProperties
.Security128bit
)
1352 for( sal_Int32 i
= 0; i
< 50; i
++ )
1354 nError
= rtl_digest_updateMD5( aDigest
, &nMD5Sum
, sizeof( nMD5Sum
) );
1355 if( nError
!= rtl_Digest_E_None
)
1360 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1372 i_pTransporter
->invalidate();
1377 io_rProperties
.EncryptionKey
.resize( MAXIMUM_RC4_KEY_LENGTH
);
1378 for( sal_Int32 i
= 0; i
< MD5_DIGEST_SIZE
; i
++ )
1379 io_rProperties
.EncryptionKey
[i
] = nMD5Sum
[i
];
1382 io_rProperties
.EncryptionKey
.clear();
1387 /**********************************
1388 Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member
1389 the step numbers down here correspond to the ones in PDF v.1.4 specfication
1391 bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8
* i_pPaddedOwnerPassword
,
1392 const sal_uInt8
* i_pPaddedUserPassword
,
1393 std::vector
< sal_uInt8
>& io_rOValue
,
1394 sal_Int32 i_nKeyLength
1397 bool bSuccess
= true;
1399 io_rOValue
.resize( ENCRYPTED_PWD_SIZE
);
1401 rtlDigest aDigest
= rtl_digest_createMD5();
1402 rtlCipher aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1403 if( aDigest
&& aCipher
)
1405 //step 1 already done, data is in i_pPaddedOwnerPassword
1408 rtlDigestError nError
= rtl_digest_updateMD5( aDigest
, i_pPaddedOwnerPassword
, ENCRYPTED_PWD_SIZE
);
1409 if( nError
== rtl_Digest_E_None
)
1411 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1413 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof(nMD5Sum
) );
1414 //step 3, only if 128 bit
1415 if( i_nKeyLength
== SECUR_128BIT_KEY
)
1418 for( i
= 0; i
< 50; i
++ )
1420 nError
= rtl_digest_updateMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1421 if( nError
!= rtl_Digest_E_None
)
1426 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1429 //Step 4, the key is in nMD5Sum
1430 //step 5 already done, data is in i_pPaddedUserPassword
1432 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1433 nMD5Sum
, i_nKeyLength
, NULL
, 0 );
1434 // encrypt the user password using the key set above
1435 rtl_cipher_encodeARCFOUR( aCipher
, i_pPaddedUserPassword
, ENCRYPTED_PWD_SIZE
, // the data to be encrypted
1436 &io_rOValue
[0], sal_Int32(io_rOValue
.size()) ); //encrypted data
1437 //Step 7, only if 128 bit
1438 if( i_nKeyLength
== SECUR_128BIT_KEY
)
1441 sal_uInt8 nLocalKey
[ SECUR_128BIT_KEY
]; // 16 = 128 bit key
1443 for( i
= 1; i
<= 19; i
++ ) // do it 19 times, start with 1
1445 for( y
= 0; y
< sizeof( nLocalKey
); y
++ )
1446 nLocalKey
[y
] = (sal_uInt8
)( nMD5Sum
[y
] ^ i
);
1448 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1449 nLocalKey
, SECUR_128BIT_KEY
, NULL
, 0 ); //destination data area, on init can be NULL
1450 rtl_cipher_encodeARCFOUR( aCipher
, &io_rOValue
[0], sal_Int32(io_rOValue
.size()), // the data to be encrypted
1451 &io_rOValue
[0], sal_Int32(io_rOValue
.size()) ); // encrypted data, can be the same as the input, encrypt "in place"
1452 //step 8, store in class data member
1463 rtl_digest_destroyMD5( aDigest
);
1465 rtl_cipher_destroyARCFOUR( aCipher
);
1472 /**********************************
1473 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)
1475 bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter
* i_pTransporter
,
1476 vcl::PDFWriter::PDFEncryptionProperties
& io_rProperties
,
1477 sal_Int32 i_nKeyLength
,
1478 sal_Int32 i_nAccessPermissions
1481 bool bSuccess
= true;
1483 io_rProperties
.UValue
.resize( ENCRYPTED_PWD_SIZE
);
1485 rtlDigest aDigest
= rtl_digest_createMD5();
1486 rtlCipher aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1487 if( aDigest
&& aCipher
)
1489 //step 1, common to both 3.4 and 3.5
1490 if( computeEncryptionKey( i_pTransporter
, io_rProperties
, i_nAccessPermissions
) )
1492 // prepare encryption key for object
1493 for( sal_Int32 i
= i_nKeyLength
, y
= 0; y
< 5 ; y
++ )
1494 io_rProperties
.EncryptionKey
[i
++] = 0;
1496 if( !io_rProperties
.Security128bit
)
1500 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1501 &io_rProperties
.EncryptionKey
[0], 5 , // key and key length
1502 NULL
, 0 ); //destination data area
1503 // encrypt the user password using the key set above, save for later use
1504 rtl_cipher_encodeARCFOUR( aCipher
, s_nPadString
, sizeof( s_nPadString
), // the data to be encrypted
1505 &io_rProperties
.UValue
[0], sal_Int32(io_rProperties
.UValue
.size()) ); //encrypted data, stored in class data member
1509 //or 3.5, for 128 bit security
1510 //step6, initialize the last 16 bytes of the encrypted user password to 0
1511 for(sal_uInt32 i
= MD5_DIGEST_SIZE
; i
< sal_uInt32(io_rProperties
.UValue
.size()); i
++)
1512 io_rProperties
.UValue
[i
] = 0;
1514 rtlDigestError nError
= rtl_digest_updateMD5( aDigest
, s_nPadString
, sizeof( s_nPadString
) );
1516 if( nError
== rtl_Digest_E_None
)
1517 nError
= rtl_digest_updateMD5( aDigest
, &io_rProperties
.DocumentIdentifier
[0], sal_Int32(io_rProperties
.DocumentIdentifier
.size()) );
1521 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1522 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof(nMD5Sum
) );
1524 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1525 &io_rProperties
.EncryptionKey
[0], SECUR_128BIT_KEY
, NULL
, 0 ); //destination data area
1526 rtl_cipher_encodeARCFOUR( aCipher
, nMD5Sum
, sizeof( nMD5Sum
), // the data to be encrypted
1527 &io_rProperties
.UValue
[0], sizeof( nMD5Sum
) ); //encrypted data, stored in class data member
1530 sal_uInt8 nLocalKey
[SECUR_128BIT_KEY
];
1532 for( i
= 1; i
<= 19; i
++ ) // do it 19 times, start with 1
1534 for( y
= 0; y
< sizeof( nLocalKey
) ; y
++ )
1535 nLocalKey
[y
] = (sal_uInt8
)( io_rProperties
.EncryptionKey
[y
] ^ i
);
1537 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1538 nLocalKey
, SECUR_128BIT_KEY
, // key and key length
1539 NULL
, 0 ); //destination data area, on init can be NULL
1540 rtl_cipher_encodeARCFOUR( aCipher
, &io_rProperties
.UValue
[0], SECUR_128BIT_KEY
, // the data to be encrypted
1541 &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 if (rGradient
.GetSteps() > 0)
2060 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */