wxIScan
wxpoppler.cpp
Go to the documentation of this file.
00001 /***************************************************************
00002  * Name:      wxpoppler.cpp
00003  * Purpose:   wxWidgets wrapper around libpoppler for PDF file
00004  *            viewing.
00005  * Author:    Daniel Nell (daniel.nell@nellresearch.de)
00006  * Created:   2008-07-08
00007  * Copyright: Daniel Nell (www.nellresearch.de)
00008  * License:
00009  **************************************************************/
00010 
00011 #include <wx/filename.h>
00012 #include <wx/filesys.h>
00013 #include <wxpoppler.h>
00014 #include <gdk-pixbuf/gdk-pixbuf.h>
00015 #if wxUSE_PDF
00016 #   include <wx/mstream.h>
00017 #   if __WXPDFDOC__
00018 #       include <wx/pdfdoc.h>
00019 #   endif // __WXPDFDOC__
00020 #endif // wxUSE_PDF
00021 
00022 /////////////////////////////////////////////////////////////////////////////
00023 //  Class wxPoppler
00024 //
00025 
00026 #if __WXPOPPLER_DYNAMIC__
00027 // Define dynamic library.
00028 wxDynamicLibrary wxPoppler::m_oWxPopplerDynamicLibrary;
00029 
00030 // Define and initialize dynamic poppler-glib functions with NULL;
00031 wxPoppler::t_poppler_document_new_from_data *wxPoppler::poppler_document_new_from_data= NULL;
00032 wxPoppler::t_poppler_document_new_from_file *wxPoppler::poppler_document_new_from_file= NULL;
00033 wxPoppler::t_poppler_document_get_page *wxPoppler::poppler_document_get_page= NULL;
00034 wxPoppler::t_poppler_page_render_to_pixbuf *wxPoppler::poppler_page_render_to_pixbuf= NULL;
00035 wxPoppler::t_poppler_page_get_size *wxPoppler::poppler_page_get_size= NULL;
00036 wxPoppler::t_poppler_document_get_n_pages *wxPoppler::poppler_document_get_n_pages= NULL;
00037 
00038 #endif // __WXPOPPLER_DYNAMIC__
00039 
00040 
00041 // Initialize static members.
00042 //
00043 int wxPoppler::m_nGlobalDpi= 300;
00044 
00045 
00046 // Standard constructor.
00047 //
00048 wxPoppler::wxPoppler()
00049 {
00050     // Initialize normal members;
00051     m_pPdfPage= NULL;
00052     m_pPdfDocument= NULL;
00053     m_nDpi= m_nGlobalDpi;
00054 
00055 #if __WXPOPPLER_DYNAMIC__
00056     if( !m_oWxPopplerDynamicLibrary.IsLoaded() )
00057     {
00058         if( m_oWxPopplerDynamicLibrary.Load( wxDynamicLibrary::CanonicalizeName( wxT( WXPOPPPLER_LIBPOPPLER ) ) ) )
00059         {
00060             poppler_document_new_from_data= (t_poppler_document_new_from_data *)m_oWxPopplerDynamicLibrary.GetSymbol( wxT( "poppler_document_new_from_data" ) );
00061             poppler_document_new_from_file= (t_poppler_document_new_from_file *)m_oWxPopplerDynamicLibrary.GetSymbol( wxT( "poppler_document_new_from_file" ) );
00062             poppler_document_get_page     = (t_poppler_document_get_page *)m_oWxPopplerDynamicLibrary.GetSymbol( wxT( "poppler_document_get_page" ) );
00063             poppler_page_render_to_pixbuf = (t_poppler_page_render_to_pixbuf *)m_oWxPopplerDynamicLibrary.GetSymbol( wxT( "poppler_page_render_to_pixbuf" ) );
00064             poppler_page_get_size         = (t_poppler_page_get_size *)m_oWxPopplerDynamicLibrary.GetSymbol( wxT( "poppler_page_get_size" ) );
00065             poppler_document_get_n_pages  = (t_poppler_document_get_n_pages *)m_oWxPopplerDynamicLibrary.GetSymbol( wxT( "poppler_document_get_n_pages" ) );
00066         }
00067         else
00068         {
00069             // Log error message.
00070             wxLogError( wxString( wxT( "wxPoppler::wxPoppler -- " ) )
00071                           + _( "Cannot load poppler library." ) );
00072         }
00073     }
00074 #endif // __WXPOPPLER_DYNAMIC__
00075 }
00076 
00077 // Virtual destructor.
00078 //
00079 wxPoppler::~wxPoppler()
00080 {
00081     // If exists delete the PDF page.
00082     if( m_pPdfPage )
00083     {
00084         g_object_unref( m_pPdfPage );
00085     }
00086 
00087     // If exists delete the PDF document.
00088     if( m_pPdfDocument )
00089     {
00090         g_object_unref( m_pPdfDocument );
00091     }
00092 }
00093 
00094 // Opens a PDF stream from file or memory depending on the parameterization.
00095 //
00096 bool wxPoppler::Open( char *pcData, const int nLength, const wxString& strFileName )
00097 {
00098     GError *pGError= NULL;
00099 
00100     if( pcData && nLength )
00101     {
00102         m_pPdfDocument= poppler_document_new_from_data( pcData, nLength, "", &pGError );
00103     }
00104     else
00105     {
00106         wxFileName oFileName( strFileName );
00107         wxString strFileNameAsUri= wxFileSystem::FileNameToURL( oFileName );
00108 //        // Add the "file://" protocol-identifier.
00109 //        wxString strFileNameAsUri= wxT( "file://" ) + strFileName;
00110 //
00111 //        // Replace backslashes by slashes to confirm to the "file://" URI rules.
00112 //        strFileNameAsUri.Replace( wxT( "\\" ), wxT( "/" ) );
00113 //
00114         // Open the file.
00115         m_pPdfDocument= poppler_document_new_from_file( strFileNameAsUri.char_str(), "", &pGError );
00116     }
00117     if( !m_pPdfDocument )
00118     {
00119         // Log error message from poppler library.
00120         if( pGError )
00121         {
00122             wxLogError( wxT( "wxPoppler::Open -- GError: %s" ),
00123                           wxString( pGError->message, wxConvLibc ).c_str() );
00124             g_clear_error( &pGError );
00125         }
00126 
00127         // Signal error.
00128         return false;
00129     }
00130     return true;
00131 }
00132 
00133 // Select the given page number to render by a consecutive call to RenderPage().
00134 //
00135 bool wxPoppler::SelectPage( int nIndex )
00136 {
00137     PopplerPage *pPdfPageTemp= poppler_document_get_page( m_pPdfDocument, nIndex );
00138 
00139     if( !pPdfPageTemp )
00140     {
00141         return false;
00142     }
00143     m_pPdfPage= pPdfPageTemp;
00144     g_object_ref( m_pPdfPage );
00145     return true;
00146 }
00147 
00148 // Render the (previously selected) PDF page and store the result in m_oImage.
00149 //
00150 bool wxPoppler::RenderPage()
00151 {
00152     if( !m_pPdfPage )
00153     {
00154         // Signal error.
00155         wxLogError( wxString( wxT( "wxPoppler::RenderPage -- " ) )
00156                       + _( "PDF page is not valid." ) );
00157         return false;
00158     }
00159 
00160     double dblWidth, dblHeight;
00161     double dblDpiBy72= (double)m_nDpi / 72.0;
00162 
00163     poppler_page_get_size( m_pPdfPage, &dblWidth, &dblHeight );
00164 
00165     int nWidth=  (int)( dblWidth  * dblDpiBy72 );
00166     int nHeight= (int)( dblHeight * dblDpiBy72 );
00167 
00168     GdkPixbuf *pGdkPixBuf= gdk_pixbuf_new( GDK_COLORSPACE_RGB, false, 8, nWidth, nHeight );
00169     if( !pGdkPixBuf )
00170     {
00171         // Signal error.
00172         wxLogError( wxString( wxT( "wxPoppler::RenderPage -- " ) )
00173                       + _( "Cannot create GdkPixbuf object." ) );
00174         return false;
00175     }
00176 
00177     poppler_page_render_to_pixbuf( m_pPdfPage, 0, 0, nWidth, nHeight, dblDpiBy72, 0, pGdkPixBuf );
00178     if(
00179         ( gdk_pixbuf_get_colorspace( pGdkPixBuf ) != GDK_COLORSPACE_RGB )
00180         || ( gdk_pixbuf_get_bits_per_sample( pGdkPixBuf ) != 8 )
00181         || ( gdk_pixbuf_get_has_alpha( pGdkPixBuf ) )
00182         || ( gdk_pixbuf_get_n_channels( pGdkPixBuf ) != 3 )
00183       )
00184     {
00185         // Signal error.
00186         wxLogError( wxString( wxT( "wxPoppler::RenderPage -- " ) )
00187                       + _( "Got wrong bitmap format from libpoppler." ) );
00188         g_object_unref( pGdkPixBuf );
00189         return false;
00190     }
00191 
00192     int nPixBufRowSize= gdk_pixbuf_get_rowstride( pGdkPixBuf );
00193     guchar *pPixBufData= gdk_pixbuf_get_pixels( pGdkPixBuf );
00194     int nImageBufRowSize= 3 * nWidth;
00195     unsigned char *pImageBuf= (unsigned char *)malloc( nImageBufRowSize * nHeight );
00196 
00197     if( !pImageBuf )
00198     {
00199         // Signal error.
00200         wxLogError( wxString( wxT( "wxPoppler::RenderPage -- " ) )
00201                       + _( "Cannot allocate a buffer for wxImage object." ) );
00202         g_object_unref( pGdkPixBuf );
00203         return false;
00204     }
00205 
00206     unsigned char *pImageBufLine= pImageBuf;
00207 
00208     for( int y= 0; y < nHeight; y++ )
00209     {
00210 
00211         memcpy( pImageBufLine, pPixBufData, nImageBufRowSize );
00212         pPixBufData += nPixBufRowSize;
00213         pImageBufLine += nImageBufRowSize;
00214     }
00215 
00216     // Create an wxImage object an set resolution information.
00217     m_oImage= wxImage( nWidth, nHeight, pImageBuf );
00218     m_oImage.SetOption( wxIMAGE_OPTION_RESOLUTIONUNIT, wxIMAGE_RESOLUTION_INCHES );
00219     m_oImage.SetOption( wxIMAGE_OPTION_RESOLUTION, m_nDpi );
00220     m_oImage.SetOption( wxIMAGE_OPTION_RESOLUTIONX, m_nDpi );
00221     m_oImage.SetOption( wxIMAGE_OPTION_RESOLUTIONY, m_nDpi );
00222 
00223     g_object_unref( pGdkPixBuf );
00224     return true;
00225 }
00226 
00227 
00228 
00229 #if wxUSE_PDF
00230 /////////////////////////////////////////////////////////////////////////////
00231 //  Class wxPDFHandler
00232 //
00233 IMPLEMENT_DYNAMIC_CLASS( wxPDFHandler, wxImageHandler )
00234 
00235 // Loads an image from a stream, putting the resulting data into image.
00236 //
00237 bool wxPDFHandler::LoadFile( wxImage* poImage, wxInputStream& oIStream, bool bVerbose, int nIndex )
00238 {
00239     // Check if there is a valid PDF file.
00240     if( !CanRead( oIStream ) )
00241     {
00242         if( bVerbose )
00243         {
00244             wxLogError( wxString( wxT( "wxPDFHandler::LoadFile -- " ) )
00245                           + _( "PDF: This is not a PDF file." ) );
00246         }
00247         return false;
00248     }
00249 
00250     // Write the input stream to a memory buffer (by using a wxMemoryOutputStream object).
00251     wxFileOffset oOffset= oIStream.TellI();
00252     oIStream.SeekI( 0, wxFromEnd );
00253     wxFileOffset nSize= oIStream.TellI();
00254     oIStream.SeekI( 0 );
00255     char *pcData= new char[nSize];
00256     wxMemoryOutputStream oOStream( pcData, nSize );
00257     oIStream.Read( oOStream );
00258     oIStream.SeekI( oOffset );
00259 
00260     int bRetC= true;
00261     int nPage= ( nIndex >= 0 ) ? nIndex : 0;
00262     wxPoppler oPoppler;
00263 
00264     // Render an image of the selected page.
00265     if(    oPoppler.Open( (char *)oOStream.GetOutputStreamBuffer()->GetBufferStart(),
00266                           oOStream.GetOutputStreamBuffer()->GetBufferSize() )
00267         && oPoppler.SelectPage( nPage )
00268         && oPoppler.RenderPage()
00269       )
00270     {
00271         (*poImage)= oPoppler.GetImage();
00272         m_nPageCount= oPoppler.GetPageCount();
00273     }
00274     else
00275     {
00276         if( bVerbose )
00277         {
00278             wxLogError( wxString( wxT( "wxPDFHandler::LoadFile -- " ) )
00279                           + _( "Cannot render temporary PDF file." ) );
00280         }
00281         bRetC= false;
00282     }
00283     delete[] pcData;
00284     return bRetC;
00285 }
00286 
00287 // Saves a given image in the output stream.
00288 //
00289 bool wxPDFHandler::SaveFile( wxImage* poImage, wxOutputStream& oOStream, bool bVerbose )
00290 {
00291 #if __WXPDFDOC__
00292     if( poImage->HasOption( wxIMAGE_OPTION_RESOLUTIONUNIT ) )
00293     {
00294         int nResolutionUnit= poImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT );
00295         int nResolution= 0;
00296 
00297         // Get image resolution-
00298         if(  poImage->HasOption( wxIMAGE_OPTION_RESOLUTION ) )
00299         {
00300             nResolution= poImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTION );
00301         }
00302         else if(  poImage->HasOption( wxIMAGE_OPTION_RESOLUTIONX ) && (  poImage->HasOption( wxIMAGE_OPTION_RESOLUTIONY ) ) )
00303         {
00304             int nResolutionX= poImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
00305             int nResolutionY= poImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
00306 
00307             if( nResolutionX == nResolutionY )
00308             {
00309                 nResolution= nResolutionX;
00310             }
00311         }
00312         if( nResolution )
00313         {
00314             // Save image in a temporary file.
00315             wxString strTempFileName= wxFileName::CreateTempFileName( wxEmptyString );
00316 
00317             poImage->SaveFile( strTempFileName, wxT( "image/jpeg" ) );
00318 
00319             // Create a PDF document, add a page, and put the image on it.
00320             wxPdfDocument oPdfDocument;
00321 
00322             oPdfDocument.AddPage( ( poImage->GetHeight() > poImage->GetWidth() ) ? wxPORTRAIT : wxLANDSCAPE );
00323 
00324             oPdfDocument.SetImageScale( (double)nResolution * ( nResolutionUnit == wxIMAGE_RESOLUTION_CM ? 2.54 : 1.0 ) / 72.0 );
00325             oPdfDocument.Image( strTempFileName, 0 , 0, 0, 0, wxT( "image/jpeg" ) );
00326 
00327             // Remove temporary file.
00328             ::wxRemoveFile( strTempFileName );
00329 
00330             // Send the PDF to the output stream.
00331             wxMemoryInputStream oMemoryInputStream( oPdfDocument.CloseAndGetBuffer() );
00332 
00333             oOStream.Write( oMemoryInputStream );
00334 
00335             // Signal success.
00336             return true;
00337         }
00338     }
00339 #endif // __WXPDFDOC__
00340     // File saving not possible or not implemented.
00341     return false;
00342 }
00343 
00344 // Check if the file contains a PDF document.
00345 //
00346 bool wxPDFHandler::DoCanRead( wxInputStream& oIStream )
00347 {
00348     unsigned char aucHeader[4];
00349 
00350     if ( !oIStream.Read( aucHeader, WXSIZEOF( aucHeader ) ) )
00351     {
00352         return false;
00353     }
00354     return memcmp( aucHeader, "%PDF", WXSIZEOF( aucHeader ) ) == 0;
00355 }
00356 #endif // wxUSE_PDF