diff --git a/sdext/source/pdfimport/test/makefile.mk b/sdext/source/pdfimport/test/makefile.mk new file mode 100755 index 000000000000..2fb8d3a62417 --- /dev/null +++ b/sdext/source/pdfimport/test/makefile.mk @@ -0,0 +1,165 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.2 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. +PRJNAME=sdext +TARGET=tests +TARGETTYPE=CUI +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE: settings.mk + +# --- test lib ------------------------------------------------------ + +.IF "$(ENABLE_PDFIMPORT)" == "NO" +@all: + @echo "PDF Import extension disabled." +.ENDIF + +.IF "$(SYSTEM_ZLIB)" == "YES" +CFLAGS+=-DSYSTEM_ZLIB +.ENDIF + +SHL1OBJS= \ + $(SLO)$/tests.obj + +SHL1LIBS=\ + $(SLB)$/pdfmisc.lib \ + $(SLB)$/pdfparse.lib \ + $(SLB)$/pdfsax.lib \ + $(SLB)$/pdfparsetree.lib \ + $(SLB)$/pdfodf.lib \ + $(SLB)$/xpdfwrapper.lib \ + $(SLB)$/pdfimport.lib + +SHL1TARGET=$(TARGET) +SHL1STDLIBS=\ + $(BASEGFXLIB) \ + $(UNOTOOLSLIB) \ + $(CANVASTOOLSLIB) \ + $(COMPHELPERLIB) \ + $(CPPUHELPERLIB) \ + $(CPPUNITLIB) \ + $(ZLIB3RDLIB) \ + $(CPPULIB) \ + $(SALLIB) + + +SHL1IMPLIB= i$(SHL1TARGET) +DEF1NAME =$(SHL1TARGET) +SHL1VERSIONMAP = export.map + +# --- pdf2xml binary ------------------------------------------------------ + +TARGET2=pdf2xml + +APP1TARGET=$(TARGET2) +APP1LIBSALCPPRT= +APP1OBJS=$(SLO)$/pdf2xml.obj + +APP1LIBS=\ + $(SLB)$/pdfmisc.lib \ + $(SLB)$/pdfparse.lib \ + $(SLB)$/pdfsax.lib \ + $(SLB)$/pdfparsetree.lib \ + $(SLB)$/pdfodf.lib \ + $(SLB)$/xpdfwrapper.lib \ + $(SLB)$/pdfimport.lib + +APP1STDLIBS=\ + $(BASEGFXLIB) \ + $(UNOTOOLSLIB) \ + $(CANVASTOOLSLIB) \ + $(COMPHELPERLIB) \ + $(CPPUHELPERLIB) \ + $(CPPUNITLIB) \ + $(ZLIB3RDLIB) \ + $(CPPULIB) \ + $(SALLIB) + +# --- pdfunzip binary ------------------------------------------------------ + +TARGET3=pdfunzip + +APP2TARGET=$(TARGET3) +APP2LIBSALCPPRT= +APP2OBJS=$(SLO)$/pdfunzip.obj + +APP2LIBS=\ + $(SLB)$/pdfmisc.lib \ + $(SLB)$/pdfparse.lib \ + $(SLB)$/pdfsax.lib \ + $(SLB)$/pdfparsetree.lib \ + $(SLB)$/pdfodf.lib \ + $(SLB)$/xpdfwrapper.lib \ + $(SLB)$/pdfimport.lib + +APP2STDLIBS=\ + $(BASEGFXLIB) \ + $(UNOTOOLSLIB) \ + $(CANVASTOOLSLIB) \ + $(COMPHELPERLIB) \ + $(CPPUHELPERLIB) \ + $(CPPUNITLIB) \ + $(ZLIB3RDLIB) \ + $(CPPULIB) \ + $(SALLIB) + +# --- Targets ------------------------------------------------------ + + .INCLUDE : target.mk + .INCLUDE : _cppunit.mk + +# --- Fake uno bootstrap & copy testfile ------------------------ + + $(BIN)$/pdfi_unittest_test.pdf : testinput.pdf + rm -f $@ + $(GNUCOPY) testinput.pdf $@ + + $(BIN)$/pdfi_unittest_test.ini : makefile.mk + rm -f $@ + @echo UNO_SERVICES= > $@ + @echo UNO_TYPES=$(UNOUCRRDB:s/\/\\/) >> $@ + +# --- Enable testshl2 execution in normal build ------------------------ + + $(MISC)$/pdfi_unittest_succeeded : $(SHL1TARGETN) $(BIN)$/pdfi_unittest_test.pdf $(BIN)$/pdfi_unittest_test.ini + rm -f $(BIN)$/pdfi_unittest_draw.xml + rm -f $(BIN)$/pdfi_unittest_writer.xml + @echo ---------------------------------------------------------- + @echo - start unit test on library $(SHL1TARGETN) + @echo ---------------------------------------------------------- + testshl2 -forward $(BIN)$/ $(SHL1TARGETN) + $(TOUCH) $@ + +#ALLTAR : $(MISC)$/pdfi_unittest_succeeded diff --git a/sdext/source/pdfimport/test/pdf2xml.cxx b/sdext/source/pdfimport/test/pdf2xml.cxx new file mode 100644 index 000000000000..3912c442845b --- /dev/null +++ b/sdext/source/pdfimport/test/pdf2xml.cxx @@ -0,0 +1,109 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pdf2xml.cxx,v $ + * + * $Revision: 1.2 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sdext.hxx" + +#include "outputwrap.hxx" +#include "contentsink.hxx" +#include "pdfihelper.hxx" +#include "wrapper.hxx" +#include "pdfparse.hxx" +#include "../pdfiadaptor.hxx" + +#include +#include +#include + +#include +#include +#include + +using namespace ::pdfi; +using namespace ::com::sun::star; + +SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) +{ + if( argc != 5 ) + return 1; + + ::rtl::OUString aBaseURL, aTmpURL, aSrcURL, aDstURL, aIniUrl; + + TreeVisitorFactorySharedPtr pTreeFactory; + if( rtl_str_compare(argv[1], "-writer") == 0 ) + pTreeFactory = createWriterTreeVisitorFactory(); + else if( rtl_str_compare(argv[1], "-draw") == 0 ) + pTreeFactory = createDrawTreeVisitorFactory(); + else if( rtl_str_compare(argv[1], "-impress") == 0 ) + pTreeFactory = createImpressTreeVisitorFactory(); + else + return 1; + + osl_getProcessWorkingDir(&aBaseURL.pData); + osl_getFileURLFromSystemPath( rtl::OUString::createFromAscii(argv[2]).pData, + &aTmpURL.pData ); + osl_getAbsoluteFileURL(aBaseURL.pData,aTmpURL.pData,&aSrcURL.pData); + + osl_getFileURLFromSystemPath( rtl::OUString::createFromAscii(argv[3]).pData, + &aTmpURL.pData ); + osl_getAbsoluteFileURL(aBaseURL.pData,aTmpURL.pData,&aDstURL.pData); + + osl_getFileURLFromSystemPath( rtl::OUString::createFromAscii(argv[4]).pData, + &aTmpURL.pData ); + osl_getAbsoluteFileURL(aBaseURL.pData,aTmpURL.pData,&aIniUrl.pData); + + // bootstrap UNO + uno::Reference< lang::XMultiServiceFactory > xFactory; + uno::Reference< uno::XComponentContext > xCtx; + try + { + xCtx = ::cppu::defaultBootstrap_InitialComponentContext(aIniUrl); + xFactory = uno::Reference< lang::XMultiServiceFactory >( xCtx->getServiceManager(), + uno::UNO_QUERY ); + if( xFactory.is() ) + ::comphelper::setProcessServiceFactory( xFactory ); + } + catch( uno::Exception& ) + { + } + + if( !xFactory.is() ) + { + OSL_TRACE( "Could not bootstrap UNO, installation must be in disorder. Exiting.\n" ); + return 1; + } + + pdfi::PDFIRawAdaptor aAdaptor( xCtx ); + aAdaptor.setTreeVisitorFactory(pTreeFactory); + aAdaptor.odfConvert( aSrcURL, new OutputWrap(aDstURL), NULL ); + + return 0; +} diff --git a/sdext/source/pdfimport/test/pdfunzip.cxx b/sdext/source/pdfimport/test/pdfunzip.cxx new file mode 100644 index 000000000000..0feaa268b760 --- /dev/null +++ b/sdext/source/pdfimport/test/pdfunzip.cxx @@ -0,0 +1,392 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pdfunzip.cxx,v $ + * + * $Revision: 1.2 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sdext.hxx" + +#include +#include +#include +#include +#include +#include +#include + +#include "pdfparse.hxx" + +using namespace rtl; +using namespace pdfparse; + +void printHelp( const char* pExe ) +{ + fprintf( stdout, + "USAGE: %s [-h,--help] [-a, --extract-add-streams] [-pw, --password ] []\n" + " -h, --help: show help\n" + " -a, --extract-add-streams: extracts additional streams to outputfile_object\n" + " and prints the mimetype found to stdout\n" + " -pw, --password: use password for decryption\n" + , pExe ); +} + +class FileEmitContext : public EmitContext +{ + oslFileHandle m_aHandle; + oslFileHandle m_aReadHandle; + unsigned int m_nReadLen; + + void openReadFile( const char* pOrigName ); + + public: + FileEmitContext( const char* pFileName, const char* pOrigName, const PDFContainer* pTop ); + virtual ~FileEmitContext(); + + virtual bool write( const void* pBuf, unsigned int nLen ) throw(); + virtual unsigned int getCurPos() throw(); + virtual bool copyOrigBytes( unsigned int nOrigOffset, unsigned int nLen ) throw(); + virtual unsigned int readOrigBytes( unsigned int nOrigOffset, unsigned int nLen, void* pBuf ) throw(); +}; + +FileEmitContext::FileEmitContext( const char* pFileName, const char* pOrigName, const PDFContainer* pTop ) + : EmitContext( pTop ), + m_aHandle( NULL ), + m_aReadHandle( NULL ), + m_nReadLen( 0 ) +{ + OUString aSysFile( OStringToOUString( OString( pFileName ), osl_getThreadTextEncoding() ) ); + OUString aURL; + if( osl_getFileURLFromSystemPath( aSysFile.pData, &aURL.pData ) != osl_File_E_None ) + { + fprintf( stderr, "filename conversion \"%s\" failed\n", pFileName ); + return; + } + + if( osl_openFile( aURL.pData, &m_aHandle, osl_File_OpenFlag_Write ) == osl_File_E_None ) + { + if( osl_setFileSize( m_aHandle, 0 ) != osl_File_E_None ) + { + fprintf( stderr, "could not truncate %s\n", pFileName ); + osl_closeFile( m_aHandle ); + m_aHandle = NULL; + } + } + else if( osl_openFile( aURL.pData, &m_aHandle, + osl_File_OpenFlag_Write |osl_File_OpenFlag_Create ) != osl_File_E_None ) + { + fprintf( stderr, "could not open %s\n", pFileName ); + return; + } + m_bDeflate = true; + + openReadFile( pOrigName ); +} + +FileEmitContext::~FileEmitContext() +{ + if( m_aHandle ) + osl_closeFile( m_aHandle ); + if( m_aReadHandle ) + osl_closeFile( m_aReadHandle ); +} + +void FileEmitContext::openReadFile( const char* pInFile ) +{ + OUString aSysFile( OStringToOUString( OString( pInFile ), osl_getThreadTextEncoding() ) ); + OUString aURL; + if( osl_getFileURLFromSystemPath( aSysFile.pData, &aURL.pData ) != osl_File_E_None ) + { + fprintf( stderr, "filename conversion \"%s\" failed\n", pInFile ); + return; + } + + if( osl_openFile( aURL.pData, &m_aReadHandle, osl_File_OpenFlag_Read ) != osl_File_E_None ) + { + fprintf( stderr, "could not open %s\n", pInFile ); + return; + } + + if( osl_setFilePos( m_aReadHandle, osl_Pos_End, 0 ) != osl_File_E_None ) + { + fprintf( stderr, "could not seek to end of %s\n", pInFile ); + osl_closeFile( m_aReadHandle ); + return; + } + + sal_uInt64 nFileSize = 0; + if( osl_getFilePos( m_aReadHandle, &nFileSize ) != osl_File_E_None ) + { + fprintf( stderr, "could not get end pos of %s\n", pInFile ); + osl_closeFile( m_aReadHandle ); + return; + } + + m_nReadLen = static_cast(nFileSize); +} + +bool FileEmitContext::write( const void* pBuf, unsigned int nLen ) throw() +{ + if( ! m_aHandle ) + return false; + + sal_uInt64 nWrite = static_cast(nLen); + sal_uInt64 nWritten = 0; + return (osl_writeFile( m_aHandle, pBuf, nWrite, &nWritten ) == osl_File_E_None) + && nWrite == nWritten; +} + +unsigned int FileEmitContext::getCurPos() throw() +{ + sal_uInt64 nFileSize = 0; + if( m_aHandle ) + { + if( osl_getFilePos( m_aHandle, &nFileSize ) != osl_File_E_None ) + nFileSize = 0; + } + return static_cast(nFileSize); +} + +bool FileEmitContext::copyOrigBytes( unsigned int nOrigOffset, unsigned int nLen ) throw() +{ + if( nOrigOffset + nLen > m_nReadLen ) + return false; + + if( osl_setFilePos( m_aReadHandle, osl_Pos_Absolut, nOrigOffset ) != osl_File_E_None ) + { + fprintf( stderr, "could not seek to offset %u\n", nOrigOffset ); + return false; + } + void* pBuf = rtl_allocateMemory( nLen ); + if( ! pBuf ) + return false; + sal_uInt64 nBytesRead = 0; + if( osl_readFile( m_aReadHandle, pBuf, nLen, &nBytesRead ) != osl_File_E_None + || nBytesRead != static_cast(nLen) ) + { + fprintf( stderr, "could not read %u bytes\n", nLen ); + rtl_freeMemory( pBuf ); + return false; + } + bool bRet = write( pBuf, nLen ); + rtl_freeMemory( pBuf ); + return bRet; +} + +unsigned int FileEmitContext::readOrigBytes( unsigned int nOrigOffset, unsigned int nLen, void* pBuf ) throw() +{ + if( nOrigOffset + nLen > m_nReadLen ) + return 0; + + if( osl_setFilePos( m_aReadHandle, osl_Pos_Absolut, nOrigOffset ) != osl_File_E_None ) + { + fprintf( stderr, "could not seek to offset %u\n", nOrigOffset ); + return 0; + } + sal_uInt64 nBytesRead = 0; + if( osl_readFile( m_aReadHandle, pBuf, nLen, &nBytesRead ) != osl_File_E_None ) + return 0; + return static_cast(nBytesRead); +} + +typedef int(*PDFFileHdl)(const char*, const char*, PDFFile*); + +int handleFile( const char* pInFile, const char* pOutFile, const char* pPassword, PDFFileHdl pHdl ) +{ + + PDFReader aParser; + int nRet = 0; + PDFEntry* pEntry = aParser.read( pInFile ); + if( pEntry ) + { + PDFFile* pPDFFile = dynamic_cast(pEntry); + if( pPDFFile ) + { + fprintf( stdout, "have a %s PDF file\n", pPDFFile->isEncrypted() ? "encrypted" : "unencrypted" ); + if( pPassword ) + fprintf( stdout, "password %s\n", + pPDFFile->setupDecryptionData( pPassword ) ? "matches" : "does not match" ); + nRet = pHdl( pInFile, pOutFile, pPDFFile ); + } + else + nRet = 20; + delete pEntry; + } + return nRet; +} + +int write_unzipFile( const char* pInFile, const char* pOutFile, PDFFile* pPDFFile ) +{ + FileEmitContext aContext( pOutFile, pInFile, pPDFFile ); + aContext.m_bDecrypt = pPDFFile->isEncrypted(); + pPDFFile->emit(aContext); + return 0; +} + +int write_addStreamArray( const char* pOutFile, PDFArray* pStreams, PDFFile* pPDFFile, const char* pInFile ) +{ + int nRet = 0; + unsigned int nArrayElements = pStreams->m_aSubElements.size(); + for( unsigned int i = 0; i < nArrayElements-1 && nRet == 0; i++ ) + { + PDFName* pMimeType = dynamic_cast(pStreams->m_aSubElements[i]); + PDFObjectRef* pStreamRef = dynamic_cast(pStreams->m_aSubElements[i+1]); + if( ! pMimeType ) + fprintf( stderr, "error: no mimetype element\n" ); + if( ! pStreamRef ) + fprintf( stderr, "error: no stream ref element\n" ); + if( pMimeType && pStreamRef ) + { + fprintf( stdout, "found stream %d %d with mimetype %s\n", + pStreamRef->m_nNumber, pStreamRef->m_nGeneration, + pMimeType->m_aName.getStr() ); + PDFObject* pObject = pPDFFile->findObject( pStreamRef->m_nNumber, pStreamRef->m_nGeneration ); + if( pObject ) + { + rtl::OStringBuffer aOutStream( pOutFile ); + aOutStream.append( "_stream_" ); + aOutStream.append( sal_Int32(pStreamRef->m_nNumber) ); + aOutStream.append( "_" ); + aOutStream.append( sal_Int32(pStreamRef->m_nGeneration) ); + FileEmitContext aContext( aOutStream.getStr(), pInFile, pPDFFile ); + aContext.m_bDecrypt = pPDFFile->isEncrypted(); + pObject->writeStream( aContext, pPDFFile ); + } + else + { + fprintf( stderr, "object not found\n" ); + nRet = 121; + } + } + else + nRet = 120; + } + return nRet; +} + +int write_addStreams( const char* pInFile, const char* pOutFile, PDFFile* pPDFFile ) +{ + // find all trailers + int nRet = 0; + unsigned int nElements = pPDFFile->m_aSubElements.size(); + for( unsigned i = 0; i < nElements && nRet == 0; i++ ) + { + PDFTrailer* pTrailer = dynamic_cast(pPDFFile->m_aSubElements[i]); + if( pTrailer && pTrailer->m_pDict ) + { + // search for AdditionalStreams entry + std::hash_map::iterator add_stream; + add_stream = pTrailer->m_pDict->m_aMap.find( "AdditionalStreams" ); + if( add_stream != pTrailer->m_pDict->m_aMap.end() ) + { + PDFArray* pStreams = dynamic_cast(add_stream->second); + if( pStreams ) + nRet = write_addStreamArray( pOutFile, pStreams, pPDFFile, pInFile ); + } + } + } + return nRet; +} + +SAL_IMPLEMENT_MAIN_WITH_ARGS( argc, argv ) +{ + const char* pInFile = NULL; + const char* pOutFile = NULL; + const char* pPassword = NULL; + OStringBuffer aOutFile( 256 ); + PDFFileHdl aHdl = write_unzipFile; + + for( int nArg = 1; nArg < argc; nArg++ ) + { + if( argv[nArg][0] == '-' ) + { + if( ! rtl_str_compare( "-pw", argv[nArg] ) || + ! rtl_str_compare( "--password" , argv[nArg] ) ) + { + if( nArg == argc-1 ) + { + fprintf( stderr, "no password given\n" ); + return 1; + } + nArg++; + pPassword = argv[nArg]; + } + else if( ! rtl_str_compare( "-h", argv[nArg] ) || + ! rtl_str_compare( "--help", argv[nArg] ) ) + { + printHelp( argv[0] ); + return 0; + } + else if( ! rtl_str_compare( "-a", argv[nArg] ) || + ! rtl_str_compare( "--extract-add-streams", argv[nArg] ) ) + { + aHdl = write_addStreams; + } + else + { + fprintf( stderr, "unrecognized option \"%s\"\n", + argv[nArg] ); + printHelp( argv[0] ); + return 1; + } + } + else if( pInFile == NULL ) + pInFile = argv[nArg]; + else if( pOutFile == NULL ) + pOutFile = argv[nArg]; + } + if( ! pInFile ) + { + fprintf( stderr, "no input file given\n" ); + return 10; + } + if( ! pOutFile ) + { + OString aFile( pInFile ); + if( aFile.getLength() > 0 ) + { + if( aFile.getLength() > 4 ) + { + if( aFile.matchIgnoreAsciiCase( OString( ".pdf" ), aFile.getLength()-4 ) ) + aOutFile.append( pInFile, aFile.getLength() - 4 ); + else + aOutFile.append( aFile ); + } + aOutFile.append( "_unzip.pdf" ); + pOutFile = aOutFile.getStr(); + } + else + { + fprintf( stderr, "no output file given\n" ); + return 11; + } + } + + return handleFile( pInFile, pOutFile, pPassword, aHdl ); +} + diff --git a/sdext/source/pdfimport/test/tests.cxx b/sdext/source/pdfimport/test/tests.cxx new file mode 100755 index 000000000000..c045b729d7b5 --- /dev/null +++ b/sdext/source/pdfimport/test/tests.cxx @@ -0,0 +1,577 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tests.cxx,v $ + * + * $Revision: 1.2 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sdext.hxx" + +#ifdef SYSTEM_ZLIB +#include "zlib.h" +#else +#include +#endif + +#include "outputwrap.hxx" +#include "contentsink.hxx" +#include "pdfihelper.hxx" +#include "wrapper.hxx" +#include "pdfparse.hxx" +#include "../pdfiadaptor.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +using namespace ::pdfparse; +using namespace ::pdfi; +using namespace ::com::sun::star; + +namespace +{ + class TestSink : public ContentSink + { + public: + TestSink() : + m_nNextFontId( 1 ), + m_aIdToFont(), + m_aFontToId(), + m_aGCStack(1), + m_aPageSize(), + m_aHyperlinkBounds(), + m_aURI(), + m_aTextOut(), + m_nNumPages(0), + m_bPageEnded(false), + m_bRedCircleSeen(false), + m_bGreenStrokeSeen(false), + m_bDashedLineSeen(false) + {} + + ~TestSink() + { + CPPUNIT_ASSERT_MESSAGE( "A4 page size (in 100th of points)", + m_aPageSize.Width == 79400 && m_aPageSize.Height == 59500 ); + CPPUNIT_ASSERT_MESSAGE( "endPage() called", m_bPageEnded ); + CPPUNIT_ASSERT_MESSAGE( "Num pages equal one", m_nNumPages == 1 ); + CPPUNIT_ASSERT_MESSAGE( "Correct hyperlink bounding box", + rtl::math::approxEqual(m_aHyperlinkBounds.X1,34.7 ) && + rtl::math::approxEqual(m_aHyperlinkBounds.Y1,386.0) && + rtl::math::approxEqual(m_aHyperlinkBounds.X2,166.7) && + rtl::math::approxEqual(m_aHyperlinkBounds.Y2,406.2) ); + CPPUNIT_ASSERT_MESSAGE( "Correct hyperlink URI", + m_aURI == ::rtl::OUString::createFromAscii( "http://download.openoffice.org/" ) ); + + const char* sText = " \n \nThis is a testtext\nNew paragraph,\nnew line\n" + "Hyperlink, this is\n?\nThis is more text\noutline mode\n?\nNew paragraph\n"; + ::rtl::OString aTmp; + m_aTextOut.makeStringAndClear().convertToString( &aTmp, + RTL_TEXTENCODING_ASCII_US, + OUSTRING_TO_OSTRING_CVTFLAGS ); + CPPUNIT_ASSERT_MESSAGE( "Imported text is \"This is a testtext New paragraph, new line" + " Hyperlink, this is * This is more text outline mode * New paragraph\"", + sText == aTmp ); + + CPPUNIT_ASSERT_MESSAGE( "red circle seen in input", m_bRedCircleSeen ); + CPPUNIT_ASSERT_MESSAGE( "green stroke seen in input", m_bGreenStrokeSeen ); + CPPUNIT_ASSERT_MESSAGE( "dashed line seen in input", m_bDashedLineSeen ); + } + + private: + GraphicsContext& getCurrentContext() { return m_aGCStack.back(); } + + // ContentSink interface implementation + virtual void setPageNum( sal_Int32 nNumPages ) + { + m_nNumPages = nNumPages; + } + + virtual void startPage( const geometry::RealSize2D& rSize ) + { + m_aPageSize = rSize; + } + + virtual void endPage() + { + m_bPageEnded = true; + } + + virtual void hyperLink( const geometry::RealRectangle2D& rBounds, + const ::rtl::OUString& rURI ) + { + m_aHyperlinkBounds = rBounds; + m_aURI = rURI; + } + + virtual void pushState() + { + m_aGCStack.push_back( m_aGCStack.back() ); + } + + virtual void popState() + { + m_aGCStack.pop_back(); + } + + virtual void setTransformation( const geometry::AffineMatrix2D& rMatrix ) + { + basegfx::unotools::homMatrixFromAffineMatrix( + getCurrentContext().Transformation, + rMatrix ); + } + + virtual void setLineDash( const uno::Sequence& dashes, + double start ) + { + GraphicsContext& rContext( getCurrentContext() ); + if( dashes.getLength() ) + comphelper::sequenceToContainer(rContext.DashArray,dashes); + CPPUNIT_ASSERT_MESSAGE( "line dashing start offset", start == 0.0 ); + } + + virtual void setFlatness( double nFlatness ) + { + getCurrentContext().Flatness = nFlatness; + } + + virtual void setLineJoin(sal_Int8 nJoin) + { + getCurrentContext().LineJoin = nJoin; + } + + virtual void setLineCap(sal_Int8 nCap) + { + getCurrentContext().LineCap = nCap; + } + + virtual void setMiterLimit(double nVal) + { + getCurrentContext().MiterLimit = nVal; + } + + virtual void setLineWidth(double nVal) + { + getCurrentContext().LineWidth = nVal; + } + + virtual void setFillColor( const rendering::ARGBColor& rColor ) + { + getCurrentContext().FillColor = rColor; + } + + virtual void setStrokeColor( const rendering::ARGBColor& rColor ) + { + getCurrentContext().LineColor = rColor; + } + + virtual void setBlendMode(sal_Int8 nMode) + { + getCurrentContext().BlendMode = nMode; + } + + virtual void setFont( const FontAttributes& rFont ) + { + FontToIdMap::const_iterator it = m_aFontToId.find( rFont ); + if( it != m_aFontToId.end() ) + getCurrentContext().FontId = it->second; + else + { + m_aFontToId[ rFont ] = m_nNextFontId; + m_aIdToFont[ m_nNextFontId ] = rFont; + getCurrentContext().FontId = m_nNextFontId; + m_nNextFontId++; + } + } + + virtual void strokePath( const uno::Reference& rPath ) + { + GraphicsContext& rContext( getCurrentContext() ); + basegfx::B2DPolyPolygon aPath = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath); + aPath.transform( rContext.Transformation ); + + if( rContext.DashArray.empty() ) + { + CPPUNIT_ASSERT_MESSAGE( "Line color is green", + rContext.LineColor.Alpha == 1.0 && + rContext.LineColor.Red == 0.0 && + rContext.LineColor.Green == 1.0 && + rContext.LineColor.Blue == 0.0 ); + + CPPUNIT_ASSERT_MESSAGE( "Line width is 0", + rtl::math::approxEqual(rContext.LineWidth, 28.3) ); + + const char* sExportString = "m53570 7650-35430 24100"; + CPPUNIT_ASSERT_MESSAGE( "Stroke is m535.7 518.5-354.3-241", + basegfx::tools::exportToSvgD( aPath ).compareToAscii(sExportString) == 0 ); + + m_bGreenStrokeSeen = true; + } + else + { + CPPUNIT_ASSERT_MESSAGE( "Dash array cons ists of four entries", + rContext.DashArray.size() == 4 && + rtl::math::approxEqual(rContext.DashArray[0],14.3764) && + rContext.DashArray[0] == rContext.DashArray[1] && + rContext.DashArray[1] == rContext.DashArray[2] && + rContext.DashArray[2] == rContext.DashArray[3] ); + + CPPUNIT_ASSERT_MESSAGE( "Line color is black", + rContext.LineColor.Alpha == 1.0 && + rContext.LineColor.Red == 0.0 && + rContext.LineColor.Green == 0.0 && + rContext.LineColor.Blue == 0.0 ); + + CPPUNIT_ASSERT_MESSAGE( "Line width is 0", + rContext.LineWidth == 0 ); + + const char* sExportString = "m49890 5670.00000000001-35430 24090"; + CPPUNIT_ASSERT_MESSAGE( "Stroke is m49890 5670.00000000001-35430 24090", + basegfx::tools::exportToSvgD( aPath ).compareToAscii(sExportString) == 0 ); + + m_bDashedLineSeen = true; + } + CPPUNIT_ASSERT_MESSAGE( "Blend mode is normal", + rContext.BlendMode == rendering::BlendMode::NORMAL ); + CPPUNIT_ASSERT_MESSAGE( "Join type is round", + rContext.LineJoin == rendering::PathJoinType::ROUND ); + CPPUNIT_ASSERT_MESSAGE( "Cap type is butt", + rContext.LineCap == rendering::PathCapType::BUTT ); + CPPUNIT_ASSERT_MESSAGE( "Line miter limit is 10", + rContext.MiterLimit == 10 ); + CPPUNIT_ASSERT_MESSAGE( "Flatness is 0", + rContext.Flatness == 1 ); + CPPUNIT_ASSERT_MESSAGE( "Font id is 0", + rContext.FontId == 0 ); + } + + virtual void fillPath( const uno::Reference& rPath ) + { + GraphicsContext& rContext( getCurrentContext() ); + basegfx::B2DPolyPolygon aPath = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath); + aPath.transform( rContext.Transformation ); + + CPPUNIT_ASSERT_MESSAGE( "Fill color is black", + rContext.FillColor.Alpha == 1.0 && + rContext.FillColor.Red == 0.0 && + rContext.FillColor.Green == 0.0 && + rContext.FillColor.Blue == 0.0 ); + CPPUNIT_ASSERT_MESSAGE( "Blend mode is normal", + rContext.BlendMode == rendering::BlendMode::NORMAL ); + CPPUNIT_ASSERT_MESSAGE( "Flatness is 10", + rContext.Flatness == 10 ); + CPPUNIT_ASSERT_MESSAGE( "Font id is 0", + rContext.FontId == 0 ); + } + + virtual void eoFillPath( const uno::Reference& rPath ) + { + GraphicsContext& rContext( getCurrentContext() ); + basegfx::B2DPolyPolygon aPath = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath); + aPath.transform( rContext.Transformation ); + + CPPUNIT_ASSERT_MESSAGE( "Fill color is black", + rContext.FillColor.Alpha == 1.0 && + rContext.FillColor.Red == 1.0 && + rContext.FillColor.Green == 0.0 && + rContext.FillColor.Blue == 0.0 ); + CPPUNIT_ASSERT_MESSAGE( "Blend mode is normal", + rContext.BlendMode == rendering::BlendMode::NORMAL ); + CPPUNIT_ASSERT_MESSAGE( "Flatness is 0", + rContext.Flatness == 1 ); + CPPUNIT_ASSERT_MESSAGE( "Font id is 0", + rContext.FontId == 0 ); + + const char* sExportString = "m12050 49610c-4310 0-7800-3490-7800-7800 0-4300 " + "3490-7790 7800-7790 4300 0 7790 3490 7790 7790 0 4310-3490 7800-7790 7800z"; + CPPUNIT_ASSERT_MESSAGE( "Stroke is a 4-bezier circle", + basegfx::tools::exportToSvgD( aPath ).compareToAscii(sExportString) == 0 ); + + m_bRedCircleSeen = true; + } + + virtual void intersectClip(const uno::Reference& rPath) + { + basegfx::B2DPolyPolygon aNewClip = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath); + basegfx::B2DPolyPolygon aCurClip = getCurrentContext().Clip; + + if( aCurClip.count() ) + aNewClip = basegfx::tools::clipPolyPolygonOnPolyPolygon( aCurClip, aNewClip, false ); + + getCurrentContext().Clip = aNewClip; + } + + virtual void intersectEoClip(const uno::Reference& rPath) + { + basegfx::B2DPolyPolygon aNewClip = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath); + basegfx::B2DPolyPolygon aCurClip = getCurrentContext().Clip; + + if( aCurClip.count() ) + aNewClip = basegfx::tools::clipPolyPolygonOnPolyPolygon( aCurClip, aNewClip, false ); + + getCurrentContext().Clip = aNewClip; + } + + virtual void drawGlyphs( const rtl::OUString& rGlyphs, + const geometry::RealRectangle2D& /*rRect*/, + const geometry::Matrix2D& /*rFontMatrix*/ ) + { + m_aTextOut.append(rGlyphs); + } + + virtual void endText() + { + m_aTextOut.append( ::rtl::OUString::createFromAscii("\n") ); + } + + virtual void drawMask(const uno::Sequence& xBitmap, + bool /*bInvert*/ ) + { + CPPUNIT_ASSERT_MESSAGE( "drawMask received two properties", + xBitmap.getLength()==3 ); + CPPUNIT_ASSERT_MESSAGE( "drawMask got URL param", + xBitmap[0].Name.compareToAscii( "URL" ) == 0 ); + CPPUNIT_ASSERT_MESSAGE( "drawMask got InputStream param", + xBitmap[1].Name.compareToAscii( "InputStream" ) == 0 ); + } + + virtual void drawImage(const uno::Sequence& xBitmap ) + { + CPPUNIT_ASSERT_MESSAGE( "drawImage received two properties", + xBitmap.getLength()==3 ); + CPPUNIT_ASSERT_MESSAGE( "drawImage got URL param", + xBitmap[0].Name.compareToAscii( "URL" ) == 0 ); + CPPUNIT_ASSERT_MESSAGE( "drawImage got InputStream param", + xBitmap[1].Name.compareToAscii( "InputStream" ) == 0 ); + } + + virtual void drawColorMaskedImage(const uno::Sequence& xBitmap, + const uno::Sequence& /*xMaskColors*/ ) + { + CPPUNIT_ASSERT_MESSAGE( "drawColorMaskedImage received two properties", + xBitmap.getLength()==3 ); + CPPUNIT_ASSERT_MESSAGE( "drawColorMaskedImage got URL param", + xBitmap[0].Name.compareToAscii( "URL" ) == 0 ); + CPPUNIT_ASSERT_MESSAGE( "drawColorMaskedImage got InputStream param", + xBitmap[1].Name.compareToAscii( "InputStream" ) == 0 ); + } + + virtual void drawMaskedImage(const uno::Sequence& xBitmap, + const uno::Sequence& xMask, + bool /*bInvertMask*/) + { + CPPUNIT_ASSERT_MESSAGE( "drawMaskedImage received two properties #1", + xBitmap.getLength()==3 ); + CPPUNIT_ASSERT_MESSAGE( "drawMaskedImage got URL param #1", + xBitmap[0].Name.compareToAscii( "URL" ) == 0 ); + CPPUNIT_ASSERT_MESSAGE( "drawMaskedImage got InputStream param #1", + xBitmap[1].Name.compareToAscii( "InputStream" ) == 0 ); + + CPPUNIT_ASSERT_MESSAGE( "drawMaskedImage received two properties #2", + xMask.getLength()==3 ); + CPPUNIT_ASSERT_MESSAGE( "drawMaskedImage got URL param #2", + xMask[0].Name.compareToAscii( "URL" ) == 0 ); + CPPUNIT_ASSERT_MESSAGE( "drawMaskedImage got InputStream param #2", + xMask[1].Name.compareToAscii( "InputStream" ) == 0 ); + } + + virtual void drawAlphaMaskedImage(const uno::Sequence& xBitmap, + const uno::Sequence& xMask) + { + CPPUNIT_ASSERT_MESSAGE( "drawAlphaMaskedImage received two properties #1", + xBitmap.getLength()==3 ); + CPPUNIT_ASSERT_MESSAGE( "drawAlphaMaskedImage got URL param #1", + xBitmap[0].Name.compareToAscii( "URL" ) == 0 ); + CPPUNIT_ASSERT_MESSAGE( "drawAlphaMaskedImage got InputStream param #1", + xBitmap[1].Name.compareToAscii( "InputStream" ) == 0 ); + + CPPUNIT_ASSERT_MESSAGE( "drawAlphaMaskedImage received two properties #2", + xMask.getLength()==3 ); + CPPUNIT_ASSERT_MESSAGE( "drawAlphaMaskedImage got URL param #2", + xMask[0].Name.compareToAscii( "URL" ) == 0 ); + CPPUNIT_ASSERT_MESSAGE( "drawAlphaMaskedImage got InputStream param #2", + xMask[1].Name.compareToAscii( "InputStream" ) == 0 ); + } + + typedef std::hash_map IdToFontMap; + typedef std::hash_map FontToIdMap; + + typedef std::hash_map IdToGCMap; + typedef std::hash_map GCToIdMap; + + typedef std::vector GraphicsContextStack; + + sal_Int32 m_nNextFontId; + IdToFontMap m_aIdToFont; + FontToIdMap m_aFontToId; + + GraphicsContextStack m_aGCStack; + geometry::RealSize2D m_aPageSize; + geometry::RealRectangle2D m_aHyperlinkBounds; + ::rtl::OUString m_aURI; + ::rtl::OUStringBuffer m_aTextOut; + sal_Int32 m_nNumPages; + bool m_bPageEnded; + bool m_bRedCircleSeen; + bool m_bGreenStrokeSeen; + bool m_bDashedLineSeen; + }; + + class PDFITest : public CppUnit::TestFixture + { + uno::Reference mxCtx; + rtl::OUString msBaseDir; + bool mbUnoInitialized; + + public: + PDFITest() : mxCtx(),msBaseDir(),mbUnoInitialized(false) + {} + + void setUp() + { + if( !mbUnoInitialized ) + { + const char* pArgs( getForwardString() ); + CPPUNIT_ASSERT_MESSAGE("Test file parameter", pArgs); + + msBaseDir = rtl::OUString::createFromAscii(pArgs); + + // bootstrap UNO + try + { + ::rtl::OUString aIniUrl; + CPPUNIT_ASSERT_MESSAGE( + "Converting ini file to URL", + osl_getFileURLFromSystemPath( + (msBaseDir+rtl::OUString::createFromAscii("pdfi_unittest_test.ini")).pData, + &aIniUrl.pData ) == osl_File_E_None ); + + mxCtx = ::cppu::defaultBootstrap_InitialComponentContext(aIniUrl); + CPPUNIT_ASSERT_MESSAGE("Getting component context", mxCtx.is()); + } + catch( uno::Exception& ) + { + CPPUNIT_ASSERT_MESSAGE("Bootstrapping UNO", false); + } + + mbUnoInitialized = true; + } + } + void tearDown() + { + } + + void testXPDFParser() + { + pdfi::ContentSinkSharedPtr pSink( new TestSink() ); + pdfi::xpdf_ImportFromFile( msBaseDir + rtl::OUString::createFromAscii("pdfi_unittest_test.pdf"), + pSink, + mxCtx ); + + // make destruction explicit, a bunch of things are + // checked in the destructor + pSink.reset(); + } + + void testOdfDrawExport() + { + pdfi::PDFIRawAdaptor aAdaptor( mxCtx ); + aAdaptor.setTreeVisitorFactory( createDrawTreeVisitorFactory() ); + + ::rtl::OUString aURL, aAbsURL, aBaseURL; + osl_getFileURLFromSystemPath( (msBaseDir + rtl::OUString::createFromAscii("pdfi_unittest_draw.xml")).pData, + &aURL.pData ); + osl_getProcessWorkingDir(&aBaseURL.pData); + osl_getAbsoluteFileURL(aBaseURL.pData,aURL.pData,&aAbsURL.pData); + CPPUNIT_ASSERT_MESSAGE("Exporting to ODF", + aAdaptor.odfConvert( msBaseDir + rtl::OUString::createFromAscii("pdfi_unittest_test.pdf"), + new OutputWrap(aAbsURL), + NULL )); + } + + void testOdfWriterExport() + { + pdfi::PDFIRawAdaptor aAdaptor( mxCtx ); + aAdaptor.setTreeVisitorFactory( createWriterTreeVisitorFactory() ); + + ::rtl::OUString aURL, aAbsURL, aBaseURL; + osl_getFileURLFromSystemPath( (msBaseDir + rtl::OUString::createFromAscii("pdfi_unittest_writer.xml")).pData, + &aURL.pData ); + osl_getProcessWorkingDir(&aBaseURL.pData); + osl_getAbsoluteFileURL(aBaseURL.pData,aURL.pData,&aAbsURL.pData); + CPPUNIT_ASSERT_MESSAGE("Exporting to ODF", + aAdaptor.odfConvert( msBaseDir + rtl::OUString::createFromAscii("pdfi_unittest_test.pdf"), + new OutputWrap(aAbsURL), + NULL )); + } + + CPPUNIT_TEST_SUITE(PDFITest); + CPPUNIT_TEST(testXPDFParser); + CPPUNIT_TEST(testOdfWriterExport); + CPPUNIT_TEST(testOdfDrawExport); + CPPUNIT_TEST_SUITE_END(); + }; + +} + +// ======================================================================= + +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(PDFITest, "PDFITest"); + + +// ----------------------------------------------------------------------------- + +// this macro creates an empty function, which will called by the RegisterAllFunctions() +// to let the user the possibility to also register some functions by hand. +NOADDITIONAL;