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