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