diff options
Diffstat (limited to 'vcl/source/gdi')
-rw-r--r-- | vcl/source/gdi/metaact.cxx | 34 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter.cxx | 24 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 610 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.hxx | 172 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl2.cxx | 507 | ||||
-rw-r--r-- | vcl/source/gdi/pngread.cxx | 28 | ||||
-rw-r--r-- | vcl/source/gdi/print2.cxx | 1 |
7 files changed, 813 insertions, 563 deletions
diff --git a/vcl/source/gdi/metaact.cxx b/vcl/source/gdi/metaact.cxx index 8c1545758c3b..79d875542509 100644 --- a/vcl/source/gdi/metaact.cxx +++ b/vcl/source/gdi/metaact.cxx @@ -1441,19 +1441,35 @@ void MetaTextArrayAction::Read( SvStream& rIStm, ImplMetaReadData* pData ) rIStm >> mnLen; rIStm >> nAryLen; + if ( mnIndex > mnLen ) + { + mnIndex = 0; + mpDXAry = 0; + return; + } + if( nAryLen ) { // #i9762#, #106172# Ensure that DX array is at least mnLen entries long - const ULONG nIntAryLen( Max(nAryLen, static_cast<sal_uInt32>(mnLen)) ); - mpDXAry = new sal_Int32[ nIntAryLen ]; - - ULONG i; - for( i = 0UL; i < nAryLen; i++ ) - rIStm >> mpDXAry[ i ]; + if ( mnLen >= nAryLen ) + { + mpDXAry = new (std::nothrow)sal_Int32[ mnLen ]; + if ( mpDXAry ) + { + ULONG i; + for( i = 0UL; i < nAryLen; i++ ) + rIStm >> mpDXAry[ i ]; - // #106172# setup remainder - for( ; i < nIntAryLen; i++ ) - mpDXAry[ i ] = 0; + // #106172# setup remainder + for( ; i < mnLen; i++ ) + mpDXAry[ i ] = 0; + } + } + else + { + mpDXAry = NULL; + return; + } } else mpDXAry = NULL; diff --git a/vcl/source/gdi/pdfwriter.cxx b/vcl/source/gdi/pdfwriter.cxx index 969bc51b3cac..23ce1dfa6169 100644 --- a/vcl/source/gdi/pdfwriter.cxx +++ b/vcl/source/gdi/pdfwriter.cxx @@ -38,9 +38,9 @@ PDFWriter::AnyWidget::~AnyWidget() { } -PDFWriter::PDFWriter( const PDFWriter::PDFWriterContext& rContext ) +PDFWriter::PDFWriter( const PDFWriter::PDFWriterContext& rContext, const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc ) : - pImplementation( new PDFWriterImpl( rContext, *this ) ) + pImplementation( new PDFWriterImpl( rContext, xEnc, *this ) ) { } @@ -69,16 +69,6 @@ PDFWriter::PDFVersion PDFWriter::GetVersion() const return ((PDFWriterImpl*)pImplementation)->getVersion(); } -void PDFWriter::SetDocInfo( const PDFDocInfo& rInfo ) -{ - ((PDFWriterImpl*)pImplementation)->setDocInfo( rInfo ); -} - -const PDFDocInfo& PDFWriter::GetDocInfo() const -{ - return ((PDFWriterImpl*)pImplementation)->getDocInfo(); -} - void PDFWriter::SetDocumentLocale( const com::sun::star::lang::Locale& rLoc ) { ((PDFWriterImpl*)pImplementation)->setDocumentLocale( rLoc ); @@ -570,7 +560,17 @@ std::set< PDFWriter::ErrorCode > PDFWriter::GetErrors() return ((PDFWriterImpl*)pImplementation)->getErrors(); } +com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder > +PDFWriter::InitEncryption( const rtl::OUString& i_rOwnerPassword, + const rtl::OUString& i_rUserPassword, + bool b128Bit + ) +{ + return PDFWriterImpl::initEncryption( i_rOwnerPassword, i_rUserPassword, b128Bit ); +} + void PDFWriter::PlayMetafile( const GDIMetaFile& i_rMTF, const vcl::PDFWriter::PlayMetafileContext& i_rPlayContext, PDFExtOutDevData* i_pData ) { ((PDFWriterImpl*)pImplementation)->playMetafile( i_rMTF, i_pData, i_rPlayContext, NULL); } + diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 5d75c829da8a..325ccef1c3a6 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -113,12 +113,10 @@ void doTestCode() aContext.Version = PDFWriter::PDF_1_4; aContext.Tagged = true; aContext.InitialPage = 2; + aContext.DocumentInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) ); + aContext.DocumentInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) ); PDFWriter aWriter( aContext ); - PDFDocInfo aDocInfo; - aDocInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) ); - aDocInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) ); - aWriter.SetDocInfo( aDocInfo ); aWriter.NewPage( 595, 842 ); aWriter.BeginStructureElement( PDFWriter::Document ); // set duration of 3 sec for first page @@ -496,6 +494,12 @@ static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; static inline double pixelToPoint( double px ) { return px/fDivisor; } static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); } +const sal_uInt8 PDFWriterImpl::s_nPadString[32] = +{ + 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, + 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A +}; + static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer ) { static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', @@ -1693,7 +1697,9 @@ void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal * class PDFWriterImpl */ -PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, PDFWriter& i_rOuterFace ) + PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, + const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc, + PDFWriter& i_rOuterFace) : m_pReferenceDevice( NULL ), m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ), @@ -1714,9 +1720,6 @@ PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, PDFWr m_aCipher( (rtlCipher)NULL ), m_aDigest( NULL ), m_bEncryptThisStream( false ), - m_aDocID( 32 ), - m_aCreationDateString( 64 ), - m_aCreationMetaDateString( 64 ), m_pEncryptionBuffer( NULL ), m_nEncryptionBufferSize( 0 ), m_bIsPDF_A1( false ), @@ -1759,14 +1762,37 @@ PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, PDFWr m_bOpen = true; -/* prepare the cypher engine, can be done in CTOR, free in DTOR */ + // setup DocInfo + setupDocInfo(); + /* prepare the cypher engine, can be done in CTOR, free in DTOR */ m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); m_aDigest = rtl_digest_createMD5(); -/* the size of the Codec default maximum */ + /* the size of the Codec default maximum */ checkEncryptionBufferSize( 0x4000 ); + if( xEnc.is() ) + prepareEncryption( xEnc ); + + if( m_aContext.Encryption.Encrypt() ) + { + // sanity check + if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE || + m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE || + m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH + ) + { + // the field lengths are invalid ? This was not setup by initEncryption. + // do not encrypt after all + m_aContext.Encryption.OValue.clear(); + m_aContext.Encryption.UValue.clear(); + OSL_ENSURE( 0, "encryption data failed sanity check, encryption disabled" ); + } + else // setup key lengths + m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength ); + } + // write header OStringBuffer aBuffer( 20 ); aBuffer.append( "%PDF-" ); @@ -1812,139 +1838,138 @@ PDFWriterImpl::~PDFWriterImpl() rtl_freeMemory( m_pEncryptionBuffer ); } -void PDFWriterImpl::setDocInfo( const PDFDocInfo& rInfo ) +void PDFWriterImpl::setupDocInfo() { - m_aDocInfo.Title = rInfo.Title; - m_aDocInfo.Author = rInfo.Author; - m_aDocInfo.Subject = rInfo.Subject; - m_aDocInfo.Keywords = rInfo.Keywords; - m_aDocInfo.Creator = rInfo.Creator; - m_aDocInfo.Producer = rInfo.Producer; + std::vector< sal_uInt8 > aId; + computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString ); + if( m_aContext.Encryption.DocumentIdentifier.empty() ) + m_aContext.Encryption.DocumentIdentifier = aId; +} -//build the document id +void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier, + const vcl::PDFWriter::PDFDocInfo& i_rDocInfo, + rtl::OString& o_rCString1, + rtl::OString& o_rCString2 + ) +{ + o_rIdentifier.clear(); + + //build the document id rtl::OString aInfoValuesOut; OStringBuffer aID( 1024 ); - if( m_aDocInfo.Title.Len() ) - appendUnicodeTextString( m_aDocInfo.Title, aID ); - if( m_aDocInfo.Author.Len() ) - appendUnicodeTextString( m_aDocInfo.Author, aID ); - if( m_aDocInfo.Subject.Len() ) - appendUnicodeTextString( m_aDocInfo.Subject, aID ); - if( m_aDocInfo.Keywords.Len() ) - appendUnicodeTextString( m_aDocInfo.Keywords, aID ); - if( m_aDocInfo.Creator.Len() ) - appendUnicodeTextString( m_aDocInfo.Creator, aID ); - if( m_aDocInfo.Producer.Len() ) - appendUnicodeTextString( m_aDocInfo.Producer, aID ); + if( i_rDocInfo.Title.Len() ) + appendUnicodeTextString( i_rDocInfo.Title, aID ); + if( i_rDocInfo.Author.Len() ) + appendUnicodeTextString( i_rDocInfo.Author, aID ); + if( i_rDocInfo.Subject.Len() ) + appendUnicodeTextString( i_rDocInfo.Subject, aID ); + if( i_rDocInfo.Keywords.Len() ) + appendUnicodeTextString( i_rDocInfo.Keywords, aID ); + if( i_rDocInfo.Creator.Len() ) + appendUnicodeTextString( i_rDocInfo.Creator, aID ); + if( i_rDocInfo.Producer.Len() ) + appendUnicodeTextString( i_rDocInfo.Producer, aID ); TimeValue aTVal, aGMT; oslDateTime aDT; osl_getSystemTime( &aGMT ); osl_getLocalTimeFromSystemTime( &aGMT, &aTVal ); osl_getDateTimeFromTimeValue( &aTVal, &aDT ); - m_aCreationDateString.append( "D:" ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); -//--> i59651, we fill the Metadata date string as well, if PDF/A is requested - if( m_bIsPDF_A1 ) - { -// according to ISO 19005-1:2005 6.7.3 the date is corrected for -// local time zone offset UTC only, whereas Acrobat 8 seems -// to use the localtime notation only -// according to a raccomandation in XMP Specification (Jan 2004, page 75) -// the Acrobat way seems the right approach - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); - m_aCreationMetaDateString.append( "-" ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); - m_aCreationMetaDateString.append( "-" ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); - m_aCreationMetaDateString.append( "T" ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); - m_aCreationMetaDateString.append( ":" ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); - m_aCreationMetaDateString.append( ":" ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); - } + rtl::OStringBuffer aCreationDateString(64), aCreationMetaDateString(64); + aCreationDateString.append( "D:" ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); + + //--> i59651, we fill the Metadata date string as well, if PDF/A is requested + // according to ISO 19005-1:2005 6.7.3 the date is corrected for + // local time zone offset UTC only, whereas Acrobat 8 seems + // to use the localtime notation only + // according to a raccomandation in XMP Specification (Jan 2004, page 75) + // the Acrobat way seems the right approach + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); + aCreationMetaDateString.append( "-" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); + aCreationMetaDateString.append( "-" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); + aCreationMetaDateString.append( "T" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); + aCreationMetaDateString.append( ":" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); + aCreationMetaDateString.append( ":" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); + sal_uInt32 nDelta = 0; if( aGMT.Seconds > aTVal.Seconds ) { - m_aCreationDateString.append( "-" ); + aCreationDateString.append( "-" ); nDelta = aGMT.Seconds-aTVal.Seconds; - if( m_bIsPDF_A1 ) - m_aCreationMetaDateString.append( "-" ); + aCreationMetaDateString.append( "-" ); } else if( aGMT.Seconds < aTVal.Seconds ) { - m_aCreationDateString.append( "+" ); + aCreationDateString.append( "+" ); nDelta = aTVal.Seconds-aGMT.Seconds; - if( m_bIsPDF_A1 ) - m_aCreationMetaDateString.append( "+" ); + aCreationMetaDateString.append( "+" ); } else { - m_aCreationDateString.append( "Z" ); - if( m_bIsPDF_A1 ) - m_aCreationMetaDateString.append( "Z" ); + aCreationDateString.append( "Z" ); + aCreationMetaDateString.append( "Z" ); } if( nDelta ) { - m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); - m_aCreationDateString.append( "'" ); - m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); - m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); - if( m_bIsPDF_A1 ) - { - m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); - m_aCreationMetaDateString.append( ":" ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); - m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); - } + aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); + aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); + aCreationDateString.append( "'" ); + aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); + aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); + + aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); + aCreationMetaDateString.append( ":" ); + aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); + aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); } - m_aCreationDateString.append( "'" ); - aID.append( m_aCreationDateString.getStr(), m_aCreationDateString.getLength() ); + aCreationDateString.append( "'" ); + aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() ); aInfoValuesOut = aID.makeStringAndClear(); + o_rCString1 = aCreationDateString.makeStringAndClear(); + o_rCString2 = aCreationMetaDateString.makeStringAndClear(); - DBG_ASSERT( m_aDigest != NULL, "PDFWrite_Impl::setDocInfo: cannot obtain a digest object !" ); - - m_aDocID.setLength( 0 ); - if( m_aDigest ) + rtlDigest aDigest = rtl_digest_createMD5(); + OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" ); + if( aDigest ) { - osl_getSystemTime( &aGMT ); - rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, &aGMT, sizeof( aGMT ) ); - if( nError == rtl_Digest_E_None ) - nError = rtl_digest_updateMD5( m_aDigest, m_aContext.URL.getStr(), m_aContext.URL.getLength()*sizeof(sal_Unicode) ); + rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) ); if( nError == rtl_Digest_E_None ) - nError = rtl_digest_updateMD5( m_aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() ); + nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() ); if( nError == rtl_Digest_E_None ) { -//the binary form of the doc id is needed for encryption stuff - rtl_digest_getMD5( m_aDigest, m_nDocID, 16 ); - for( unsigned int i = 0; i < 16; i++ ) - appendHex( m_nDocID[i], m_aDocID ); + o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 ); + //the binary form of the doc id is needed for encryption stuff + rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 ); } } } @@ -1957,7 +1982,7 @@ append the string as unicode hex, encrypted if needed inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer ) { rOutBuffer.append( "<" ); - if( m_aContext.Encrypt ) + if( m_aContext.Encryption.Encrypt() ) { const sal_Unicode* pStr = rInString.getStr(); sal_Int32 nLen = rInString.getLength(); @@ -1994,7 +2019,7 @@ inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInSt rOutBuffer.append( "(" ); sal_Int32 nChars = rInString.getLength(); //check for encryption, if ok, encrypt the string, then convert with appndLiteralString - if( m_aContext.Encrypt && checkEncryptionBufferSize( nChars ) ) + if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) ) { //encrypt the string in a buffer, then append it enableStringEncryption( nInObjectNumber ); @@ -2384,9 +2409,6 @@ SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectDa sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ) { - if( m_aContext.Encrypt && m_aPages.empty() ) - initEncryption(); - endPage(); m_nCurrentPage = m_aPages.size(); m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) ); @@ -5881,7 +5903,7 @@ bool PDFWriterImpl::emitCatalog() } // viewer preferences, if we had some, then emit if( m_aContext.HideViewerToolbar || - ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aDocInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) || + ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) || m_aContext.HideViewerMenubar || m_aContext.HideViewerWindowControls || m_aContext.FitWindow || m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) || @@ -5898,7 +5920,7 @@ bool PDFWriterImpl::emitCatalog() aLine.append( "/FitWindow true\n" ); if( m_aContext.CenterWindow ) aLine.append( "/CenterWindow true\n" ); - if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aDocInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) + if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) aLine.append( "/DisplayDocTitle true\n" ); if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) aLine.append( "/Direction/R2L\n" ); @@ -6002,40 +6024,40 @@ sal_Int32 PDFWriterImpl::emitInfoDict( ) aLine.append( nObject ); aLine.append( " 0 obj\n" "<<" ); - if( m_aDocInfo.Title.Len() ) + if( m_aContext.DocumentInfo.Title.Len() ) { aLine.append( "/Title" ); - appendUnicodeTextStringEncrypt( m_aDocInfo.Title, nObject, aLine ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine ); aLine.append( "\n" ); } - if( m_aDocInfo.Author.Len() ) + if( m_aContext.DocumentInfo.Author.Len() ) { aLine.append( "/Author" ); - appendUnicodeTextStringEncrypt( m_aDocInfo.Author, nObject, aLine ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine ); aLine.append( "\n" ); } - if( m_aDocInfo.Subject.Len() ) + if( m_aContext.DocumentInfo.Subject.Len() ) { aLine.append( "/Subject" ); - appendUnicodeTextStringEncrypt( m_aDocInfo.Subject, nObject, aLine ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine ); aLine.append( "\n" ); } - if( m_aDocInfo.Keywords.Len() ) + if( m_aContext.DocumentInfo.Keywords.Len() ) { aLine.append( "/Keywords" ); - appendUnicodeTextStringEncrypt( m_aDocInfo.Keywords, nObject, aLine ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine ); aLine.append( "\n" ); } - if( m_aDocInfo.Creator.Len() ) + if( m_aContext.DocumentInfo.Creator.Len() ) { aLine.append( "/Creator" ); - appendUnicodeTextStringEncrypt( m_aDocInfo.Creator, nObject, aLine ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine ); aLine.append( "\n" ); } - if( m_aDocInfo.Producer.Len() ) + if( m_aContext.DocumentInfo.Producer.Len() ) { aLine.append( "/Producer" ); - appendUnicodeTextStringEncrypt( m_aDocInfo.Producer, nObject, aLine ); + appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine ); aLine.append( "\n" ); } @@ -6281,45 +6303,45 @@ sal_Int32 PDFWriterImpl::emitDocumentMetadata() aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" ); aMetadataStream.append( " </rdf:Description>\n" ); //... Dublin Core properties go here - if( m_aDocInfo.Title.Len() || - m_aDocInfo.Author.Len() || - m_aDocInfo.Subject.Len() ) + if( m_aContext.DocumentInfo.Title.Len() || + m_aContext.DocumentInfo.Author.Len() || + m_aContext.DocumentInfo.Subject.Len() ) { aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" ); - if( m_aDocInfo.Title.Len() ) + if( m_aContext.DocumentInfo.Title.Len() ) { // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) aMetadataStream.append( " <dc:title>\n" ); aMetadataStream.append( " <rdf:Alt>\n" ); aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); rtl::OUString aTitle; - escapeStringXML( m_aDocInfo.Title, aTitle ); + escapeStringXML( m_aContext.DocumentInfo.Title, aTitle ); aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) ); aMetadataStream.append( "</rdf:li>\n" ); aMetadataStream.append( " </rdf:Alt>\n" ); aMetadataStream.append( " </dc:title>\n" ); } - if( m_aDocInfo.Author.Len() ) + if( m_aContext.DocumentInfo.Author.Len() ) { aMetadataStream.append( " <dc:creator>\n" ); aMetadataStream.append( " <rdf:Seq>\n" ); aMetadataStream.append( " <rdf:li>" ); rtl::OUString aAuthor; - escapeStringXML( m_aDocInfo.Author, aAuthor ); + escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor ); aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) ); aMetadataStream.append( "</rdf:li>\n" ); aMetadataStream.append( " </rdf:Seq>\n" ); aMetadataStream.append( " </dc:creator>\n" ); } - if( m_aDocInfo.Subject.Len() ) + if( m_aContext.DocumentInfo.Subject.Len() ) { // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) aMetadataStream.append( " <dc:description>\n" ); aMetadataStream.append( " <rdf:Alt>\n" ); aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); rtl::OUString aSubject; - escapeStringXML( m_aDocInfo.Subject, aSubject ); + escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject ); aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) ); aMetadataStream.append( "</rdf:li>\n" ); aMetadataStream.append( " </rdf:Alt>\n" ); @@ -6329,24 +6351,24 @@ sal_Int32 PDFWriterImpl::emitDocumentMetadata() } //... PDF properties go here - if( m_aDocInfo.Producer.Len() || - m_aDocInfo.Keywords.Len() ) + if( m_aContext.DocumentInfo.Producer.Len() || + m_aContext.DocumentInfo.Keywords.Len() ) { aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" ); - if( m_aDocInfo.Producer.Len() ) + if( m_aContext.DocumentInfo.Producer.Len() ) { aMetadataStream.append( " <pdf:Producer>" ); rtl::OUString aProducer; - escapeStringXML( m_aDocInfo.Producer, aProducer ); + escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer ); aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) ); aMetadataStream.append( "</pdf:Producer>\n" ); } - if( m_aDocInfo.Keywords.Len() ) + if( m_aContext.DocumentInfo.Keywords.Len() ) { aMetadataStream.append( " <pdf:Keywords>" ); rtl::OUString aKeywords; - escapeStringXML( m_aDocInfo.Keywords, aKeywords ); + escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords ); aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) ); aMetadataStream.append( "</pdf:Keywords>\n" ); } @@ -6355,11 +6377,11 @@ sal_Int32 PDFWriterImpl::emitDocumentMetadata() aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" ); - if( m_aDocInfo.Creator.Len() ) + if( m_aContext.DocumentInfo.Creator.Len() ) { aMetadataStream.append( " <xmp:CreatorTool>" ); rtl::OUString aCreator; - escapeStringXML( m_aDocInfo.Creator, aCreator ); + escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator ); aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) ); aMetadataStream.append( "</xmp:CreatorTool>\n" ); } @@ -6415,7 +6437,7 @@ bool PDFWriterImpl::emitTrailer() sal_Int32 nSecObject = 0; - if( m_aContext.Encrypt == true ) + if( m_aContext.Encryption.Encrypt() ) { //emit the security information //must be emitted as indirect dictionary object, since @@ -6429,16 +6451,16 @@ bool PDFWriterImpl::emitTrailer() aLineS.append( " 0 obj\n" "<</Filter/Standard/V " ); // check the version - if( m_aContext.Security128bit == true ) + if( m_aContext.Encryption.Security128bit ) aLineS.append( "2/Length 128/R 3" ); else aLineS.append( "1/R 2" ); // emit the owner password, must not be encrypted aLineS.append( "/O(" ); - appendLiteralString( (const sal_Char*)m_nEncryptedOwnerPassword, 32, aLineS ); + appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS ); aLineS.append( ")/U(" ); - appendLiteralString( (const sal_Char*)m_nEncryptedUserPassword, 32, aLineS ); + appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS ); aLineS.append( ")/P " );// the permission set aLineS.append( m_nAccessPermissions ); aLineS.append( ">>\nendobj\n\n" ); @@ -6504,13 +6526,21 @@ bool PDFWriterImpl::emitTrailer() aLine.append( nDocInfoObject ); aLine.append( " 0 R\n" ); } - if( m_aDocID.getLength() ) + if( ! m_aContext.Encryption.DocumentIdentifier.empty() ) { aLine.append( "/ID [ <" ); - aLine.append( m_aDocID.getStr(), m_aDocID.getLength() ); + for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); + it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) + { + appendHex( sal_Int8(*it), aLine ); + } aLine.append( ">\n" "<" ); - aLine.append( m_aDocID.getStr(), m_aDocID.getLength() ); + for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); + it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) + { + appendHex( sal_Int8(*it), aLine ); + } aLine.append( "> ]\n" ); } if( aDocChecksum.getLength() ) @@ -7403,7 +7433,14 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bT // try to handle ligatures and such if( i < nGlyphs-1 ) { - pUnicodesPerGlyph[i] = nChars = pCharPosAry[i+1] - pCharPosAry[i]; + nChars = pCharPosAry[i+1] - pCharPosAry[i]; + // #i115618# fix for simple RTL+CTL cases + // TODO: sanitize for RTL ligatures, more complex CTL, etc. + if( nChars < 0 ) + nChars = -nChars; + else if( nChars == 0 ) + nChars = 1; + pUnicodesPerGlyph[i] = nChars; for( int n = 1; n < nChars; n++ ) aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) ); } @@ -9661,7 +9698,7 @@ bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) aLine.append( "[ /Indexed/DeviceRGB " ); aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) ); aLine.append( "\n<" ); - if( m_aContext.Encrypt ) + if( m_aContext.Encryption.Encrypt() ) { enableStringEncryption( rObject.m_nObject ); //check encryption buffer size @@ -12047,268 +12084,5 @@ void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream } } -/************************************************************* -begin i12626 methods - -Implements Algorithm 3.2, step 1 only -*/ -void PDFWriterImpl::padPassword( rtl::OUString aPassword, sal_uInt8 *paPasswordTarget ) -{ -// get ansi-1252 version of the password string CHECKIT ! i12626 - rtl::OString aString = rtl::OUStringToOString( aPassword, RTL_TEXTENCODING_MS_1252 ); - -//copy the string to the target - sal_Int32 nToCopy = ( aString.getLength() < 32 ) ? aString.getLength() : 32; - sal_Int32 nCurrentChar; - - for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ ) - paPasswordTarget[nCurrentChar] = (sal_uInt8)( aString.getStr()[nCurrentChar] ); - -//pad it - if( nCurrentChar < 32 ) - {//fill with standard byte string - sal_Int32 i,y; - for( i = nCurrentChar, y = 0 ; i < 32; i++, y++ ) - paPasswordTarget[i] = m_nPadString[y]; - } -} - -/********************************** -Algorithm 3.2 Compute the encryption key used - -step 1 should already be done before calling, the paThePaddedPassword parameter should contain -the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter, -it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used - -TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec. - -*/ -void PDFWriterImpl::computeEncryptionKey(sal_uInt8 *paThePaddedPassword, sal_uInt8 *paEncryptionKey ) -{ -//step 2 - if( m_aDigest ) - { - rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, paThePaddedPassword, ENCRYPTED_PWD_SIZE ); -//step 3 - if( nError == rtl_Digest_E_None ) - nError = rtl_digest_updateMD5( m_aDigest, m_nEncryptedOwnerPassword , sizeof( m_nEncryptedOwnerPassword ) ); -//Step 4 - sal_uInt8 nPerm[4]; - - nPerm[0] = (sal_uInt8)m_nAccessPermissions; - nPerm[1] = (sal_uInt8)( m_nAccessPermissions >> 8 ); - nPerm[2] = (sal_uInt8)( m_nAccessPermissions >> 16 ); - nPerm[3] = (sal_uInt8)( m_nAccessPermissions >> 24 ); - - if( nError == rtl_Digest_E_None ) - nError = rtl_digest_updateMD5( m_aDigest, nPerm , sizeof( nPerm ) ); - -//step 5, get the document ID, binary form - if( nError == rtl_Digest_E_None ) - nError = rtl_digest_updateMD5( m_aDigest, m_nDocID , sizeof( m_nDocID ) ); -//get the digest - sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; - if( nError == rtl_Digest_E_None ) - { - rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) ); - -//step 6, only if 128 bit - if( m_aContext.Security128bit ) - { - for( sal_Int32 i = 0; i < 50; i++ ) - { - nError = rtl_digest_updateMD5( m_aDigest, &nMD5Sum, sizeof( nMD5Sum ) ); - if( nError != rtl_Digest_E_None ) - break; - rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) ); - } - } - } -//Step 7 - for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ ) - paEncryptionKey[i] = nMD5Sum[i]; - } -} - -/********************************** -Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member -the step numbers down here correspond to the ones in PDF v.1.4 specfication -*/ -void PDFWriterImpl::computeODictionaryValue() -{ -//step 1 already done, data is in m_nPaddedOwnerPassword -//step 2 - if( m_aDigest ) - { - rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, &m_nPaddedOwnerPassword, sizeof( m_nPaddedOwnerPassword ) ); - if( nError == rtl_Digest_E_None ) - { - sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; - - rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof(nMD5Sum) ); -//step 3, only if 128 bit - if( m_aContext.Security128bit ) - { - sal_Int32 i; - for( i = 0; i < 50; i++ ) - { - nError = rtl_digest_updateMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) ); - if( nError != rtl_Digest_E_None ) - break; - rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) ); - } - } -//Step 4, the key is in nMD5Sum -//step 5 already done, data is in m_nPaddedUserPassword -//step 6 - rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, - nMD5Sum, m_nKeyLength , NULL, 0 ); -// encrypt the user password using the key set above - rtl_cipher_encodeARCFOUR( m_aCipher, m_nPaddedUserPassword, sizeof( m_nPaddedUserPassword ), // the data to be encrypted - m_nEncryptedOwnerPassword, sizeof( m_nEncryptedOwnerPassword ) ); //encrypted data, stored in class data member -//Step 7, only if 128 bit - if( m_aContext.Security128bit ) - { - sal_uInt32 i, y; - sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key - for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1 - { - for( y = 0; y < sizeof( nLocalKey ); y++ ) - nLocalKey[y] = (sal_uInt8)( nMD5Sum[y] ^ i ); - - rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, - nLocalKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area, on init can be NULL - rtl_cipher_encodeARCFOUR( m_aCipher, m_nEncryptedOwnerPassword, sizeof( m_nEncryptedOwnerPassword ), // the data to be encrypted - m_nEncryptedOwnerPassword, sizeof( m_nEncryptedOwnerPassword ) ); // encrypted data, can be the same as the input, encrypt "in place" -//step 8, store in class data member - } - } - } - } -} - -/********************************** -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) -*/ -void PDFWriterImpl::computeUDictionaryValue() -{ -//step 1, common to both 3.4 and 3.5 - computeEncryptionKey( m_nPaddedUserPassword , m_nEncryptionKey ); - - if( m_aContext.Security128bit == false ) - { -//3.4 -//step 2 and 3 - rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, - m_nEncryptionKey, 5 , // key and key length - NULL, 0 ); //destination data area -// encrypt the user password using the key set above, save for later use - rtl_cipher_encodeARCFOUR( m_aCipher, m_nPadString, sizeof( m_nPadString ), // the data to be encrypted - m_nEncryptedUserPassword, sizeof( m_nEncryptedUserPassword ) ); //encrypted data, stored in class data member - } - else - { -//or 3.5, for 128 bit security -//step6, initilize the last 16 bytes of the encrypted user password to 0 - for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sizeof( m_nEncryptedUserPassword ); i++) - m_nEncryptedUserPassword[i] = 0; -//step 2 - if( m_aDigest ) - { - rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, m_nPadString, sizeof( m_nPadString ) ); -//step 3 - if( nError == rtl_Digest_E_None ) - nError = rtl_digest_updateMD5( m_aDigest, m_nDocID , sizeof(m_nDocID) ); - - sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; - rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof(nMD5Sum) ); -//Step 4 - rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, - m_nEncryptionKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area - rtl_cipher_encodeARCFOUR( m_aCipher, nMD5Sum, sizeof( nMD5Sum ), // the data to be encrypted - m_nEncryptedUserPassword, sizeof( nMD5Sum ) ); //encrypted data, stored in class data member -//step 5 - sal_uInt32 i, y; - sal_uInt8 nLocalKey[SECUR_128BIT_KEY]; - - for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1 - { - for( y = 0; y < sizeof( nLocalKey ) ; y++ ) - nLocalKey[y] = (sal_uInt8)( m_nEncryptionKey[y] ^ i ); - - rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, - nLocalKey, SECUR_128BIT_KEY, // key and key length - NULL, 0 ); //destination data area, on init can be NULL - rtl_cipher_encodeARCFOUR( m_aCipher, m_nEncryptedUserPassword, SECUR_128BIT_KEY, // the data to be encrypted - m_nEncryptedUserPassword, SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place" - } - } - } -} - -/* init the encryption engine -1. init the document id, used both for building the document id and for building the encryption key(s) -2. build the encryption key following algorithms described in the PDF specification - */ -void PDFWriterImpl::initEncryption() -{ - m_aOwnerPassword = m_aContext.OwnerPassword; - m_aUserPassword = m_aContext.UserPassword; -/* password stuff computing, before sending out anything */ - DBG_ASSERT( m_aCipher != NULL, "PDFWriterImpl::initEncryption: a cipher (ARCFOUR) object is not available !" ); - DBG_ASSERT( m_aDigest != NULL, "PDFWriterImpl::initEncryption: a digest (MD5) object is not available !" ); - - if( m_aCipher && m_aDigest ) - { -//if there is no owner password, force it to the user password - if( m_aOwnerPassword.getLength() == 0 ) - m_aOwnerPassword = m_aUserPassword; - - initPadString(); -/* -1) pad passwords -*/ - padPassword( m_aOwnerPassword, m_nPaddedOwnerPassword ); - padPassword( m_aUserPassword, m_nPaddedUserPassword ); -/* -2) compute the access permissions, in numerical form - -the default value depends on the revision 2 (40 bit) or 3 (128 bit security): -- for 40 bit security the unused bit must be set to 1, since they are not used -- for 128 bit security the same bit must be preset to 0 and set later if needed -according to the table 3.15, pdf v 1.4 */ - m_nAccessPermissions = ( m_aContext.Security128bit ) ? 0xfffff0c0 : 0xffffffc0 ; - -/* check permissions for 40 bit security case */ - m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanPrintTheDocument ) ? 1 << 2 : 0; - m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanModifyTheContent ) ? 1 << 3 : 0; - m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanCopyOrExtract ) ? 1 << 4 : 0; - m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanAddOrModify ) ? 1 << 5 : 0; - m_nKeyLength = SECUR_40BIT_KEY; - m_nRC4KeyLength = SECUR_40BIT_KEY+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5 - - if( m_aContext.Security128bit ) - { - m_nKeyLength = SECUR_128BIT_KEY; - m_nRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum - // permitted value is 16 - m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanFillInteractive ) ? 1 << 8 : 0; - m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanExtractForAccessibility ) ? 1 << 9 : 0; - m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanAssemble ) ? 1 << 10 : 0; - m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanPrintFull ) ? 1 << 11 : 0; - } - computeODictionaryValue(); - computeUDictionaryValue(); - -//clear out exceding key values, prepares for generation number default to 0 as well -// see checkAndEnableStreamEncryption in pdfwriter_impl.hxx - sal_Int32 i, y; - for( i = m_nKeyLength, y = 0; y < 5 ; y++ ) - m_nEncryptionKey[i++] = 0; - } - else //either no cipher or no digest or both, something is wrong with memory or something else - m_aContext.Encrypt = false; //then turn the encryption off -} -/* end i12626 methods */ diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx index 9457aea5f0c2..5702bee23ea5 100644 --- a/vcl/source/gdi/pdfwriter_impl.hxx +++ b/vcl/source/gdi/pdfwriter_impl.hxx @@ -58,6 +58,7 @@ class ImplFontSelectData; class ImplFontMetricData; class FontSubsetInfo; class ZCodec; +class EncHashTransporter; // the maximum password length #define ENCRYPTED_PWD_SIZE 32 @@ -595,7 +596,6 @@ private: MapMode m_aMapMode; // PDFWriterImpl scaled units std::vector< PDFPage > m_aPages; - PDFDocInfo m_aDocInfo; /* maps object numbers to file offsets (needed for xref) */ std::vector< sal_uInt64 > m_aObjects; /* contains Bitmaps until they are written to the @@ -796,116 +796,37 @@ i12626 /* used to cipher the stream data and for password management */ rtlCipher m_aCipher; rtlDigest m_aDigest; -/* pad string used for password in Standard security handler */ - sal_uInt8 m_nPadString[ENCRYPTED_PWD_SIZE]; -/* the owner password, in clear text */ - rtl::OUString m_aOwnerPassword; -/* the padded owner password */ - sal_uInt8 m_nPaddedOwnerPassword[ENCRYPTED_PWD_SIZE]; -/* the encryption dictionary owner password, according to algorithm 3.3 */ - sal_uInt8 m_nEncryptedOwnerPassword[ENCRYPTED_PWD_SIZE]; -/* the user password, in clear text */ - rtl::OUString m_aUserPassword; -/* the padded user password */ - sal_uInt8 m_nPaddedUserPassword[ENCRYPTED_PWD_SIZE]; -/* the encryption dictionary user password, according to algorithm 3.4 or 3.5 depending on the - security handler revision */ - sal_uInt8 m_nEncryptedUserPassword[ENCRYPTED_PWD_SIZE]; - -/* the encryption key, formed with the user password according to algorithm 3.2, maximum length is 16 bytes + 3 + 2 - for 128 bit security */ - sal_uInt8 m_nEncryptionKey[MAXIMUM_RC4_KEY_LENGTH]; + /* pad string used for password in Standard security handler */ + static const sal_uInt8 s_nPadString[ENCRYPTED_PWD_SIZE]; + + /* the encryption key, formed with the user password according to algorithm 3.2, maximum length is 16 bytes + 3 + 2 + for 128 bit security */ sal_Int32 m_nKeyLength; // key length, 16 or 5 sal_Int32 m_nRC4KeyLength; // key length, 16 or 10, to be input to the algorith 3.1 -/* set to true if the following stream must be encrypted, used inside writeBuffer() */ + /* set to true if the following stream must be encrypted, used inside writeBuffer() */ sal_Bool m_bEncryptThisStream; -/* the numerical value of the access permissions, according to PDF spec, must be signed */ + /* the numerical value of the access permissions, according to PDF spec, must be signed */ sal_Int32 m_nAccessPermissions; -/* the document ID, the raw MD5 hash */ - sal_uInt8 m_nDocID[MD5_DIGEST_SIZE]; -/* string buffer to hold document ID, this is the output string */ - rtl::OStringBuffer m_aDocID; -/* string to hold the PDF creation date */ - rtl::OStringBuffer m_aCreationDateString; -/* string to hold the PDF creation date, for PDF/A metadata */ - rtl::OStringBuffer m_aCreationMetaDateString; -/* the buffer where the data are encrypted, dynamically allocated */ + /* string to hold the PDF creation date */ + rtl::OString m_aCreationDateString; + /* string to hold the PDF creation date, for PDF/A metadata */ + rtl::OString m_aCreationMetaDateString; + /* the buffer where the data are encrypted, dynamically allocated */ sal_uInt8 *m_pEncryptionBuffer; -/* size of the buffer */ + /* size of the buffer */ sal_Int32 m_nEncryptionBufferSize; -/* check and reallocate the buffer for encryption */ - sal_Bool checkEncryptionBufferSize( register sal_Int32 newSize ) - { - if( m_nEncryptionBufferSize < newSize ) - { -/* reallocate the buffer, the used function allocate as rtl_allocateMemory - if the pointer parameter is NULL */ - m_pEncryptionBuffer = (sal_uInt8*)rtl_reallocateMemory( m_pEncryptionBuffer, newSize ); - if( m_pEncryptionBuffer ) - m_nEncryptionBufferSize = newSize; - else - m_nEncryptionBufferSize = 0; - } - return ( m_nEncryptionBufferSize != 0 ); - } -/* init the internal pad string */ - void initPadString() - { - static const sal_uInt8 nPadString[32] = - { - 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, - 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A - }; - - for(sal_uInt32 i = 0; i < sizeof( nPadString ); i++ ) - m_nPadString[i] = nPadString[i]; - - }; -/* initialize the encryption engine */ - void initEncryption(); - -/* this function implements part of the PDF spec algorithm 3.1 in encryption, the rest (the actual encryption) is in PDFWriterImpl::writeBuffer */ - void checkAndEnableStreamEncryption( register sal_Int32 nObject ) - { - if( m_aContext.Encrypt ) - { - m_bEncryptThisStream = true; - register sal_Int32 i = m_nKeyLength; - m_nEncryptionKey[i++] = (sal_uInt8)nObject; - m_nEncryptionKey[i++] = (sal_uInt8)( nObject >> 8 ); - m_nEncryptionKey[i++] = (sal_uInt8)( nObject >> 16 ); -//the other location of m_nEncryptionKey are already set to 0, our fixed generation number -// do the MD5 hash - sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; - // the i+2 to take into account the generation number, always zero - rtl_digest_MD5( &m_nEncryptionKey, i+2, nMD5Sum, sizeof(nMD5Sum) ); -// initialize the RC4 with the key -// key legth: see algoritm 3.1, step 4: (N+5) max 16 - rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 ); - } - }; + /* check and reallocate the buffer for encryption */ + sal_Bool checkEncryptionBufferSize( register sal_Int32 newSize ); + /* this function implements part of the PDF spec algorithm 3.1 in encryption, the rest (the actual encryption) is in PDFWriterImpl::writeBuffer */ + void checkAndEnableStreamEncryption( register sal_Int32 nObject ); void disableStreamEncryption() { m_bEncryptThisStream = false; }; -/* */ - void enableStringEncryption( register sal_Int32 nObject ) - { - register sal_Int32 i = m_nKeyLength; - m_nEncryptionKey[i++] = (sal_uInt8)nObject; - m_nEncryptionKey[i++] = (sal_uInt8)( nObject >> 8 ); - m_nEncryptionKey[i++] = (sal_uInt8)( nObject >> 16 ); -//the other location of m_nEncryptionKey are already set to 0, our fixed generation number -// do the MD5 hash - sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; - // the i+2 to take into account the generation number, always zero - rtl_digest_MD5( &m_nEncryptionKey, i+2, nMD5Sum, sizeof(nMD5Sum) ); -// initialize the RC4 with the key -// key legth: see algoritm 3.1, step 4: (N+5) max 16 - rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 ); - }; + /* */ + void enableStringEncryption( register sal_Int32 nObject ); // test if the encryption is active, if yes than encrypt the unicode string and add to the OStringBuffer parameter void appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ); @@ -1097,18 +1018,38 @@ i12626 sal_Bool m_bIsPDF_A1; PDFWriter& m_rOuterFace; -/* -i12626 -methods for PDF security - - pad a password according algorithm 3.2, step 1 */ - void padPassword( const rtl::OUString aPassword, sal_uInt8 *paPasswordTarget ); -/* algorithm 3.2: compute an encryption key */ - void computeEncryptionKey( sal_uInt8 *paThePaddedPassword, sal_uInt8 *paEncryptionKey ); -/* algorithm 3.3: computing the encryption dictionary'ss owner password value ( /O ) */ - void computeODictionaryValue(); -/* algorithm 3.4 or 3.5: computing the encryption dictionary's user password value ( /U ) revision 2 or 3 of the standard security handler */ - void computeUDictionaryValue(); + /* + i12626 + methods for PDF security + + pad a password according algorithm 3.2, step 1 */ + static void padPassword( const rtl::OUString& i_rPassword, sal_uInt8* o_pPaddedPW ); + /* algorithm 3.2: compute an encryption key */ + static bool computeEncryptionKey( EncHashTransporter*, + vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, + sal_Int32 i_nAccessPermissions + ); + /* algorithm 3.3: computing the encryption dictionary'ss owner password value ( /O ) */ + static bool computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword, const sal_uInt8* i_pPaddedUserPassword, + std::vector< sal_uInt8 >& io_rOValue, + sal_Int32 i_nKeyLength + ); + /* algorithm 3.4 or 3.5: computing the encryption dictionary's user password value ( /U ) revision 2 or 3 of the standard security handler */ + static bool computeUDictionaryValue( EncHashTransporter* i_pTransporter, + vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, + sal_Int32 i_nKeyLength, + sal_Int32 i_nAccessPermissions + ); + + static void computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier, + const vcl::PDFWriter::PDFDocInfo& i_rDocInfo, + rtl::OString& o_rCString1, + rtl::OString& o_rCString2 + ); + static sal_Int32 computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties, + sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength ); + void setupDocInfo(); + bool prepareEncryption( const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& ); // helper for playMetafile void implWriteGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient, @@ -1117,9 +1058,14 @@ methods for PDF security VirtualDevice* pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& ); public: - PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, PDFWriter& ); + PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >&, PDFWriter& ); ~PDFWriterImpl(); + static com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder > + initEncryption( const rtl::OUString& i_rOwnerPassword, + const rtl::OUString& i_rUserPassword, + bool b128Bit ); + /* for OutputDevice so the reference device can have a list * that contains only suitable fonts (subsettable or builtin) * produces a new font list @@ -1152,8 +1098,6 @@ public: } PDFWriter::PDFVersion getVersion() const { return m_aContext.Version; } - void setDocInfo( const PDFDocInfo& rInfo ); - const PDFDocInfo& getDocInfo() const { return m_aDocInfo; } void setDocumentLocale( const com::sun::star::lang::Locale& rLoc ) { m_aContext.DocumentLocale = rLoc; } diff --git a/vcl/source/gdi/pdfwriter_impl2.cxx b/vcl/source/gdi/pdfwriter_impl2.cxx index c01b8a9771d8..ee1fe1cc6bc5 100644 --- a/vcl/source/gdi/pdfwriter_impl2.cxx +++ b/vcl/source/gdi/pdfwriter_impl2.cxx @@ -28,6 +28,7 @@ #include "precompiled_vcl.hxx" #include "pdfwriter_impl.hxx" + #include "vcl/pdfextoutdevdata.hxx" #include "vcl/virdev.hxx" #include "vcl/gdimtf.hxx" @@ -36,12 +37,16 @@ #include "vcl/svdata.hxx" #include "unotools/streamwrap.hxx" #include "unotools/processfactory.hxx" - #include "comphelper/processfactory.hxx" + #include "com/sun/star/beans/PropertyValue.hpp" #include "com/sun/star/io/XSeekable.hpp" #include "com/sun/star/graphic/XGraphicProvider.hpp" +#include "cppuhelper/implbase1.hxx" + +#include <rtl/digest.h> + using namespace vcl; using namespace rtl; using namespace com::sun::star; @@ -1032,4 +1037,504 @@ void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevDa delete pPrivateDevice; } +// Encryption methods + +/* a crutch to transport an rtlDigest safely though UNO API + this is needed for the PDF export dialog, which otherwise would have to pass + clear text passwords down till they can be used in PDFWriter. Unfortunately + the MD5 sum of the password (which is needed to create the PDF encryption key) + is not sufficient, since an rtl MD5 digest cannot be created in an arbitrary state + which would be needed in PDFWriterImpl::computeEncryptionKey. +*/ +class EncHashTransporter : public cppu::WeakImplHelper1 < com::sun::star::beans::XMaterialHolder > +{ + rtlDigest maUDigest; + sal_IntPtr maID; + std::vector< sal_uInt8 > maOValue; + + static std::map< sal_IntPtr, EncHashTransporter* > sTransporters; +public: + EncHashTransporter() + : maUDigest( rtl_digest_createMD5() ) + { + maID = reinterpret_cast< sal_IntPtr >(this); + while( sTransporters.find( maID ) != sTransporters.end() ) // paranoia mode + maID++; + sTransporters[ maID ] = this; + } + + virtual ~EncHashTransporter() + { + sTransporters.erase( maID ); + if( maUDigest ) + rtl_digest_destroyMD5( maUDigest ); + OSL_TRACE( "EncHashTransporter freed\n" ); + } + + rtlDigest getUDigest() const { return maUDigest; }; + std::vector< sal_uInt8 >& getOValue() { return maOValue; } + void invalidate() + { + if( maUDigest ) + { + rtl_digest_destroyMD5( maUDigest ); + maUDigest = NULL; + } + } + + // XMaterialHolder + virtual uno::Any SAL_CALL getMaterial() throw() + { + return uno::makeAny( sal_Int64(maID) ); + } + + static EncHashTransporter* getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& ); + +}; + +std::map< sal_IntPtr, EncHashTransporter* > EncHashTransporter::sTransporters; + +EncHashTransporter* EncHashTransporter::getEncHashTransporter( const uno::Reference< beans::XMaterialHolder >& xRef ) +{ + EncHashTransporter* pResult = NULL; + if( xRef.is() ) + { + uno::Any aMat( xRef->getMaterial() ); + sal_Int64 nMat = 0; + if( aMat >>= nMat ) + { + std::map< sal_IntPtr, EncHashTransporter* >::iterator it = sTransporters.find( static_cast<sal_IntPtr>(nMat) ); + if( it != sTransporters.end() ) + pResult = it->second; + } + } + return pResult; +} + +sal_Bool PDFWriterImpl::checkEncryptionBufferSize( register sal_Int32 newSize ) +{ + if( m_nEncryptionBufferSize < newSize ) + { + /* reallocate the buffer, the used function allocate as rtl_allocateMemory + if the pointer parameter is NULL */ + m_pEncryptionBuffer = (sal_uInt8*)rtl_reallocateMemory( m_pEncryptionBuffer, newSize ); + if( m_pEncryptionBuffer ) + m_nEncryptionBufferSize = newSize; + else + m_nEncryptionBufferSize = 0; + } + return ( m_nEncryptionBufferSize != 0 ); +} + +void PDFWriterImpl::checkAndEnableStreamEncryption( register sal_Int32 nObject ) +{ + if( m_aContext.Encryption.Encrypt() ) + { + m_bEncryptThisStream = true; + sal_Int32 i = m_nKeyLength; + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject; + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 ); + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 ); + //the other location of m_nEncryptionKey are already set to 0, our fixed generation number + // do the MD5 hash + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + // the i+2 to take into account the generation number, always zero + rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) ); + // initialize the RC4 with the key + // key legth: see algoritm 3.1, step 4: (N+5) max 16 + rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 ); + } +} + +void PDFWriterImpl::enableStringEncryption( register sal_Int32 nObject ) +{ + if( m_aContext.Encryption.Encrypt() ) + { + sal_Int32 i = m_nKeyLength; + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)nObject; + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 8 ); + m_aContext.Encryption.EncryptionKey[i++] = (sal_uInt8)( nObject >> 16 ); + //the other location of m_nEncryptionKey are already set to 0, our fixed generation number + // do the MD5 hash + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + // the i+2 to take into account the generation number, always zero + rtl_digest_MD5( &m_aContext.Encryption.EncryptionKey[0], i+2, nMD5Sum, sizeof(nMD5Sum) ); + // initialize the RC4 with the key + // key legth: see algoritm 3.1, step 4: (N+5) max 16 + rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, nMD5Sum, m_nRC4KeyLength, NULL, 0 ); + } +} + +/* init the encryption engine +1. init the document id, used both for building the document id and for building the encryption key(s) +2. build the encryption key following algorithms described in the PDF specification + */ +uno::Reference< beans::XMaterialHolder > PDFWriterImpl::initEncryption( const rtl::OUString& i_rOwnerPassword, + const rtl::OUString& i_rUserPassword, + bool b128Bit + ) +{ + uno::Reference< beans::XMaterialHolder > xResult; + if( i_rOwnerPassword.getLength() || i_rUserPassword.getLength() ) + { + EncHashTransporter* pTransporter = new EncHashTransporter; + xResult = pTransporter; + + // get padded passwords + sal_uInt8 aPadUPW[ENCRYPTED_PWD_SIZE], aPadOPW[ENCRYPTED_PWD_SIZE]; + padPassword( i_rOwnerPassword.getLength() ? i_rOwnerPassword : i_rUserPassword, aPadOPW ); + padPassword( i_rUserPassword, aPadUPW ); + sal_Int32 nKeyLength = SECUR_40BIT_KEY; + if( b128Bit ) + nKeyLength = SECUR_128BIT_KEY; + + if( computeODictionaryValue( aPadOPW, aPadUPW, pTransporter->getOValue(), nKeyLength ) ) + { + rtlDigest aDig = pTransporter->getUDigest(); + if( rtl_digest_updateMD5( aDig, aPadUPW, ENCRYPTED_PWD_SIZE ) != rtl_Digest_E_None ) + xResult.clear(); + } + else + xResult.clear(); + + // trash temporary padded cleartext PWDs + rtl_zeroMemory( aPadOPW, sizeof(aPadOPW) ); + rtl_zeroMemory( aPadUPW, sizeof(aPadUPW) ); + + } + return xResult; +} + +bool PDFWriterImpl::prepareEncryption( const uno::Reference< beans::XMaterialHolder >& xEnc ) +{ + bool bSuccess = false; + EncHashTransporter* pTransporter = EncHashTransporter::getEncHashTransporter( xEnc ); + if( pTransporter ) + { + sal_Int32 nKeyLength = 0, nRC4KeyLength = 0; + sal_Int32 nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, nKeyLength, nRC4KeyLength ); + m_aContext.Encryption.OValue = pTransporter->getOValue(); + bSuccess = computeUDictionaryValue( pTransporter, m_aContext.Encryption, nKeyLength, nAccessPermissions ); + } + if( ! bSuccess ) + { + m_aContext.Encryption.OValue.clear(); + m_aContext.Encryption.UValue.clear(); + m_aContext.Encryption.EncryptionKey.clear(); + } + return bSuccess; +} + +sal_Int32 PDFWriterImpl::computeAccessPermissions( const vcl::PDFWriter::PDFEncryptionProperties& i_rProperties, + sal_Int32& o_rKeyLength, sal_Int32& o_rRC4KeyLength ) +{ + /* + 2) compute the access permissions, in numerical form + + the default value depends on the revision 2 (40 bit) or 3 (128 bit security): + - for 40 bit security the unused bit must be set to 1, since they are not used + - for 128 bit security the same bit must be preset to 0 and set later if needed + according to the table 3.15, pdf v 1.4 */ + sal_Int32 nAccessPermissions = ( i_rProperties.Security128bit ) ? 0xfffff0c0 : 0xffffffc0 ; + + /* check permissions for 40 bit security case */ + nAccessPermissions |= ( i_rProperties.CanPrintTheDocument ) ? 1 << 2 : 0; + nAccessPermissions |= ( i_rProperties.CanModifyTheContent ) ? 1 << 3 : 0; + nAccessPermissions |= ( i_rProperties.CanCopyOrExtract ) ? 1 << 4 : 0; + nAccessPermissions |= ( i_rProperties.CanAddOrModify ) ? 1 << 5 : 0; + o_rKeyLength = SECUR_40BIT_KEY; + o_rRC4KeyLength = SECUR_40BIT_KEY+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5 + + if( i_rProperties.Security128bit ) + { + o_rKeyLength = SECUR_128BIT_KEY; + o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum + // permitted value is 16 + nAccessPermissions |= ( i_rProperties.CanFillInteractive ) ? 1 << 8 : 0; + nAccessPermissions |= ( i_rProperties.CanExtractForAccessibility ) ? 1 << 9 : 0; + nAccessPermissions |= ( i_rProperties.CanAssemble ) ? 1 << 10 : 0; + nAccessPermissions |= ( i_rProperties.CanPrintFull ) ? 1 << 11 : 0; + } + return nAccessPermissions; +} + +/************************************************************* +begin i12626 methods + +Implements Algorithm 3.2, step 1 only +*/ +void PDFWriterImpl::padPassword( const rtl::OUString& i_rPassword, sal_uInt8* o_pPaddedPW ) +{ + // get ansi-1252 version of the password string CHECKIT ! i12626 + rtl::OString aString( rtl::OUStringToOString( i_rPassword, RTL_TEXTENCODING_MS_1252 ) ); + + //copy the string to the target + sal_Int32 nToCopy = ( aString.getLength() < ENCRYPTED_PWD_SIZE ) ? aString.getLength() : ENCRYPTED_PWD_SIZE; + sal_Int32 nCurrentChar; + + for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ ) + o_pPaddedPW[nCurrentChar] = (sal_uInt8)( aString.getStr()[nCurrentChar] ); + + //pad it with standard byte string + sal_Int32 i,y; + for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ ) + o_pPaddedPW[i] = s_nPadString[y]; + + // trash memory of temporary clear text password + rtl_zeroMemory( (sal_Char*)aString.getStr(), aString.getLength() ); +} + +/********************************** +Algorithm 3.2 Compute the encryption key used + +step 1 should already be done before calling, the paThePaddedPassword parameter should contain +the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter, +it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used + +TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec. + +*/ +bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter* i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, sal_Int32 i_nAccessPermissions ) +{ + bool bSuccess = true; + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + + // transporter contains an MD5 digest with the padded user password already + rtlDigest aDigest = i_pTransporter->getUDigest(); + rtlDigestError nError = rtl_Digest_E_None; + if( aDigest ) + { + //step 3 + if( ! io_rProperties.OValue.empty() ) + nError = rtl_digest_updateMD5( aDigest, &io_rProperties.OValue[0] , sal_Int32(io_rProperties.OValue.size()) ); + else + bSuccess = false; + //Step 4 + sal_uInt8 nPerm[4]; + + nPerm[0] = (sal_uInt8)i_nAccessPermissions; + nPerm[1] = (sal_uInt8)( i_nAccessPermissions >> 8 ); + nPerm[2] = (sal_uInt8)( i_nAccessPermissions >> 16 ); + nPerm[3] = (sal_uInt8)( i_nAccessPermissions >> 24 ); + + if( nError == rtl_Digest_E_None ) + nError = rtl_digest_updateMD5( aDigest, nPerm , sizeof( nPerm ) ); + + //step 5, get the document ID, binary form + if( nError == rtl_Digest_E_None ) + nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) ); + //get the digest + if( nError == rtl_Digest_E_None ) + { + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); + + //step 6, only if 128 bit + if( io_rProperties.Security128bit ) + { + for( sal_Int32 i = 0; i < 50; i++ ) + { + nError = rtl_digest_updateMD5( aDigest, &nMD5Sum, sizeof( nMD5Sum ) ); + if( nError != rtl_Digest_E_None ) + { + bSuccess = false; + break; + } + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); + } + } + } + } + else + bSuccess = false; + + i_pTransporter->invalidate(); + + //Step 7 + if( bSuccess ) + { + io_rProperties.EncryptionKey.resize( MAXIMUM_RC4_KEY_LENGTH ); + for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ ) + io_rProperties.EncryptionKey[i] = nMD5Sum[i]; + } + else + io_rProperties.EncryptionKey.clear(); + + return bSuccess; +} + +/********************************** +Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member +the step numbers down here correspond to the ones in PDF v.1.4 specfication +*/ +bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8* i_pPaddedOwnerPassword, + const sal_uInt8* i_pPaddedUserPassword, + std::vector< sal_uInt8 >& io_rOValue, + sal_Int32 i_nKeyLength + ) +{ + bool bSuccess = true; + + io_rOValue.resize( ENCRYPTED_PWD_SIZE ); + + rtlDigest aDigest = rtl_digest_createMD5(); + rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); + if( aDigest && aCipher) + { + //step 1 already done, data is in i_pPaddedOwnerPassword + //step 2 + + rtlDigestError nError = rtl_digest_updateMD5( aDigest, i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE ); + if( nError == rtl_Digest_E_None ) + { + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) ); +//step 3, only if 128 bit + if( i_nKeyLength == SECUR_128BIT_KEY ) + { + sal_Int32 i; + for( i = 0; i < 50; i++ ) + { + nError = rtl_digest_updateMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); + if( nError != rtl_Digest_E_None ) + { + bSuccess = false; + break; + } + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof( nMD5Sum ) ); + } + } + //Step 4, the key is in nMD5Sum + //step 5 already done, data is in i_pPaddedUserPassword + //step 6 + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + nMD5Sum, i_nKeyLength , NULL, 0 ); + // encrypt the user password using the key set above + rtl_cipher_encodeARCFOUR( aCipher, i_pPaddedUserPassword, ENCRYPTED_PWD_SIZE, // the data to be encrypted + &io_rOValue[0], sal_Int32(io_rOValue.size()) ); //encrypted data + //Step 7, only if 128 bit + if( i_nKeyLength == SECUR_128BIT_KEY ) + { + sal_uInt32 i, y; + sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key + + for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1 + { + for( y = 0; y < sizeof( nLocalKey ); y++ ) + nLocalKey[y] = (sal_uInt8)( nMD5Sum[y] ^ i ); + + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + nLocalKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area, on init can be NULL + rtl_cipher_encodeARCFOUR( aCipher, &io_rOValue[0], sal_Int32(io_rOValue.size()), // the data to be encrypted + &io_rOValue[0], sal_Int32(io_rOValue.size()) ); // encrypted data, can be the same as the input, encrypt "in place" + //step 8, store in class data member + } + } + } + else + bSuccess = false; + } + else + bSuccess = false; + + if( aDigest ) + rtl_digest_destroyMD5( aDigest ); + if( aCipher ) + rtl_cipher_destroyARCFOUR( aCipher ); + + if( ! bSuccess ) + io_rOValue.clear(); + return bSuccess; +} + +/********************************** +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) +*/ +bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter* i_pTransporter, + vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, + sal_Int32 i_nKeyLength, + sal_Int32 i_nAccessPermissions + ) +{ + bool bSuccess = true; + + io_rProperties.UValue.resize( ENCRYPTED_PWD_SIZE ); + + rtlDigest aDigest = rtl_digest_createMD5(); + rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); + if( aDigest && aCipher ) + { + //step 1, common to both 3.4 and 3.5 + if( computeEncryptionKey( i_pTransporter, io_rProperties, i_nAccessPermissions ) ) + { + // prepare encryption key for object + for( sal_Int32 i = i_nKeyLength, y = 0; y < 5 ; y++ ) + io_rProperties.EncryptionKey[i++] = 0; + + if( io_rProperties.Security128bit == false ) + { + //3.4 + //step 2 and 3 + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + &io_rProperties.EncryptionKey[0], 5 , // key and key length + NULL, 0 ); //destination data area + // encrypt the user password using the key set above, save for later use + rtl_cipher_encodeARCFOUR( aCipher, s_nPadString, sizeof( s_nPadString ), // the data to be encrypted + &io_rProperties.UValue[0], sal_Int32(io_rProperties.UValue.size()) ); //encrypted data, stored in class data member + } + else + { + //or 3.5, for 128 bit security + //step6, initilize the last 16 bytes of the encrypted user password to 0 + for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sal_uInt32(io_rProperties.UValue.size()); i++) + io_rProperties.UValue[i] = 0; + //step 2 + rtlDigestError nError = rtl_digest_updateMD5( aDigest, s_nPadString, sizeof( s_nPadString ) ); + //step 3 + if( nError == rtl_Digest_E_None ) + nError = rtl_digest_updateMD5( aDigest, &io_rProperties.DocumentIdentifier[0], sal_Int32(io_rProperties.DocumentIdentifier.size()) ); + else + bSuccess = false; + + sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; + rtl_digest_getMD5( aDigest, nMD5Sum, sizeof(nMD5Sum) ); + //Step 4 + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + &io_rProperties.EncryptionKey[0], SECUR_128BIT_KEY, NULL, 0 ); //destination data area + rtl_cipher_encodeARCFOUR( aCipher, nMD5Sum, sizeof( nMD5Sum ), // the data to be encrypted + &io_rProperties.UValue[0], sizeof( nMD5Sum ) ); //encrypted data, stored in class data member + //step 5 + sal_uInt32 i, y; + sal_uInt8 nLocalKey[SECUR_128BIT_KEY]; + + for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1 + { + for( y = 0; y < sizeof( nLocalKey ) ; y++ ) + nLocalKey[y] = (sal_uInt8)( io_rProperties.EncryptionKey[y] ^ i ); + + rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode, + nLocalKey, SECUR_128BIT_KEY, // key and key length + NULL, 0 ); //destination data area, on init can be NULL + rtl_cipher_encodeARCFOUR( aCipher, &io_rProperties.UValue[0], SECUR_128BIT_KEY, // the data to be encrypted + &io_rProperties.UValue[0], SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place" + } + } + } + else + bSuccess = false; + } + else + bSuccess = false; + + if( aDigest ) + rtl_digest_destroyMD5( aDigest ); + if( aCipher ) + rtl_cipher_destroyARCFOUR( aCipher ); + + if( ! bSuccess ) + io_rProperties.UValue.clear(); + return bSuccess; +} + +/* end i12626 methods */ diff --git a/vcl/source/gdi/pngread.cxx b/vcl/source/gdi/pngread.cxx index 11971db34378..df67c4974d47 100644 --- a/vcl/source/gdi/pngread.cxx +++ b/vcl/source/gdi/pngread.cxx @@ -411,7 +411,9 @@ BitmapEx PNGReaderImpl::GetBitmapEx( const Size& rPreviewSizeHint ) case PNGCHUNK_IDAT : { - if ( !mbIDAT ) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes + if ( !mpInflateInBuf ) // taking care that the header has properly been read + mbStatus = FALSE; + else if ( !mbIDAT ) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes ImplReadIDAT(); } break; @@ -527,7 +529,7 @@ BOOL PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint ) mbIDAT = mbAlphaChannel = mbTransparent = FALSE; mbGrayScale = mbRGBTriple = FALSE; mnTargetDepth = mnPngDepth; - mnScansize = ( ( maOrigSize.Width() * mnPngDepth ) + 7 ) >> 3; + sal_uInt64 nScansize64 = ( ( static_cast< sal_uInt64 >( maOrigSize.Width() ) * mnPngDepth ) + 7 ) >> 3; // valid color types are 0,2,3,4 & 6 switch ( mnColorType ) @@ -557,7 +559,7 @@ BOOL PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint ) case 2 : // each pixel is an RGB triple { mbRGBTriple = TRUE; - mnScansize *= 3; + nScansize64 *= 3; switch ( mnPngDepth ) { case 16 : // we have to reduce the bitmap @@ -590,7 +592,7 @@ BOOL PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint ) case 4 : // each pixel is a grayscale sample followed by an alpha sample { - mnScansize *= 2; + nScansize64 *= 2; mbAlphaChannel = TRUE; switch ( mnPngDepth ) { @@ -608,7 +610,7 @@ BOOL PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint ) case 6 : // each pixel is an RGB triple followed by an alpha sample { mbRGBTriple = TRUE; - mnScansize *= 4; + nScansize64 *= 4; mbAlphaChannel = TRUE; switch (mnPngDepth ) { @@ -626,16 +628,24 @@ BOOL PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint ) return FALSE; } - mnBPP = mnScansize / maOrigSize.Width(); + mnBPP = static_cast< sal_uInt32 >( nScansize64 / maOrigSize.Width() ); if ( !mnBPP ) mnBPP = 1; - mnScansize++; // each scanline includes one filterbyte + nScansize64++; // each scanline includes one filterbyte + + if ( nScansize64 > SAL_MAX_UINT32 ) + return FALSE; + + mnScansize = static_cast< sal_uInt32 >( nScansize64 ); // TODO: switch between both scanlines instead of copying - mpInflateInBuf = new BYTE[ mnScansize ]; + mpInflateInBuf = new (std::nothrow) BYTE[ mnScansize ]; mpScanCurrent = mpInflateInBuf; - mpScanPrior = new BYTE[ mnScansize ]; + mpScanPrior = new (std::nothrow) BYTE[ mnScansize ]; + + if ( !mpInflateInBuf || !mpScanPrior ) + return FALSE; // calculate target size from original size and the preview hint if( rPreviewSizeHint.Width() || rPreviewSizeHint.Height() ) diff --git a/vcl/source/gdi/print2.cxx b/vcl/source/gdi/print2.cxx index 25ba80003fd7..5c2a742a10ba 100644 --- a/vcl/source/gdi/print2.cxx +++ b/vcl/source/gdi/print2.cxx @@ -407,6 +407,7 @@ static Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevic { const MetaLineAction& rMetaLineAction = static_cast<const MetaLineAction&>(rAct); aActionBounds = Rectangle( rMetaLineAction.GetStartPoint(), rMetaLineAction.GetEndPoint() ); + aActionBounds.Justify(); const long nLineWidth(rMetaLineAction.GetLineInfo().GetWidth()); if(nLineWidth) { |