/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sdext.hxx" #define BASEGFX_STATICLIBRARY #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() { ASSERT_TRUE(m_aPageSize.Width == 79400 && m_aPageSize.Height == 59500) << "A4 page size (in 100th of points)"; ASSERT_TRUE(m_bPageEnded) << "endPage() called"; ASSERT_TRUE(m_nNumPages == 1) << "Num pages equal one"; ASSERT_TRUE(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)) << "Correct hyperlink bounding box"; ASSERT_TRUE(m_aURI == ::rtl::OUString::createFromAscii( "http://download.openoffice.org/" )) << "Correct hyperlink URI"; 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 ); ASSERT_TRUE( sText == aTmp ) << "Imported text is \"This is a testtext New paragraph, new line" " Hyperlink, this is * This is more text outline mode * New paragraph\""; ASSERT_TRUE(m_bRedCircleSeen) << "red circle seen in input"; ASSERT_TRUE(m_bGreenStrokeSeen) << "green stroke seen in input"; ASSERT_TRUE(m_bDashedLineSeen) << "dashed line seen in input"; } 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); ASSERT_TRUE(start == 0.0) << "line dashing start offset"; } 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() ) { ASSERT_TRUE(rContext.LineColor.Alpha == 1.0 && rContext.LineColor.Red == 0.0 && rContext.LineColor.Green == 1.0 && rContext.LineColor.Blue == 0.0) << "Line color is green"; ASSERT_TRUE(rtl::math::approxEqual(rContext.LineWidth, 28.3)) << "Line width is 0"; const char* sExportString = "m53570 7650-35430 24100"; ASSERT_TRUE(basegfx::tools::exportToSvgD( aPath, true, true, false ).compareToAscii(sExportString) == 0) << "Stroke is m535.7 518.5-354.3-241"; m_bGreenStrokeSeen = true; } else { ASSERT_TRUE(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]) << "Dash array cons ists of four entries"; ASSERT_TRUE(rContext.LineColor.Alpha == 1.0 && rContext.LineColor.Red == 0.0 && rContext.LineColor.Green == 0.0 && rContext.LineColor.Blue == 0.0) << "Line color is black"; ASSERT_TRUE(rContext.LineWidth == 0) << "Line width is 0"; const char* sExportString = "m49890 5670.00000000001-35430 24090"; ASSERT_TRUE(basegfx::tools::exportToSvgD( aPath, true, true, false ).compareToAscii(sExportString) == 0) << "Stroke is m49890 5670.00000000001-35430 24090"; m_bDashedLineSeen = true; } ASSERT_TRUE(rContext.BlendMode == rendering::BlendMode::NORMAL) << "Blend mode is normal"; ASSERT_TRUE(rContext.LineJoin == rendering::PathJoinType::ROUND) << "Join type is round"; ASSERT_TRUE(rContext.LineCap == rendering::PathCapType::BUTT) << "Cap type is butt"; ASSERT_TRUE(rContext.MiterLimit == 10) << "Line miter limit is 10"; ASSERT_TRUE(rContext.Flatness == 1) << "Flatness is 0"; ASSERT_TRUE(rContext.FontId == 0) << "Font id is 0"; } virtual void fillPath( const uno::Reference& rPath ) { GraphicsContext& rContext( getCurrentContext() ); basegfx::B2DPolyPolygon aPath = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath); aPath.transform( rContext.Transformation ); ASSERT_TRUE(rContext.FillColor.Alpha == 1.0 && rContext.FillColor.Red == 0.0 && rContext.FillColor.Green == 0.0 && rContext.FillColor.Blue == 0.0) << "Fill color is black"; ASSERT_TRUE(rContext.BlendMode == rendering::BlendMode::NORMAL) << "Blend mode is normal"; ASSERT_TRUE(rContext.Flatness == 10) << "Flatness is 10"; ASSERT_TRUE(rContext.FontId == 0) << "Font id is 0"; } virtual void eoFillPath( const uno::Reference& rPath ) { GraphicsContext& rContext( getCurrentContext() ); basegfx::B2DPolyPolygon aPath = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath); aPath.transform( rContext.Transformation ); ASSERT_TRUE(rContext.FillColor.Alpha == 1.0 && rContext.FillColor.Red == 1.0 && rContext.FillColor.Green == 0.0 && rContext.FillColor.Blue == 0.0) << "Fill color is black"; ASSERT_TRUE(rContext.BlendMode == rendering::BlendMode::NORMAL) << "Blend mode is normal"; ASSERT_TRUE(rContext.Flatness == 1) << "Flatness is 0"; ASSERT_TRUE(rContext.FontId == 0) << "Font id is 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"; ASSERT_TRUE(basegfx::tools::exportToSvgD( aPath, true, true, false ).compareToAscii(sExportString) == 0) << "Stroke is a 4-bezier circle"; 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() ) // #i92985# adapted API from (..., false, false) to (..., true, false) aNewClip = basegfx::tools::clipPolyPolygonOnPolyPolygon( aCurClip, aNewClip, true, 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() ) // #i92985# adapted API from (..., false, false) to (..., true, false) aNewClip = basegfx::tools::clipPolyPolygonOnPolyPolygon( aCurClip, aNewClip, true, 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*/ ) { ASSERT_TRUE(xBitmap.getLength()==3) << "drawMask received two properties"; ASSERT_TRUE(xBitmap[0].Name.compareToAscii( "URL" ) == 0) << "drawMask got URL param"; ASSERT_TRUE(xBitmap[1].Name.compareToAscii( "InputStream" ) == 0) << "drawMask got InputStream param"; } virtual void drawImage(const uno::Sequence& xBitmap ) { ASSERT_TRUE(xBitmap.getLength()==3) << "drawImage received two properties"; ASSERT_TRUE(xBitmap[0].Name.compareToAscii( "URL" ) == 0) << "drawImage got URL param"; ASSERT_TRUE(xBitmap[1].Name.compareToAscii( "InputStream" ) == 0) << "drawImage got InputStream param"; } virtual void drawColorMaskedImage(const uno::Sequence& xBitmap, const uno::Sequence& /*xMaskColors*/ ) { ASSERT_TRUE(xBitmap.getLength()==3) << "drawColorMaskedImage received two properties"; ASSERT_TRUE(xBitmap[0].Name.compareToAscii( "URL" ) == 0) << "drawColorMaskedImage got URL param"; ASSERT_TRUE(xBitmap[1].Name.compareToAscii( "InputStream" ) == 0) << "drawColorMaskedImage got InputStream param"; } virtual void drawMaskedImage(const uno::Sequence& xBitmap, const uno::Sequence& xMask, bool /*bInvertMask*/) { ASSERT_TRUE(xBitmap.getLength()==3) << "drawMaskedImage received two properties #1"; ASSERT_TRUE(xBitmap[0].Name.compareToAscii( "URL" ) == 0) << "drawMaskedImage got URL param #1"; ASSERT_TRUE(xBitmap[1].Name.compareToAscii( "InputStream" ) == 0) << "drawMaskedImage got InputStream param #1"; ASSERT_TRUE(xMask.getLength()==3) << "drawMaskedImage received two properties #2"; ASSERT_TRUE(xMask[0].Name.compareToAscii( "URL" ) == 0) << "drawMaskedImage got URL param #2"; ASSERT_TRUE(xMask[1].Name.compareToAscii( "InputStream" ) == 0) << "drawMaskedImage got InputStream param #2"; } virtual void drawAlphaMaskedImage(const uno::Sequence& xBitmap, const uno::Sequence& xMask) { ASSERT_TRUE(xBitmap.getLength()==3) << "drawAlphaMaskedImage received two properties #1"; ASSERT_TRUE(xBitmap[0].Name.compareToAscii( "URL" ) == 0) << "drawAlphaMaskedImage got URL param #1"; ASSERT_TRUE(xBitmap[1].Name.compareToAscii( "InputStream" ) == 0) << "drawAlphaMaskedImage got InputStream param #1"; ASSERT_TRUE(xMask.getLength()==3) << "drawAlphaMaskedImage received two properties #2"; ASSERT_TRUE(xMask[0].Name.compareToAscii( "URL" ) == 0) << "drawAlphaMaskedImage got URL param #2"; ASSERT_TRUE(xMask[1].Name.compareToAscii( "InputStream" ) == 0) << "drawAlphaMaskedImage got InputStream param #2"; } virtual void setTextRenderMode( sal_Int32 ) { } 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 ::testing::Test { protected: uno::Reference mxCtx; rtl::OUString msBaseDir; bool mbUnoInitialized; public: PDFITest() : mxCtx(),msBaseDir(),mbUnoInitialized(false) {} void SetUp() { if( !mbUnoInitialized ) { const char* pArgs( getenv("TESTS_FORWARD_STRING") ); ASSERT_TRUE(pArgs) << "Test file parameter"; msBaseDir = rtl::OUString::createFromAscii(pArgs); // bootstrap UNO try { ::rtl::OUString aIniUrl; ASSERT_TRUE( osl_getFileURLFromSystemPath( (msBaseDir+rtl::OUString::createFromAscii("pdfi_unittest_test.ini")).pData, &aIniUrl.pData ) == osl_File_E_None ) << "Converting ini file to URL"; mxCtx = ::cppu::defaultBootstrap_InitialComponentContext(aIniUrl); ASSERT_TRUE(mxCtx.is()) << "Getting component context"; } catch( uno::Exception& ) { FAIL() << "Bootstrapping UNO"; } mbUnoInitialized = true; } } void TearDown() { } }; TEST_F(PDFITest, testXPDFParser) { pdfi::ContentSinkSharedPtr pSink( new TestSink() ); pdfi::xpdf_ImportFromFile( msBaseDir + rtl::OUString::createFromAscii("pdfi_unittest_test.pdf"), pSink, uno::Reference< task::XInteractionHandler >(), rtl::OUString(), mxCtx ); // make destruction explicit, a bunch of things are // checked in the destructor pSink.reset(); } TEST_F(PDFITest, 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); ASSERT_TRUE(aAdaptor.odfConvert( msBaseDir + rtl::OUString::createFromAscii("pdfi_unittest_test.pdf"), new OutputWrap(aAbsURL), NULL )) << "Exporting to ODF"; } TEST_F(PDFITest, 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); ASSERT_TRUE(aAdaptor.odfConvert( msBaseDir + rtl::OUString::createFromAscii("pdfi_unittest_test.pdf"), new OutputWrap(aAbsURL), NULL )) << "Exporting to ODF"; } } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }