bump product version to 7.2.5.1
[LibreOffice.git] / vcl / source / gdi / pdfwriter_impl2.cxx
blob9d486e1aaa4fdbf2993bcd4e8eb1fd38398270f2
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/BitmapReadAccess.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>
46 #include <o3tl/unit_conversion.hxx>
48 #include <sal/log.hxx>
49 #include <memory>
51 using namespace vcl;
52 using namespace com::sun::star;
53 using namespace com::sun::star::uno;
54 using namespace com::sun::star::beans;
56 static bool lcl_canUsePDFAxialShading(const Gradient& rGradient);
58 void PDFWriterImpl::implWriteGradient( const tools::PolyPolygon& i_rPolyPoly, const Gradient& i_rGradient,
59 VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
61 GDIMetaFile aTmpMtf;
63 i_pDummyVDev->AddGradientActions( i_rPolyPoly.GetBoundRect(), i_rGradient, aTmpMtf );
65 m_rOuterFace.Push();
66 m_rOuterFace.IntersectClipRegion( i_rPolyPoly.getB2DPolyPolygon() );
67 playMetafile( aTmpMtf, nullptr, i_rContext, i_pDummyVDev );
68 m_rOuterFace.Pop();
71 void PDFWriterImpl::implWriteBitmapEx( const Point& i_rPoint, const Size& i_rSize, const BitmapEx& i_rBitmapEx, const Graphic& i_Graphic,
72 VirtualDevice const * i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
74 if ( i_rBitmapEx.IsEmpty() || !i_rSize.Width() || !i_rSize.Height() )
75 return;
77 BitmapEx aBitmapEx( i_rBitmapEx );
78 Point aPoint( i_rPoint );
79 Size aSize( i_rSize );
81 // #i19065# Negative sizes have mirror semantics on
82 // OutputDevice. BitmapEx and co. have no idea about that, so
83 // perform that _before_ doing anything with aBitmapEx.
84 BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE);
85 if( aSize.Width() < 0 )
87 aSize.setWidth( aSize.Width() * -1 );
88 aPoint.AdjustX( -(aSize.Width()) );
89 nMirrorFlags |= BmpMirrorFlags::Horizontal;
91 if( aSize.Height() < 0 )
93 aSize.setHeight( aSize.Height() * -1 );
94 aPoint.AdjustY( -(aSize.Height()) );
95 nMirrorFlags |= BmpMirrorFlags::Vertical;
98 if( nMirrorFlags != BmpMirrorFlags::NONE )
100 aBitmapEx.Mirror( nMirrorFlags );
103 bool bIsJpeg = false, bIsPng = false;
104 if( i_Graphic.GetType() != GraphicType::NONE && i_Graphic.GetBitmapEx() == aBitmapEx )
106 GfxLinkType eType = i_Graphic.GetGfxLink().GetType();
107 bIsJpeg = (eType == GfxLinkType::NativeJpg);
108 bIsPng = (eType == GfxLinkType::NativePng);
111 // Do not downsample images smaller than 50x50px.
112 const Size aBmpSize(aBitmapEx.GetSizePixel());
113 if (i_rContext.m_nMaxImageResolution > 50 && aBmpSize.getWidth() > 50
114 && aBmpSize.getHeight() > 50)
116 // do downsampling if necessary
117 const Size aDstSizeTwip( i_pDummyVDev->PixelToLogic(i_pDummyVDev->LogicToPixel(aSize), MapMode(MapUnit::MapTwip)) );
118 const double fBmpPixelX = aBmpSize.Width();
119 const double fBmpPixelY = aBmpSize.Height();
120 const double fMaxPixelX
121 = o3tl::convert<double>(aDstSizeTwip.Width(), o3tl::Length::twip, o3tl::Length::in)
122 * i_rContext.m_nMaxImageResolution;
123 const double fMaxPixelY
124 = o3tl::convert<double>(aDstSizeTwip.Height(), o3tl::Length::twip, o3tl::Length::in)
125 * i_rContext.m_nMaxImageResolution;
127 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
128 if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) ||
129 ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) &&
130 ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) )
132 // do scaling
133 Size aNewBmpSize;
134 const double fBmpWH = fBmpPixelX / fBmpPixelY;
135 const double fMaxWH = fMaxPixelX / fMaxPixelY;
137 if( fBmpWH < fMaxWH )
139 aNewBmpSize.setWidth( FRound( fMaxPixelY * fBmpWH ) );
140 aNewBmpSize.setHeight( FRound( fMaxPixelY ) );
142 else if( fBmpWH > 0.0 )
144 aNewBmpSize.setWidth( FRound( fMaxPixelX ) );
145 aNewBmpSize.setHeight( FRound( fMaxPixelX / fBmpWH) );
148 if( aNewBmpSize.Width() && aNewBmpSize.Height() )
150 // #i121233# Use best quality for PDF exports
151 aBitmapEx.Scale( aNewBmpSize, BmpScaleFlag::BestQuality );
153 else
155 aBitmapEx.SetEmpty();
160 const Size aSizePixel( aBitmapEx.GetSizePixel() );
161 if ( !(aSizePixel.Width() && aSizePixel.Height()) )
162 return;
164 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
166 auto ePixelFormat = aBitmapEx.GetBitmap().getPixelFormat();
167 if (ePixelFormat != vcl::PixelFormat::N1_BPP)
168 aBitmapEx.Convert(BmpConversion::N8BitGreys);
170 bool bUseJPGCompression = !i_rContext.m_bOnlyLosslessCompression;
171 if ( bIsPng || ( aSizePixel.Width() < 32 ) || ( aSizePixel.Height() < 32 ) )
172 bUseJPGCompression = false;
174 auto pStrm=std::make_shared<SvMemoryStream>();
175 AlphaMask aAlphaMask;
177 bool bTrueColorJPG = true;
178 if ( bUseJPGCompression )
180 // TODO this checks could be done much earlier, saving us
181 // from trying conversion & stores before...
182 if ( !aBitmapEx.IsAlpha() )
184 const auto& rCacheEntry=m_aPDFBmpCache.find(
185 aBitmapEx.GetChecksum());
186 if ( rCacheEntry != m_aPDFBmpCache.end() )
188 m_rOuterFace.DrawJPGBitmap( *rCacheEntry->second, true, aSizePixel,
189 tools::Rectangle( aPoint, aSize ), aAlphaMask, i_Graphic );
190 return;
193 sal_uInt32 nZippedFileSize = 0; // sj: we will calculate the filesize of a zipped bitmap
194 if ( !bIsJpeg ) // to determine if jpeg compression is useful
196 SvMemoryStream aTemp;
197 aTemp.SetCompressMode( aTemp.GetCompressMode() | SvStreamCompressFlags::ZBITMAP );
198 aTemp.SetVersion( SOFFICE_FILEFORMAT_40 ); // sj: up from version 40 our bitmap stream operator
199 WriteDIBBitmapEx(aBitmapEx, aTemp); // is capable of zlib stream compression
200 nZippedFileSize = aTemp.TellEnd();
202 if ( aBitmapEx.IsAlpha() )
203 aAlphaMask = aBitmapEx.GetAlpha();
204 Graphic aGraphic(BitmapEx(aBitmapEx.GetBitmap()));
206 Sequence< PropertyValue > aFilterData( 2 );
207 aFilterData[ 0 ].Name = "Quality";
208 aFilterData[ 0 ].Value <<= sal_Int32(i_rContext.m_nJPEGQuality);
209 aFilterData[ 1 ].Name = "ColorMode";
210 aFilterData[ 1 ].Value <<= sal_Int32(0);
214 uno::Reference < io::XStream > xStream = new utl::OStreamWrapper( *pStrm );
215 uno::Reference< io::XSeekable > xSeekable( xStream, UNO_QUERY_THROW );
216 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
217 uno::Reference< graphic::XGraphicProvider > xGraphicProvider( graphic::GraphicProvider::create(xContext) );
218 uno::Reference< graphic::XGraphic > xGraphic( aGraphic.GetXGraphic() );
219 uno::Reference < io::XOutputStream > xOut( xStream->getOutputStream() );
220 uno::Sequence< beans::PropertyValue > aOutMediaProperties( 3 );
221 aOutMediaProperties[0].Name = "OutputStream";
222 aOutMediaProperties[0].Value <<= xOut;
223 aOutMediaProperties[1].Name = "MimeType";
224 aOutMediaProperties[1].Value <<= OUString("image/jpeg");
225 aOutMediaProperties[2].Name = "FilterData";
226 aOutMediaProperties[2].Value <<= aFilterData;
227 xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties );
228 xOut->flush();
229 if ( !bIsJpeg && xSeekable->getLength() > nZippedFileSize )
231 bUseJPGCompression = false;
233 else
235 pStrm->Seek( STREAM_SEEK_TO_END );
237 xSeekable->seek( 0 );
238 Sequence< PropertyValue > aArgs( 1 );
239 aArgs[ 0 ].Name = "InputStream";
240 aArgs[ 0 ].Value <<= xStream;
241 uno::Reference< XPropertySet > xPropSet( xGraphicProvider->queryGraphicDescriptor( aArgs ) );
242 if ( xPropSet.is() )
244 sal_Int16 nBitsPerPixel = 24;
245 if ( xPropSet->getPropertyValue("BitsPerPixel") >>= nBitsPerPixel )
247 bTrueColorJPG = nBitsPerPixel != 8;
252 catch( uno::Exception& )
254 bUseJPGCompression = false;
257 if ( bUseJPGCompression )
259 m_rOuterFace.DrawJPGBitmap( *pStrm, bTrueColorJPG, aSizePixel, tools::Rectangle( aPoint, aSize ), aAlphaMask, i_Graphic );
260 if (!aBitmapEx.IsAlpha() && bTrueColorJPG)
262 // Cache last jpeg export
263 m_aPDFBmpCache.insert(
264 {aBitmapEx.GetChecksum(), pStrm});
267 else if ( aBitmapEx.IsAlpha() )
268 m_rOuterFace.DrawBitmapEx( aPoint, aSize, aBitmapEx );
269 else
270 m_rOuterFace.DrawBitmap( aPoint, aSize, aBitmapEx.GetBitmap(), i_Graphic );
274 void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevData* i_pOutDevData, const vcl::PDFWriter::PlayMetafileContext& i_rContext, VirtualDevice* pDummyVDev )
276 bool bAssertionFired( false );
278 ScopedVclPtr<VirtualDevice> xPrivateDevice;
279 if( ! pDummyVDev )
281 xPrivateDevice.disposeAndReset(VclPtr<VirtualDevice>::Create());
282 pDummyVDev = xPrivateDevice.get();
283 pDummyVDev->EnableOutput( false );
284 pDummyVDev->SetMapMode( i_rMtf.GetPrefMapMode() );
286 const GDIMetaFile& aMtf( i_rMtf );
288 for( sal_uInt32 i = 0, nCount = aMtf.GetActionSize(); i < nCount; )
290 if ( !i_pOutDevData || !i_pOutDevData->PlaySyncPageAct( m_rOuterFace, i, aMtf ) )
292 const MetaAction* pAction = aMtf.GetAction( i );
293 const MetaActionType nType = pAction->GetType();
295 switch( nType )
297 case MetaActionType::PIXEL:
299 const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
300 m_rOuterFace.DrawPixel( pA->GetPoint(), pA->GetColor() );
302 break;
304 case MetaActionType::POINT:
306 const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
307 m_rOuterFace.DrawPixel( pA->GetPoint() );
309 break;
311 case MetaActionType::LINE:
313 const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
314 if ( pA->GetLineInfo().IsDefault() )
315 m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint() );
316 else
317 m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint(), pA->GetLineInfo() );
319 break;
321 case MetaActionType::RECT:
323 const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
324 m_rOuterFace.DrawRect( pA->GetRect() );
326 break;
328 case MetaActionType::ROUNDRECT:
330 const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
331 m_rOuterFace.DrawRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
333 break;
335 case MetaActionType::ELLIPSE:
337 const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
338 m_rOuterFace.DrawEllipse( pA->GetRect() );
340 break;
342 case MetaActionType::ARC:
344 const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
345 m_rOuterFace.DrawArc( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
347 break;
349 case MetaActionType::PIE:
351 const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
352 m_rOuterFace.DrawPie( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
354 break;
356 case MetaActionType::CHORD:
358 const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
359 m_rOuterFace.DrawChord( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
361 break;
363 case MetaActionType::POLYGON:
365 const MetaPolygonAction* pA = static_cast<const MetaPolygonAction*>(pAction);
366 m_rOuterFace.DrawPolygon( pA->GetPolygon() );
368 break;
370 case MetaActionType::POLYLINE:
372 const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
373 if ( pA->GetLineInfo().IsDefault() )
374 m_rOuterFace.DrawPolyLine( pA->GetPolygon() );
375 else
376 m_rOuterFace.DrawPolyLine( pA->GetPolygon(), pA->GetLineInfo() );
378 break;
380 case MetaActionType::POLYPOLYGON:
382 const MetaPolyPolygonAction* pA = static_cast<const MetaPolyPolygonAction*>(pAction);
383 m_rOuterFace.DrawPolyPolygon( pA->GetPolyPolygon() );
385 break;
387 case MetaActionType::GRADIENT:
389 const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
390 const Gradient& rGradient = pA->GetGradient();
391 if (lcl_canUsePDFAxialShading(rGradient))
393 m_rOuterFace.DrawGradient( pA->GetRect(), rGradient );
395 else
397 const tools::PolyPolygon aPolyPoly( pA->GetRect() );
398 implWriteGradient( aPolyPoly, rGradient, pDummyVDev, i_rContext );
401 break;
403 case MetaActionType::GRADIENTEX:
405 const MetaGradientExAction* pA = static_cast<const MetaGradientExAction*>(pAction);
406 const Gradient& rGradient = pA->GetGradient();
408 if (lcl_canUsePDFAxialShading(rGradient))
409 m_rOuterFace.DrawGradient( pA->GetPolyPolygon(), rGradient );
410 else
411 implWriteGradient( pA->GetPolyPolygon(), rGradient, pDummyVDev, i_rContext );
413 break;
415 case MetaActionType::HATCH:
417 const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pAction);
418 m_rOuterFace.DrawHatch( pA->GetPolyPolygon(), pA->GetHatch() );
420 break;
422 case MetaActionType::Transparent:
424 const MetaTransparentAction* pA = static_cast<const MetaTransparentAction*>(pAction);
425 m_rOuterFace.DrawTransparent( pA->GetPolyPolygon(), pA->GetTransparence() );
427 break;
429 case MetaActionType::FLOATTRANSPARENT:
431 const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
433 GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
434 const Point& rPos = pA->GetPoint();
435 const Size& rSize= pA->GetSize();
436 const Gradient& rTransparenceGradient = pA->GetGradient();
438 // special case constant alpha value
439 if( rTransparenceGradient.GetStartColor() == rTransparenceGradient.GetEndColor() )
441 const Color aTransCol( rTransparenceGradient.GetStartColor() );
442 const sal_uInt16 nTransPercent = aTransCol.GetLuminance() * 100 / 255;
443 m_rOuterFace.BeginTransparencyGroup();
444 playMetafile( aTmpMtf, nullptr, i_rContext, pDummyVDev );
445 m_rOuterFace.EndTransparencyGroup( tools::Rectangle( rPos, rSize ), nTransPercent );
447 else
449 const Size aDstSizeTwip( pDummyVDev->PixelToLogic(pDummyVDev->LogicToPixel(rSize), MapMode(MapUnit::MapTwip)) );
451 // i#115962# Always use at least 300 DPI for bitmap conversion of transparence gradients,
452 // else the quality is not acceptable (see bugdoc as example)
453 sal_Int32 nMaxBmpDPI(300);
455 if( i_rContext.m_nMaxImageResolution > 50 )
457 if ( nMaxBmpDPI > i_rContext.m_nMaxImageResolution )
458 nMaxBmpDPI = i_rContext.m_nMaxImageResolution;
460 const sal_Int32 nPixelX = o3tl::convert<double>(aDstSizeTwip.Width(), o3tl::Length::twip, o3tl::Length::in) * nMaxBmpDPI;
461 const sal_Int32 nPixelY = o3tl::convert<double>(aDstSizeTwip.Height(), o3tl::Length::twip, o3tl::Length::in) * nMaxBmpDPI;
462 if ( nPixelX && nPixelY )
464 Size aDstSizePixel( nPixelX, nPixelY );
465 ScopedVclPtrInstance<VirtualDevice> xVDev;
466 if( xVDev->SetOutputSizePixel( aDstSizePixel ) )
468 Bitmap aPaint, aMask;
469 AlphaMask aAlpha;
470 Point aPoint;
472 MapMode aMapMode( pDummyVDev->GetMapMode() );
473 aMapMode.SetOrigin( aPoint );
474 xVDev->SetMapMode( aMapMode );
475 Size aDstSize( xVDev->PixelToLogic( aDstSizePixel ) );
477 Point aMtfOrigin( aTmpMtf.GetPrefMapMode().GetOrigin() );
478 if ( aMtfOrigin.X() || aMtfOrigin.Y() )
479 aTmpMtf.Move( -aMtfOrigin.X(), -aMtfOrigin.Y() );
480 double fScaleX = static_cast<double>(aDstSize.Width()) / static_cast<double>(aTmpMtf.GetPrefSize().Width());
481 double fScaleY = static_cast<double>(aDstSize.Height()) / static_cast<double>(aTmpMtf.GetPrefSize().Height());
482 if( fScaleX != 1.0 || fScaleY != 1.0 )
483 aTmpMtf.Scale( fScaleX, fScaleY );
484 aTmpMtf.SetPrefMapMode( aMapMode );
486 // create paint bitmap
487 aTmpMtf.WindStart();
488 aTmpMtf.Play(*xVDev, aPoint, aDstSize);
489 aTmpMtf.WindStart();
491 xVDev->EnableMapMode( false );
492 aPaint = xVDev->GetBitmap( aPoint, aDstSizePixel );
493 xVDev->EnableMapMode();
495 // create mask bitmap
496 xVDev->SetLineColor( COL_BLACK );
497 xVDev->SetFillColor( COL_BLACK );
498 xVDev->DrawRect( tools::Rectangle( aPoint, aDstSize ) );
499 xVDev->SetDrawMode( DrawModeFlags::WhiteLine | DrawModeFlags::WhiteFill | DrawModeFlags::WhiteText |
500 DrawModeFlags::WhiteBitmap | DrawModeFlags::WhiteGradient );
501 aTmpMtf.WindStart();
502 aTmpMtf.Play(*xVDev, aPoint, aDstSize);
503 aTmpMtf.WindStart();
504 xVDev->EnableMapMode( false );
505 aMask = xVDev->GetBitmap( aPoint, aDstSizePixel );
506 xVDev->EnableMapMode();
508 // create alpha mask from gradient
509 xVDev->SetDrawMode( DrawModeFlags::GrayGradient );
510 xVDev->DrawGradient( tools::Rectangle( aPoint, aDstSize ), rTransparenceGradient );
511 xVDev->SetDrawMode( DrawModeFlags::Default );
512 xVDev->EnableMapMode( false );
513 xVDev->DrawMask( aPoint, aDstSizePixel, aMask, COL_WHITE );
514 aAlpha = xVDev->GetBitmap( aPoint, aDstSizePixel );
516 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
517 implWriteBitmapEx( rPos, rSize, BitmapEx( aPaint, aAlpha ), aGraphic, pDummyVDev, i_rContext );
522 break;
524 case MetaActionType::EPS:
526 const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pAction);
527 const GDIMetaFile& aSubstitute( pA->GetSubstitute() );
529 m_rOuterFace.Push();
530 pDummyVDev->Push();
532 MapMode aMapMode( aSubstitute.GetPrefMapMode() );
533 Size aOutSize( OutputDevice::LogicToLogic( pA->GetSize(), pDummyVDev->GetMapMode(), aMapMode ) );
534 aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) );
535 aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) );
536 aMapMode.SetOrigin( OutputDevice::LogicToLogic( pA->GetPoint(), pDummyVDev->GetMapMode(), aMapMode ) );
538 m_rOuterFace.SetMapMode( aMapMode );
539 pDummyVDev->SetMapMode( aMapMode );
540 playMetafile( aSubstitute, nullptr, i_rContext, pDummyVDev );
541 pDummyVDev->Pop();
542 m_rOuterFace.Pop();
544 break;
546 case MetaActionType::COMMENT:
547 if( ! i_rContext.m_bTransparenciesWereRemoved )
549 const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
551 if( pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
553 const MetaGradientExAction* pGradAction = nullptr;
554 bool bDone = false;
556 while( !bDone && ( ++i < nCount ) )
558 pAction = aMtf.GetAction( i );
560 if( pAction->GetType() == MetaActionType::GRADIENTEX )
561 pGradAction = static_cast<const MetaGradientExAction*>(pAction);
562 else if( ( pAction->GetType() == MetaActionType::COMMENT ) &&
563 ( static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END")) )
565 bDone = true;
569 if( pGradAction )
571 if (lcl_canUsePDFAxialShading(pGradAction->GetGradient()))
573 m_rOuterFace.DrawGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient() );
575 else
577 implWriteGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), pDummyVDev, i_rContext );
581 else
583 const sal_uInt8* pData = pA->GetData();
584 if ( pData )
586 SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pData), pA->GetDataSize(), StreamMode::READ );
587 bool bSkipSequence = false;
588 OString sSeqEnd;
590 if( pA->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
592 sSeqEnd = OString("XPATHSTROKE_SEQ_END");
593 SvtGraphicStroke aStroke;
594 ReadSvtGraphicStroke( aMemStm, aStroke );
596 tools::Polygon aPath;
597 aStroke.getPath( aPath );
599 tools::PolyPolygon aStartArrow;
600 tools::PolyPolygon aEndArrow;
601 double fTransparency( aStroke.getTransparency() );
602 double fStrokeWidth( aStroke.getStrokeWidth() );
603 SvtGraphicStroke::DashArray aDashArray;
605 aStroke.getStartArrow( aStartArrow );
606 aStroke.getEndArrow( aEndArrow );
607 aStroke.getDashArray( aDashArray );
609 bSkipSequence = true;
610 if ( aStartArrow.Count() || aEndArrow.Count() )
611 bSkipSequence = false;
612 if ( !aDashArray.empty() && ( fStrokeWidth != 0.0 ) && ( fTransparency == 0.0 ) )
613 bSkipSequence = false;
614 if ( bSkipSequence )
616 PDFWriter::ExtLineInfo aInfo;
617 aInfo.m_fLineWidth = fStrokeWidth;
618 aInfo.m_fTransparency = fTransparency;
619 aInfo.m_fMiterLimit = aStroke.getMiterLimit();
620 switch( aStroke.getCapType() )
622 default:
623 case SvtGraphicStroke::capButt: aInfo.m_eCap = PDFWriter::capButt;break;
624 case SvtGraphicStroke::capRound: aInfo.m_eCap = PDFWriter::capRound;break;
625 case SvtGraphicStroke::capSquare: aInfo.m_eCap = PDFWriter::capSquare;break;
627 switch( aStroke.getJoinType() )
629 default:
630 case SvtGraphicStroke::joinMiter: aInfo.m_eJoin = PDFWriter::joinMiter;break;
631 case SvtGraphicStroke::joinRound: aInfo.m_eJoin = PDFWriter::joinRound;break;
632 case SvtGraphicStroke::joinBevel: aInfo.m_eJoin = PDFWriter::joinBevel;break;
633 case SvtGraphicStroke::joinNone:
634 aInfo.m_eJoin = PDFWriter::joinMiter;
635 aInfo.m_fMiterLimit = 0.0;
636 break;
638 aInfo.m_aDashArray = aDashArray;
640 if(SvtGraphicStroke::joinNone == aStroke.getJoinType()
641 && fStrokeWidth > 0.0)
643 // emulate no edge rounding by handling single edges
644 const sal_uInt16 nPoints(aPath.GetSize());
645 const bool bCurve(aPath.HasFlags());
647 for(sal_uInt16 a(0); a + 1 < nPoints; a++)
649 if(bCurve
650 && PolyFlags::Normal != aPath.GetFlags(a + 1)
651 && a + 2 < nPoints
652 && PolyFlags::Normal != aPath.GetFlags(a + 2)
653 && a + 3 < nPoints)
655 const tools::Polygon aSnippet(4,
656 aPath.GetConstPointAry() + a,
657 aPath.GetConstFlagAry() + a);
658 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
659 a += 2;
661 else
663 const tools::Polygon aSnippet(2,
664 aPath.GetConstPointAry() + a);
665 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
669 else
671 m_rOuterFace.DrawPolyLine( aPath, aInfo );
675 else if ( pA->GetComment() == "XPATHFILL_SEQ_BEGIN" )
677 sSeqEnd = OString("XPATHFILL_SEQ_END");
678 SvtGraphicFill aFill;
679 ReadSvtGraphicFill( aMemStm, aFill );
681 if ( ( aFill.getFillType() == SvtGraphicFill::fillSolid ) && ( aFill.getFillRule() == SvtGraphicFill::fillEvenOdd ) )
683 double fTransparency = aFill.getTransparency();
684 if ( fTransparency == 0.0 )
686 tools::PolyPolygon aPath;
687 aFill.getPath( aPath );
689 bSkipSequence = true;
690 m_rOuterFace.DrawPolyPolygon( aPath );
692 else if ( fTransparency == 1.0 )
693 bSkipSequence = true;
696 if ( bSkipSequence )
698 while( ++i < nCount )
700 pAction = aMtf.GetAction( i );
701 if ( pAction->GetType() == MetaActionType::COMMENT )
703 OString sComment( static_cast<const MetaCommentAction*>(pAction)->GetComment() );
704 if (sComment == sSeqEnd)
705 break;
707 // #i44496#
708 // the replacement action for stroke is a filled rectangle
709 // the set fillcolor of the replacement is part of the graphics
710 // state and must not be skipped
711 else if( pAction->GetType() == MetaActionType::FILLCOLOR )
713 const MetaFillColorAction* pMA = static_cast<const MetaFillColorAction*>(pAction);
714 if( pMA->IsSetting() )
715 m_rOuterFace.SetFillColor( pMA->GetColor() );
716 else
717 m_rOuterFace.SetFillColor();
724 break;
726 case MetaActionType::BMP:
728 const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
729 BitmapEx aBitmapEx( pA->GetBitmap() );
730 Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
731 aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
732 if( ! ( aSize.Width() && aSize.Height() ) )
733 aSize = pDummyVDev->PixelToLogic( aBitmapEx.GetSizePixel() );
735 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
736 implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, aGraphic, pDummyVDev, i_rContext );
738 break;
740 case MetaActionType::BMPSCALE:
742 const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
743 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
744 implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), BitmapEx( pA->GetBitmap() ), aGraphic, pDummyVDev, i_rContext );
746 break;
748 case MetaActionType::BMPSCALEPART:
750 const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
751 BitmapEx aBitmapEx( pA->GetBitmap() );
752 aBitmapEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
753 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
754 implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
756 break;
758 case MetaActionType::BMPEX:
760 const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction);
761 const BitmapEx& aBitmapEx( pA->GetBitmapEx() );
762 Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
763 aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
764 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
765 implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, aGraphic, pDummyVDev, i_rContext );
767 break;
769 case MetaActionType::BMPEXSCALE:
771 const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
772 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
773 implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), pA->GetBitmapEx(), aGraphic, pDummyVDev, i_rContext );
775 break;
777 case MetaActionType::BMPEXSCALEPART:
779 const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
780 BitmapEx aBitmapEx( pA->GetBitmapEx() );
781 aBitmapEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
782 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
783 implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
785 break;
787 case MetaActionType::MASK:
788 case MetaActionType::MASKSCALE:
789 case MetaActionType::MASKSCALEPART:
791 SAL_WARN( "vcl", "MetaMask...Action not supported yet" );
793 break;
795 case MetaActionType::TEXT:
797 const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
798 m_rOuterFace.DrawText( pA->GetPoint(), pA->GetText().copy( pA->GetIndex(), std::min<sal_Int32>(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) ) );
800 break;
802 case MetaActionType::TEXTRECT:
804 const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
805 m_rOuterFace.DrawText( pA->GetRect(), pA->GetText(), pA->GetStyle() );
807 break;
809 case MetaActionType::TEXTARRAY:
811 const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
812 m_rOuterFace.DrawTextArray( pA->GetPoint(), pA->GetText(), pA->GetDXArray(), pA->GetIndex(), pA->GetLen() );
814 break;
816 case MetaActionType::STRETCHTEXT:
818 const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
819 m_rOuterFace.DrawStretchText( pA->GetPoint(), pA->GetWidth(), pA->GetText(), pA->GetIndex(), pA->GetLen() );
821 break;
823 case MetaActionType::TEXTLINE:
825 const MetaTextLineAction* pA = static_cast<const MetaTextLineAction*>(pAction);
826 m_rOuterFace.DrawTextLine( pA->GetStartPoint(), pA->GetWidth(), pA->GetStrikeout(), pA->GetUnderline(), pA->GetOverline() );
829 break;
831 case MetaActionType::CLIPREGION:
833 const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
835 if( pA->IsClipping() )
837 if( pA->GetRegion().IsEmpty() )
838 m_rOuterFace.SetClipRegion( basegfx::B2DPolyPolygon() );
839 else
841 const vcl::Region& aReg( pA->GetRegion() );
842 m_rOuterFace.SetClipRegion( aReg.GetAsB2DPolyPolygon() );
845 else
846 m_rOuterFace.SetClipRegion();
848 break;
850 case MetaActionType::ISECTRECTCLIPREGION:
852 const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction);
853 m_rOuterFace.IntersectClipRegion( pA->GetRect() );
855 break;
857 case MetaActionType::ISECTREGIONCLIPREGION:
859 const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction);
860 const vcl::Region& aReg( pA->GetRegion() );
861 m_rOuterFace.IntersectClipRegion( aReg.GetAsB2DPolyPolygon() );
863 break;
865 case MetaActionType::MOVECLIPREGION:
867 const MetaMoveClipRegionAction* pA = static_cast<const MetaMoveClipRegionAction*>(pAction);
868 m_rOuterFace.MoveClipRegion( pA->GetHorzMove(), pA->GetVertMove() );
870 break;
872 case MetaActionType::MAPMODE:
874 const_cast< MetaAction* >( pAction )->Execute( pDummyVDev );
875 m_rOuterFace.SetMapMode( pDummyVDev->GetMapMode() );
877 break;
879 case MetaActionType::LINECOLOR:
881 const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction);
883 if( pA->IsSetting() )
884 m_rOuterFace.SetLineColor( pA->GetColor() );
885 else
886 m_rOuterFace.SetLineColor();
888 break;
890 case MetaActionType::FILLCOLOR:
892 const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction);
894 if( pA->IsSetting() )
895 m_rOuterFace.SetFillColor( pA->GetColor() );
896 else
897 m_rOuterFace.SetFillColor();
899 break;
901 case MetaActionType::TEXTLINECOLOR:
903 const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction);
905 if( pA->IsSetting() )
906 m_rOuterFace.SetTextLineColor( pA->GetColor() );
907 else
908 m_rOuterFace.SetTextLineColor();
910 break;
912 case MetaActionType::OVERLINECOLOR:
914 const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction);
916 if( pA->IsSetting() )
917 m_rOuterFace.SetOverlineColor( pA->GetColor() );
918 else
919 m_rOuterFace.SetOverlineColor();
921 break;
923 case MetaActionType::TEXTFILLCOLOR:
925 const MetaTextFillColorAction* pA = static_cast<const MetaTextFillColorAction*>(pAction);
927 if( pA->IsSetting() )
928 m_rOuterFace.SetTextFillColor( pA->GetColor() );
929 else
930 m_rOuterFace.SetTextFillColor();
932 break;
934 case MetaActionType::TEXTCOLOR:
936 const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pAction);
937 m_rOuterFace.SetTextColor( pA->GetColor() );
939 break;
941 case MetaActionType::TEXTALIGN:
943 const MetaTextAlignAction* pA = static_cast<const MetaTextAlignAction*>(pAction);
944 m_rOuterFace.SetTextAlign( pA->GetTextAlign() );
946 break;
948 case MetaActionType::FONT:
950 const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
951 m_rOuterFace.SetFont( pA->GetFont() );
953 break;
955 case MetaActionType::PUSH:
957 const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
959 pDummyVDev->Push( pA->GetFlags() );
960 m_rOuterFace.Push( pA->GetFlags() );
962 break;
964 case MetaActionType::POP:
966 pDummyVDev->Pop();
967 m_rOuterFace.Pop();
969 break;
971 case MetaActionType::LAYOUTMODE:
973 const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
974 m_rOuterFace.SetLayoutMode( pA->GetLayoutMode() );
976 break;
978 case MetaActionType::TEXTLANGUAGE:
980 const MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
981 m_rOuterFace.SetDigitLanguage( pA->GetTextLanguage() );
983 break;
985 case MetaActionType::WALLPAPER:
987 const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
988 m_rOuterFace.DrawWallpaper( pA->GetRect(), pA->GetWallpaper() );
990 break;
992 case MetaActionType::RASTEROP:
994 // !!! >>> we don't want to support this actions
996 break;
998 case MetaActionType::REFPOINT:
1000 // !!! >>> we don't want to support this actions
1002 break;
1004 default:
1005 // #i24604# Made assertion fire only once per
1006 // metafile. The asserted actions here are all
1007 // deprecated
1008 if( !bAssertionFired )
1010 bAssertionFired = true;
1011 SAL_WARN( "vcl", "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered " << static_cast<int>(nType) );
1013 break;
1015 i++;
1020 // Encryption methods
1022 /* a crutch to transport a ::comphelper::Hash safely though UNO API
1023 this is needed for the PDF export dialog, which otherwise would have to pass
1024 clear text passwords down till they can be used in PDFWriter. Unfortunately
1025 the MD5 sum of the password (which is needed to create the PDF encryption key)
1026 is not sufficient, since an MD5 digest cannot be created in an arbitrary state
1027 which would be needed in PDFWriterImpl::computeEncryptionKey.
1029 class EncHashTransporter : public cppu::WeakImplHelper < css::beans::XMaterialHolder >
1031 ::std::unique_ptr<::comphelper::Hash> m_pDigest;
1032 sal_IntPtr maID;
1033 std::vector< sal_uInt8 > maOValue;
1035 static std::map< sal_IntPtr, EncHashTransporter* > sTransporters;
1036 public:
1037 EncHashTransporter()
1038 : m_pDigest(new ::comphelper::Hash(::comphelper::HashType::MD5))
1040 maID = reinterpret_cast< sal_IntPtr >(this);
1041 while( sTransporters.find( maID ) != sTransporters.end() ) // paranoia mode
1042 maID++;
1043 sTransporters[ maID ] = this;
1046 virtual ~EncHashTransporter() override
1048 sTransporters.erase( maID );
1049 SAL_INFO( "vcl", "EncHashTransporter freed" );
1052 ::comphelper::Hash* getUDigest() { return m_pDigest.get(); };
1053 std::vector< sal_uInt8 >& getOValue() { return maOValue; }
1054 void invalidate()
1056 m_pDigest.reset();
1059 // XMaterialHolder
1060 virtual uno::Any SAL_CALL getMaterial() override
1062 return uno::makeAny( sal_Int64(maID) );
1065 static EncHashTransporter* getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& );
1069 std::map< sal_IntPtr, EncHashTransporter* > EncHashTransporter::sTransporters;
1071 EncHashTransporter* EncHashTransporter::getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& xRef )
1073 EncHashTransporter* pResult = nullptr;
1074 if( xRef.is() )
1076 uno::Any aMat( xRef->getMaterial() );
1077 sal_Int64 nMat = 0;
1078 if( aMat >>= nMat )
1080 std::map< sal_IntPtr, EncHashTransporter* >::iterator it = sTransporters.find( static_cast<sal_IntPtr>(nMat) );
1081 if( it != sTransporters.end() )
1082 pResult = it->second;
1085 return pResult;
1088 void PDFWriterImpl::checkAndEnableStreamEncryption( sal_Int32 nObject )
1090 if( !m_aContext.Encryption.Encrypt() )
1091 return;
1093 m_bEncryptThisStream = true;
1094 sal_Int32 i = m_nKeyLength;
1095 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>(nObject);
1096 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject >> 8 );
1097 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject >> 16 );
1098 // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1099 // do the MD5 hash
1100 ::std::vector<unsigned char> const nMD5Sum(::comphelper::Hash::calculateHash(
1101 m_aContext.Encryption.EncryptionKey.data(), i+2, ::comphelper::HashType::MD5));
1102 // the i+2 to take into account the generation number, always zero
1103 // initialize the RC4 with the key
1104 // key length: see algorithm 3.1, step 4: (N+5) max 16
1105 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum.data(), m_nRC4KeyLength, nullptr, 0 );
1108 void PDFWriterImpl::enableStringEncryption( sal_Int32 nObject )
1110 if( !m_aContext.Encryption.Encrypt() )
1111 return;
1113 sal_Int32 i = m_nKeyLength;
1114 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>(nObject);
1115 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject >> 8 );
1116 m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject >> 16 );
1117 // the other location of m_nEncryptionKey is already set to 0, our fixed generation number
1118 // do the MD5 hash
1119 // the i+2 to take into account the generation number, always zero
1120 ::std::vector<unsigned char> const nMD5Sum(::comphelper::Hash::calculateHash(
1121 m_aContext.Encryption.EncryptionKey.data(), i+2, ::comphelper::HashType::MD5));
1122 // initialize the RC4 with the key
1123 // key length: see algorithm 3.1, step 4: (N+5) max 16
1124 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum.data(), m_nRC4KeyLength, nullptr, 0 );
1127 /* init the encryption engine
1128 1. init the document id, used both for building the document id and for building the encryption key(s)
1129 2. build the encryption key following algorithms described in the PDF specification
1131 uno::Reference< beans::XMaterialHolder > PDFWriterImpl::initEncryption( const OUString& i_rOwnerPassword,
1132 const OUString& i_rUserPassword
1135 uno::Reference< beans::XMaterialHolder > xResult;
1136 if( !i_rOwnerPassword.isEmpty() || !i_rUserPassword.isEmpty() )
1138 rtl::Reference<EncHashTransporter> pTransporter = new EncHashTransporter;
1139 xResult = pTransporter;
1141 // get padded passwords
1142 sal_uInt8 aPadUPW[ENCRYPTED_PWD_SIZE], aPadOPW[ENCRYPTED_PWD_SIZE];
1143 padPassword( i_rOwnerPassword.isEmpty() ? i_rUserPassword : i_rOwnerPassword, aPadOPW );
1144 padPassword( i_rUserPassword, aPadUPW );
1146 if( computeODictionaryValue( aPadOPW, aPadUPW, pTransporter->getOValue(), SECUR_128BIT_KEY ) )
1148 pTransporter->getUDigest()->update(aPadUPW, ENCRYPTED_PWD_SIZE);
1150 else
1151 xResult.clear();
1153 // trash temporary padded cleartext PWDs
1154 rtl_secureZeroMemory (aPadOPW, sizeof(aPadOPW));
1155 rtl_secureZeroMemory (aPadUPW, sizeof(aPadUPW));
1157 return xResult;
1160 bool PDFWriterImpl::prepareEncryption( const uno::Reference< beans::XMaterialHolder >& xEnc )
1162 bool bSuccess = false;
1163 EncHashTransporter* pTransporter = EncHashTransporter::getEncHashTransporter( xEnc );
1164 if( pTransporter )
1166 sal_Int32 nKeyLength = 0, nRC4KeyLength = 0;
1167 sal_Int32 nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, nKeyLength, nRC4KeyLength );
1168 m_aContext.Encryption.OValue = pTransporter->getOValue();
1169 bSuccess = computeUDictionaryValue( pTransporter, m_aContext.Encryption, nKeyLength, nAccessPermissions );
1171 if( ! bSuccess )
1173 m_aContext.Encryption.OValue.clear();
1174 m_aContext.Encryption.UValue.clear();
1175 m_aContext.Encryption.EncryptionKey.clear();
1177 return bSuccess;
1180 sal_Int32 PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties,
1181 sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength )
1184 2) compute the access permissions, in numerical form
1186 the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
1187 - for 40 bit security the unused bit must be set to 1, since they are not used
1188 - for 128 bit security the same bit must be preset to 0 and set later if needed
1189 according to the table 3.15, pdf v 1.4 */
1190 sal_Int32 nAccessPermissions = 0xfffff0c0;
1192 o_rKeyLength = SECUR_128BIT_KEY;
1193 o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16,
1194 // thus maximum permitted value is 16
1196 nAccessPermissions |= ( i_rProperties.CanPrintTheDocument ) ? 1 << 2 : 0;
1197 nAccessPermissions |= ( i_rProperties.CanModifyTheContent ) ? 1 << 3 : 0;
1198 nAccessPermissions |= ( i_rProperties.CanCopyOrExtract ) ? 1 << 4 : 0;
1199 nAccessPermissions |= ( i_rProperties.CanAddOrModify ) ? 1 << 5 : 0;
1200 nAccessPermissions |= ( i_rProperties.CanFillInteractive ) ? 1 << 8 : 0;
1201 nAccessPermissions |= ( i_rProperties.CanExtractForAccessibility ) ? 1 << 9 : 0;
1202 nAccessPermissions |= ( i_rProperties.CanAssemble ) ? 1 << 10 : 0;
1203 nAccessPermissions |= ( i_rProperties.CanPrintFull ) ? 1 << 11 : 0;
1204 return nAccessPermissions;
1207 /*************************************************************
1208 begin i12626 methods
1210 Implements Algorithm 3.2, step 1 only
1212 void PDFWriterImpl::padPassword( std::u16string_view i_rPassword, sal_uInt8* o_pPaddedPW )
1214 // get ansi-1252 version of the password string CHECKIT ! i12626
1215 OString aString( OUStringToOString( i_rPassword, RTL_TEXTENCODING_MS_1252 ) );
1217 //copy the string to the target
1218 sal_Int32 nToCopy = ( aString.getLength() < ENCRYPTED_PWD_SIZE ) ? aString.getLength() : ENCRYPTED_PWD_SIZE;
1219 sal_Int32 nCurrentChar;
1221 for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ )
1222 o_pPaddedPW[nCurrentChar] = static_cast<sal_uInt8>( aString[nCurrentChar] );
1224 //pad it with standard byte string
1225 sal_Int32 i,y;
1226 for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ )
1227 o_pPaddedPW[i] = s_nPadString[y];
1230 /**********************************
1231 Algorithm 3.2 Compute the encryption key used
1233 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
1234 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
1235 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
1237 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
1240 bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter* i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, sal_Int32 i_nAccessPermissions )
1242 bool bSuccess = true;
1243 ::std::vector<unsigned char> nMD5Sum;
1245 // transporter contains an MD5 digest with the padded user password already
1246 ::comphelper::Hash *const pDigest = i_pTransporter->getUDigest();
1247 if (pDigest)
1249 //step 3
1250 if( ! io_rProperties.OValue.empty() )
1251 pDigest->update(io_rProperties.OValue.data(), io_rProperties.OValue.size());
1252 else
1253 bSuccess = false;
1254 //Step 4
1255 sal_uInt8 nPerm[4];
1257 nPerm[0] = static_cast<sal_uInt8>(i_nAccessPermissions);
1258 nPerm[1] = static_cast<sal_uInt8>( i_nAccessPermissions >> 8 );
1259 nPerm[2] = static_cast<sal_uInt8>( i_nAccessPermissions >> 16 );
1260 nPerm[3] = static_cast<sal_uInt8>( i_nAccessPermissions >> 24 );
1262 pDigest->update(nPerm, sizeof(nPerm));
1264 //step 5, get the document ID, binary form
1265 pDigest->update(io_rProperties.DocumentIdentifier.data(), io_rProperties.DocumentIdentifier.size());
1266 //get the digest
1267 nMD5Sum = pDigest->finalize();
1269 //step 6, only if 128 bit
1270 for (sal_Int32 i = 0; i < 50; i++)
1272 nMD5Sum = ::comphelper::Hash::calculateHash(nMD5Sum.data(), nMD5Sum.size(), ::comphelper::HashType::MD5);
1275 else
1276 bSuccess = false;
1278 i_pTransporter->invalidate();
1280 //Step 7
1281 if( bSuccess )
1283 io_rProperties.EncryptionKey.resize( MAXIMUM_RC4_KEY_LENGTH );
1284 for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ )
1285 io_rProperties.EncryptionKey[i] = nMD5Sum[i];
1287 else
1288 io_rProperties.EncryptionKey.clear();
1290 return bSuccess;
1293 /**********************************
1294 Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member
1295 the step numbers down here correspond to the ones in PDF v.1.4 specification
1297 bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword,
1298 const sal_uInt8* i_pPaddedUserPassword,
1299 std::vector< sal_uInt8 >& io_rOValue,
1300 sal_Int32 i_nKeyLength
1303 bool bSuccess = true;
1305 io_rOValue.resize( ENCRYPTED_PWD_SIZE );
1307 rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1308 if (aCipher)
1310 //step 1 already done, data is in i_pPaddedOwnerPassword
1311 //step 2
1313 ::std::vector<unsigned char> nMD5Sum(::comphelper::Hash::calculateHash(
1314 i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE, ::comphelper::HashType::MD5));
1315 //step 3, only if 128 bit
1316 if (i_nKeyLength == SECUR_128BIT_KEY)
1318 sal_Int32 i;
1319 for (i = 0; i < 50; i++)
1321 nMD5Sum = ::comphelper::Hash::calculateHash(nMD5Sum.data(), nMD5Sum.size(), ::comphelper::HashType::MD5);
1324 //Step 4, the key is in nMD5Sum
1325 //step 5 already done, data is in i_pPaddedUserPassword
1326 //step 6
1327 if (rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1328 nMD5Sum.data(), i_nKeyLength , nullptr, 0 )
1329 == rtl_Cipher_E_None)
1331 // encrypt the user password using the key set above
1332 rtl_cipher_encodeARCFOUR( aCipher, i_pPaddedUserPassword, ENCRYPTED_PWD_SIZE, // the data to be encrypted
1333 io_rOValue.data(), sal_Int32(io_rOValue.size()) ); //encrypted data
1334 //Step 7, only if 128 bit
1335 if( i_nKeyLength == SECUR_128BIT_KEY )
1337 sal_uInt32 i;
1338 size_t y;
1339 sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key
1341 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1343 for( y = 0; y < sizeof( nLocalKey ); y++ )
1344 nLocalKey[y] = static_cast<sal_uInt8>( nMD5Sum[y] ^ i );
1346 if (rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1347 nLocalKey, SECUR_128BIT_KEY, nullptr, 0 ) //destination data area, on init can be NULL
1348 != rtl_Cipher_E_None)
1350 bSuccess = false;
1351 break;
1353 rtl_cipher_encodeARCFOUR( aCipher, io_rOValue.data(), sal_Int32(io_rOValue.size()), // the data to be encrypted
1354 io_rOValue.data(), sal_Int32(io_rOValue.size()) ); // encrypted data, can be the same as the input, encrypt "in place"
1355 //step 8, store in class data member
1359 else
1360 bSuccess = false;
1362 else
1363 bSuccess = false;
1365 if( aCipher )
1366 rtl_cipher_destroyARCFOUR( aCipher );
1368 if( ! bSuccess )
1369 io_rOValue.clear();
1370 return bSuccess;
1373 /**********************************
1374 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)
1376 bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter* i_pTransporter,
1377 vcl::PDFWriter::PDFEncryptionProperties& io_rProperties,
1378 sal_Int32 i_nKeyLength,
1379 sal_Int32 i_nAccessPermissions
1382 bool bSuccess = true;
1384 io_rProperties.UValue.resize( ENCRYPTED_PWD_SIZE );
1386 ::comphelper::Hash aDigest(::comphelper::HashType::MD5);
1387 rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1388 if (aCipher)
1390 //step 1, common to both 3.4 and 3.5
1391 if( computeEncryptionKey( i_pTransporter, io_rProperties, i_nAccessPermissions ) )
1393 // prepare encryption key for object
1394 for( sal_Int32 i = i_nKeyLength, y = 0; y < 5 ; y++ )
1395 io_rProperties.EncryptionKey[i++] = 0;
1397 //or 3.5, for 128 bit security
1398 //step6, initialize the last 16 bytes of the encrypted user password to 0
1399 for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sal_uInt32(io_rProperties.UValue.size()); i++)
1400 io_rProperties.UValue[i] = 0;
1401 //steps 2 and 3
1402 aDigest.update(s_nPadString, sizeof(s_nPadString));
1403 aDigest.update(io_rProperties.DocumentIdentifier.data(), io_rProperties.DocumentIdentifier.size());
1405 ::std::vector<unsigned char> const nMD5Sum(aDigest.finalize());
1406 //Step 4
1407 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1408 io_rProperties.EncryptionKey.data(), SECUR_128BIT_KEY, nullptr, 0 ); //destination data area
1409 rtl_cipher_encodeARCFOUR( aCipher, nMD5Sum.data(), nMD5Sum.size(), // the data to be encrypted
1410 io_rProperties.UValue.data(), SECUR_128BIT_KEY ); //encrypted data, stored in class data member
1411 //step 5
1412 sal_uInt32 i;
1413 size_t y;
1414 sal_uInt8 nLocalKey[SECUR_128BIT_KEY];
1416 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
1418 for( y = 0; y < sizeof( nLocalKey ) ; y++ )
1419 nLocalKey[y] = static_cast<sal_uInt8>( io_rProperties.EncryptionKey[y] ^ i );
1421 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
1422 nLocalKey, SECUR_128BIT_KEY, // key and key length
1423 nullptr, 0 ); //destination data area, on init can be NULL
1424 rtl_cipher_encodeARCFOUR( aCipher, io_rProperties.UValue.data(), SECUR_128BIT_KEY, // the data to be encrypted
1425 io_rProperties.UValue.data(), SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place"
1428 else
1429 bSuccess = false;
1431 else
1432 bSuccess = false;
1434 if( aCipher )
1435 rtl_cipher_destroyARCFOUR( aCipher );
1437 if( ! bSuccess )
1438 io_rProperties.UValue.clear();
1439 return bSuccess;
1442 /* end i12626 methods */
1444 const tools::Long unsetRun[256] =
1446 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
1447 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
1448 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */
1449 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */
1450 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1451 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */
1452 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1453 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */
1454 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
1455 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
1456 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
1457 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
1458 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
1459 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
1460 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
1461 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */
1464 const tools::Long setRun[256] =
1466 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
1467 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1468 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
1469 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
1470 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
1471 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
1472 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */
1473 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
1474 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */
1475 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */
1476 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */
1477 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */
1478 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */
1479 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */
1480 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
1481 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
1484 static bool isSet( const Scanline i_pLine, tools::Long i_nIndex )
1486 return (i_pLine[ i_nIndex/8 ] & (0x80 >> (i_nIndex&7))) != 0;
1489 static tools::Long findBitRunImpl( const Scanline i_pLine, tools::Long i_nStartIndex, tools::Long i_nW, bool i_bSet )
1491 tools::Long nIndex = i_nStartIndex;
1492 if( nIndex < i_nW )
1494 const sal_uInt8 * pByte = i_pLine + (nIndex/8);
1495 sal_uInt8 nByte = *pByte;
1497 // run up to byte boundary
1498 tools::Long nBitInByte = (nIndex & 7);
1499 if( nBitInByte )
1501 sal_uInt8 nMask = 0x80 >> nBitInByte;
1502 while( nBitInByte != 8 )
1504 if( (nByte & nMask) != (i_bSet ? nMask : 0) )
1505 return std::min(nIndex, i_nW);
1506 nMask = nMask >> 1;
1507 nBitInByte++;
1508 nIndex++;
1510 if( nIndex < i_nW )
1512 pByte++;
1513 nByte = *pByte;
1517 sal_uInt8 nRunByte;
1518 const tools::Long* pRunTable;
1519 if( i_bSet )
1521 nRunByte = 0xff;
1522 pRunTable = setRun;
1524 else
1526 nRunByte = 0;
1527 pRunTable = unsetRun;
1530 if( nIndex < i_nW )
1532 while( nByte == nRunByte )
1534 nIndex += 8;
1536 if (nIndex >= i_nW)
1537 break;
1539 pByte++;
1540 nByte = *pByte;
1544 if( nIndex < i_nW )
1546 nIndex += pRunTable[nByte];
1549 return std::min(nIndex, i_nW);
1552 static tools::Long findBitRun(const Scanline i_pLine, tools::Long i_nStartIndex, tools::Long i_nW, bool i_bSet)
1554 if (i_nStartIndex < 0)
1555 return i_nW;
1557 return findBitRunImpl(i_pLine, i_nStartIndex, i_nW, i_bSet);
1560 static tools::Long findBitRun(const Scanline i_pLine, tools::Long i_nStartIndex, tools::Long i_nW)
1562 if (i_nStartIndex < 0)
1563 return i_nW;
1565 const bool bSet = i_nStartIndex < i_nW && isSet(i_pLine, i_nStartIndex);
1567 return findBitRunImpl(i_pLine, i_nStartIndex, i_nW, bSet);
1570 struct BitStreamState
1572 sal_uInt8 mnBuffer;
1573 sal_uInt32 mnNextBitPos;
1575 BitStreamState()
1576 : mnBuffer( 0 )
1577 , mnNextBitPos( 8 )
1581 const sal_uInt8& getByte() const { return mnBuffer; }
1582 void flush() { mnNextBitPos = 8; mnBuffer = 0; }
1585 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState )
1587 while( i_nLength > io_rState.mnNextBitPos )
1589 io_rState.mnBuffer |= static_cast<sal_uInt8>( i_nCode >> (i_nLength - io_rState.mnNextBitPos) );
1590 i_nLength -= io_rState.mnNextBitPos;
1591 writeBuffer( &io_rState.getByte(), 1 );
1592 io_rState.flush();
1594 assert(i_nLength < 9);
1595 static const unsigned int msbmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1596 io_rState.mnBuffer |= static_cast<sal_uInt8>( (i_nCode & msbmask[i_nLength]) << (io_rState.mnNextBitPos - i_nLength) );
1597 io_rState.mnNextBitPos -= i_nLength;
1598 if( io_rState.mnNextBitPos == 0 )
1600 writeBuffer( &io_rState.getByte(), 1 );
1601 io_rState.flush();
1605 namespace {
1607 struct PixelCode
1609 sal_uInt32 mnEncodedPixels;
1610 sal_uInt32 mnCodeBits;
1611 sal_uInt32 mnCode;
1616 const PixelCode WhitePixelCodes[] =
1618 { 0, 8, 0x35 }, // 0011 0101
1619 { 1, 6, 0x7 }, // 0001 11
1620 { 2, 4, 0x7 }, // 0111
1621 { 3, 4, 0x8 }, // 1000
1622 { 4, 4, 0xB }, // 1011
1623 { 5, 4, 0xC }, // 1100
1624 { 6, 4, 0xE }, // 1110
1625 { 7, 4, 0xF }, // 1111
1626 { 8, 5, 0x13 }, // 1001 1
1627 { 9, 5, 0x14 }, // 1010 0
1628 { 10, 5, 0x7 }, // 0011 1
1629 { 11, 5, 0x8 }, // 0100 0
1630 { 12, 6, 0x8 }, // 0010 00
1631 { 13, 6, 0x3 }, // 0000 11
1632 { 14, 6, 0x34 }, // 1101 00
1633 { 15, 6, 0x35 }, // 1101 01
1634 { 16, 6, 0x2A }, // 1010 10
1635 { 17, 6, 0x2B }, // 1010 11
1636 { 18, 7, 0x27 }, // 0100 111
1637 { 19, 7, 0xC }, // 0001 100
1638 { 20, 7, 0x8 }, // 0001 000
1639 { 21, 7, 0x17 }, // 0010 111
1640 { 22, 7, 0x3 }, // 0000 011
1641 { 23, 7, 0x4 }, // 0000 100
1642 { 24, 7, 0x28 }, // 0101 000
1643 { 25, 7, 0x2B }, // 0101 011
1644 { 26, 7, 0x13 }, // 0010 011
1645 { 27, 7, 0x24 }, // 0100 100
1646 { 28, 7, 0x18 }, // 0011 000
1647 { 29, 8, 0x2 }, // 0000 0010
1648 { 30, 8, 0x3 }, // 0000 0011
1649 { 31, 8, 0x1A }, // 0001 1010
1650 { 32, 8, 0x1B }, // 0001 1011
1651 { 33, 8, 0x12 }, // 0001 0010
1652 { 34, 8, 0x13 }, // 0001 0011
1653 { 35, 8, 0x14 }, // 0001 0100
1654 { 36, 8, 0x15 }, // 0001 0101
1655 { 37, 8, 0x16 }, // 0001 0110
1656 { 38, 8, 0x17 }, // 0001 0111
1657 { 39, 8, 0x28 }, // 0010 1000
1658 { 40, 8, 0x29 }, // 0010 1001
1659 { 41, 8, 0x2A }, // 0010 1010
1660 { 42, 8, 0x2B }, // 0010 1011
1661 { 43, 8, 0x2C }, // 0010 1100
1662 { 44, 8, 0x2D }, // 0010 1101
1663 { 45, 8, 0x4 }, // 0000 0100
1664 { 46, 8, 0x5 }, // 0000 0101
1665 { 47, 8, 0xA }, // 0000 1010
1666 { 48, 8, 0xB }, // 0000 1011
1667 { 49, 8, 0x52 }, // 0101 0010
1668 { 50, 8, 0x53 }, // 0101 0011
1669 { 51, 8, 0x54 }, // 0101 0100
1670 { 52, 8, 0x55 }, // 0101 0101
1671 { 53, 8, 0x24 }, // 0010 0100
1672 { 54, 8, 0x25 }, // 0010 0101
1673 { 55, 8, 0x58 }, // 0101 1000
1674 { 56, 8, 0x59 }, // 0101 1001
1675 { 57, 8, 0x5A }, // 0101 1010
1676 { 58, 8, 0x5B }, // 0101 1011
1677 { 59, 8, 0x4A }, // 0100 1010
1678 { 60, 8, 0x4B }, // 0100 1011
1679 { 61, 8, 0x32 }, // 0011 0010
1680 { 62, 8, 0x33 }, // 0011 0011
1681 { 63, 8, 0x34 }, // 0011 0100
1682 { 64, 5, 0x1B }, // 1101 1
1683 { 128, 5, 0x12 }, // 1001 0
1684 { 192, 6, 0x17 }, // 0101 11
1685 { 256, 7, 0x37 }, // 0110 111
1686 { 320, 8, 0x36 }, // 0011 0110
1687 { 384, 8, 0x37 }, // 0011 0111
1688 { 448, 8, 0x64 }, // 0110 0100
1689 { 512, 8, 0x65 }, // 0110 0101
1690 { 576, 8, 0x68 }, // 0110 1000
1691 { 640, 8, 0x67 }, // 0110 0111
1692 { 704, 9, 0xCC }, // 0110 0110 0
1693 { 768, 9, 0xCD }, // 0110 0110 1
1694 { 832, 9, 0xD2 }, // 0110 1001 0
1695 { 896, 9, 0xD3 }, // 0110 1001 1
1696 { 960, 9, 0xD4 }, // 0110 1010 0
1697 { 1024, 9, 0xD5 }, // 0110 1010 1
1698 { 1088, 9, 0xD6 }, // 0110 1011 0
1699 { 1152, 9, 0xD7 }, // 0110 1011 1
1700 { 1216, 9, 0xD8 }, // 0110 1100 0
1701 { 1280, 9, 0xD9 }, // 0110 1100 1
1702 { 1344, 9, 0xDA }, // 0110 1101 0
1703 { 1408, 9, 0xDB }, // 0110 1101 1
1704 { 1472, 9, 0x98 }, // 0100 1100 0
1705 { 1536, 9, 0x99 }, // 0100 1100 1
1706 { 1600, 9, 0x9A }, // 0100 1101 0
1707 { 1664, 6, 0x18 }, // 0110 00
1708 { 1728, 9, 0x9B }, // 0100 1101 1
1709 { 1792, 11, 0x8 }, // 0000 0001 000
1710 { 1856, 11, 0xC }, // 0000 0001 100
1711 { 1920, 11, 0xD }, // 0000 0001 101
1712 { 1984, 12, 0x12 }, // 0000 0001 0010
1713 { 2048, 12, 0x13 }, // 0000 0001 0011
1714 { 2112, 12, 0x14 }, // 0000 0001 0100
1715 { 2176, 12, 0x15 }, // 0000 0001 0101
1716 { 2240, 12, 0x16 }, // 0000 0001 0110
1717 { 2304, 12, 0x17 }, // 0000 0001 0111
1718 { 2368, 12, 0x1C }, // 0000 0001 1100
1719 { 2432, 12, 0x1D }, // 0000 0001 1101
1720 { 2496, 12, 0x1E }, // 0000 0001 1110
1721 { 2560, 12, 0x1F } // 0000 0001 1111
1724 const PixelCode BlackPixelCodes[] =
1726 { 0, 10, 0x37 }, // 0000 1101 11
1727 { 1, 3, 0x2 }, // 010
1728 { 2, 2, 0x3 }, // 11
1729 { 3, 2, 0x2 }, // 10
1730 { 4, 3, 0x3 }, // 011
1731 { 5, 4, 0x3 }, // 0011
1732 { 6, 4, 0x2 }, // 0010
1733 { 7, 5, 0x3 }, // 0001 1
1734 { 8, 6, 0x5 }, // 0001 01
1735 { 9, 6, 0x4 }, // 0001 00
1736 { 10, 7, 0x4 }, // 0000 100
1737 { 11, 7, 0x5 }, // 0000 101
1738 { 12, 7, 0x7 }, // 0000 111
1739 { 13, 8, 0x4 }, // 0000 0100
1740 { 14, 8, 0x7 }, // 0000 0111
1741 { 15, 9, 0x18 }, // 0000 1100 0
1742 { 16, 10, 0x17 }, // 0000 0101 11
1743 { 17, 10, 0x18 }, // 0000 0110 00
1744 { 18, 10, 0x8 }, // 0000 0010 00
1745 { 19, 11, 0x67 }, // 0000 1100 111
1746 { 20, 11, 0x68 }, // 0000 1101 000
1747 { 21, 11, 0x6C }, // 0000 1101 100
1748 { 22, 11, 0x37 }, // 0000 0110 111
1749 { 23, 11, 0x28 }, // 0000 0101 000
1750 { 24, 11, 0x17 }, // 0000 0010 111
1751 { 25, 11, 0x18 }, // 0000 0011 000
1752 { 26, 12, 0xCA }, // 0000 1100 1010
1753 { 27, 12, 0xCB }, // 0000 1100 1011
1754 { 28, 12, 0xCC }, // 0000 1100 1100
1755 { 29, 12, 0xCD }, // 0000 1100 1101
1756 { 30, 12, 0x68 }, // 0000 0110 1000
1757 { 31, 12, 0x69 }, // 0000 0110 1001
1758 { 32, 12, 0x6A }, // 0000 0110 1010
1759 { 33, 12, 0x6B }, // 0000 0110 1011
1760 { 34, 12, 0xD2 }, // 0000 1101 0010
1761 { 35, 12, 0xD3 }, // 0000 1101 0011
1762 { 36, 12, 0xD4 }, // 0000 1101 0100
1763 { 37, 12, 0xD5 }, // 0000 1101 0101
1764 { 38, 12, 0xD6 }, // 0000 1101 0110
1765 { 39, 12, 0xD7 }, // 0000 1101 0111
1766 { 40, 12, 0x6C }, // 0000 0110 1100
1767 { 41, 12, 0x6D }, // 0000 0110 1101
1768 { 42, 12, 0xDA }, // 0000 1101 1010
1769 { 43, 12, 0xDB }, // 0000 1101 1011
1770 { 44, 12, 0x54 }, // 0000 0101 0100
1771 { 45, 12, 0x55 }, // 0000 0101 0101
1772 { 46, 12, 0x56 }, // 0000 0101 0110
1773 { 47, 12, 0x57 }, // 0000 0101 0111
1774 { 48, 12, 0x64 }, // 0000 0110 0100
1775 { 49, 12, 0x65 }, // 0000 0110 0101
1776 { 50, 12, 0x52 }, // 0000 0101 0010
1777 { 51, 12, 0x53 }, // 0000 0101 0011
1778 { 52, 12, 0x24 }, // 0000 0010 0100
1779 { 53, 12, 0x37 }, // 0000 0011 0111
1780 { 54, 12, 0x38 }, // 0000 0011 1000
1781 { 55, 12, 0x27 }, // 0000 0010 0111
1782 { 56, 12, 0x28 }, // 0000 0010 1000
1783 { 57, 12, 0x58 }, // 0000 0101 1000
1784 { 58, 12, 0x59 }, // 0000 0101 1001
1785 { 59, 12, 0x2B }, // 0000 0010 1011
1786 { 60, 12, 0x2C }, // 0000 0010 1100
1787 { 61, 12, 0x5A }, // 0000 0101 1010
1788 { 62, 12, 0x66 }, // 0000 0110 0110
1789 { 63, 12, 0x67 }, // 0000 0110 0111
1790 { 64, 10, 0xF }, // 0000 0011 11
1791 { 128, 12, 0xC8 }, // 0000 1100 1000
1792 { 192, 12, 0xC9 }, // 0000 1100 1001
1793 { 256, 12, 0x5B }, // 0000 0101 1011
1794 { 320, 12, 0x33 }, // 0000 0011 0011
1795 { 384, 12, 0x34 }, // 0000 0011 0100
1796 { 448, 12, 0x35 }, // 0000 0011 0101
1797 { 512, 13, 0x6C }, // 0000 0011 0110 0
1798 { 576, 13, 0x6D }, // 0000 0011 0110 1
1799 { 640, 13, 0x4A }, // 0000 0010 0101 0
1800 { 704, 13, 0x4B }, // 0000 0010 0101 1
1801 { 768, 13, 0x4C }, // 0000 0010 0110 0
1802 { 832, 13, 0x4D }, // 0000 0010 0110 1
1803 { 896, 13, 0x72 }, // 0000 0011 1001 0
1804 { 960, 13, 0x73 }, // 0000 0011 1001 1
1805 { 1024, 13, 0x74 }, // 0000 0011 1010 0
1806 { 1088, 13, 0x75 }, // 0000 0011 1010 1
1807 { 1152, 13, 0x76 }, // 0000 0011 1011 0
1808 { 1216, 13, 0x77 }, // 0000 0011 1011 1
1809 { 1280, 13, 0x52 }, // 0000 0010 1001 0
1810 { 1344, 13, 0x53 }, // 0000 0010 1001 1
1811 { 1408, 13, 0x54 }, // 0000 0010 1010 0
1812 { 1472, 13, 0x55 }, // 0000 0010 1010 1
1813 { 1536, 13, 0x5A }, // 0000 0010 1101 0
1814 { 1600, 13, 0x5B }, // 0000 0010 1101 1
1815 { 1664, 13, 0x64 }, // 0000 0011 0010 0
1816 { 1728, 13, 0x65 }, // 0000 0011 0010 1
1817 { 1792, 11, 0x8 }, // 0000 0001 000
1818 { 1856, 11, 0xC }, // 0000 0001 100
1819 { 1920, 11, 0xD }, // 0000 0001 101
1820 { 1984, 12, 0x12 }, // 0000 0001 0010
1821 { 2048, 12, 0x13 }, // 0000 0001 0011
1822 { 2112, 12, 0x14 }, // 0000 0001 0100
1823 { 2176, 12, 0x15 }, // 0000 0001 0101
1824 { 2240, 12, 0x16 }, // 0000 0001 0110
1825 { 2304, 12, 0x17 }, // 0000 0001 0111
1826 { 2368, 12, 0x1C }, // 0000 0001 1100
1827 { 2432, 12, 0x1D }, // 0000 0001 1101
1828 { 2496, 12, 0x1E }, // 0000 0001 1110
1829 { 2560, 12, 0x1F } // 0000 0001 1111
1832 void PDFWriterImpl::putG4Span( tools::Long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState )
1834 const PixelCode* pTable = i_bWhitePixel ? WhitePixelCodes : BlackPixelCodes;
1835 // maximum encoded span is 2560 consecutive pixels
1836 while( i_nSpan > 2623 )
1838 // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1839 putG4Bits( pTable[103].mnCodeBits, pTable[103].mnCode, io_rState );
1840 i_nSpan -= pTable[103].mnEncodedPixels;
1842 // write multiples of 64 pixels up to 2560
1843 if( i_nSpan > 63 )
1845 sal_uInt32 nTabIndex = 63 + (i_nSpan >> 6);
1846 OSL_ASSERT( pTable[nTabIndex].mnEncodedPixels == static_cast<sal_uInt32>(64*(i_nSpan >> 6)) );
1847 putG4Bits( pTable[nTabIndex].mnCodeBits, pTable[nTabIndex].mnCode, io_rState );
1848 i_nSpan -= pTable[nTabIndex].mnEncodedPixels;
1850 putG4Bits( pTable[i_nSpan].mnCodeBits, pTable[i_nSpan].mnCode, io_rState );
1853 void PDFWriterImpl::writeG4Stream( BitmapReadAccess const * i_pBitmap )
1855 tools::Long nW = i_pBitmap->Width();
1856 tools::Long nH = i_pBitmap->Height();
1857 if( nW <= 0 || nH <= 0 )
1858 return;
1859 if( i_pBitmap->GetBitCount() != 1 )
1860 return;
1862 BitStreamState aBitState;
1864 // the first reference line is virtual and completely empty
1865 std::unique_ptr<sal_uInt8[]> pFirstRefLine(new sal_uInt8[nW/8 + 1]);
1866 memset(pFirstRefLine.get(), 0, nW/8 + 1);
1867 Scanline pRefLine = pFirstRefLine.get();
1868 for( tools::Long nY = 0; nY < nH; nY++ )
1870 const Scanline pCurLine = i_pBitmap->GetScanline( nY );
1871 tools::Long nLineIndex = 0;
1872 bool bRunSet = (*pCurLine & 0x80) != 0;
1873 bool bRefSet = (*pRefLine & 0x80) != 0;
1874 tools::Long nRunIndex1 = bRunSet ? 0 : findBitRun( pCurLine, 0, nW, bRunSet );
1875 tools::Long nRefIndex1 = bRefSet ? 0 : findBitRun( pRefLine, 0, nW, bRefSet );
1876 for( ; nLineIndex < nW; )
1878 tools::Long nRefIndex2 = findBitRun( pRefLine, nRefIndex1, nW );
1879 if( nRefIndex2 >= nRunIndex1 )
1881 tools::Long nDiff = nRefIndex1 - nRunIndex1;
1882 if( -3 <= nDiff && nDiff <= 3 )
1883 { // vertical coding
1884 static const struct
1886 sal_uInt32 mnCodeBits;
1887 sal_uInt32 mnCode;
1888 } VerticalCodes[7] = {
1889 { 7, 0x03 }, // 0000 011
1890 { 6, 0x03 }, // 0000 11
1891 { 3, 0x03 }, // 011
1892 { 1, 0x1 }, // 1
1893 { 3, 0x2 }, // 010
1894 { 6, 0x02 }, // 0000 10
1895 { 7, 0x02 } // 0000 010
1897 // convert to index
1898 nDiff += 3;
1900 // emit diff code
1901 putG4Bits( VerticalCodes[nDiff].mnCodeBits, VerticalCodes[nDiff].mnCode, aBitState );
1902 nLineIndex = nRunIndex1;
1904 else
1905 { // difference too large, horizontal coding
1906 // emit horz code 001
1907 putG4Bits( 3, 0x1, aBitState );
1908 tools::Long nRunIndex2 = findBitRun( pCurLine, nRunIndex1, nW );
1909 bool bWhiteFirst = ( nLineIndex + nRunIndex1 == 0 || ! isSet( pCurLine, nLineIndex ) );
1910 putG4Span( nRunIndex1 - nLineIndex, bWhiteFirst, aBitState );
1911 putG4Span( nRunIndex2 - nRunIndex1, ! bWhiteFirst, aBitState );
1912 nLineIndex = nRunIndex2;
1915 else
1916 { // emit pass code 0001
1917 putG4Bits( 4, 0x1, aBitState );
1918 nLineIndex = nRefIndex2;
1920 if( nLineIndex < nW )
1922 bool bSet = isSet( pCurLine, nLineIndex );
1923 nRunIndex1 = findBitRun( pCurLine, nLineIndex, nW, bSet );
1924 nRefIndex1 = findBitRun( pRefLine, nLineIndex, nW, ! bSet );
1925 nRefIndex1 = findBitRun( pRefLine, nRefIndex1, nW, bSet );
1929 // the current line is the reference for the next line
1930 pRefLine = pCurLine;
1932 // terminate strip with EOFB
1933 putG4Bits( 12, 1, aBitState );
1934 putG4Bits( 12, 1, aBitState );
1935 if( aBitState.mnNextBitPos != 8 )
1937 writeBuffer( &aBitState.getByte(), 1 );
1938 aBitState.flush();
1942 void PDFWriterImpl::DrawHatchLine_DrawLine(const Point& rStartPoint, const Point& rEndPoint)
1944 drawLine(rStartPoint, rEndPoint);
1947 static bool lcl_canUsePDFAxialShading(const Gradient& rGradient) {
1948 switch (rGradient.GetStyle())
1950 case GradientStyle::Linear:
1951 case GradientStyle::Axial:
1952 break;
1953 default:
1954 return false;
1957 // TODO: handle step count
1958 return rGradient.GetSteps() <= 0;
1961 void PDFWriterImpl::ImplClearFontData(bool bNewFontLists)
1963 VirtualDevice::ImplClearFontData(bNewFontLists);
1964 if (bNewFontLists && AcquireGraphics())
1966 ReleaseFontCollection();
1967 ReleaseFontCache();
1971 void PDFWriterImpl::ImplRefreshFontData(bool bNewFontLists)
1973 if (bNewFontLists && AcquireGraphics())
1975 SetFontCollectionFromSVData();
1976 ResetNewFontCache();
1980 vcl::Region PDFWriterImpl::ClipToDeviceBounds(vcl::Region aRegion) const
1982 return aRegion;
1986 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */