Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / gdi / pdfwriter_impl2.cxx
blob4dbc13bc60793bc888b78e2cfa76a78281d44829
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "pdfwriter_impl.hxx"
22 #include <vcl/pdfextoutdevdata.hxx>
23 #include <vcl/virdev.hxx>
24 #include <vcl/gdimtf.hxx>
25 #include <vcl/metaact.hxx>
26 #include <vcl/bitmapaccess.hxx>
27 #include <vcl/graph.hxx>
29 #include <unotools/streamwrap.hxx>
31 #include <tools/helpers.hxx>
32 #include <tools/fract.hxx>
33 #include <tools/stream.hxx>
35 #include <comphelper/fileformat.h>
36 #include <comphelper/hash.hxx>
37 #include <comphelper/processfactory.hxx>
39 #include <com/sun/star/beans/PropertyValue.hpp>
40 #include <com/sun/star/io/XSeekable.hpp>
41 #include <com/sun/star/graphic/GraphicProvider.hpp>
42 #include <com/sun/star/graphic/XGraphicProvider.hpp>
43 #include <com/sun/star/beans/XMaterialHolder.hpp>
45 #include <cppuhelper/implbase.hxx>
47 #include <sal/log.hxx>
48 #include <memory>
50 using namespace vcl;
51 using namespace com::sun::star;
52 using namespace com::sun::star::uno;
53 using namespace com::sun::star::beans;
55 static bool lcl_canUsePDFAxialShading(const Gradient& rGradient);
57 void PDFWriterImpl::implWriteGradient( const tools::PolyPolygon& i_rPolyPoly, const Gradient& i_rGradient,
58 VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
60 GDIMetaFile aTmpMtf;
62 i_pDummyVDev->AddGradientActions( i_rPolyPoly.GetBoundRect(), i_rGradient, aTmpMtf );
64 m_rOuterFace.Push();
65 m_rOuterFace.IntersectClipRegion( i_rPolyPoly.getB2DPolyPolygon() );
66 playMetafile( aTmpMtf, nullptr, i_rContext, i_pDummyVDev );
67 m_rOuterFace.Pop();
70 void PDFWriterImpl::implWriteBitmapEx( const Point& i_rPoint, const Size& i_rSize, const BitmapEx& i_rBitmapEx, const Graphic& i_Graphic,
71 VirtualDevice const * i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
73 if ( i_rBitmapEx.IsEmpty() || !i_rSize.Width() || !i_rSize.Height() )
74 return;
76 BitmapEx aBitmapEx( i_rBitmapEx );
77 Point aPoint( i_rPoint );
78 Size aSize( i_rSize );
80 // #i19065# Negative sizes have mirror semantics on
81 // OutputDevice. BitmapEx and co. have no idea about that, so
82 // perform that _before_ doing anything with aBitmapEx.
83 BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE);
84 if( aSize.Width() < 0 )
86 aSize.setWidth( aSize.Width() * -1 );
87 aPoint.AdjustX( -(aSize.Width()) );
88 nMirrorFlags |= BmpMirrorFlags::Horizontal;
90 if( aSize.Height() < 0 )
92 aSize.setHeight( aSize.Height() * -1 );
93 aPoint.AdjustY( -(aSize.Height()) );
94 nMirrorFlags |= BmpMirrorFlags::Vertical;
97 if( nMirrorFlags != BmpMirrorFlags::NONE )
99 aBitmapEx.Mirror( nMirrorFlags );
102 bool bIsJpeg = false, bIsPng = false;
103 if( i_Graphic.GetType() != GraphicType::NONE && i_Graphic.GetBitmapEx() == aBitmapEx )
105 GfxLinkType eType = i_Graphic.GetGfxLink().GetType();
106 bIsJpeg = (eType == GfxLinkType::NativeJpg);
107 bIsPng = (eType == GfxLinkType::NativePng);
110 if( i_rContext.m_nMaxImageResolution > 50 )
112 // do downsampling if necessary
113 const Size aDstSizeTwip( i_pDummyVDev->PixelToLogic(i_pDummyVDev->LogicToPixel(aSize), MapMode(MapUnit::MapTwip)) );
114 const Size aBmpSize( aBitmapEx.GetSizePixel() );
115 const double fBmpPixelX = aBmpSize.Width();
116 const double fBmpPixelY = aBmpSize.Height();
117 const double fMaxPixelX = aDstSizeTwip.Width() * i_rContext.m_nMaxImageResolution / 1440.0;
118 const double fMaxPixelY = aDstSizeTwip.Height() * i_rContext.m_nMaxImageResolution / 1440.0;
120 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
121 if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) ||
122 ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) &&
123 ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) )
125 // do scaling
126 Size aNewBmpSize;
127 const double fBmpWH = fBmpPixelX / fBmpPixelY;
128 const double fMaxWH = fMaxPixelX / fMaxPixelY;
130 if( fBmpWH < fMaxWH )
132 aNewBmpSize.setWidth( FRound( fMaxPixelY * fBmpWH ) );
133 aNewBmpSize.setHeight( FRound( fMaxPixelY ) );
135 else if( fBmpWH > 0.0 )
137 aNewBmpSize.setWidth( FRound( fMaxPixelX ) );
138 aNewBmpSize.setHeight( FRound( fMaxPixelX / fBmpWH) );
141 if( aNewBmpSize.Width() && aNewBmpSize.Height() )
143 // #i121233# Use best quality for PDF exports
144 aBitmapEx.Scale( aNewBmpSize, BmpScaleFlag::BestQuality );
146 else
148 aBitmapEx.SetEmpty();
153 const Size aSizePixel( aBitmapEx.GetSizePixel() );
154 if ( aSizePixel.Width() && aSizePixel.Height() )
156 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
158 BmpConversion eConv = BmpConversion::N8BitGreys;
159 int nDepth = aBitmapEx.GetBitmap().GetBitCount();
160 if( nDepth <= 4 )
161 eConv = BmpConversion::N4BitGreys;
162 if( nDepth > 1 )
163 aBitmapEx.Convert( eConv );
165 bool bUseJPGCompression = !i_rContext.m_bOnlyLosslessCompression;
166 if ( bIsPng || ( aSizePixel.Width() < 32 ) || ( aSizePixel.Height() < 32 ) )
167 bUseJPGCompression = false;
169 SvMemoryStream aStrm;
170 Bitmap aMask;
172 bool bTrueColorJPG = true;
173 if ( bUseJPGCompression )
176 sal_uInt32 nZippedFileSize = 0; // sj: we will calculate the filesize of a zipped bitmap
177 if ( !bIsJpeg ) // to determine if jpeg compression is useful
179 SvMemoryStream aTemp;
180 aTemp.SetCompressMode( aTemp.GetCompressMode() | SvStreamCompressFlags::ZBITMAP );
181 aTemp.SetVersion( SOFFICE_FILEFORMAT_40 ); // sj: up from version 40 our bitmap stream operator
182 WriteDIBBitmapEx(aBitmapEx, aTemp); // is capable of zlib stream compression
183 nZippedFileSize = aTemp.TellEnd();
185 if ( aBitmapEx.IsTransparent() )
187 if ( aBitmapEx.IsAlpha() )
188 aMask = aBitmapEx.GetAlpha().GetBitmap();
189 else
190 aMask = aBitmapEx.GetMask();
192 Graphic aGraphic( aBitmapEx.GetBitmap() );
194 Sequence< PropertyValue > aFilterData( 2 );
195 aFilterData[ 0 ].Name = "Quality";
196 aFilterData[ 0 ].Value <<= sal_Int32(i_rContext.m_nJPEGQuality);
197 aFilterData[ 1 ].Name = "ColorMode";
198 aFilterData[ 1 ].Value <<= sal_Int32(0);
202 uno::Reference < io::XStream > xStream = new utl::OStreamWrapper( aStrm );
203 uno::Reference< io::XSeekable > xSeekable( xStream, UNO_QUERY_THROW );
204 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
205 uno::Reference< graphic::XGraphicProvider > xGraphicProvider( graphic::GraphicProvider::create(xContext) );
206 uno::Reference< graphic::XGraphic > xGraphic( aGraphic.GetXGraphic() );
207 uno::Reference < io::XOutputStream > xOut( xStream->getOutputStream() );
208 uno::Sequence< beans::PropertyValue > aOutMediaProperties( 3 );
209 aOutMediaProperties[0].Name = "OutputStream";
210 aOutMediaProperties[0].Value <<= xOut;
211 aOutMediaProperties[1].Name = "MimeType";
212 aOutMediaProperties[1].Value <<= OUString("image/jpeg");
213 aOutMediaProperties[2].Name = "FilterData";
214 aOutMediaProperties[2].Value <<= aFilterData;
215 xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties );
216 xOut->flush();
217 if ( !bIsJpeg && xSeekable->getLength() > nZippedFileSize )
219 bUseJPGCompression = false;
221 else
223 aStrm.Seek( STREAM_SEEK_TO_END );
225 xSeekable->seek( 0 );
226 Sequence< PropertyValue > aArgs( 1 );
227 aArgs[ 0 ].Name = "InputStream";
228 aArgs[ 0 ].Value <<= xStream;
229 uno::Reference< XPropertySet > xPropSet( xGraphicProvider->queryGraphicDescriptor( aArgs ) );
230 if ( xPropSet.is() )
232 sal_Int16 nBitsPerPixel = 24;
233 if ( xPropSet->getPropertyValue("BitsPerPixel") >>= nBitsPerPixel )
235 bTrueColorJPG = nBitsPerPixel != 8;
240 catch( uno::Exception& )
242 bUseJPGCompression = false;
245 if ( bUseJPGCompression )
246 m_rOuterFace.DrawJPGBitmap( aStrm, bTrueColorJPG, aSizePixel, tools::Rectangle( aPoint, aSize ), aMask, i_Graphic );
247 else if ( aBitmapEx.IsTransparent() )
248 m_rOuterFace.DrawBitmapEx( aPoint, aSize, aBitmapEx );
249 else
250 m_rOuterFace.DrawBitmap( aPoint, aSize, aBitmapEx.GetBitmap(), i_Graphic );
255 void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevData* i_pOutDevData, const vcl::PDFWriter::PlayMetafileContext& i_rContext, VirtualDevice* pDummyVDev )
257 bool bAssertionFired( false );
259 ScopedVclPtr<VirtualDevice> xPrivateDevice;
260 if( ! pDummyVDev )
262 xPrivateDevice.disposeAndReset(VclPtr<VirtualDevice>::Create());
263 pDummyVDev = xPrivateDevice.get();
264 pDummyVDev->EnableOutput( false );
265 pDummyVDev->SetMapMode( i_rMtf.GetPrefMapMode() );
267 const GDIMetaFile& aMtf( i_rMtf );
269 for( sal_uInt32 i = 0, nCount = aMtf.GetActionSize(); i < nCount; )
271 if ( !i_pOutDevData || !i_pOutDevData->PlaySyncPageAct( m_rOuterFace, i, aMtf ) )
273 const MetaAction* pAction = aMtf.GetAction( i );
274 const MetaActionType nType = pAction->GetType();
276 switch( nType )
278 case MetaActionType::PIXEL:
280 const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
281 m_rOuterFace.DrawPixel( pA->GetPoint(), pA->GetColor() );
283 break;
285 case MetaActionType::POINT:
287 const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
288 m_rOuterFace.DrawPixel( pA->GetPoint() );
290 break;
292 case MetaActionType::LINE:
294 const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
295 if ( pA->GetLineInfo().IsDefault() )
296 m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint() );
297 else
298 m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint(), pA->GetLineInfo() );
300 break;
302 case MetaActionType::RECT:
304 const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
305 m_rOuterFace.DrawRect( pA->GetRect() );
307 break;
309 case MetaActionType::ROUNDRECT:
311 const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
312 m_rOuterFace.DrawRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
314 break;
316 case MetaActionType::ELLIPSE:
318 const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
319 m_rOuterFace.DrawEllipse( pA->GetRect() );
321 break;
323 case MetaActionType::ARC:
325 const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
326 m_rOuterFace.DrawArc( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
328 break;
330 case MetaActionType::PIE:
332 const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
333 m_rOuterFace.DrawPie( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
335 break;
337 case MetaActionType::CHORD:
339 const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
340 m_rOuterFace.DrawChord( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
342 break;
344 case MetaActionType::POLYGON:
346 const MetaPolygonAction* pA = static_cast<const MetaPolygonAction*>(pAction);
347 m_rOuterFace.DrawPolygon( pA->GetPolygon() );
349 break;
351 case MetaActionType::POLYLINE:
353 const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
354 if ( pA->GetLineInfo().IsDefault() )
355 m_rOuterFace.DrawPolyLine( pA->GetPolygon() );
356 else
357 m_rOuterFace.DrawPolyLine( pA->GetPolygon(), pA->GetLineInfo() );
359 break;
361 case MetaActionType::POLYPOLYGON:
363 const MetaPolyPolygonAction* pA = static_cast<const MetaPolyPolygonAction*>(pAction);
364 m_rOuterFace.DrawPolyPolygon( pA->GetPolyPolygon() );
366 break;
368 case MetaActionType::GRADIENT:
370 const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
371 const Gradient& rGradient = pA->GetGradient();
372 if (lcl_canUsePDFAxialShading(rGradient))
374 m_rOuterFace.DrawGradient( pA->GetRect(), rGradient );
376 else
378 const tools::PolyPolygon aPolyPoly( pA->GetRect() );
379 implWriteGradient( aPolyPoly, rGradient, pDummyVDev, i_rContext );
382 break;
384 case MetaActionType::GRADIENTEX:
386 const MetaGradientExAction* pA = static_cast<const MetaGradientExAction*>(pAction);
387 const Gradient& rGradient = pA->GetGradient();
389 if (lcl_canUsePDFAxialShading(rGradient))
390 m_rOuterFace.DrawGradient( pA->GetPolyPolygon(), rGradient );
391 else
392 implWriteGradient( pA->GetPolyPolygon(), rGradient, pDummyVDev, i_rContext );
394 break;
396 case MetaActionType::HATCH:
398 const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pAction);
399 m_rOuterFace.DrawHatch( pA->GetPolyPolygon(), pA->GetHatch() );
401 break;
403 case MetaActionType::Transparent:
405 const MetaTransparentAction* pA = static_cast<const MetaTransparentAction*>(pAction);
406 m_rOuterFace.DrawTransparent( pA->GetPolyPolygon(), pA->GetTransparence() );
408 break;
410 case MetaActionType::FLOATTRANSPARENT:
412 const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
414 GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
415 const Point& rPos = pA->GetPoint();
416 const Size& rSize= pA->GetSize();
417 const Gradient& rTransparenceGradient = pA->GetGradient();
419 // special case constant alpha value
420 if( rTransparenceGradient.GetStartColor() == rTransparenceGradient.GetEndColor() )
422 const Color aTransCol( rTransparenceGradient.GetStartColor() );
423 const sal_uInt16 nTransPercent = aTransCol.GetLuminance() * 100 / 255;
424 m_rOuterFace.BeginTransparencyGroup();
425 playMetafile( aTmpMtf, nullptr, i_rContext, pDummyVDev );
426 m_rOuterFace.EndTransparencyGroup( tools::Rectangle( rPos, rSize ), nTransPercent );
428 else
430 const Size aDstSizeTwip( pDummyVDev->PixelToLogic(pDummyVDev->LogicToPixel(rSize), MapMode(MapUnit::MapTwip)) );
432 // i#115962# Always use at least 300 DPI for bitmap conversion of transparence gradients,
433 // else the quality is not acceptable (see bugdoc as example)
434 sal_Int32 nMaxBmpDPI(300);
436 if( i_rContext.m_nMaxImageResolution > 50 )
438 if ( nMaxBmpDPI > i_rContext.m_nMaxImageResolution )
439 nMaxBmpDPI = i_rContext.m_nMaxImageResolution;
441 const sal_Int32 nPixelX = static_cast<sal_Int32>(static_cast<double>(aDstSizeTwip.Width()) * static_cast<double>(nMaxBmpDPI) / 1440.0);
442 const sal_Int32 nPixelY = static_cast<sal_Int32>(static_cast<double>(aDstSizeTwip.Height()) * static_cast<double>(nMaxBmpDPI) / 1440.0);
443 if ( nPixelX && nPixelY )
445 Size aDstSizePixel( nPixelX, nPixelY );
446 ScopedVclPtrInstance<VirtualDevice> xVDev;
447 if( xVDev->SetOutputSizePixel( aDstSizePixel ) )
449 Bitmap aPaint, aMask;
450 AlphaMask aAlpha;
451 Point aPoint;
453 MapMode aMapMode( pDummyVDev->GetMapMode() );
454 aMapMode.SetOrigin( aPoint );
455 xVDev->SetMapMode( aMapMode );
456 Size aDstSize( xVDev->PixelToLogic( aDstSizePixel ) );
458 Point aMtfOrigin( aTmpMtf.GetPrefMapMode().GetOrigin() );
459 if ( aMtfOrigin.X() || aMtfOrigin.Y() )
460 aTmpMtf.Move( -aMtfOrigin.X(), -aMtfOrigin.Y() );
461 double fScaleX = static_cast<double>(aDstSize.Width()) / static_cast<double>(aTmpMtf.GetPrefSize().Width());
462 double fScaleY = static_cast<double>(aDstSize.Height()) / static_cast<double>(aTmpMtf.GetPrefSize().Height());
463 if( fScaleX != 1.0 || fScaleY != 1.0 )
464 aTmpMtf.Scale( fScaleX, fScaleY );
465 aTmpMtf.SetPrefMapMode( aMapMode );
467 // create paint bitmap
468 aTmpMtf.WindStart();
469 aTmpMtf.Play( xVDev.get(), aPoint, aDstSize );
470 aTmpMtf.WindStart();
472 xVDev->EnableMapMode( false );
473 aPaint = xVDev->GetBitmap( aPoint, aDstSizePixel );
474 xVDev->EnableMapMode();
476 // create mask bitmap
477 xVDev->SetLineColor( COL_BLACK );
478 xVDev->SetFillColor( COL_BLACK );
479 xVDev->DrawRect( tools::Rectangle( aPoint, aDstSize ) );
480 xVDev->SetDrawMode( DrawModeFlags::WhiteLine | DrawModeFlags::WhiteFill | DrawModeFlags::WhiteText |
481 DrawModeFlags::WhiteBitmap | DrawModeFlags::WhiteGradient );
482 aTmpMtf.WindStart();
483 aTmpMtf.Play( xVDev.get(), aPoint, aDstSize );
484 aTmpMtf.WindStart();
485 xVDev->EnableMapMode( false );
486 aMask = xVDev->GetBitmap( aPoint, aDstSizePixel );
487 xVDev->EnableMapMode();
489 // create alpha mask from gradient
490 xVDev->SetDrawMode( DrawModeFlags::GrayGradient );
491 xVDev->DrawGradient( tools::Rectangle( aPoint, aDstSize ), rTransparenceGradient );
492 xVDev->SetDrawMode( DrawModeFlags::Default );
493 xVDev->EnableMapMode( false );
494 xVDev->DrawMask( aPoint, aDstSizePixel, aMask, COL_WHITE );
495 aAlpha = xVDev->GetBitmap( aPoint, aDstSizePixel );
497 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
498 implWriteBitmapEx( rPos, rSize, BitmapEx( aPaint, aAlpha ), aGraphic, pDummyVDev, i_rContext );
503 break;
505 case MetaActionType::EPS:
507 const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pAction);
508 const GDIMetaFile& aSubstitute( pA->GetSubstitute() );
510 m_rOuterFace.Push();
511 pDummyVDev->Push();
513 MapMode aMapMode( aSubstitute.GetPrefMapMode() );
514 Size aOutSize( OutputDevice::LogicToLogic( pA->GetSize(), pDummyVDev->GetMapMode(), aMapMode ) );
515 aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) );
516 aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) );
517 aMapMode.SetOrigin( OutputDevice::LogicToLogic( pA->GetPoint(), pDummyVDev->GetMapMode(), aMapMode ) );
519 m_rOuterFace.SetMapMode( aMapMode );
520 pDummyVDev->SetMapMode( aMapMode );
521 playMetafile( aSubstitute, nullptr, i_rContext, pDummyVDev );
522 pDummyVDev->Pop();
523 m_rOuterFace.Pop();
525 break;
527 case MetaActionType::COMMENT:
528 if( ! i_rContext.m_bTransparenciesWereRemoved )
530 const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
532 if( pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
534 const MetaGradientExAction* pGradAction = nullptr;
535 bool bDone = false;
537 while( !bDone && ( ++i < nCount ) )
539 pAction = aMtf.GetAction( i );
541 if( pAction->GetType() == MetaActionType::GRADIENTEX )
542 pGradAction = static_cast<const MetaGradientExAction*>(pAction);
543 else if( ( pAction->GetType() == MetaActionType::COMMENT ) &&
544 ( static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END")) )
546 bDone = true;
550 if( pGradAction )
552 if (lcl_canUsePDFAxialShading(pGradAction->GetGradient()))
554 m_rOuterFace.DrawGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient() );
556 else
558 implWriteGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), pDummyVDev, i_rContext );
562 else
564 const sal_uInt8* pData = pA->GetData();
565 if ( pData )
567 SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pData), pA->GetDataSize(), StreamMode::READ );
568 bool bSkipSequence = false;
569 OString sSeqEnd;
571 if( pA->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
573 sSeqEnd = OString("XPATHSTROKE_SEQ_END");
574 SvtGraphicStroke aStroke;
575 ReadSvtGraphicStroke( aMemStm, aStroke );
577 tools::Polygon aPath;
578 aStroke.getPath( aPath );
580 tools::PolyPolygon aStartArrow;
581 tools::PolyPolygon aEndArrow;
582 double fTransparency( aStroke.getTransparency() );
583 double fStrokeWidth( aStroke.getStrokeWidth() );
584 SvtGraphicStroke::DashArray aDashArray;
586 aStroke.getStartArrow( aStartArrow );
587 aStroke.getEndArrow( aEndArrow );
588 aStroke.getDashArray( aDashArray );
590 bSkipSequence = true;
591 if ( aStartArrow.Count() || aEndArrow.Count() )
592 bSkipSequence = false;
593 if ( !aDashArray.empty() && ( fStrokeWidth != 0.0 ) && ( fTransparency == 0.0 ) )
594 bSkipSequence = false;
595 if ( bSkipSequence )
597 PDFWriter::ExtLineInfo aInfo;
598 aInfo.m_fLineWidth = fStrokeWidth;
599 aInfo.m_fTransparency = fTransparency;
600 aInfo.m_fMiterLimit = aStroke.getMiterLimit();
601 switch( aStroke.getCapType() )
603 default:
604 case SvtGraphicStroke::capButt: aInfo.m_eCap = PDFWriter::capButt;break;
605 case SvtGraphicStroke::capRound: aInfo.m_eCap = PDFWriter::capRound;break;
606 case SvtGraphicStroke::capSquare: aInfo.m_eCap = PDFWriter::capSquare;break;
608 switch( aStroke.getJoinType() )
610 default:
611 case SvtGraphicStroke::joinMiter: aInfo.m_eJoin = PDFWriter::joinMiter;break;
612 case SvtGraphicStroke::joinRound: aInfo.m_eJoin = PDFWriter::joinRound;break;
613 case SvtGraphicStroke::joinBevel: aInfo.m_eJoin = PDFWriter::joinBevel;break;
614 case SvtGraphicStroke::joinNone:
615 aInfo.m_eJoin = PDFWriter::joinMiter;
616 aInfo.m_fMiterLimit = 0.0;
617 break;
619 aInfo.m_aDashArray = aDashArray;
621 if(SvtGraphicStroke::joinNone == aStroke.getJoinType()
622 && fStrokeWidth > 0.0)
624 // emulate no edge rounding by handling single edges
625 const sal_uInt16 nPoints(aPath.GetSize());
626 const bool bCurve(aPath.HasFlags());
628 for(sal_uInt16 a(0); a + 1 < nPoints; a++)
630 if(bCurve
631 && PolyFlags::Normal != aPath.GetFlags(a + 1)
632 && a + 2 < nPoints
633 && PolyFlags::Normal != aPath.GetFlags(a + 2)
634 && a + 3 < nPoints)
636 const tools::Polygon aSnippet(4,
637 aPath.GetConstPointAry() + a,
638 aPath.GetConstFlagAry() + a);
639 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
640 a += 2;
642 else
644 const tools::Polygon aSnippet(2,
645 aPath.GetConstPointAry() + a);
646 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
650 else
652 m_rOuterFace.DrawPolyLine( aPath, aInfo );
656 else if ( pA->GetComment() == "XPATHFILL_SEQ_BEGIN" )
658 sSeqEnd = OString("XPATHFILL_SEQ_END");
659 SvtGraphicFill aFill;
660 ReadSvtGraphicFill( aMemStm, aFill );
662 if ( ( aFill.getFillType() == SvtGraphicFill::fillSolid ) && ( aFill.getFillRule() == SvtGraphicFill::fillEvenOdd ) )
664 double fTransparency = aFill.getTransparency();
665 if ( fTransparency == 0.0 )
667 tools::PolyPolygon aPath;
668 aFill.getPath( aPath );
670 bSkipSequence = true;
671 m_rOuterFace.DrawPolyPolygon( aPath );
673 else if ( fTransparency == 1.0 )
674 bSkipSequence = true;
676 /* #i81548# removing optimization for fill textures, because most of the texture settings are not
677 exported properly. In OpenOffice 3.1 the drawing layer will support graphic primitives, then it
678 will not be a problem to optimize the filltexture export. But for wysiwyg is more important than
679 filesize.
680 else if( aFill.getFillType() == SvtGraphicFill::fillTexture && aFill.isTiling() )
682 sal_Int32 nPattern = mnCachePatternId;
683 Graphic aPatternGraphic;
684 aFill.getGraphic( aPatternGraphic );
685 bool bUseCache = false;
686 SvtGraphicFill::Transform aPatTransform;
687 aFill.getTransform( aPatTransform );
689 if( mnCachePatternId >= 0 )
691 SvtGraphicFill::Transform aCacheTransform;
692 maCacheFill.getTransform( aCacheTransform );
693 if( aCacheTransform.matrix[0] == aPatTransform.matrix[0] &&
694 aCacheTransform.matrix[1] == aPatTransform.matrix[1] &&
695 aCacheTransform.matrix[2] == aPatTransform.matrix[2] &&
696 aCacheTransform.matrix[3] == aPatTransform.matrix[3] &&
697 aCacheTransform.matrix[4] == aPatTransform.matrix[4] &&
698 aCacheTransform.matrix[5] == aPatTransform.matrix[5]
701 Graphic aCacheGraphic;
702 maCacheFill.getGraphic( aCacheGraphic );
703 if( aCacheGraphic == aPatternGraphic )
704 bUseCache = true;
708 if( ! bUseCache )
711 // paint graphic to metafile
712 GDIMetaFile aPattern;
713 pDummyVDev->SetConnectMetaFile( &aPattern );
714 pDummyVDev->Push();
715 pDummyVDev->SetMapMode( aPatternGraphic.GetPrefMapMode() );
717 aPatternGraphic.Draw( &rDummyVDev, Point( 0, 0 ) );
718 pDummyVDev->Pop();
719 pDummyVDev->SetConnectMetaFile( NULL );
720 aPattern.WindStart();
722 MapMode aPatternMapMode( aPatternGraphic.GetPrefMapMode() );
723 // prepare pattern from metafile
724 Size aPrefSize( aPatternGraphic.GetPrefSize() );
725 // FIXME: this magic -1 shouldn't be necessary
726 aPrefSize.Width() -= 1;
727 aPrefSize.Height() -= 1;
728 aPrefSize = m_rOuterFace.GetReferenceDevice()->
729 LogicToLogic( aPrefSize,
730 &aPatternMapMode,
731 &m_rOuterFace.GetReferenceDevice()->GetMapMode() );
732 // build bounding rectangle of pattern
733 Rectangle aBound( Point( 0, 0 ), aPrefSize );
734 m_rOuterFace.BeginPattern( aBound );
735 m_rOuterFace.Push();
736 pDummyVDev->Push();
737 m_rOuterFace.SetMapMode( aPatternMapMode );
738 pDummyVDev->SetMapMode( aPatternMapMode );
739 ImplWriteActions( m_rOuterFace, NULL, aPattern, rDummyVDev );
740 pDummyVDev->Pop();
741 m_rOuterFace.Pop();
743 nPattern = m_rOuterFace.EndPattern( aPatTransform );
745 // try some caching and reuse pattern
746 mnCachePatternId = nPattern;
747 maCacheFill = aFill;
750 // draw polypolygon with pattern fill
751 tools::PolyPolygon aPath;
752 aFill.getPath( aPath );
753 m_rOuterFace.DrawPolyPolygon( aPath, nPattern, aFill.getFillRule() == SvtGraphicFill::fillEvenOdd );
755 bSkipSequence = true;
759 if ( bSkipSequence )
761 while( ++i < nCount )
763 pAction = aMtf.GetAction( i );
764 if ( pAction->GetType() == MetaActionType::COMMENT )
766 OString sComment( static_cast<const MetaCommentAction*>(pAction)->GetComment() );
767 if (sComment == sSeqEnd)
768 break;
770 // #i44496#
771 // the replacement action for stroke is a filled rectangle
772 // the set fillcolor of the replacement is part of the graphics
773 // state and must not be skipped
774 else if( pAction->GetType() == MetaActionType::FILLCOLOR )
776 const MetaFillColorAction* pMA = static_cast<const MetaFillColorAction*>(pAction);
777 if( pMA->IsSetting() )
778 m_rOuterFace.SetFillColor( pMA->GetColor() );
779 else
780 m_rOuterFace.SetFillColor();
787 break;
789 case MetaActionType::BMP:
791 const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
792 BitmapEx aBitmapEx( pA->GetBitmap() );
793 Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
794 aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
795 if( ! ( aSize.Width() && aSize.Height() ) )
796 aSize = pDummyVDev->PixelToLogic( aBitmapEx.GetSizePixel() );
798 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
799 implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, aGraphic, pDummyVDev, i_rContext );
801 break;
803 case MetaActionType::BMPSCALE:
805 const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
806 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
807 implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), BitmapEx( pA->GetBitmap() ), aGraphic, pDummyVDev, i_rContext );
809 break;
811 case MetaActionType::BMPSCALEPART:
813 const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
814 BitmapEx aBitmapEx( pA->GetBitmap() );
815 aBitmapEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
816 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
817 implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
819 break;
821 case MetaActionType::BMPEX:
823 const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction);
824 const BitmapEx& aBitmapEx( pA->GetBitmapEx() );
825 Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
826 aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
827 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
828 implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, aGraphic, pDummyVDev, i_rContext );
830 break;
832 case MetaActionType::BMPEXSCALE:
834 const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
835 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
836 implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), pA->GetBitmapEx(), aGraphic, pDummyVDev, i_rContext );
838 break;
840 case MetaActionType::BMPEXSCALEPART:
842 const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
843 BitmapEx aBitmapEx( pA->GetBitmapEx() );
844 aBitmapEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
845 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
846 implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
848 break;
850 case MetaActionType::MASK:
851 case MetaActionType::MASKSCALE:
852 case MetaActionType::MASKSCALEPART:
854 SAL_WARN( "vcl", "MetaMask...Action not supported yet" );
856 break;
858 case MetaActionType::TEXT:
860 const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
861 m_rOuterFace.DrawText( pA->GetPoint(), pA->GetText().copy( pA->GetIndex(), std::min<sal_Int32>(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) ) );
863 break;
865 case MetaActionType::TEXTRECT:
867 const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
868 m_rOuterFace.DrawText( pA->GetRect(), pA->GetText(), pA->GetStyle() );
870 break;
872 case MetaActionType::TEXTARRAY:
874 const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
875 m_rOuterFace.DrawTextArray( pA->GetPoint(), pA->GetText(), pA->GetDXArray(), pA->GetIndex(), pA->GetLen() );
877 break;
879 case MetaActionType::STRETCHTEXT:
881 const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
882 m_rOuterFace.DrawStretchText( pA->GetPoint(), pA->GetWidth(), pA->GetText(), pA->GetIndex(), pA->GetLen() );
884 break;
886 case MetaActionType::TEXTLINE:
888 const MetaTextLineAction* pA = static_cast<const MetaTextLineAction*>(pAction);
889 m_rOuterFace.DrawTextLine( pA->GetStartPoint(), pA->GetWidth(), pA->GetStrikeout(), pA->GetUnderline(), pA->GetOverline() );
892 break;
894 case MetaActionType::CLIPREGION:
896 const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
898 if( pA->IsClipping() )
900 if( pA->GetRegion().IsEmpty() )
901 m_rOuterFace.SetClipRegion( basegfx::B2DPolyPolygon() );
902 else
904 const vcl::Region& aReg( pA->GetRegion() );
905 m_rOuterFace.SetClipRegion( aReg.GetAsB2DPolyPolygon() );
908 else
909 m_rOuterFace.SetClipRegion();
911 break;
913 case MetaActionType::ISECTRECTCLIPREGION:
915 const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction);
916 m_rOuterFace.IntersectClipRegion( pA->GetRect() );
918 break;
920 case MetaActionType::ISECTREGIONCLIPREGION:
922 const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction);
923 const vcl::Region& aReg( pA->GetRegion() );
924 m_rOuterFace.IntersectClipRegion( aReg.GetAsB2DPolyPolygon() );
926 break;
928 case MetaActionType::MOVECLIPREGION:
930 const MetaMoveClipRegionAction* pA = static_cast<const MetaMoveClipRegionAction*>(pAction);
931 m_rOuterFace.MoveClipRegion( pA->GetHorzMove(), pA->GetVertMove() );
933 break;
935 case MetaActionType::MAPMODE:
937 const_cast< MetaAction* >( pAction )->Execute( pDummyVDev );
938 m_rOuterFace.SetMapMode( pDummyVDev->GetMapMode() );
940 break;
942 case MetaActionType::LINECOLOR:
944 const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction);
946 if( pA->IsSetting() )
947 m_rOuterFace.SetLineColor( pA->GetColor() );
948 else
949 m_rOuterFace.SetLineColor();
951 break;
953 case MetaActionType::FILLCOLOR:
955 const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction);
957 if( pA->IsSetting() )
958 m_rOuterFace.SetFillColor( pA->GetColor() );
959 else
960 m_rOuterFace.SetFillColor();
962 break;
964 case MetaActionType::TEXTLINECOLOR:
966 const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction);
968 if( pA->IsSetting() )
969 m_rOuterFace.SetTextLineColor( pA->GetColor() );
970 else
971 m_rOuterFace.SetTextLineColor();
973 break;
975 case MetaActionType::OVERLINECOLOR:
977 const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction);
979 if( pA->IsSetting() )
980 m_rOuterFace.SetOverlineColor( pA->GetColor() );
981 else
982 m_rOuterFace.SetOverlineColor();
984 break;
986 case MetaActionType::TEXTFILLCOLOR:
988 const MetaTextFillColorAction* pA = static_cast<const MetaTextFillColorAction*>(pAction);
990 if( pA->IsSetting() )
991 m_rOuterFace.SetTextFillColor( pA->GetColor() );
992 else
993 m_rOuterFace.SetTextFillColor();
995 break;
997 case MetaActionType::TEXTCOLOR:
999 const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pAction);
1000 m_rOuterFace.SetTextColor( pA->GetColor() );
1002 break;
1004 case MetaActionType::TEXTALIGN:
1006 const MetaTextAlignAction* pA = static_cast<const MetaTextAlignAction*>(pAction);
1007 m_rOuterFace.SetTextAlign( pA->GetTextAlign() );
1009 break;
1011 case MetaActionType::FONT:
1013 const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
1014 m_rOuterFace.SetFont( pA->GetFont() );
1016 break;
1018 case MetaActionType::PUSH:
1020 const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
1022 pDummyVDev->Push( pA->GetFlags() );
1023 m_rOuterFace.Push( pA->GetFlags() );
1025 break;
1027 case MetaActionType::POP:
1029 pDummyVDev->Pop();
1030 m_rOuterFace.Pop();
1032 break;
1034 case MetaActionType::LAYOUTMODE:
1036 const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
1037 m_rOuterFace.SetLayoutMode( pA->GetLayoutMode() );
1039 break;
1041 case MetaActionType::TEXTLANGUAGE:
1043 const MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
1044 m_rOuterFace.SetDigitLanguage( pA->GetTextLanguage() );
1046 break;
1048 case MetaActionType::WALLPAPER:
1050 const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
1051 m_rOuterFace.DrawWallpaper( pA->GetRect(), pA->GetWallpaper() );
1053 break;
1055 case MetaActionType::RASTEROP:
1057 // !!! >>> we don't want to support this actions
1059 break;
1061 case MetaActionType::REFPOINT:
1063 // !!! >>> we don't want to support this actions
1065 break;
1067 default:
1068 // #i24604# Made assertion fire only once per
1069 // metafile. The asserted actions here are all
1070 // deprecated
1071 if( !bAssertionFired )
1073 bAssertionFired = true;
1074 SAL_WARN( "vcl", "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered " << static_cast<int>(nType) );
1076 break;
1078 i++;
1083 // Encryption methods
1085 /* a crutch to transport a ::comphelper::Hash safely though UNO API
1086 this is needed for the PDF export dialog, which otherwise would have to pass
1087 clear text passwords down till they can be used in PDFWriter. Unfortunately
1088 the MD5 sum of the password (which is needed to create the PDF encryption key)
1089 is not sufficient, since an MD5 digest cannot be created in an arbitrary state
1090 which would be needed in PDFWriterImpl::computeEncryptionKey.
1092 class EncHashTransporter : public cppu::WeakImplHelper < css::beans::XMaterialHolder >
1094 ::std::unique_ptr<::comphelper::Hash> m_pDigest;
1095 sal_IntPtr maID;
1096 std::vector< sal_uInt8 > maOValue;
1098 static std::map< sal_IntPtr, EncHashTransporter* > sTransporters;
1099 public:
1100 EncHashTransporter()
1101 : m_pDigest(new ::comphelper::Hash(::comphelper::HashType::MD5))
1103 maID = reinterpret_cast< sal_IntPtr >(this);
1104 while( sTransporters.find( maID ) != sTransporters.end() ) // paranoia mode
1105 maID++;
1106 sTransporters[ maID ] = this;
1109 virtual ~EncHashTransporter() override
1111 sTransporters.erase( maID );
1112 SAL_INFO( "vcl", "EncHashTransporter freed" );
1115 ::comphelper::Hash* getUDigest() { return m_pDigest.get(); };
1116 std::vector< sal_uInt8 >& getOValue() { return maOValue; }
1117 void invalidate()
1119 m_pDigest.reset();
1122 // XMaterialHolder
1123 virtual uno::Any SAL_CALL getMaterial() override
1125 return uno::makeAny( sal_Int64(maID) );
1128 static EncHashTransporter* getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& );
1132 std::map< sal_IntPtr, EncHashTransporter* > EncHashTransporter::sTransporters;
1134 EncHashTransporter* EncHashTransporter::getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& xRef )
1136 EncHashTransporter* pResult = nullptr;
1137 if( xRef.is() )
1139 uno::Any aMat( xRef->getMaterial() );
1140 sal_Int64 nMat = 0;
1141 if( aMat >>= nMat )
1143 std::map< sal_IntPtr, EncHashTransporter* >::iterator it = sTransporters.find( static_cast<sal_IntPtr>(nMat) );
1144 if( it != sTransporters.end() )
1145 pResult = it->second;
1148 return pResult;
1151 void PDFWriterImpl::checkAndEnableStreamEncryption( sal_Int32 nObject )
1153 if( m_aContext.Encryption.Encrypt() )
1155 m_bEncryptThisStream = true;
1156 sal_Int32 i = m_nKeyLength;
1157 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>(nObject);
1158 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject >> 8 );
1159 m_aContext.Encryption.EncryptionKey[i++] = static_cast<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 ::std::vector<unsigned char> const nMD5Sum(::comphelper::Hash::calculateHash(
1163 m_aContext.Encryption.EncryptionKey.data(), i+2, ::comphelper::HashType::MD5));
1164 // the i+2 to take into account the generation number, always zero
1165 // initialize the RC4 with the key
1166 // key length: see algorithm 3.1, step 4: (N+5) max 16
1167 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum.data(), m_nRC4KeyLength, nullptr, 0 );
1171 void PDFWriterImpl::enableStringEncryption( sal_Int32 nObject )
1173 if( m_aContext.Encryption.Encrypt() )
1175 sal_Int32 i = m_nKeyLength;
1176 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>(nObject);
1177 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject >> 8 );
1178 m_aContext.Encryption.EncryptionKey[i++] = static_cast<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 // the i+2 to take into account the generation number, always zero
1182 ::std::vector<unsigned char> const nMD5Sum(::comphelper::Hash::calculateHash(
1183 m_aContext.Encryption.EncryptionKey.data(), i+2, ::comphelper::HashType::MD5));
1184 // initialize the RC4 with the key
1185 // key length: see algorithm 3.1, step 4: (N+5) max 16
1186 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum.data(), m_nRC4KeyLength, nullptr, 0 );
1190 /* init the encryption engine
1191 1. init the document id, used both for building the document id and for building the encryption key(s)
1192 2. build the encryption key following algorithms described in the PDF specification
1194 uno::Reference< beans::XMaterialHolder > PDFWriterImpl::initEncryption( const OUString& i_rOwnerPassword,
1195 const OUString& i_rUserPassword
1198 uno::Reference< beans::XMaterialHolder > xResult;
1199 if( !i_rOwnerPassword.isEmpty() || !i_rUserPassword.isEmpty() )
1201 EncHashTransporter* pTransporter = new EncHashTransporter;
1202 xResult = pTransporter;
1204 // get padded passwords
1205 sal_uInt8 aPadUPW[ENCRYPTED_PWD_SIZE], aPadOPW[ENCRYPTED_PWD_SIZE];
1206 padPassword( i_rOwnerPassword.isEmpty() ? i_rUserPassword : i_rOwnerPassword, aPadOPW );
1207 padPassword( i_rUserPassword, aPadUPW );
1209 if( computeODictionaryValue( aPadOPW, aPadUPW, pTransporter->getOValue(), SECUR_128BIT_KEY ) )
1211 pTransporter->getUDigest()->update(aPadUPW, ENCRYPTED_PWD_SIZE);
1213 else
1214 xResult.clear();
1216 // trash temporary padded cleartext PWDs
1217 rtl_secureZeroMemory (aPadOPW, sizeof(aPadOPW));
1218 rtl_secureZeroMemory (aPadUPW, sizeof(aPadUPW));
1220 return xResult;
1223 bool PDFWriterImpl::prepareEncryption( const uno::Reference< beans::XMaterialHolder >& xEnc )
1225 bool bSuccess = false;
1226 EncHashTransporter* pTransporter = EncHashTransporter::getEncHashTransporter( xEnc );
1227 if( pTransporter )
1229 sal_Int32 nKeyLength = 0, nRC4KeyLength = 0;
1230 sal_Int32 nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, nKeyLength, nRC4KeyLength );
1231 m_aContext.Encryption.OValue = pTransporter->getOValue();
1232 bSuccess = computeUDictionaryValue( pTransporter, m_aContext.Encryption, nKeyLength, nAccessPermissions );
1234 if( ! bSuccess )
1236 m_aContext.Encryption.OValue.clear();
1237 m_aContext.Encryption.UValue.clear();
1238 m_aContext.Encryption.EncryptionKey.clear();
1240 return bSuccess;
1243 sal_Int32 PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties,
1244 sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength )
1247 2) compute the access permissions, in numerical form
1249 the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
1250 - for 40 bit security the unused bit must be set to 1, since they are not used
1251 - for 128 bit security the same bit must be preset to 0 and set later if needed
1252 according to the table 3.15, pdf v 1.4 */
1253 sal_Int32 nAccessPermissions = 0xfffff0c0;
1255 o_rKeyLength = SECUR_128BIT_KEY;
1256 o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16,
1257 // thus maximum permitted value is 16
1259 nAccessPermissions |= ( i_rProperties.CanPrintTheDocument ) ? 1 << 2 : 0;
1260 nAccessPermissions |= ( i_rProperties.CanModifyTheContent ) ? 1 << 3 : 0;
1261 nAccessPermissions |= ( i_rProperties.CanCopyOrExtract ) ? 1 << 4 : 0;
1262 nAccessPermissions |= ( i_rProperties.CanAddOrModify ) ? 1 << 5 : 0;
1263 nAccessPermissions |= ( i_rProperties.CanFillInteractive ) ? 1 << 8 : 0;
1264 nAccessPermissions |= ( i_rProperties.CanExtractForAccessibility ) ? 1 << 9 : 0;
1265 nAccessPermissions |= ( i_rProperties.CanAssemble ) ? 1 << 10 : 0;
1266 nAccessPermissions |= ( i_rProperties.CanPrintFull ) ? 1 << 11 : 0;
1267 return nAccessPermissions;
1270 /*************************************************************
1271 begin i12626 methods
1273 Implements Algorithm 3.2, step 1 only
1275 void PDFWriterImpl::padPassword( const OUString& i_rPassword, sal_uInt8* o_pPaddedPW )
1277 // get ansi-1252 version of the password string CHECKIT ! i12626
1278 OString aString( OUStringToOString( i_rPassword, RTL_TEXTENCODING_MS_1252 ) );
1280 //copy the string to the target
1281 sal_Int32 nToCopy = ( aString.getLength() < ENCRYPTED_PWD_SIZE ) ? aString.getLength() : ENCRYPTED_PWD_SIZE;
1282 sal_Int32 nCurrentChar;
1284 for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ )
1285 o_pPaddedPW[nCurrentChar] = static_cast<sal_uInt8>( aString[nCurrentChar] );
1287 //pad it with standard byte string
1288 sal_Int32 i,y;
1289 for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ )
1290 o_pPaddedPW[i] = s_nPadString[y];
1293 /**********************************
1294 Algorithm 3.2 Compute the encryption key used
1296 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
1297 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
1298 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
1300 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
1303 bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter* i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, sal_Int32 i_nAccessPermissions )
1305 bool bSuccess = true;
1306 ::std::vector<unsigned char> nMD5Sum;
1308 // transporter contains an MD5 digest with the padded user password already
1309 ::comphelper::Hash *const pDigest = i_pTransporter->getUDigest();
1310 if (pDigest)
1312 //step 3
1313 if( ! io_rProperties.OValue.empty() )
1314 pDigest->update(io_rProperties.OValue.data(), io_rProperties.OValue.size());
1315 else
1316 bSuccess = false;
1317 //Step 4
1318 sal_uInt8 nPerm[4];
1320 nPerm[0] = static_cast<sal_uInt8>(i_nAccessPermissions);
1321 nPerm[1] = static_cast<sal_uInt8>( i_nAccessPermissions >> 8 );
1322 nPerm[2] = static_cast<sal_uInt8>( i_nAccessPermissions >> 16 );
1323 nPerm[3] = static_cast<sal_uInt8>( i_nAccessPermissions >> 24 );
1325 pDigest->update(nPerm, sizeof(nPerm));
1327 //step 5, get the document ID, binary form
1328 pDigest->update(io_rProperties.DocumentIdentifier.data(), io_rProperties.DocumentIdentifier.size());
1329 //get the digest
1330 nMD5Sum = pDigest->finalize();
1332 //step 6, only if 128 bit
1333 for (sal_Int32 i = 0; i < 50; i++)
1335 nMD5Sum = ::comphelper::Hash::calculateHash(nMD5Sum.data(), nMD5Sum.size(), ::comphelper::HashType::MD5);
1338 else
1339 bSuccess = false;
1341 i_pTransporter->invalidate();
1343 //Step 7
1344 if( bSuccess )
1346 io_rProperties.EncryptionKey.resize( MAXIMUM_RC4_KEY_LENGTH );
1347 for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ )
1348 io_rProperties.EncryptionKey[i] = nMD5Sum[i];
1350 else
1351 io_rProperties.EncryptionKey.clear();
1353 return bSuccess;
1356 /**********************************
1357 Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member
1358 the step numbers down here correspond to the ones in PDF v.1.4 specification
1360 bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword,
1361 const sal_uInt8* i_pPaddedUserPassword,
1362 std::vector< sal_uInt8 >& io_rOValue,
1363 sal_Int32 i_nKeyLength
1366 bool bSuccess = true;
1368 io_rOValue.resize( ENCRYPTED_PWD_SIZE );
1370 rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1371 if (aCipher)
1373 //step 1 already done, data is in i_pPaddedOwnerPassword
1374 //step 2
1376 ::std::vector<unsigned char> nMD5Sum(::comphelper::Hash::calculateHash(
1377 i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE, ::comphelper::HashType::MD5));
1378 //step 3, only if 128 bit
1379 if (i_nKeyLength == SECUR_128BIT_KEY)
1381 sal_Int32 i;
1382 for (i = 0; i < 50; i++)
1384 nMD5Sum = ::comphelper::Hash::calculateHash(nMD5Sum.data(), nMD5Sum.size(), ::comphelper::HashType::MD5);
1387 //Step 4, the key is in nMD5Sum
1388 //step 5 already done, data is in i_pPaddedUserPassword
1389 //step 6
1390 if (rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1391 nMD5Sum.data(), i_nKeyLength , nullptr, 0 )
1392 == rtl_Cipher_E_None)
1394 // encrypt the user password using the key set above
1395 rtl_cipher_encodeARCFOUR( aCipher, i_pPaddedUserPassword, ENCRYPTED_PWD_SIZE, // the data to be encrypted
1396 io_rOValue.data(), sal_Int32(io_rOValue.size()) ); //encrypted data
1397 //Step 7, only if 128 bit
1398 if( i_nKeyLength == SECUR_128BIT_KEY )
1400 sal_uInt32 i;
1401 size_t y;
1402 sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key
1404 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1406 for( y = 0; y < sizeof( nLocalKey ); y++ )
1407 nLocalKey[y] = static_cast<sal_uInt8>( nMD5Sum[y] ^ i );
1409 if (rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1410 nLocalKey, SECUR_128BIT_KEY, nullptr, 0 ) //destination data area, on init can be NULL
1411 != rtl_Cipher_E_None)
1413 bSuccess = false;
1414 break;
1416 rtl_cipher_encodeARCFOUR( aCipher, io_rOValue.data(), sal_Int32(io_rOValue.size()), // the data to be encrypted
1417 io_rOValue.data(), sal_Int32(io_rOValue.size()) ); // encrypted data, can be the same as the input, encrypt "in place"
1418 //step 8, store in class data member
1422 else
1423 bSuccess = false;
1425 else
1426 bSuccess = false;
1428 if( aCipher )
1429 rtl_cipher_destroyARCFOUR( aCipher );
1431 if( ! bSuccess )
1432 io_rOValue.clear();
1433 return bSuccess;
1436 /**********************************
1437 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)
1439 bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter* i_pTransporter,
1440 vcl::PDFWriter::PDFEncryptionProperties& io_rProperties,
1441 sal_Int32 i_nKeyLength,
1442 sal_Int32 i_nAccessPermissions
1445 bool bSuccess = true;
1447 io_rProperties.UValue.resize( ENCRYPTED_PWD_SIZE );
1449 ::comphelper::Hash aDigest(::comphelper::HashType::MD5);
1450 rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1451 if (aCipher)
1453 //step 1, common to both 3.4 and 3.5
1454 if( computeEncryptionKey( i_pTransporter, io_rProperties, i_nAccessPermissions ) )
1456 // prepare encryption key for object
1457 for( sal_Int32 i = i_nKeyLength, y = 0; y < 5 ; y++ )
1458 io_rProperties.EncryptionKey[i++] = 0;
1460 //or 3.5, for 128 bit security
1461 //step6, initialize the last 16 bytes of the encrypted user password to 0
1462 for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sal_uInt32(io_rProperties.UValue.size()); i++)
1463 io_rProperties.UValue[i] = 0;
1464 //steps 2 and 3
1465 aDigest.update(s_nPadString, sizeof(s_nPadString));
1466 aDigest.update(io_rProperties.DocumentIdentifier.data(), io_rProperties.DocumentIdentifier.size());
1468 ::std::vector<unsigned char> const nMD5Sum(aDigest.finalize());
1469 //Step 4
1470 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1471 io_rProperties.EncryptionKey.data(), SECUR_128BIT_KEY, nullptr, 0 ); //destination data area
1472 rtl_cipher_encodeARCFOUR( aCipher, nMD5Sum.data(), nMD5Sum.size(), // the data to be encrypted
1473 io_rProperties.UValue.data(), SECUR_128BIT_KEY ); //encrypted data, stored in class data member
1474 //step 5
1475 sal_uInt32 i;
1476 size_t y;
1477 sal_uInt8 nLocalKey[SECUR_128BIT_KEY];
1479 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1481 for( y = 0; y < sizeof( nLocalKey ) ; y++ )
1482 nLocalKey[y] = static_cast<sal_uInt8>( io_rProperties.EncryptionKey[y] ^ i );
1484 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1485 nLocalKey, SECUR_128BIT_KEY, // key and key length
1486 nullptr, 0 ); //destination data area, on init can be NULL
1487 rtl_cipher_encodeARCFOUR( aCipher, io_rProperties.UValue.data(), SECUR_128BIT_KEY, // the data to be encrypted
1488 io_rProperties.UValue.data(), SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place"
1491 else
1492 bSuccess = false;
1494 else
1495 bSuccess = false;
1497 if( aCipher )
1498 rtl_cipher_destroyARCFOUR( aCipher );
1500 if( ! bSuccess )
1501 io_rProperties.UValue.clear();
1502 return bSuccess;
1505 /* end i12626 methods */
1507 static const long unsetRun[256] =
1509 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
1510 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
1511 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */
1512 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */
1513 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1514 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */
1515 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1516 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */
1517 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
1518 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
1519 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
1520 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
1521 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
1522 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
1523 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
1524 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */
1527 static const long setRun[256] =
1529 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
1530 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1531 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
1532 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
1533 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
1534 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
1535 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */
1536 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
1537 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */
1538 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */
1539 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */
1540 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */
1541 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */
1542 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */
1543 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
1544 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
1547 static bool isSet( const Scanline i_pLine, long i_nIndex )
1549 return (i_pLine[ i_nIndex/8 ] & (0x80 >> (i_nIndex&7))) != 0;
1552 static long findBitRunImpl( const Scanline i_pLine, long i_nStartIndex, long i_nW, bool i_bSet )
1554 long nIndex = i_nStartIndex;
1555 if( nIndex < i_nW )
1557 const sal_uInt8 * pByte = i_pLine + (nIndex/8);
1558 sal_uInt8 nByte = *pByte;
1560 // run up to byte boundary
1561 long nBitInByte = (nIndex & 7);
1562 if( nBitInByte )
1564 sal_uInt8 nMask = 0x80 >> nBitInByte;
1565 while( nBitInByte != 8 )
1567 if( (nByte & nMask) != (i_bSet ? nMask : 0) )
1568 return std::min(nIndex, i_nW);
1569 nMask = nMask >> 1;
1570 nBitInByte++;
1571 nIndex++;
1573 if( nIndex < i_nW )
1575 pByte++;
1576 nByte = *pByte;
1580 sal_uInt8 nRunByte;
1581 const long* pRunTable;
1582 if( i_bSet )
1584 nRunByte = 0xff;
1585 pRunTable = setRun;
1587 else
1589 nRunByte = 0;
1590 pRunTable = unsetRun;
1593 if( nIndex < i_nW )
1595 while( nByte == nRunByte )
1597 nIndex += 8;
1599 if (nIndex >= i_nW)
1600 break;
1602 pByte++;
1603 nByte = *pByte;
1607 if( nIndex < i_nW )
1609 nIndex += pRunTable[nByte];
1612 return std::min(nIndex, i_nW);
1615 static long findBitRun(const Scanline i_pLine, long i_nStartIndex, long i_nW, bool i_bSet)
1617 if (i_nStartIndex < 0)
1618 return i_nW;
1620 return findBitRunImpl(i_pLine, i_nStartIndex, i_nW, i_bSet);
1623 static long findBitRun(const Scanline i_pLine, long i_nStartIndex, long i_nW)
1625 if (i_nStartIndex < 0)
1626 return i_nW;
1628 const bool bSet = i_nStartIndex < i_nW && isSet(i_pLine, i_nStartIndex);
1630 return findBitRunImpl(i_pLine, i_nStartIndex, i_nW, bSet);
1633 struct BitStreamState
1635 sal_uInt8 mnBuffer;
1636 sal_uInt32 mnNextBitPos;
1638 BitStreamState()
1639 : mnBuffer( 0 )
1640 , mnNextBitPos( 8 )
1644 const sal_uInt8& getByte() const { return mnBuffer; }
1645 void flush() { mnNextBitPos = 8; mnBuffer = 0; }
1648 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState )
1650 while( i_nLength > io_rState.mnNextBitPos )
1652 io_rState.mnBuffer |= static_cast<sal_uInt8>( i_nCode >> (i_nLength - io_rState.mnNextBitPos) );
1653 i_nLength -= io_rState.mnNextBitPos;
1654 writeBuffer( &io_rState.getByte(), 1 );
1655 io_rState.flush();
1657 assert(i_nLength < 9);
1658 static const unsigned int msbmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1659 io_rState.mnBuffer |= static_cast<sal_uInt8>( (i_nCode & msbmask[i_nLength]) << (io_rState.mnNextBitPos - i_nLength) );
1660 io_rState.mnNextBitPos -= i_nLength;
1661 if( io_rState.mnNextBitPos == 0 )
1663 writeBuffer( &io_rState.getByte(), 1 );
1664 io_rState.flush();
1668 struct PixelCode
1670 sal_uInt32 const mnEncodedPixels;
1671 sal_uInt32 const mnCodeBits;
1672 sal_uInt32 const mnCode;
1675 static const PixelCode WhitePixelCodes[] =
1677 { 0, 8, 0x35 }, // 0011 0101
1678 { 1, 6, 0x7 }, // 0001 11
1679 { 2, 4, 0x7 }, // 0111
1680 { 3, 4, 0x8 }, // 1000
1681 { 4, 4, 0xB }, // 1011
1682 { 5, 4, 0xC }, // 1100
1683 { 6, 4, 0xE }, // 1110
1684 { 7, 4, 0xF }, // 1111
1685 { 8, 5, 0x13 }, // 1001 1
1686 { 9, 5, 0x14 }, // 1010 0
1687 { 10, 5, 0x7 }, // 0011 1
1688 { 11, 5, 0x8 }, // 0100 0
1689 { 12, 6, 0x8 }, // 0010 00
1690 { 13, 6, 0x3 }, // 0000 11
1691 { 14, 6, 0x34 }, // 1101 00
1692 { 15, 6, 0x35 }, // 1101 01
1693 { 16, 6, 0x2A }, // 1010 10
1694 { 17, 6, 0x2B }, // 1010 11
1695 { 18, 7, 0x27 }, // 0100 111
1696 { 19, 7, 0xC }, // 0001 100
1697 { 20, 7, 0x8 }, // 0001 000
1698 { 21, 7, 0x17 }, // 0010 111
1699 { 22, 7, 0x3 }, // 0000 011
1700 { 23, 7, 0x4 }, // 0000 100
1701 { 24, 7, 0x28 }, // 0101 000
1702 { 25, 7, 0x2B }, // 0101 011
1703 { 26, 7, 0x13 }, // 0010 011
1704 { 27, 7, 0x24 }, // 0100 100
1705 { 28, 7, 0x18 }, // 0011 000
1706 { 29, 8, 0x2 }, // 0000 0010
1707 { 30, 8, 0x3 }, // 0000 0011
1708 { 31, 8, 0x1A }, // 0001 1010
1709 { 32, 8, 0x1B }, // 0001 1011
1710 { 33, 8, 0x12 }, // 0001 0010
1711 { 34, 8, 0x13 }, // 0001 0011
1712 { 35, 8, 0x14 }, // 0001 0100
1713 { 36, 8, 0x15 }, // 0001 0101
1714 { 37, 8, 0x16 }, // 0001 0110
1715 { 38, 8, 0x17 }, // 0001 0111
1716 { 39, 8, 0x28 }, // 0010 1000
1717 { 40, 8, 0x29 }, // 0010 1001
1718 { 41, 8, 0x2A }, // 0010 1010
1719 { 42, 8, 0x2B }, // 0010 1011
1720 { 43, 8, 0x2C }, // 0010 1100
1721 { 44, 8, 0x2D }, // 0010 1101
1722 { 45, 8, 0x4 }, // 0000 0100
1723 { 46, 8, 0x5 }, // 0000 0101
1724 { 47, 8, 0xA }, // 0000 1010
1725 { 48, 8, 0xB }, // 0000 1011
1726 { 49, 8, 0x52 }, // 0101 0010
1727 { 50, 8, 0x53 }, // 0101 0011
1728 { 51, 8, 0x54 }, // 0101 0100
1729 { 52, 8, 0x55 }, // 0101 0101
1730 { 53, 8, 0x24 }, // 0010 0100
1731 { 54, 8, 0x25 }, // 0010 0101
1732 { 55, 8, 0x58 }, // 0101 1000
1733 { 56, 8, 0x59 }, // 0101 1001
1734 { 57, 8, 0x5A }, // 0101 1010
1735 { 58, 8, 0x5B }, // 0101 1011
1736 { 59, 8, 0x4A }, // 0100 1010
1737 { 60, 8, 0x4B }, // 0100 1011
1738 { 61, 8, 0x32 }, // 0011 0010
1739 { 62, 8, 0x33 }, // 0011 0011
1740 { 63, 8, 0x34 }, // 0011 0100
1741 { 64, 5, 0x1B }, // 1101 1
1742 { 128, 5, 0x12 }, // 1001 0
1743 { 192, 6, 0x17 }, // 0101 11
1744 { 256, 7, 0x37 }, // 0110 111
1745 { 320, 8, 0x36 }, // 0011 0110
1746 { 384, 8, 0x37 }, // 0011 0111
1747 { 448, 8, 0x64 }, // 0110 0100
1748 { 512, 8, 0x65 }, // 0110 0101
1749 { 576, 8, 0x68 }, // 0110 1000
1750 { 640, 8, 0x67 }, // 0110 0111
1751 { 704, 9, 0xCC }, // 0110 0110 0
1752 { 768, 9, 0xCD }, // 0110 0110 1
1753 { 832, 9, 0xD2 }, // 0110 1001 0
1754 { 896, 9, 0xD3 }, // 0110 1001 1
1755 { 960, 9, 0xD4 }, // 0110 1010 0
1756 { 1024, 9, 0xD5 }, // 0110 1010 1
1757 { 1088, 9, 0xD6 }, // 0110 1011 0
1758 { 1152, 9, 0xD7 }, // 0110 1011 1
1759 { 1216, 9, 0xD8 }, // 0110 1100 0
1760 { 1280, 9, 0xD9 }, // 0110 1100 1
1761 { 1344, 9, 0xDA }, // 0110 1101 0
1762 { 1408, 9, 0xDB }, // 0110 1101 1
1763 { 1472, 9, 0x98 }, // 0100 1100 0
1764 { 1536, 9, 0x99 }, // 0100 1100 1
1765 { 1600, 9, 0x9A }, // 0100 1101 0
1766 { 1664, 6, 0x18 }, // 0110 00
1767 { 1728, 9, 0x9B }, // 0100 1101 1
1768 { 1792, 11, 0x8 }, // 0000 0001 000
1769 { 1856, 11, 0xC }, // 0000 0001 100
1770 { 1920, 11, 0xD }, // 0000 0001 101
1771 { 1984, 12, 0x12 }, // 0000 0001 0010
1772 { 2048, 12, 0x13 }, // 0000 0001 0011
1773 { 2112, 12, 0x14 }, // 0000 0001 0100
1774 { 2176, 12, 0x15 }, // 0000 0001 0101
1775 { 2240, 12, 0x16 }, // 0000 0001 0110
1776 { 2304, 12, 0x17 }, // 0000 0001 0111
1777 { 2368, 12, 0x1C }, // 0000 0001 1100
1778 { 2432, 12, 0x1D }, // 0000 0001 1101
1779 { 2496, 12, 0x1E }, // 0000 0001 1110
1780 { 2560, 12, 0x1F } // 0000 0001 1111
1783 static const PixelCode BlackPixelCodes[] =
1785 { 0, 10, 0x37 }, // 0000 1101 11
1786 { 1, 3, 0x2 }, // 010
1787 { 2, 2, 0x3 }, // 11
1788 { 3, 2, 0x2 }, // 10
1789 { 4, 3, 0x3 }, // 011
1790 { 5, 4, 0x3 }, // 0011
1791 { 6, 4, 0x2 }, // 0010
1792 { 7, 5, 0x3 }, // 0001 1
1793 { 8, 6, 0x5 }, // 0001 01
1794 { 9, 6, 0x4 }, // 0001 00
1795 { 10, 7, 0x4 }, // 0000 100
1796 { 11, 7, 0x5 }, // 0000 101
1797 { 12, 7, 0x7 }, // 0000 111
1798 { 13, 8, 0x4 }, // 0000 0100
1799 { 14, 8, 0x7 }, // 0000 0111
1800 { 15, 9, 0x18 }, // 0000 1100 0
1801 { 16, 10, 0x17 }, // 0000 0101 11
1802 { 17, 10, 0x18 }, // 0000 0110 00
1803 { 18, 10, 0x8 }, // 0000 0010 00
1804 { 19, 11, 0x67 }, // 0000 1100 111
1805 { 20, 11, 0x68 }, // 0000 1101 000
1806 { 21, 11, 0x6C }, // 0000 1101 100
1807 { 22, 11, 0x37 }, // 0000 0110 111
1808 { 23, 11, 0x28 }, // 0000 0101 000
1809 { 24, 11, 0x17 }, // 0000 0010 111
1810 { 25, 11, 0x18 }, // 0000 0011 000
1811 { 26, 12, 0xCA }, // 0000 1100 1010
1812 { 27, 12, 0xCB }, // 0000 1100 1011
1813 { 28, 12, 0xCC }, // 0000 1100 1100
1814 { 29, 12, 0xCD }, // 0000 1100 1101
1815 { 30, 12, 0x68 }, // 0000 0110 1000
1816 { 31, 12, 0x69 }, // 0000 0110 1001
1817 { 32, 12, 0x6A }, // 0000 0110 1010
1818 { 33, 12, 0x6B }, // 0000 0110 1011
1819 { 34, 12, 0xD2 }, // 0000 1101 0010
1820 { 35, 12, 0xD3 }, // 0000 1101 0011
1821 { 36, 12, 0xD4 }, // 0000 1101 0100
1822 { 37, 12, 0xD5 }, // 0000 1101 0101
1823 { 38, 12, 0xD6 }, // 0000 1101 0110
1824 { 39, 12, 0xD7 }, // 0000 1101 0111
1825 { 40, 12, 0x6C }, // 0000 0110 1100
1826 { 41, 12, 0x6D }, // 0000 0110 1101
1827 { 42, 12, 0xDA }, // 0000 1101 1010
1828 { 43, 12, 0xDB }, // 0000 1101 1011
1829 { 44, 12, 0x54 }, // 0000 0101 0100
1830 { 45, 12, 0x55 }, // 0000 0101 0101
1831 { 46, 12, 0x56 }, // 0000 0101 0110
1832 { 47, 12, 0x57 }, // 0000 0101 0111
1833 { 48, 12, 0x64 }, // 0000 0110 0100
1834 { 49, 12, 0x65 }, // 0000 0110 0101
1835 { 50, 12, 0x52 }, // 0000 0101 0010
1836 { 51, 12, 0x53 }, // 0000 0101 0011
1837 { 52, 12, 0x24 }, // 0000 0010 0100
1838 { 53, 12, 0x37 }, // 0000 0011 0111
1839 { 54, 12, 0x38 }, // 0000 0011 1000
1840 { 55, 12, 0x27 }, // 0000 0010 0111
1841 { 56, 12, 0x28 }, // 0000 0010 1000
1842 { 57, 12, 0x58 }, // 0000 0101 1000
1843 { 58, 12, 0x59 }, // 0000 0101 1001
1844 { 59, 12, 0x2B }, // 0000 0010 1011
1845 { 60, 12, 0x2C }, // 0000 0010 1100
1846 { 61, 12, 0x5A }, // 0000 0101 1010
1847 { 62, 12, 0x66 }, // 0000 0110 0110
1848 { 63, 12, 0x67 }, // 0000 0110 0111
1849 { 64, 10, 0xF }, // 0000 0011 11
1850 { 128, 12, 0xC8 }, // 0000 1100 1000
1851 { 192, 12, 0xC9 }, // 0000 1100 1001
1852 { 256, 12, 0x5B }, // 0000 0101 1011
1853 { 320, 12, 0x33 }, // 0000 0011 0011
1854 { 384, 12, 0x34 }, // 0000 0011 0100
1855 { 448, 12, 0x35 }, // 0000 0011 0101
1856 { 512, 13, 0x6C }, // 0000 0011 0110 0
1857 { 576, 13, 0x6D }, // 0000 0011 0110 1
1858 { 640, 13, 0x4A }, // 0000 0010 0101 0
1859 { 704, 13, 0x4B }, // 0000 0010 0101 1
1860 { 768, 13, 0x4C }, // 0000 0010 0110 0
1861 { 832, 13, 0x4D }, // 0000 0010 0110 1
1862 { 896, 13, 0x72 }, // 0000 0011 1001 0
1863 { 960, 13, 0x73 }, // 0000 0011 1001 1
1864 { 1024, 13, 0x74 }, // 0000 0011 1010 0
1865 { 1088, 13, 0x75 }, // 0000 0011 1010 1
1866 { 1152, 13, 0x76 }, // 0000 0011 1011 0
1867 { 1216, 13, 0x77 }, // 0000 0011 1011 1
1868 { 1280, 13, 0x52 }, // 0000 0010 1001 0
1869 { 1344, 13, 0x53 }, // 0000 0010 1001 1
1870 { 1408, 13, 0x54 }, // 0000 0010 1010 0
1871 { 1472, 13, 0x55 }, // 0000 0010 1010 1
1872 { 1536, 13, 0x5A }, // 0000 0010 1101 0
1873 { 1600, 13, 0x5B }, // 0000 0010 1101 1
1874 { 1664, 13, 0x64 }, // 0000 0011 0010 0
1875 { 1728, 13, 0x65 }, // 0000 0011 0010 1
1876 { 1792, 11, 0x8 }, // 0000 0001 000
1877 { 1856, 11, 0xC }, // 0000 0001 100
1878 { 1920, 11, 0xD }, // 0000 0001 101
1879 { 1984, 12, 0x12 }, // 0000 0001 0010
1880 { 2048, 12, 0x13 }, // 0000 0001 0011
1881 { 2112, 12, 0x14 }, // 0000 0001 0100
1882 { 2176, 12, 0x15 }, // 0000 0001 0101
1883 { 2240, 12, 0x16 }, // 0000 0001 0110
1884 { 2304, 12, 0x17 }, // 0000 0001 0111
1885 { 2368, 12, 0x1C }, // 0000 0001 1100
1886 { 2432, 12, 0x1D }, // 0000 0001 1101
1887 { 2496, 12, 0x1E }, // 0000 0001 1110
1888 { 2560, 12, 0x1F } // 0000 0001 1111
1891 void PDFWriterImpl::putG4Span( long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState )
1893 const PixelCode* pTable = i_bWhitePixel ? WhitePixelCodes : BlackPixelCodes;
1894 // maximum encoded span is 2560 consecutive pixels
1895 while( i_nSpan > 2623 )
1897 // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1898 putG4Bits( pTable[103].mnCodeBits, pTable[103].mnCode, io_rState );
1899 i_nSpan -= pTable[103].mnEncodedPixels;
1901 // write multiples of 64 pixels up to 2560
1902 if( i_nSpan > 63 )
1904 sal_uInt32 nTabIndex = 63 + (i_nSpan >> 6);
1905 OSL_ASSERT( pTable[nTabIndex].mnEncodedPixels == static_cast<sal_uInt32>(64*(i_nSpan >> 6)) );
1906 putG4Bits( pTable[nTabIndex].mnCodeBits, pTable[nTabIndex].mnCode, io_rState );
1907 i_nSpan -= pTable[nTabIndex].mnEncodedPixels;
1909 putG4Bits( pTable[i_nSpan].mnCodeBits, pTable[i_nSpan].mnCode, io_rState );
1912 void PDFWriterImpl::writeG4Stream( BitmapReadAccess const * i_pBitmap )
1914 long nW = i_pBitmap->Width();
1915 long nH = i_pBitmap->Height();
1916 if( nW <= 0 || nH <= 0 )
1917 return;
1918 if( i_pBitmap->GetBitCount() != 1 )
1919 return;
1921 BitStreamState aBitState;
1923 // the first reference line is virtual and completely empty
1924 std::unique_ptr<sal_uInt8[]> pFirstRefLine(new sal_uInt8[nW/8 + 1]);
1925 memset(pFirstRefLine.get(), 0, nW/8 + 1);
1926 Scanline pRefLine = pFirstRefLine.get();
1927 for( long nY = 0; nY < nH; nY++ )
1929 const Scanline pCurLine = i_pBitmap->GetScanline( nY );
1930 long nLineIndex = 0;
1931 bool bRunSet = (*pCurLine & 0x80) != 0;
1932 bool bRefSet = (*pRefLine & 0x80) != 0;
1933 long nRunIndex1 = bRunSet ? 0 : findBitRun( pCurLine, 0, nW, bRunSet );
1934 long nRefIndex1 = bRefSet ? 0 : findBitRun( pRefLine, 0, nW, bRefSet );
1935 for( ; nLineIndex < nW; )
1937 long nRefIndex2 = findBitRun( pRefLine, nRefIndex1, nW );
1938 if( nRefIndex2 >= nRunIndex1 )
1940 long nDiff = nRefIndex1 - nRunIndex1;
1941 if( -3 <= nDiff && nDiff <= 3 )
1942 { // vertical coding
1943 static const struct
1945 sal_uInt32 const mnCodeBits;
1946 sal_uInt32 const mnCode;
1947 } VerticalCodes[7] = {
1948 { 7, 0x03 }, // 0000 011
1949 { 6, 0x03 }, // 0000 11
1950 { 3, 0x03 }, // 011
1951 { 1, 0x1 }, // 1
1952 { 3, 0x2 }, // 010
1953 { 6, 0x02 }, // 0000 10
1954 { 7, 0x02 } // 0000 010
1956 // convert to index
1957 nDiff += 3;
1959 // emit diff code
1960 putG4Bits( VerticalCodes[nDiff].mnCodeBits, VerticalCodes[nDiff].mnCode, aBitState );
1961 nLineIndex = nRunIndex1;
1963 else
1964 { // difference too large, horizontal coding
1965 // emit horz code 001
1966 putG4Bits( 3, 0x1, aBitState );
1967 long nRunIndex2 = findBitRun( pCurLine, nRunIndex1, nW );
1968 bool bWhiteFirst = ( nLineIndex + nRunIndex1 == 0 || ! isSet( pCurLine, nLineIndex ) );
1969 putG4Span( nRunIndex1 - nLineIndex, bWhiteFirst, aBitState );
1970 putG4Span( nRunIndex2 - nRunIndex1, ! bWhiteFirst, aBitState );
1971 nLineIndex = nRunIndex2;
1974 else
1975 { // emit pass code 0001
1976 putG4Bits( 4, 0x1, aBitState );
1977 nLineIndex = nRefIndex2;
1979 if( nLineIndex < nW )
1981 bool bSet = isSet( pCurLine, nLineIndex );
1982 nRunIndex1 = findBitRun( pCurLine, nLineIndex, nW, bSet );
1983 nRefIndex1 = findBitRun( pRefLine, nLineIndex, nW, ! bSet );
1984 nRefIndex1 = findBitRun( pRefLine, nRefIndex1, nW, bSet );
1988 // the current line is the reference for the next line
1989 pRefLine = pCurLine;
1991 // terminate strip with EOFB
1992 putG4Bits( 12, 1, aBitState );
1993 putG4Bits( 12, 1, aBitState );
1994 if( aBitState.mnNextBitPos != 8 )
1996 writeBuffer( &aBitState.getByte(), 1 );
1997 aBitState.flush();
2001 void PDFWriterImpl::DrawHatchLine_DrawLine(const Point& rStartPoint, const Point& rEndPoint)
2003 drawLine(rStartPoint, rEndPoint);
2006 static bool lcl_canUsePDFAxialShading(const Gradient& rGradient) {
2007 switch (rGradient.GetStyle())
2009 case GradientStyle::Linear:
2010 case GradientStyle::Axial:
2011 break;
2012 default:
2013 return false;
2016 // TODO: handle step count
2017 return rGradient.GetSteps() <= 0;
2020 void PDFWriterImpl::ImplClearFontData(bool bNewFontLists)
2022 VirtualDevice::ImplClearFontData(bNewFontLists);
2023 if (bNewFontLists && AcquireGraphics())
2025 ReleaseFontCollection();
2026 ReleaseFontCache();
2030 void PDFWriterImpl::ImplRefreshFontData(bool bNewFontLists)
2032 if (bNewFontLists && AcquireGraphics())
2034 SetFontCollectionFromSVData();
2035 ResetNewFontCache();
2039 vcl::Region PDFWriterImpl::ClipToDeviceBounds(vcl::Region aRegion) const
2041 return aRegion;
2045 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */