Planet PDF Forum Planet PDF Forum
  New Posts New Posts RSS Feed - StmObj offset in Cross-References Stream
  FAQ FAQ  Forum Search   Register Register  Login Login

Hi, welcome to the Foxit Planet PDF Forum. If you have PDF or Adobe Acrobat questions then the right place to ask them is here, in this forum.

StmObj offset in Cross-References Stream

 Post Reply Post Reply
Author
Vincenzo1968 View Drop Down
New Member
New Member
Avatar

Joined: 01 Sep 2019
Location: Palermo - Italy
Points: 7
Post Options Post Options   Quote Vincenzo1968 Quote  Post ReplyReply Direct Link To This Post Topic: StmObj offset in Cross-References Stream
    Posted: 01 Sep 2019 at 9:01am
Hi everyone,

I'm developing a software for extracting text from a pdf file.

I can read the xref table from a pdf in version 1.4 or lower, so as to read the offsets of the various objects present without having to sequentially read the entire file at the beginning.

Now I would like to do the same thing with the Cross-References Stream present from version 1.5 (and higher).

But the object of type XRef contains, for compressed objects(Type = 2), only the number of the object of the stream object that contains it and not its offset, as can be read on the specifications:
The object number of the object stream in which this object is stored. (The generation number of the object stream shall be implicitly 0.)


Is there any way to get the offsets of these objects without having to sequentially read the whole file?

Thanks a lot
(and I apologize for my English).
Back to Top
Vincenzo1968 View Drop Down
New Member
New Member
Avatar

Joined: 01 Sep 2019
Location: Palermo - Italy
Points: 7
Post Options Post Options   Quote Vincenzo1968 Quote  Post ReplyReply Direct Link To This Post Posted: 01 Sep 2019 at 9:14am
The program can be downloaded and installed, on a Linux-* nix environment, with the following terminal commands:
cd $HOME

mkdir myprojs

cd myprojs

git clone https://github.com/Vincenzo1968/mypdfsearch


cd mypdfsearch

./configure

make

sudo make install

Windows users can download the precompiled executable from here: https://vlcfreecode.netsons.org/

or they can download the sources and compile them using MINGW64.
Back to Top
Vincenzo1968 View Drop Down
New Member
New Member
Avatar

Joined: 01 Sep 2019
Location: Palermo - Italy
Points: 7
Post Options Post Options   Quote Vincenzo1968 Quote  Post ReplyReply Direct Link To This Post Posted: 02 Sep 2019 at 2:49pm

At the moment I arrange with the following function that does a prescanning of the file and takes the objects with the relative offset:

