LCOV - code coverage report
Current view: top level - frmts/pdf - pdfio.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 115 137 83.9 %
Date: 2026-02-06 20:12:57 Functions: 18 22 81.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PDF driver
       4             :  * Purpose:  GDALDataset driver for PDF dataset.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_pdf.h"
      14             : 
      15             : #ifdef HAVE_POPPLER
      16             : 
      17             : #include "pdfio.h"
      18             : 
      19             : #include "cpl_vsi.h"
      20             : 
      21         233 : static vsi_l_offset VSIPDFFileStreamGetSize(VSILFILE *f)
      22             : {
      23         233 :     VSIFSeekL(f, 0, SEEK_END);
      24         233 :     vsi_l_offset nSize = VSIFTellL(f);
      25         233 :     VSIFSeekL(f, 0, SEEK_SET);
      26         233 :     return nSize;
      27             : }
      28             : 
      29             : /************************************************************************/
      30             : /*                          VSIPDFFileStream()                          */
      31             : /************************************************************************/
      32             : 
      33         233 : VSIPDFFileStream::VSIPDFFileStream(VSILFILE *fIn, const char *pszFilename,
      34         233 :                                    Object &&dictA)
      35         233 :     : BaseStream(std::move(dictA),
      36         466 :                  static_cast<Goffset>(VSIPDFFileStreamGetSize(fIn))),
      37         233 :       poParent(nullptr), poFilename(new GooString(pszFilename)), f(fIn)
      38             : {
      39         233 : }
      40             : 
      41             : /************************************************************************/
      42             : /*                          VSIPDFFileStream()                          */
      43             : /************************************************************************/
      44             : 
      45       10438 : VSIPDFFileStream::VSIPDFFileStream(VSIPDFFileStream *poParentIn,
      46             :                                    vsi_l_offset startA, bool limitedA,
      47       10438 :                                    vsi_l_offset lengthA, Object &&dictA)
      48       10438 :     : BaseStream(std::move(dictA), static_cast<Goffset>(lengthA)),
      49       10438 :       poParent(poParentIn), poFilename(poParentIn->poFilename),
      50       10438 :       f(poParentIn->f), nStart(startA), bLimited(limitedA), nLength(lengthA)
      51             : {
      52       10438 : }
      53             : 
      54             : /************************************************************************/
      55             : /*                         ~VSIPDFFileStream()                          */
      56             : /************************************************************************/
      57             : 
      58       21342 : VSIPDFFileStream::~VSIPDFFileStream()
      59             : {
      60       10671 :     close();
      61       10671 :     if (poParent == nullptr)
      62             :     {
      63         233 :         delete poFilename;
      64             :     }
      65       21342 : }
      66             : 
      67             : /************************************************************************/
      68             : /*                                copy()                                */
      69             : /************************************************************************/
      70             : 
      71             : #if POPPLER_MAJOR_VERSION > 26 ||                                              \
      72             :     (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION >= 2)
      73             : std::unique_ptr<BaseStream> VSIPDFFileStream::copy()
      74             : {
      75             :     return std::make_unique<VSIPDFFileStream>(poParent, nStart, bLimited,
      76             :                                               nLength, dict.copy());
      77             : }
      78             : #else
      79           0 : BaseStream *VSIPDFFileStream::copy()
      80             : {
      81           0 :     return new VSIPDFFileStream(poParent, nStart, bLimited, nLength,
      82           0 :                                 dict.copy());
      83             : }
      84             : #endif
      85             : 
      86             : /************************************************************************/
      87             : /*                           makeSubStream()                            */
      88             : /************************************************************************/
      89             : 
      90             : #if POPPLER_MAJOR_VERSION > 25 ||                                              \
      91             :     (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 5)
      92             : std::unique_ptr<Stream> VSIPDFFileStream::makeSubStream(Goffset startA,
      93             :                                                         bool limitedA,
      94             :                                                         Goffset lengthA,
      95             :                                                         Object &&dictA)
      96             : {
      97             :     return std::make_unique<VSIPDFFileStream>(this, startA, limitedA, lengthA,
      98             :                                               std::move(dictA));
      99             : }
     100             : #else
     101       10438 : Stream *VSIPDFFileStream::makeSubStream(Goffset startA, bool limitedA,
     102             :                                         Goffset lengthA, Object &&dictA)
     103             : {
     104             :     return new VSIPDFFileStream(this, startA, limitedA, lengthA,
     105       10438 :                                 std::move(dictA));
     106             : }
     107             : #endif
     108             : 
     109             : /************************************************************************/
     110             : /*                               getPos()                               */
     111             : /************************************************************************/
     112             : 
     113        2546 : Goffset VSIPDFFileStream::getPos()
     114             : {
     115        2546 :     return static_cast<Goffset>(nCurrentPos);
     116             : }
     117             : 
     118             : /************************************************************************/
     119             : /*                              getStart()                              */
     120             : /************************************************************************/
     121             : 
     122         474 : Goffset VSIPDFFileStream::getStart()
     123             : {
     124         474 :     return static_cast<Goffset>(nStart);
     125             : }
     126             : 
     127             : /************************************************************************/
     128             : /*                              getKind()                               */
     129             : /************************************************************************/
     130             : 
     131           0 : StreamKind VSIPDFFileStream::getKind() const
     132             : {
     133           0 :     return strFile;
     134             : }
     135             : 
     136             : /************************************************************************/
     137             : /*                            getFileName()                             */
     138             : /************************************************************************/
     139             : 
     140         466 : GooString *VSIPDFFileStream::getFileName()
     141             : {
     142         466 :     return poFilename;
     143             : }
     144             : 
     145             : /************************************************************************/
     146             : /*                             FillBuffer()                             */
     147             : /************************************************************************/
     148             : 
     149       42407 : int VSIPDFFileStream::FillBuffer()
     150             : {
     151       42407 :     if (nBufferLength == 0)
     152           0 :         return FALSE;
     153       42407 :     if (nBufferLength != -1 && nBufferLength < BUFFER_SIZE)
     154         455 :         return FALSE;
     155             : 
     156       41952 :     nPosInBuffer = 0;
     157             :     int nToRead;
     158       41952 :     if (!bLimited)
     159       10981 :         nToRead = BUFFER_SIZE;
     160       30971 :     else if (nCurrentPos + BUFFER_SIZE > nStart + nLength)
     161        1296 :         nToRead = static_cast<int>(nStart + nLength - nCurrentPos);
     162             :     else
     163       29675 :         nToRead = BUFFER_SIZE;
     164       41952 :     if (nToRead < 0)
     165           0 :         return FALSE;
     166       41952 :     nBufferLength = static_cast<int>(VSIFReadL(abyBuffer, 1, nToRead, f));
     167       41952 :     if (nBufferLength == 0)
     168          11 :         return FALSE;
     169             : 
     170             :     // Since we now report a non-zero length (as BaseStream::length member),
     171             :     // PDFDoc::getPage() can go to the linearized mode if the file is
     172             :     // linearized, and thus create a pageCache. If so, in PDFDoc::~PDFDoc(), if
     173             :     // pageCache is not null, it would try to access the stream (str) through
     174             :     // getPageCount(), but we have just freed and nullify str before in
     175             :     // PDFFreeDoc(). So make as if the file is not linearized to avoid those
     176             :     // issues... All this is due to our attempt of avoiding cross-heap issues
     177             :     // with allocation and liberation of VSIPDFFileStream as PDFDoc::str member.
     178       41941 :     if (nCurrentPos == 0 || nCurrentPos == VSI_L_OFFSET_MAX)
     179             :     {
     180      506966 :         for (int i = 0;
     181      506966 :              i < nBufferLength - static_cast<int>(strlen("/Linearized ")); i++)
     182             :         {
     183      506446 :             if (memcmp(abyBuffer + i, "/Linearized ", strlen("/Linearized ")) ==
     184             :                 0)
     185             :             {
     186           0 :                 bFoundLinearizedHint = true;
     187           0 :                 memcpy(abyBuffer + i, "/XXXXXXXXXX ", strlen("/Linearized "));
     188           0 :                 break;
     189             :             }
     190             :         }
     191             :     }
     192             : 
     193       41941 :     return TRUE;
     194             : }
     195             : 
     196             : /************************************************************************/
     197             : /*                              getChar()                               */
     198             : /************************************************************************/
     199             : 
     200             : /* The unoptimized version performs a bit less since we must go through */
     201             : /* the whole virtual I/O chain for each character reading. We save a few */
     202             : /* percent with this extra internal caching */
     203             : 
     204    31424600 : int VSIPDFFileStream::getChar()
     205             : {
     206             : #ifdef unoptimized_version
     207             :     GByte chRead;
     208             :     if (bLimited && nCurrentPos >= nStart + nLength)
     209             :         return EOF;
     210             :     if (VSIFReadL(&chRead, 1, 1, f) == 0)
     211             :         return EOF;
     212             : #else
     213    31424600 :     if (nPosInBuffer == nBufferLength)
     214             :     {
     215       41271 :         if (!FillBuffer() || nPosInBuffer >= nBufferLength)
     216         165 :             return EOF;
     217             :     }
     218             : 
     219    31424500 :     GByte chRead = abyBuffer[nPosInBuffer];
     220    31424500 :     nPosInBuffer++;
     221             : #endif
     222    31424500 :     nCurrentPos++;
     223    31424500 :     return chRead;
     224             : }
     225             : 
     226             : /************************************************************************/
     227             : /*                         getUnfilteredChar()                          */
     228             : /************************************************************************/
     229             : 
     230           0 : int VSIPDFFileStream::getUnfilteredChar()
     231             : {
     232           0 :     return getChar();
     233             : }
     234             : 
     235             : /************************************************************************/
     236             : /*                              lookChar()                              */
     237             : /************************************************************************/
     238             : 
     239         528 : int VSIPDFFileStream::lookChar()
     240             : {
     241             : #ifdef unoptimized_version
     242             :     int nPosBefore = nCurrentPos;
     243             :     int chRead = getChar();
     244             :     if (chRead == EOF)
     245             :         return EOF;
     246             :     VSIFSeekL(f, nCurrentPos = nPosBefore, SEEK_SET);
     247             :     return chRead;
     248             : #else
     249         528 :     int chRead = getChar();
     250         528 :     if (chRead == EOF)
     251          28 :         return EOF;
     252         500 :     nPosInBuffer--;
     253         500 :     nCurrentPos--;
     254         500 :     return chRead;
     255             : #endif
     256             : }
     257             : 
     258             : /************************************************************************/
     259             : /*                               reset()                                */
     260             : /************************************************************************/
     261             : 
     262             : #if POPPLER_MAJOR_VERSION > 25
     263             : bool VSIPDFFileStream::rewind()
     264             : #elif POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2
     265             : bool VSIPDFFileStream::reset()
     266             : #else
     267       10414 : void VSIPDFFileStream::reset()
     268             : #endif
     269             : {
     270       10414 :     nSavedPos = VSIFTellL(f);
     271       10414 :     bHasSavedPos = TRUE;
     272       10414 :     VSIFSeekL(f, nCurrentPos = nStart, SEEK_SET);
     273       10414 :     nPosInBuffer = -1;
     274       10414 :     nBufferLength = -1;
     275             : #if POPPLER_MAJOR_VERSION > 25 ||                                              \
     276             :     (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)
     277             :     return true;
     278             : #endif
     279       10414 : }
     280             : 
     281             : /************************************************************************/
     282             : /*                          unfilteredReset()                           */
     283             : /************************************************************************/
     284             : 
     285             : #if POPPLER_MAJOR_VERSION > 25
     286             : bool VSIPDFFileStream::unfilteredRewind()
     287             : {
     288             :     return rewind();
     289             : }
     290             : #elif POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 3
     291             : bool VSIPDFFileStream::unfilteredReset()
     292             : {
     293             :     return reset();
     294             : }
     295             : #else
     296           0 : void VSIPDFFileStream::unfilteredReset()
     297             : {
     298           0 :     reset();
     299           0 : }
     300             : #endif
     301             : 
     302             : /************************************************************************/
     303             : /*                               close()                                */
     304             : /************************************************************************/
     305             : 
     306       19474 : void VSIPDFFileStream::close()
     307             : {
     308       19474 :     if (bHasSavedPos)
     309             :     {
     310        9768 :         nCurrentPos = nSavedPos;
     311        9768 :         VSIFSeekL(f, nCurrentPos, SEEK_SET);
     312             :     }
     313       19474 :     bHasSavedPos = FALSE;
     314       19474 :     nSavedPos = 0;
     315       19474 : }
     316             : 
     317             : /************************************************************************/
     318             : /*                               setPos()                               */
     319             : /************************************************************************/
     320             : 
     321        2293 : void VSIPDFFileStream::setPos(Goffset pos, int dir)
     322             : {
     323        2293 :     if (dir >= 0)
     324             :     {
     325        1815 :         VSIFSeekL(f, nCurrentPos = pos, SEEK_SET);
     326             :     }
     327             :     else
     328             :     {
     329         478 :         if (bLimited == false)
     330             :         {
     331         478 :             VSIFSeekL(f, 0, SEEK_END);
     332             :         }
     333             :         else
     334             :         {
     335           0 :             VSIFSeekL(f, nStart + nLength, SEEK_SET);
     336             :         }
     337         478 :         vsi_l_offset size = VSIFTellL(f);
     338         478 :         vsi_l_offset newpos = static_cast<vsi_l_offset>(pos);
     339         478 :         if (newpos > size)
     340          38 :             newpos = size;
     341         478 :         VSIFSeekL(f, nCurrentPos = size - newpos, SEEK_SET);
     342             :     }
     343        2293 :     nPosInBuffer = -1;
     344        2293 :     nBufferLength = -1;
     345        2293 : }
     346             : 
     347             : /************************************************************************/
     348             : /*                             moveStart()                              */
     349             : /************************************************************************/
     350             : 
     351         233 : void VSIPDFFileStream::moveStart(Goffset delta)
     352             : {
     353         233 :     nStart += delta;
     354         233 :     nCurrentPos = nStart;
     355         233 :     VSIFSeekL(f, nCurrentPos, SEEK_SET);
     356         233 :     nPosInBuffer = -1;
     357         233 :     nBufferLength = -1;
     358         233 : }
     359             : 
     360             : /************************************************************************/
     361             : /*                            hasGetChars()                             */
     362             : /************************************************************************/
     363             : 
     364         880 : bool VSIPDFFileStream::hasGetChars()
     365             : {
     366         880 :     return true;
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /*                              getChars()                              */
     371             : /************************************************************************/
     372             : 
     373         880 : int VSIPDFFileStream::getChars(int nChars, unsigned char *buffer)
     374             : {
     375         880 :     int nRead = 0;
     376        2263 :     while (nRead < nChars)
     377             :     {
     378        1684 :         int nToRead = nChars - nRead;
     379        1684 :         if (nPosInBuffer == nBufferLength)
     380             :         {
     381        1136 :             if (!bLimited && nToRead > BUFFER_SIZE)
     382             :             {
     383             :                 int nJustRead =
     384           0 :                     static_cast<int>(VSIFReadL(buffer + nRead, 1, nToRead, f));
     385           0 :                 nPosInBuffer = -1;
     386           0 :                 nBufferLength = -1;
     387           0 :                 nCurrentPos += nJustRead;
     388           0 :                 nRead += nJustRead;
     389           0 :                 break;
     390             :             }
     391        1136 :             else if (!FillBuffer() || nPosInBuffer >= nBufferLength)
     392         301 :                 break;
     393             :         }
     394        1383 :         if (nToRead > nBufferLength - nPosInBuffer)
     395         804 :             nToRead = nBufferLength - nPosInBuffer;
     396             : 
     397        1383 :         memcpy(buffer + nRead, abyBuffer + nPosInBuffer, nToRead);
     398        1383 :         nPosInBuffer += nToRead;
     399        1383 :         nCurrentPos += nToRead;
     400        1383 :         nRead += nToRead;
     401             :     }
     402         880 :     return nRead;
     403             : }
     404             : 
     405             : #endif

Generated by: LCOV version 1.14