1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19 #include <pdf/pdfwriter_impl.hxx>
20 #include <pdf/EncryptionHashTransporter.hxx>
21
22 #include <vcl/pdfextoutdevdata.hxx>
23 #include <vcl/virdev.hxx>
24 #include <vcl/gdimtf.hxx>
25 #include <vcl/metaact.hxx>
26 #include <vcl/BitmapReadAccess.hxx>
27 #include <vcl/graph.hxx>
28 #include <pdf/IPDFEncryptor.hxx>
29
30 #include <unotools/streamwrap.hxx>
31
32 #include <tools/helpers.hxx>
33 #include <tools/fract.hxx>
34 #include <tools/stream.hxx>
35
36 #include <comphelper/fileformat.h>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/propertyvalue.hxx>
39
40 #include <com/sun/star/beans/PropertyValue.hpp>
41 #include <com/sun/star/io/XSeekable.hpp>
42 #include <com/sun/star/graphic/GraphicProvider.hpp>
43 #include <com/sun/star/graphic/XGraphicProvider.hpp>
44 #include <com/sun/star/beans/XMaterialHolder.hpp>
45
46 #include <o3tl/unit_conversion.hxx>
47 #include <osl/diagnose.h>
48 #include <vcl/skia/SkiaHelper.hxx>
49
50 #include <sal/log.hxx>
51 #include <memory>
52
53 using namespace vcl;
54 using namespace com::sun::star;
55 using namespace com::sun::star::uno;
56 using namespace com::sun::star::beans;
57
58 static bool lcl_canUsePDFAxialShading(const Gradient& rGradient);
59
implWriteGradient(const tools::PolyPolygon & i_rPolyPoly,const Gradient & i_rGradient,VirtualDevice * i_pDummyVDev,const vcl::PDFWriter::PlayMetafileContext & i_rContext)60 void PDFWriterImpl::implWriteGradient( const tools::PolyPolygon& i_rPolyPoly, const Gradient& i_rGradient,
61 VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
62 {
63 GDIMetaFile aTmpMtf;
64 Gradient aGradient(i_rGradient);
65
66 aGradient.AddGradientActions( i_rPolyPoly.GetBoundRect(), aTmpMtf );
67
68 m_rOuterFace.Push();
69 m_rOuterFace.IntersectClipRegion( i_rPolyPoly.getB2DPolyPolygon() );
70 playMetafile( aTmpMtf, nullptr, i_rContext, i_pDummyVDev );
71 m_rOuterFace.Pop();
72 }
73
implWriteBitmapEx(const Point & i_rPoint,const Size & i_rSize,const BitmapEx & i_rBitmapEx,const Graphic & i_Graphic,VirtualDevice const * i_pDummyVDev,const vcl::PDFWriter::PlayMetafileContext & i_rContext)74 void PDFWriterImpl::implWriteBitmapEx( const Point& i_rPoint, const Size& i_rSize, const BitmapEx& i_rBitmapEx, const Graphic& i_Graphic,
75 VirtualDevice const * i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext )
76 {
77 if ( i_rBitmapEx.IsEmpty() || !i_rSize.Width() || !i_rSize.Height() )
78 return;
79
80 BitmapEx aBitmapEx( i_rBitmapEx );
81 Point aPoint( i_rPoint );
82 Size aSize( i_rSize );
83
84 // #i19065# Negative sizes have mirror semantics on
85 // OutputDevice. BitmapEx and co. have no idea about that, so
86 // perform that _before_ doing anything with aBitmapEx.
87 BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE);
88 if( aSize.Width() < 0 )
89 {
90 aSize.setWidth( aSize.Width() * -1 );
91 aPoint.AdjustX( -(aSize.Width()) );
92 nMirrorFlags |= BmpMirrorFlags::Horizontal;
93 }
94 if( aSize.Height() < 0 )
95 {
96 aSize.setHeight( aSize.Height() * -1 );
97 aPoint.AdjustY( -(aSize.Height()) );
98 nMirrorFlags |= BmpMirrorFlags::Vertical;
99 }
100
101 if( nMirrorFlags != BmpMirrorFlags::NONE )
102 {
103 aBitmapEx.Mirror( nMirrorFlags );
104 }
105
106 bool bIsJpeg = false, bIsPng = false;
107 if( i_Graphic.GetType() != GraphicType::NONE && i_Graphic.GetBitmapEx() == aBitmapEx )
108 {
109 GfxLinkType eType = i_Graphic.GetGfxLink().GetType();
110 bIsJpeg = (eType == GfxLinkType::NativeJpg);
111 bIsPng = (eType == GfxLinkType::NativePng);
112 }
113
114 // Do not downsample images smaller than 50x50px.
115 const Size aBmpSize(aBitmapEx.GetSizePixel());
116 if (i_rContext.m_nMaxImageResolution > 50 && aBmpSize.getWidth() > 50
117 && aBmpSize.getHeight() > 50)
118 {
119 // do downsampling if necessary
120 const Size aDstSizeTwip( i_pDummyVDev->PixelToLogic(i_pDummyVDev->LogicToPixel(aSize), MapMode(MapUnit::MapTwip)) );
121 const double fBmpPixelX = aBmpSize.Width();
122 const double fBmpPixelY = aBmpSize.Height();
123 const double fMaxPixelX
124 = o3tl::convert<double>(aDstSizeTwip.Width(), o3tl::Length::twip, o3tl::Length::in)
125 * i_rContext.m_nMaxImageResolution;
126 const double fMaxPixelY
127 = o3tl::convert<double>(aDstSizeTwip.Height(), o3tl::Length::twip, o3tl::Length::in)
128 * i_rContext.m_nMaxImageResolution;
129
130 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
131 if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) ||
132 ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) &&
133 ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) )
134 {
135 // do scaling
136 Size aNewBmpSize;
137 const double fBmpWH = fBmpPixelX / fBmpPixelY;
138 const double fMaxWH = fMaxPixelX / fMaxPixelY;
139
140 if( fBmpWH < fMaxWH )
141 {
142 aNewBmpSize.setWidth(basegfx::fround<tools::Long>(fMaxPixelY * fBmpWH));
143 aNewBmpSize.setHeight(basegfx::fround<tools::Long>(fMaxPixelY));
144 }
145 else if( fBmpWH > 0.0 )
146 {
147 aNewBmpSize.setWidth(basegfx::fround<tools::Long>(fMaxPixelX));
148 aNewBmpSize.setHeight(basegfx::fround<tools::Long>(fMaxPixelX / fBmpWH));
149 }
150
151 if( aNewBmpSize.Width() && aNewBmpSize.Height() )
152 {
153 // #i121233# Use best quality for PDF exports
154 aBitmapEx.Scale( aNewBmpSize, BmpScaleFlag::BestQuality );
155 }
156 else
157 {
158 aBitmapEx.SetEmpty();
159 }
160 }
161 }
162
163 const Size aSizePixel( aBitmapEx.GetSizePixel() );
164 if ( !(aSizePixel.Width() && aSizePixel.Height()) )
165 return;
166
167 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
168 aBitmapEx.Convert(BmpConversion::N8BitGreys);
169 bool bUseJPGCompression = !i_rContext.m_bOnlyLosslessCompression;
170 if ( bIsPng || ( aSizePixel.Width() < 32 ) || ( aSizePixel.Height() < 32 ) )
171 bUseJPGCompression = false;
172
173 auto pStrm=std::make_shared<SvMemoryStream>();
174 AlphaMask aAlphaMask;
175
176 bool bTrueColorJPG = true;
177 if ( bUseJPGCompression )
178 {
179 // TODO this checks could be done much earlier, saving us
180 // from trying conversion & stores before...
181 if ( !aBitmapEx.IsAlpha() )
182 {
183 const auto aCacheEntry=m_aPDFBmpCache.find(
184 aBitmapEx.GetChecksum());
185 if ( aCacheEntry != m_aPDFBmpCache.end() )
186 {
187 m_rOuterFace.DrawJPGBitmap( *aCacheEntry->second, true, aSizePixel,
188 tools::Rectangle( aPoint, aSize ), aAlphaMask, i_Graphic );
189 return;
190 }
191 }
192 sal_uInt32 nZippedFileSize = 0; // sj: we will calculate the filesize of a zipped bitmap
193 if ( !bIsJpeg ) // to determine if jpeg compression is useful
194 {
195 SvMemoryStream aTemp;
196 aTemp.SetCompressMode( aTemp.GetCompressMode() | SvStreamCompressFlags::ZBITMAP );
197 aTemp.SetVersion( SOFFICE_FILEFORMAT_40 ); // sj: up from version 40 our bitmap stream operator
198 WriteDIBBitmapEx(aBitmapEx, aTemp); // is capable of zlib stream compression
199 nZippedFileSize = aTemp.TellEnd();
200 }
201 if ( aBitmapEx.IsAlpha() )
202 aAlphaMask = aBitmapEx.GetAlphaMask();
203 Graphic aGraphic(BitmapEx(aBitmapEx.GetBitmap()));
204
205 Sequence< PropertyValue > aFilterData{
206 comphelper::makePropertyValue(u"Quality"_ustr, sal_Int32(i_rContext.m_nJPEGQuality)),
207 comphelper::makePropertyValue(u"ColorMode"_ustr, sal_Int32(0))
208 };
209
210 try
211 {
212 uno::Reference < io::XStream > xStream = new utl::OStreamWrapper( *pStrm );
213 uno::Reference< io::XSeekable > xSeekable( xStream, UNO_QUERY_THROW );
214 const uno::Reference< uno::XComponentContext >& xContext( comphelper::getProcessComponentContext() );
215 uno::Reference< graphic::XGraphicProvider > xGraphicProvider( graphic::GraphicProvider::create(xContext) );
216 uno::Reference< graphic::XGraphic > xGraphic( aGraphic.GetXGraphic() );
217 uno::Reference < io::XOutputStream > xOut( xStream->getOutputStream() );
218 uno::Sequence< beans::PropertyValue > aOutMediaProperties{
219 comphelper::makePropertyValue(u"OutputStream"_ustr, xOut),
220 comphelper::makePropertyValue(u"MimeType"_ustr, u"image/jpeg"_ustr),
221 comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData)
222 };
223 xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties );
224 xOut->flush();
225 if ( !bIsJpeg && xSeekable->getLength() > nZippedFileSize )
226 {
227 bUseJPGCompression = false;
228 }
229 else
230 {
231 pStrm->Seek( STREAM_SEEK_TO_END );
232
233 xSeekable->seek( 0 );
234 Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(u"InputStream"_ustr,
235 xStream) };
236 uno::Reference< XPropertySet > xPropSet( xGraphicProvider->queryGraphicDescriptor( aArgs ) );
237 if ( xPropSet.is() )
238 {
239 sal_Int16 nBitsPerPixel = 24;
240 if ( xPropSet->getPropertyValue(u"BitsPerPixel"_ustr) >>= nBitsPerPixel )
241 {
242 bTrueColorJPG = nBitsPerPixel != 8;
243 }
244 }
245 }
246 }
247 catch( uno::Exception& )
248 {
249 bUseJPGCompression = false;
250 }
251 }
252 if ( bUseJPGCompression )
253 {
254 m_rOuterFace.DrawJPGBitmap( *pStrm, bTrueColorJPG, aSizePixel, tools::Rectangle( aPoint, aSize ), aAlphaMask, i_Graphic );
255 if (!aBitmapEx.IsAlpha() && bTrueColorJPG)
256 {
257 // Cache last jpeg export
258 m_aPDFBmpCache.insert(
259 {aBitmapEx.GetChecksum(), pStrm});
260 }
261 }
262 else if ( aBitmapEx.IsAlpha() )
263 m_rOuterFace.DrawBitmapEx( aPoint, aSize, Bitmap(aBitmapEx) );
264 else
265 m_rOuterFace.DrawBitmap( aPoint, aSize, aBitmapEx.GetBitmap(), i_Graphic );
266
267 }
268
playMetafile(const GDIMetaFile & i_rMtf,vcl::PDFExtOutDevData * i_pOutDevData,const vcl::PDFWriter::PlayMetafileContext & i_rContext,VirtualDevice * pDummyVDev)269 void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevData* i_pOutDevData, const vcl::PDFWriter::PlayMetafileContext& i_rContext, VirtualDevice* pDummyVDev )
270 {
271 bool bAssertionFired( false );
272
273 ScopedVclPtr<VirtualDevice> xPrivateDevice;
274 if( ! pDummyVDev )
275 {
276 xPrivateDevice.disposeAndReset(VclPtr<VirtualDevice>::Create());
277 pDummyVDev = xPrivateDevice.get();
278 pDummyVDev->EnableOutput( false );
279 pDummyVDev->SetMapMode( i_rMtf.GetPrefMapMode() );
280 }
281 const GDIMetaFile& aMtf( i_rMtf );
282
283 for( sal_uInt32 i = 0, nCount = aMtf.GetActionSize(); i < nCount; )
284 {
285 if ( !i_pOutDevData || !i_pOutDevData->PlaySyncPageAct( m_rOuterFace, i, aMtf ) )
286 {
287 const MetaAction* pAction = aMtf.GetAction( i );
288 const MetaActionType nType = pAction->GetType();
289
290 switch( nType )
291 {
292 case MetaActionType::PIXEL:
293 {
294 const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
295 m_rOuterFace.DrawPixel( pA->GetPoint(), pA->GetColor() );
296 }
297 break;
298
299 case MetaActionType::POINT:
300 {
301 const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
302 m_rOuterFace.DrawPixel( pA->GetPoint() );
303 }
304 break;
305
306 case MetaActionType::LINE:
307 {
308 const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
309 if ( pA->GetLineInfo().IsDefault() )
310 m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint() );
311 else
312 m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint(), pA->GetLineInfo() );
313 }
314 break;
315
316 case MetaActionType::RECT:
317 {
318 const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
319 m_rOuterFace.DrawRect( pA->GetRect() );
320 }
321 break;
322
323 case MetaActionType::ROUNDRECT:
324 {
325 const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
326 m_rOuterFace.DrawRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
327 }
328 break;
329
330 case MetaActionType::ELLIPSE:
331 {
332 const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
333 m_rOuterFace.DrawEllipse( pA->GetRect() );
334 }
335 break;
336
337 case MetaActionType::ARC:
338 {
339 const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
340 m_rOuterFace.DrawArc( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
341 }
342 break;
343
344 case MetaActionType::PIE:
345 {
346 const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
347 m_rOuterFace.DrawPie( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
348 }
349 break;
350
351 case MetaActionType::CHORD:
352 {
353 const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
354 m_rOuterFace.DrawChord( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
355 }
356 break;
357
358 case MetaActionType::POLYGON:
359 {
360 const MetaPolygonAction* pA = static_cast<const MetaPolygonAction*>(pAction);
361 m_rOuterFace.DrawPolygon( pA->GetPolygon() );
362 }
363 break;
364
365 case MetaActionType::POLYLINE:
366 {
367 const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
368 if ( pA->GetLineInfo().IsDefault() )
369 m_rOuterFace.DrawPolyLine( pA->GetPolygon() );
370 else
371 m_rOuterFace.DrawPolyLine( pA->GetPolygon(), pA->GetLineInfo() );
372 }
373 break;
374
375 case MetaActionType::POLYPOLYGON:
376 {
377 const MetaPolyPolygonAction* pA = static_cast<const MetaPolyPolygonAction*>(pAction);
378 m_rOuterFace.DrawPolyPolygon( pA->GetPolyPolygon() );
379 }
380 break;
381
382 case MetaActionType::GRADIENT:
383 {
384 const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
385 const Gradient& rGradient = pA->GetGradient();
386 if (lcl_canUsePDFAxialShading(rGradient))
387 {
388 m_rOuterFace.DrawGradient( pA->GetRect(), rGradient );
389 }
390 else
391 {
392 const tools::PolyPolygon aPolyPoly( pA->GetRect() );
393 implWriteGradient( aPolyPoly, rGradient, pDummyVDev, i_rContext );
394 }
395 }
396 break;
397
398 case MetaActionType::GRADIENTEX:
399 {
400 const MetaGradientExAction* pA = static_cast<const MetaGradientExAction*>(pAction);
401 const Gradient& rGradient = pA->GetGradient();
402
403 if (lcl_canUsePDFAxialShading(rGradient))
404 m_rOuterFace.DrawGradient( pA->GetPolyPolygon(), rGradient );
405 else
406 implWriteGradient( pA->GetPolyPolygon(), rGradient, pDummyVDev, i_rContext );
407 }
408 break;
409
410 case MetaActionType::HATCH:
411 {
412 const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pAction);
413 m_rOuterFace.DrawHatch( pA->GetPolyPolygon(), pA->GetHatch() );
414 }
415 break;
416
417 case MetaActionType::Transparent:
418 {
419 const MetaTransparentAction* pA = static_cast<const MetaTransparentAction*>(pAction);
420 m_rOuterFace.DrawTransparent( pA->GetPolyPolygon(), pA->GetTransparence() );
421 }
422 break;
423
424 case MetaActionType::FLOATTRANSPARENT:
425 {
426 const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
427
428 GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
429 const Point& rPos = pA->GetPoint();
430 const Size& rSize= pA->GetSize();
431 const Gradient& rTransparenceGradient = pA->GetGradient();
432
433 // special case constant alpha value
434 if( rTransparenceGradient.GetStartColor() == rTransparenceGradient.GetEndColor() )
435 {
436 const Color aTransCol( rTransparenceGradient.GetStartColor() );
437 const sal_uInt16 nTransPercent = aTransCol.GetLuminance() * 100 / 255;
438 m_rOuterFace.BeginTransparencyGroup();
439
440 // tdf#138826 adjust the aTmpMtf to start at rPos (see also #i112076#)
441 Point aMtfOrigin(aTmpMtf.GetPrefMapMode().GetOrigin());
442 if (rPos != aMtfOrigin)
443 aTmpMtf.Move(rPos.X() - aMtfOrigin.X(), rPos.Y() - aMtfOrigin.Y());
444
445 playMetafile( aTmpMtf, nullptr, i_rContext, pDummyVDev );
446 m_rOuterFace.EndTransparencyGroup( tools::Rectangle( rPos, rSize ), nTransPercent );
447 }
448 else
449 {
450 const Size aDstSizeTwip( pDummyVDev->PixelToLogic(pDummyVDev->LogicToPixel(rSize), MapMode(MapUnit::MapTwip)) );
451
452 // i#115962# Always use at least 300 DPI for bitmap conversion of transparence gradients,
453 // else the quality is not acceptable (see bugdoc as example)
454 sal_Int32 nMaxBmpDPI(300);
455
456 if( i_rContext.m_nMaxImageResolution > 50 )
457 {
458 if ( nMaxBmpDPI > i_rContext.m_nMaxImageResolution )
459 nMaxBmpDPI = i_rContext.m_nMaxImageResolution;
460 }
461 const sal_Int32 nPixelX = o3tl::convert<double>(aDstSizeTwip.Width(), o3tl::Length::twip, o3tl::Length::in) * nMaxBmpDPI;
462 const sal_Int32 nPixelY = o3tl::convert<double>(aDstSizeTwip.Height(), o3tl::Length::twip, o3tl::Length::in) * nMaxBmpDPI;
463 if ( nPixelX && nPixelY )
464 {
465 Size aDstSizePixel( nPixelX, nPixelY );
466 ScopedVclPtrInstance<VirtualDevice> xVDev(DeviceFormat::WITH_ALPHA);
467 if( xVDev->SetOutputSizePixel( aDstSizePixel, true, true ) )
468 {
469 Point aPoint;
470
471 MapMode aMapMode( pDummyVDev->GetMapMode() );
472 aMapMode.SetOrigin( aPoint );
473 xVDev->SetMapMode( aMapMode );
474 const bool bVDevOldMap = xVDev->IsMapModeEnabled();
475 Size aDstSize( xVDev->PixelToLogic( aDstSizePixel ) );
476
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 );
485
486 // create paint bitmap
487 aTmpMtf.WindStart();
488 aTmpMtf.Play(*xVDev, aPoint, aDstSize);
489 aTmpMtf.WindStart();
490 xVDev->EnableMapMode( false );
491 BitmapEx aPaint(xVDev->GetBitmap(aPoint, xVDev->GetOutputSizePixel()));
492 xVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here!
493
494 // create alpha mask from gradient
495 xVDev->SetDrawMode( DrawModeFlags::GrayGradient );
496 xVDev->DrawGradient( tools::Rectangle( aPoint, aDstSize ), rTransparenceGradient );
497 xVDev->SetDrawMode( DrawModeFlags::Default );
498 xVDev->EnableMapMode( false );
499
500 AlphaMask aAlpha(xVDev->GetBitmap(Point(), xVDev->GetOutputSizePixel()));
501 #if HAVE_FEATURE_SKIA
502 #if OSL_DEBUG_LEVEL > 0
503 // In release builds, we always invert
504 // regardless of whether Skia is enabled or not.
505 // But in debug builds, we can't invert when
506 // Skia is enabled.
507 if ( !SkiaHelper::isVCLSkiaEnabled() )
508 #endif
509 #endif
510 {
511 // When Skia is disabled, the alpha mask
512 // must be inverted a second time. To test
513 // this code, export the following
514 // document to PDF:
515 // https://bugs.documentfoundation.org/attachment.cgi?id=188084
516 aAlpha.Invert(); // convert to alpha
517 }
518 aAlpha.BlendWith(aPaint.GetAlphaMask());
519
520 xVDev.disposeAndClear();
521
522 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
523 implWriteBitmapEx( rPos, rSize, BitmapEx( aPaint.GetBitmap(), aAlpha ), aGraphic, pDummyVDev, i_rContext );
524 }
525 }
526 }
527 }
528 break;
529
530 case MetaActionType::EPS:
531 {
532 const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pAction);
533 const GDIMetaFile& aSubstitute( pA->GetSubstitute() );
534
535 m_rOuterFace.Push();
536 pDummyVDev->Push();
537
538 MapMode aMapMode( aSubstitute.GetPrefMapMode() );
539 Size aOutSize( OutputDevice::LogicToLogic( pA->GetSize(), pDummyVDev->GetMapMode(), aMapMode ) );
540 aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) );
541 aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) );
542 aMapMode.SetOrigin( OutputDevice::LogicToLogic( pA->GetPoint(), pDummyVDev->GetMapMode(), aMapMode ) );
543
544 m_rOuterFace.SetMapMode( aMapMode );
545 pDummyVDev->SetMapMode( aMapMode );
546 playMetafile( aSubstitute, nullptr, i_rContext, pDummyVDev );
547 pDummyVDev->Pop();
548 m_rOuterFace.Pop();
549 }
550 break;
551
552 case MetaActionType::COMMENT:
553 if( ! i_rContext.m_bTransparenciesWereRemoved )
554 {
555 const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
556
557 if( pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
558 {
559 const MetaGradientExAction* pGradAction = nullptr;
560 bool bDone = false;
561
562 while( !bDone && ( ++i < nCount ) )
563 {
564 pAction = aMtf.GetAction( i );
565
566 if( pAction->GetType() == MetaActionType::GRADIENTEX )
567 pGradAction = static_cast<const MetaGradientExAction*>(pAction);
568 else if( ( pAction->GetType() == MetaActionType::COMMENT ) &&
569 ( static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END")) )
570 {
571 bDone = true;
572 }
573 }
574
575 if( pGradAction )
576 {
577 if (lcl_canUsePDFAxialShading(pGradAction->GetGradient()))
578 {
579 m_rOuterFace.DrawGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient() );
580 }
581 else
582 {
583 implWriteGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), pDummyVDev, i_rContext );
584 }
585 }
586 }
587 else
588 {
589 const sal_uInt8* pData = pA->GetData();
590 if ( pData )
591 {
592 SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pData), pA->GetDataSize(), StreamMode::READ );
593 bool bSkipSequence = false;
594 OString sSeqEnd;
595
596 if( pA->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
597 {
598 sSeqEnd = "XPATHSTROKE_SEQ_END"_ostr;
599 SvtGraphicStroke aStroke;
600 ReadSvtGraphicStroke( aMemStm, aStroke );
601
602 tools::Polygon aPath;
603 aStroke.getPath( aPath );
604
605 tools::PolyPolygon aStartArrow;
606 tools::PolyPolygon aEndArrow;
607 double fTransparency( aStroke.getTransparency() );
608 double fStrokeWidth( aStroke.getStrokeWidth() );
609 SvtGraphicStroke::DashArray aDashArray;
610
611 aStroke.getStartArrow( aStartArrow );
612 aStroke.getEndArrow( aEndArrow );
613 aStroke.getDashArray( aDashArray );
614
615 bSkipSequence = true;
616 if ( aStartArrow.Count() || aEndArrow.Count() )
617 bSkipSequence = false;
618 if ( !aDashArray.empty() && ( fStrokeWidth != 0.0 ) && ( fTransparency == 0.0 ) )
619 bSkipSequence = false;
620 if ( bSkipSequence )
621 {
622 PDFWriter::ExtLineInfo aInfo;
623 aInfo.m_fLineWidth = fStrokeWidth;
624 aInfo.m_fTransparency = fTransparency;
625 aInfo.m_fMiterLimit = aStroke.getMiterLimit();
626 switch( aStroke.getCapType() )
627 {
628 default:
629 case SvtGraphicStroke::capButt: aInfo.m_eCap = PDFWriter::capButt;break;
630 case SvtGraphicStroke::capRound: aInfo.m_eCap = PDFWriter::capRound;break;
631 case SvtGraphicStroke::capSquare: aInfo.m_eCap = PDFWriter::capSquare;break;
632 }
633 switch( aStroke.getJoinType() )
634 {
635 default:
636 case SvtGraphicStroke::joinMiter: aInfo.m_eJoin = PDFWriter::joinMiter;break;
637 case SvtGraphicStroke::joinRound: aInfo.m_eJoin = PDFWriter::joinRound;break;
638 case SvtGraphicStroke::joinBevel: aInfo.m_eJoin = PDFWriter::joinBevel;break;
639 case SvtGraphicStroke::joinNone:
640 aInfo.m_eJoin = PDFWriter::joinMiter;
641 aInfo.m_fMiterLimit = 0.0;
642 break;
643 }
644 aInfo.m_aDashArray = std::move(aDashArray);
645
646 if(SvtGraphicStroke::joinNone == aStroke.getJoinType()
647 && fStrokeWidth > 0.0)
648 {
649 // emulate no edge rounding by handling single edges
650 const sal_uInt16 nPoints(aPath.GetSize());
651 const bool bCurve(aPath.HasFlags());
652
653 for(sal_uInt16 a(0); a + 1 < nPoints; a++)
654 {
655 if(bCurve
656 && PolyFlags::Normal != aPath.GetFlags(a + 1)
657 && a + 2 < nPoints
658 && PolyFlags::Normal != aPath.GetFlags(a + 2)
659 && a + 3 < nPoints)
660 {
661 const tools::Polygon aSnippet(4,
662 aPath.GetConstPointAry() + a,
663 aPath.GetConstFlagAry() + a);
664 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
665 a += 2;
666 }
667 else
668 {
669 const tools::Polygon aSnippet(2,
670 aPath.GetConstPointAry() + a);
671 m_rOuterFace.DrawPolyLine( aSnippet, aInfo );
672 }
673 }
674 }
675 else
676 {
677 m_rOuterFace.DrawPolyLine( aPath, aInfo );
678 }
679 }
680 }
681 else if ( pA->GetComment() == "XPATHFILL_SEQ_BEGIN" )
682 {
683 sSeqEnd = "XPATHFILL_SEQ_END"_ostr;
684 SvtGraphicFill aFill;
685 ReadSvtGraphicFill( aMemStm, aFill );
686
687 if ( ( aFill.getFillType() == SvtGraphicFill::fillSolid ) && ( aFill.getFillRule() == SvtGraphicFill::fillEvenOdd ) )
688 {
689 double fTransparency = aFill.getTransparency();
690 if ( fTransparency == 0.0 )
691 {
692 tools::PolyPolygon aPath;
693 aFill.getPath( aPath );
694
695 bSkipSequence = true;
696 m_rOuterFace.DrawPolyPolygon( aPath );
697 }
698 else if ( fTransparency == 1.0 )
699 bSkipSequence = true;
700 }
701 }
702 if ( bSkipSequence )
703 {
704 while( ++i < nCount )
705 {
706 pAction = aMtf.GetAction( i );
707 if ( pAction->GetType() == MetaActionType::COMMENT )
708 {
709 OString sComment( static_cast<const MetaCommentAction*>(pAction)->GetComment() );
710 if (sComment == sSeqEnd)
711 break;
712 }
713 // #i44496#
714 // the replacement action for stroke is a filled rectangle
715 // the set fillcolor of the replacement is part of the graphics
716 // state and must not be skipped
717 else if( pAction->GetType() == MetaActionType::FILLCOLOR )
718 {
719 const MetaFillColorAction* pMA = static_cast<const MetaFillColorAction*>(pAction);
720 if( pMA->IsSetting() )
721 m_rOuterFace.SetFillColor( pMA->GetColor() );
722 else
723 m_rOuterFace.SetFillColor();
724 }
725 }
726 }
727 }
728 }
729 }
730 break;
731
732 case MetaActionType::BMP:
733 {
734 const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
735 BitmapEx aBitmapEx( pA->GetBitmap() );
736 Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
737 aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
738 if( ! ( aSize.Width() && aSize.Height() ) )
739 aSize = pDummyVDev->PixelToLogic( aBitmapEx.GetSizePixel() );
740
741 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
742 implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, aGraphic, pDummyVDev, i_rContext );
743 }
744 break;
745
746 case MetaActionType::BMPSCALE:
747 {
748 const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
749 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
750 implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), BitmapEx( pA->GetBitmap() ), aGraphic, pDummyVDev, i_rContext );
751 }
752 break;
753
754 case MetaActionType::BMPSCALEPART:
755 {
756 const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
757 BitmapEx aBitmapEx( pA->GetBitmap() );
758 aBitmapEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
759 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
760 implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
761 }
762 break;
763
764 case MetaActionType::BMPEX:
765 {
766 const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction);
767 BitmapEx aBitmapEx( pA->GetBitmapEx() );
768 Size aSize( OutputDevice::LogicToLogic( aBitmapEx.GetPrefSize(),
769 aBitmapEx.GetPrefMapMode(), pDummyVDev->GetMapMode() ) );
770 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
771 implWriteBitmapEx( pA->GetPoint(), aSize, aBitmapEx, aGraphic, pDummyVDev, i_rContext );
772 }
773 break;
774
775 case MetaActionType::BMPEXSCALE:
776 {
777 const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
778 BitmapEx aBitmapEx( pA->GetBitmapEx() );
779 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
780 implWriteBitmapEx( pA->GetPoint(), pA->GetSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
781 }
782 break;
783
784 case MetaActionType::BMPEXSCALEPART:
785 {
786 const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
787
788 BitmapEx aBitmapEx( pA->GetBitmapEx() );
789 aBitmapEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
790 Graphic aGraphic = i_pOutDevData ? i_pOutDevData->GetCurrentGraphic() : Graphic();
791 implWriteBitmapEx( pA->GetDestPoint(), pA->GetDestSize(), aBitmapEx, aGraphic, pDummyVDev, i_rContext );
792 }
793 break;
794
795 case MetaActionType::MASK:
796 case MetaActionType::MASKSCALE:
797 case MetaActionType::MASKSCALEPART:
798 {
799 SAL_WARN( "vcl", "MetaMask...Action not supported yet" );
800 }
801 break;
802
803 case MetaActionType::TEXT:
804 {
805 const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
806 m_rOuterFace.DrawText( pA->GetPoint(), pA->GetText().copy( pA->GetIndex(), std::min<sal_Int32>(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) ) );
807 }
808 break;
809
810 case MetaActionType::TEXTRECT:
811 {
812 const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
813 m_rOuterFace.DrawText( pA->GetRect(), pA->GetText(), pA->GetStyle() );
814 }
815 break;
816
817 case MetaActionType::TEXTARRAY:
818 {
819 const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
820 m_rOuterFace.DrawTextArray(pA->GetPoint(), pA->GetText(), pA->GetDXArray(),
821 pA->GetKashidaArray(), pA->GetIndex(), pA->GetLen(),
822 pA->GetLayoutContextIndex(),
823 pA->GetLayoutContextLen());
824 }
825 break;
826
827 case MetaActionType::STRETCHTEXT:
828 {
829 const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
830 m_rOuterFace.DrawStretchText( pA->GetPoint(), pA->GetWidth(), pA->GetText(), pA->GetIndex(), pA->GetLen() );
831 }
832 break;
833
834 case MetaActionType::TEXTLINE:
835 {
836 const MetaTextLineAction* pA = static_cast<const MetaTextLineAction*>(pAction);
837 m_rOuterFace.DrawTextLine( pA->GetStartPoint(), pA->GetWidth(), pA->GetStrikeout(), pA->GetUnderline(), pA->GetOverline() );
838
839 }
840 break;
841
842 case MetaActionType::CLIPREGION:
843 {
844 const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
845
846 if( pA->IsClipping() )
847 {
848 if( pA->GetRegion().IsEmpty() )
849 m_rOuterFace.SetClipRegion( basegfx::B2DPolyPolygon() );
850 else
851 {
852 const vcl::Region& aReg( pA->GetRegion() );
853 m_rOuterFace.SetClipRegion( aReg.GetAsB2DPolyPolygon() );
854 }
855 }
856 else
857 m_rOuterFace.SetClipRegion();
858 }
859 break;
860
861 case MetaActionType::ISECTRECTCLIPREGION:
862 {
863 const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction);
864 m_rOuterFace.IntersectClipRegion( pA->GetRect() );
865 }
866 break;
867
868 case MetaActionType::ISECTREGIONCLIPREGION:
869 {
870 const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction);
871 const vcl::Region& aReg( pA->GetRegion() );
872 m_rOuterFace.IntersectClipRegion( aReg.GetAsB2DPolyPolygon() );
873 }
874 break;
875
876 case MetaActionType::MOVECLIPREGION:
877 {
878 const MetaMoveClipRegionAction* pA = static_cast<const MetaMoveClipRegionAction*>(pAction);
879 m_rOuterFace.MoveClipRegion( pA->GetHorzMove(), pA->GetVertMove() );
880 }
881 break;
882
883 case MetaActionType::MAPMODE:
884 {
885 const_cast< MetaAction* >( pAction )->Execute( pDummyVDev );
886 m_rOuterFace.SetMapMode( pDummyVDev->GetMapMode() );
887 }
888 break;
889
890 case MetaActionType::LINECOLOR:
891 {
892 const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction);
893
894 if( pA->IsSetting() )
895 m_rOuterFace.SetLineColor( pA->GetColor() );
896 else
897 m_rOuterFace.SetLineColor();
898 }
899 break;
900
901 case MetaActionType::FILLCOLOR:
902 {
903 const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction);
904
905 if( pA->IsSetting() )
906 m_rOuterFace.SetFillColor( pA->GetColor() );
907 else
908 m_rOuterFace.SetFillColor();
909 }
910 break;
911
912 case MetaActionType::TEXTLINECOLOR:
913 {
914 const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction);
915
916 if( pA->IsSetting() )
917 m_rOuterFace.SetTextLineColor( pA->GetColor() );
918 else
919 m_rOuterFace.SetTextLineColor();
920 }
921 break;
922
923 case MetaActionType::OVERLINECOLOR:
924 {
925 const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction);
926
927 if( pA->IsSetting() )
928 m_rOuterFace.SetOverlineColor( pA->GetColor() );
929 else
930 m_rOuterFace.SetOverlineColor();
931 }
932 break;
933
934 case MetaActionType::TEXTFILLCOLOR:
935 {
936 const MetaTextFillColorAction* pA = static_cast<const MetaTextFillColorAction*>(pAction);
937
938 if( pA->IsSetting() )
939 m_rOuterFace.SetTextFillColor( pA->GetColor() );
940 else
941 m_rOuterFace.SetTextFillColor();
942 }
943 break;
944
945 case MetaActionType::TEXTCOLOR:
946 {
947 const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pAction);
948 m_rOuterFace.SetTextColor( pA->GetColor() );
949 }
950 break;
951
952 case MetaActionType::TEXTALIGN:
953 {
954 const MetaTextAlignAction* pA = static_cast<const MetaTextAlignAction*>(pAction);
955 m_rOuterFace.SetTextAlign( pA->GetTextAlign() );
956 }
957 break;
958
959 case MetaActionType::FONT:
960 {
961 const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
962 m_rOuterFace.SetFont( pA->GetFont() );
963 }
964 break;
965
966 case MetaActionType::PUSH:
967 {
968 const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
969
970 pDummyVDev->Push( pA->GetFlags() );
971 m_rOuterFace.Push( pA->GetFlags() );
972 }
973 break;
974
975 case MetaActionType::POP:
976 {
977 pDummyVDev->Pop();
978 m_rOuterFace.Pop();
979 }
980 break;
981
982 case MetaActionType::LAYOUTMODE:
983 {
984 const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
985 m_rOuterFace.SetLayoutMode( pA->GetLayoutMode() );
986 }
987 break;
988
989 case MetaActionType::TEXTLANGUAGE:
990 {
991 const MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
992 m_rOuterFace.SetDigitLanguage( pA->GetTextLanguage() );
993 }
994 break;
995
996 case MetaActionType::WALLPAPER:
997 {
998 const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
999 m_rOuterFace.DrawWallpaper( pA->GetRect(), pA->GetWallpaper() );
1000 }
1001 break;
1002
1003 case MetaActionType::RASTEROP:
1004 case MetaActionType::REFPOINT:
1005 {
1006 // !!! >>> we don't want to support this actions
1007 }
1008 break;
1009
1010 default:
1011 // #i24604# Made assertion fire only once per
1012 // metafile. The asserted actions here are all
1013 // deprecated
1014 if( !bAssertionFired )
1015 {
1016 bAssertionFired = true;
1017 SAL_WARN( "vcl", "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered " << static_cast<int>(nType) );
1018 }
1019 break;
1020 }
1021 i++;
1022 }
1023 }
1024 }
1025
1026 // Encryption methods
1027
checkAndEnableStreamEncryption(sal_Int32 nObject)1028 void PDFWriterImpl::checkAndEnableStreamEncryption(sal_Int32 nObject)
1029 {
1030 if (!m_aContext.Encryption.canEncrypt() || !m_pPDFEncryptor)
1031 return;
1032
1033 m_pPDFEncryptor->enableStreamEncryption();
1034 m_pPDFEncryptor->setupEncryption(m_aContext.Encryption.EncryptionKey, nObject);
1035 }
1036
disableStreamEncryption()1037 void PDFWriterImpl::disableStreamEncryption()
1038 {
1039 if (m_pPDFEncryptor)
1040 m_pPDFEncryptor->disableStreamEncryption();
1041 }
1042
enableStringEncryption(sal_Int32 nObject)1043 void PDFWriterImpl::enableStringEncryption(sal_Int32 nObject)
1044 {
1045 if (!m_aContext.Encryption.canEncrypt() || !m_pPDFEncryptor)
1046 return;
1047
1048 m_pPDFEncryptor->setupEncryption(m_aContext.Encryption.EncryptionKey, nObject);
1049 }
1050
1051 const tools::Long unsetRun[256] =
1052 {
1053 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
1054 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */
1055 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */
1056 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */
1057 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1058 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */
1059 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1060 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */
1061 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
1062 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
1063 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
1064 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
1065 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
1066 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
1067 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
1068 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */
1069 };
1070
1071 const tools::Long setRun[256] =
1072 {
1073 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
1074 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1075 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
1076 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
1077 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
1078 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
1079 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */
1080 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
1081 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */
1082 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */
1083 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */
1084 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */
1085 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */
1086 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */
1087 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */
1088 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */
1089 };
1090
isSet(const Scanline i_pLine,tools::Long i_nIndex)1091 static bool isSet( const Scanline i_pLine, tools::Long i_nIndex )
1092 {
1093 return (i_pLine[ i_nIndex/8 ] & (0x80 >> (i_nIndex&7))) != 0;
1094 }
1095
findBitRunImpl(const Scanline i_pLine,tools::Long i_nStartIndex,tools::Long i_nW,bool i_bSet)1096 static tools::Long findBitRunImpl( const Scanline i_pLine, tools::Long i_nStartIndex, tools::Long i_nW, bool i_bSet )
1097 {
1098 tools::Long nIndex = i_nStartIndex;
1099 if( nIndex < i_nW )
1100 {
1101 const sal_uInt8 * pByte = i_pLine + (nIndex/8);
1102 sal_uInt8 nByte = *pByte;
1103
1104 // run up to byte boundary
1105 tools::Long nBitInByte = (nIndex & 7);
1106 if( nBitInByte )
1107 {
1108 sal_uInt8 nMask = 0x80 >> nBitInByte;
1109 while( nBitInByte != 8 )
1110 {
1111 if( (nByte & nMask) != (i_bSet ? nMask : 0) )
1112 return std::min(nIndex, i_nW);
1113 nMask = nMask >> 1;
1114 nBitInByte++;
1115 nIndex++;
1116 }
1117 if( nIndex < i_nW )
1118 {
1119 pByte++;
1120 nByte = *pByte;
1121 }
1122 }
1123
1124 sal_uInt8 nRunByte;
1125 const tools::Long* pRunTable;
1126 if( i_bSet )
1127 {
1128 nRunByte = 0xff;
1129 pRunTable = setRun;
1130 }
1131 else
1132 {
1133 nRunByte = 0;
1134 pRunTable = unsetRun;
1135 }
1136
1137 if( nIndex < i_nW )
1138 {
1139 while( nByte == nRunByte )
1140 {
1141 nIndex += 8;
1142
1143 if (nIndex >= i_nW)
1144 break;
1145
1146 pByte++;
1147 nByte = *pByte;
1148 }
1149 }
1150
1151 if( nIndex < i_nW )
1152 {
1153 nIndex += pRunTable[nByte];
1154 }
1155 }
1156 return std::min(nIndex, i_nW);
1157 }
1158
findBitRun(const Scanline i_pLine,tools::Long i_nStartIndex,tools::Long i_nW,bool i_bSet)1159 static tools::Long findBitRun(const Scanline i_pLine, tools::Long i_nStartIndex, tools::Long i_nW, bool i_bSet)
1160 {
1161 if (i_nStartIndex < 0)
1162 return i_nW;
1163
1164 return findBitRunImpl(i_pLine, i_nStartIndex, i_nW, i_bSet);
1165 }
1166
findBitRun(const Scanline i_pLine,tools::Long i_nStartIndex,tools::Long i_nW)1167 static tools::Long findBitRun(const Scanline i_pLine, tools::Long i_nStartIndex, tools::Long i_nW)
1168 {
1169 if (i_nStartIndex < 0)
1170 return i_nW;
1171
1172 const bool bSet = i_nStartIndex < i_nW && isSet(i_pLine, i_nStartIndex);
1173
1174 return findBitRunImpl(i_pLine, i_nStartIndex, i_nW, bSet);
1175 }
1176
1177 struct BitStreamState
1178 {
1179 sal_uInt8 mnBuffer;
1180 sal_uInt32 mnNextBitPos;
1181
BitStreamStateBitStreamState1182 BitStreamState()
1183 : mnBuffer( 0 )
1184 , mnNextBitPos( 8 )
1185 {
1186 }
1187
getByteBitStreamState1188 const sal_uInt8& getByte() const { return mnBuffer; }
flushBitStreamState1189 void flush() { mnNextBitPos = 8; mnBuffer = 0; }
1190 };
1191
putG4Bits(sal_uInt32 i_nLength,sal_uInt32 i_nCode,BitStreamState & io_rState)1192 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState )
1193 {
1194 while( i_nLength > io_rState.mnNextBitPos )
1195 {
1196 io_rState.mnBuffer |= static_cast<sal_uInt8>( i_nCode >> (i_nLength - io_rState.mnNextBitPos) );
1197 i_nLength -= io_rState.mnNextBitPos;
1198 (void)writeBufferBytes( &io_rState.getByte(), 1 );
1199 io_rState.flush();
1200 }
1201 assert(i_nLength < 9);
1202 static const unsigned int msbmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1203 io_rState.mnBuffer |= static_cast<sal_uInt8>( (i_nCode & msbmask[i_nLength]) << (io_rState.mnNextBitPos - i_nLength) );
1204 io_rState.mnNextBitPos -= i_nLength;
1205 if( io_rState.mnNextBitPos == 0 )
1206 {
1207 (void)writeBufferBytes( &io_rState.getByte(), 1 );
1208 io_rState.flush();
1209 }
1210 }
1211
1212 namespace {
1213
1214 struct PixelCode
1215 {
1216 sal_uInt32 mnEncodedPixels;
1217 sal_uInt32 mnCodeBits;
1218 sal_uInt32 mnCode;
1219 };
1220
1221 }
1222
1223 const PixelCode WhitePixelCodes[] =
1224 {
1225 { 0, 8, 0x35 }, // 0011 0101
1226 { 1, 6, 0x7 }, // 0001 11
1227 { 2, 4, 0x7 }, // 0111
1228 { 3, 4, 0x8 }, // 1000
1229 { 4, 4, 0xB }, // 1011
1230 { 5, 4, 0xC }, // 1100
1231 { 6, 4, 0xE }, // 1110
1232 { 7, 4, 0xF }, // 1111
1233 { 8, 5, 0x13 }, // 1001 1
1234 { 9, 5, 0x14 }, // 1010 0
1235 { 10, 5, 0x7 }, // 0011 1
1236 { 11, 5, 0x8 }, // 0100 0
1237 { 12, 6, 0x8 }, // 0010 00
1238 { 13, 6, 0x3 }, // 0000 11
1239 { 14, 6, 0x34 }, // 1101 00
1240 { 15, 6, 0x35 }, // 1101 01
1241 { 16, 6, 0x2A }, // 1010 10
1242 { 17, 6, 0x2B }, // 1010 11
1243 { 18, 7, 0x27 }, // 0100 111
1244 { 19, 7, 0xC }, // 0001 100
1245 { 20, 7, 0x8 }, // 0001 000
1246 { 21, 7, 0x17 }, // 0010 111
1247 { 22, 7, 0x3 }, // 0000 011
1248 { 23, 7, 0x4 }, // 0000 100
1249 { 24, 7, 0x28 }, // 0101 000
1250 { 25, 7, 0x2B }, // 0101 011
1251 { 26, 7, 0x13 }, // 0010 011
1252 { 27, 7, 0x24 }, // 0100 100
1253 { 28, 7, 0x18 }, // 0011 000
1254 { 29, 8, 0x2 }, // 0000 0010
1255 { 30, 8, 0x3 }, // 0000 0011
1256 { 31, 8, 0x1A }, // 0001 1010
1257 { 32, 8, 0x1B }, // 0001 1011
1258 { 33, 8, 0x12 }, // 0001 0010
1259 { 34, 8, 0x13 }, // 0001 0011
1260 { 35, 8, 0x14 }, // 0001 0100
1261 { 36, 8, 0x15 }, // 0001 0101
1262 { 37, 8, 0x16 }, // 0001 0110
1263 { 38, 8, 0x17 }, // 0001 0111
1264 { 39, 8, 0x28 }, // 0010 1000
1265 { 40, 8, 0x29 }, // 0010 1001
1266 { 41, 8, 0x2A }, // 0010 1010
1267 { 42, 8, 0x2B }, // 0010 1011
1268 { 43, 8, 0x2C }, // 0010 1100
1269 { 44, 8, 0x2D }, // 0010 1101
1270 { 45, 8, 0x4 }, // 0000 0100
1271 { 46, 8, 0x5 }, // 0000 0101
1272 { 47, 8, 0xA }, // 0000 1010
1273 { 48, 8, 0xB }, // 0000 1011
1274 { 49, 8, 0x52 }, // 0101 0010
1275 { 50, 8, 0x53 }, // 0101 0011
1276 { 51, 8, 0x54 }, // 0101 0100
1277 { 52, 8, 0x55 }, // 0101 0101
1278 { 53, 8, 0x24 }, // 0010 0100
1279 { 54, 8, 0x25 }, // 0010 0101
1280 { 55, 8, 0x58 }, // 0101 1000
1281 { 56, 8, 0x59 }, // 0101 1001
1282 { 57, 8, 0x5A }, // 0101 1010
1283 { 58, 8, 0x5B }, // 0101 1011
1284 { 59, 8, 0x4A }, // 0100 1010
1285 { 60, 8, 0x4B }, // 0100 1011
1286 { 61, 8, 0x32 }, // 0011 0010
1287 { 62, 8, 0x33 }, // 0011 0011
1288 { 63, 8, 0x34 }, // 0011 0100
1289 { 64, 5, 0x1B }, // 1101 1
1290 { 128, 5, 0x12 }, // 1001 0
1291 { 192, 6, 0x17 }, // 0101 11
1292 { 256, 7, 0x37 }, // 0110 111
1293 { 320, 8, 0x36 }, // 0011 0110
1294 { 384, 8, 0x37 }, // 0011 0111
1295 { 448, 8, 0x64 }, // 0110 0100
1296 { 512, 8, 0x65 }, // 0110 0101
1297 { 576, 8, 0x68 }, // 0110 1000
1298 { 640, 8, 0x67 }, // 0110 0111
1299 { 704, 9, 0xCC }, // 0110 0110 0
1300 { 768, 9, 0xCD }, // 0110 0110 1
1301 { 832, 9, 0xD2 }, // 0110 1001 0
1302 { 896, 9, 0xD3 }, // 0110 1001 1
1303 { 960, 9, 0xD4 }, // 0110 1010 0
1304 { 1024, 9, 0xD5 }, // 0110 1010 1
1305 { 1088, 9, 0xD6 }, // 0110 1011 0
1306 { 1152, 9, 0xD7 }, // 0110 1011 1
1307 { 1216, 9, 0xD8 }, // 0110 1100 0
1308 { 1280, 9, 0xD9 }, // 0110 1100 1
1309 { 1344, 9, 0xDA }, // 0110 1101 0
1310 { 1408, 9, 0xDB }, // 0110 1101 1
1311 { 1472, 9, 0x98 }, // 0100 1100 0
1312 { 1536, 9, 0x99 }, // 0100 1100 1
1313 { 1600, 9, 0x9A }, // 0100 1101 0
1314 { 1664, 6, 0x18 }, // 0110 00
1315 { 1728, 9, 0x9B }, // 0100 1101 1
1316 { 1792, 11, 0x8 }, // 0000 0001 000
1317 { 1856, 11, 0xC }, // 0000 0001 100
1318 { 1920, 11, 0xD }, // 0000 0001 101
1319 { 1984, 12, 0x12 }, // 0000 0001 0010
1320 { 2048, 12, 0x13 }, // 0000 0001 0011
1321 { 2112, 12, 0x14 }, // 0000 0001 0100
1322 { 2176, 12, 0x15 }, // 0000 0001 0101
1323 { 2240, 12, 0x16 }, // 0000 0001 0110
1324 { 2304, 12, 0x17 }, // 0000 0001 0111
1325 { 2368, 12, 0x1C }, // 0000 0001 1100
1326 { 2432, 12, 0x1D }, // 0000 0001 1101
1327 { 2496, 12, 0x1E }, // 0000 0001 1110
1328 { 2560, 12, 0x1F } // 0000 0001 1111
1329 };
1330
1331 const PixelCode BlackPixelCodes[] =
1332 {
1333 { 0, 10, 0x37 }, // 0000 1101 11
1334 { 1, 3, 0x2 }, // 010
1335 { 2, 2, 0x3 }, // 11
1336 { 3, 2, 0x2 }, // 10
1337 { 4, 3, 0x3 }, // 011
1338 { 5, 4, 0x3 }, // 0011
1339 { 6, 4, 0x2 }, // 0010
1340 { 7, 5, 0x3 }, // 0001 1
1341 { 8, 6, 0x5 }, // 0001 01
1342 { 9, 6, 0x4 }, // 0001 00
1343 { 10, 7, 0x4 }, // 0000 100
1344 { 11, 7, 0x5 }, // 0000 101
1345 { 12, 7, 0x7 }, // 0000 111
1346 { 13, 8, 0x4 }, // 0000 0100
1347 { 14, 8, 0x7 }, // 0000 0111
1348 { 15, 9, 0x18 }, // 0000 1100 0
1349 { 16, 10, 0x17 }, // 0000 0101 11
1350 { 17, 10, 0x18 }, // 0000 0110 00
1351 { 18, 10, 0x8 }, // 0000 0010 00
1352 { 19, 11, 0x67 }, // 0000 1100 111
1353 { 20, 11, 0x68 }, // 0000 1101 000
1354 { 21, 11, 0x6C }, // 0000 1101 100
1355 { 22, 11, 0x37 }, // 0000 0110 111
1356 { 23, 11, 0x28 }, // 0000 0101 000
1357 { 24, 11, 0x17 }, // 0000 0010 111
1358 { 25, 11, 0x18 }, // 0000 0011 000
1359 { 26, 12, 0xCA }, // 0000 1100 1010
1360 { 27, 12, 0xCB }, // 0000 1100 1011
1361 { 28, 12, 0xCC }, // 0000 1100 1100
1362 { 29, 12, 0xCD }, // 0000 1100 1101
1363 { 30, 12, 0x68 }, // 0000 0110 1000
1364 { 31, 12, 0x69 }, // 0000 0110 1001
1365 { 32, 12, 0x6A }, // 0000 0110 1010
1366 { 33, 12, 0x6B }, // 0000 0110 1011
1367 { 34, 12, 0xD2 }, // 0000 1101 0010
1368 { 35, 12, 0xD3 }, // 0000 1101 0011
1369 { 36, 12, 0xD4 }, // 0000 1101 0100
1370 { 37, 12, 0xD5 }, // 0000 1101 0101
1371 { 38, 12, 0xD6 }, // 0000 1101 0110
1372 { 39, 12, 0xD7 }, // 0000 1101 0111
1373 { 40, 12, 0x6C }, // 0000 0110 1100
1374 { 41, 12, 0x6D }, // 0000 0110 1101
1375 { 42, 12, 0xDA }, // 0000 1101 1010
1376 { 43, 12, 0xDB }, // 0000 1101 1011
1377 { 44, 12, 0x54 }, // 0000 0101 0100
1378 { 45, 12, 0x55 }, // 0000 0101 0101
1379 { 46, 12, 0x56 }, // 0000 0101 0110
1380 { 47, 12, 0x57 }, // 0000 0101 0111
1381 { 48, 12, 0x64 }, // 0000 0110 0100
1382 { 49, 12, 0x65 }, // 0000 0110 0101
1383 { 50, 12, 0x52 }, // 0000 0101 0010
1384 { 51, 12, 0x53 }, // 0000 0101 0011
1385 { 52, 12, 0x24 }, // 0000 0010 0100
1386 { 53, 12, 0x37 }, // 0000 0011 0111
1387 { 54, 12, 0x38 }, // 0000 0011 1000
1388 { 55, 12, 0x27 }, // 0000 0010 0111
1389 { 56, 12, 0x28 }, // 0000 0010 1000
1390 { 57, 12, 0x58 }, // 0000 0101 1000
1391 { 58, 12, 0x59 }, // 0000 0101 1001
1392 { 59, 12, 0x2B }, // 0000 0010 1011
1393 { 60, 12, 0x2C }, // 0000 0010 1100
1394 { 61, 12, 0x5A }, // 0000 0101 1010
1395 { 62, 12, 0x66 }, // 0000 0110 0110
1396 { 63, 12, 0x67 }, // 0000 0110 0111
1397 { 64, 10, 0xF }, // 0000 0011 11
1398 { 128, 12, 0xC8 }, // 0000 1100 1000
1399 { 192, 12, 0xC9 }, // 0000 1100 1001
1400 { 256, 12, 0x5B }, // 0000 0101 1011
1401 { 320, 12, 0x33 }, // 0000 0011 0011
1402 { 384, 12, 0x34 }, // 0000 0011 0100
1403 { 448, 12, 0x35 }, // 0000 0011 0101
1404 { 512, 13, 0x6C }, // 0000 0011 0110 0
1405 { 576, 13, 0x6D }, // 0000 0011 0110 1
1406 { 640, 13, 0x4A }, // 0000 0010 0101 0
1407 { 704, 13, 0x4B }, // 0000 0010 0101 1
1408 { 768, 13, 0x4C }, // 0000 0010 0110 0
1409 { 832, 13, 0x4D }, // 0000 0010 0110 1
1410 { 896, 13, 0x72 }, // 0000 0011 1001 0
1411 { 960, 13, 0x73 }, // 0000 0011 1001 1
1412 { 1024, 13, 0x74 }, // 0000 0011 1010 0
1413 { 1088, 13, 0x75 }, // 0000 0011 1010 1
1414 { 1152, 13, 0x76 }, // 0000 0011 1011 0
1415 { 1216, 13, 0x77 }, // 0000 0011 1011 1
1416 { 1280, 13, 0x52 }, // 0000 0010 1001 0
1417 { 1344, 13, 0x53 }, // 0000 0010 1001 1
1418 { 1408, 13, 0x54 }, // 0000 0010 1010 0
1419 { 1472, 13, 0x55 }, // 0000 0010 1010 1
1420 { 1536, 13, 0x5A }, // 0000 0010 1101 0
1421 { 1600, 13, 0x5B }, // 0000 0010 1101 1
1422 { 1664, 13, 0x64 }, // 0000 0011 0010 0
1423 { 1728, 13, 0x65 }, // 0000 0011 0010 1
1424 { 1792, 11, 0x8 }, // 0000 0001 000
1425 { 1856, 11, 0xC }, // 0000 0001 100
1426 { 1920, 11, 0xD }, // 0000 0001 101
1427 { 1984, 12, 0x12 }, // 0000 0001 0010
1428 { 2048, 12, 0x13 }, // 0000 0001 0011
1429 { 2112, 12, 0x14 }, // 0000 0001 0100
1430 { 2176, 12, 0x15 }, // 0000 0001 0101
1431 { 2240, 12, 0x16 }, // 0000 0001 0110
1432 { 2304, 12, 0x17 }, // 0000 0001 0111
1433 { 2368, 12, 0x1C }, // 0000 0001 1100
1434 { 2432, 12, 0x1D }, // 0000 0001 1101
1435 { 2496, 12, 0x1E }, // 0000 0001 1110
1436 { 2560, 12, 0x1F } // 0000 0001 1111
1437 };
1438
putG4Span(tools::Long i_nSpan,bool i_bWhitePixel,BitStreamState & io_rState)1439 void PDFWriterImpl::putG4Span( tools::Long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState )
1440 {
1441 const PixelCode* pTable = i_bWhitePixel ? WhitePixelCodes : BlackPixelCodes;
1442 // maximum encoded span is 2560 consecutive pixels
1443 while( i_nSpan > 2623 )
1444 {
1445 // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table
1446 putG4Bits( pTable[103].mnCodeBits, pTable[103].mnCode, io_rState );
1447 i_nSpan -= pTable[103].mnEncodedPixels;
1448 }
1449 // write multiples of 64 pixels up to 2560
1450 if( i_nSpan > 63 )
1451 {
1452 sal_uInt32 nTabIndex = 63 + (i_nSpan >> 6);
1453 OSL_ASSERT( pTable[nTabIndex].mnEncodedPixels == static_cast<sal_uInt32>(64*(i_nSpan >> 6)) );
1454 putG4Bits( pTable[nTabIndex].mnCodeBits, pTable[nTabIndex].mnCode, io_rState );
1455 i_nSpan -= pTable[nTabIndex].mnEncodedPixels;
1456 }
1457 putG4Bits( pTable[i_nSpan].mnCodeBits, pTable[i_nSpan].mnCode, io_rState );
1458 }
1459
writeG4Stream(BitmapReadAccess const * i_pBitmap)1460 void PDFWriterImpl::writeG4Stream( BitmapReadAccess const * i_pBitmap )
1461 {
1462 tools::Long nW = i_pBitmap->Width();
1463 tools::Long nH = i_pBitmap->Height();
1464 if( nW <= 0 || nH <= 0 )
1465 return;
1466 if( i_pBitmap->GetBitCount() != 1 )
1467 return;
1468
1469 BitStreamState aBitState;
1470
1471 // the first reference line is virtual and completely empty
1472 std::unique_ptr<sal_uInt8[]> pFirstRefLine(new sal_uInt8[nW/8 + 1]);
1473 memset(pFirstRefLine.get(), 0, nW/8 + 1);
1474 Scanline pRefLine = pFirstRefLine.get();
1475 for( tools::Long nY = 0; nY < nH; nY++ )
1476 {
1477 const Scanline pCurLine = i_pBitmap->GetScanline( nY );
1478 tools::Long nLineIndex = 0;
1479 bool bRunSet = (*pCurLine & 0x80) != 0;
1480 bool bRefSet = (*pRefLine & 0x80) != 0;
1481 tools::Long nRunIndex1 = bRunSet ? 0 : findBitRun( pCurLine, 0, nW, bRunSet );
1482 tools::Long nRefIndex1 = bRefSet ? 0 : findBitRun( pRefLine, 0, nW, bRefSet );
1483 for( ; nLineIndex < nW; )
1484 {
1485 tools::Long nRefIndex2 = findBitRun( pRefLine, nRefIndex1, nW );
1486 if( nRefIndex2 >= nRunIndex1 )
1487 {
1488 tools::Long nDiff = nRefIndex1 - nRunIndex1;
1489 if( -3 <= nDiff && nDiff <= 3 )
1490 { // vertical coding
1491 static const struct
1492 {
1493 sal_uInt32 mnCodeBits;
1494 sal_uInt32 mnCode;
1495 } VerticalCodes[7] = {
1496 { 7, 0x03 }, // 0000 011
1497 { 6, 0x03 }, // 0000 11
1498 { 3, 0x03 }, // 011
1499 { 1, 0x1 }, // 1
1500 { 3, 0x2 }, // 010
1501 { 6, 0x02 }, // 0000 10
1502 { 7, 0x02 } // 0000 010
1503 };
1504 // convert to index
1505 nDiff += 3;
1506
1507 // emit diff code
1508 putG4Bits( VerticalCodes[nDiff].mnCodeBits, VerticalCodes[nDiff].mnCode, aBitState );
1509 nLineIndex = nRunIndex1;
1510 }
1511 else
1512 { // difference too large, horizontal coding
1513 // emit horz code 001
1514 putG4Bits( 3, 0x1, aBitState );
1515 tools::Long nRunIndex2 = findBitRun( pCurLine, nRunIndex1, nW );
1516 bool bWhiteFirst = ( nLineIndex + nRunIndex1 == 0 || ! isSet( pCurLine, nLineIndex ) );
1517 putG4Span( nRunIndex1 - nLineIndex, bWhiteFirst, aBitState );
1518 putG4Span( nRunIndex2 - nRunIndex1, ! bWhiteFirst, aBitState );
1519 nLineIndex = nRunIndex2;
1520 }
1521 }
1522 else
1523 { // emit pass code 0001
1524 putG4Bits( 4, 0x1, aBitState );
1525 nLineIndex = nRefIndex2;
1526 }
1527 if( nLineIndex < nW )
1528 {
1529 bool bSet = isSet( pCurLine, nLineIndex );
1530 nRunIndex1 = findBitRun( pCurLine, nLineIndex, nW, bSet );
1531 nRefIndex1 = findBitRun( pRefLine, nLineIndex, nW, ! bSet );
1532 nRefIndex1 = findBitRun( pRefLine, nRefIndex1, nW, bSet );
1533 }
1534 }
1535
1536 // the current line is the reference for the next line
1537 pRefLine = pCurLine;
1538 }
1539 // terminate strip with EOFB
1540 putG4Bits( 12, 1, aBitState );
1541 putG4Bits( 12, 1, aBitState );
1542 if( aBitState.mnNextBitPos != 8 )
1543 {
1544 (void)writeBufferBytes( &aBitState.getByte(), 1 );
1545 aBitState.flush();
1546 }
1547 }
1548
DrawHatchLine_DrawLine(const Point & rStartPoint,const Point & rEndPoint)1549 void PDFWriterImpl::DrawHatchLine_DrawLine(const Point& rStartPoint, const Point& rEndPoint)
1550 {
1551 drawLine(rStartPoint, rEndPoint);
1552 }
1553
lcl_canUsePDFAxialShading(const Gradient & rGradient)1554 static bool lcl_canUsePDFAxialShading(const Gradient& rGradient) {
1555 switch (rGradient.GetStyle())
1556 {
1557 case css::awt::GradientStyle_LINEAR:
1558 case css::awt::GradientStyle_AXIAL:
1559 break;
1560 default:
1561 return false;
1562 }
1563
1564 // TODO: handle step count
1565 return rGradient.GetSteps() <= 0;
1566 }
1567
ImplClearFontData(bool bNewFontLists)1568 void PDFWriterImpl::ImplClearFontData(bool bNewFontLists)
1569 {
1570 VirtualDevice::ImplClearFontData(bNewFontLists);
1571 if (bNewFontLists && AcquireGraphics())
1572 {
1573 ReleaseFontCollection();
1574 ReleaseFontCache();
1575 }
1576 }
1577
ImplRefreshFontData(bool bNewFontLists)1578 void PDFWriterImpl::ImplRefreshFontData(bool bNewFontLists)
1579 {
1580 if (bNewFontLists && AcquireGraphics())
1581 {
1582 SetFontCollectionFromSVData();
1583 ResetNewFontCache();
1584 }
1585 }
1586
ClipToDeviceBounds(vcl::Region aRegion) const1587 vcl::Region PDFWriterImpl::ClipToDeviceBounds(vcl::Region aRegion) const
1588 {
1589 return aRegion;
1590 }
1591
1592
1593 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1594