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 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 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 aTemp << aBitmapEx; // 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 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: 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 1101 virtual ~EncHashTransporter() 1102 { 1103 sTransporters.erase( maID ); 1104 if( maUDigest ) 1105 rtl_digest_destroyMD5( maUDigest ); 1106 OSL_TRACE( "EncHashTransporter freed\n" ); 1107 } 1108 1109 rtlDigest getUDigest() const { return maUDigest; }; 1110 std::vector< sal_uInt8 >& getOValue() { return maOValue; } 1111 void invalidate() 1112 { 1113 if( maUDigest ) 1114 { 1115 rtl_digest_destroyMD5( maUDigest ); 1116 maUDigest = NULL; 1117 } 1118 } 1119 1120 // XMaterialHolder 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 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 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 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 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 */ 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 1684 BitStreamState() 1685 : mnBuffer( 0 ) 1686 , mnNextBitPos( 8 ) 1687 { 1688 } 1689 1690 const sal_uInt8* getByte() const { return &mnBuffer; } 1691 void flush() { mnNextBitPos = 8; mnBuffer = 0; } 1692 }; 1693 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 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 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