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