int PreParseFile(Params *pParams, char *pszFileName)
{
    int retValue = 1;
    FILE *fp = NULL;
    size_t bytesRead = 0;
    uint32_t curPos = 0;
    unsigned char myBlock[BLOCK_SIZE];
    unsigned char c;
    unsigned char lexeme[128];
    int k = 0;
   
    int Number = 0;
    int Generation = 0;
    uint32_t Offset = 0;
    uint32_t ObjOffset = 0;
   
    PreParseStates state = S_PP0;
   
    fp = fopen(pszFileName, "rb");
    if ( fp == NULL )
    {
        wprintf(L"ERROR PreParseFile: fopen failed for file '%s'.\n", pszFileName);
        fwprintf(pParams->fpErrors, L"ERROR PreParseFile: fopen failed for file '%s'.\n\n", pszFileName);
        retValue = 0;
        goto uscita;
    }
   
    wprintf(L"\n");
   
    while ( (bytesRead = fread(myBlock, 1, BLOCK_SIZE, fp)) )
    {
        curPos = 0;
       
        while ( curPos < bytesRead )
        {
            c = myBlock[curPos];
           
            if ( k >= 128 )
            {
                k = 0;
                state = S_PP0;
            }
           
            switch ( state )
            {
                case S_PP0:
                    k = 0;
                    ObjOffset = 0;
                   
                    if ( '%' == c )
                    {
                        curPos++;
                        Offset++;
                        if ( curPos >= bytesRead )
                        {
                            bytesRead = fread(myBlock, 1, BLOCK_SIZE, fp);
                            if ( bytesRead <= 0 )
                                goto uscita;
                            curPos = 0;
                        }
                        c = myBlock[curPos];
                        while ( '\n' != c  && '\r' != c )
                        {
                            curPos++;
                            Offset++;
                            if ( curPos >= bytesRead )
                            {
                                bytesRead = fread(myBlock, 1, BLOCK_SIZE, fp);
                                if ( bytesRead <= 0 )
                                    goto uscita;
                                curPos = 0;
                            }
                            c = myBlock[curPos];
                        }
                        if ( '\r' == c )
                        {
                            curPos++;
                            Offset++;
                            if ( curPos >= bytesRead )
                            {
                                bytesRead = fread(myBlock, 1, BLOCK_SIZE, fp);
                                if ( bytesRead <= 0 )
                                    goto uscita;
                                curPos = 0;
                            }
                            c = myBlock[curPos];
                           
                            if ( c >= '1' && c <= '9' )
                            {
                                lexeme[k++] = c;
                                ObjOffset = Offset;
                                state = S_PP1;
                            }
                        }
                    }
                    else if ( c >= '1' && c <= '9' )
                    {
                        lexeme[k++] = c;
                        ObjOffset = Offset;
                        state = S_PP1;
                    }
                    break;
                case S_PP1:
                    if ( c >= '0' && c <= '9' )
                    {
                        lexeme[k++] = c;
                    }
                    else if ( IsDelimiterChar(c) )
                    {
                        lexeme[k] = '\0';
                        k = 0;
                        Number = atoi((char*)lexeme);
                        state = S_PP2;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP2:
                    if ( c >= '0' && c <= '9' )
                    {
                        lexeme[k++] = c;
                    }
                    else if ( k > 0 && IsDelimiterChar(c) )
                    {
                        lexeme[k] = '\0';
                        k = 0;
                        Generation = atoi((char*)lexeme);
                        state = S_PP3;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP3:
                    if ( 'o' == c )
                    {
                        state = S_PP4;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP4:
                    if ( 'b' == c )
                    {
                        state = S_PP5;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP5:
                    if ( 'j' == c )
                    {
                        state = S_PP6;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP6:
                    if ( IsDelimiterChar(c) )
                    {
                        wprintf(L"OBJECT(%d %d obj) FOUND AT OFFSET %lu\n", Number, Generation, ObjOffset);
                    }
                    state = S_PP0;
                    break;
                default:
                    break;
            }
           
            curPos++;
            Offset++;
        }
    }
   
    wprintf(L"\n");
       
uscita:

    if ( NULL != fp )
    {
        fclose(fp);
        fp = NULL;
    }
   
    return retValue;
}

Back to Top
Vincenzo1968 View Drop Down
New Member
New Member
Avatar

Joined: 01 Sep 2019
Location: Palermo - Italy
Points: 7
Post Options Post Options   Quote Vincenzo1968 Quote  Post ReplyReply Direct Link To This Post Posted: 02 Sep 2019 at 2:51pm
Typical output:
...
OBJECT(876 0 obj) FOUND AT OFFSET 8272697
OBJECT(877 0 obj) FOUND AT OFFSET 8273083
OBJECT(878 0 obj) FOUND AT OFFSET 8275969
OBJECT(879 0 obj) FOUND AT OFFSET 8282559
OBJECT(880 0 obj) FOUND AT OFFSET 8287341
OBJECT(881 0 obj) FOUND AT OFFSET 8287484
OBJECT(882 0 obj) FOUND AT OFFSET 8291247
OBJECT(883 0 obj) FOUND AT OFFSET 8291590
OBJECT(884 0 obj) FOUND AT OFFSET 8291859
...
Back to Top
Vincenzo1968 View Drop Down
New Member
New Member
Avatar

Joined: 01 Sep 2019
Location: Palermo - Italy
Points: 7
Post Options Post Options   Quote Vincenzo1968 Quote  Post ReplyReply Direct Link To This Post Posted: 02 Sep 2019 at 8:19pm
While I was there, I modified the function so that it also calculates, for each object it finds, the offset of the relative stream, if there is one:
typedef enum tagPreParseStates
{
    S_PPError,
    S_PP0,
    S_PP1,
    S_PP2,   
    S_PP3,
    S_PP4,
    S_PP5,
    S_PP6,
    S_PP7,
    S_PP8,
    S_PP9,
    S_PP10,
    S_PP11,
    S_PP12,
    S_PP13,
    S_PP14,
    S_PP15,
    S_PP16,
    S_PP17,
    S_PP18
} PreParseStates;

int getObjsOffsets(Params *pParams, char *pszFileName)
{
    int retValue = 1;
    FILE *fp = NULL;
    size_t bytesRead = 0;
    uint32_t curPos = 0;
    unsigned char myBlock[BLOCK_SIZE];
    unsigned char myPrevBlock[BLOCK_SIZE];
    unsigned char c;
    unsigned char lexeme[128];
    int k = 0;
   
    uint32_t Number = 0;
    uint32_t Generation = 0;
    uint32_t Offset = 0;
    uint32_t ObjOffset = 0;
    uint32_t ObjStreamOffset = 0;
    uint32_t ObjStreamLength = 0;
   
    uint32_t countObjs = 0;
    uint32_t maxObjNum = 0;
    uint32_t maxObjGenNum = 0;
       
    int bStreamState = 0;
   
    PreParseStates state = S_PP0;
   
    fp = fopen(pszFileName, "rb");
    if ( fp == NULL )
    {
        wprintf(L"ERROR PreParseFile: fopen failed for file '%s'.\n", pszFileName);
        fwprintf(pParams->fpErrors, L"ERROR PreParseFile: fopen failed for file '%s'.\n\n", pszFileName);
        retValue = 0;
        goto uscita;
    }
   
    wprintf(L"\n");
       
    while ( (bytesRead = fread(myBlock, 1, BLOCK_SIZE, fp)) )
    {
        curPos = 0;
       
        while ( curPos < bytesRead )
        {
            c = myBlock[curPos];
           
            if ( k >= 128 )
            {
                k = 0;
                state = S_PP0;
            }
           
            switch ( state )
            {
                case S_PP0:
                    k = 0;
                   
                    if ( '%' == c )
                    {
                        curPos++;
                        Offset++;
                        if ( curPos >= bytesRead )
                        {
                            bytesRead = fread(myBlock, 1, BLOCK_SIZE, fp);
                            if ( bytesRead <= 0 )
                                goto uscita;
                            curPos = 0;
                        }
                        c = myBlock[curPos];
                        while ( '\n' != c  && '\r' != c )
                        {
                            curPos++;
                            Offset++;
                            if ( curPos >= bytesRead )
                            {
                                bytesRead = fread(myBlock, 1, BLOCK_SIZE, fp);
                                if ( bytesRead <= 0 )
                                    goto uscita;
                                curPos = 0;
                            }
                            c = myBlock[curPos];
                        }
                        if ( '\r' == c )
                        {
                            curPos++;
                            Offset++;
                            if ( curPos >= bytesRead )
                            {
                                bytesRead = fread(myBlock, 1, BLOCK_SIZE, fp);
                                if ( bytesRead <= 0 )
                                    goto uscita;
                                curPos = 0;
                            }
                            c = myBlock[curPos];
                           
                            if ( c >= '1' && c <= '9' )
                            {
                                lexeme[k++] = c;
                                ObjOffset = Offset;
                                state = S_PP1;
                            }
                        }
                    }
                    else if ( c >= '1' && c <= '9' )
                    {
                        lexeme[k++] = c;
                        ObjOffset = Offset;
                        state = S_PP1;
                    }
                    else if ( !bStreamState && 's' == c )
                    {
                        state = S_PP7;
                    }
                    else if ( bStreamState && 'e' == c )
                    {
                        state = S_PP13;
                    }
                    break;
                case S_PP1:
                    if ( c >= '0' && c <= '9' )
                    {
                        lexeme[k++] = c;
                    }
                    else if ( IsDelimiterChar(c) )
                    {
                        lexeme[k] = '\0';
                        k = 0;
                        Number = atoi((char*)lexeme);
                        state = S_PP2;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP2:
                    if ( c >= '0' && c <= '9' )
                    {
                        lexeme[k++] = c;
                    }
                    else if ( k > 0 && IsDelimiterChar(c) )
                    {
                        lexeme[k] = '\0';
                        k = 0;
                        Generation = atoi((char*)lexeme);
                        state = S_PP3;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP3:
                    if ( 'o' == c )
                    {
                        state = S_PP4;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP4:
                    if ( 'b' == c )
                    {
                        state = S_PP5;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP5:
                    if ( 'j' == c )
                    {
                        state = S_PP6;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP6:
                    if ( IsDelimiterChar(c) )
                    {
                        countObjs++;
                        if ( maxObjNum < Number )
                            maxObjNum = Number;
                        if ( maxObjGenNum < Generation )
                            maxObjGenNum = Generation;
                        wprintf(L"OBJECT(%lu %lu obj) FOUND AT OFFSET %lu\n", Number, Generation, ObjOffset);
                    }
                    state = S_PP0;
                    break;
                case S_PP7:
                    if ( 't' == c )
                    {
                        state = S_PP8;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP8:
                    if ( 'r' == c )
                    {
                        state = S_PP9;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP9:
                    if ( 'e' == c )
                    {
                        state = S_PP10;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP10:
                    if ( 'a' == c )
                    {
                        state = S_PP11;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP11:
                    if ( 'm' == c )
                    {
                        state = S_PP12;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP12:
                    if ( IsDelimiterChar(c) )
                    {
                        if ( !bStreamState ) // stream
                        {
                            ObjStreamOffset = Offset + 1;
                            bStreamState = 1;
                           
                            if ( '\r' == c )
                            {
                                curPos++;
                                Offset++;
                                if ( curPos >= bytesRead )
                                {
                                    bytesRead = fread(myBlock, 1, BLOCK_SIZE, fp);
                                    if ( bytesRead <= 0 )
                                        goto uscita;
                                    curPos = 0;
                                }
                                c = myBlock[curPos];
                               
                                if ( '\n' == c )
                                {
                                    ObjStreamOffset++;
                                }
                            }
                           
                            wprintf(L"\tOBJECT STREAM FOUND AT OFFSET %lu\n", ObjStreamOffset);
                        }
                        else // endstream
                        {
                            int nTemp;
                           
                            ObjStreamLength = (Offset - ObjStreamOffset) - 9;
                            bStreamState = 0;
                           
                            nTemp = curPos - (9 + 2);
                            if ( nTemp >= 0 )
                            {
                                char c1, c2;
                                c1 = myBlock[nTemp];
                                c2 = myBlock[nTemp + 1];
                                if ( '\r' == c1 )
                                    ObjStreamLength -= 2;
                                else if ( '\n' == c2 )
                                    ObjStreamLength--;
                            }
                            else if ( -1 == nTemp )
                            {
                                char c1, c2;
                                c1 = myBlock[BLOCK_SIZE - 1];
                                c2 = myBlock[0];
                                if ( '\r' == c1 )
                                    ObjStreamLength -= 2;
                                else if ( '\n' == c2 )
                                    ObjStreamLength--;
                            }
                            else
                            {
                                char c1, c2;
                                c1 = myBlock[BLOCK_SIZE + nTemp];
                                nTemp++;
                                c2 = myBlock[BLOCK_SIZE + nTemp];
                                if ( '\r' == c1 )
                                    ObjStreamLength -= 2;
                                else if ( '\n' == c2 )
                                    ObjStreamLength--;
                            }
                           
                            wprintf(L"\t\tOBJECT STREAM LENGTH = %lu\n", ObjStreamLength);
                        }                       
                    }
                    state = S_PP0;
                    break;
                case S_PP13:
                    if ( 'n' == c )
                    {
                        state = S_PP14;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP14:
                    if ( 'd' == c )
                    {
                        state = S_PP15;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP15:
                    if ( 's' == c )
                    {
                        state = S_PP7;
                    }
                    else if ( 'o' == c )
                    {
                        state = S_PP16;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP16:
                    if ( 'b' == c )
                    {
                        state = S_PP17;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP17:
                    if ( 'j' == c )
                    {
                        state = S_PP18;
                    }
                    else
                    {
                        state = S_PP0;
                    }
                    break;
                case S_PP18:
                    if ( IsDelimiterChar(c) )
                    {
                        if ( bStreamState )
                        {
                            ObjStreamLength = (Offset - ObjStreamOffset) - 6;
                            bStreamState = 0;
                            wprintf(L"\t\tWARNING: STREAM NOT CLOSED BY 'endstream', but 'endobj' keyword.\n", ObjStreamLength);
                            wprintf(L"\t\tOBJECT STREAM LENGTH = %lu\n", ObjStreamLength);
                        }
                    }
                    state = S_PP0;
                    break;
                default:
                    break;
            }
           
            curPos++;
            Offset++;
        }
       
        memcpy(myPrevBlock, myBlock, bytesRead);   
    }
   
    wprintf(L"\n");
   
    wprintf(L"FOUND %lu OBJECTS.\n\tMAXIMUM OBJ NUMBER IS: %lu.\n\t\tMAXIMUM OBJ GENERATION NUMBER IS: %lu\n", countObjs, maxObjNum, maxObjGenNum);
   
    wprintf(L"\n");
       
uscita:

    if ( NULL != fp )
    {
        fclose(fp);
        fp = NULL;
    }
   
    return retValue;
}

Typical output:
OBJECT(1240 0 obj) FOUND AT OFFSET 16
OBJECT(1467 0 obj) FOUND AT OFFSET 116
    OBJECT STREAM FOUND AT OFFSET 367
        OBJECT STREAM LENGTH = 604
OBJECT(1567 0 obj) FOUND AT OFFSET 1356
    OBJECT STREAM FOUND AT OFFSET 1431
        OBJECT STREAM LENGTH = 2126
OBJECT(1241 0 obj) FOUND AT OFFSET 3576
OBJECT(1242 0 obj) FOUND AT OFFSET 3664
OBJECT(1243 0 obj) FOUND AT OFFSET 3943
    OBJECT STREAM FOUND AT OFFSET 4025
        OBJECT STREAM LENGTH = 7281
...
...
OBJECT(882 0 obj) FOUND AT OFFSET 8291247
    OBJECT STREAM FOUND AT OFFSET 8291324
        OBJECT STREAM LENGTH = 247
OBJECT(883 0 obj) FOUND AT OFFSET 8291590
    OBJECT STREAM FOUND AT OFFSET 8291666
        OBJECT STREAM LENGTH = 174
OBJECT(884 0 obj) FOUND AT OFFSET 8291859
    OBJECT STREAM FOUND AT OFFSET 8292081
        OBJECT STREAM LENGTH = 2421

FOUND 1113 OBJECTS.
    MAXIMUM OBJ NUMBER IS: 1567.
        MAXIMUM OBJ GENERATION NUMBER IS: 0

Back to Top
Vincenzo1968 View Drop Down
New Member
New Member
Avatar

Joined: 01 Sep 2019
Location: Palermo - Italy
Points: 7
Post Options Post Options   Quote Vincenzo1968 Quote  Post ReplyReply Direct Link To This Post Posted: 08 Sep 2019 at 3:09pm
For the sake of completeness also the "IsDelimiterChar" function code:

#define DELIM_FALSE           0
#define DELIM_SPACECHAR       1
#define DELIM_SPECIALSYMBOL   2

int IsDelimiterChar(unsigned char c)
{
    switch ( c )
    {
        case ' ':
        case '\r':
        case '\n':
        case '\f':
        case '\t':
        case '\0':
            return DELIM_SPACECHAR;
            break;
        case '%':
        case '(':
        case ')':
        case '<':
        case '>':
        case '[':
        case ']':
        case '{':
        case '}':
        case '/':
            return DELIM_SPECIALSYMBOL;
            break;             
        default:
            return DELIM_FALSE;
            break;
    }
     
    return DELIM_FALSE;
}

Back to Top
Vincenzo1968 View Drop Down
New Member
New Member
Avatar

Joined: 01 Sep 2019
Location: Palermo - Italy
Points: 7
Post Options Post Options   Quote Vincenzo1968 Quote  Post ReplyReply Direct Link To This Post Posted: 14 Sep 2019 at 2:57pm
Having received no response, I decided to implement it with the initial sequential reading of the file.
Now the program is able to read pdf files of any version, even greater than 1.4.
Any interested parties can download, for free (GNU GPL3 license), the program by following the instructions above, in the second post of this thread.
Back to Top
 Post Reply Post Reply
  Share Topic   

Forum Jump Forum Permissions View Drop Down

Forum Software by Web Wiz Forums® version 11.10
Copyright ©2001-2017 Web Wiz Ltd.

This page was generated in 0.059 seconds.