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 if( aNewBmpSize.Width() && aNewBmpSize.Height() ) 136 aBitmapEx.Scale( aNewBmpSize ); 137 else 138 aBitmapEx.SetEmpty(); 139 } 140 } 141 142 const Size aSizePixel( aBitmapEx.GetSizePixel() ); 143 if ( aSizePixel.Width() && aSizePixel.Height() ) 144 { 145 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 146 { 147 BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS; 148 int nDepth = aBitmapEx.GetBitmap().GetBitCount(); 149 if( nDepth <= 4 ) 150 eConv = BMP_CONVERSION_4BIT_GREYS; 151 if( nDepth > 1 ) 152 aBitmapEx.Convert( eConv ); 153 } 154 sal_Bool bUseJPGCompression = !i_rContext.m_bOnlyLosslessCompression; 155 if ( ( aSizePixel.Width() < 32 ) || ( aSizePixel.Height() < 32 ) ) 156 bUseJPGCompression = sal_False; 157 158 SvMemoryStream aStrm; 159 Bitmap aMask; 160 161 bool bTrueColorJPG = true; 162 if ( bUseJPGCompression ) 163 { 164 sal_uInt32 nZippedFileSize; // sj: we will calculate the filesize of a zipped bitmap 165 { // to determine if jpeg compression is usefull 166 SvMemoryStream aTemp; 167 aTemp.SetCompressMode( aTemp.GetCompressMode() | COMPRESSMODE_ZBITMAP ); 168 aTemp.SetVersion( SOFFICE_FILEFORMAT_40 ); // sj: up from version 40 our bitmap stream operator 169 aTemp << aBitmapEx; // is capable of zlib stream compression 170 aTemp.Seek( STREAM_SEEK_TO_END ); 171 nZippedFileSize = aTemp.Tell(); 172 } 173 if ( aBitmapEx.IsTransparent() ) 174 { 175 if ( aBitmapEx.IsAlpha() ) 176 aMask = aBitmapEx.GetAlpha().GetBitmap(); 177 else 178 aMask = aBitmapEx.GetMask(); 179 } 180 Graphic aGraphic( aBitmapEx.GetBitmap() ); 181 sal_Int32 nColorMode = 0; 182 183 Sequence< PropertyValue > aFilterData( 2 ); 184 aFilterData[ 0 ].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "Quality" ) ); 185 aFilterData[ 0 ].Value <<= sal_Int32(i_rContext.m_nJPEGQuality); 186 aFilterData[ 1 ].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "ColorMode" ) ); 187 aFilterData[ 1 ].Value <<= nColorMode; 188 189 try 190 { 191 uno::Reference < io::XStream > xStream = new utl::OStreamWrapper( aStrm ); 192 uno::Reference< io::XSeekable > xSeekable( xStream, UNO_QUERY_THROW ); 193 uno::Reference< graphic::XGraphicProvider > xGraphicProvider( ImplGetSVData()->maAppData.mxMSF->createInstance( 194 OUString::createFromAscii( "com.sun.star.graphic.GraphicProvider" ) ), UNO_QUERY ); 195 if ( xGraphicProvider.is() ) 196 { 197 uno::Reference< graphic::XGraphic > xGraphic( aGraphic.GetXGraphic() ); 198 uno::Reference < io::XOutputStream > xOut( xStream->getOutputStream() ); 199 rtl::OUString aMimeType( ::rtl::OUString::createFromAscii( "image/jpeg" ) ); 200 uno::Sequence< beans::PropertyValue > aOutMediaProperties( 3 ); 201 aOutMediaProperties[0].Name = ::rtl::OUString::createFromAscii( "OutputStream" ); 202 aOutMediaProperties[0].Value <<= xOut; 203 aOutMediaProperties[1].Name = ::rtl::OUString::createFromAscii( "MimeType" ); 204 aOutMediaProperties[1].Value <<= aMimeType; 205 aOutMediaProperties[2].Name = ::rtl::OUString::createFromAscii( "FilterData" ); 206 aOutMediaProperties[2].Value <<= aFilterData; 207 xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties ); 208 xOut->flush(); 209 if ( xSeekable->getLength() > nZippedFileSize ) 210 { 211 bUseJPGCompression = sal_False; 212 } 213 else 214 { 215 aStrm.Seek( STREAM_SEEK_TO_END ); 216 217 xSeekable->seek( 0 ); 218 Sequence< PropertyValue > aArgs( 1 ); 219 aArgs[ 0 ].Name = ::rtl::OUString::createFromAscii( "InputStream" ); 220 aArgs[ 0 ].Value <<= xStream; 221 uno::Reference< XPropertySet > xPropSet( xGraphicProvider->queryGraphicDescriptor( aArgs ) ); 222 if ( xPropSet.is() ) 223 { 224 sal_Int16 nBitsPerPixel = 24; 225 if ( xPropSet->getPropertyValue( ::rtl::OUString::createFromAscii( "BitsPerPixel" ) ) >>= nBitsPerPixel ) 226 { 227 bTrueColorJPG = nBitsPerPixel != 8; 228 } 229 } 230 } 231 } 232 else 233 bUseJPGCompression = sal_False; 234 } 235 catch( uno::Exception& ) 236 { 237 bUseJPGCompression = sal_False; 238 } 239 } 240 if ( bUseJPGCompression ) 241 m_rOuterFace.DrawJPGBitmap( aStrm, bTrueColorJPG, aSizePixel, Rectangle( aPoint, aSize ), aMask ); 242 else if ( aBitmapEx.IsTransparent() ) 243 m_rOuterFace.DrawBitmapEx( aPoint, aSize, aBitmapEx ); 244 else 245 m_rOuterFace.DrawBitmap( aPoint, aSize, aBitmapEx.GetBitmap() ); 246 } 247 } 248 } 249 250 251 // ----------------------------------------------------------------------------- 252 253 void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevData* i_pOutDevData, const vcl::PDFWriter::PlayMetafileContext& i_rContext, VirtualDevice* pDummyVDev ) 254 { 255 bool bAssertionFired( false ); 256 257 VirtualDevice* pPrivateDevice = NULL; 258 if( ! pDummyVDev ) 259 { 260 pPrivateDevice = pDummyVDev = new VirtualDevice(); 261 pDummyVDev->EnableOutput( sal_False ); 262 pDummyVDev->SetMapMode( i_rMtf.GetPrefMapMode() ); 263 } 264 GDIMetaFile aMtf( i_rMtf ); 265 266 for( sal_uInt32 i = 0, nCount = aMtf.GetActionCount(); i < nCount; ) 267 { 268 if ( !i_pOutDevData || !i_pOutDevData->PlaySyncPageAct( m_rOuterFace, i ) ) 269 { 270 const MetaAction* pAction = aMtf.GetAction( i ); 271 const sal_uInt16 nType = pAction->GetType(); 272 273 switch( nType ) 274 { 275 case( META_PIXEL_ACTION ): 276 { 277 const MetaPixelAction* pA = (const MetaPixelAction*) pAction; 278 m_rOuterFace.DrawPixel( pA->GetPoint(), pA->GetColor() ); 279 } 280 break; 281 282 case( META_POINT_ACTION ): 283 { 284 const MetaPointAction* pA = (const MetaPointAction*) pAction; 285 m_rOuterFace.DrawPixel( pA->GetPoint() ); 286 } 287 break; 288 289 case( META_LINE_ACTION ): 290 { 291 const MetaLineAction* pA = (const MetaLineAction*) pAction; 292 if ( pA->GetLineInfo().IsDefault() ) 293 m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint() ); 294 else 295 m_rOuterFace.DrawLine( pA->GetStartPoint(), pA->GetEndPoint(), pA->GetLineInfo() ); 296 } 297 break; 298 299 case( META_RECT_ACTION ): 300 { 301 const MetaRectAction* pA = (const MetaRectAction*) pAction; 302 m_rOuterFace.DrawRect( pA->GetRect() ); 303 } 304 break; 305 306 case( META_ROUNDRECT_ACTION ): 307 { 308 const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pAction; 309 m_rOuterFace.DrawRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() ); 310 } 311 break; 312 313 case( META_ELLIPSE_ACTION ): 314 { 315 const MetaEllipseAction* pA = (const MetaEllipseAction*) pAction; 316 m_rOuterFace.DrawEllipse( pA->GetRect() ); 317 } 318 break; 319 320 case( META_ARC_ACTION ): 321 { 322 const MetaArcAction* pA = (const MetaArcAction*) pAction; 323 m_rOuterFace.DrawArc( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() ); 324 } 325 break; 326 327 case( META_PIE_ACTION ): 328 { 329 const MetaArcAction* pA = (const MetaArcAction*) pAction; 330 m_rOuterFace.DrawPie( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() ); 331 } 332 break; 333 334 case( META_CHORD_ACTION ): 335 { 336 const MetaChordAction* pA = (const MetaChordAction*) pAction; 337 m_rOuterFace.DrawChord( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() ); 338 } 339 break; 340 341 case( META_POLYGON_ACTION ): 342 { 343 const MetaPolygonAction* pA = (const MetaPolygonAction*) pAction; 344 m_rOuterFace.DrawPolygon( pA->GetPolygon() ); 345 } 346 break; 347 348 case( META_POLYLINE_ACTION ): 349 { 350 const MetaPolyLineAction* pA = (const MetaPolyLineAction*) pAction; 351 if ( pA->GetLineInfo().IsDefault() ) 352 m_rOuterFace.DrawPolyLine( pA->GetPolygon() ); 353 else 354 m_rOuterFace.DrawPolyLine( pA->GetPolygon(), pA->GetLineInfo() ); 355 } 356 break; 357 358 case( META_POLYPOLYGON_ACTION ): 359 { 360 const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*) pAction; 361 m_rOuterFace.DrawPolyPolygon( pA->GetPolyPolygon() ); 362 } 363 break; 364 365 case( META_GRADIENT_ACTION ): 366 { 367 const MetaGradientAction* pA = (const MetaGradientAction*) pAction; 368 #ifdef USE_PDFGRADIENTS 369 m_rOuterFace.DrawGradient( pA->GetRect(), pA->GetGradient() ); 370 #else 371 const PolyPolygon aPolyPoly( pA->GetRect() ); 372 implWriteGradient( aPolyPoly, pA->GetGradient(), pDummyVDev, i_rContext ); 373 #endif 374 } 375 break; 376 377 case( META_GRADIENTEX_ACTION ): 378 { 379 const MetaGradientExAction* pA = (const MetaGradientExAction*) pAction; 380 #ifdef USE_PDFGRADIENTS 381 m_rOuterFace.DrawGradient( pA->GetPolyPolygon(), pA->GetGradient() ); 382 #else 383 implWriteGradient( pA->GetPolyPolygon(), pA->GetGradient(), pDummyVDev, i_rContext ); 384 #endif 385 } 386 break; 387 388 case META_HATCH_ACTION: 389 { 390 const MetaHatchAction* pA = (const MetaHatchAction*) pAction; 391 m_rOuterFace.DrawHatch( pA->GetPolyPolygon(), pA->GetHatch() ); 392 } 393 break; 394 395 case( META_TRANSPARENT_ACTION ): 396 { 397 const MetaTransparentAction* pA = (const MetaTransparentAction*) pAction; 398 m_rOuterFace.DrawTransparent( pA->GetPolyPolygon(), pA->GetTransparence() ); 399 } 400 break; 401 402 case( META_FLOATTRANSPARENT_ACTION ): 403 { 404 const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*) pAction; 405 406 GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() ); 407 const Point& rPos = pA->GetPoint(); 408 const Size& rSize= pA->GetSize(); 409 const Gradient& rTransparenceGradient = pA->GetGradient(); 410 411 // special case constant alpha value 412 if( rTransparenceGradient.GetStartColor() == rTransparenceGradient.GetEndColor() ) 413 { 414 const Color aTransCol( rTransparenceGradient.GetStartColor() ); 415 const sal_uInt16 nTransPercent = aTransCol.GetLuminance() * 100 / 255; 416 m_rOuterFace.BeginTransparencyGroup(); 417 playMetafile( aTmpMtf, NULL, i_rContext, pDummyVDev ); 418 m_rOuterFace.EndTransparencyGroup( Rectangle( rPos, rSize ), nTransPercent ); 419 } 420 else 421 { 422 const Size aDstSizeTwip( pDummyVDev->PixelToLogic( pDummyVDev->LogicToPixel( rSize ), MAP_TWIP ) ); 423 424 // #115962# Always use at least 300 DPI for bitmap conversion of transparence gradients, 425 // else the quality is not acceptable (see bugdoc as example) 426 // sal_Int32 nMaxBmpDPI = i_rContext.m_bOnlyLosslessCompression ? 300 : 72; 427 sal_Int32 nMaxBmpDPI(300); 428 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 default: 1052 // #i24604# Made assertion fire only once per 1053 // metafile. The asserted actions here are all 1054 // deprecated 1055 if( !bAssertionFired ) 1056 { 1057 bAssertionFired = true; 1058 DBG_ERROR( "PDFExport::ImplWriteActions: deprecated and unsupported MetaAction encountered" ); 1059 } 1060 break; 1061 } 1062 i++; 1063 } 1064 } 1065 1066 delete pPrivateDevice; 1067 } 1068 1069 // Encryption methods 1070 1071 /* a crutch to transport an rtlDigest safely though UNO API 1072 this is needed for the PDF export dialog, which otherwise would have to pass 1073 clear text passwords down till they can be used in PDFWriter. Unfortunately 1074 the MD5 sum of the password (which is needed to create the PDF encryption key) 1075 is not sufficient, since an rtl MD5 digest cannot be created in an arbitrary state 1076 which would be needed in PDFWriterImpl::computeEncryptionKey. 1077 */ 1078 class EncHashTransporter : public cppu::WeakImplHelper1 < com::sun::star::beans::XMaterialHolder > 1079 { 1080 rtlDigest maUDigest; 1081 sal_IntPtr maID; 1082 std::vector< sal_uInt8 > maOValue; 1083 1084 static std::map< sal_IntPtr, EncHashTransporter* > sTransporters; 1085 public: 1086 EncHashTransporter() 1087 : maUDigest( rtl_digest_createMD5() ) 1088 { 1089 maID = reinterpret_cast< sal_IntPtr >(this); 1090 while( sTransporters.find( maID ) != sTransporters.end() ) // paranoia mode 1091 maID++; 1092 sTransporters[ maID ] = this; 1093 } 1094 1095 virtual ~EncHashTransporter() 1096 { 1097 sTransporters.erase( maID ); 1098 if( maUDigest ) 1099 rtl_digest_destroyMD5( maUDigest ); 1100 OSL_TRACE( "EncHashTransporter freed\n" ); 1101 } 1102 1103 rtlDigest getUDigest() const { return maUDigest; }; 1104 std::vector< sal_uInt8 >& getOValue() { return maOValue; } 1105 void invalidate() 1106 { 1107 if( maUDigest ) 1108 { 1109 rtl_digest_destroyMD5( maUDigest ); 1110 maUDigest = NULL; 1111 } 1112 } 1113 1114 // XMaterialHolder 1115 virtual uno::Any SAL_CALL getMaterial() throw() 1116 { 1117 return uno::makeAny( sal_Int64(maID) ); 1118 } 1119 1120 static EncHashTransporter* getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& ); 1121 1122 }; 1123 1124 std::map< sal_IntPtr, EncHashTransporter* > EncHashTransporter::sTransporters; 1125 1126 EncHashTransporter* EncHashTransporter::getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& xRef ) 1127 { 1128 EncHashTransporter* pResult = NULL; 1129 if( xRef.is() ) 1130 { 1131 uno::Any aMat( xRef->getMaterial() ); 1132 sal_Int64 nMat = 0; 1133 if( aMat >>= nMat ) 1134 { 1135 std::map< sal_IntPtr, EncHashTransporter* >::iterator it = sTransporters.find( static_cast<sal_IntPtr>(nMat) ); 1136 if( it != sTransporters.end() ) 1137 pResult = it->second; 1138 } 1139 } 1140 return pResult; 1141 } 1142 1143 sal_Bool PDFWriterImpl::checkEncryptionBufferSize( register sal_Int32 newSize ) 1144 { 1145 if( m_nEncryptionBufferSize < newSize ) 1146 { 1147 /* reallocate the buffer, the used function allocate as rtl_allocateMemory 1148 if the pointer parameter is NULL */ 1149 m_pEncryptionBuffer = (sal_uInt8*)rtl_reallocateMemory( m_pEncryptionBuffer, newSize ); 1150 if( m_pEncryptionBuffer ) 1151 m_nEncryptionBufferSize = newSize; 1152 else 1153 m_nEncryptionBufferSize = 0; 1154 } 1155 return ( m_nEncryptionBufferSize != 0 ); 1156 } 1157 1158 void PDFWriterImpl::checkAndEnableStreamEncryption( register sal_Int32 nObject ) 1159 { 1160 if( m_aContext.Encryption.Encrypt() ) 1161 { 1162 m_bEncryptThisStream = true; 1163 sal_Int32 i = m_nKeyLength; 1164 m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject; 1165 m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 ); 1166 m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 ); 1167 //the other location of m_nEncryptionKey are already set to 0, our fixed generation number 1168 // do the MD5 hash 1169 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; 1170 // the i+2 to take into account the generation number, always zero 1171 rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) ); 1172 // initialize the RC4 with the key 1173 // key legth: see algoritm 3.1, step 4: (N+5) max 16 1174 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 ); 1175 } 1176 } 1177 1178 void PDFWriterImpl::enableStringEncryption( register sal_Int32 nObject ) 1179 { 1180 if( m_aContext.Encryption.Encrypt() ) 1181 { 1182 sal_Int32 i = m_nKeyLength; 1183 m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject; 1184 m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 ); 1185 m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 ); 1186 //the other location of m_nEncryptionKey are already set to 0, our fixed generation number 1187 // do the MD5 hash 1188 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; 1189 // the i+2 to take into account the generation number, always zero 1190 rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) ); 1191 // initialize the RC4 with the key 1192 // key legth: see algoritm 3.1, step 4: (N+5) max 16 1193 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 ); 1194 } 1195 } 1196 1197 /* init the encryption engine 1198 1. init the document id, used both for building the document id and for building the encryption key(s) 1199 2. build the encryption key following algorithms described in the PDF specification 1200 */ 1201 uno::Reference< beans::XMaterialHolder > PDFWriterImpl::initEncryption( const rtl::OUString& i_rOwnerPassword, 1202 const rtl::OUString& i_rUserPassword, 1203 bool b128Bit 1204 ) 1205 { 1206 uno::Reference< beans::XMaterialHolder > xResult; 1207 if( i_rOwnerPassword.getLength() || i_rUserPassword.getLength() ) 1208 { 1209 EncHashTransporter* pTransporter = new EncHashTransporter; 1210 xResult = pTransporter; 1211 1212 // get padded passwords 1213 sal_uInt8 aPadUPW[ENCRYPTED_PWD_SIZE], aPadOPW[ENCRYPTED_PWD_SIZE]; 1214 padPassword( i_rOwnerPassword.getLength() ? i_rOwnerPassword : i_rUserPassword, aPadOPW ); 1215 padPassword( i_rUserPassword, aPadUPW ); 1216 sal_Int32 nKeyLength = SECUR_40BIT_KEY; 1217 if( b128Bit ) 1218 nKeyLength = SECUR_128BIT_KEY; 1219 1220 if( computeODictionaryValue( aPadOPW, aPadUPW, pTransporter->getOValue(), nKeyLength ) ) 1221 { 1222 rtlDigest aDig = pTransporter->getUDigest(); 1223 if( rtl_digest_updateMD5( aDig, aPadUPW, ENCRYPTED_PWD_SIZE ) != rtl_Digest_E_None ) 1224 xResult.clear(); 1225 } 1226 else 1227 xResult.clear(); 1228 1229 // trash temporary padded cleartext PWDs 1230 rtl_zeroMemory( aPadOPW, sizeof(aPadOPW) ); 1231 rtl_zeroMemory( aPadUPW, sizeof(aPadUPW) ); 1232 1233 } 1234 return xResult; 1235 } 1236 1237 bool PDFWriterImpl::prepareEncryption( const uno::Reference< beans::XMaterialHolder >& xEnc ) 1238 { 1239 bool bSuccess = false; 1240 EncHashTransporter* pTransporter = EncHashTransporter::getEncHashTransporter( xEnc ); 1241 if( pTransporter ) 1242 { 1243 sal_Int32 nKeyLength = 0, nRC4KeyLength = 0; 1244 sal_Int32 nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, nKeyLength, nRC4KeyLength ); 1245 m_aContext.Encryption.OValue = pTransporter->getOValue(); 1246 bSuccess = computeUDictionaryValue( pTransporter, m_aContext.Encryption, nKeyLength, nAccessPermissions ); 1247 } 1248 if( ! bSuccess ) 1249 { 1250 m_aContext.Encryption.OValue.clear(); 1251 m_aContext.Encryption.UValue.clear(); 1252 m_aContext.Encryption.EncryptionKey.clear(); 1253 } 1254 return bSuccess; 1255 } 1256 1257 sal_Int32 PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties, 1258 sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength ) 1259 { 1260 /* 1261 2) compute the access permissions, in numerical form 1262 1263 the default value depends on the revision 2 (40 bit) or 3 (128 bit security): 1264 - for 40 bit security the unused bit must be set to 1, since they are not used 1265 - for 128 bit security the same bit must be preset to 0 and set later if needed 1266 according to the table 3.15, pdf v 1.4 */ 1267 sal_Int32 nAccessPermissions = ( i_rProperties.Security128bit ) ? 0xfffff0c0 : 0xffffffc0 ; 1268 1269 /* check permissions for 40 bit security case */ 1270 nAccessPermissions |= ( i_rProperties.CanPrintTheDocument ) ? 1 << 2 : 0; 1271 nAccessPermissions |= ( i_rProperties.CanModifyTheContent ) ? 1 << 3 : 0; 1272 nAccessPermissions |= ( i_rProperties.CanCopyOrExtract ) ? 1 << 4 : 0; 1273 nAccessPermissions |= ( i_rProperties.CanAddOrModify ) ? 1 << 5 : 0; 1274 o_rKeyLength = SECUR_40BIT_KEY; 1275 o_rRC4KeyLength = SECUR_40BIT_KEY+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5 1276 1277 if( i_rProperties.Security128bit ) 1278 { 1279 o_rKeyLength = SECUR_128BIT_KEY; 1280 o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum 1281 // permitted value is 16 1282 nAccessPermissions |= ( i_rProperties.CanFillInteractive ) ? 1 << 8 : 0; 1283 nAccessPermissions |= ( i_rProperties.CanExtractForAccessibility ) ? 1 << 9 : 0; 1284 nAccessPermissions |= ( i_rProperties.CanAssemble ) ? 1 << 10 : 0; 1285 nAccessPermissions |= ( i_rProperties.CanPrintFull ) ? 1 << 11 : 0; 1286 } 1287 return nAccessPermissions; 1288 } 1289 1290 /************************************************************* 1291 begin i12626 methods 1292 1293 Implements Algorithm 3.2, step 1 only 1294 */ 1295 void PDFWriterImpl::padPassword( const rtl::OUString& i_rPassword, sal_uInt8* o_pPaddedPW ) 1296 { 1297 // get ansi-1252 version of the password string CHECKIT ! i12626 1298 rtl::OString aString( rtl::OUStringToOString( i_rPassword, RTL_TEXTENCODING_MS_1252 ) ); 1299 1300 //copy the string to the target 1301 sal_Int32 nToCopy = ( aString.getLength() < ENCRYPTED_PWD_SIZE ) ? aString.getLength() : ENCRYPTED_PWD_SIZE; 1302 sal_Int32 nCurrentChar; 1303 1304 for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ ) 1305 o_pPaddedPW[nCurrentChar] = (sal_uInt8)( aString.getStr()[nCurrentChar] ); 1306 1307 //pad it with standard byte string 1308 sal_Int32 i,y; 1309 for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ ) 1310 o_pPaddedPW[i] = s_nPadString[y]; 1311 1312 // trash memory of temporary clear text password 1313 rtl_zeroMemory( (sal_Char*)aString.getStr(), aString.getLength() ); 1314 } 1315 1316 /********************************** 1317 Algorithm 3.2 Compute the encryption key used 1318 1319 step 1 should already be done before calling, the paThePaddedPassword parameter should contain 1320 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter, 1321 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used 1322 1323 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec. 1324 1325 */ 1326 bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter* i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, sal_Int32 i_nAccessPermissions ) 1327 { 1328 bool bSuccess = true; 1329 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; 1330 1331 // transporter contains an MD5 digest with the padded user password already 1332 rtlDigest aDigest = i_pTransporter->getUDigest(); 1333 rtlDigestError nError = rtl_Digest_E_None; 1334 if( aDigest ) 1335 { 1336 //step 3 1337 if( ! io_rProperties.OValue.empty() ) 1338 nError = rtl_digest_updateMD5( aDigest, &io_rProperties.OValue[0] , sal_Int32(io_rProperties.OValue.size()) ); 1339 else 1340 bSuccess = false; 1341 //Step 4 1342 sal_uInt8 nPerm[4]; 1343 1344 nPerm[0] = (sal_uInt8)i_nAccessPermissions; 1345 nPerm[1] = (sal_uInt8)( i_nAccessPermissions >> 8 ); 1346 nPerm[2] = (sal_uInt8)( i_nAccessPermissions >> 16 ); 1347 nPerm[3] = (sal_uInt8)( i_nAccessPermissions >> 24 ); 1348 1349 if( nError == rtl_Digest_E_None ) 1350 nError = rtl_digest_updateMD5( aDigest, nPerm , sizeof( nPerm ) ); 1351 1352 //step 5, get the document ID, binary form 1353 if( nError == rtl_Digest_E_None ) 1354 nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) ); 1355 //get the digest 1356 if( nError == rtl_Digest_E_None ) 1357 { 1358 rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); 1359 1360 //step 6, only if 128 bit 1361 if( io_rProperties.Security128bit ) 1362 { 1363 for( sal_Int32 i = 0; i < 50; i++ ) 1364 { 1365 nError = rtl_digest_updateMD5( aDigest, &nMD5Sum, sizeof( nMD5Sum ) ); 1366 if( nError != rtl_Digest_E_None ) 1367 { 1368 bSuccess = false; 1369 break; 1370 } 1371 rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); 1372 } 1373 } 1374 } 1375 } 1376 else 1377 bSuccess = false; 1378 1379 i_pTransporter->invalidate(); 1380 1381 //Step 7 1382 if( bSuccess ) 1383 { 1384 io_rProperties.EncryptionKey.resize( MAXIMUM_RC4_KEY_LENGTH ); 1385 for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ ) 1386 io_rProperties.EncryptionKey[i] = nMD5Sum[i]; 1387 } 1388 else 1389 io_rProperties.EncryptionKey.clear(); 1390 1391 return bSuccess; 1392 } 1393 1394 /********************************** 1395 Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member 1396 the step numbers down here correspond to the ones in PDF v.1.4 specfication 1397 */ 1398 bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword, 1399 const sal_uInt8* i_pPaddedUserPassword, 1400 std::vector< sal_uInt8 >& io_rOValue, 1401 sal_Int32 i_nKeyLength 1402 ) 1403 { 1404 bool bSuccess = true; 1405 1406 io_rOValue.resize( ENCRYPTED_PWD_SIZE ); 1407 1408 rtlDigest aDigest = rtl_digest_createMD5(); 1409 rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); 1410 if( aDigest && aCipher) 1411 { 1412 //step 1 already done, data is in i_pPaddedOwnerPassword 1413 //step 2 1414 1415 rtlDigestError nError = rtl_digest_updateMD5( aDigest, i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE ); 1416 if( nError == rtl_Digest_E_None ) 1417 { 1418 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; 1419 1420 rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) ); 1421 //step 3, only if 128 bit 1422 if( i_nKeyLength == SECUR_128BIT_KEY ) 1423 { 1424 sal_Int32 i; 1425 for( i = 0; i < 50; i++ ) 1426 { 1427 nError = rtl_digest_updateMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); 1428 if( nError != rtl_Digest_E_None ) 1429 { 1430 bSuccess = false; 1431 break; 1432 } 1433 rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); 1434 } 1435 } 1436 //Step 4, the key is in nMD5Sum 1437 //step 5 already done, data is in i_pPaddedUserPassword 1438 //step 6 1439 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, 1440 nMD5Sum, i_nKeyLength , NULL, 0 ); 1441 // encrypt the user password using the key set above 1442 rtl_cipher_encodeARCFOUR( aCipher, i_pPaddedUserPassword, ENCRYPTED_PWD_SIZE, // the data to be encrypted 1443 &io_rOValue[0], sal_Int32(io_rOValue.size()) ); //encrypted data 1444 //Step 7, only if 128 bit 1445 if( i_nKeyLength == SECUR_128BIT_KEY ) 1446 { 1447 sal_uInt32 i, y; 1448 sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key 1449 1450 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1 1451 { 1452 for( y = 0; y < sizeof( nLocalKey ); y++ ) 1453 nLocalKey[y] = (sal_uInt8)( nMD5Sum[y] ^ i ); 1454 1455 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, 1456 nLocalKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area, on init can be NULL 1457 rtl_cipher_encodeARCFOUR( aCipher, &io_rOValue[0], sal_Int32(io_rOValue.size()), // the data to be encrypted 1458 &io_rOValue[0], sal_Int32(io_rOValue.size()) ); // encrypted data, can be the same as the input, encrypt "in place" 1459 //step 8, store in class data member 1460 } 1461 } 1462 } 1463 else 1464 bSuccess = false; 1465 } 1466 else 1467 bSuccess = false; 1468 1469 if( aDigest ) 1470 rtl_digest_destroyMD5( aDigest ); 1471 if( aCipher ) 1472 rtl_cipher_destroyARCFOUR( aCipher ); 1473 1474 if( ! bSuccess ) 1475 io_rOValue.clear(); 1476 return bSuccess; 1477 } 1478 1479 /********************************** 1480 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) 1481 */ 1482 bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter* i_pTransporter, 1483 vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, 1484 sal_Int32 i_nKeyLength, 1485 sal_Int32 i_nAccessPermissions 1486 ) 1487 { 1488 bool bSuccess = true; 1489 1490 io_rProperties.UValue.resize( ENCRYPTED_PWD_SIZE ); 1491 1492 rtlDigest aDigest = rtl_digest_createMD5(); 1493 rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); 1494 if( aDigest && aCipher ) 1495 { 1496 //step 1, common to both 3.4 and 3.5 1497 if( computeEncryptionKey( i_pTransporter, io_rProperties, i_nAccessPermissions ) ) 1498 { 1499 // prepare encryption key for object 1500 for( sal_Int32 i = i_nKeyLength, y = 0; y < 5 ; y++ ) 1501 io_rProperties.EncryptionKey[i++] = 0; 1502 1503 if( io_rProperties.Security128bit == false ) 1504 { 1505 //3.4 1506 //step 2 and 3 1507 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, 1508 &io_rProperties.EncryptionKey[0], 5 , // key and key length 1509 NULL, 0 ); //destination data area 1510 // encrypt the user password using the key set above, save for later use 1511 rtl_cipher_encodeARCFOUR( aCipher, s_nPadString, sizeof( s_nPadString ), // the data to be encrypted 1512 &io_rProperties.UValue[0], sal_Int32(io_rProperties.UValue.size()) ); //encrypted data, stored in class data member 1513 } 1514 else 1515 { 1516 //or 3.5, for 128 bit security 1517 //step6, initilize the last 16 bytes of the encrypted user password to 0 1518 for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sal_uInt32(io_rProperties.UValue.size()); i++) 1519 io_rProperties.UValue[i] = 0; 1520 //step 2 1521 rtlDigestError nError = rtl_digest_updateMD5( aDigest, s_nPadString, sizeof( s_nPadString ) ); 1522 //step 3 1523 if( nError == rtl_Digest_E_None ) 1524 nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) ); 1525 else 1526 bSuccess = false; 1527 1528 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; 1529 rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) ); 1530 //Step 4 1531 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, 1532 &io_rProperties.EncryptionKey[0], SECUR_128BIT_KEY, NULL, 0 ); //destination data area 1533 rtl_cipher_encodeARCFOUR( aCipher, nMD5Sum, sizeof( nMD5Sum ), // the data to be encrypted 1534 &io_rProperties.UValue[0], sizeof( nMD5Sum ) ); //encrypted data, stored in class data member 1535 //step 5 1536 sal_uInt32 i, y; 1537 sal_uInt8 nLocalKey[SECUR_128BIT_KEY]; 1538 1539 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1 1540 { 1541 for( y = 0; y < sizeof( nLocalKey ) ; y++ ) 1542 nLocalKey[y] = (sal_uInt8)( io_rProperties.EncryptionKey[y] ^ i ); 1543 1544 rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, 1545 nLocalKey, SECUR_128BIT_KEY, // key and key length 1546 NULL, 0 ); //destination data area, on init can be NULL 1547 rtl_cipher_encodeARCFOUR( aCipher, &io_rProperties.UValue[0], SECUR_128BIT_KEY, // the data to be encrypted 1548 &io_rProperties.UValue[0], SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place" 1549 } 1550 } 1551 } 1552 else 1553 bSuccess = false; 1554 } 1555 else 1556 bSuccess = false; 1557 1558 if( aDigest ) 1559 rtl_digest_destroyMD5( aDigest ); 1560 if( aCipher ) 1561 rtl_cipher_destroyARCFOUR( aCipher ); 1562 1563 if( ! bSuccess ) 1564 io_rProperties.UValue.clear(); 1565 return bSuccess; 1566 } 1567 1568 /* end i12626 methods */ 1569 1570 static const long unsetRun[256] = 1571 { 1572 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */ 1573 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */ 1574 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */ 1575 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */ 1576 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */ 1577 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */ 1578 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */ 1579 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */ 1580 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */ 1581 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */ 1582 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */ 1583 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */ 1584 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */ 1585 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */ 1586 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */ 1587 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */ 1588 }; 1589 1590 static const long setRun[256] = 1591 { 1592 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */ 1593 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ 1594 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */ 1595 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ 1596 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ 1597 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ 1598 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */ 1599 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */ 1600 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */ 1601 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */ 1602 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */ 1603 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */ 1604 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */ 1605 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */ 1606 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */ 1607 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */ 1608 }; 1609 1610 inline bool isSet( const Scanline i_pLine, long i_nIndex ) 1611 { 1612 return (i_pLine[ i_nIndex/8 ] & (0x80 >> (i_nIndex&7))) != 0; 1613 } 1614 1615 long findBitRun( const Scanline i_pLine, long i_nStartIndex, long i_nW, bool i_bSet ) 1616 { 1617 if( i_nStartIndex < 0 ) 1618 return i_nW; 1619 1620 long nIndex = i_nStartIndex; 1621 if( nIndex < i_nW ) 1622 { 1623 const sal_uInt8 * pByte = static_cast<sal_uInt8*>(i_pLine) + (nIndex/8); 1624 sal_uInt8 nByte = *pByte; 1625 1626 // run up to byte boundary 1627 long nBitInByte = (nIndex & 7); 1628 if( nBitInByte ) 1629 { 1630 sal_uInt8 nMask = 0x80 >> nBitInByte; 1631 while( nBitInByte != 8 ) 1632 { 1633 if( (nByte & nMask) != (i_bSet ? nMask : 0) ) 1634 return nIndex < i_nW ? nIndex : i_nW; 1635 nMask = nMask >> 1; 1636 nBitInByte++; 1637 nIndex++; 1638 } 1639 if( nIndex < i_nW ) 1640 { 1641 pByte++; 1642 nByte = *pByte; 1643 } 1644 } 1645 1646 sal_uInt8 nRunByte; 1647 const long* pRunTable; 1648 if( i_bSet ) 1649 { 1650 nRunByte = 0xff; 1651 pRunTable = setRun; 1652 } 1653 else 1654 { 1655 nRunByte = 0; 1656 pRunTable = unsetRun; 1657 } 1658 1659 while( nByte == nRunByte && nIndex < i_nW ) 1660 { 1661 nIndex += 8; 1662 pByte++; 1663 nByte = *pByte; 1664 } 1665 if( nIndex < i_nW ) 1666 { 1667 nIndex += pRunTable[nByte]; 1668 } 1669 } 1670 return nIndex < i_nW ? nIndex : i_nW; 1671 } 1672 1673 struct BitStreamState 1674 { 1675 sal_uInt8 mnBuffer; 1676 sal_uInt32 mnNextBitPos; 1677 1678 BitStreamState() 1679 : mnBuffer( 0 ) 1680 , mnNextBitPos( 8 ) 1681 { 1682 } 1683 1684 const sal_uInt8* getByte() const { return &mnBuffer; } 1685 void flush() { mnNextBitPos = 8; mnBuffer = 0; } 1686 }; 1687 1688 void PDFWriterImpl::putG4Bits( sal_uInt32 i_nLength, sal_uInt32 i_nCode, BitStreamState& io_rState ) 1689 { 1690 while( i_nLength > io_rState.mnNextBitPos ) 1691 { 1692 io_rState.mnBuffer |= static_cast<sal_uInt8>( i_nCode >> (i_nLength - io_rState.mnNextBitPos) ); 1693 i_nLength -= io_rState.mnNextBitPos; 1694 writeBuffer( io_rState.getByte(), 1 ); 1695 io_rState.flush(); 1696 } 1697 OSL_ASSERT( i_nLength < 9 ); 1698 static const unsigned int msbmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; 1699 io_rState.mnBuffer |= static_cast<sal_uInt8>( (i_nCode & msbmask[i_nLength]) << (io_rState.mnNextBitPos - i_nLength) ); 1700 io_rState.mnNextBitPos -= i_nLength; 1701 if( io_rState.mnNextBitPos == 0 ) 1702 { 1703 writeBuffer( io_rState.getByte(), 1 ); 1704 io_rState.flush(); 1705 } 1706 } 1707 1708 struct PixelCode 1709 { 1710 sal_uInt32 mnEncodedPixels; 1711 sal_uInt32 mnCodeBits; 1712 sal_uInt32 mnCode; 1713 }; 1714 1715 static const PixelCode WhitePixelCodes[] = 1716 { 1717 { 0, 8, 0x35 }, // 0011 0101 1718 { 1, 6, 0x7 }, // 0001 11 1719 { 2, 4, 0x7 }, // 0111 1720 { 3, 4, 0x8 }, // 1000 1721 { 4, 4, 0xB }, // 1011 1722 { 5, 4, 0xC }, // 1100 1723 { 6, 4, 0xE }, // 1110 1724 { 7, 4, 0xF }, // 1111 1725 { 8, 5, 0x13 }, // 1001 1 1726 { 9, 5, 0x14 }, // 1010 0 1727 { 10, 5, 0x7 }, // 0011 1 1728 { 11, 5, 0x8 }, // 0100 0 1729 { 12, 6, 0x8 }, // 0010 00 1730 { 13, 6, 0x3 }, // 0000 11 1731 { 14, 6, 0x34 }, // 1101 00 1732 { 15, 6, 0x35 }, // 1101 01 1733 { 16, 6, 0x2A }, // 1010 10 1734 { 17, 6, 0x2B }, // 1010 11 1735 { 18, 7, 0x27 }, // 0100 111 1736 { 19, 7, 0xC }, // 0001 100 1737 { 20, 7, 0x8 }, // 0001 000 1738 { 21, 7, 0x17 }, // 0010 111 1739 { 22, 7, 0x3 }, // 0000 011 1740 { 23, 7, 0x4 }, // 0000 100 1741 { 24, 7, 0x28 }, // 0101 000 1742 { 25, 7, 0x2B }, // 0101 011 1743 { 26, 7, 0x13 }, // 0010 011 1744 { 27, 7, 0x24 }, // 0100 100 1745 { 28, 7, 0x18 }, // 0011 000 1746 { 29, 8, 0x2 }, // 0000 0010 1747 { 30, 8, 0x3 }, // 0000 0011 1748 { 31, 8, 0x1A }, // 0001 1010 1749 { 32, 8, 0x1B }, // 0001 1011 1750 { 33, 8, 0x12 }, // 0001 0010 1751 { 34, 8, 0x13 }, // 0001 0011 1752 { 35, 8, 0x14 }, // 0001 0100 1753 { 36, 8, 0x15 }, // 0001 0101 1754 { 37, 8, 0x16 }, // 0001 0110 1755 { 38, 8, 0x17 }, // 0001 0111 1756 { 39, 8, 0x28 }, // 0010 1000 1757 { 40, 8, 0x29 }, // 0010 1001 1758 { 41, 8, 0x2A }, // 0010 1010 1759 { 42, 8, 0x2B }, // 0010 1011 1760 { 43, 8, 0x2C }, // 0010 1100 1761 { 44, 8, 0x2D }, // 0010 1101 1762 { 45, 8, 0x4 }, // 0000 0100 1763 { 46, 8, 0x5 }, // 0000 0101 1764 { 47, 8, 0xA }, // 0000 1010 1765 { 48, 8, 0xB }, // 0000 1011 1766 { 49, 8, 0x52 }, // 0101 0010 1767 { 50, 8, 0x53 }, // 0101 0011 1768 { 51, 8, 0x54 }, // 0101 0100 1769 { 52, 8, 0x55 }, // 0101 0101 1770 { 53, 8, 0x24 }, // 0010 0100 1771 { 54, 8, 0x25 }, // 0010 0101 1772 { 55, 8, 0x58 }, // 0101 1000 1773 { 56, 8, 0x59 }, // 0101 1001 1774 { 57, 8, 0x5A }, // 0101 1010 1775 { 58, 8, 0x5B }, // 0101 1011 1776 { 59, 8, 0x4A }, // 0100 1010 1777 { 60, 8, 0x4B }, // 0100 1011 1778 { 61, 8, 0x32 }, // 0011 0010 1779 { 62, 8, 0x33 }, // 0011 0011 1780 { 63, 8, 0x34 }, // 0011 0100 1781 { 64, 5, 0x1B }, // 1101 1 1782 { 128, 5, 0x12 }, // 1001 0 1783 { 192, 6, 0x17 }, // 0101 11 1784 { 256, 7, 0x37 }, // 0110 111 1785 { 320, 8, 0x36 }, // 0011 0110 1786 { 384, 8, 0x37 }, // 0011 0111 1787 { 448, 8, 0x64 }, // 0110 0100 1788 { 512, 8, 0x65 }, // 0110 0101 1789 { 576, 8, 0x68 }, // 0110 1000 1790 { 640, 8, 0x67 }, // 0110 0111 1791 { 704, 9, 0xCC }, // 0110 0110 0 1792 { 768, 9, 0xCD }, // 0110 0110 1 1793 { 832, 9, 0xD2 }, // 0110 1001 0 1794 { 896, 9, 0xD3 }, // 0110 1001 1 1795 { 960, 9, 0xD4 }, // 0110 1010 0 1796 { 1024, 9, 0xD5 }, // 0110 1010 1 1797 { 1088, 9, 0xD6 }, // 0110 1011 0 1798 { 1152, 9, 0xD7 }, // 0110 1011 1 1799 { 1216, 9, 0xD8 }, // 0110 1100 0 1800 { 1280, 9, 0xD9 }, // 0110 1100 1 1801 { 1344, 9, 0xDA }, // 0110 1101 0 1802 { 1408, 9, 0xDB }, // 0110 1101 1 1803 { 1472, 9, 0x98 }, // 0100 1100 0 1804 { 1536, 9, 0x99 }, // 0100 1100 1 1805 { 1600, 9, 0x9A }, // 0100 1101 0 1806 { 1664, 6, 0x18 }, // 0110 00 1807 { 1728, 9, 0x9B }, // 0100 1101 1 1808 { 1792, 11, 0x8 }, // 0000 0001 000 1809 { 1856, 11, 0xC }, // 0000 0001 100 1810 { 1920, 11, 0xD }, // 0000 0001 101 1811 { 1984, 12, 0x12 }, // 0000 0001 0010 1812 { 2048, 12, 0x13 }, // 0000 0001 0011 1813 { 2112, 12, 0x14 }, // 0000 0001 0100 1814 { 2176, 12, 0x15 }, // 0000 0001 0101 1815 { 2240, 12, 0x16 }, // 0000 0001 0110 1816 { 2304, 12, 0x17 }, // 0000 0001 0111 1817 { 2368, 12, 0x1C }, // 0000 0001 1100 1818 { 2432, 12, 0x1D }, // 0000 0001 1101 1819 { 2496, 12, 0x1E }, // 0000 0001 1110 1820 { 2560, 12, 0x1F } // 0000 0001 1111 1821 }; 1822 1823 static const PixelCode BlackPixelCodes[] = 1824 { 1825 { 0, 10, 0x37 }, // 0000 1101 11 1826 { 1, 3, 0x2 }, // 010 1827 { 2, 2, 0x3 }, // 11 1828 { 3, 2, 0x2 }, // 10 1829 { 4, 3, 0x3 }, // 011 1830 { 5, 4, 0x3 }, // 0011 1831 { 6, 4, 0x2 }, // 0010 1832 { 7, 5, 0x3 }, // 0001 1 1833 { 8, 6, 0x5 }, // 0001 01 1834 { 9, 6, 0x4 }, // 0001 00 1835 { 10, 7, 0x4 }, // 0000 100 1836 { 11, 7, 0x5 }, // 0000 101 1837 { 12, 7, 0x7 }, // 0000 111 1838 { 13, 8, 0x4 }, // 0000 0100 1839 { 14, 8, 0x7 }, // 0000 0111 1840 { 15, 9, 0x18 }, // 0000 1100 0 1841 { 16, 10, 0x17 }, // 0000 0101 11 1842 { 17, 10, 0x18 }, // 0000 0110 00 1843 { 18, 10, 0x8 }, // 0000 0010 00 1844 { 19, 11, 0x67 }, // 0000 1100 111 1845 { 20, 11, 0x68 }, // 0000 1101 000 1846 { 21, 11, 0x6C }, // 0000 1101 100 1847 { 22, 11, 0x37 }, // 0000 0110 111 1848 { 23, 11, 0x28 }, // 0000 0101 000 1849 { 24, 11, 0x17 }, // 0000 0010 111 1850 { 25, 11, 0x18 }, // 0000 0011 000 1851 { 26, 12, 0xCA }, // 0000 1100 1010 1852 { 27, 12, 0xCB }, // 0000 1100 1011 1853 { 28, 12, 0xCC }, // 0000 1100 1100 1854 { 29, 12, 0xCD }, // 0000 1100 1101 1855 { 30, 12, 0x68 }, // 0000 0110 1000 1856 { 31, 12, 0x69 }, // 0000 0110 1001 1857 { 32, 12, 0x6A }, // 0000 0110 1010 1858 { 33, 12, 0x6B }, // 0000 0110 1011 1859 { 34, 12, 0xD2 }, // 0000 1101 0010 1860 { 35, 12, 0xD3 }, // 0000 1101 0011 1861 { 36, 12, 0xD4 }, // 0000 1101 0100 1862 { 37, 12, 0xD5 }, // 0000 1101 0101 1863 { 38, 12, 0xD6 }, // 0000 1101 0110 1864 { 39, 12, 0xD7 }, // 0000 1101 0111 1865 { 40, 12, 0x6C }, // 0000 0110 1100 1866 { 41, 12, 0x6D }, // 0000 0110 1101 1867 { 42, 12, 0xDA }, // 0000 1101 1010 1868 { 43, 12, 0xDB }, // 0000 1101 1011 1869 { 44, 12, 0x54 }, // 0000 0101 0100 1870 { 45, 12, 0x55 }, // 0000 0101 0101 1871 { 46, 12, 0x56 }, // 0000 0101 0110 1872 { 47, 12, 0x57 }, // 0000 0101 0111 1873 { 48, 12, 0x64 }, // 0000 0110 0100 1874 { 49, 12, 0x65 }, // 0000 0110 0101 1875 { 50, 12, 0x52 }, // 0000 0101 0010 1876 { 51, 12, 0x53 }, // 0000 0101 0011 1877 { 52, 12, 0x24 }, // 0000 0010 0100 1878 { 53, 12, 0x37 }, // 0000 0011 0111 1879 { 54, 12, 0x38 }, // 0000 0011 1000 1880 { 55, 12, 0x27 }, // 0000 0010 0111 1881 { 56, 12, 0x28 }, // 0000 0010 1000 1882 { 57, 12, 0x58 }, // 0000 0101 1000 1883 { 58, 12, 0x59 }, // 0000 0101 1001 1884 { 59, 12, 0x2B }, // 0000 0010 1011 1885 { 60, 12, 0x2C }, // 0000 0010 1100 1886 { 61, 12, 0x5A }, // 0000 0101 1010 1887 { 62, 12, 0x66 }, // 0000 0110 0110 1888 { 63, 12, 0x67 }, // 0000 0110 0111 1889 { 64, 10, 0xF }, // 0000 0011 11 1890 { 128, 12, 0xC8 }, // 0000 1100 1000 1891 { 192, 12, 0xC9 }, // 0000 1100 1001 1892 { 256, 12, 0x5B }, // 0000 0101 1011 1893 { 320, 12, 0x33 }, // 0000 0011 0011 1894 { 384, 12, 0x34 }, // 0000 0011 0100 1895 { 448, 12, 0x35 }, // 0000 0011 0101 1896 { 512, 13, 0x6C }, // 0000 0011 0110 0 1897 { 576, 13, 0x6D }, // 0000 0011 0110 1 1898 { 640, 13, 0x4A }, // 0000 0010 0101 0 1899 { 704, 13, 0x4B }, // 0000 0010 0101 1 1900 { 768, 13, 0x4C }, // 0000 0010 0110 0 1901 { 832, 13, 0x4D }, // 0000 0010 0110 1 1902 { 896, 13, 0x72 }, // 0000 0011 1001 0 1903 { 960, 13, 0x73 }, // 0000 0011 1001 1 1904 { 1024, 13, 0x74 }, // 0000 0011 1010 0 1905 { 1088, 13, 0x75 }, // 0000 0011 1010 1 1906 { 1152, 13, 0x76 }, // 0000 0011 1011 0 1907 { 1216, 13, 0x77 }, // 0000 0011 1011 1 1908 { 1280, 13, 0x52 }, // 0000 0010 1001 0 1909 { 1344, 13, 0x53 }, // 0000 0010 1001 1 1910 { 1408, 13, 0x54 }, // 0000 0010 1010 0 1911 { 1472, 13, 0x55 }, // 0000 0010 1010 1 1912 { 1536, 13, 0x5A }, // 0000 0010 1101 0 1913 { 1600, 13, 0x5B }, // 0000 0010 1101 1 1914 { 1664, 13, 0x64 }, // 0000 0011 0010 0 1915 { 1728, 13, 0x65 }, // 0000 0011 0010 1 1916 { 1792, 11, 0x8 }, // 0000 0001 000 1917 { 1856, 11, 0xC }, // 0000 0001 100 1918 { 1920, 11, 0xD }, // 0000 0001 101 1919 { 1984, 12, 0x12 }, // 0000 0001 0010 1920 { 2048, 12, 0x13 }, // 0000 0001 0011 1921 { 2112, 12, 0x14 }, // 0000 0001 0100 1922 { 2176, 12, 0x15 }, // 0000 0001 0101 1923 { 2240, 12, 0x16 }, // 0000 0001 0110 1924 { 2304, 12, 0x17 }, // 0000 0001 0111 1925 { 2368, 12, 0x1C }, // 0000 0001 1100 1926 { 2432, 12, 0x1D }, // 0000 0001 1101 1927 { 2496, 12, 0x1E }, // 0000 0001 1110 1928 { 2560, 12, 0x1F } // 0000 0001 1111 1929 }; 1930 1931 1932 void PDFWriterImpl::putG4Span( long i_nSpan, bool i_bWhitePixel, BitStreamState& io_rState ) 1933 { 1934 const PixelCode* pTable = i_bWhitePixel ? WhitePixelCodes : BlackPixelCodes; 1935 // maximum encoded span is 2560 consecutive pixels 1936 while( i_nSpan > 2623 ) 1937 { 1938 // write 2560 bits, that is entry (63 + (2560 >> 6)) == 103 in the appropriate table 1939 putG4Bits( pTable[103].mnCodeBits, pTable[103].mnCode, io_rState ); 1940 i_nSpan -= pTable[103].mnEncodedPixels; 1941 } 1942 // write multiples of 64 pixels up to 2560 1943 if( i_nSpan > 63 ) 1944 { 1945 sal_uInt32 nTabIndex = 63 + (i_nSpan >> 6); 1946 OSL_ASSERT( pTable[nTabIndex].mnEncodedPixels == static_cast<sal_uInt32>(64*(i_nSpan >> 6)) ); 1947 putG4Bits( pTable[nTabIndex].mnCodeBits, pTable[nTabIndex].mnCode, io_rState ); 1948 i_nSpan -= pTable[nTabIndex].mnEncodedPixels; 1949 } 1950 putG4Bits( pTable[i_nSpan].mnCodeBits, pTable[i_nSpan].mnCode, io_rState ); 1951 } 1952 1953 void PDFWriterImpl::writeG4Stream( BitmapReadAccess* i_pBitmap ) 1954 { 1955 long nW = i_pBitmap->Width(); 1956 long nH = i_pBitmap->Height(); 1957 if( nW <= 0 || nH <= 0 ) 1958 return; 1959 if( i_pBitmap->GetBitCount() != 1 ) 1960 return; 1961 1962 BitStreamState aBitState; 1963 1964 // the first reference line is virtual and completely empty 1965 const Scanline pFirstRefLine = (Scanline)rtl_allocateZeroMemory( nW/8 + 1 ); 1966 Scanline pRefLine = pFirstRefLine; 1967 for( long nY = 0; nY < nH; nY++ ) 1968 { 1969 const Scanline pCurLine = i_pBitmap->GetScanline( nY ); 1970 long nLineIndex = 0; 1971 bool bRunSet = (*pCurLine & 0x80) ? true : false; 1972 bool bRefSet = (*pRefLine & 0x80) ? true : false; 1973 long nRunIndex1 = bRunSet ? 0 : findBitRun( pCurLine, 0, nW, bRunSet ); 1974 long nRefIndex1 = bRefSet ? 0 : findBitRun( pRefLine, 0, nW, bRefSet ); 1975 for( ; nLineIndex < nW; ) 1976 { 1977 long nRefIndex2 = findBitRun( pRefLine, nRefIndex1, nW, isSet( pRefLine, nRefIndex1 ) ); 1978 if( nRefIndex2 >= nRunIndex1 ) 1979 { 1980 long nDiff = nRefIndex1 - nRunIndex1; 1981 if( -3 <= nDiff && nDiff <= 3 ) 1982 { // vertical coding 1983 static const struct 1984 { 1985 sal_uInt32 mnCodeBits; 1986 sal_uInt32 mnCode; 1987 } VerticalCodes[7] = { 1988 { 7, 0x03 }, // 0000 011 1989 { 6, 0x03 }, // 0000 11 1990 { 3, 0x03 }, // 011 1991 { 1, 0x1 }, // 1 1992 { 3, 0x2 }, // 010 1993 { 6, 0x02 }, // 0000 10 1994 { 7, 0x02 } // 0000 010 1995 }; 1996 // convert to index 1997 nDiff += 3; 1998 1999 // emit diff code 2000 putG4Bits( VerticalCodes[nDiff].mnCodeBits, VerticalCodes[nDiff].mnCode, aBitState ); 2001 nLineIndex = nRunIndex1; 2002 } 2003 else 2004 { // difference too large, horizontal coding 2005 // emit horz code 001 2006 putG4Bits( 3, 0x1, aBitState ); 2007 long nRunIndex2 = findBitRun( pCurLine, nRunIndex1, nW, isSet( pCurLine, nRunIndex1 ) ); 2008 bool bWhiteFirst = ( nLineIndex + nRunIndex1 == 0 || ! isSet( pCurLine, nLineIndex ) ); 2009 putG4Span( nRunIndex1 - nLineIndex, bWhiteFirst, aBitState ); 2010 putG4Span( nRunIndex2 - nRunIndex1, ! bWhiteFirst, aBitState ); 2011 nLineIndex = nRunIndex2; 2012 } 2013 } 2014 else 2015 { // emit pass code 0001 2016 putG4Bits( 4, 0x1, aBitState ); 2017 nLineIndex = nRefIndex2; 2018 } 2019 if( nLineIndex < nW ) 2020 { 2021 bool bSet = isSet( pCurLine, nLineIndex ); 2022 nRunIndex1 = findBitRun( pCurLine, nLineIndex, nW, bSet ); 2023 nRefIndex1 = findBitRun( pRefLine, nLineIndex, nW, ! bSet ); 2024 nRefIndex1 = findBitRun( pRefLine, nRefIndex1, nW, bSet ); 2025 } 2026 } 2027 2028 // the current line is the reference for the next line 2029 pRefLine = pCurLine; 2030 } 2031 // terminate strip with EOFB 2032 putG4Bits( 12, 1, aBitState ); 2033 putG4Bits( 12, 1, aBitState ); 2034 if( aBitState.mnNextBitPos != 8 ) 2035 { 2036 writeBuffer( aBitState.getByte(), 1 ); 2037 aBitState.flush(); 2038 } 2039 2040 rtl_freeMemory( pFirstRefLine ); 2041 } 2042