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 "comphelper/processfactory.hxx"
35 #include "com/sun/star/beans/PropertyValue.hpp"
36 #include "com/sun/star/io/XSeekable.hpp"
37 #include "com/sun/star/graphic/GraphicProvider.hpp"
38 #include "com/sun/star/graphic/XGraphicProvider.hpp"
40 #include "cppuhelper/implbase1.hxx"
42 #include <rtl/digest.h>
44 #undef USE_PDFGRADIENTS
48 using namespace com::sun::star
;
49 using namespace com::sun::star::uno
;
50 using namespace com::sun::star::beans
;
52 // -----------------------------------------------------------------------------
54 void PDFWriterImpl::implWriteGradient( const 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 // -----------------------------------------------------------------------------
69 void PDFWriterImpl::implWriteBitmapEx( const Point
& i_rPoint
, const Size
& i_rSize
, const BitmapEx
& i_rBitmapEx
,
70 VirtualDevice
* i_pDummyVDev
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
)
72 if ( !i_rBitmapEx
.IsEmpty() && i_rSize
.Width() && i_rSize
.Height() )
74 BitmapEx
aBitmapEx( i_rBitmapEx
);
75 Point
aPoint( i_rPoint
);
76 Size
aSize( i_rSize
);
78 // #i19065# Negative sizes have mirror semantics on
79 // OutputDevice. BitmapEx and co. have no idea about that, so
80 // perform that _before_ doing anything with aBitmapEx.
81 sal_uLong
nMirrorFlags(BMP_MIRROR_NONE
);
82 if( aSize
.Width() < 0 )
85 aPoint
.X() -= aSize
.Width();
86 nMirrorFlags
|= BMP_MIRROR_HORZ
;
88 if( aSize
.Height() < 0 )
91 aPoint
.Y() -= aSize
.Height();
92 nMirrorFlags
|= BMP_MIRROR_VERT
;
95 if( nMirrorFlags
!= BMP_MIRROR_NONE
)
97 aBitmapEx
.Mirror( nMirrorFlags
);
99 if( i_rContext
.m_nMaxImageResolution
> 50 )
101 // do downsampling if necessary
102 const Size
aDstSizeTwip( i_pDummyVDev
->PixelToLogic( i_pDummyVDev
->LogicToPixel( aSize
), MAP_TWIP
) );
103 const Size
aBmpSize( aBitmapEx
.GetSizePixel() );
104 const double fBmpPixelX
= aBmpSize
.Width();
105 const double fBmpPixelY
= aBmpSize
.Height();
106 const double fMaxPixelX
= aDstSizeTwip
.Width() * i_rContext
.m_nMaxImageResolution
/ 1440.0;
107 const double fMaxPixelY
= aDstSizeTwip
.Height() * i_rContext
.m_nMaxImageResolution
/ 1440.0;
109 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
110 if( ( ( fBmpPixelX
> ( fMaxPixelX
+ 4 ) ) ||
111 ( fBmpPixelY
> ( fMaxPixelY
+ 4 ) ) ) &&
112 ( fBmpPixelY
> 0.0 ) && ( fMaxPixelY
> 0.0 ) )
116 const double fBmpWH
= fBmpPixelX
/ fBmpPixelY
;
117 const double fMaxWH
= fMaxPixelX
/ fMaxPixelY
;
119 if( fBmpWH
< fMaxWH
)
121 aNewBmpSize
.Width() = FRound( fMaxPixelY
* fBmpWH
);
122 aNewBmpSize
.Height() = FRound( fMaxPixelY
);
124 else if( fBmpWH
> 0.0 )
126 aNewBmpSize
.Width() = FRound( fMaxPixelX
);
127 aNewBmpSize
.Height() = FRound( fMaxPixelX
/ fBmpWH
);
130 if( aNewBmpSize
.Width() && aNewBmpSize
.Height() )
132 // #i121233# Use best quality for PDF exports
133 aBitmapEx
.Scale( aNewBmpSize
, BMP_SCALE_BESTQUALITY
);
137 aBitmapEx
.SetEmpty();
142 const Size
aSizePixel( aBitmapEx
.GetSizePixel() );
143 if ( aSizePixel
.Width() && aSizePixel
.Height() )
145 if( m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
)
147 BmpConversion eConv
= BMP_CONVERSION_8BIT_GREYS
;
148 int nDepth
= aBitmapEx
.GetBitmap().GetBitCount();
150 eConv
= BMP_CONVERSION_4BIT_GREYS
;
152 aBitmapEx
.Convert( eConv
);
154 sal_Bool bUseJPGCompression
= !i_rContext
.m_bOnlyLosslessCompression
;
155 if ( ( aSizePixel
.Width() < 32 ) || ( aSizePixel
.Height() < 32 ) )
156 bUseJPGCompression
= sal_False
;
158 SvMemoryStream aStrm
;
161 bool bTrueColorJPG
= true;
162 if ( bUseJPGCompression
)
164 sal_uInt32 nZippedFileSize
; // sj: we will calculate the filesize of a zipped bitmap
165 { // to determine if jpeg compression is useful
166 SvMemoryStream aTemp
;
167 aTemp
.SetCompressMode( aTemp
.GetCompressMode() | COMPRESSMODE_ZBITMAP
);
168 aTemp
.SetVersion( SOFFICE_FILEFORMAT_40
); // sj: up from version 40 our bitmap stream operator
169 aTemp
<< aBitmapEx
; // is capable of zlib stream compression
170 aTemp
.Seek( STREAM_SEEK_TO_END
);
171 nZippedFileSize
= aTemp
.Tell();
173 if ( aBitmapEx
.IsTransparent() )
175 if ( aBitmapEx
.IsAlpha() )
176 aMask
= aBitmapEx
.GetAlpha().GetBitmap();
178 aMask
= aBitmapEx
.GetMask();
180 Graphic
aGraphic( aBitmapEx
.GetBitmap() );
181 sal_Int32 nColorMode
= 0;
183 Sequence
< PropertyValue
> aFilterData( 2 );
184 aFilterData
[ 0 ].Name
= OUString( "Quality" );
185 aFilterData
[ 0 ].Value
<<= sal_Int32(i_rContext
.m_nJPEGQuality
);
186 aFilterData
[ 1 ].Name
= OUString( "ColorMode" );
187 aFilterData
[ 1 ].Value
<<= nColorMode
;
191 uno::Reference
< io::XStream
> xStream
= new utl::OStreamWrapper( aStrm
);
192 uno::Reference
< io::XSeekable
> xSeekable( xStream
, UNO_QUERY_THROW
);
193 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
194 uno::Reference
< graphic::XGraphicProvider
> xGraphicProvider( graphic::GraphicProvider::create(xContext
) );
195 uno::Reference
< graphic::XGraphic
> xGraphic( aGraphic
.GetXGraphic() );
196 uno::Reference
< io::XOutputStream
> xOut( xStream
->getOutputStream() );
197 OUString
aMimeType("image/jpeg");
198 uno::Sequence
< beans::PropertyValue
> aOutMediaProperties( 3 );
199 aOutMediaProperties
[0].Name
= OUString("OutputStream");
200 aOutMediaProperties
[0].Value
<<= xOut
;
201 aOutMediaProperties
[1].Name
= OUString("MimeType");
202 aOutMediaProperties
[1].Value
<<= aMimeType
;
203 aOutMediaProperties
[2].Name
= OUString("FilterData");
204 aOutMediaProperties
[2].Value
<<= aFilterData
;
205 xGraphicProvider
->storeGraphic( xGraphic
, aOutMediaProperties
);
207 if ( xSeekable
->getLength() > nZippedFileSize
)
209 bUseJPGCompression
= sal_False
;
213 aStrm
.Seek( STREAM_SEEK_TO_END
);
215 xSeekable
->seek( 0 );
216 Sequence
< PropertyValue
> aArgs( 1 );
217 aArgs
[ 0 ].Name
= OUString("InputStream");
218 aArgs
[ 0 ].Value
<<= xStream
;
219 uno::Reference
< XPropertySet
> xPropSet( xGraphicProvider
->queryGraphicDescriptor( aArgs
) );
222 sal_Int16 nBitsPerPixel
= 24;
223 if ( xPropSet
->getPropertyValue( OUString("BitsPerPixel") ) >>= nBitsPerPixel
)
225 bTrueColorJPG
= nBitsPerPixel
!= 8;
230 catch( uno::Exception
& )
232 bUseJPGCompression
= sal_False
;
235 if ( bUseJPGCompression
)
236 m_rOuterFace
.DrawJPGBitmap( aStrm
, bTrueColorJPG
, aSizePixel
, Rectangle( aPoint
, aSize
), aMask
);
237 else if ( aBitmapEx
.IsTransparent() )
238 m_rOuterFace
.DrawBitmapEx( aPoint
, aSize
, aBitmapEx
);
240 m_rOuterFace
.DrawBitmap( aPoint
, aSize
, aBitmapEx
.GetBitmap() );
246 // -----------------------------------------------------------------------------
248 void PDFWriterImpl::playMetafile( const GDIMetaFile
& i_rMtf
, vcl::PDFExtOutDevData
* i_pOutDevData
, const vcl::PDFWriter::PlayMetafileContext
& i_rContext
, VirtualDevice
* pDummyVDev
)
250 bool bAssertionFired( false );
252 VirtualDevice
* pPrivateDevice
= NULL
;
255 pPrivateDevice
= pDummyVDev
= new VirtualDevice();
256 pDummyVDev
->EnableOutput( sal_False
);
257 pDummyVDev
->SetMapMode( i_rMtf
.GetPrefMapMode() );
259 GDIMetaFile
aMtf( i_rMtf
);
261 for( sal_uInt32 i
= 0, nCount
= aMtf
.GetActionSize(); i
< (sal_uInt32
)nCount
; )
263 if ( !i_pOutDevData
|| !i_pOutDevData
->PlaySyncPageAct( m_rOuterFace
, i
) )
265 const MetaAction
* pAction
= aMtf
.GetAction( i
);
266 const sal_uInt16 nType
= pAction
->GetType();
270 case( META_PIXEL_ACTION
):
272 const MetaPixelAction
* pA
= (const MetaPixelAction
*) pAction
;
273 m_rOuterFace
.DrawPixel( pA
->GetPoint(), pA
->GetColor() );
277 case( META_POINT_ACTION
):
279 const MetaPointAction
* pA
= (const MetaPointAction
*) pAction
;
280 m_rOuterFace
.DrawPixel( pA
->GetPoint() );
284 case( META_LINE_ACTION
):
286 const MetaLineAction
* pA
= (const MetaLineAction
*) pAction
;
287 if ( pA
->GetLineInfo().IsDefault() )
288 m_rOuterFace
.DrawLine( pA
->GetStartPoint(), pA
->GetEndPoint() );
290 m_rOuterFace
.DrawLine( pA
->GetStartPoint(), pA
->GetEndPoint(), pA
->GetLineInfo() );
294 case( META_RECT_ACTION
):
296 const MetaRectAction
* pA
= (const MetaRectAction
*) pAction
;
297 m_rOuterFace
.DrawRect( pA
->GetRect() );
301 case( META_ROUNDRECT_ACTION
):
303 const MetaRoundRectAction
* pA
= (const MetaRoundRectAction
*) pAction
;
304 m_rOuterFace
.DrawRect( pA
->GetRect(), pA
->GetHorzRound(), pA
->GetVertRound() );
308 case( META_ELLIPSE_ACTION
):
310 const MetaEllipseAction
* pA
= (const MetaEllipseAction
*) pAction
;
311 m_rOuterFace
.DrawEllipse( pA
->GetRect() );
315 case( META_ARC_ACTION
):
317 const MetaArcAction
* pA
= (const MetaArcAction
*) pAction
;
318 m_rOuterFace
.DrawArc( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
322 case( META_PIE_ACTION
):
324 const MetaArcAction
* pA
= (const MetaArcAction
*) pAction
;
325 m_rOuterFace
.DrawPie( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
329 case( META_CHORD_ACTION
):
331 const MetaChordAction
* pA
= (const MetaChordAction
*) pAction
;
332 m_rOuterFace
.DrawChord( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint() );
336 case( META_POLYGON_ACTION
):
338 const MetaPolygonAction
* pA
= (const MetaPolygonAction
*) pAction
;
339 m_rOuterFace
.DrawPolygon( pA
->GetPolygon() );
343 case( META_POLYLINE_ACTION
):
345 const MetaPolyLineAction
* pA
= (const MetaPolyLineAction
*) pAction
;
346 if ( pA
->GetLineInfo().IsDefault() )
347 m_rOuterFace
.DrawPolyLine( pA
->GetPolygon() );
349 m_rOuterFace
.DrawPolyLine( pA
->GetPolygon(), pA
->GetLineInfo() );
353 case( META_POLYPOLYGON_ACTION
):
355 const MetaPolyPolygonAction
* pA
= (const MetaPolyPolygonAction
*) pAction
;
356 m_rOuterFace
.DrawPolyPolygon( pA
->GetPolyPolygon() );
360 case( META_GRADIENT_ACTION
):
362 const MetaGradientAction
* pA
= (const MetaGradientAction
*) pAction
;
363 #ifdef USE_PDFGRADIENTS
364 m_rOuterFace
.DrawGradient( pA
->GetRect(), pA
->GetGradient() );
366 const PolyPolygon
aPolyPoly( pA
->GetRect() );
367 implWriteGradient( aPolyPoly
, pA
->GetGradient(), pDummyVDev
, i_rContext
);
372 case( META_GRADIENTEX_ACTION
):
374 const MetaGradientExAction
* pA
= (const MetaGradientExAction
*) pAction
;
375 #ifdef USE_PDFGRADIENTS
376 m_rOuterFace
.DrawGradient( pA
->GetPolyPolygon(), pA
->GetGradient() );
378 implWriteGradient( pA
->GetPolyPolygon(), pA
->GetGradient(), pDummyVDev
, i_rContext
);
383 case META_HATCH_ACTION
:
385 const MetaHatchAction
* pA
= (const MetaHatchAction
*) pAction
;
386 m_rOuterFace
.DrawHatch( pA
->GetPolyPolygon(), pA
->GetHatch() );
390 case( META_TRANSPARENT_ACTION
):
392 const MetaTransparentAction
* pA
= (const MetaTransparentAction
*) pAction
;
393 m_rOuterFace
.DrawTransparent( pA
->GetPolyPolygon(), pA
->GetTransparence() );
397 case( META_FLOATTRANSPARENT_ACTION
):
399 const MetaFloatTransparentAction
* pA
= (const MetaFloatTransparentAction
*) pAction
;
401 GDIMetaFile
aTmpMtf( pA
->GetGDIMetaFile() );
402 const Point
& rPos
= pA
->GetPoint();
403 const Size
& rSize
= pA
->GetSize();
404 const Gradient
& rTransparenceGradient
= pA
->GetGradient();
406 // special case constant alpha value
407 if( rTransparenceGradient
.GetStartColor() == rTransparenceGradient
.GetEndColor() )
409 const Color
aTransCol( rTransparenceGradient
.GetStartColor() );
410 const sal_uInt16 nTransPercent
= aTransCol
.GetLuminance() * 100 / 255;
411 m_rOuterFace
.BeginTransparencyGroup();
412 playMetafile( aTmpMtf
, NULL
, i_rContext
, pDummyVDev
);
413 m_rOuterFace
.EndTransparencyGroup( Rectangle( rPos
, rSize
), nTransPercent
);
417 const Size
aDstSizeTwip( pDummyVDev
->PixelToLogic( pDummyVDev
->LogicToPixel( rSize
), MAP_TWIP
) );
419 // i#115962# Always use at least 300 DPI for bitmap conversion of transparence gradients,
420 // else the quality is not acceptable (see bugdoc as example)
421 sal_Int32
nMaxBmpDPI(300);
423 if( i_rContext
.m_nMaxImageResolution
> 50 )
425 if ( nMaxBmpDPI
> i_rContext
.m_nMaxImageResolution
)
426 nMaxBmpDPI
= i_rContext
.m_nMaxImageResolution
;
428 const sal_Int32 nPixelX
= (sal_Int32
)((double)aDstSizeTwip
.Width() * (double)nMaxBmpDPI
/ 1440.0);
429 const sal_Int32 nPixelY
= (sal_Int32
)((double)aDstSizeTwip
.Height() * (double)nMaxBmpDPI
/ 1440.0);
430 if ( nPixelX
&& nPixelY
)
432 Size
aDstSizePixel( nPixelX
, nPixelY
);
433 VirtualDevice
* pVDev
= new VirtualDevice
;
434 if( pVDev
->SetOutputSizePixel( aDstSizePixel
) )
436 Bitmap aPaint
, aMask
;
440 MapMode
aMapMode( pDummyVDev
->GetMapMode() );
441 aMapMode
.SetOrigin( aPoint
);
442 pVDev
->SetMapMode( aMapMode
);
443 Size
aDstSize( pVDev
->PixelToLogic( aDstSizePixel
) );
445 Point
aMtfOrigin( aTmpMtf
.GetPrefMapMode().GetOrigin() );
446 if ( aMtfOrigin
.X() || aMtfOrigin
.Y() )
447 aTmpMtf
.Move( -aMtfOrigin
.X(), -aMtfOrigin
.Y() );
448 double fScaleX
= (double)aDstSize
.Width() / (double)aTmpMtf
.GetPrefSize().Width();
449 double fScaleY
= (double)aDstSize
.Height() / (double)aTmpMtf
.GetPrefSize().Height();
450 if( fScaleX
!= 1.0 || fScaleY
!= 1.0 )
451 aTmpMtf
.Scale( fScaleX
, fScaleY
);
452 aTmpMtf
.SetPrefMapMode( aMapMode
);
454 // create paint bitmap
456 aTmpMtf
.Play( pVDev
, aPoint
, aDstSize
);
459 pVDev
->EnableMapMode( sal_False
);
460 aPaint
= pVDev
->GetBitmap( aPoint
, aDstSizePixel
);
461 pVDev
->EnableMapMode( sal_True
);
463 // create mask bitmap
464 pVDev
->SetLineColor( COL_BLACK
);
465 pVDev
->SetFillColor( COL_BLACK
);
466 pVDev
->DrawRect( Rectangle( aPoint
, aDstSize
) );
467 pVDev
->SetDrawMode( DRAWMODE_WHITELINE
| DRAWMODE_WHITEFILL
| DRAWMODE_WHITETEXT
|
468 DRAWMODE_WHITEBITMAP
| DRAWMODE_WHITEGRADIENT
);
470 aTmpMtf
.Play( pVDev
, aPoint
, aDstSize
);
472 pVDev
->EnableMapMode( sal_False
);
473 aMask
= pVDev
->GetBitmap( aPoint
, aDstSizePixel
);
474 pVDev
->EnableMapMode( sal_True
);
476 // create alpha mask from gradient
477 pVDev
->SetDrawMode( DRAWMODE_GRAYGRADIENT
);
478 pVDev
->DrawGradient( Rectangle( aPoint
, aDstSize
), rTransparenceGradient
);
479 pVDev
->SetDrawMode( DRAWMODE_DEFAULT
);
480 pVDev
->EnableMapMode( sal_False
);
481 pVDev
->DrawMask( aPoint
, aDstSizePixel
, aMask
, Color( COL_WHITE
) );
482 aAlpha
= pVDev
->GetBitmap( aPoint
, aDstSizePixel
);
483 implWriteBitmapEx( rPos
, rSize
, BitmapEx( aPaint
, aAlpha
), pDummyVDev
, i_rContext
);
491 case( META_EPS_ACTION
):
493 const MetaEPSAction
* pA
= (const MetaEPSAction
*) pAction
;
494 const GDIMetaFile
aSubstitute( pA
->GetSubstitute() );
499 MapMode
aMapMode( aSubstitute
.GetPrefMapMode() );
500 Size
aOutSize( pDummyVDev
->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( pDummyVDev
->LogicToLogic( pA
->GetPoint(), pDummyVDev
->GetMapMode(), aMapMode
) );
505 m_rOuterFace
.SetMapMode( aMapMode
);
506 pDummyVDev
->SetMapMode( aMapMode
);
507 playMetafile( aSubstitute
, NULL
, i_rContext
, pDummyVDev
);
513 case( META_COMMENT_ACTION
):
514 if( ! i_rContext
.m_bTransparenciesWereRemoved
)
516 const MetaCommentAction
* pA
= (const MetaCommentAction
*) pAction
;
518 if( pA
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
520 const MetaGradientExAction
* pGradAction
= NULL
;
521 sal_Bool bDone
= sal_False
;
523 while( !bDone
&& ( ++i
< nCount
) )
525 pAction
= aMtf
.GetAction( i
);
527 if( pAction
->GetType() == META_GRADIENTEX_ACTION
)
528 pGradAction
= (const MetaGradientExAction
*) pAction
;
529 else if( ( pAction
->GetType() == META_COMMENT_ACTION
) &&
530 ( ( (const MetaCommentAction
*) pAction
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END")) )
538 #ifdef USE_PDFGRADIENTS
539 m_rOuterFace
.DrawGradient( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient() );
541 implWriteGradient( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient(), pDummyVDev
, i_rContext
);
547 const sal_uInt8
* pData
= pA
->GetData();
550 SvMemoryStream
aMemStm( (void*)pData
, pA
->GetDataSize(), STREAM_READ
);
551 sal_Bool bSkipSequence
= sal_False
;
554 if( pA
->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
556 sSeqEnd
= OString("XPATHSTROKE_SEQ_END");
557 SvtGraphicStroke aStroke
;
561 aStroke
.getPath( aPath
);
563 PolyPolygon aStartArrow
;
564 PolyPolygon aEndArrow
;
565 double fTransparency( aStroke
.getTransparency() );
566 double fStrokeWidth( aStroke
.getStrokeWidth() );
567 SvtGraphicStroke::DashArray aDashArray
;
569 aStroke
.getStartArrow( aStartArrow
);
570 aStroke
.getEndArrow( aEndArrow
);
571 aStroke
.getDashArray( aDashArray
);
573 bSkipSequence
= sal_True
;
574 if ( aStartArrow
.Count() || aEndArrow
.Count() )
575 bSkipSequence
= sal_False
;
576 if ( aDashArray
.size() && ( fStrokeWidth
!= 0.0 ) && ( fTransparency
== 0.0 ) )
577 bSkipSequence
= sal_False
;
580 PDFWriter::ExtLineInfo aInfo
;
581 aInfo
.m_fLineWidth
= fStrokeWidth
;
582 aInfo
.m_fTransparency
= fTransparency
;
583 aInfo
.m_fMiterLimit
= aStroke
.getMiterLimit();
584 switch( aStroke
.getCapType() )
587 case SvtGraphicStroke::capButt
: aInfo
.m_eCap
= PDFWriter::capButt
;break;
588 case SvtGraphicStroke::capRound
: aInfo
.m_eCap
= PDFWriter::capRound
;break;
589 case SvtGraphicStroke::capSquare
: aInfo
.m_eCap
= PDFWriter::capSquare
;break;
591 switch( aStroke
.getJoinType() )
594 case SvtGraphicStroke::joinMiter
: aInfo
.m_eJoin
= PDFWriter::joinMiter
;break;
595 case SvtGraphicStroke::joinRound
: aInfo
.m_eJoin
= PDFWriter::joinRound
;break;
596 case SvtGraphicStroke::joinBevel
: aInfo
.m_eJoin
= PDFWriter::joinBevel
;break;
597 case SvtGraphicStroke::joinNone
:
598 aInfo
.m_eJoin
= PDFWriter::joinMiter
;
599 aInfo
.m_fMiterLimit
= 0.0;
602 aInfo
.m_aDashArray
= aDashArray
;
604 if(SvtGraphicStroke::joinNone
== aStroke
.getJoinType()
605 && fStrokeWidth
> 0.0)
607 // emulate no edge rounding by handling single edges
608 const sal_uInt16
nPoints(aPath
.GetSize());
609 const bool bCurve(aPath
.HasFlags());
611 for(sal_uInt16
a(0); a
+ 1 < nPoints
; a
++)
614 && POLY_NORMAL
!= aPath
.GetFlags(a
+ 1)
616 && POLY_NORMAL
!= aPath
.GetFlags(a
+ 2)
619 const Polygon
aSnippet(4,
620 aPath
.GetConstPointAry() + a
,
621 aPath
.GetConstFlagAry() + a
);
622 m_rOuterFace
.DrawPolyLine( aSnippet
, aInfo
);
627 const Polygon
aSnippet(2,
628 aPath
.GetConstPointAry() + a
);
629 m_rOuterFace
.DrawPolyLine( aSnippet
, aInfo
);
635 m_rOuterFace
.DrawPolyLine( aPath
, aInfo
);
639 else if ( pA
->GetComment() == "XPATHFILL_SEQ_BEGIN" )
641 sSeqEnd
= OString("XPATHFILL_SEQ_END");
642 SvtGraphicFill aFill
;
645 if ( ( aFill
.getFillType() == SvtGraphicFill::fillSolid
) && ( aFill
.getFillRule() == SvtGraphicFill::fillEvenOdd
) )
647 double fTransparency
= aFill
.getTransparency();
648 if ( fTransparency
== 0.0 )
651 aFill
.getPath( aPath
);
653 bSkipSequence
= sal_True
;
654 m_rOuterFace
.DrawPolyPolygon( aPath
);
656 else if ( fTransparency
== 1.0 )
657 bSkipSequence
= sal_True
;
659 /* #i81548# removing optimization for fill textures, because most of the texture settings are not
660 exported properly. In OpenOffice 3.1 the drawing layer will support graphic primitives, then it
661 will not be a problem to optimize the filltexture export. But for wysiwyg is more important than
663 else if( aFill.getFillType() == SvtGraphicFill::fillTexture && aFill.isTiling() )
665 sal_Int32 nPattern = mnCachePatternId;
666 Graphic aPatternGraphic;
667 aFill.getGraphic( aPatternGraphic );
668 bool bUseCache = false;
669 SvtGraphicFill::Transform aPatTransform;
670 aFill.getTransform( aPatTransform );
672 if( mnCachePatternId >= 0 )
674 SvtGraphicFill::Transform aCacheTransform;
675 maCacheFill.getTransform( aCacheTransform );
676 if( aCacheTransform.matrix[0] == aPatTransform.matrix[0] &&
677 aCacheTransform.matrix[1] == aPatTransform.matrix[1] &&
678 aCacheTransform.matrix[2] == aPatTransform.matrix[2] &&
679 aCacheTransform.matrix[3] == aPatTransform.matrix[3] &&
680 aCacheTransform.matrix[4] == aPatTransform.matrix[4] &&
681 aCacheTransform.matrix[5] == aPatTransform.matrix[5]
684 Graphic aCacheGraphic;
685 maCacheFill.getGraphic( aCacheGraphic );
686 if( aCacheGraphic == aPatternGraphic )
694 // paint graphic to metafile
695 GDIMetaFile aPattern;
696 pDummyVDev->SetConnectMetaFile( &aPattern );
698 pDummyVDev->SetMapMode( aPatternGraphic.GetPrefMapMode() );
700 aPatternGraphic.Draw( &rDummyVDev, Point( 0, 0 ) );
702 pDummyVDev->SetConnectMetaFile( NULL );
703 aPattern.WindStart();
705 MapMode aPatternMapMode( aPatternGraphic.GetPrefMapMode() );
706 // prepare pattern from metafile
707 Size aPrefSize( aPatternGraphic.GetPrefSize() );
708 // FIXME: this magic -1 shouldn't be necessary
709 aPrefSize.Width() -= 1;
710 aPrefSize.Height() -= 1;
711 aPrefSize = m_rOuterFace.GetReferenceDevice()->
712 LogicToLogic( aPrefSize,
714 &m_rOuterFace.GetReferenceDevice()->GetMapMode() );
715 // build bounding rectangle of pattern
716 Rectangle aBound( Point( 0, 0 ), aPrefSize );
717 m_rOuterFace.BeginPattern( aBound );
720 m_rOuterFace.SetMapMode( aPatternMapMode );
721 pDummyVDev->SetMapMode( aPatternMapMode );
722 ImplWriteActions( m_rOuterFace, NULL, aPattern, rDummyVDev );
726 nPattern = m_rOuterFace.EndPattern( aPatTransform );
728 // try some caching and reuse pattern
729 mnCachePatternId = nPattern;
733 // draw polypolygon with pattern fill
735 aFill.getPath( aPath );
736 m_rOuterFace.DrawPolyPolygon( aPath, nPattern, aFill.getFillRule() == SvtGraphicFill::fillEvenOdd );
738 bSkipSequence = sal_True;
744 while( ++i
< nCount
)
746 pAction
= aMtf
.GetAction( i
);
747 if ( pAction
->GetType() == META_COMMENT_ACTION
)
749 OString
sComment( ((MetaCommentAction
*)pAction
)->GetComment() );
750 if (sComment
== sSeqEnd
)
754 // the replacement action for stroke is a filled rectangle
755 // the set fillcolor of the replacement is part of the graphics
756 // state and must not be skipped
757 else if( pAction
->GetType() == META_FILLCOLOR_ACTION
)
759 const MetaFillColorAction
* pMA
= (const MetaFillColorAction
*) pAction
;
760 if( pMA
->IsSetting() )
761 m_rOuterFace
.SetFillColor( pMA
->GetColor() );
763 m_rOuterFace
.SetFillColor();
772 case( META_BMP_ACTION
):
774 const MetaBmpAction
* pA
= (const MetaBmpAction
*) pAction
;
775 BitmapEx
aBitmapEx( pA
->GetBitmap() );
776 Size
aSize( OutputDevice::LogicToLogic( aBitmapEx
.GetPrefSize(),
777 aBitmapEx
.GetPrefMapMode(), pDummyVDev
->GetMapMode() ) );
778 if( ! ( aSize
.Width() && aSize
.Height() ) )
779 aSize
= pDummyVDev
->PixelToLogic( aBitmapEx
.GetSizePixel() );
780 implWriteBitmapEx( pA
->GetPoint(), aSize
, aBitmapEx
, pDummyVDev
, i_rContext
);
784 case( META_BMPSCALE_ACTION
):
786 const MetaBmpScaleAction
* pA
= (const MetaBmpScaleAction
*) pAction
;
787 implWriteBitmapEx( pA
->GetPoint(), pA
->GetSize(), BitmapEx( pA
->GetBitmap() ), pDummyVDev
, i_rContext
);
791 case( META_BMPSCALEPART_ACTION
):
793 const MetaBmpScalePartAction
* pA
= (const MetaBmpScalePartAction
*) pAction
;
794 BitmapEx
aBitmapEx( pA
->GetBitmap() );
795 aBitmapEx
.Crop( Rectangle( pA
->GetSrcPoint(), pA
->GetSrcSize() ) );
796 implWriteBitmapEx( pA
->GetDestPoint(), pA
->GetDestSize(), aBitmapEx
, pDummyVDev
, i_rContext
);
800 case( META_BMPEX_ACTION
):
802 const MetaBmpExAction
* pA
= (const MetaBmpExAction
*) pAction
;
803 BitmapEx
aBitmapEx( pA
->GetBitmapEx() );
804 Size
aSize( OutputDevice::LogicToLogic( aBitmapEx
.GetPrefSize(),
805 aBitmapEx
.GetPrefMapMode(), pDummyVDev
->GetMapMode() ) );
806 implWriteBitmapEx( pA
->GetPoint(), aSize
, aBitmapEx
, pDummyVDev
, i_rContext
);
810 case( META_BMPEXSCALE_ACTION
):
812 const MetaBmpExScaleAction
* pA
= (const MetaBmpExScaleAction
*) pAction
;
813 implWriteBitmapEx( pA
->GetPoint(), pA
->GetSize(), pA
->GetBitmapEx(), pDummyVDev
, i_rContext
);
817 case( META_BMPEXSCALEPART_ACTION
):
819 const MetaBmpExScalePartAction
* pA
= (const MetaBmpExScalePartAction
*) pAction
;
820 BitmapEx
aBitmapEx( pA
->GetBitmapEx() );
821 aBitmapEx
.Crop( Rectangle( pA
->GetSrcPoint(), pA
->GetSrcSize() ) );
822 implWriteBitmapEx( pA
->GetDestPoint(), pA
->GetDestSize(), aBitmapEx
, pDummyVDev
, i_rContext
);
826 case( META_MASK_ACTION
):
827 case( META_MASKSCALE_ACTION
):
828 case( META_MASKSCALEPART_ACTION
):
830 OSL_TRACE( "MetaMask...Action not supported yet" );
834 case( META_TEXT_ACTION
):
836 const MetaTextAction
* pA
= (const MetaTextAction
*) pAction
;
837 m_rOuterFace
.DrawText( pA
->GetPoint(), String( pA
->GetText(), pA
->GetIndex(), pA
->GetLen() ) );
841 case( META_TEXTRECT_ACTION
):
843 const MetaTextRectAction
* pA
= (const MetaTextRectAction
*) pAction
;
844 m_rOuterFace
.DrawText( pA
->GetRect(), String( pA
->GetText() ), pA
->GetStyle() );
848 case( META_TEXTARRAY_ACTION
):
850 const MetaTextArrayAction
* pA
= (const MetaTextArrayAction
*) pAction
;
851 m_rOuterFace
.DrawTextArray( pA
->GetPoint(), pA
->GetText(), pA
->GetDXArray(), pA
->GetIndex(), pA
->GetLen() );
855 case( META_STRETCHTEXT_ACTION
):
857 const MetaStretchTextAction
* pA
= (const MetaStretchTextAction
*) pAction
;
858 m_rOuterFace
.DrawStretchText( pA
->GetPoint(), pA
->GetWidth(), pA
->GetText(), pA
->GetIndex(), pA
->GetLen() );
863 case( META_TEXTLINE_ACTION
):
865 const MetaTextLineAction
* pA
= (const MetaTextLineAction
*) pAction
;
866 m_rOuterFace
.DrawTextLine( pA
->GetStartPoint(), pA
->GetWidth(), pA
->GetStrikeout(), pA
->GetUnderline(), pA
->GetOverline() );
871 case( META_CLIPREGION_ACTION
):
873 const MetaClipRegionAction
* pA
= (const MetaClipRegionAction
*) pAction
;
875 if( pA
->IsClipping() )
877 if( pA
->GetRegion().IsEmpty() )
878 m_rOuterFace
.SetClipRegion( basegfx::B2DPolyPolygon() );
881 Region
aReg( pA
->GetRegion() );
882 m_rOuterFace
.SetClipRegion( aReg
.ConvertToB2DPolyPolygon() );
886 m_rOuterFace
.SetClipRegion();
890 case( META_ISECTRECTCLIPREGION_ACTION
):
892 const MetaISectRectClipRegionAction
* pA
= (const MetaISectRectClipRegionAction
*) pAction
;
893 m_rOuterFace
.IntersectClipRegion( pA
->GetRect() );
897 case( META_ISECTREGIONCLIPREGION_ACTION
):
899 const MetaISectRegionClipRegionAction
* pA
= (const MetaISectRegionClipRegionAction
*) pAction
;
900 Region
aReg( pA
->GetRegion() );
901 m_rOuterFace
.IntersectClipRegion( aReg
.ConvertToB2DPolyPolygon() );
905 case( META_MOVECLIPREGION_ACTION
):
907 const MetaMoveClipRegionAction
* pA
= (const MetaMoveClipRegionAction
*) pAction
;
908 m_rOuterFace
.MoveClipRegion( pA
->GetHorzMove(), pA
->GetVertMove() );
912 case( META_MAPMODE_ACTION
):
914 const_cast< MetaAction
* >( pAction
)->Execute( pDummyVDev
);
915 m_rOuterFace
.SetMapMode( pDummyVDev
->GetMapMode() );
919 case( META_LINECOLOR_ACTION
):
921 const MetaLineColorAction
* pA
= (const MetaLineColorAction
*) pAction
;
923 if( pA
->IsSetting() )
924 m_rOuterFace
.SetLineColor( pA
->GetColor() );
926 m_rOuterFace
.SetLineColor();
930 case( META_FILLCOLOR_ACTION
):
932 const MetaFillColorAction
* pA
= (const MetaFillColorAction
*) pAction
;
934 if( pA
->IsSetting() )
935 m_rOuterFace
.SetFillColor( pA
->GetColor() );
937 m_rOuterFace
.SetFillColor();
941 case( META_TEXTLINECOLOR_ACTION
):
943 const MetaTextLineColorAction
* pA
= (const MetaTextLineColorAction
*) pAction
;
945 if( pA
->IsSetting() )
946 m_rOuterFace
.SetTextLineColor( pA
->GetColor() );
948 m_rOuterFace
.SetTextLineColor();
952 case( META_OVERLINECOLOR_ACTION
):
954 const MetaOverlineColorAction
* pA
= (const MetaOverlineColorAction
*) pAction
;
956 if( pA
->IsSetting() )
957 m_rOuterFace
.SetOverlineColor( pA
->GetColor() );
959 m_rOuterFace
.SetOverlineColor();
963 case( META_TEXTFILLCOLOR_ACTION
):
965 const MetaTextFillColorAction
* pA
= (const MetaTextFillColorAction
*) pAction
;
967 if( pA
->IsSetting() )
968 m_rOuterFace
.SetTextFillColor( pA
->GetColor() );
970 m_rOuterFace
.SetTextFillColor();
974 case( META_TEXTCOLOR_ACTION
):
976 const MetaTextColorAction
* pA
= (const MetaTextColorAction
*) pAction
;
977 m_rOuterFace
.SetTextColor( pA
->GetColor() );
981 case( META_TEXTALIGN_ACTION
):
983 const MetaTextAlignAction
* pA
= (const MetaTextAlignAction
*) pAction
;
984 m_rOuterFace
.SetTextAlign( pA
->GetTextAlign() );
988 case( META_FONT_ACTION
):
990 const MetaFontAction
* pA
= (const MetaFontAction
*) pAction
;
991 m_rOuterFace
.SetFont( pA
->GetFont() );
995 case( META_PUSH_ACTION
):
997 const MetaPushAction
* pA
= (const MetaPushAction
*) pAction
;
999 pDummyVDev
->Push( pA
->GetFlags() );
1000 m_rOuterFace
.Push( pA
->GetFlags() );
1004 case( META_POP_ACTION
):
1011 case( META_LAYOUTMODE_ACTION
):
1013 const MetaLayoutModeAction
* pA
= (const MetaLayoutModeAction
*) pAction
;
1014 m_rOuterFace
.SetLayoutMode( pA
->GetLayoutMode() );
1018 case META_TEXTLANGUAGE_ACTION
:
1020 const MetaTextLanguageAction
* pA
= (const MetaTextLanguageAction
*) pAction
;
1021 m_rOuterFace
.SetDigitLanguage( pA
->GetTextLanguage() );
1025 case( META_WALLPAPER_ACTION
):
1027 const MetaWallpaperAction
* pA
= (const MetaWallpaperAction
*) pAction
;
1028 m_rOuterFace
.DrawWallpaper( pA
->GetRect(), pA
->GetWallpaper() );
1032 case( META_RASTEROP_ACTION
):
1034 // !!! >>> we don't want to support this actions
1038 case( META_REFPOINT_ACTION
):
1040 // !!! >>> we don't want to support this actions
1045 // #i24604# Made assertion fire only once per
1046 // metafile. The asserted actions here are all
1048 if( !bAssertionFired
)
1050 bAssertionFired
= true;
1051 OSL_TRACE( "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered" );
1059 delete pPrivateDevice
;
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()
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 sal_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
= (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 are 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 legth: see algoritm 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 are 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 legth: see algoritm 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 memset( aPadOPW
, 0, sizeof(aPadOPW
) );
1224 memset( aPadUPW
, 0, sizeof(aPadUPW
) );
1230 bool PDFWriterImpl::prepareEncryption( const uno::Reference
< beans::XMaterialHolder
>& xEnc
)
1232 bool bSuccess
= false;
1233 EncHashTransporter
* pTransporter
= EncHashTransporter::getEncHashTransporter( xEnc
);
1236 sal_Int32 nKeyLength
= 0, nRC4KeyLength
= 0;
1237 sal_Int32 nAccessPermissions
= computeAccessPermissions( m_aContext
.Encryption
, nKeyLength
, nRC4KeyLength
);
1238 m_aContext
.Encryption
.OValue
= pTransporter
->getOValue();
1239 bSuccess
= computeUDictionaryValue( pTransporter
, m_aContext
.Encryption
, nKeyLength
, nAccessPermissions
);
1243 m_aContext
.Encryption
.OValue
.clear();
1244 m_aContext
.Encryption
.UValue
.clear();
1245 m_aContext
.Encryption
.EncryptionKey
.clear();
1250 sal_Int32
PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties
& i_rProperties
,
1251 sal_Int32
& o_rKeyLength
, sal_Int32
& o_rRC4KeyLength
)
1254 2) compute the access permissions, in numerical form
1256 the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
1257 - for 40 bit security the unused bit must be set to 1, since they are not used
1258 - for 128 bit security the same bit must be preset to 0 and set later if needed
1259 according to the table 3.15, pdf v 1.4 */
1260 sal_Int32 nAccessPermissions
= ( i_rProperties
.Security128bit
) ? 0xfffff0c0 : 0xffffffc0 ;
1262 /* check permissions for 40 bit security case */
1263 nAccessPermissions
|= ( i_rProperties
.CanPrintTheDocument
) ? 1 << 2 : 0;
1264 nAccessPermissions
|= ( i_rProperties
.CanModifyTheContent
) ? 1 << 3 : 0;
1265 nAccessPermissions
|= ( i_rProperties
.CanCopyOrExtract
) ? 1 << 4 : 0;
1266 nAccessPermissions
|= ( i_rProperties
.CanAddOrModify
) ? 1 << 5 : 0;
1267 o_rKeyLength
= SECUR_40BIT_KEY
;
1268 o_rRC4KeyLength
= SECUR_40BIT_KEY
+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5
1270 if( i_rProperties
.Security128bit
)
1272 o_rKeyLength
= SECUR_128BIT_KEY
;
1273 o_rRC4KeyLength
= 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum
1274 // permitted value is 16
1275 nAccessPermissions
|= ( i_rProperties
.CanFillInteractive
) ? 1 << 8 : 0;
1276 nAccessPermissions
|= ( i_rProperties
.CanExtractForAccessibility
) ? 1 << 9 : 0;
1277 nAccessPermissions
|= ( i_rProperties
.CanAssemble
) ? 1 << 10 : 0;
1278 nAccessPermissions
|= ( i_rProperties
.CanPrintFull
) ? 1 << 11 : 0;
1280 return nAccessPermissions
;
1283 /*************************************************************
1284 begin i12626 methods
1286 Implements Algorithm 3.2, step 1 only
1288 void PDFWriterImpl::padPassword( const OUString
& i_rPassword
, sal_uInt8
* o_pPaddedPW
)
1290 // get ansi-1252 version of the password string CHECKIT ! i12626
1291 OString
aString( OUStringToOString( i_rPassword
, RTL_TEXTENCODING_MS_1252
) );
1293 //copy the string to the target
1294 sal_Int32 nToCopy
= ( aString
.getLength() < ENCRYPTED_PWD_SIZE
) ? aString
.getLength() : ENCRYPTED_PWD_SIZE
;
1295 sal_Int32 nCurrentChar
;
1297 for( nCurrentChar
= 0; nCurrentChar
< nToCopy
; nCurrentChar
++ )
1298 o_pPaddedPW
[nCurrentChar
] = (sal_uInt8
)( aString
.getStr()[nCurrentChar
] );
1300 //pad it with standard byte string
1302 for( i
= nCurrentChar
, y
= 0 ; i
< ENCRYPTED_PWD_SIZE
; i
++, y
++ )
1303 o_pPaddedPW
[i
] = s_nPadString
[y
];
1306 /**********************************
1307 Algorithm 3.2 Compute the encryption key used
1309 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
1310 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
1311 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
1313 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
1316 bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter
* i_pTransporter
, vcl::PDFWriter::PDFEncryptionProperties
& io_rProperties
, sal_Int32 i_nAccessPermissions
)
1318 bool bSuccess
= true;
1319 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1321 // transporter contains an MD5 digest with the padded user password already
1322 rtlDigest aDigest
= i_pTransporter
->getUDigest();
1323 rtlDigestError nError
= rtl_Digest_E_None
;
1327 if( ! io_rProperties
.OValue
.empty() )
1328 nError
= rtl_digest_updateMD5( aDigest
, &io_rProperties
.OValue
[0] , sal_Int32(io_rProperties
.OValue
.size()) );
1334 nPerm
[0] = (sal_uInt8
)i_nAccessPermissions
;
1335 nPerm
[1] = (sal_uInt8
)( i_nAccessPermissions
>> 8 );
1336 nPerm
[2] = (sal_uInt8
)( i_nAccessPermissions
>> 16 );
1337 nPerm
[3] = (sal_uInt8
)( i_nAccessPermissions
>> 24 );
1339 if( nError
== rtl_Digest_E_None
)
1340 nError
= rtl_digest_updateMD5( aDigest
, nPerm
, sizeof( nPerm
) );
1342 //step 5, get the document ID, binary form
1343 if( nError
== rtl_Digest_E_None
)
1344 nError
= rtl_digest_updateMD5( aDigest
, &io_rProperties
.DocumentIdentifier
[0], sal_Int32(io_rProperties
.DocumentIdentifier
.size()) );
1346 if( nError
== rtl_Digest_E_None
)
1348 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1350 //step 6, only if 128 bit
1351 if( io_rProperties
.Security128bit
)
1353 for( sal_Int32 i
= 0; i
< 50; i
++ )
1355 nError
= rtl_digest_updateMD5( aDigest
, &nMD5Sum
, sizeof( nMD5Sum
) );
1356 if( nError
!= rtl_Digest_E_None
)
1361 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1369 i_pTransporter
->invalidate();
1374 io_rProperties
.EncryptionKey
.resize( MAXIMUM_RC4_KEY_LENGTH
);
1375 for( sal_Int32 i
= 0; i
< MD5_DIGEST_SIZE
; i
++ )
1376 io_rProperties
.EncryptionKey
[i
] = nMD5Sum
[i
];
1379 io_rProperties
.EncryptionKey
.clear();
1384 /**********************************
1385 Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member
1386 the step numbers down here correspond to the ones in PDF v.1.4 specfication
1388 bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8
* i_pPaddedOwnerPassword
,
1389 const sal_uInt8
* i_pPaddedUserPassword
,
1390 std::vector
< sal_uInt8
>& io_rOValue
,
1391 sal_Int32 i_nKeyLength
1394 bool bSuccess
= true;
1396 io_rOValue
.resize( ENCRYPTED_PWD_SIZE
);
1398 rtlDigest aDigest
= rtl_digest_createMD5();
1399 rtlCipher aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1400 if( aDigest
&& aCipher
)
1402 //step 1 already done, data is in i_pPaddedOwnerPassword
1405 rtlDigestError nError
= rtl_digest_updateMD5( aDigest
, i_pPaddedOwnerPassword
, ENCRYPTED_PWD_SIZE
);
1406 if( nError
== rtl_Digest_E_None
)
1408 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1410 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof(nMD5Sum
) );
1411 //step 3, only if 128 bit
1412 if( i_nKeyLength
== SECUR_128BIT_KEY
)
1415 for( i
= 0; i
< 50; i
++ )
1417 nError
= rtl_digest_updateMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1418 if( nError
!= rtl_Digest_E_None
)
1423 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof( nMD5Sum
) );
1426 //Step 4, the key is in nMD5Sum
1427 //step 5 already done, data is in i_pPaddedUserPassword
1429 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1430 nMD5Sum
, i_nKeyLength
, NULL
, 0 );
1431 // encrypt the user password using the key set above
1432 rtl_cipher_encodeARCFOUR( aCipher
, i_pPaddedUserPassword
, ENCRYPTED_PWD_SIZE
, // the data to be encrypted
1433 &io_rOValue
[0], sal_Int32(io_rOValue
.size()) ); //encrypted data
1434 //Step 7, only if 128 bit
1435 if( i_nKeyLength
== SECUR_128BIT_KEY
)
1438 sal_uInt8 nLocalKey
[ SECUR_128BIT_KEY
]; // 16 = 128 bit key
1440 for( i
= 1; i
<= 19; i
++ ) // do it 19 times, start with 1
1442 for( y
= 0; y
< sizeof( nLocalKey
); y
++ )
1443 nLocalKey
[y
] = (sal_uInt8
)( nMD5Sum
[y
] ^ i
);
1445 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1446 nLocalKey
, SECUR_128BIT_KEY
, NULL
, 0 ); //destination data area, on init can be NULL
1447 rtl_cipher_encodeARCFOUR( aCipher
, &io_rOValue
[0], sal_Int32(io_rOValue
.size()), // the data to be encrypted
1448 &io_rOValue
[0], sal_Int32(io_rOValue
.size()) ); // encrypted data, can be the same as the input, encrypt "in place"
1449 //step 8, store in class data member
1460 rtl_digest_destroyMD5( aDigest
);
1462 rtl_cipher_destroyARCFOUR( aCipher
);
1469 /**********************************
1470 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)
1472 bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter
* i_pTransporter
,
1473 vcl::PDFWriter::PDFEncryptionProperties
& io_rProperties
,
1474 sal_Int32 i_nKeyLength
,
1475 sal_Int32 i_nAccessPermissions
1478 bool bSuccess
= true;
1480 io_rProperties
.UValue
.resize( ENCRYPTED_PWD_SIZE
);
1482 rtlDigest aDigest
= rtl_digest_createMD5();
1483 rtlCipher aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1484 if( aDigest
&& aCipher
)
1486 //step 1, common to both 3.4 and 3.5
1487 if( computeEncryptionKey( i_pTransporter
, io_rProperties
, i_nAccessPermissions
) )
1489 // prepare encryption key for object
1490 for( sal_Int32 i
= i_nKeyLength
, y
= 0; y
< 5 ; y
++ )
1491 io_rProperties
.EncryptionKey
[i
++] = 0;
1493 if( io_rProperties
.Security128bit
== false )
1497 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1498 &io_rProperties
.EncryptionKey
[0], 5 , // key and key length
1499 NULL
, 0 ); //destination data area
1500 // encrypt the user password using the key set above, save for later use
1501 rtl_cipher_encodeARCFOUR( aCipher
, s_nPadString
, sizeof( s_nPadString
), // the data to be encrypted
1502 &io_rProperties
.UValue
[0], sal_Int32(io_rProperties
.UValue
.size()) ); //encrypted data, stored in class data member
1506 //or 3.5, for 128 bit security
1507 //step6, initilize the last 16 bytes of the encrypted user password to 0
1508 for(sal_uInt32 i
= MD5_DIGEST_SIZE
; i
< sal_uInt32(io_rProperties
.UValue
.size()); i
++)
1509 io_rProperties
.UValue
[i
] = 0;
1511 rtlDigestError nError
= rtl_digest_updateMD5( aDigest
, s_nPadString
, sizeof( s_nPadString
) );
1513 if( nError
== rtl_Digest_E_None
)
1514 nError
= rtl_digest_updateMD5( aDigest
, &io_rProperties
.DocumentIdentifier
[0], sal_Int32(io_rProperties
.DocumentIdentifier
.size()) );
1518 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
1519 rtl_digest_getMD5( aDigest
, nMD5Sum
, sizeof(nMD5Sum
) );
1521 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1522 &io_rProperties
.EncryptionKey
[0], SECUR_128BIT_KEY
, NULL
, 0 ); //destination data area
1523 rtl_cipher_encodeARCFOUR( aCipher
, nMD5Sum
, sizeof( nMD5Sum
), // the data to be encrypted
1524 &io_rProperties
.UValue
[0], sizeof( nMD5Sum
) ); //encrypted data, stored in class data member
1527 sal_uInt8 nLocalKey
[SECUR_128BIT_KEY
];
1529 for( i
= 1; i
<= 19; i
++ ) // do it 19 times, start with 1
1531 for( y
= 0; y
< sizeof( nLocalKey
) ; y
++ )
1532 nLocalKey
[y
] = (sal_uInt8
)( io_rProperties
.EncryptionKey
[y
] ^ i
);
1534 rtl_cipher_initARCFOUR( aCipher
, rtl_Cipher_DirectionEncode
,
1535 nLocalKey
, SECUR_128BIT_KEY
, // key and key length
1536 NULL
, 0 ); //destination data area, on init can be NULL
1537 rtl_cipher_encodeARCFOUR( aCipher
, &io_rProperties
.UValue
[0], SECUR_128BIT_KEY
, // the data to be encrypted
1538 &io_rProperties
.UValue
[0], SECUR_128BIT_KEY
); // encrypted data, can be the same as the input, encrypt "in place"
1549 rtl_digest_destroyMD5( aDigest
);
1551 rtl_cipher_destroyARCFOUR( aCipher
);
1554 io_rProperties
.UValue
.clear();
1558 /* end i12626 methods */
1560 static const long unsetRun
[256] =
1562 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
1563 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
1564 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */
1565 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */
1566 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1567 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */
1568 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1569 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */
1570 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
1571 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
1572 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
1573 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
1574 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
1575 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
1576 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
1577 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */
1580 static const long setRun
[256] =
1582 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
1583 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1584 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
1585 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
1586 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
1587 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
1588 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */
1589 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
1590 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */
1591 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */
1592 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */
1593 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */
1594 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */
1595 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */
1596 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
1597 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
1600 inline bool isSet( const Scanline i_pLine
, long i_nIndex
)
1602 return (i_pLine
[ i_nIndex
/8 ] & (0x80 >> (i_nIndex
&7))) != 0;
1605 long findBitRun( const Scanline i_pLine
, long i_nStartIndex
, long i_nW
, bool i_bSet
)
1607 if( i_nStartIndex
< 0 )
1610 long nIndex
= i_nStartIndex
;
1613 const sal_uInt8
* pByte
= static_cast<sal_uInt8
*>(i_pLine
) + (nIndex
/8);
1614 sal_uInt8 nByte
= *pByte
;
1616 // run up to byte boundary
1617 long nBitInByte
= (nIndex
& 7);
1620 sal_uInt8 nMask
= 0x80 >> nBitInByte
;
1621 while( nBitInByte
!= 8 )
1623 if( (nByte
& nMask
) != (i_bSet
? nMask
: 0) )
1624 return nIndex
< i_nW
? nIndex
: i_nW
;
1637 const long* pRunTable
;
1646 pRunTable
= unsetRun
;
1651 while( nByte
== nRunByte
)
1665 nIndex
+= pRunTable
[nByte
];
1668 return nIndex
< i_nW
? nIndex
: i_nW
;
1671 struct BitStreamState
1674 sal_uInt32 mnNextBitPos
;
1682 const sal_uInt8
* getByte() const { return &mnBuffer
; }
1683 void flush() { mnNextBitPos
= 8; mnBuffer
= 0; }
1686 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength
, sal_uInt32 i_nCode
, BitStreamState
& io_rState
)
1688 while( i_nLength
> io_rState
.mnNextBitPos
)
1690 io_rState
.mnBuffer
|= static_cast<sal_uInt8
>( i_nCode
>> (i_nLength
- io_rState
.mnNextBitPos
) );
1691 i_nLength
-= io_rState
.mnNextBitPos
;
1692 writeBuffer( io_rState
.getByte(), 1 );
1695 OSL_ASSERT( i_nLength
< 9 );
1696 static const unsigned int msbmask
[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1697 io_rState
.mnBuffer
|= static_cast<sal_uInt8
>( (i_nCode
& msbmask
[i_nLength
]) << (io_rState
.mnNextBitPos
- i_nLength
) );
1698 io_rState
.mnNextBitPos
-= i_nLength
;
1699 if( io_rState
.mnNextBitPos
== 0 )
1701 writeBuffer( io_rState
.getByte(), 1 );
1708 sal_uInt32 mnEncodedPixels
;
1709 sal_uInt32 mnCodeBits
;
1713 static const PixelCode WhitePixelCodes
[] =
1715 { 0, 8, 0x35 }, // 0011 0101
1716 { 1, 6, 0x7 }, // 0001 11
1717 { 2, 4, 0x7 }, // 0111
1718 { 3, 4, 0x8 }, // 1000
1719 { 4, 4, 0xB }, // 1011
1720 { 5, 4, 0xC }, // 1100
1721 { 6, 4, 0xE }, // 1110
1722 { 7, 4, 0xF }, // 1111
1723 { 8, 5, 0x13 }, // 1001 1
1724 { 9, 5, 0x14 }, // 1010 0
1725 { 10, 5, 0x7 }, // 0011 1
1726 { 11, 5, 0x8 }, // 0100 0
1727 { 12, 6, 0x8 }, // 0010 00
1728 { 13, 6, 0x3 }, // 0000 11
1729 { 14, 6, 0x34 }, // 1101 00
1730 { 15, 6, 0x35 }, // 1101 01
1731 { 16, 6, 0x2A }, // 1010 10
1732 { 17, 6, 0x2B }, // 1010 11
1733 { 18, 7, 0x27 }, // 0100 111
1734 { 19, 7, 0xC }, // 0001 100
1735 { 20, 7, 0x8 }, // 0001 000
1736 { 21, 7, 0x17 }, // 0010 111
1737 { 22, 7, 0x3 }, // 0000 011
1738 { 23, 7, 0x4 }, // 0000 100
1739 { 24, 7, 0x28 }, // 0101 000
1740 { 25, 7, 0x2B }, // 0101 011
1741 { 26, 7, 0x13 }, // 0010 011
1742 { 27, 7, 0x24 }, // 0100 100
1743 { 28, 7, 0x18 }, // 0011 000
1744 { 29, 8, 0x2 }, // 0000 0010
1745 { 30, 8, 0x3 }, // 0000 0011
1746 { 31, 8, 0x1A }, // 0001 1010
1747 { 32, 8, 0x1B }, // 0001 1011
1748 { 33, 8, 0x12 }, // 0001 0010
1749 { 34, 8, 0x13 }, // 0001 0011
1750 { 35, 8, 0x14 }, // 0001 0100
1751 { 36, 8, 0x15 }, // 0001 0101
1752 { 37, 8, 0x16 }, // 0001 0110
1753 { 38, 8, 0x17 }, // 0001 0111
1754 { 39, 8, 0x28 }, // 0010 1000
1755 { 40, 8, 0x29 }, // 0010 1001
1756 { 41, 8, 0x2A }, // 0010 1010
1757 { 42, 8, 0x2B }, // 0010 1011
1758 { 43, 8, 0x2C }, // 0010 1100
1759 { 44, 8, 0x2D }, // 0010 1101
1760 { 45, 8, 0x4 }, // 0000 0100
1761 { 46, 8, 0x5 }, // 0000 0101
1762 { 47, 8, 0xA }, // 0000 1010
1763 { 48, 8, 0xB }, // 0000 1011
1764 { 49, 8, 0x52 }, // 0101 0010
1765 { 50, 8, 0x53 }, // 0101 0011
1766 { 51, 8, 0x54 }, // 0101 0100
1767 { 52, 8, 0x55 }, // 0101 0101
1768 { 53, 8, 0x24 }, // 0010 0100
1769 { 54, 8, 0x25 }, // 0010 0101
1770 { 55, 8, 0x58 }, // 0101 1000
1771 { 56, 8, 0x59 }, // 0101 1001
1772 { 57, 8, 0x5A }, // 0101 1010
1773 { 58, 8, 0x5B }, // 0101 1011
1774 { 59, 8, 0x4A }, // 0100 1010
1775 { 60, 8, 0x4B }, // 0100 1011
1776 { 61, 8, 0x32 }, // 0011 0010
1777 { 62, 8, 0x33 }, // 0011 0011
1778 { 63, 8, 0x34 }, // 0011 0100
1779 { 64, 5, 0x1B }, // 1101 1
1780 { 128, 5, 0x12 }, // 1001 0
1781 { 192, 6, 0x17 }, // 0101 11
1782 { 256, 7, 0x37 }, // 0110 111
1783 { 320, 8, 0x36 }, // 0011 0110
1784 { 384, 8, 0x37 }, // 0011 0111
1785 { 448, 8, 0x64 }, // 0110 0100
1786 { 512, 8, 0x65 }, // 0110 0101
1787 { 576, 8, 0x68 }, // 0110 1000
1788 { 640, 8, 0x67 }, // 0110 0111
1789 { 704, 9, 0xCC }, // 0110 0110 0
1790 { 768, 9, 0xCD }, // 0110 0110 1
1791 { 832, 9, 0xD2 }, // 0110 1001 0
1792 { 896, 9, 0xD3 }, // 0110 1001 1
1793 { 960, 9, 0xD4 }, // 0110 1010 0
1794 { 1024, 9, 0xD5 }, // 0110 1010 1
1795 { 1088, 9, 0xD6 }, // 0110 1011 0
1796 { 1152, 9, 0xD7 }, // 0110 1011 1
1797 { 1216, 9, 0xD8 }, // 0110 1100 0
1798 { 1280, 9, 0xD9 }, // 0110 1100 1
1799 { 1344, 9, 0xDA }, // 0110 1101 0
1800 { 1408, 9, 0xDB }, // 0110 1101 1
1801 { 1472, 9, 0x98 }, // 0100 1100 0
1802 { 1536, 9, 0x99 }, // 0100 1100 1
1803 { 1600, 9, 0x9A }, // 0100 1101 0
1804 { 1664, 6, 0x18 }, // 0110 00
1805 { 1728, 9, 0x9B }, // 0100 1101 1
1806 { 1792, 11, 0x8 }, // 0000 0001 000
1807 { 1856, 11, 0xC }, // 0000 0001 100
1808 { 1920, 11, 0xD }, // 0000 0001 101
1809 { 1984, 12, 0x12 }, // 0000 0001 0010
1810 { 2048, 12, 0x13 }, // 0000 0001 0011
1811 { 2112, 12, 0x14 }, // 0000 0001 0100
1812 { 2176, 12, 0x15 }, // 0000 0001 0101
1813 { 2240, 12, 0x16 }, // 0000 0001 0110
1814 { 2304, 12, 0x17 }, // 0000 0001 0111
1815 { 2368, 12, 0x1C }, // 0000 0001 1100
1816 { 2432, 12, 0x1D }, // 0000 0001 1101
1817 { 2496, 12, 0x1E }, // 0000 0001 1110
1818 { 2560, 12, 0x1F } // 0000 0001 1111
1821 static const PixelCode BlackPixelCodes
[] =
1823 { 0, 10, 0x37 }, // 0000 1101 11
1824 { 1, 3, 0x2 }, // 010
1825 { 2, 2, 0x3 }, // 11
1826 { 3, 2, 0x2 }, // 10
1827 { 4, 3, 0x3 }, // 011
1828 { 5, 4, 0x3 }, // 0011
1829 { 6, 4, 0x2 }, // 0010
1830 { 7, 5, 0x3 }, // 0001 1
1831 { 8, 6, 0x5 }, // 0001 01
1832 { 9, 6, 0x4 }, // 0001 00
1833 { 10, 7, 0x4 }, // 0000 100
1834 { 11, 7, 0x5 }, // 0000 101
1835 { 12, 7, 0x7 }, // 0000 111
1836 { 13, 8, 0x4 }, // 0000 0100
1837 { 14, 8, 0x7 }, // 0000 0111
1838 { 15, 9, 0x18 }, // 0000 1100 0
1839 { 16, 10, 0x17 }, // 0000 0101 11
1840 { 17, 10, 0x18 }, // 0000 0110 00
1841 { 18, 10, 0x8 }, // 0000 0010 00
1842 { 19, 11, 0x67 }, // 0000 1100 111
1843 { 20, 11, 0x68 }, // 0000 1101 000
1844 { 21, 11, 0x6C }, // 0000 1101 100
1845 { 22, 11, 0x37 }, // 0000 0110 111
1846 { 23, 11, 0x28 }, // 0000 0101 000
1847 { 24, 11, 0x17 }, // 0000 0010 111
1848 { 25, 11, 0x18 }, // 0000 0011 000
1849 { 26, 12, 0xCA }, // 0000 1100 1010
1850 { 27, 12, 0xCB }, // 0000 1100 1011
1851 { 28, 12, 0xCC }, // 0000 1100 1100
1852 { 29, 12, 0xCD }, // 0000 1100 1101
1853 { 30, 12, 0x68 }, // 0000 0110 1000
1854 { 31, 12, 0x69 }, // 0000 0110 1001
1855 { 32, 12, 0x6A }, // 0000 0110 1010
1856 { 33, 12, 0x6B }, // 0000 0110 1011
1857 { 34, 12, 0xD2 }, // 0000 1101 0010
1858 { 35, 12, 0xD3 }, // 0000 1101 0011
1859 { 36, 12, 0xD4 }, // 0000 1101 0100
1860 { 37, 12, 0xD5 }, // 0000 1101 0101
1861 { 38, 12, 0xD6 }, // 0000 1101 0110
1862 { 39, 12, 0xD7 }, // 0000 1101 0111
1863 { 40, 12, 0x6C }, // 0000 0110 1100
1864 { 41, 12, 0x6D }, // 0000 0110 1101
1865 { 42, 12, 0xDA }, // 0000 1101 1010
1866 { 43, 12, 0xDB }, // 0000 1101 1011
1867 { 44, 12, 0x54 }, // 0000 0101 0100
1868 { 45, 12, 0x55 }, // 0000 0101 0101
1869 { 46, 12, 0x56 }, // 0000 0101 0110
1870 { 47, 12, 0x57 }, // 0000 0101 0111
1871 { 48, 12, 0x64 }, // 0000 0110 0100
1872 { 49, 12, 0x65 }, // 0000 0110 0101
1873 { 50, 12, 0x52 }, // 0000 0101 0010
1874 { 51, 12, 0x53 }, // 0000 0101 0011
1875 { 52, 12, 0x24 }, // 0000 0010 0100
1876 { 53, 12, 0x37 }, // 0000 0011 0111
1877 { 54, 12, 0x38 }, // 0000 0011 1000
1878 { 55, 12, 0x27 }, // 0000 0010 0111
1879 { 56, 12, 0x28 }, // 0000 0010 1000
1880 { 57, 12, 0x58 }, // 0000 0101 1000
1881 { 58, 12, 0x59 }, // 0000 0101 1001
1882 { 59, 12, 0x2B }, // 0000 0010 1011
1883 { 60, 12, 0x2C }, // 0000 0010 1100
1884 { 61, 12, 0x5A }, // 0000 0101 1010
1885 { 62, 12, 0x66 }, // 0000 0110 0110
1886 { 63, 12, 0x67 }, // 0000 0110 0111
1887 { 64, 10, 0xF }, // 0000 0011 11
1888 { 128, 12, 0xC8 }, // 0000 1100 1000
1889 { 192, 12, 0xC9 }, // 0000 1100 1001
1890 { 256, 12, 0x5B }, // 0000 0101 1011
1891 { 320, 12, 0x33 }, // 0000 0011 0011
1892 { 384, 12, 0x34 }, // 0000 0011 0100
1893 { 448, 12, 0x35 }, // 0000 0011 0101
1894 { 512, 13, 0x6C }, // 0000 0011 0110 0
1895 { 576, 13, 0x6D }, // 0000 0011 0110 1
1896 { 640, 13, 0x4A }, // 0000 0010 0101 0
1897 { 704, 13, 0x4B }, // 0000 0010 0101 1
1898 { 768, 13, 0x4C }, // 0000 0010 0110 0
1899 { 832, 13, 0x4D }, // 0000 0010 0110 1
1900 { 896, 13, 0x72 }, // 0000 0011 1001 0
1901 { 960, 13, 0x73 }, // 0000 0011 1001 1
1902 { 1024, 13, 0x74 }, // 0000 0011 1010 0
1903 { 1088, 13, 0x75 }, // 0000 0011 1010 1
1904 { 1152, 13, 0x76 }, // 0000 0011 1011 0
1905 { 1216, 13, 0x77 }, // 0000 0011 1011 1
1906 { 1280, 13, 0x52 }, // 0000 0010 1001 0
1907 { 1344, 13, 0x53 }, // 0000 0010 1001 1
1908 { 1408, 13, 0x54 }, // 0000 0010 1010 0
1909 { 1472, 13, 0x55 }, // 0000 0010 1010 1
1910 { 1536, 13, 0x5A }, // 0000 0010 1101 0
1911 { 1600, 13, 0x5B }, // 0000 0010 1101 1
1912 { 1664, 13, 0x64 }, // 0000 0011 0010 0
1913 { 1728, 13, 0x65 }, // 0000 0011 0010 1
1914 { 1792, 11, 0x8 }, // 0000 0001 000
1915 { 1856, 11, 0xC }, // 0000 0001 100
1916 { 1920, 11, 0xD }, // 0000 0001 101
1917 { 1984, 12, 0x12 }, // 0000 0001 0010
1918 { 2048, 12, 0x13 }, // 0000 0001 0011
1919 { 2112, 12, 0x14 }, // 0000 0001 0100
1920 { 2176, 12, 0x15 }, // 0000 0001 0101
1921 { 2240, 12, 0x16 }, // 0000 0001 0110
1922 { 2304, 12, 0x17 }, // 0000 0001 0111
1923 { 2368, 12, 0x1C }, // 0000 0001 1100
1924 { 2432, 12, 0x1D }, // 0000 0001 1101
1925 { 2496, 12, 0x1E }, // 0000 0001 1110
1926 { 2560, 12, 0x1F } // 0000 0001 1111
1930 void PDFWriterImpl::putG4Span( long i_nSpan
, bool i_bWhitePixel
, BitStreamState
& io_rState
)
1932 const PixelCode
* pTable
= i_bWhitePixel
? WhitePixelCodes
: BlackPixelCodes
;
1933 // maximum encoded span is 2560 consecutive pixels
1934 while( i_nSpan
> 2623 )
1936 // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1937 putG4Bits( pTable
[103].mnCodeBits
, pTable
[103].mnCode
, io_rState
);
1938 i_nSpan
-= pTable
[103].mnEncodedPixels
;
1940 // write multiples of 64 pixels up to 2560
1943 sal_uInt32 nTabIndex
= 63 + (i_nSpan
>> 6);
1944 OSL_ASSERT( pTable
[nTabIndex
].mnEncodedPixels
== static_cast<sal_uInt32
>(64*(i_nSpan
>> 6)) );
1945 putG4Bits( pTable
[nTabIndex
].mnCodeBits
, pTable
[nTabIndex
].mnCode
, io_rState
);
1946 i_nSpan
-= pTable
[nTabIndex
].mnEncodedPixels
;
1948 putG4Bits( pTable
[i_nSpan
].mnCodeBits
, pTable
[i_nSpan
].mnCode
, io_rState
);
1951 void PDFWriterImpl::writeG4Stream( BitmapReadAccess
* i_pBitmap
)
1953 long nW
= i_pBitmap
->Width();
1954 long nH
= i_pBitmap
->Height();
1955 if( nW
<= 0 || nH
<= 0 )
1957 if( i_pBitmap
->GetBitCount() != 1 )
1960 BitStreamState aBitState
;
1962 // the first reference line is virtual and completely empty
1963 const Scanline pFirstRefLine
= (Scanline
)rtl_allocateZeroMemory( nW
/8 + 1 );
1964 Scanline pRefLine
= pFirstRefLine
;
1965 for( long nY
= 0; nY
< nH
; nY
++ )
1967 const Scanline pCurLine
= i_pBitmap
->GetScanline( nY
);
1968 long nLineIndex
= 0;
1969 bool bRunSet
= (*pCurLine
& 0x80) ? true : false;
1970 bool bRefSet
= (*pRefLine
& 0x80) ? true : false;
1971 long nRunIndex1
= bRunSet
? 0 : findBitRun( pCurLine
, 0, nW
, bRunSet
);
1972 long nRefIndex1
= bRefSet
? 0 : findBitRun( pRefLine
, 0, nW
, bRefSet
);
1973 for( ; nLineIndex
< nW
; )
1975 long nRefIndex2
= findBitRun( pRefLine
, nRefIndex1
, nW
, isSet( pRefLine
, nRefIndex1
) );
1976 if( nRefIndex2
>= nRunIndex1
)
1978 long nDiff
= nRefIndex1
- nRunIndex1
;
1979 if( -3 <= nDiff
&& nDiff
<= 3 )
1980 { // vertical coding
1983 sal_uInt32 mnCodeBits
;
1985 } VerticalCodes
[7] = {
1986 { 7, 0x03 }, // 0000 011
1987 { 6, 0x03 }, // 0000 11
1991 { 6, 0x02 }, // 0000 10
1992 { 7, 0x02 } // 0000 010
1998 putG4Bits( VerticalCodes
[nDiff
].mnCodeBits
, VerticalCodes
[nDiff
].mnCode
, aBitState
);
1999 nLineIndex
= nRunIndex1
;
2002 { // difference too large, horizontal coding
2003 // emit horz code 001
2004 putG4Bits( 3, 0x1, aBitState
);
2005 long nRunIndex2
= findBitRun( pCurLine
, nRunIndex1
, nW
, isSet( pCurLine
, nRunIndex1
) );
2006 bool bWhiteFirst
= ( nLineIndex
+ nRunIndex1
== 0 || ! isSet( pCurLine
, nLineIndex
) );
2007 putG4Span( nRunIndex1
- nLineIndex
, bWhiteFirst
, aBitState
);
2008 putG4Span( nRunIndex2
- nRunIndex1
, ! bWhiteFirst
, aBitState
);
2009 nLineIndex
= nRunIndex2
;
2013 { // emit pass code 0001
2014 putG4Bits( 4, 0x1, aBitState
);
2015 nLineIndex
= nRefIndex2
;
2017 if( nLineIndex
< nW
)
2019 bool bSet
= isSet( pCurLine
, nLineIndex
);
2020 nRunIndex1
= findBitRun( pCurLine
, nLineIndex
, nW
, bSet
);
2021 nRefIndex1
= findBitRun( pRefLine
, nLineIndex
, nW
, ! bSet
);
2022 nRefIndex1
= findBitRun( pRefLine
, nRefIndex1
, nW
, bSet
);
2026 // the current line is the reference for the next line
2027 pRefLine
= pCurLine
;
2029 // terminate strip with EOFB
2030 putG4Bits( 12, 1, aBitState
);
2031 putG4Bits( 12, 1, aBitState
);
2032 if( aBitState
.mnNextBitPos
!= 8 )
2034 writeBuffer( aBitState
.getByte(), 1 );
2038 rtl_freeMemory( pFirstRefLine
);
2041 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */