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