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