Home | History | Annotate | Download | only in fpdf_page
      1 // Copyright 2014 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "../../../include/fpdfapi/fpdf_page.h"
      8 #include "../../../include/fpdfapi/fpdf_module.h"
      9 #include "../../../include/fxcodec/fx_codec.h"
     10 #include "pageint.h"
     11 #include <limits.h>
     12 extern const FX_LPCSTR _PDF_OpCharType =
     13     "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII"
     14     "IIVIIIIVIIVIIIIIVVIIIIIIIIIIIIII"
     15     "IIVVVVVVIVVVVVVIVVVVVIIVVIIIIIII"
     16     "IIVVVVVVVVVVVVVVIVVVIIVVIVVIIIII"
     17     "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII"
     18     "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII"
     19     "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII"
     20     "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII";
     21 FX_BOOL _PDF_HasInvalidOpChar(FX_LPCSTR op)
     22 {
     23     if(!op) {
     24         return FALSE;
     25     }
     26     FX_BYTE ch;
     27     while((ch = *op++)) {
     28         if(_PDF_OpCharType[ch] == 'I') {
     29             return TRUE;
     30         }
     31     }
     32     return FALSE;
     33 }
     34 FX_DWORD CPDF_StreamContentParser::Parse(FX_LPCBYTE pData, FX_DWORD dwSize, FX_DWORD max_cost)
     35 {
     36     if (m_Level > _FPDF_MAX_FORM_LEVEL_) {
     37         return dwSize;
     38     }
     39     FX_DWORD InitObjCount = m_pObjectList->CountObjects();
     40     CPDF_StreamParser syntax(pData, dwSize);
     41     m_pSyntax = &syntax;
     42     m_CompatCount = 0;
     43     while (1) {
     44         FX_DWORD cost = m_pObjectList->CountObjects() - InitObjCount;
     45         if (max_cost && cost >= max_cost) {
     46             break;
     47         }
     48         switch (syntax.ParseNextElement()) {
     49             case CPDF_StreamParser::EndOfData:
     50                 return m_pSyntax->GetPos();
     51             case CPDF_StreamParser::Keyword:
     52                 if(!OnOperator((char*)syntax.GetWordBuf()) && _PDF_HasInvalidOpChar((char*)syntax.GetWordBuf())) {
     53                     m_bAbort = TRUE;
     54                 }
     55                 if (m_bAbort) {
     56                     return m_pSyntax->GetPos();
     57                 }
     58                 ClearAllParams();
     59                 break;
     60             case CPDF_StreamParser::Number:
     61                 AddNumberParam((char*)syntax.GetWordBuf(), syntax.GetWordSize());
     62                 break;
     63             case CPDF_StreamParser::Name:
     64                 AddNameParam((FX_LPCSTR)syntax.GetWordBuf() + 1, syntax.GetWordSize() - 1);
     65                 break;
     66             default:
     67                 AddObjectParam(syntax.GetObject());
     68         }
     69     }
     70     return m_pSyntax->GetPos();
     71 }
     72 void _PDF_ReplaceAbbr(CPDF_Object* pObj);
     73 void CPDF_StreamContentParser::Handle_BeginImage()
     74 {
     75     FX_FILESIZE savePos = m_pSyntax->GetPos();
     76     CPDF_Dictionary* pDict = CPDF_Dictionary::Create();
     77     while (1) {
     78         CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement();
     79         if (type == CPDF_StreamParser::Keyword) {
     80             CFX_ByteString bsKeyword(m_pSyntax->GetWordBuf(), m_pSyntax->GetWordSize());
     81             if (bsKeyword != FX_BSTRC("ID")) {
     82                 m_pSyntax->SetPos(savePos);
     83                 pDict->Release();
     84                 return;
     85             }
     86         }
     87         if (type != CPDF_StreamParser::Name) {
     88             break;
     89         }
     90         CFX_ByteString key((FX_LPCSTR)m_pSyntax->GetWordBuf() + 1, m_pSyntax->GetWordSize() - 1);
     91         CPDF_Object* pObj = m_pSyntax->ReadNextObject();
     92         if (!key.IsEmpty()) {
     93             pDict->SetAt(key, pObj, m_pDocument);
     94         } else {
     95             pObj->Release();
     96         }
     97     }
     98     _PDF_ReplaceAbbr(pDict);
     99     CPDF_Object* pCSObj = NULL;
    100     if (pDict->KeyExist(FX_BSTRC("ColorSpace"))) {
    101         pCSObj = pDict->GetElementValue(FX_BSTRC("ColorSpace"));
    102         if (pCSObj->GetType() == PDFOBJ_NAME) {
    103             CFX_ByteString name = pCSObj->GetString();
    104             if (name != FX_BSTRC("DeviceRGB") && name != FX_BSTRC("DeviceGray") && name != FX_BSTRC("DeviceCMYK")) {
    105                 pCSObj = FindResourceObj(FX_BSTRC("ColorSpace"), name);
    106                 if (pCSObj && !pCSObj->GetObjNum()) {
    107                     pCSObj = pCSObj->Clone();
    108                     pDict->SetAt(FX_BSTRC("ColorSpace"), pCSObj, m_pDocument);
    109                 }
    110             }
    111         }
    112     }
    113     CPDF_Stream* pStream = m_pSyntax->ReadInlineStream(m_pDocument, pDict, pCSObj, m_Options.m_bDecodeInlineImage);
    114     while (1) {
    115         CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement();
    116         if (type == CPDF_StreamParser::EndOfData) {
    117             break;
    118         }
    119         if (type != CPDF_StreamParser::Keyword) {
    120             continue;
    121         }
    122         if (m_pSyntax->GetWordSize() == 2 && m_pSyntax->GetWordBuf()[0] == 'E' &&
    123                 m_pSyntax->GetWordBuf()[1] == 'I') {
    124             break;
    125         }
    126     }
    127     if (m_Options.m_bTextOnly) {
    128         if (pStream) {
    129             pStream->Release();
    130         } else {
    131             pDict->Release();
    132         }
    133         return;
    134     }
    135     pDict->SetAtName(FX_BSTRC("Subtype"), FX_BSTRC("Image"));
    136     CPDF_ImageObject *pImgObj = AddImage(pStream, NULL, TRUE);
    137     if (!pImgObj) {
    138         if (pStream) {
    139             pStream->Release();
    140         } else {
    141             pDict->Release();
    142         }
    143     }
    144 }
    145 void CPDF_StreamContentParser::ParsePathObject()
    146 {
    147     FX_FLOAT params[6] = {0};
    148     int nParams = 0;
    149     int last_pos = m_pSyntax->GetPos();
    150     while (1) {
    151         CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement();
    152         FX_BOOL bProcessed = TRUE;
    153         switch (type) {
    154             case CPDF_StreamParser::EndOfData:
    155                 return;
    156             case CPDF_StreamParser::Keyword: {
    157                     int len = m_pSyntax->GetWordSize();
    158                     if (len == 1) {
    159                         switch (m_pSyntax->GetWordBuf()[0]) {
    160                             case 'm':
    161                                 AddPathPoint(params[0], params[1], FXPT_MOVETO);
    162                                 nParams = 0;
    163                                 break;
    164                             case 'l':
    165                                 AddPathPoint(params[0], params[1], FXPT_LINETO);
    166                                 nParams = 0;
    167                                 break;
    168                             case 'c':
    169                                 AddPathPoint(params[0], params[1], FXPT_BEZIERTO);
    170                                 AddPathPoint(params[2], params[3], FXPT_BEZIERTO);
    171                                 AddPathPoint(params[4], params[5], FXPT_BEZIERTO);
    172                                 nParams = 0;
    173                                 break;
    174                             case 'v':
    175                                 AddPathPoint(m_PathCurrentX, m_PathCurrentY, FXPT_BEZIERTO);
    176                                 AddPathPoint(params[0], params[1], FXPT_BEZIERTO);
    177                                 AddPathPoint(params[2], params[3], FXPT_BEZIERTO);
    178                                 nParams = 0;
    179                                 break;
    180                             case 'y':
    181                                 AddPathPoint(params[0], params[1], FXPT_BEZIERTO);
    182                                 AddPathPoint(params[2], params[3], FXPT_BEZIERTO);
    183                                 AddPathPoint(params[2], params[3], FXPT_BEZIERTO);
    184                                 nParams = 0;
    185                                 break;
    186                             case 'h':
    187                                 Handle_ClosePath();
    188                                 nParams = 0;
    189                                 break;
    190                             default:
    191                                 bProcessed = FALSE;
    192                                 break;
    193                         }
    194                     } else if (len == 2) {
    195                         if (m_pSyntax->GetWordBuf()[0] == 'r' && m_pSyntax->GetWordBuf()[1] == 'e') {
    196                             AddPathRect(params[0], params[1], params[2], params[3]);
    197                             nParams = 0;
    198                         } else {
    199                             bProcessed = FALSE;
    200                         }
    201                     } else {
    202                         bProcessed = FALSE;
    203                     }
    204                     if (bProcessed) {
    205                         last_pos = m_pSyntax->GetPos();
    206                     }
    207                     break;
    208                 }
    209             case CPDF_StreamParser::Number: {
    210                     if (nParams == 6) {
    211                         break;
    212                     }
    213                     FX_BOOL bInteger;
    214                     int value;
    215                     FX_atonum(CFX_ByteStringC(m_pSyntax->GetWordBuf(), m_pSyntax->GetWordSize()), bInteger, &value);
    216                     params[nParams++] = bInteger ? (FX_FLOAT)value : *(FX_FLOAT*)&value;
    217                     break;
    218                 }
    219             default:
    220                 bProcessed = FALSE;
    221         }
    222         if (!bProcessed) {
    223             m_pSyntax->SetPos(last_pos);
    224             return;
    225         }
    226     }
    227 }
    228 CPDF_StreamParser::CPDF_StreamParser(const FX_BYTE* pData, FX_DWORD dwSize)
    229 {
    230     m_pBuf = pData;
    231     m_Size = dwSize;
    232     m_Pos = 0;
    233     m_pLastObj = NULL;
    234 }
    235 CPDF_StreamParser::~CPDF_StreamParser()
    236 {
    237     if (m_pLastObj) {
    238         m_pLastObj->Release();
    239     }
    240 }
    241 FX_DWORD _DecodeAllScanlines(ICodec_ScanlineDecoder* pDecoder, FX_LPBYTE& dest_buf, FX_DWORD& dest_size)
    242 {
    243     if (pDecoder == NULL) {
    244         return (FX_DWORD) - 1;
    245     }
    246     int ncomps = pDecoder->CountComps();
    247     int bpc = pDecoder->GetBPC();
    248     int width = pDecoder->GetWidth();
    249     int height = pDecoder->GetHeight();
    250     int pitch = (width * ncomps * bpc + 7) / 8;
    251     if (height == 0 || pitch > (1 << 30) / height) {
    252         delete pDecoder;
    253         return -1;
    254     }
    255     dest_size = pitch * height;
    256     dest_buf = FX_Alloc( FX_BYTE, dest_size);
    257     for (int row = 0; row < height; row ++) {
    258         FX_LPBYTE pLine = pDecoder->GetScanline(row);
    259         if (pLine == NULL) {
    260             break;
    261         }
    262         FXSYS_memcpy32(dest_buf + row * pitch, pLine, pitch);
    263     }
    264     FX_DWORD srcoff = pDecoder->GetSrcOffset();
    265     delete pDecoder;
    266     return srcoff;
    267 }
    268 ICodec_ScanlineDecoder* FPDFAPI_CreateFaxDecoder(FX_LPCBYTE src_buf, FX_DWORD src_size, int width, int height,
    269         const CPDF_Dictionary* pParams);
    270 FX_DWORD _A85Decode(const FX_BYTE* src_buf, FX_DWORD src_size, FX_LPBYTE& dest_buf, FX_DWORD& dest_size);
    271 FX_DWORD _HexDecode(const FX_BYTE* src_buf, FX_DWORD src_size, FX_LPBYTE& dest_buf, FX_DWORD& dest_size);
    272 FX_DWORD FPDFAPI_FlateOrLZWDecode(FX_BOOL bLZW, const FX_BYTE* src_buf, FX_DWORD src_size, CPDF_Dictionary* pParams,
    273                                   FX_DWORD estimated_size, FX_LPBYTE& dest_buf, FX_DWORD& dest_size);
    274 FX_DWORD PDF_DecodeInlineStream(const FX_BYTE* src_buf, FX_DWORD limit,
    275                                 int width, int height, CFX_ByteString& decoder,
    276                                 CPDF_Dictionary* pParam, FX_LPBYTE& dest_buf, FX_DWORD& dest_size)
    277 {
    278     if (decoder == FX_BSTRC("CCITTFaxDecode") || decoder == FX_BSTRC("CCF")) {
    279         ICodec_ScanlineDecoder* pDecoder = FPDFAPI_CreateFaxDecoder(src_buf, limit, width, height, pParam);
    280         return _DecodeAllScanlines(pDecoder, dest_buf, dest_size);
    281     } else if (decoder == FX_BSTRC("ASCII85Decode") || decoder == FX_BSTRC("A85")) {
    282         return _A85Decode(src_buf, limit, dest_buf, dest_size);
    283     } else if (decoder == FX_BSTRC("ASCIIHexDecode") || decoder == FX_BSTRC("AHx")) {
    284         return _HexDecode(src_buf, limit, dest_buf, dest_size);
    285     } else if (decoder == FX_BSTRC("FlateDecode") || decoder == FX_BSTRC("Fl")) {
    286         return FPDFAPI_FlateOrLZWDecode(FALSE, src_buf, limit, pParam, dest_size, dest_buf, dest_size);
    287     } else if (decoder == FX_BSTRC("LZWDecode") || decoder == FX_BSTRC("LZW")) {
    288         return FPDFAPI_FlateOrLZWDecode(TRUE, src_buf, limit, pParam, 0, dest_buf, dest_size);
    289     } else if (decoder == FX_BSTRC("DCTDecode") || decoder == FX_BSTRC("DCT")) {
    290         ICodec_ScanlineDecoder* pDecoder = CPDF_ModuleMgr::Get()->GetJpegModule()->CreateDecoder(
    291                                                src_buf, limit, width, height, 0, pParam ? pParam->GetInteger(FX_BSTRC("ColorTransform"), 1) : 1);
    292         return _DecodeAllScanlines(pDecoder, dest_buf, dest_size);
    293     } else if (decoder == FX_BSTRC("RunLengthDecode") || decoder == FX_BSTRC("RL")) {
    294         return RunLengthDecode(src_buf, limit, dest_buf, dest_size);
    295     }
    296     dest_size = 0;
    297     dest_buf = 0;
    298     return (FX_DWORD) - 1;
    299 }
    300 extern const FX_LPCSTR _PDF_CharType;
    301 CPDF_Stream* CPDF_StreamParser::ReadInlineStream(CPDF_Document* pDoc, CPDF_Dictionary* pDict, CPDF_Object* pCSObj, FX_BOOL bDecode)
    302 {
    303     if (m_Pos == m_Size) {
    304         return NULL;
    305     }
    306     if (_PDF_CharType[m_pBuf[m_Pos]] == 'W') {
    307         m_Pos ++;
    308     }
    309     CFX_ByteString Decoder;
    310     CPDF_Dictionary* pParam = NULL;
    311     CPDF_Object* pFilter = pDict->GetElementValue(FX_BSTRC("Filter"));
    312     if (pFilter == NULL) {
    313     } else if (pFilter->GetType() == PDFOBJ_ARRAY) {
    314         Decoder = ((CPDF_Array*)pFilter)->GetString(0);
    315         CPDF_Array* pParams = pDict->GetArray(FX_BSTRC("DecodeParms"));
    316         if (pParams) {
    317             pParam = pParams->GetDict(0);
    318         }
    319     } else {
    320         Decoder = pFilter->GetString();
    321         pParam = pDict->GetDict(FX_BSTRC("DecodeParms"));
    322     }
    323     FX_DWORD width = pDict->GetInteger(FX_BSTRC("Width"));
    324     FX_DWORD height = pDict->GetInteger(FX_BSTRC("Height"));
    325     FX_DWORD OrigSize = 0;
    326     if (pCSObj != NULL) {
    327         FX_DWORD bpc = pDict->GetInteger(FX_BSTRC("BitsPerComponent"));
    328         FX_DWORD nComponents = 1;
    329         CPDF_ColorSpace* pCS = pDoc->LoadColorSpace(pCSObj);
    330         if (pCS == NULL) {
    331             nComponents = 3;
    332         } else {
    333             nComponents = pCS->CountComponents();
    334             pDoc->GetPageData()->ReleaseColorSpace(pCSObj);
    335         }
    336         FX_DWORD pitch = width;
    337         if (bpc && pitch > INT_MAX / bpc) {
    338             return NULL;
    339         }
    340         pitch *= bpc;
    341         if (nComponents && pitch > INT_MAX / nComponents) {
    342             return NULL;
    343         }
    344         pitch *= nComponents;
    345         if (pitch > INT_MAX - 7) {
    346             return NULL;
    347         }
    348         pitch += 7;
    349         pitch /= 8;
    350         OrigSize = pitch;
    351     } else {
    352         if (width > INT_MAX - 7) {
    353             return NULL;
    354         }
    355         OrigSize = ((width + 7) / 8);
    356     }
    357     if (height && OrigSize > INT_MAX / height) {
    358         return NULL;
    359     }
    360     OrigSize *= height;
    361     FX_LPBYTE pData = NULL;
    362     FX_DWORD dwStreamSize;
    363     if (Decoder.IsEmpty()) {
    364         if (OrigSize > m_Size - m_Pos) {
    365             OrigSize = m_Size - m_Pos;
    366         }
    367         pData = FX_Alloc(FX_BYTE, OrigSize);
    368         FXSYS_memcpy32(pData, m_pBuf + m_Pos, OrigSize);
    369         dwStreamSize = OrigSize;
    370         m_Pos += OrigSize;
    371     } else {
    372         FX_DWORD dwDestSize = OrigSize;
    373         dwStreamSize = PDF_DecodeInlineStream(m_pBuf + m_Pos, m_Size - m_Pos, width, height, Decoder, pParam,
    374                                               pData, dwDestSize);
    375         if ((int)dwStreamSize < 0) {
    376             return NULL;
    377         }
    378         if (bDecode) {
    379             m_Pos += dwStreamSize;
    380             dwStreamSize = dwDestSize;
    381             if (pFilter->GetType() == PDFOBJ_ARRAY) {
    382                 ((CPDF_Array*)pFilter)->RemoveAt(0);
    383                 CPDF_Array* pParams = pDict->GetArray(FX_BSTRC("DecodeParms"));
    384                 if (pParams) {
    385                     pParams->RemoveAt(0);
    386                 }
    387             } else {
    388                 pDict->RemoveAt(FX_BSTRC("Filter"));
    389                 pDict->RemoveAt(FX_BSTRC("DecodeParms"));
    390             }
    391         } else {
    392             if (pData) {
    393                 FX_Free(pData);
    394             }
    395             FX_DWORD dwSavePos = m_Pos;
    396             m_Pos += dwStreamSize;
    397             while (1) {
    398                 FX_DWORD dwPrevPos = m_Pos;
    399                 CPDF_StreamParser::SyntaxType type = ParseNextElement();
    400                 if (type == CPDF_StreamParser::EndOfData) {
    401                     break;
    402                 }
    403                 if (type != CPDF_StreamParser::Keyword) {
    404                     dwStreamSize += m_Pos - dwPrevPos;
    405                     continue;
    406                 }
    407                 if (GetWordSize() == 2 && GetWordBuf()[0] == 'E' &&
    408                         GetWordBuf()[1] == 'I') {
    409                     m_Pos = dwPrevPos;
    410                     break;
    411                 }
    412                 dwStreamSize += m_Pos - dwPrevPos;
    413             }
    414             m_Pos = dwSavePos;
    415             pData = FX_Alloc(FX_BYTE, dwStreamSize);
    416             FXSYS_memcpy32(pData, m_pBuf + m_Pos, dwStreamSize);
    417             m_Pos += dwStreamSize;
    418         }
    419     }
    420     pDict->SetAtInteger(FX_BSTRC("Length"), (int)dwStreamSize);
    421     return CPDF_Stream::Create(pData, dwStreamSize, pDict);
    422 }
    423 #define MAX_WORD_BUFFER 256
    424 #define MAX_STRING_LENGTH	32767
    425 #define FXDWORD_TRUE FXDWORD_FROM_LSBFIRST(0x65757274)
    426 #define FXDWORD_NULL FXDWORD_FROM_LSBFIRST(0x6c6c756e)
    427 #define FXDWORD_FALS FXDWORD_FROM_LSBFIRST(0x736c6166)
    428 CPDF_StreamParser::SyntaxType CPDF_StreamParser::ParseNextElement()
    429 {
    430     if (m_pLastObj) {
    431         m_pLastObj->Release();
    432         m_pLastObj = NULL;
    433     }
    434     m_WordSize = 0;
    435     FX_BOOL bIsNumber = TRUE;
    436     if (m_Pos >= m_Size) {
    437         return EndOfData;
    438     }
    439     int ch = m_pBuf[m_Pos++];
    440     int type = _PDF_CharType[ch];
    441     while (1) {
    442         while (type == 'W') {
    443             if (m_Size <= m_Pos) {
    444                 return EndOfData;
    445             }
    446             ch = m_pBuf[m_Pos++];
    447             type = _PDF_CharType[ch];
    448         }
    449         if (ch != '%') {
    450             break;
    451         }
    452         while (1) {
    453             if (m_Size <= m_Pos) {
    454                 return EndOfData;
    455             }
    456             ch = m_pBuf[m_Pos++];
    457             if (ch == '\r' || ch == '\n') {
    458                 break;
    459             }
    460         }
    461         type = _PDF_CharType[ch];
    462     }
    463     if (type == 'D' && ch != '/') {
    464         m_Pos --;
    465         m_pLastObj = ReadNextObject();
    466         return Others;
    467     }
    468     while (1) {
    469         if (m_WordSize < MAX_WORD_BUFFER) {
    470             m_WordBuffer[m_WordSize++] = ch;
    471         }
    472         if (type != 'N') {
    473             bIsNumber = FALSE;
    474         }
    475         if (m_Size <= m_Pos) {
    476             break;
    477         }
    478         ch = m_pBuf[m_Pos++];
    479         type = _PDF_CharType[ch];
    480         if (type == 'D' || type == 'W') {
    481             m_Pos --;
    482             break;
    483         }
    484     }
    485     m_WordBuffer[m_WordSize] = 0;
    486     if (bIsNumber) {
    487         return Number;
    488     }
    489     if (m_WordBuffer[0] == '/') {
    490         return Name;
    491     }
    492     if (m_WordSize == 4) {
    493         if (*(FX_DWORD*)m_WordBuffer == FXDWORD_TRUE) {
    494             m_pLastObj = CPDF_Boolean::Create(TRUE);
    495             return Others;
    496         }
    497         if (*(FX_DWORD*)m_WordBuffer == FXDWORD_NULL) {
    498             m_pLastObj = CPDF_Null::Create();
    499             return Others;
    500         }
    501     } else if (m_WordSize == 5) {
    502         if (*(FX_DWORD*)m_WordBuffer == FXDWORD_FALS && m_WordBuffer[4] == 'e') {
    503             m_pLastObj = CPDF_Boolean::Create(FALSE);
    504             return Others;
    505         }
    506     }
    507     return Keyword;
    508 }
    509 void CPDF_StreamParser::SkipPathObject()
    510 {
    511     FX_DWORD command_startpos = m_Pos;
    512     if (m_Pos >= m_Size) {
    513         return;
    514     }
    515     int ch = m_pBuf[m_Pos++];
    516     int type = _PDF_CharType[ch];
    517     while (1) {
    518         while (type == 'W') {
    519             if (m_Pos >= m_Size) {
    520                 return;
    521             }
    522             ch = m_pBuf[m_Pos++];
    523             type = _PDF_CharType[ch];
    524         }
    525         if (type != 'N') {
    526             m_Pos = command_startpos;
    527             return;
    528         }
    529         while (1) {
    530             while (type != 'W') {
    531                 if (m_Pos >= m_Size) {
    532                     return;
    533                 }
    534                 ch = m_pBuf[m_Pos++];
    535                 type = _PDF_CharType[ch];
    536             }
    537             while (type == 'W') {
    538                 if (m_Pos >= m_Size) {
    539                     return;
    540                 }
    541                 ch = m_pBuf[m_Pos++];
    542                 type = _PDF_CharType[ch];
    543             }
    544             if (type == 'N') {
    545                 continue;
    546             }
    547             FX_DWORD op_startpos = m_Pos - 1;
    548             while (type != 'W' && type != 'D') {
    549                 if (m_Pos >= m_Size) {
    550                     return;
    551                 }
    552                 ch = m_pBuf[m_Pos++];
    553                 type = _PDF_CharType[ch];
    554             }
    555             if (m_Pos - op_startpos == 2) {
    556                 int op = m_pBuf[op_startpos];
    557                 if (op == 'm' || op == 'l' || op == 'c' || op == 'v' || op == 'y') {
    558                     command_startpos = m_Pos;
    559                     break;
    560                 }
    561             } else if (m_Pos - op_startpos == 3) {
    562                 if (m_pBuf[op_startpos] == 'r' && m_pBuf[op_startpos + 1] == 'e') {
    563                     command_startpos = m_Pos;
    564                     break;
    565                 }
    566             }
    567             m_Pos = command_startpos;
    568             return;
    569         }
    570     }
    571 }
    572 CPDF_Object* CPDF_StreamParser::ReadNextObject(FX_BOOL bAllowNestedArray, FX_BOOL bInArray)
    573 {
    574     FX_BOOL bIsNumber;
    575     GetNextWord(bIsNumber);
    576     if (m_WordSize == 0) {
    577         return NULL;
    578     }
    579     if (bIsNumber) {
    580         m_WordBuffer[m_WordSize] = 0;
    581         return CPDF_Number::Create(CFX_ByteStringC(m_WordBuffer, m_WordSize));
    582     }
    583     int first_char = m_WordBuffer[0];
    584     if (first_char == '/') {
    585         return CPDF_Name::Create(PDF_NameDecode(CFX_ByteStringC(m_WordBuffer + 1, m_WordSize - 1)));
    586     }
    587     if (first_char == '(') {
    588         return CPDF_String::Create(ReadString());
    589     }
    590     if (first_char == '<') {
    591         if (m_WordSize == 1) {
    592             return CPDF_String::Create(ReadHexString(), TRUE);
    593         }
    594         CPDF_Dictionary* pDict = CPDF_Dictionary::Create();
    595         while (1) {
    596             GetNextWord(bIsNumber);
    597             if (m_WordSize == 0) {
    598                 pDict->Release();
    599                 return NULL;
    600             }
    601             if (m_WordSize == 2 && m_WordBuffer[0] == '>') {
    602                 break;
    603             }
    604             if (m_WordBuffer[0] != '/') {
    605                 pDict->Release();
    606                 return NULL;
    607             }
    608             CFX_ByteString key = PDF_NameDecode(CFX_ByteStringC(m_WordBuffer + 1, m_WordSize - 1));
    609             CPDF_Object* pObj = ReadNextObject(TRUE);
    610             if (pObj == NULL) {
    611                 if (pDict) {
    612                     pDict->Release();
    613                 }
    614                 return NULL;
    615             }
    616             if (!key.IsEmpty()) {
    617                 pDict->SetAt(key, pObj);
    618             } else {
    619                 pObj->Release();
    620             }
    621         }
    622         return pDict;
    623     }
    624     if (first_char == '[') {
    625         if (!bAllowNestedArray && bInArray) {
    626             return NULL;
    627         }
    628         CPDF_Array* pArray = CPDF_Array::Create();
    629         while (1) {
    630             CPDF_Object* pObj = ReadNextObject(bAllowNestedArray, TRUE);
    631             if (pObj == NULL) {
    632                 if (m_WordSize == 0 || m_WordBuffer[0] == ']') {
    633                     return pArray;
    634                 }
    635                 if (m_WordBuffer[0] == '[') {
    636                     continue;
    637                 }
    638             } else {
    639                 pArray->Add(pObj);
    640             }
    641         }
    642     }
    643     if (m_WordSize == 4) {
    644         if (*(FX_DWORD*)m_WordBuffer == FXDWORD_TRUE) {
    645             return CPDF_Boolean::Create(TRUE);
    646         }
    647         if (*(FX_DWORD*)m_WordBuffer == FXDWORD_NULL) {
    648             return CPDF_Null::Create();
    649         }
    650     } else if (m_WordSize == 5) {
    651         if (*(FX_DWORD*)m_WordBuffer == FXDWORD_FALS && m_WordBuffer[4] == 'e') {
    652             return CPDF_Boolean::Create(FALSE);
    653         }
    654     }
    655     return NULL;
    656 }
    657 void CPDF_StreamParser::GetNextWord(FX_BOOL& bIsNumber)
    658 {
    659     m_WordSize = 0;
    660     bIsNumber = TRUE;
    661     if (m_Size <= m_Pos) {
    662         return;
    663     }
    664     int ch = m_pBuf[m_Pos++];
    665     int type = _PDF_CharType[ch];
    666     while (1) {
    667         while (type == 'W') {
    668             if (m_Size <= m_Pos) {
    669                 return;
    670             }
    671             ch = m_pBuf[m_Pos++];
    672             type = _PDF_CharType[ch];
    673         }
    674         if (ch != '%') {
    675             break;
    676         }
    677         while (1) {
    678             if (m_Size <= m_Pos) {
    679                 return;
    680             }
    681             ch = m_pBuf[m_Pos++];
    682             if (ch == '\r' || ch == '\n') {
    683                 break;
    684             }
    685         }
    686         type = _PDF_CharType[ch];
    687     }
    688     if (type == 'D') {
    689         bIsNumber = FALSE;
    690         m_WordBuffer[m_WordSize++] = ch;
    691         if (ch == '/') {
    692             while (1) {
    693                 if (m_Size <= m_Pos) {
    694                     return;
    695                 }
    696                 ch = m_pBuf[m_Pos++];
    697                 type = _PDF_CharType[ch];
    698                 if (type != 'R' && type != 'N') {
    699                     m_Pos --;
    700                     return;
    701                 }
    702                 if (m_WordSize < MAX_WORD_BUFFER) {
    703                     m_WordBuffer[m_WordSize++] = ch;
    704                 }
    705             }
    706         } else if (ch == '<') {
    707             if (m_Size <= m_Pos) {
    708                 return;
    709             }
    710             ch = m_pBuf[m_Pos++];
    711             if (ch == '<') {
    712                 m_WordBuffer[m_WordSize++] = ch;
    713             } else {
    714                 m_Pos --;
    715             }
    716         } else if (ch == '>') {
    717             if (m_Size <= m_Pos) {
    718                 return;
    719             }
    720             ch = m_pBuf[m_Pos++];
    721             if (ch == '>') {
    722                 m_WordBuffer[m_WordSize++] = ch;
    723             } else {
    724                 m_Pos --;
    725             }
    726         }
    727         return;
    728     }
    729     while (1) {
    730         if (m_WordSize < MAX_WORD_BUFFER) {
    731             m_WordBuffer[m_WordSize++] = ch;
    732         }
    733         if (type != 'N') {
    734             bIsNumber = FALSE;
    735         }
    736         if (m_Size <= m_Pos) {
    737             return;
    738         }
    739         ch = m_pBuf[m_Pos++];
    740         type = _PDF_CharType[ch];
    741         if (type == 'D' || type == 'W') {
    742             m_Pos --;
    743             break;
    744         }
    745     }
    746 }
    747 CFX_ByteString CPDF_StreamParser::ReadString()
    748 {
    749     if (m_Size <= m_Pos) {
    750         return CFX_ByteString();
    751     }
    752     int ch = m_pBuf[m_Pos++];
    753     CFX_ByteTextBuf buf;
    754     int parlevel = 0;
    755     int status = 0, iEscCode = 0;
    756     while (1) {
    757         switch (status) {
    758             case 0:
    759                 if (ch == ')') {
    760                     if (parlevel == 0) {
    761                         if (buf.GetLength() > MAX_STRING_LENGTH) {
    762                             return CFX_ByteString(buf.GetBuffer(), MAX_STRING_LENGTH);
    763                         }
    764                         return buf.GetByteString();
    765                     }
    766                     parlevel --;
    767                     buf.AppendChar(')');
    768                 } else if (ch == '(') {
    769                     parlevel ++;
    770                     buf.AppendChar('(');
    771                 } else if (ch == '\\') {
    772                     status = 1;
    773                 } else {
    774                     buf.AppendChar((char)ch);
    775                 }
    776                 break;
    777             case 1:
    778                 if (ch >= '0' && ch <= '7') {
    779                     iEscCode = ch - '0';
    780                     status = 2;
    781                     break;
    782                 }
    783                 if (ch == 'n') {
    784                     buf.AppendChar('\n');
    785                 } else if (ch == 'r') {
    786                     buf.AppendChar('\r');
    787                 } else if (ch == 't') {
    788                     buf.AppendChar('\t');
    789                 } else if (ch == 'b') {
    790                     buf.AppendChar('\b');
    791                 } else if (ch == 'f') {
    792                     buf.AppendChar('\f');
    793                 } else if (ch == '\r') {
    794                     status = 4;
    795                     break;
    796                 } else if (ch == '\n') {
    797                 } else {
    798                     buf.AppendChar(ch);
    799                 }
    800                 status = 0;
    801                 break;
    802             case 2:
    803                 if (ch >= '0' && ch <= '7') {
    804                     iEscCode = iEscCode * 8 + ch - '0';
    805                     status = 3;
    806                 } else {
    807                     buf.AppendChar(iEscCode);
    808                     status = 0;
    809                     continue;
    810                 }
    811                 break;
    812             case 3:
    813                 if (ch >= '0' && ch <= '7') {
    814                     iEscCode = iEscCode * 8 + ch - '0';
    815                     buf.AppendChar(iEscCode);
    816                     status = 0;
    817                 } else {
    818                     buf.AppendChar(iEscCode);
    819                     status = 0;
    820                     continue;
    821                 }
    822                 break;
    823             case 4:
    824                 status = 0;
    825                 if (ch != '\n') {
    826                     continue;
    827                 }
    828                 break;
    829         }
    830         if (m_Size <= m_Pos) {
    831             break;
    832         }
    833         ch = m_pBuf[m_Pos++];
    834     }
    835     if (m_Size > m_Pos) {
    836         ch = m_pBuf[m_Pos++];
    837     }
    838     if (buf.GetLength() > MAX_STRING_LENGTH) {
    839         return CFX_ByteString(buf.GetBuffer(), MAX_STRING_LENGTH);
    840     }
    841     return buf.GetByteString();
    842 }
    843 CFX_ByteString CPDF_StreamParser::ReadHexString()
    844 {
    845     if (m_Size <= m_Pos) {
    846         return CFX_ByteString();
    847     }
    848     int ch = m_pBuf[m_Pos++];
    849     CFX_ByteTextBuf buf;
    850     FX_BOOL bFirst = TRUE;
    851     int code = 0;
    852     while (1) {
    853         if (ch == '>') {
    854             break;
    855         }
    856         if (ch >= '0' && ch <= '9') {
    857             if (bFirst) {
    858                 code = (ch - '0') * 16;
    859             } else {
    860                 code += ch - '0';
    861                 buf.AppendChar((char)code);
    862             }
    863             bFirst = !bFirst;
    864         } else if (ch >= 'A' && ch <= 'F') {
    865             if (bFirst) {
    866                 code = (ch - 'A' + 10) * 16;
    867             } else {
    868                 code += ch - 'A' + 10;
    869                 buf.AppendChar((char)code);
    870             }
    871             bFirst = !bFirst;
    872         } else if (ch >= 'a' && ch <= 'f') {
    873             if (bFirst) {
    874                 code = (ch - 'a' + 10) * 16;
    875             } else {
    876                 code += ch - 'a' + 10;
    877                 buf.AppendChar((char)code);
    878             }
    879             bFirst = !bFirst;
    880         }
    881         if (m_Size <= m_Pos) {
    882             break;
    883         }
    884         ch = m_pBuf[m_Pos++];
    885     }
    886     if (!bFirst) {
    887         buf.AppendChar((char)code);
    888     }
    889     if (buf.GetLength() > MAX_STRING_LENGTH) {
    890         return CFX_ByteString(buf.GetBuffer(), MAX_STRING_LENGTH);
    891     }
    892     return buf.GetByteString();
    893 }
    894 #define PAGEPARSE_STAGE_GETCONTENT		1
    895 #define PAGEPARSE_STAGE_PARSE			2
    896 #define PAGEPARSE_STAGE_CHECKCLIP		3
    897 CPDF_ContentParser::CPDF_ContentParser()
    898 {
    899     m_pParser = NULL;
    900     m_pStreamArray = NULL;
    901     m_pSingleStream = NULL;
    902     m_pData = NULL;
    903     m_Status = Ready;
    904     m_pType3Char = NULL;
    905 }
    906 CPDF_ContentParser::~CPDF_ContentParser()
    907 {
    908     Clear();
    909 }
    910 void CPDF_ContentParser::Clear()
    911 {
    912     if (m_pParser) {
    913         delete m_pParser;
    914     }
    915     if (m_pSingleStream) {
    916         delete m_pSingleStream;
    917     }
    918     if (m_pStreamArray) {
    919         for (FX_DWORD i = 0; i < m_nStreams; i ++)
    920             if (m_pStreamArray[i]) {
    921                 delete m_pStreamArray[i];
    922             }
    923         FX_Free(m_pStreamArray);
    924     }
    925     if (m_pData && m_pSingleStream == NULL) {
    926         FX_Free((void*)m_pData);
    927     }
    928     m_pParser = NULL;
    929     m_pStreamArray = NULL;
    930     m_pSingleStream = NULL;
    931     m_pData = NULL;
    932     m_Status = Ready;
    933 }
    934 void CPDF_ContentParser::Start(CPDF_Page* pPage, CPDF_ParseOptions* pOptions)
    935 {
    936     if (m_Status != Ready || pPage == NULL || pPage->m_pDocument == NULL || pPage->m_pFormDict == NULL) {
    937         m_Status = Done;
    938         return;
    939     }
    940     m_pObjects = pPage;
    941     m_bForm = FALSE;
    942     if (pOptions) {
    943         m_Options = *pOptions;
    944     }
    945     m_Status = ToBeContinued;
    946     m_InternalStage = PAGEPARSE_STAGE_GETCONTENT;
    947     m_CurrentOffset = 0;
    948     CPDF_Object* pContent = pPage->m_pFormDict->GetElementValue(FX_BSTRC("Contents"));
    949     if (pContent == NULL) {
    950         m_Status = Done;
    951         return;
    952     }
    953     if (pContent->GetType() == PDFOBJ_STREAM) {
    954         m_nStreams = 0;
    955         m_pSingleStream = FX_NEW CPDF_StreamAcc;
    956         m_pSingleStream->LoadAllData((CPDF_Stream*)pContent, FALSE);
    957     } else if (pContent->GetType() == PDFOBJ_ARRAY) {
    958         CPDF_Array* pArray = (CPDF_Array*)pContent;
    959         m_nStreams = pArray->GetCount();
    960         if (m_nStreams == 0) {
    961             m_Status = Done;
    962             return;
    963         }
    964         m_pStreamArray = FX_Alloc(CPDF_StreamAcc*, m_nStreams);
    965         FXSYS_memset32(m_pStreamArray, 0, sizeof(CPDF_StreamAcc*) * m_nStreams);
    966     } else {
    967         m_Status = Done;
    968         return;
    969     }
    970 }
    971 void CPDF_ContentParser::Start(CPDF_Form* pForm, CPDF_AllStates* pGraphicStates,
    972                                CFX_AffineMatrix* pParentMatrix, CPDF_Type3Char* pType3Char, CPDF_ParseOptions* pOptions, int level)
    973 {
    974     m_pType3Char = pType3Char;
    975     m_pObjects = pForm;
    976     m_bForm = TRUE;
    977     CFX_AffineMatrix form_matrix = pForm->m_pFormDict->GetMatrix(FX_BSTRC("Matrix"));
    978     if (pGraphicStates) {
    979         form_matrix.Concat(pGraphicStates->m_CTM);
    980     }
    981     CPDF_Array* pBBox = pForm->m_pFormDict->GetArray(FX_BSTRC("BBox"));
    982     CFX_FloatRect form_bbox;
    983     CPDF_Path ClipPath;
    984     if (pBBox) {
    985         form_bbox = pBBox->GetRect();
    986         ClipPath.New();
    987         ClipPath.AppendRect(form_bbox.left, form_bbox.bottom, form_bbox.right, form_bbox.top);
    988         ClipPath.Transform(&form_matrix);
    989         if (pParentMatrix) {
    990             ClipPath.Transform(pParentMatrix);
    991         }
    992         form_bbox.Transform(&form_matrix);
    993         if (pParentMatrix) {
    994             form_bbox.Transform(pParentMatrix);
    995         }
    996     }
    997     CPDF_Dictionary* pResources = pForm->m_pFormDict->GetDict(FX_BSTRC("Resources"));
    998     m_pParser = FX_NEW CPDF_StreamContentParser;
    999     m_pParser->Initialize();
   1000     m_pParser->PrepareParse(pForm->m_pDocument, pForm->m_pPageResources, pForm->m_pResources, pParentMatrix, pForm,
   1001                             pResources, &form_bbox, pOptions, pGraphicStates, level);
   1002     m_pParser->m_pCurStates->m_CTM = form_matrix;
   1003     m_pParser->m_pCurStates->m_ParentMatrix = form_matrix;
   1004     if (ClipPath.NotNull()) {
   1005         m_pParser->m_pCurStates->m_ClipPath.AppendPath(ClipPath, FXFILL_WINDING, TRUE);
   1006     }
   1007     if (pForm->m_Transparency & PDFTRANS_GROUP) {
   1008         CPDF_GeneralStateData* pData = m_pParser->m_pCurStates->m_GeneralState.GetModify();
   1009         pData->m_BlendType = FXDIB_BLEND_NORMAL;
   1010         pData->m_StrokeAlpha = 1.0f;
   1011         pData->m_FillAlpha = 1.0f;
   1012         pData->m_pSoftMask = NULL;
   1013     }
   1014     m_nStreams = 0;
   1015     m_pSingleStream = FX_NEW CPDF_StreamAcc;
   1016     if (pForm->m_pDocument) {
   1017         m_pSingleStream->LoadAllData(pForm->m_pFormStream, FALSE);
   1018     } else {
   1019         m_pSingleStream->LoadAllData(pForm->m_pFormStream, FALSE);
   1020     }
   1021     m_pData = (FX_LPBYTE)m_pSingleStream->GetData();
   1022     m_Size = m_pSingleStream->GetSize();
   1023     m_Status = ToBeContinued;
   1024     m_InternalStage = PAGEPARSE_STAGE_PARSE;
   1025     m_CurrentOffset = 0;
   1026 }
   1027 void CPDF_ContentParser::Continue(IFX_Pause* pPause)
   1028 {
   1029     int steps = 0;
   1030     while (m_Status == ToBeContinued) {
   1031         if (m_InternalStage == PAGEPARSE_STAGE_GETCONTENT) {
   1032             if (m_CurrentOffset == m_nStreams) {
   1033                 if (m_pStreamArray) {
   1034                     m_Size = 0;
   1035                     FX_DWORD i;
   1036                     for (i = 0; i < m_nStreams; i ++) {
   1037                         FX_DWORD size = m_pStreamArray[i]->GetSize();
   1038                         if (m_Size + size + 1 <= m_Size) {
   1039 							m_Status = Done;
   1040 							return;
   1041                         }
   1042                         m_Size += size + 1;
   1043                     }
   1044                     m_pData = FX_Alloc(FX_BYTE, m_Size);
   1045                     if (!m_pData) {
   1046                         m_Status = Done;
   1047                         return;
   1048                     }
   1049                     FX_DWORD pos = 0;
   1050                     for (i = 0; i < m_nStreams; i ++) {
   1051                         FXSYS_memcpy32(m_pData + pos, m_pStreamArray[i]->GetData(), m_pStreamArray[i]->GetSize());
   1052                         pos += m_pStreamArray[i]->GetSize() + 1;
   1053                         m_pData[pos - 1] = ' ';
   1054                         delete m_pStreamArray[i];
   1055                     }
   1056                     FX_Free(m_pStreamArray);
   1057                     m_pStreamArray = NULL;
   1058                 } else {
   1059                     m_pData = (FX_LPBYTE)m_pSingleStream->GetData();
   1060                     m_Size = m_pSingleStream->GetSize();
   1061                 }
   1062                 m_InternalStage = PAGEPARSE_STAGE_PARSE;
   1063                 m_CurrentOffset = 0;
   1064             } else {
   1065                 CPDF_Array* pContent = m_pObjects->m_pFormDict->GetArray(FX_BSTRC("Contents"));
   1066                 m_pStreamArray[m_CurrentOffset] = FX_NEW CPDF_StreamAcc;
   1067                 CPDF_Stream* pStreamObj = (CPDF_Stream*)pContent->GetElementValue(m_CurrentOffset);
   1068                 m_pStreamArray[m_CurrentOffset]->LoadAllData(pStreamObj, FALSE);
   1069                 m_CurrentOffset ++;
   1070             }
   1071         }
   1072         if (m_InternalStage == PAGEPARSE_STAGE_PARSE) {
   1073             if (m_pParser == NULL) {
   1074                 m_pParser = FX_NEW CPDF_StreamContentParser;
   1075                 m_pParser->Initialize();
   1076                 m_pParser->PrepareParse(m_pObjects->m_pDocument, m_pObjects->m_pPageResources, NULL, NULL, m_pObjects,
   1077                                         m_pObjects->m_pResources, &m_pObjects->m_BBox, &m_Options, NULL, 0);
   1078                 m_pParser->m_pCurStates->m_ColorState.GetModify()->Default();
   1079             }
   1080             if (m_CurrentOffset >= m_Size) {
   1081                 m_InternalStage = PAGEPARSE_STAGE_CHECKCLIP;
   1082             } else {
   1083                 m_CurrentOffset += m_pParser->Parse(m_pData + m_CurrentOffset, m_Size - m_CurrentOffset, PARSE_STEP_LIMIT);
   1084                 if (m_pParser->m_bAbort) {
   1085                     m_InternalStage = PAGEPARSE_STAGE_CHECKCLIP;
   1086                     continue;
   1087                 }
   1088             }
   1089         }
   1090         if (m_InternalStage == PAGEPARSE_STAGE_CHECKCLIP) {
   1091             if (m_pType3Char) {
   1092                 m_pType3Char->m_bColored = m_pParser->m_bColored;
   1093                 m_pType3Char->m_Width = FXSYS_round(m_pParser->m_Type3Data[0] * 1000);
   1094                 m_pType3Char->m_BBox.left = FXSYS_round(m_pParser->m_Type3Data[2] * 1000);
   1095                 m_pType3Char->m_BBox.bottom = FXSYS_round(m_pParser->m_Type3Data[3] * 1000);
   1096                 m_pType3Char->m_BBox.right = FXSYS_round(m_pParser->m_Type3Data[4] * 1000);
   1097                 m_pType3Char->m_BBox.top = FXSYS_round(m_pParser->m_Type3Data[5] * 1000);
   1098             }
   1099             FX_POSITION pos = m_pObjects->m_ObjectList.GetHeadPosition();
   1100             while (pos) {
   1101                 CPDF_PageObject* pObj = (CPDF_PageObject*)m_pObjects->m_ObjectList.GetNext(pos);
   1102                 if (pObj->m_ClipPath.IsNull()) {
   1103                     continue;
   1104                 }
   1105                 if (pObj->m_ClipPath.GetPathCount() != 1) {
   1106                     continue;
   1107                 }
   1108                 if (pObj->m_ClipPath.GetTextCount()) {
   1109                     continue;
   1110                 }
   1111                 CPDF_Path ClipPath = pObj->m_ClipPath.GetPath(0);
   1112                 if (!ClipPath.IsRect() || pObj->m_Type == PDFPAGE_SHADING) {
   1113                     continue;
   1114                 }
   1115                 CFX_FloatRect old_rect(ClipPath.GetPointX(0), ClipPath.GetPointY(0),
   1116                                        ClipPath.GetPointX(2), ClipPath.GetPointY(2));
   1117                 CFX_FloatRect obj_rect(pObj->m_Left, pObj->m_Bottom, pObj->m_Right, pObj->m_Top);
   1118                 if (old_rect.Contains(obj_rect)) {
   1119                     pObj->m_ClipPath.SetNull();
   1120                 }
   1121             }
   1122             m_Status = Done;
   1123             return;
   1124         }
   1125         steps ++;
   1126         if (pPause && pPause->NeedToPauseNow()) {
   1127             break;
   1128         }
   1129     }
   1130 }
   1131 int CPDF_ContentParser::EstimateProgress()
   1132 {
   1133     if (m_Status == Ready) {
   1134         return 0;
   1135     }
   1136     if (m_Status == Done) {
   1137         return 100;
   1138     }
   1139     if (m_InternalStage == PAGEPARSE_STAGE_GETCONTENT) {
   1140         return 10;
   1141     }
   1142     if (m_InternalStage == PAGEPARSE_STAGE_CHECKCLIP) {
   1143         return 90;
   1144     }
   1145     return 10 + 80 * m_CurrentOffset / m_Size;
   1146 }
   1147