Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "pdfwriter_impl.hxx"
21 :
22 : #include "vcl/pdfextoutdevdata.hxx"
23 : #include "vcl/virdev.hxx"
24 : #include "vcl/gdimtf.hxx"
25 : #include "vcl/metaact.hxx"
26 : #include "vcl/bmpacc.hxx"
27 : #include "vcl/graph.hxx"
28 :
29 : #include "svdata.hxx"
30 :
31 : #include "unotools/streamwrap.hxx"
32 :
33 : #include "comphelper/processfactory.hxx"
34 :
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"
39 :
40 : #include "cppuhelper/implbase1.hxx"
41 :
42 : #include <rtl/digest.h>
43 : #include <boost/scoped_ptr.hpp>
44 :
45 : using namespace vcl;
46 : using namespace com::sun::star;
47 : using namespace com::sun::star::uno;
48 : using namespace com::sun::star::beans;
49 :
50 : static bool lcl_canUsePDFAxialShading(const Gradient& rGradient);
51 :
52 0 : void PDFWriterImpl::implWriteGradient( const tools::PolyPolygon& i_rPolyPoly, const Gradient& i_rGradient,
53 : VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
54 : {
55 0 : GDIMetaFile aTmpMtf;
56 :
57 0 : i_pDummyVDev->AddGradientActions( i_rPolyPoly.GetBoundRect(), i_rGradient, aTmpMtf );
58 :
59 0 : m_rOuterFace.Push();
60 0 : m_rOuterFace.IntersectClipRegion( i_rPolyPoly.getB2DPolyPolygon() );
61 0 : playMetafile( aTmpMtf, NULL, i_rContext, i_pDummyVDev );
62 0 : m_rOuterFace.Pop();
63 0 : }
64 :
65 0 : void PDFWriterImpl::implWriteBitmapEx( const Point& i_rPoint, const Size& i_rSize, const BitmapEx& i_rBitmapEx,
66 : VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
67 : {
68 0 : if ( !i_rBitmapEx.IsEmpty() && i_rSize.Width() && i_rSize.Height() )
69 : {
70 0 : BitmapEx aBitmapEx( i_rBitmapEx );
71 0 : Point aPoint( i_rPoint );
72 0 : Size aSize( i_rSize );
73 :
74 : // #i19065# Negative sizes have mirror semantics on
75 : // OutputDevice. BitmapEx and co. have no idea about that, so
76 : // perform that _before_ doing anything with aBitmapEx.
77 0 : sal_uLong nMirrorFlags(BMP_MIRROR_NONE);
78 0 : if( aSize.Width() < 0 )
79 : {
80 0 : aSize.Width() *= -1;
81 0 : aPoint.X() -= aSize.Width();
82 0 : nMirrorFlags |= BMP_MIRROR_HORZ;
83 : }
84 0 : if( aSize.Height() < 0 )
85 : {
86 0 : aSize.Height() *= -1;
87 0 : aPoint.Y() -= aSize.Height();
88 0 : nMirrorFlags |= BMP_MIRROR_VERT;
89 : }
90 :
91 0 : if( nMirrorFlags != BMP_MIRROR_NONE )
92 : {
93 0 : aBitmapEx.Mirror( nMirrorFlags );
94 : }
95 0 : if( i_rContext.m_nMaxImageResolution > 50 )
96 : {
97 : // do downsampling if necessary
98 0 : const Size aDstSizeTwip( i_pDummyVDev->PixelToLogic( i_pDummyVDev->LogicToPixel( aSize ), MAP_TWIP ) );
99 0 : const Size aBmpSize( aBitmapEx.GetSizePixel() );
100 0 : const double fBmpPixelX = aBmpSize.Width();
101 0 : const double fBmpPixelY = aBmpSize.Height();
102 0 : const double fMaxPixelX = aDstSizeTwip.Width() * i_rContext.m_nMaxImageResolution / 1440.0;
103 0 : const double fMaxPixelY = aDstSizeTwip.Height() * i_rContext.m_nMaxImageResolution / 1440.0;
104 :
105 : // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
106 0 : if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) ||
107 0 : ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) &&
108 0 : ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) )
109 : {
110 : // do scaling
111 0 : Size aNewBmpSize;
112 0 : const double fBmpWH = fBmpPixelX / fBmpPixelY;
113 0 : const double fMaxWH = fMaxPixelX / fMaxPixelY;
114 :
115 0 : if( fBmpWH < fMaxWH )
116 : {
117 0 : aNewBmpSize.Width() = FRound( fMaxPixelY * fBmpWH );
118 0 : aNewBmpSize.Height() = FRound( fMaxPixelY );
119 : }
120 0 : else if( fBmpWH > 0.0 )
121 : {
122 0 : aNewBmpSize.Width() = FRound( fMaxPixelX );
123 0 : aNewBmpSize.Height() = FRound( fMaxPixelX / fBmpWH);
124 : }
125 :
126 0 : if( aNewBmpSize.Width() && aNewBmpSize.Height() )
127 : {
128 : // #i121233# Use best quality for PDF exports
129 0 : aBitmapEx.Scale( aNewBmpSize, BMP_SCALE_BESTQUALITY );
130 : }
131 : else
132 : {
133 0 : aBitmapEx.SetEmpty();
134 : }
135 : }
136 : }
137 :
138 0 : const Size aSizePixel( aBitmapEx.GetSizePixel() );
139 0 : if ( aSizePixel.Width() && aSizePixel.Height() )
140 : {
141 0 : if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
142 : {
143 0 : BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
144 0 : int nDepth = aBitmapEx.GetBitmap().GetBitCount();
145 0 : if( nDepth <= 4 )
146 0 : eConv = BMP_CONVERSION_4BIT_GREYS;
147 0 : if( nDepth > 1 )
148 0 : aBitmapEx.Convert( eConv );
149 : }
150 0 : bool bUseJPGCompression = !i_rContext.m_bOnlyLosslessCompression;
151 0 : if ( ( aSizePixel.Width() < 32 ) || ( aSizePixel.Height() < 32 ) )
152 0 : bUseJPGCompression = false;
153 :
154 0 : SvMemoryStream aStrm;
155 0 : Bitmap aMask;
156 :
157 0 : bool bTrueColorJPG = true;
158 0 : if ( bUseJPGCompression )
159 : {
160 : sal_uInt32 nZippedFileSize; // sj: we will calculate the filesize of a zipped bitmap
161 : { // to determine if jpeg compression is useful
162 0 : SvMemoryStream aTemp;
163 0 : aTemp.SetCompressMode( aTemp.GetCompressMode() | COMPRESSMODE_ZBITMAP );
164 0 : aTemp.SetVersion( SOFFICE_FILEFORMAT_40 ); // sj: up from version 40 our bitmap stream operator
165 0 : WriteDIBBitmapEx(aBitmapEx, aTemp); // is capable of zlib stream compression
166 0 : aTemp.Seek( STREAM_SEEK_TO_END );
167 0 : nZippedFileSize = aTemp.Tell();
168 : }
169 0 : if ( aBitmapEx.IsTransparent() )
170 : {
171 0 : if ( aBitmapEx.IsAlpha() )
172 0 : aMask = aBitmapEx.GetAlpha().GetBitmap();
173 : else
174 0 : aMask = aBitmapEx.GetMask();
175 : }
176 0 : Graphic aGraphic( aBitmapEx.GetBitmap() );
177 0 : sal_Int32 nColorMode = 0;
178 :
179 0 : Sequence< PropertyValue > aFilterData( 2 );
180 0 : aFilterData[ 0 ].Name = "Quality";
181 0 : aFilterData[ 0 ].Value <<= sal_Int32(i_rContext.m_nJPEGQuality);
182 0 : aFilterData[ 1 ].Name = "ColorMode";
183 0 : aFilterData[ 1 ].Value <<= nColorMode;
184 :
185 : try
186 : {
187 0 : uno::Reference < io::XStream > xStream = new utl::OStreamWrapper( aStrm );
188 0 : uno::Reference< io::XSeekable > xSeekable( xStream, UNO_QUERY_THROW );
189 0 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
190 0 : uno::Reference< graphic::XGraphicProvider > xGraphicProvider( graphic::GraphicProvider::create(xContext) );
191 0 : uno::Reference< graphic::XGraphic > xGraphic( aGraphic.GetXGraphic() );
192 0 : uno::Reference < io::XOutputStream > xOut( xStream->getOutputStream() );
193 0 : OUString aMimeType("image/jpeg");
194 0 : uno::Sequence< beans::PropertyValue > aOutMediaProperties( 3 );
195 0 : aOutMediaProperties[0].Name = "OutputStream";
196 0 : aOutMediaProperties[0].Value <<= xOut;
197 0 : aOutMediaProperties[1].Name = "MimeType";
198 0 : aOutMediaProperties[1].Value <<= aMimeType;
199 0 : aOutMediaProperties[2].Name = "FilterData";
200 0 : aOutMediaProperties[2].Value <<= aFilterData;
201 0 : xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties );
202 0 : xOut->flush();
203 0 : if ( xSeekable->getLength() > nZippedFileSize )
204 : {
205 0 : bUseJPGCompression = false;
206 : }
207 : else
208 : {
209 0 : aStrm.Seek( STREAM_SEEK_TO_END );
210 :
211 0 : xSeekable->seek( 0 );
212 0 : Sequence< PropertyValue > aArgs( 1 );
213 0 : aArgs[ 0 ].Name = "InputStream";
214 0 : aArgs[ 0 ].Value <<= xStream;
215 0 : uno::Reference< XPropertySet > xPropSet( xGraphicProvider->queryGraphicDescriptor( aArgs ) );
216 0 : if ( xPropSet.is() )
217 : {
218 0 : sal_Int16 nBitsPerPixel = 24;
219 0 : if ( xPropSet->getPropertyValue("BitsPerPixel") >>= nBitsPerPixel )
220 : {
221 0 : bTrueColorJPG = nBitsPerPixel != 8;
222 : }
223 0 : }
224 0 : }
225 : }
226 0 : catch( uno::Exception& )
227 : {
228 0 : bUseJPGCompression = false;
229 0 : }
230 : }
231 0 : if ( bUseJPGCompression )
232 0 : m_rOuterFace.DrawJPGBitmap( aStrm, bTrueColorJPG, aSizePixel, Rectangle( aPoint, aSize ), aMask );
233 0 : else if ( aBitmapEx.IsTransparent() )
234 0 : m_rOuterFace.DrawBitmapEx( aPoint, aSize, aBitmapEx );
235 : else
236 0 : m_rOuterFace.DrawBitmap( aPoint, aSize, aBitmapEx.GetBitmap() );
237 0 : }
238 : }
239 0 : }
240 :
241 0 : void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevData* i_pOutDevData, const vcl::PDFWriter::PlayMetafileContext& i_rContext, VirtualDevice* pDummyVDev )
242 : {
243 0 : bool bAssertionFired( false );
244 :
245 0 : boost::scoped_ptr<VirtualDevice> pPrivateDevice;
246 0 : if( ! pDummyVDev )
247 : {
248 0 : pPrivateDevice.reset(new VirtualDevice());
249 0 : pDummyVDev = pPrivateDevice.get();
250 0 : pDummyVDev->EnableOutput( false );
251 0 : pDummyVDev->SetMapMode( i_rMtf.GetPrefMapMode() );
252 : }
253 0 : GDIMetaFile aMtf( i_rMtf );
254 :
255 0 : for( sal_uInt32 i = 0, nCount = aMtf.GetActionSize(); i < (sal_uInt32)nCount; )
256 : {
257 0 : if ( !i_pOutDevData || !i_pOutDevData->PlaySyncPageAct( m_rOuterFace, i ) )
258 : {
259 0 : const MetaAction* pAction = aMtf.GetAction( i );
260 0 : const sal_uInt16 nType = pAction->GetType();
261 :
262 0 : switch( nType )
263 : {
264 : case( META_PIXEL_ACTION ):
265 : {
266 0 : const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
267 0 : m_rOuterFace.DrawPixel( pA->GetPoint(), pA->GetColor() );
268 : }
269 0 : break;
270 :
271 : case( META_POINT_ACTION ):
272 : {
273 0 : const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
274 0 : m_rOuterFace.DrawPixel( pA->GetPoint() );
275 : }
276 0 : break;
277 :
278 : case( META_LINE_ACTION ):
279 : {
280 0 : const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
281 0 : if ( pA->GetLineInfo().IsDefault() )
282 0 : m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint() );
283 : else
284 0 : m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint(), pA->GetLineInfo() );
285 : }
286 0 : break;
287 :
288 : case( META_RECT_ACTION ):
289 : {
290 0 : const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
291 0 : m_rOuterFace.DrawRect( pA->GetRect() );
292 : }
293 0 : break;
294 :
295 : case( META_ROUNDRECT_ACTION ):
296 : {
297 0 : const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
298 0 : m_rOuterFace.DrawRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
299 : }
300 0 : break;
301 :
302 : case( META_ELLIPSE_ACTION ):
303 : {
304 0 : const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
305 0 : m_rOuterFace.DrawEllipse( pA->GetRect() );
306 : }
307 0 : break;
308 :
309 : case( META_ARC_ACTION ):
310 : {
311 0 : const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
312 0 : m_rOuterFace.DrawArc( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
313 : }
314 0 : break;
315 :
316 : case( META_PIE_ACTION ):
317 : {
318 0 : const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
319 0 : m_rOuterFace.DrawPie( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
320 : }
321 0 : break;
322 :
323 : case( META_CHORD_ACTION ):
324 : {
325 0 : const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
326 0 : m_rOuterFace.DrawChord( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
327 : }
328 0 : break;
329 :
330 : case( META_POLYGON_ACTION ):
331 : {
332 0 : const MetaPolygonAction* pA = static_cast<const MetaPolygonAction*>(pAction);
333 0 : m_rOuterFace.DrawPolygon( pA->GetPolygon() );
334 : }
335 0 : break;
336 :
337 : case( META_POLYLINE_ACTION ):
338 : {
339 0 : const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
340 0 : if ( pA->GetLineInfo().IsDefault() )
341 0 : m_rOuterFace.DrawPolyLine( pA->GetPolygon() );
342 : else
343 0 : m_rOuterFace.DrawPolyLine( pA->GetPolygon(), pA->GetLineInfo() );
344 : }
345 0 : break;
346 :
347 : case( META_POLYPOLYGON_ACTION ):
348 : {
349 0 : const MetaPolyPolygonAction* pA = static_cast<const MetaPolyPolygonAction*>(pAction);
350 0 : m_rOuterFace.DrawPolyPolygon( pA->GetPolyPolygon() );
351 : }
352 0 : break;
353 :
354 : case( META_GRADIENT_ACTION ):
355 : {
356 0 : const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
357 0 : const Gradient& rGradient = pA->GetGradient();
358 0 : if (lcl_canUsePDFAxialShading(rGradient))
359 : {
360 0 : m_rOuterFace.DrawGradient( pA->GetRect(), rGradient );
361 : }
362 : else
363 : {
364 0 : const tools::PolyPolygon aPolyPoly( pA->GetRect() );
365 0 : implWriteGradient( aPolyPoly, rGradient, pDummyVDev, i_rContext );
366 : }
367 : }
368 0 : break;
369 :
370 : case( META_GRADIENTEX_ACTION ):
371 : {
372 0 : const MetaGradientExAction* pA = static_cast<const MetaGradientExAction*>(pAction);
373 0 : const Gradient& rGradient = pA->GetGradient();
374 :
375 0 : if (lcl_canUsePDFAxialShading(rGradient))
376 0 : m_rOuterFace.DrawGradient( pA->GetPolyPolygon(), rGradient );
377 : else
378 0 : implWriteGradient( pA->GetPolyPolygon(), rGradient, pDummyVDev, i_rContext );
379 : }
380 0 : break;
381 :
382 : case META_HATCH_ACTION:
383 : {
384 0 : const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pAction);
385 0 : m_rOuterFace.DrawHatch( pA->GetPolyPolygon(), pA->GetHatch() );
386 : }
387 0 : break;
388 :
389 : case( META_TRANSPARENT_ACTION ):
390 : {
391 0 : const MetaTransparentAction* pA = static_cast<const MetaTransparentAction*>(pAction);
392 0 : m_rOuterFace.DrawTransparent( pA->GetPolyPolygon(), pA->GetTransparence() );
393 : }
394 0 : break;
395 :
396 : case( META_FLOATTRANSPARENT_ACTION ):
397 : {
398 0 : const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
399 :
400 0 : GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
401 0 : const Point& rPos = pA->GetPoint();
402 0 : const Size& rSize= pA->GetSize();
403 0 : const Gradient& rTransparenceGradient = pA->GetGradient();
404 :
405 : // special case constant alpha value
406 0 : if( rTransparenceGradient.GetStartColor() == rTransparenceGradient.GetEndColor() )
407 : {
408 0 : const Color aTransCol( rTransparenceGradient.GetStartColor() );
409 0 : const sal_uInt16 nTransPercent = aTransCol.GetLuminance() * 100 / 255;
410 0 : m_rOuterFace.BeginTransparencyGroup();
411 0 : playMetafile( aTmpMtf, NULL, i_rContext, pDummyVDev );
412 0 : m_rOuterFace.EndTransparencyGroup( Rectangle( rPos, rSize ), nTransPercent );
413 : }
414 : else
415 : {
416 0 : const Size aDstSizeTwip( pDummyVDev->PixelToLogic( pDummyVDev->LogicToPixel( rSize ), MAP_TWIP ) );
417 :
418 : // i#115962# Always use at least 300 DPI for bitmap conversion of transparence gradients,
419 : // else the quality is not acceptable (see bugdoc as example)
420 0 : sal_Int32 nMaxBmpDPI(300);
421 :
422 0 : if( i_rContext.m_nMaxImageResolution > 50 )
423 : {
424 0 : if ( nMaxBmpDPI > i_rContext.m_nMaxImageResolution )
425 0 : nMaxBmpDPI = i_rContext.m_nMaxImageResolution;
426 : }
427 0 : const sal_Int32 nPixelX = (sal_Int32)((double)aDstSizeTwip.Width() * (double)nMaxBmpDPI / 1440.0);
428 0 : const sal_Int32 nPixelY = (sal_Int32)((double)aDstSizeTwip.Height() * (double)nMaxBmpDPI / 1440.0);
429 0 : if ( nPixelX && nPixelY )
430 : {
431 0 : Size aDstSizePixel( nPixelX, nPixelY );
432 0 : boost::scoped_ptr<VirtualDevice> pVDev(new VirtualDevice);
433 0 : if( pVDev->SetOutputSizePixel( aDstSizePixel ) )
434 : {
435 0 : Bitmap aPaint, aMask;
436 0 : AlphaMask aAlpha;
437 0 : Point aPoint;
438 :
439 0 : MapMode aMapMode( pDummyVDev->GetMapMode() );
440 0 : aMapMode.SetOrigin( aPoint );
441 0 : pVDev->SetMapMode( aMapMode );
442 0 : Size aDstSize( pVDev->PixelToLogic( aDstSizePixel ) );
443 :
444 0 : Point aMtfOrigin( aTmpMtf.GetPrefMapMode().GetOrigin() );
445 0 : if ( aMtfOrigin.X() || aMtfOrigin.Y() )
446 0 : aTmpMtf.Move( -aMtfOrigin.X(), -aMtfOrigin.Y() );
447 0 : double fScaleX = (double)aDstSize.Width() / (double)aTmpMtf.GetPrefSize().Width();
448 0 : double fScaleY = (double)aDstSize.Height() / (double)aTmpMtf.GetPrefSize().Height();
449 0 : if( fScaleX != 1.0 || fScaleY != 1.0 )
450 0 : aTmpMtf.Scale( fScaleX, fScaleY );
451 0 : aTmpMtf.SetPrefMapMode( aMapMode );
452 :
453 : // create paint bitmap
454 0 : aTmpMtf.WindStart();
455 0 : aTmpMtf.Play( pVDev.get(), aPoint, aDstSize );
456 0 : aTmpMtf.WindStart();
457 :
458 0 : pVDev->EnableMapMode( false );
459 0 : aPaint = pVDev->GetBitmap( aPoint, aDstSizePixel );
460 0 : pVDev->EnableMapMode( true );
461 :
462 : // create mask bitmap
463 0 : pVDev->SetLineColor( COL_BLACK );
464 0 : pVDev->SetFillColor( COL_BLACK );
465 0 : pVDev->DrawRect( Rectangle( aPoint, aDstSize ) );
466 0 : pVDev->SetDrawMode( DRAWMODE_WHITELINE | DRAWMODE_WHITEFILL | DRAWMODE_WHITETEXT |
467 0 : DRAWMODE_WHITEBITMAP | DRAWMODE_WHITEGRADIENT );
468 0 : aTmpMtf.WindStart();
469 0 : aTmpMtf.Play( pVDev.get(), aPoint, aDstSize );
470 0 : aTmpMtf.WindStart();
471 0 : pVDev->EnableMapMode( false );
472 0 : aMask = pVDev->GetBitmap( aPoint, aDstSizePixel );
473 0 : pVDev->EnableMapMode( true );
474 :
475 : // create alpha mask from gradient
476 0 : pVDev->SetDrawMode( DRAWMODE_GRAYGRADIENT );
477 0 : pVDev->DrawGradient( Rectangle( aPoint, aDstSize ), rTransparenceGradient );
478 0 : pVDev->SetDrawMode( DRAWMODE_DEFAULT );
479 0 : pVDev->EnableMapMode( false );
480 0 : pVDev->DrawMask( aPoint, aDstSizePixel, aMask, Color( COL_WHITE ) );
481 0 : aAlpha = pVDev->GetBitmap( aPoint, aDstSizePixel );
482 0 : implWriteBitmapEx( rPos, rSize, BitmapEx( aPaint, aAlpha ), pDummyVDev, i_rContext );
483 0 : }
484 : }
485 0 : }
486 : }
487 0 : break;
488 :
489 : case( META_EPS_ACTION ):
490 : {
491 0 : const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pAction);
492 0 : const GDIMetaFile aSubstitute( pA->GetSubstitute() );
493 :
494 0 : m_rOuterFace.Push();
495 0 : pDummyVDev->Push();
496 :
497 0 : MapMode aMapMode( aSubstitute.GetPrefMapMode() );
498 0 : Size aOutSize( OutputDevice::LogicToLogic( pA->GetSize(), pDummyVDev->GetMapMode(), aMapMode ) );
499 0 : aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) );
500 0 : aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) );
501 0 : aMapMode.SetOrigin( OutputDevice::LogicToLogic( pA->GetPoint(), pDummyVDev->GetMapMode(), aMapMode ) );
502 :
503 0 : m_rOuterFace.SetMapMode( aMapMode );
504 0 : pDummyVDev->SetMapMode( aMapMode );
505 0 : playMetafile( aSubstitute, NULL, i_rContext, pDummyVDev );
506 0 : pDummyVDev->Pop();
507 0 : m_rOuterFace.Pop();
508 : }
509 0 : break;
510 :
511 : case( META_COMMENT_ACTION ):
512 0 : if( ! i_rContext.m_bTransparenciesWereRemoved )
513 : {
514 0 : const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
515 :
516 0 : if( pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
517 : {
518 0 : const MetaGradientExAction* pGradAction = NULL;
519 0 : bool bDone = false;
520 :
521 0 : while( !bDone && ( ++i < nCount ) )
522 : {
523 0 : pAction = aMtf.GetAction( i );
524 :
525 0 : if( pAction->GetType() == META_GRADIENTEX_ACTION )
526 0 : pGradAction = static_cast<const MetaGradientExAction*>(pAction);
527 0 : else if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
528 0 : ( static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END")) )
529 : {
530 0 : bDone = true;
531 : }
532 : }
533 :
534 0 : if( pGradAction )
535 : {
536 0 : if (lcl_canUsePDFAxialShading(pGradAction->GetGradient()))
537 : {
538 0 : m_rOuterFace.DrawGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient() );
539 : }
540 : else
541 : {
542 0 : implWriteGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), pDummyVDev, i_rContext );
543 : }
544 : }
545 : }
546 : else
547 : {
548 0 : const sal_uInt8* pData = pA->GetData();
549 0 : if ( pData )
550 : {
551 0 : SvMemoryStream aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ );
552 0 : bool bSkipSequence = false;
553 0 : OString sSeqEnd;
554 :
555 0 : if( pA->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
556 : {
557 0 : sSeqEnd = OString("XPATHSTROKE_SEQ_END");
558 0 : SvtGraphicStroke aStroke;
559 0 : ReadSvtGraphicStroke( aMemStm, aStroke );
560 :
561 0 : Polygon aPath;
562 0 : aStroke.getPath( aPath );
563 :
564 0 : tools::PolyPolygon aStartArrow;
565 0 : tools::PolyPolygon aEndArrow;
566 0 : double fTransparency( aStroke.getTransparency() );
567 0 : double fStrokeWidth( aStroke.getStrokeWidth() );
568 0 : SvtGraphicStroke::DashArray aDashArray;
569 :
570 0 : aStroke.getStartArrow( aStartArrow );
571 0 : aStroke.getEndArrow( aEndArrow );
572 0 : aStroke.getDashArray( aDashArray );
573 :
574 0 : bSkipSequence = true;
575 0 : if ( aStartArrow.Count() || aEndArrow.Count() )
576 0 : bSkipSequence = false;
577 0 : if ( aDashArray.size() && ( fStrokeWidth != 0.0 ) && ( fTransparency == 0.0 ) )
578 0 : bSkipSequence = false;
579 0 : if ( bSkipSequence )
580 : {
581 0 : PDFWriter::ExtLineInfo aInfo;
582 0 : aInfo.m_fLineWidth = fStrokeWidth;
583 0 : aInfo.m_fTransparency = fTransparency;
584 0 : aInfo.m_fMiterLimit = aStroke.getMiterLimit();
585 0 : switch( aStroke.getCapType() )
586 : {
587 : default:
588 0 : case SvtGraphicStroke::capButt: aInfo.m_eCap = PDFWriter::capButt;break;
589 0 : case SvtGraphicStroke::capRound: aInfo.m_eCap = PDFWriter::capRound;break;
590 0 : case SvtGraphicStroke::capSquare: aInfo.m_eCap = PDFWriter::capSquare;break;
591 : }
592 0 : switch( aStroke.getJoinType() )
593 : {
594 : default:
595 0 : case SvtGraphicStroke::joinMiter: aInfo.m_eJoin = PDFWriter::joinMiter;break;
596 0 : case SvtGraphicStroke::joinRound: aInfo.m_eJoin = PDFWriter::joinRound;break;
597 0 : case SvtGraphicStroke::joinBevel: aInfo.m_eJoin = PDFWriter::joinBevel;break;
598 : case SvtGraphicStroke::joinNone:
599 0 : aInfo.m_eJoin = PDFWriter::joinMiter;
600 0 : aInfo.m_fMiterLimit = 0.0;
601 0 : break;
602 : }
603 0 : aInfo.m_aDashArray = aDashArray;
604 :
605 0 : if(SvtGraphicStroke::joinNone == aStroke.getJoinType()
606 0 : && fStrokeWidth > 0.0)
607 : {
608 : // emulate no edge rounding by handling single edges
609 0 : const sal_uInt16 nPoints(aPath.GetSize());
610 0 : const bool bCurve(aPath.HasFlags());
611 :
612 0 : for(sal_uInt16 a(0); a + 1 < nPoints; a++)
613 : {
614 0 : if(bCurve
615 0 : && POLY_NORMAL != aPath.GetFlags(a + 1)
616 0 : && a + 2 < nPoints
617 0 : && POLY_NORMAL != aPath.GetFlags(a + 2)
618 0 : && a + 3 < nPoints)
619 : {
620 : const Polygon aSnippet(4,
621 0 : aPath.GetConstPointAry() + a,
622 0 : aPath.GetConstFlagAry() + a);
623 0 : m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
624 0 : a += 2;
625 : }
626 : else
627 : {
628 : const Polygon aSnippet(2,
629 0 : aPath.GetConstPointAry() + a);
630 0 : m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
631 : }
632 : }
633 : }
634 : else
635 : {
636 0 : m_rOuterFace.DrawPolyLine( aPath, aInfo );
637 0 : }
638 0 : }
639 : }
640 0 : else if ( pA->GetComment() == "XPATHFILL_SEQ_BEGIN" )
641 : {
642 0 : sSeqEnd = OString("XPATHFILL_SEQ_END");
643 0 : SvtGraphicFill aFill;
644 0 : ReadSvtGraphicFill( aMemStm, aFill );
645 :
646 0 : if ( ( aFill.getFillType() == SvtGraphicFill::fillSolid ) && ( aFill.getFillRule() == SvtGraphicFill::fillEvenOdd ) )
647 : {
648 0 : double fTransparency = aFill.getTransparency();
649 0 : if ( fTransparency == 0.0 )
650 : {
651 0 : tools::PolyPolygon aPath;
652 0 : aFill.getPath( aPath );
653 :
654 0 : bSkipSequence = true;
655 0 : m_rOuterFace.DrawPolyPolygon( aPath );
656 : }
657 0 : else if ( fTransparency == 1.0 )
658 0 : bSkipSequence = true;
659 0 : }
660 : /* #i81548# removing optimization for fill textures, because most of the texture settings are not
661 : exported properly. In OpenOffice 3.1 the drawing layer will support graphic primitives, then it
662 : will not be a problem to optimize the filltexture export. But for wysiwyg is more important than
663 : filesize.
664 : else if( aFill.getFillType() == SvtGraphicFill::fillTexture && aFill.isTiling() )
665 : {
666 : sal_Int32 nPattern = mnCachePatternId;
667 : Graphic aPatternGraphic;
668 : aFill.getGraphic( aPatternGraphic );
669 : bool bUseCache = false;
670 : SvtGraphicFill::Transform aPatTransform;
671 : aFill.getTransform( aPatTransform );
672 :
673 : if( mnCachePatternId >= 0 )
674 : {
675 : SvtGraphicFill::Transform aCacheTransform;
676 : maCacheFill.getTransform( aCacheTransform );
677 : if( aCacheTransform.matrix[0] == aPatTransform.matrix[0] &&
678 : aCacheTransform.matrix[1] == aPatTransform.matrix[1] &&
679 : aCacheTransform.matrix[2] == aPatTransform.matrix[2] &&
680 : aCacheTransform.matrix[3] == aPatTransform.matrix[3] &&
681 : aCacheTransform.matrix[4] == aPatTransform.matrix[4] &&
682 : aCacheTransform.matrix[5] == aPatTransform.matrix[5]
683 : )
684 : {
685 : Graphic aCacheGraphic;
686 : maCacheFill.getGraphic( aCacheGraphic );
687 : if( aCacheGraphic == aPatternGraphic )
688 : bUseCache = true;
689 : }
690 : }
691 :
692 : if( ! bUseCache )
693 : {
694 :
695 : // paint graphic to metafile
696 : GDIMetaFile aPattern;
697 : pDummyVDev->SetConnectMetaFile( &aPattern );
698 : pDummyVDev->Push();
699 : pDummyVDev->SetMapMode( aPatternGraphic.GetPrefMapMode() );
700 :
701 : aPatternGraphic.Draw( &rDummyVDev, Point( 0, 0 ) );
702 : pDummyVDev->Pop();
703 : pDummyVDev->SetConnectMetaFile( NULL );
704 : aPattern.WindStart();
705 :
706 : MapMode aPatternMapMode( aPatternGraphic.GetPrefMapMode() );
707 : // prepare pattern from metafile
708 : Size aPrefSize( aPatternGraphic.GetPrefSize() );
709 : // FIXME: this magic -1 shouldn't be necessary
710 : aPrefSize.Width() -= 1;
711 : aPrefSize.Height() -= 1;
712 : aPrefSize = m_rOuterFace.GetReferenceDevice()->
713 : LogicToLogic( aPrefSize,
714 : &aPatternMapMode,
715 : &m_rOuterFace.GetReferenceDevice()->GetMapMode() );
716 : // build bounding rectangle of pattern
717 : Rectangle aBound( Point( 0, 0 ), aPrefSize );
718 : m_rOuterFace.BeginPattern( aBound );
719 : m_rOuterFace.Push();
720 : pDummyVDev->Push();
721 : m_rOuterFace.SetMapMode( aPatternMapMode );
722 : pDummyVDev->SetMapMode( aPatternMapMode );
723 : ImplWriteActions( m_rOuterFace, NULL, aPattern, rDummyVDev );
724 : pDummyVDev->Pop();
725 : m_rOuterFace.Pop();
726 :
727 : nPattern = m_rOuterFace.EndPattern( aPatTransform );
728 :
729 : // try some caching and reuse pattern
730 : mnCachePatternId = nPattern;
731 : maCacheFill = aFill;
732 : }
733 :
734 : // draw polypolygon with pattern fill
735 : tools::PolyPolygon aPath;
736 : aFill.getPath( aPath );
737 : m_rOuterFace.DrawPolyPolygon( aPath, nPattern, aFill.getFillRule() == SvtGraphicFill::fillEvenOdd );
738 :
739 : bSkipSequence = true;
740 : }
741 : */
742 : }
743 0 : if ( bSkipSequence )
744 : {
745 0 : while( ++i < nCount )
746 : {
747 0 : pAction = aMtf.GetAction( i );
748 0 : if ( pAction->GetType() == META_COMMENT_ACTION )
749 : {
750 0 : OString sComment( static_cast<const MetaCommentAction*>(pAction)->GetComment() );
751 0 : if (sComment == sSeqEnd)
752 0 : break;
753 : }
754 : // #i44496#
755 : // the replacement action for stroke is a filled rectangle
756 : // the set fillcolor of the replacement is part of the graphics
757 : // state and must not be skipped
758 0 : else if( pAction->GetType() == META_FILLCOLOR_ACTION )
759 : {
760 0 : const MetaFillColorAction* pMA = static_cast<const MetaFillColorAction*>(pAction);
761 0 : if( pMA->IsSetting() )
762 0 : m_rOuterFace.SetFillColor( pMA->GetColor() );
763 : else
764 0 : m_rOuterFace.SetFillColor();
765 : }
766 : }
767 0 : }
768 : }
769 : }
770 : }
771 0 : break;
772 :
773 : case( META_BMP_ACTION ):
774 : {
775 0 : const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
776 0 : BitmapEx aBitmapEx( pA->GetBitmap() );
777 0 : Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
778 0 : aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
779 0 : if( ! ( aSize.Width() && aSize.Height() ) )
780 0 : aSize = pDummyVDev->PixelToLogic( aBitmapEx.GetSizePixel() );
781 0 : implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, pDummyVDev, i_rContext );
782 : }
783 0 : break;
784 :
785 : case( META_BMPSCALE_ACTION ):
786 : {
787 0 : const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
788 0 : implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), BitmapEx( pA->GetBitmap() ), pDummyVDev, i_rContext );
789 : }
790 0 : break;
791 :
792 : case( META_BMPSCALEPART_ACTION ):
793 : {
794 0 : const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
795 0 : BitmapEx aBitmapEx( pA->GetBitmap() );
796 0 : aBitmapEx.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
797 0 : implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, pDummyVDev, i_rContext );
798 : }
799 0 : break;
800 :
801 : case( META_BMPEX_ACTION ):
802 : {
803 0 : const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction);
804 0 : BitmapEx aBitmapEx( pA->GetBitmapEx() );
805 0 : Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
806 0 : aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
807 0 : implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, pDummyVDev, i_rContext );
808 : }
809 0 : break;
810 :
811 : case( META_BMPEXSCALE_ACTION ):
812 : {
813 0 : const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
814 0 : implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), pA->GetBitmapEx(), pDummyVDev, i_rContext );
815 : }
816 0 : break;
817 :
818 : case( META_BMPEXSCALEPART_ACTION ):
819 : {
820 0 : const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
821 0 : BitmapEx aBitmapEx( pA->GetBitmapEx() );
822 0 : aBitmapEx.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
823 0 : implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, pDummyVDev, i_rContext );
824 : }
825 0 : break;
826 :
827 : case( META_MASK_ACTION ):
828 : case( META_MASKSCALE_ACTION ):
829 : case( META_MASKSCALEPART_ACTION ):
830 : {
831 : OSL_TRACE( "MetaMask...Action not supported yet" );
832 : }
833 0 : break;
834 :
835 : case( META_TEXT_ACTION ):
836 : {
837 0 : const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
838 0 : m_rOuterFace.DrawText( pA->GetPoint(), pA->GetText().copy( pA->GetIndex(), std::min<sal_Int32>(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) ) );
839 : }
840 0 : break;
841 :
842 : case( META_TEXTRECT_ACTION ):
843 : {
844 0 : const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
845 0 : m_rOuterFace.DrawText( pA->GetRect(), pA->GetText(), pA->GetStyle() );
846 : }
847 0 : break;
848 :
849 : case( META_TEXTARRAY_ACTION ):
850 : {
851 0 : const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
852 0 : m_rOuterFace.DrawTextArray( pA->GetPoint(), pA->GetText(), pA->GetDXArray(), pA->GetIndex(), pA->GetLen() );
853 : }
854 0 : break;
855 :
856 : case( META_STRETCHTEXT_ACTION ):
857 : {
858 0 : const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
859 0 : m_rOuterFace.DrawStretchText( pA->GetPoint(), pA->GetWidth(), pA->GetText(), pA->GetIndex(), pA->GetLen() );
860 : }
861 0 : break;
862 :
863 : case( META_TEXTLINE_ACTION ):
864 : {
865 0 : const MetaTextLineAction* pA = static_cast<const MetaTextLineAction*>(pAction);
866 0 : m_rOuterFace.DrawTextLine( pA->GetStartPoint(), pA->GetWidth(), pA->GetStrikeout(), pA->GetUnderline(), pA->GetOverline() );
867 :
868 : }
869 0 : break;
870 :
871 : case( META_CLIPREGION_ACTION ):
872 : {
873 0 : const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
874 :
875 0 : if( pA->IsClipping() )
876 : {
877 0 : if( pA->GetRegion().IsEmpty() )
878 0 : m_rOuterFace.SetClipRegion( basegfx::B2DPolyPolygon() );
879 : else
880 : {
881 0 : vcl::Region aReg( pA->GetRegion() );
882 0 : m_rOuterFace.SetClipRegion( aReg.GetAsB2DPolyPolygon() );
883 : }
884 : }
885 : else
886 0 : m_rOuterFace.SetClipRegion();
887 : }
888 0 : break;
889 :
890 : case( META_ISECTRECTCLIPREGION_ACTION ):
891 : {
892 0 : const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction);
893 0 : m_rOuterFace.IntersectClipRegion( pA->GetRect() );
894 : }
895 0 : break;
896 :
897 : case( META_ISECTREGIONCLIPREGION_ACTION ):
898 : {
899 0 : const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction);
900 0 : vcl::Region aReg( pA->GetRegion() );
901 0 : m_rOuterFace.IntersectClipRegion( aReg.GetAsB2DPolyPolygon() );
902 : }
903 0 : break;
904 :
905 : case( META_MOVECLIPREGION_ACTION ):
906 : {
907 0 : const MetaMoveClipRegionAction* pA = static_cast<const MetaMoveClipRegionAction*>(pAction);
908 0 : m_rOuterFace.MoveClipRegion( pA->GetHorzMove(), pA->GetVertMove() );
909 : }
910 0 : break;
911 :
912 : case( META_MAPMODE_ACTION ):
913 : {
914 0 : const_cast< MetaAction* >( pAction )->Execute( pDummyVDev );
915 0 : m_rOuterFace.SetMapMode( pDummyVDev->GetMapMode() );
916 : }
917 0 : break;
918 :
919 : case( META_LINECOLOR_ACTION ):
920 : {
921 0 : const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction);
922 :
923 0 : if( pA->IsSetting() )
924 0 : m_rOuterFace.SetLineColor( pA->GetColor() );
925 : else
926 0 : m_rOuterFace.SetLineColor();
927 : }
928 0 : break;
929 :
930 : case( META_FILLCOLOR_ACTION ):
931 : {
932 0 : const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction);
933 :
934 0 : if( pA->IsSetting() )
935 0 : m_rOuterFace.SetFillColor( pA->GetColor() );
936 : else
937 0 : m_rOuterFace.SetFillColor();
938 : }
939 0 : break;
940 :
941 : case( META_TEXTLINECOLOR_ACTION ):
942 : {
943 0 : const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction);
944 :
945 0 : if( pA->IsSetting() )
946 0 : m_rOuterFace.SetTextLineColor( pA->GetColor() );
947 : else
948 0 : m_rOuterFace.SetTextLineColor();
949 : }
950 0 : break;
951 :
952 : case( META_OVERLINECOLOR_ACTION ):
953 : {
954 0 : const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction);
955 :
956 0 : if( pA->IsSetting() )
957 0 : m_rOuterFace.SetOverlineColor( pA->GetColor() );
958 : else
959 0 : m_rOuterFace.SetOverlineColor();
960 : }
961 0 : break;
962 :
963 : case( META_TEXTFILLCOLOR_ACTION ):
964 : {
965 0 : const MetaTextFillColorAction* pA = static_cast<const MetaTextFillColorAction*>(pAction);
966 :
967 0 : if( pA->IsSetting() )
968 0 : m_rOuterFace.SetTextFillColor( pA->GetColor() );
969 : else
970 0 : m_rOuterFace.SetTextFillColor();
971 : }
972 0 : break;
973 :
974 : case( META_TEXTCOLOR_ACTION ):
975 : {
976 0 : const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pAction);
977 0 : m_rOuterFace.SetTextColor( pA->GetColor() );
978 : }
979 0 : break;
980 :
981 : case( META_TEXTALIGN_ACTION ):
982 : {
983 0 : const MetaTextAlignAction* pA = static_cast<const MetaTextAlignAction*>(pAction);
984 0 : m_rOuterFace.SetTextAlign( pA->GetTextAlign() );
985 : }
986 0 : break;
987 :
988 : case( META_FONT_ACTION ):
989 : {
990 0 : const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
991 0 : m_rOuterFace.SetFont( pA->GetFont() );
992 : }
993 0 : break;
994 :
995 : case( META_PUSH_ACTION ):
996 : {
997 0 : const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
998 :
999 0 : pDummyVDev->Push( pA->GetFlags() );
1000 0 : m_rOuterFace.Push( pA->GetFlags() );
1001 : }
1002 0 : break;
1003 :
1004 : case( META_POP_ACTION ):
1005 : {
1006 0 : pDummyVDev->Pop();
1007 0 : m_rOuterFace.Pop();
1008 : }
1009 0 : break;
1010 :
1011 : case( META_LAYOUTMODE_ACTION ):
1012 : {
1013 0 : const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
1014 0 : m_rOuterFace.SetLayoutMode( pA->GetLayoutMode() );
1015 : }
1016 0 : break;
1017 :
1018 : case META_TEXTLANGUAGE_ACTION:
1019 : {
1020 0 : const MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
1021 0 : m_rOuterFace.SetDigitLanguage( pA->GetTextLanguage() );
1022 : }
1023 0 : break;
1024 :
1025 : case( META_WALLPAPER_ACTION ):
1026 : {
1027 0 : const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
1028 0 : m_rOuterFace.DrawWallpaper( pA->GetRect(), pA->GetWallpaper() );
1029 : }
1030 0 : break;
1031 :
1032 : case( META_RASTEROP_ACTION ):
1033 : {
1034 : // !!! >>> we don't want to support this actions
1035 : }
1036 0 : break;
1037 :
1038 : case( META_REFPOINT_ACTION ):
1039 : {
1040 : // !!! >>> we don't want to support this actions
1041 : }
1042 0 : break;
1043 :
1044 : default:
1045 : // #i24604# Made assertion fire only once per
1046 : // metafile. The asserted actions here are all
1047 : // deprecated
1048 0 : if( !bAssertionFired )
1049 : {
1050 0 : bAssertionFired = true;
1051 : OSL_TRACE( "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered" );
1052 : }
1053 0 : break;
1054 : }
1055 0 : i++;
1056 : }
1057 0 : }
1058 0 : }
1059 :
1060 : // Encryption methods
1061 :
1062 : /* a crutch to transport an rtlDigest safely though UNO API
1063 : this is needed for the PDF export dialog, which otherwise would have to pass
1064 : clear text passwords down till they can be used in PDFWriter. Unfortunately
1065 : the MD5 sum of the password (which is needed to create the PDF encryption key)
1066 : is not sufficient, since an rtl MD5 digest cannot be created in an arbitrary state
1067 : which would be needed in PDFWriterImpl::computeEncryptionKey.
1068 : */
1069 : class EncHashTransporter : public cppu::WeakImplHelper1 < com::sun::star::beans::XMaterialHolder >
1070 : {
1071 : rtlDigest maUDigest;
1072 : sal_IntPtr maID;
1073 : std::vector< sal_uInt8 > maOValue;
1074 :
1075 : static std::map< sal_IntPtr, EncHashTransporter* > sTransporters;
1076 : public:
1077 0 : EncHashTransporter()
1078 0 : : maUDigest( rtl_digest_createMD5() )
1079 : {
1080 0 : maID = reinterpret_cast< sal_IntPtr >(this);
1081 0 : while( sTransporters.find( maID ) != sTransporters.end() ) // paranoia mode
1082 0 : maID++;
1083 0 : sTransporters[ maID ] = this;
1084 0 : }
1085 :
1086 0 : virtual ~EncHashTransporter()
1087 0 : {
1088 0 : sTransporters.erase( maID );
1089 0 : if( maUDigest )
1090 0 : rtl_digest_destroyMD5( maUDigest );
1091 : OSL_TRACE( "EncHashTransporter freed" );
1092 0 : }
1093 :
1094 0 : rtlDigest getUDigest() const { return maUDigest; };
1095 0 : std::vector< sal_uInt8 >& getOValue() { return maOValue; }
1096 0 : void invalidate()
1097 : {
1098 0 : if( maUDigest )
1099 : {
1100 0 : rtl_digest_destroyMD5( maUDigest );
1101 0 : maUDigest = NULL;
1102 : }
1103 0 : }
1104 :
1105 : // XMaterialHolder
1106 0 : virtual uno::Any SAL_CALL getMaterial() throw(std::exception) SAL_OVERRIDE
1107 : {
1108 0 : return uno::makeAny( sal_Int64(maID) );
1109 : }
1110 :
1111 : static EncHashTransporter* getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& );
1112 :
1113 : };
1114 :
1115 411 : std::map< sal_IntPtr, EncHashTransporter* > EncHashTransporter::sTransporters;
1116 :
1117 0 : EncHashTransporter* EncHashTransporter::getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& xRef )
1118 : {
1119 0 : EncHashTransporter* pResult = NULL;
1120 0 : if( xRef.is() )
1121 : {
1122 0 : uno::Any aMat( xRef->getMaterial() );
1123 0 : sal_Int64 nMat = 0;
1124 0 : if( aMat >>= nMat )
1125 : {
1126 0 : std::map< sal_IntPtr, EncHashTransporter* >::iterator it = sTransporters.find( static_cast<sal_IntPtr>(nMat) );
1127 0 : if( it != sTransporters.end() )
1128 0 : pResult = it->second;
1129 0 : }
1130 : }
1131 0 : return pResult;
1132 : }
1133 :
1134 0 : bool PDFWriterImpl::checkEncryptionBufferSize( register sal_Int32 newSize )
1135 : {
1136 0 : if( m_nEncryptionBufferSize < newSize )
1137 : {
1138 : /* reallocate the buffer, the used function allocate as rtl_allocateMemory
1139 : if the pointer parameter is NULL */
1140 0 : m_pEncryptionBuffer = (sal_uInt8*)rtl_reallocateMemory( m_pEncryptionBuffer, newSize );
1141 0 : if( m_pEncryptionBuffer )
1142 0 : m_nEncryptionBufferSize = newSize;
1143 : else
1144 0 : m_nEncryptionBufferSize = 0;
1145 : }
1146 0 : return ( m_nEncryptionBufferSize != 0 );
1147 : }
1148 :
1149 0 : void PDFWriterImpl::checkAndEnableStreamEncryption( register sal_Int32 nObject )
1150 : {
1151 0 : if( m_aContext.Encryption.Encrypt() )
1152 : {
1153 0 : m_bEncryptThisStream = true;
1154 0 : sal_Int32 i = m_nKeyLength;
1155 0 : m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject;
1156 0 : m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 );
1157 0 : m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 );
1158 : // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1159 : // do the MD5 hash
1160 : sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1161 : // the i+2 to take into account the generation number, always zero
1162 0 : rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) );
1163 : // initialize the RC4 with the key
1164 : // key length: see algorithm 3.1, step 4: (N+5) max 16
1165 0 : rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 );
1166 : }
1167 0 : }
1168 :
1169 0 : void PDFWriterImpl::enableStringEncryption( register sal_Int32 nObject )
1170 : {
1171 0 : if( m_aContext.Encryption.Encrypt() )
1172 : {
1173 0 : sal_Int32 i = m_nKeyLength;
1174 0 : m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject;
1175 0 : m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 );
1176 0 : m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 );
1177 : // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1178 : // do the MD5 hash
1179 : sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1180 : // the i+2 to take into account the generation number, always zero
1181 0 : rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) );
1182 : // initialize the RC4 with the key
1183 : // key length: see algorithm 3.1, step 4: (N+5) max 16
1184 0 : rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 );
1185 : }
1186 0 : }
1187 :
1188 : /* init the encryption engine
1189 : 1. init the document id, used both for building the document id and for building the encryption key(s)
1190 : 2. build the encryption key following algorithms described in the PDF specification
1191 : */
1192 0 : uno::Reference< beans::XMaterialHolder > PDFWriterImpl::initEncryption( const OUString& i_rOwnerPassword,
1193 : const OUString& i_rUserPassword,
1194 : bool b128Bit
1195 : )
1196 : {
1197 0 : uno::Reference< beans::XMaterialHolder > xResult;
1198 0 : if( !i_rOwnerPassword.isEmpty() || !i_rUserPassword.isEmpty() )
1199 : {
1200 0 : EncHashTransporter* pTransporter = new EncHashTransporter;
1201 0 : xResult = pTransporter;
1202 :
1203 : // get padded passwords
1204 : sal_uInt8 aPadUPW[ENCRYPTED_PWD_SIZE], aPadOPW[ENCRYPTED_PWD_SIZE];
1205 0 : padPassword( i_rOwnerPassword.isEmpty() ? i_rUserPassword : i_rOwnerPassword, aPadOPW );
1206 0 : padPassword( i_rUserPassword, aPadUPW );
1207 0 : sal_Int32 nKeyLength = SECUR_40BIT_KEY;
1208 0 : if( b128Bit )
1209 0 : nKeyLength = SECUR_128BIT_KEY;
1210 :
1211 0 : if( computeODictionaryValue( aPadOPW, aPadUPW, pTransporter->getOValue(), nKeyLength ) )
1212 : {
1213 0 : rtlDigest aDig = pTransporter->getUDigest();
1214 0 : if( rtl_digest_updateMD5( aDig, aPadUPW, ENCRYPTED_PWD_SIZE ) != rtl_Digest_E_None )
1215 0 : xResult.clear();
1216 : }
1217 : else
1218 0 : xResult.clear();
1219 :
1220 : // trash temporary padded cleartext PWDs
1221 0 : memset( aPadOPW, 0, sizeof(aPadOPW) );
1222 0 : memset( aPadUPW, 0, sizeof(aPadUPW) );
1223 :
1224 : }
1225 0 : return xResult;
1226 : }
1227 :
1228 0 : bool PDFWriterImpl::prepareEncryption( const uno::Reference< beans::XMaterialHolder >& xEnc )
1229 : {
1230 0 : bool bSuccess = false;
1231 0 : EncHashTransporter* pTransporter = EncHashTransporter::getEncHashTransporter( xEnc );
1232 0 : if( pTransporter )
1233 : {
1234 0 : sal_Int32 nKeyLength = 0, nRC4KeyLength = 0;
1235 0 : sal_Int32 nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, nKeyLength, nRC4KeyLength );
1236 0 : m_aContext.Encryption.OValue = pTransporter->getOValue();
1237 0 : bSuccess = computeUDictionaryValue( pTransporter, m_aContext.Encryption, nKeyLength, nAccessPermissions );
1238 : }
1239 0 : if( ! bSuccess )
1240 : {
1241 0 : m_aContext.Encryption.OValue.clear();
1242 0 : m_aContext.Encryption.UValue.clear();
1243 0 : m_aContext.Encryption.EncryptionKey.clear();
1244 : }
1245 0 : return bSuccess;
1246 : }
1247 :
1248 0 : sal_Int32 PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties,
1249 : sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength )
1250 : {
1251 : /*
1252 : 2) compute the access permissions, in numerical form
1253 :
1254 : the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
1255 : - for 40 bit security the unused bit must be set to 1, since they are not used
1256 : - for 128 bit security the same bit must be preset to 0 and set later if needed
1257 : according to the table 3.15, pdf v 1.4 */
1258 0 : sal_Int32 nAccessPermissions = ( i_rProperties.Security128bit ) ? 0xfffff0c0 : 0xffffffc0 ;
1259 :
1260 : /* check permissions for 40 bit security case */
1261 0 : nAccessPermissions |= ( i_rProperties.CanPrintTheDocument ) ? 1 << 2 : 0;
1262 0 : nAccessPermissions |= ( i_rProperties.CanModifyTheContent ) ? 1 << 3 : 0;
1263 0 : nAccessPermissions |= ( i_rProperties.CanCopyOrExtract ) ? 1 << 4 : 0;
1264 0 : nAccessPermissions |= ( i_rProperties.CanAddOrModify ) ? 1 << 5 : 0;
1265 0 : o_rKeyLength = SECUR_40BIT_KEY;
1266 0 : o_rRC4KeyLength = SECUR_40BIT_KEY+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5
1267 :
1268 0 : if( i_rProperties.Security128bit )
1269 : {
1270 0 : o_rKeyLength = SECUR_128BIT_KEY;
1271 0 : o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum
1272 : // permitted value is 16
1273 0 : nAccessPermissions |= ( i_rProperties.CanFillInteractive ) ? 1 << 8 : 0;
1274 0 : nAccessPermissions |= ( i_rProperties.CanExtractForAccessibility ) ? 1 << 9 : 0;
1275 0 : nAccessPermissions |= ( i_rProperties.CanAssemble ) ? 1 << 10 : 0;
1276 0 : nAccessPermissions |= ( i_rProperties.CanPrintFull ) ? 1 << 11 : 0;
1277 : }
1278 0 : return nAccessPermissions;
1279 : }
1280 :
1281 : /*************************************************************
1282 : begin i12626 methods
1283 :
1284 : Implements Algorithm 3.2, step 1 only
1285 : */
1286 0 : void PDFWriterImpl::padPassword( const OUString& i_rPassword, sal_uInt8* o_pPaddedPW )
1287 : {
1288 : // get ansi-1252 version of the password string CHECKIT ! i12626
1289 0 : OString aString( OUStringToOString( i_rPassword, RTL_TEXTENCODING_MS_1252 ) );
1290 :
1291 : //copy the string to the target
1292 0 : sal_Int32 nToCopy = ( aString.getLength() < ENCRYPTED_PWD_SIZE ) ? aString.getLength() : ENCRYPTED_PWD_SIZE;
1293 : sal_Int32 nCurrentChar;
1294 :
1295 0 : for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ )
1296 0 : o_pPaddedPW[nCurrentChar] = (sal_uInt8)( aString[nCurrentChar] );
1297 :
1298 : //pad it with standard byte string
1299 : sal_Int32 i,y;
1300 0 : for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ )
1301 0 : o_pPaddedPW[i] = s_nPadString[y];
1302 0 : }
1303 :
1304 : /**********************************
1305 : Algorithm 3.2 Compute the encryption key used
1306 :
1307 : step 1 should already be done before calling, the paThePaddedPassword parameter should contain
1308 : the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
1309 : it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
1310 :
1311 : TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
1312 :
1313 : */
1314 0 : bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter* i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, sal_Int32 i_nAccessPermissions )
1315 : {
1316 0 : bool bSuccess = true;
1317 : sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1318 :
1319 : // transporter contains an MD5 digest with the padded user password already
1320 0 : rtlDigest aDigest = i_pTransporter->getUDigest();
1321 0 : rtlDigestError nError = rtl_Digest_E_None;
1322 0 : if( aDigest )
1323 : {
1324 : //step 3
1325 0 : if( ! io_rProperties.OValue.empty() )
1326 0 : nError = rtl_digest_updateMD5( aDigest, &io_rProperties.OValue[0] , sal_Int32(io_rProperties.OValue.size()) );
1327 : else
1328 0 : bSuccess = false;
1329 : //Step 4
1330 : sal_uInt8 nPerm[4];
1331 :
1332 0 : nPerm[0] = (sal_uInt8)i_nAccessPermissions;
1333 0 : nPerm[1] = (sal_uInt8)( i_nAccessPermissions >> 8 );
1334 0 : nPerm[2] = (sal_uInt8)( i_nAccessPermissions >> 16 );
1335 0 : nPerm[3] = (sal_uInt8)( i_nAccessPermissions >> 24 );
1336 :
1337 0 : if( nError == rtl_Digest_E_None )
1338 0 : nError = rtl_digest_updateMD5( aDigest, nPerm , sizeof( nPerm ) );
1339 :
1340 : //step 5, get the document ID, binary form
1341 0 : if( nError == rtl_Digest_E_None )
1342 0 : nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) );
1343 : //get the digest
1344 0 : if( nError == rtl_Digest_E_None )
1345 : {
1346 0 : rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1347 :
1348 : //step 6, only if 128 bit
1349 0 : if( io_rProperties.Security128bit )
1350 : {
1351 0 : for( sal_Int32 i = 0; i < 50; i++ )
1352 : {
1353 0 : nError = rtl_digest_updateMD5( aDigest, &nMD5Sum, sizeof( nMD5Sum ) );
1354 0 : if( nError != rtl_Digest_E_None )
1355 : {
1356 0 : bSuccess = false;
1357 0 : break;
1358 : }
1359 0 : rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1360 : }
1361 : }
1362 : }
1363 : else
1364 : {
1365 0 : bSuccess = false;
1366 : }
1367 : }
1368 : else
1369 0 : bSuccess = false;
1370 :
1371 0 : i_pTransporter->invalidate();
1372 :
1373 : //Step 7
1374 0 : if( bSuccess )
1375 : {
1376 0 : io_rProperties.EncryptionKey.resize( MAXIMUM_RC4_KEY_LENGTH );
1377 0 : for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ )
1378 0 : io_rProperties.EncryptionKey[i] = nMD5Sum[i];
1379 : }
1380 : else
1381 0 : io_rProperties.EncryptionKey.clear();
1382 :
1383 0 : return bSuccess;
1384 : }
1385 :
1386 : /**********************************
1387 : Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member
1388 : the step numbers down here correspond to the ones in PDF v.1.4 specfication
1389 : */
1390 0 : bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword,
1391 : const sal_uInt8* i_pPaddedUserPassword,
1392 : std::vector< sal_uInt8 >& io_rOValue,
1393 : sal_Int32 i_nKeyLength
1394 : )
1395 : {
1396 0 : bool bSuccess = true;
1397 :
1398 0 : io_rOValue.resize( ENCRYPTED_PWD_SIZE );
1399 :
1400 0 : rtlDigest aDigest = rtl_digest_createMD5();
1401 0 : rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1402 0 : if( aDigest && aCipher)
1403 : {
1404 : //step 1 already done, data is in i_pPaddedOwnerPassword
1405 : //step 2
1406 :
1407 0 : rtlDigestError nError = rtl_digest_updateMD5( aDigest, i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE );
1408 0 : if( nError == rtl_Digest_E_None )
1409 : {
1410 : sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1411 :
1412 0 : rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) );
1413 : //step 3, only if 128 bit
1414 0 : if( i_nKeyLength == SECUR_128BIT_KEY )
1415 : {
1416 : sal_Int32 i;
1417 0 : for( i = 0; i < 50; i++ )
1418 : {
1419 0 : nError = rtl_digest_updateMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1420 0 : if( nError != rtl_Digest_E_None )
1421 : {
1422 0 : bSuccess = false;
1423 0 : break;
1424 : }
1425 0 : rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) );
1426 : }
1427 : }
1428 : //Step 4, the key is in nMD5Sum
1429 : //step 5 already done, data is in i_pPaddedUserPassword
1430 : //step 6
1431 : rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1432 0 : nMD5Sum, i_nKeyLength , NULL, 0 );
1433 : // encrypt the user password using the key set above
1434 : rtl_cipher_encodeARCFOUR( aCipher, i_pPaddedUserPassword, ENCRYPTED_PWD_SIZE, // the data to be encrypted
1435 0 : &io_rOValue[0], sal_Int32(io_rOValue.size()) ); //encrypted data
1436 : //Step 7, only if 128 bit
1437 0 : if( i_nKeyLength == SECUR_128BIT_KEY )
1438 : {
1439 : sal_uInt32 i, y;
1440 : sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key
1441 :
1442 0 : for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1443 : {
1444 0 : for( y = 0; y < sizeof( nLocalKey ); y++ )
1445 0 : nLocalKey[y] = (sal_uInt8)( nMD5Sum[y] ^ i );
1446 :
1447 : rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1448 0 : nLocalKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area, on init can be NULL
1449 0 : rtl_cipher_encodeARCFOUR( aCipher, &io_rOValue[0], sal_Int32(io_rOValue.size()), // the data to be encrypted
1450 0 : &io_rOValue[0], sal_Int32(io_rOValue.size()) ); // encrypted data, can be the same as the input, encrypt "in place"
1451 : //step 8, store in class data member
1452 : }
1453 : }
1454 : }
1455 : else
1456 0 : bSuccess = false;
1457 : }
1458 : else
1459 0 : bSuccess = false;
1460 :
1461 0 : if( aDigest )
1462 0 : rtl_digest_destroyMD5( aDigest );
1463 0 : if( aCipher )
1464 0 : rtl_cipher_destroyARCFOUR( aCipher );
1465 :
1466 0 : if( ! bSuccess )
1467 0 : io_rOValue.clear();
1468 0 : return bSuccess;
1469 : }
1470 :
1471 : /**********************************
1472 : 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)
1473 : */
1474 0 : bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter* i_pTransporter,
1475 : vcl::PDFWriter::PDFEncryptionProperties& io_rProperties,
1476 : sal_Int32 i_nKeyLength,
1477 : sal_Int32 i_nAccessPermissions
1478 : )
1479 : {
1480 0 : bool bSuccess = true;
1481 :
1482 0 : io_rProperties.UValue.resize( ENCRYPTED_PWD_SIZE );
1483 :
1484 0 : rtlDigest aDigest = rtl_digest_createMD5();
1485 0 : rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1486 0 : if( aDigest && aCipher )
1487 : {
1488 : //step 1, common to both 3.4 and 3.5
1489 0 : if( computeEncryptionKey( i_pTransporter, io_rProperties, i_nAccessPermissions ) )
1490 : {
1491 : // prepare encryption key for object
1492 0 : for( sal_Int32 i = i_nKeyLength, y = 0; y < 5 ; y++ )
1493 0 : io_rProperties.EncryptionKey[i++] = 0;
1494 :
1495 0 : if( io_rProperties.Security128bit == false )
1496 : {
1497 : //3.4
1498 : //step 2 and 3
1499 : rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1500 0 : &io_rProperties.EncryptionKey[0], 5 , // key and key length
1501 0 : NULL, 0 ); //destination data area
1502 : // encrypt the user password using the key set above, save for later use
1503 : rtl_cipher_encodeARCFOUR( aCipher, s_nPadString, sizeof( s_nPadString ), // the data to be encrypted
1504 0 : &io_rProperties.UValue[0], sal_Int32(io_rProperties.UValue.size()) ); //encrypted data, stored in class data member
1505 : }
1506 : else
1507 : {
1508 : //or 3.5, for 128 bit security
1509 : //step6, initialize the last 16 bytes of the encrypted user password to 0
1510 0 : for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sal_uInt32(io_rProperties.UValue.size()); i++)
1511 0 : io_rProperties.UValue[i] = 0;
1512 : //step 2
1513 0 : rtlDigestError nError = rtl_digest_updateMD5( aDigest, s_nPadString, sizeof( s_nPadString ) );
1514 : //step 3
1515 0 : if( nError == rtl_Digest_E_None )
1516 0 : nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) );
1517 : else
1518 0 : bSuccess = false;
1519 :
1520 : sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
1521 0 : rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) );
1522 : //Step 4
1523 : rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1524 0 : &io_rProperties.EncryptionKey[0], SECUR_128BIT_KEY, NULL, 0 ); //destination data area
1525 : rtl_cipher_encodeARCFOUR( aCipher, nMD5Sum, sizeof( nMD5Sum ), // the data to be encrypted
1526 0 : &io_rProperties.UValue[0], sizeof( nMD5Sum ) ); //encrypted data, stored in class data member
1527 : //step 5
1528 : sal_uInt32 i, y;
1529 : sal_uInt8 nLocalKey[SECUR_128BIT_KEY];
1530 :
1531 0 : for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1532 : {
1533 0 : for( y = 0; y < sizeof( nLocalKey ) ; y++ )
1534 0 : nLocalKey[y] = (sal_uInt8)( io_rProperties.EncryptionKey[y] ^ i );
1535 :
1536 : rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1537 : nLocalKey, SECUR_128BIT_KEY, // key and key length
1538 0 : NULL, 0 ); //destination data area, on init can be NULL
1539 0 : rtl_cipher_encodeARCFOUR( aCipher, &io_rProperties.UValue[0], SECUR_128BIT_KEY, // the data to be encrypted
1540 0 : &io_rProperties.UValue[0], SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place"
1541 : }
1542 : }
1543 : }
1544 : else
1545 0 : bSuccess = false;
1546 : }
1547 : else
1548 0 : bSuccess = false;
1549 :
1550 0 : if( aDigest )
1551 0 : rtl_digest_destroyMD5( aDigest );
1552 0 : if( aCipher )
1553 0 : rtl_cipher_destroyARCFOUR( aCipher );
1554 :
1555 0 : if( ! bSuccess )
1556 0 : io_rProperties.UValue.clear();
1557 0 : return bSuccess;
1558 : }
1559 :
1560 : /* end i12626 methods */
1561 :
1562 : static const long unsetRun[256] =
1563 : {
1564 : 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
1565 : 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
1566 : 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */
1567 : 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */
1568 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1569 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */
1570 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1571 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */
1572 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
1573 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
1574 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
1575 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
1576 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
1577 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
1578 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
1579 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */
1580 : };
1581 :
1582 : static const long setRun[256] =
1583 : {
1584 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
1585 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1586 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
1587 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
1588 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
1589 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
1590 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */
1591 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
1592 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */
1593 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */
1594 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */
1595 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */
1596 : 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */
1597 : 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */
1598 : 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
1599 : 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
1600 : };
1601 :
1602 0 : inline bool isSet( const Scanline i_pLine, long i_nIndex )
1603 : {
1604 0 : return (i_pLine[ i_nIndex/8 ] & (0x80 >> (i_nIndex&7))) != 0;
1605 : }
1606 :
1607 0 : long findBitRun( const Scanline i_pLine, long i_nStartIndex, long i_nW, bool i_bSet )
1608 : {
1609 0 : if( i_nStartIndex < 0 )
1610 0 : return i_nW;
1611 :
1612 0 : long nIndex = i_nStartIndex;
1613 0 : if( nIndex < i_nW )
1614 : {
1615 0 : const sal_uInt8 * pByte = static_cast<sal_uInt8*>(i_pLine) + (nIndex/8);
1616 0 : sal_uInt8 nByte = *pByte;
1617 :
1618 : // run up to byte boundary
1619 0 : long nBitInByte = (nIndex & 7);
1620 0 : if( nBitInByte )
1621 : {
1622 0 : sal_uInt8 nMask = 0x80 >> nBitInByte;
1623 0 : while( nBitInByte != 8 )
1624 : {
1625 0 : if( (nByte & nMask) != (i_bSet ? nMask : 0) )
1626 0 : return nIndex < i_nW ? nIndex : i_nW;
1627 0 : nMask = nMask >> 1;
1628 0 : nBitInByte++;
1629 0 : nIndex++;
1630 : }
1631 0 : if( nIndex < i_nW )
1632 : {
1633 0 : pByte++;
1634 0 : nByte = *pByte;
1635 : }
1636 : }
1637 :
1638 : sal_uInt8 nRunByte;
1639 : const long* pRunTable;
1640 0 : if( i_bSet )
1641 : {
1642 0 : nRunByte = 0xff;
1643 0 : pRunTable = setRun;
1644 : }
1645 : else
1646 : {
1647 0 : nRunByte = 0;
1648 0 : pRunTable = unsetRun;
1649 : }
1650 :
1651 0 : if( nIndex < i_nW )
1652 : {
1653 0 : while( nByte == nRunByte )
1654 : {
1655 0 : nIndex += 8;
1656 :
1657 0 : if (nIndex >= i_nW)
1658 0 : break;
1659 :
1660 0 : pByte++;
1661 0 : nByte = *pByte;
1662 : }
1663 : }
1664 :
1665 0 : if( nIndex < i_nW )
1666 : {
1667 0 : nIndex += pRunTable[nByte];
1668 : }
1669 : }
1670 0 : return nIndex < i_nW ? nIndex : i_nW;
1671 : }
1672 :
1673 : struct BitStreamState
1674 : {
1675 : sal_uInt8 mnBuffer;
1676 : sal_uInt32 mnNextBitPos;
1677 :
1678 0 : BitStreamState()
1679 : : mnBuffer( 0 )
1680 0 : , mnNextBitPos( 8 )
1681 : {
1682 0 : }
1683 :
1684 0 : const sal_uInt8& getByte() const { return mnBuffer; }
1685 0 : void flush() { mnNextBitPos = 8; mnBuffer = 0; }
1686 : };
1687 :
1688 0 : void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState )
1689 : {
1690 0 : while( i_nLength > io_rState.mnNextBitPos )
1691 : {
1692 0 : io_rState.mnBuffer |= static_cast<sal_uInt8>( i_nCode >> (i_nLength - io_rState.mnNextBitPos) );
1693 0 : i_nLength -= io_rState.mnNextBitPos;
1694 0 : writeBuffer( &io_rState.getByte(), 1 );
1695 0 : io_rState.flush();
1696 : }
1697 : OSL_ASSERT( i_nLength < 9 );
1698 : static const unsigned int msbmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1699 0 : io_rState.mnBuffer |= static_cast<sal_uInt8>( (i_nCode & msbmask[i_nLength]) << (io_rState.mnNextBitPos - i_nLength) );
1700 0 : io_rState.mnNextBitPos -= i_nLength;
1701 0 : if( io_rState.mnNextBitPos == 0 )
1702 : {
1703 0 : writeBuffer( &io_rState.getByte(), 1 );
1704 0 : io_rState.flush();
1705 : }
1706 0 : }
1707 :
1708 : struct PixelCode
1709 : {
1710 : sal_uInt32 mnEncodedPixels;
1711 : sal_uInt32 mnCodeBits;
1712 : sal_uInt32 mnCode;
1713 : };
1714 :
1715 : static const PixelCode WhitePixelCodes[] =
1716 : {
1717 : { 0, 8, 0x35 }, // 0011 0101
1718 : { 1, 6, 0x7 }, // 0001 11
1719 : { 2, 4, 0x7 }, // 0111
1720 : { 3, 4, 0x8 }, // 1000
1721 : { 4, 4, 0xB }, // 1011
1722 : { 5, 4, 0xC }, // 1100
1723 : { 6, 4, 0xE }, // 1110
1724 : { 7, 4, 0xF }, // 1111
1725 : { 8, 5, 0x13 }, // 1001 1
1726 : { 9, 5, 0x14 }, // 1010 0
1727 : { 10, 5, 0x7 }, // 0011 1
1728 : { 11, 5, 0x8 }, // 0100 0
1729 : { 12, 6, 0x8 }, // 0010 00
1730 : { 13, 6, 0x3 }, // 0000 11
1731 : { 14, 6, 0x34 }, // 1101 00
1732 : { 15, 6, 0x35 }, // 1101 01
1733 : { 16, 6, 0x2A }, // 1010 10
1734 : { 17, 6, 0x2B }, // 1010 11
1735 : { 18, 7, 0x27 }, // 0100 111
1736 : { 19, 7, 0xC }, // 0001 100
1737 : { 20, 7, 0x8 }, // 0001 000
1738 : { 21, 7, 0x17 }, // 0010 111
1739 : { 22, 7, 0x3 }, // 0000 011
1740 : { 23, 7, 0x4 }, // 0000 100
1741 : { 24, 7, 0x28 }, // 0101 000
1742 : { 25, 7, 0x2B }, // 0101 011
1743 : { 26, 7, 0x13 }, // 0010 011
1744 : { 27, 7, 0x24 }, // 0100 100
1745 : { 28, 7, 0x18 }, // 0011 000
1746 : { 29, 8, 0x2 }, // 0000 0010
1747 : { 30, 8, 0x3 }, // 0000 0011
1748 : { 31, 8, 0x1A }, // 0001 1010
1749 : { 32, 8, 0x1B }, // 0001 1011
1750 : { 33, 8, 0x12 }, // 0001 0010
1751 : { 34, 8, 0x13 }, // 0001 0011
1752 : { 35, 8, 0x14 }, // 0001 0100
1753 : { 36, 8, 0x15 }, // 0001 0101
1754 : { 37, 8, 0x16 }, // 0001 0110
1755 : { 38, 8, 0x17 }, // 0001 0111
1756 : { 39, 8, 0x28 }, // 0010 1000
1757 : { 40, 8, 0x29 }, // 0010 1001
1758 : { 41, 8, 0x2A }, // 0010 1010
1759 : { 42, 8, 0x2B }, // 0010 1011
1760 : { 43, 8, 0x2C }, // 0010 1100
1761 : { 44, 8, 0x2D }, // 0010 1101
1762 : { 45, 8, 0x4 }, // 0000 0100
1763 : { 46, 8, 0x5 }, // 0000 0101
1764 : { 47, 8, 0xA }, // 0000 1010
1765 : { 48, 8, 0xB }, // 0000 1011
1766 : { 49, 8, 0x52 }, // 0101 0010
1767 : { 50, 8, 0x53 }, // 0101 0011
1768 : { 51, 8, 0x54 }, // 0101 0100
1769 : { 52, 8, 0x55 }, // 0101 0101
1770 : { 53, 8, 0x24 }, // 0010 0100
1771 : { 54, 8, 0x25 }, // 0010 0101
1772 : { 55, 8, 0x58 }, // 0101 1000
1773 : { 56, 8, 0x59 }, // 0101 1001
1774 : { 57, 8, 0x5A }, // 0101 1010
1775 : { 58, 8, 0x5B }, // 0101 1011
1776 : { 59, 8, 0x4A }, // 0100 1010
1777 : { 60, 8, 0x4B }, // 0100 1011
1778 : { 61, 8, 0x32 }, // 0011 0010
1779 : { 62, 8, 0x33 }, // 0011 0011
1780 : { 63, 8, 0x34 }, // 0011 0100
1781 : { 64, 5, 0x1B }, // 1101 1
1782 : { 128, 5, 0x12 }, // 1001 0
1783 : { 192, 6, 0x17 }, // 0101 11
1784 : { 256, 7, 0x37 }, // 0110 111
1785 : { 320, 8, 0x36 }, // 0011 0110
1786 : { 384, 8, 0x37 }, // 0011 0111
1787 : { 448, 8, 0x64 }, // 0110 0100
1788 : { 512, 8, 0x65 }, // 0110 0101
1789 : { 576, 8, 0x68 }, // 0110 1000
1790 : { 640, 8, 0x67 }, // 0110 0111
1791 : { 704, 9, 0xCC }, // 0110 0110 0
1792 : { 768, 9, 0xCD }, // 0110 0110 1
1793 : { 832, 9, 0xD2 }, // 0110 1001 0
1794 : { 896, 9, 0xD3 }, // 0110 1001 1
1795 : { 960, 9, 0xD4 }, // 0110 1010 0
1796 : { 1024, 9, 0xD5 }, // 0110 1010 1
1797 : { 1088, 9, 0xD6 }, // 0110 1011 0
1798 : { 1152, 9, 0xD7 }, // 0110 1011 1
1799 : { 1216, 9, 0xD8 }, // 0110 1100 0
1800 : { 1280, 9, 0xD9 }, // 0110 1100 1
1801 : { 1344, 9, 0xDA }, // 0110 1101 0
1802 : { 1408, 9, 0xDB }, // 0110 1101 1
1803 : { 1472, 9, 0x98 }, // 0100 1100 0
1804 : { 1536, 9, 0x99 }, // 0100 1100 1
1805 : { 1600, 9, 0x9A }, // 0100 1101 0
1806 : { 1664, 6, 0x18 }, // 0110 00
1807 : { 1728, 9, 0x9B }, // 0100 1101 1
1808 : { 1792, 11, 0x8 }, // 0000 0001 000
1809 : { 1856, 11, 0xC }, // 0000 0001 100
1810 : { 1920, 11, 0xD }, // 0000 0001 101
1811 : { 1984, 12, 0x12 }, // 0000 0001 0010
1812 : { 2048, 12, 0x13 }, // 0000 0001 0011
1813 : { 2112, 12, 0x14 }, // 0000 0001 0100
1814 : { 2176, 12, 0x15 }, // 0000 0001 0101
1815 : { 2240, 12, 0x16 }, // 0000 0001 0110
1816 : { 2304, 12, 0x17 }, // 0000 0001 0111
1817 : { 2368, 12, 0x1C }, // 0000 0001 1100
1818 : { 2432, 12, 0x1D }, // 0000 0001 1101
1819 : { 2496, 12, 0x1E }, // 0000 0001 1110
1820 : { 2560, 12, 0x1F } // 0000 0001 1111
1821 : };
1822 :
1823 : static const PixelCode BlackPixelCodes[] =
1824 : {
1825 : { 0, 10, 0x37 }, // 0000 1101 11
1826 : { 1, 3, 0x2 }, // 010
1827 : { 2, 2, 0x3 }, // 11
1828 : { 3, 2, 0x2 }, // 10
1829 : { 4, 3, 0x3 }, // 011
1830 : { 5, 4, 0x3 }, // 0011
1831 : { 6, 4, 0x2 }, // 0010
1832 : { 7, 5, 0x3 }, // 0001 1
1833 : { 8, 6, 0x5 }, // 0001 01
1834 : { 9, 6, 0x4 }, // 0001 00
1835 : { 10, 7, 0x4 }, // 0000 100
1836 : { 11, 7, 0x5 }, // 0000 101
1837 : { 12, 7, 0x7 }, // 0000 111
1838 : { 13, 8, 0x4 }, // 0000 0100
1839 : { 14, 8, 0x7 }, // 0000 0111
1840 : { 15, 9, 0x18 }, // 0000 1100 0
1841 : { 16, 10, 0x17 }, // 0000 0101 11
1842 : { 17, 10, 0x18 }, // 0000 0110 00
1843 : { 18, 10, 0x8 }, // 0000 0010 00
1844 : { 19, 11, 0x67 }, // 0000 1100 111
1845 : { 20, 11, 0x68 }, // 0000 1101 000
1846 : { 21, 11, 0x6C }, // 0000 1101 100
1847 : { 22, 11, 0x37 }, // 0000 0110 111
1848 : { 23, 11, 0x28 }, // 0000 0101 000
1849 : { 24, 11, 0x17 }, // 0000 0010 111
1850 : { 25, 11, 0x18 }, // 0000 0011 000
1851 : { 26, 12, 0xCA }, // 0000 1100 1010
1852 : { 27, 12, 0xCB }, // 0000 1100 1011
1853 : { 28, 12, 0xCC }, // 0000 1100 1100
1854 : { 29, 12, 0xCD }, // 0000 1100 1101
1855 : { 30, 12, 0x68 }, // 0000 0110 1000
1856 : { 31, 12, 0x69 }, // 0000 0110 1001
1857 : { 32, 12, 0x6A }, // 0000 0110 1010
1858 : { 33, 12, 0x6B }, // 0000 0110 1011
1859 : { 34, 12, 0xD2 }, // 0000 1101 0010
1860 : { 35, 12, 0xD3 }, // 0000 1101 0011
1861 : { 36, 12, 0xD4 }, // 0000 1101 0100
1862 : { 37, 12, 0xD5 }, // 0000 1101 0101
1863 : { 38, 12, 0xD6 }, // 0000 1101 0110
1864 : { 39, 12, 0xD7 }, // 0000 1101 0111
1865 : { 40, 12, 0x6C }, // 0000 0110 1100
1866 : { 41, 12, 0x6D }, // 0000 0110 1101
1867 : { 42, 12, 0xDA }, // 0000 1101 1010
1868 : { 43, 12, 0xDB }, // 0000 1101 1011
1869 : { 44, 12, 0x54 }, // 0000 0101 0100
1870 : { 45, 12, 0x55 }, // 0000 0101 0101
1871 : { 46, 12, 0x56 }, // 0000 0101 0110
1872 : { 47, 12, 0x57 }, // 0000 0101 0111
1873 : { 48, 12, 0x64 }, // 0000 0110 0100
1874 : { 49, 12, 0x65 }, // 0000 0110 0101
1875 : { 50, 12, 0x52 }, // 0000 0101 0010
1876 : { 51, 12, 0x53 }, // 0000 0101 0011
1877 : { 52, 12, 0x24 }, // 0000 0010 0100
1878 : { 53, 12, 0x37 }, // 0000 0011 0111
1879 : { 54, 12, 0x38 }, // 0000 0011 1000
1880 : { 55, 12, 0x27 }, // 0000 0010 0111
1881 : { 56, 12, 0x28 }, // 0000 0010 1000
1882 : { 57, 12, 0x58 }, // 0000 0101 1000
1883 : { 58, 12, 0x59 }, // 0000 0101 1001
1884 : { 59, 12, 0x2B }, // 0000 0010 1011
1885 : { 60, 12, 0x2C }, // 0000 0010 1100
1886 : { 61, 12, 0x5A }, // 0000 0101 1010
1887 : { 62, 12, 0x66 }, // 0000 0110 0110
1888 : { 63, 12, 0x67 }, // 0000 0110 0111
1889 : { 64, 10, 0xF }, // 0000 0011 11
1890 : { 128, 12, 0xC8 }, // 0000 1100 1000
1891 : { 192, 12, 0xC9 }, // 0000 1100 1001
1892 : { 256, 12, 0x5B }, // 0000 0101 1011
1893 : { 320, 12, 0x33 }, // 0000 0011 0011
1894 : { 384, 12, 0x34 }, // 0000 0011 0100
1895 : { 448, 12, 0x35 }, // 0000 0011 0101
1896 : { 512, 13, 0x6C }, // 0000 0011 0110 0
1897 : { 576, 13, 0x6D }, // 0000 0011 0110 1
1898 : { 640, 13, 0x4A }, // 0000 0010 0101 0
1899 : { 704, 13, 0x4B }, // 0000 0010 0101 1
1900 : { 768, 13, 0x4C }, // 0000 0010 0110 0
1901 : { 832, 13, 0x4D }, // 0000 0010 0110 1
1902 : { 896, 13, 0x72 }, // 0000 0011 1001 0
1903 : { 960, 13, 0x73 }, // 0000 0011 1001 1
1904 : { 1024, 13, 0x74 }, // 0000 0011 1010 0
1905 : { 1088, 13, 0x75 }, // 0000 0011 1010 1
1906 : { 1152, 13, 0x76 }, // 0000 0011 1011 0
1907 : { 1216, 13, 0x77 }, // 0000 0011 1011 1
1908 : { 1280, 13, 0x52 }, // 0000 0010 1001 0
1909 : { 1344, 13, 0x53 }, // 0000 0010 1001 1
1910 : { 1408, 13, 0x54 }, // 0000 0010 1010 0
1911 : { 1472, 13, 0x55 }, // 0000 0010 1010 1
1912 : { 1536, 13, 0x5A }, // 0000 0010 1101 0
1913 : { 1600, 13, 0x5B }, // 0000 0010 1101 1
1914 : { 1664, 13, 0x64 }, // 0000 0011 0010 0
1915 : { 1728, 13, 0x65 }, // 0000 0011 0010 1
1916 : { 1792, 11, 0x8 }, // 0000 0001 000
1917 : { 1856, 11, 0xC }, // 0000 0001 100
1918 : { 1920, 11, 0xD }, // 0000 0001 101
1919 : { 1984, 12, 0x12 }, // 0000 0001 0010
1920 : { 2048, 12, 0x13 }, // 0000 0001 0011
1921 : { 2112, 12, 0x14 }, // 0000 0001 0100
1922 : { 2176, 12, 0x15 }, // 0000 0001 0101
1923 : { 2240, 12, 0x16 }, // 0000 0001 0110
1924 : { 2304, 12, 0x17 }, // 0000 0001 0111
1925 : { 2368, 12, 0x1C }, // 0000 0001 1100
1926 : { 2432, 12, 0x1D }, // 0000 0001 1101
1927 : { 2496, 12, 0x1E }, // 0000 0001 1110
1928 : { 2560, 12, 0x1F } // 0000 0001 1111
1929 : };
1930 :
1931 0 : void PDFWriterImpl::putG4Span( long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState )
1932 : {
1933 0 : const PixelCode* pTable = i_bWhitePixel ? WhitePixelCodes : BlackPixelCodes;
1934 : // maximum encoded span is 2560 consecutive pixels
1935 0 : while( i_nSpan > 2623 )
1936 : {
1937 : // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1938 0 : putG4Bits( pTable[103].mnCodeBits, pTable[103].mnCode, io_rState );
1939 0 : i_nSpan -= pTable[103].mnEncodedPixels;
1940 : }
1941 : // write multiples of 64 pixels up to 2560
1942 0 : if( i_nSpan > 63 )
1943 : {
1944 0 : sal_uInt32 nTabIndex = 63 + (i_nSpan >> 6);
1945 : OSL_ASSERT( pTable[nTabIndex].mnEncodedPixels == static_cast<sal_uInt32>(64*(i_nSpan >> 6)) );
1946 0 : putG4Bits( pTable[nTabIndex].mnCodeBits, pTable[nTabIndex].mnCode, io_rState );
1947 0 : i_nSpan -= pTable[nTabIndex].mnEncodedPixels;
1948 : }
1949 0 : putG4Bits( pTable[i_nSpan].mnCodeBits, pTable[i_nSpan].mnCode, io_rState );
1950 0 : }
1951 :
1952 0 : void PDFWriterImpl::writeG4Stream( BitmapReadAccess* i_pBitmap )
1953 : {
1954 0 : long nW = i_pBitmap->Width();
1955 0 : long nH = i_pBitmap->Height();
1956 0 : if( nW <= 0 || nH <= 0 )
1957 0 : return;
1958 0 : if( i_pBitmap->GetBitCount() != 1 )
1959 0 : return;
1960 :
1961 0 : BitStreamState aBitState;
1962 :
1963 : // the first reference line is virtual and completely empty
1964 0 : const Scanline pFirstRefLine = (Scanline)rtl_allocateZeroMemory( nW/8 + 1 );
1965 0 : Scanline pRefLine = pFirstRefLine;
1966 0 : for( long nY = 0; nY < nH; nY++ )
1967 : {
1968 0 : const Scanline pCurLine = i_pBitmap->GetScanline( nY );
1969 0 : long nLineIndex = 0;
1970 0 : bool bRunSet = (*pCurLine & 0x80) ? true : false;
1971 0 : bool bRefSet = (*pRefLine & 0x80) ? true : false;
1972 0 : long nRunIndex1 = bRunSet ? 0 : findBitRun( pCurLine, 0, nW, bRunSet );
1973 0 : long nRefIndex1 = bRefSet ? 0 : findBitRun( pRefLine, 0, nW, bRefSet );
1974 0 : for( ; nLineIndex < nW; )
1975 : {
1976 0 : long nRefIndex2 = findBitRun( pRefLine, nRefIndex1, nW, isSet( pRefLine, nRefIndex1 ) );
1977 0 : if( nRefIndex2 >= nRunIndex1 )
1978 : {
1979 0 : long nDiff = nRefIndex1 - nRunIndex1;
1980 0 : if( -3 <= nDiff && nDiff <= 3 )
1981 : { // vertical coding
1982 : static const struct
1983 : {
1984 : sal_uInt32 mnCodeBits;
1985 : sal_uInt32 mnCode;
1986 : } VerticalCodes[7] = {
1987 : { 7, 0x03 }, // 0000 011
1988 : { 6, 0x03 }, // 0000 11
1989 : { 3, 0x03 }, // 011
1990 : { 1, 0x1 }, // 1
1991 : { 3, 0x2 }, // 010
1992 : { 6, 0x02 }, // 0000 10
1993 : { 7, 0x02 } // 0000 010
1994 : };
1995 : // convert to index
1996 0 : nDiff += 3;
1997 :
1998 : // emit diff code
1999 0 : putG4Bits( VerticalCodes[nDiff].mnCodeBits, VerticalCodes[nDiff].mnCode, aBitState );
2000 0 : nLineIndex = nRunIndex1;
2001 : }
2002 : else
2003 : { // difference too large, horizontal coding
2004 : // emit horz code 001
2005 0 : putG4Bits( 3, 0x1, aBitState );
2006 0 : long nRunIndex2 = findBitRun( pCurLine, nRunIndex1, nW, isSet( pCurLine, nRunIndex1 ) );
2007 0 : bool bWhiteFirst = ( nLineIndex + nRunIndex1 == 0 || ! isSet( pCurLine, nLineIndex ) );
2008 0 : putG4Span( nRunIndex1 - nLineIndex, bWhiteFirst, aBitState );
2009 0 : putG4Span( nRunIndex2 - nRunIndex1, ! bWhiteFirst, aBitState );
2010 0 : nLineIndex = nRunIndex2;
2011 : }
2012 : }
2013 : else
2014 : { // emit pass code 0001
2015 0 : putG4Bits( 4, 0x1, aBitState );
2016 0 : nLineIndex = nRefIndex2;
2017 : }
2018 0 : if( nLineIndex < nW )
2019 : {
2020 0 : bool bSet = isSet( pCurLine, nLineIndex );
2021 0 : nRunIndex1 = findBitRun( pCurLine, nLineIndex, nW, bSet );
2022 0 : nRefIndex1 = findBitRun( pRefLine, nLineIndex, nW, ! bSet );
2023 0 : nRefIndex1 = findBitRun( pRefLine, nRefIndex1, nW, bSet );
2024 : }
2025 : }
2026 :
2027 : // the current line is the reference for the next line
2028 0 : pRefLine = pCurLine;
2029 : }
2030 : // terminate strip with EOFB
2031 0 : putG4Bits( 12, 1, aBitState );
2032 0 : putG4Bits( 12, 1, aBitState );
2033 0 : if( aBitState.mnNextBitPos != 8 )
2034 : {
2035 0 : writeBuffer( &aBitState.getByte(), 1 );
2036 0 : aBitState.flush();
2037 : }
2038 :
2039 0 : rtl_freeMemory( pFirstRefLine );
2040 : }
2041 :
2042 0 : static bool lcl_canUsePDFAxialShading(const Gradient& rGradient) {
2043 0 : switch (rGradient.GetStyle())
2044 : {
2045 : case GradientStyle_LINEAR:
2046 : case GradientStyle_AXIAL:
2047 0 : break;
2048 : default:
2049 0 : return false;
2050 : }
2051 :
2052 : // TODO: handle step count
2053 0 : if (rGradient.GetSteps() > 0)
2054 0 : return false;
2055 :
2056 0 : return true;
2057 1233 : }
2058 :
2059 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|