Cleanup some numeric code.
[pdfium.git] / core / src / fpdfapi / fpdf_parser / fpdf_parser_utility.cpp
index cc3d29c..d7c4136 100644 (file)
-// Copyright 2014 PDFium Authors. All rights reserved.\r
-// Use of this source code is governed by a BSD-style license that can be\r
-// found in the LICENSE file.\r
\r
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
-\r
-#include "../../../include/fpdfapi/fpdf_parser.h"\r
-extern const FX_LPCSTR _PDF_CharType =\r
-    "WRRRRRRRRWWRWWRRRRRRRRRRRRRRRRRR"\r
-    "WRRRRDRRDDRNRNNDNNNNNNNNNNRRDRDR"\r
-    "RRRRRRRRRRRRRRRRRRRRRRRRRRRDRDRR"\r
-    "RRRRRRRRRRRRRRRRRRRRRRRRRRRDRDRR"\r
-    "WRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"\r
-    "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"\r
-    "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"\r
-    "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRW";\r
-#ifndef MAX_PATH\r
-#define MAX_PATH 4096\r
-#endif\r
-CPDF_SimpleParser::CPDF_SimpleParser(FX_LPCBYTE pData, FX_DWORD dwSize)\r
-{\r
-    m_pData = pData;\r
-    m_dwSize = dwSize;\r
-    m_dwCurPos = 0;\r
-}\r
-CPDF_SimpleParser::CPDF_SimpleParser(FX_BSTR str)\r
-{\r
-    m_pData = str;\r
-    m_dwSize = str.GetLength();\r
-    m_dwCurPos = 0;\r
-}\r
-void CPDF_SimpleParser::ParseWord(FX_LPCBYTE& pStart, FX_DWORD& dwSize, int& type)\r
-{\r
-    pStart = NULL;\r
-    dwSize = 0;\r
-    type = PDFWORD_EOF;\r
-    FX_BYTE ch;\r
-    char chartype;\r
-    while (1) {\r
-        if (m_dwSize <= m_dwCurPos) {\r
-            return;\r
-        }\r
-        ch = m_pData[m_dwCurPos++];\r
-        chartype = _PDF_CharType[ch];\r
-        while (chartype == 'W') {\r
-            if (m_dwSize <= m_dwCurPos) {\r
-                return;\r
-            }\r
-            ch = m_pData[m_dwCurPos++];\r
-            chartype = _PDF_CharType[ch];\r
-        }\r
-        if (ch != '%') {\r
-            break;\r
-        }\r
-        while (1) {\r
-            if (m_dwSize <= m_dwCurPos) {\r
-                return;\r
-            }\r
-            ch = m_pData[m_dwCurPos++];\r
-            if (ch == '\r' || ch == '\n') {\r
-                break;\r
-            }\r
-        }\r
-        chartype = _PDF_CharType[ch];\r
-    }\r
-    FX_DWORD start_pos = m_dwCurPos - 1;\r
-    pStart = m_pData + start_pos;\r
-    if (chartype == 'D') {\r
-        if (ch == '/') {\r
-            while (1) {\r
-                if (m_dwSize <= m_dwCurPos) {\r
-                    return;\r
-                }\r
-                ch = m_pData[m_dwCurPos++];\r
-                chartype = _PDF_CharType[ch];\r
-                if (chartype != 'R' && chartype != 'N') {\r
-                    m_dwCurPos --;\r
-                    dwSize = m_dwCurPos - start_pos;\r
-                    type = PDFWORD_NAME;\r
-                    return;\r
-                }\r
-            }\r
-        } else {\r
-            type = PDFWORD_DELIMITER;\r
-            dwSize = 1;\r
-            if (ch == '<') {\r
-                if (m_dwSize <= m_dwCurPos) {\r
-                    return;\r
-                }\r
-                ch = m_pData[m_dwCurPos++];\r
-                if (ch == '<') {\r
-                    dwSize = 2;\r
-                } else {\r
-                    m_dwCurPos --;\r
-                }\r
-            } else if (ch == '>') {\r
-                if (m_dwSize <= m_dwCurPos) {\r
-                    return;\r
-                }\r
-                ch = m_pData[m_dwCurPos++];\r
-                if (ch == '>') {\r
-                    dwSize = 2;\r
-                } else {\r
-                    m_dwCurPos --;\r
-                }\r
-            }\r
-        }\r
-        return;\r
-    }\r
-    type = PDFWORD_NUMBER;\r
-    dwSize = 1;\r
-    while (1) {\r
-        if (chartype != 'N') {\r
-            type = PDFWORD_TEXT;\r
-        }\r
-        if (m_dwSize <= m_dwCurPos) {\r
-            return;\r
-        }\r
-        ch = m_pData[m_dwCurPos++];\r
-        chartype = _PDF_CharType[ch];\r
-        if (chartype == 'D' || chartype == 'W') {\r
-            m_dwCurPos --;\r
-            break;\r
-        }\r
-        dwSize ++;\r
-    }\r
-}\r
-CFX_ByteStringC CPDF_SimpleParser::GetWord()\r
-{\r
-    FX_LPCBYTE pStart;\r
-    FX_DWORD dwSize;\r
-    int type;\r
-    ParseWord(pStart, dwSize, type);\r
-    if (dwSize == 1 && pStart[0] == '<') {\r
-        while (m_dwCurPos < m_dwSize && m_pData[m_dwCurPos] != '>') {\r
-            m_dwCurPos ++;\r
-        }\r
-        if (m_dwCurPos < m_dwSize) {\r
-            m_dwCurPos ++;\r
-        }\r
-        return CFX_ByteStringC(pStart, (FX_STRSIZE)(m_dwCurPos - (pStart - m_pData)));\r
-    } else if (dwSize == 1 && pStart[0] == '(') {\r
-        int level = 1;\r
-        while (m_dwCurPos < m_dwSize) {\r
-            if (m_pData[m_dwCurPos] == ')') {\r
-                level --;\r
-                if (level == 0) {\r
-                    break;\r
-                }\r
-            }\r
-            if (m_pData[m_dwCurPos] == '\\') {\r
-                if (m_dwSize <= m_dwCurPos) {\r
-                    break;\r
-                }\r
-                m_dwCurPos ++;\r
-            } else if (m_pData[m_dwCurPos] == '(') {\r
-                level ++;\r
-            }\r
-            if (m_dwSize <= m_dwCurPos) {\r
-                break;\r
-            }\r
-            m_dwCurPos ++;\r
-        }\r
-        if (m_dwCurPos < m_dwSize) {\r
-            m_dwCurPos ++;\r
-        }\r
-        return CFX_ByteStringC(pStart, (FX_STRSIZE)(m_dwCurPos - (pStart - m_pData)));\r
-    }\r
-    return CFX_ByteStringC(pStart, dwSize);\r
-}\r
-FX_BOOL CPDF_SimpleParser::SearchToken(FX_BSTR token)\r
-{\r
-    int token_len = token.GetLength();\r
-    while (m_dwCurPos < m_dwSize - token_len) {\r
-        if (FXSYS_memcmp32(m_pData + m_dwCurPos, token, token_len) == 0) {\r
-            break;\r
-        }\r
-        m_dwCurPos ++;\r
-    }\r
-    if (m_dwCurPos == m_dwSize - token_len) {\r
-        return FALSE;\r
-    }\r
-    m_dwCurPos += token_len;\r
-    return TRUE;\r
-}\r
-FX_BOOL CPDF_SimpleParser::SkipWord(FX_BSTR token)\r
-{\r
-    while (1) {\r
-        CFX_ByteStringC word = GetWord();\r
-        if (word.IsEmpty()) {\r
-            return FALSE;\r
-        }\r
-        if (word == token) {\r
-            return TRUE;\r
-        }\r
-    }\r
-    return FALSE;\r
-}\r
-FX_BOOL CPDF_SimpleParser::FindTagPair(FX_BSTR start_token, FX_BSTR end_token,\r
-                                       FX_DWORD& start_pos, FX_DWORD& end_pos)\r
-{\r
-    if (!start_token.IsEmpty()) {\r
-        if (!SkipWord(start_token)) {\r
-            return FALSE;\r
-        }\r
-        start_pos = m_dwCurPos;\r
-    }\r
-    while (1) {\r
-        end_pos = m_dwCurPos;\r
-        CFX_ByteStringC word = GetWord();\r
-        if (word.IsEmpty()) {\r
-            return FALSE;\r
-        }\r
-        if (word == end_token) {\r
-            return TRUE;\r
-        }\r
-    }\r
-    return FALSE;\r
-}\r
-FX_BOOL CPDF_SimpleParser::FindTagParam(FX_BSTR token, int nParams)\r
-{\r
-    nParams ++;\r
-    FX_DWORD* pBuf = FX_Alloc(FX_DWORD, nParams);\r
-    int buf_index = 0;\r
-    int buf_count = 0;\r
-    while (1) {\r
-        pBuf[buf_index++] = m_dwCurPos;\r
-        if (buf_index == nParams) {\r
-            buf_index = 0;\r
-        }\r
-        buf_count ++;\r
-        if (buf_count > nParams) {\r
-            buf_count = nParams;\r
-        }\r
-        CFX_ByteStringC word = GetWord();\r
-        if (word.IsEmpty()) {\r
-            FX_Free(pBuf);\r
-            return FALSE;\r
-        }\r
-        if (word == token) {\r
-            if (buf_count < nParams) {\r
-                continue;\r
-            }\r
-            m_dwCurPos = pBuf[buf_index];\r
-            FX_Free(pBuf);\r
-            return TRUE;\r
-        }\r
-    }\r
-    return FALSE;\r
-}\r
-static int _hex2dec(char ch)\r
-{\r
-    if (ch >= '0' && ch <= '9') {\r
-        return ch - '0';\r
-    }\r
-    if (ch >= 'a' && ch <= 'f') {\r
-        return ch - 'a' + 10;\r
-    }\r
-    if (ch >= 'A' && ch <= 'F') {\r
-        return ch - 'A' + 10;\r
-    }\r
-    return 0;\r
-}\r
-CFX_ByteString PDF_NameDecode(FX_BSTR bstr)\r
-{\r
-    int size = bstr.GetLength();\r
-    FX_LPCSTR pSrc = bstr.GetCStr();\r
-    if (FXSYS_memchr(pSrc, '#', size) == NULL) {\r
-        return bstr;\r
-    }\r
-    CFX_ByteString result;\r
-    FX_LPSTR pDestStart = result.GetBuffer(size);\r
-    FX_LPSTR pDest = pDestStart;\r
-    for (int i = 0; i < size; i ++) {\r
-        if (pSrc[i] == '#' && i < size - 2) {\r
-            *pDest ++ = _hex2dec(pSrc[i + 1]) * 16 + _hex2dec(pSrc[i + 2]);\r
-            i += 2;\r
-        } else {\r
-            *pDest ++ = pSrc[i];\r
-        }\r
-    }\r
-    result.ReleaseBuffer((FX_STRSIZE)(pDest - pDestStart));\r
-    return result;\r
-}\r
-CFX_ByteString PDF_NameDecode(const CFX_ByteString& orig)\r
-{\r
-    if (FXSYS_memchr((FX_LPCSTR)orig, '#', orig.GetLength()) == NULL) {\r
-        return orig;\r
-    }\r
-    return PDF_NameDecode(CFX_ByteStringC(orig));\r
-}\r
-CFX_ByteString PDF_NameEncode(const CFX_ByteString& orig)\r
-{\r
-    FX_LPBYTE src_buf = (FX_LPBYTE)(FX_LPCSTR)orig;\r
-    int src_len = orig.GetLength();\r
-    int dest_len = 0;\r
-    int i;\r
-    for (i = 0; i < src_len; i ++) {\r
-        FX_BYTE ch = src_buf[i];\r
-        if (ch >= 0x80 || _PDF_CharType[ch] == 'W' || ch == '#' ||\r
-                _PDF_CharType[ch] == 'D') {\r
-            dest_len += 3;\r
-        } else {\r
-            dest_len ++;\r
-        }\r
-    }\r
-    if (dest_len == src_len) {\r
-        return orig;\r
-    }\r
-    CFX_ByteString res;\r
-    FX_LPSTR dest_buf = res.GetBuffer(dest_len);\r
-    dest_len = 0;\r
-    for (i = 0; i < src_len; i ++) {\r
-        FX_BYTE ch = src_buf[i];\r
-        if (ch >= 0x80 || _PDF_CharType[ch] == 'W' || ch == '#' ||\r
-                _PDF_CharType[ch] == 'D') {\r
-            dest_buf[dest_len++] = '#';\r
-            dest_buf[dest_len++] = "0123456789ABCDEF"[ch / 16];\r
-            dest_buf[dest_len++] = "0123456789ABCDEF"[ch % 16];\r
-        } else {\r
-            dest_buf[dest_len++] = ch;\r
-        }\r
-    }\r
-    dest_buf[dest_len] = 0;\r
-    res.ReleaseBuffer();\r
-    return res;\r
-}\r
-CFX_ByteTextBuf& operator << (CFX_ByteTextBuf& buf, const CPDF_Object* pObj)\r
-{\r
-    if (pObj == NULL) {\r
-        buf << FX_BSTRC(" null");\r
-        return buf;\r
-    }\r
-    switch (pObj->GetType()) {\r
-        case PDFOBJ_NULL:\r
-            buf << FX_BSTRC(" null");\r
-            break;\r
-        case PDFOBJ_BOOLEAN:\r
-        case PDFOBJ_NUMBER:\r
-            buf << " " << pObj->GetString();\r
-            break;\r
-        case PDFOBJ_STRING: {\r
-                CFX_ByteString str = pObj->GetString();\r
-                FX_BOOL bHex = ((CPDF_String*)pObj)->IsHex();\r
-                buf << PDF_EncodeString(str, bHex);\r
-                break;\r
-            }\r
-        case PDFOBJ_NAME: {\r
-                CFX_ByteString str = pObj->GetString();\r
-                buf << FX_BSTRC("/") << PDF_NameEncode(str);\r
-                break;\r
-            }\r
-        case PDFOBJ_REFERENCE: {\r
-                CPDF_Reference* p = (CPDF_Reference*)pObj;\r
-                buf << " " << p->GetRefObjNum() << FX_BSTRC(" 0 R ");\r
-                break;\r
-            }\r
-        case PDFOBJ_ARRAY: {\r
-                CPDF_Array* p = (CPDF_Array*)pObj;\r
-                buf << FX_BSTRC("[");\r
-                for (FX_DWORD i = 0; i < p->GetCount(); i ++) {\r
-                    CPDF_Object* pElement = p->GetElement(i);\r
-                    if (pElement->GetObjNum()) {\r
-                        buf << " " << pElement->GetObjNum() << FX_BSTRC(" 0 R");\r
-                    } else {\r
-                        buf << pElement;\r
-                    }\r
-                }\r
-                buf << FX_BSTRC("]");\r
-                break;\r
-            }\r
-        case PDFOBJ_DICTIONARY: {\r
-                CPDF_Dictionary* p = (CPDF_Dictionary*)pObj;\r
-                buf << FX_BSTRC("<<");\r
-                FX_POSITION pos = p->GetStartPos();\r
-                while (pos) {\r
-                    CFX_ByteString key;\r
-                    CPDF_Object* pValue = p->GetNextElement(pos, key);\r
-                    buf << FX_BSTRC("/") << PDF_NameEncode(key);\r
-                    if (pValue->GetObjNum()) {\r
-                        buf << " " << pValue->GetObjNum() << FX_BSTRC(" 0 R ");\r
-                    } else {\r
-                        buf << pValue;\r
-                    }\r
-                }\r
-                buf << FX_BSTRC(">>");\r
-                break;\r
-            }\r
-        case PDFOBJ_STREAM: {\r
-                CPDF_Stream* p = (CPDF_Stream*)pObj;\r
-                buf << p->GetDict() << FX_BSTRC("stream\r\n");\r
-                CPDF_StreamAcc acc;\r
-                acc.LoadAllData(p, TRUE);\r
-                buf.AppendBlock(acc.GetData(), acc.GetSize());\r
-                buf << FX_BSTRC("\r\nendstream");\r
-                break;\r
-            }\r
-        default:\r
-            ASSERT(FALSE);\r
-            break;\r
-    }\r
-    return buf;\r
-}\r
-FX_FLOAT PDF_ClipFloat(FX_FLOAT f)\r
-{\r
-    if (f < 0) {\r
-        return 0;\r
-    }\r
-    if (f > 1.0f) {\r
-        return 1.0f;\r
-    }\r
-    return f;\r
-}\r
-static CPDF_Object* SearchNumberNode(CPDF_Dictionary* pNode, int num)\r
-{\r
-    CPDF_Array* pLimits = pNode->GetArray("Limits");\r
-    if (pLimits && (num < pLimits->GetInteger(0) || num > pLimits->GetInteger(1))) {\r
-        return NULL;\r
-    }\r
-    CPDF_Array* pNumbers = pNode->GetArray("Nums");\r
-    if (pNumbers) {\r
-        FX_DWORD dwCount = pNumbers->GetCount() / 2;\r
-        for (FX_DWORD i = 0; i < dwCount; i ++) {\r
-            int index = pNumbers->GetInteger(i * 2);\r
-            if (num == index) {\r
-                return pNumbers->GetElementValue(i * 2 + 1);\r
-            }\r
-            if (index > num) {\r
-                break;\r
-            }\r
-        }\r
-        return NULL;\r
-    }\r
-    CPDF_Array* pKids = pNode->GetArray("Kids");\r
-    if (pKids == NULL) {\r
-        return NULL;\r
-    }\r
-    for (FX_DWORD i = 0; i < pKids->GetCount(); i ++) {\r
-        CPDF_Dictionary* pKid = pKids->GetDict(i);\r
-        if (pKid == NULL) {\r
-            continue;\r
-        }\r
-        CPDF_Object* pFound = SearchNumberNode(pKid, num);\r
-        if (pFound) {\r
-            return pFound;\r
-        }\r
-    }\r
-    return NULL;\r
-}\r
-CPDF_Object* CPDF_NumberTree::LookupValue(int num)\r
-{\r
-    return SearchNumberNode(m_pRoot, num);\r
-}\r
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "../../../include/fpdfapi/fpdf_parser.h"
+#include "../../../include/fxcrt/fx_ext.h"
+
+// Indexed by 8-bit character code, contains either:
+//   'W' - for whitespace: NUL, TAB, CR, LF, FF, 0x80, 0xff
+//   'N' - for numeric: 0123456789+-.
+//   'D' - for delimiter: %()/<>[]{}
+//   'R' - otherwise.
+const char PDF_CharType[256] = {
+    // NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL  BS   HT   LF   VT   FF   CR   SO
+    // SI
+    'W', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'W', 'W', 'R', 'W', 'W', 'R',
+    'R',
+
+    // DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB  CAN  EM   SUB  ESC  FS   GS   RS
+    // US
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R',
+
+    // SP    !    "    #    $    %    &    ยด    (    )    *    +    ,    -    .
+    // /
+    'W', 'R', 'R', 'R', 'R', 'D', 'R', 'R', 'D', 'D', 'R', 'N', 'R', 'N', 'N',
+    'D',
+
+    // 0    1    2    3    4    5    6    7    8    9    :    ;    <    =    > ?
+    'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'R', 'R', 'D', 'R', 'D',
+    'R',
+
+    // @    A    B    C    D    E    F    G    H    I    J    K    L    M    N O
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R',
+
+    // P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^ _
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'D', 'R', 'D', 'R',
+    'R',
+
+    // `    a    b    c    d    e    f    g    h    i    j    k    l    m    n o
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R',
+
+    // p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
+    // DEL
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'D', 'R', 'D', 'R',
+    'R',
+
+    'W', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
+    'R', 'R', 'R', 'R', 'R', 'R', 'R', 'W'};
+
+#ifndef MAX_PATH
+#define MAX_PATH 4096
+#endif
+CPDF_SimpleParser::CPDF_SimpleParser(const uint8_t* pData, FX_DWORD dwSize) {
+  m_pData = pData;
+  m_dwSize = dwSize;
+  m_dwCurPos = 0;
+}
+CPDF_SimpleParser::CPDF_SimpleParser(const CFX_ByteStringC& str) {
+  m_pData = str.GetPtr();
+  m_dwSize = str.GetLength();
+  m_dwCurPos = 0;
+}
+void CPDF_SimpleParser::ParseWord(const uint8_t*& pStart,
+                                  FX_DWORD& dwSize,
+                                  int& type) {
+  pStart = NULL;
+  dwSize = 0;
+  type = PDFWORD_EOF;
+  uint8_t ch;
+  while (1) {
+    if (m_dwSize <= m_dwCurPos)
+      return;
+    ch = m_pData[m_dwCurPos++];
+    while (PDFCharIsWhitespace(ch)) {
+      if (m_dwSize <= m_dwCurPos)
+        return;
+      ch = m_pData[m_dwCurPos++];
+    }
+
+    if (ch != '%')
+      break;
+
+    while (1) {
+      if (m_dwSize <= m_dwCurPos)
+        return;
+      ch = m_pData[m_dwCurPos++];
+      if (ch == '\r' || ch == '\n')
+        break;
+    }
+  }
+
+  FX_DWORD start_pos = m_dwCurPos - 1;
+  pStart = m_pData + start_pos;
+  if (PDFCharIsDelimiter(ch)) {
+    if (ch == '/') {
+      while (1) {
+        if (m_dwSize <= m_dwCurPos)
+          return;
+        ch = m_pData[m_dwCurPos++];
+        if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) {
+          m_dwCurPos--;
+          dwSize = m_dwCurPos - start_pos;
+          type = PDFWORD_NAME;
+          return;
+        }
+      }
+    } else {
+      type = PDFWORD_DELIMITER;
+      dwSize = 1;
+      if (ch == '<') {
+        if (m_dwSize <= m_dwCurPos)
+          return;
+        ch = m_pData[m_dwCurPos++];
+        if (ch == '<')
+          dwSize = 2;
+        else
+          m_dwCurPos--;
+      } else if (ch == '>') {
+        if (m_dwSize <= m_dwCurPos)
+          return;
+        ch = m_pData[m_dwCurPos++];
+        if (ch == '>')
+          dwSize = 2;
+        else
+          m_dwCurPos--;
+      }
+    }
+    return;
+  }
+
+  type = PDFWORD_NUMBER;
+  dwSize = 1;
+  while (1) {
+    if (!PDFCharIsNumeric(ch))
+      type = PDFWORD_TEXT;
+    if (m_dwSize <= m_dwCurPos)
+      return;
+    ch = m_pData[m_dwCurPos++];
+
+    if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) {
+      m_dwCurPos--;
+      break;
+    }
+    dwSize++;
+  }
+}
+CFX_ByteStringC CPDF_SimpleParser::GetWord() {
+  const uint8_t* pStart;
+  FX_DWORD dwSize;
+  int type;
+  ParseWord(pStart, dwSize, type);
+  if (dwSize == 1 && pStart[0] == '<') {
+    while (m_dwCurPos < m_dwSize && m_pData[m_dwCurPos] != '>') {
+      m_dwCurPos++;
+    }
+    if (m_dwCurPos < m_dwSize) {
+      m_dwCurPos++;
+    }
+    return CFX_ByteStringC(pStart,
+                           (FX_STRSIZE)(m_dwCurPos - (pStart - m_pData)));
+  }
+  if (dwSize == 1 && pStart[0] == '(') {
+    int level = 1;
+    while (m_dwCurPos < m_dwSize) {
+      if (m_pData[m_dwCurPos] == ')') {
+        level--;
+        if (level == 0) {
+          break;
+        }
+      }
+      if (m_pData[m_dwCurPos] == '\\') {
+        if (m_dwSize <= m_dwCurPos) {
+          break;
+        }
+        m_dwCurPos++;
+      } else if (m_pData[m_dwCurPos] == '(') {
+        level++;
+      }
+      if (m_dwSize <= m_dwCurPos) {
+        break;
+      }
+      m_dwCurPos++;
+    }
+    if (m_dwCurPos < m_dwSize) {
+      m_dwCurPos++;
+    }
+    return CFX_ByteStringC(pStart,
+                           (FX_STRSIZE)(m_dwCurPos - (pStart - m_pData)));
+  }
+  return CFX_ByteStringC(pStart, dwSize);
+}
+FX_BOOL CPDF_SimpleParser::SearchToken(const CFX_ByteStringC& token) {
+  int token_len = token.GetLength();
+  while (m_dwCurPos < m_dwSize - token_len) {
+    if (FXSYS_memcmp(m_pData + m_dwCurPos, token.GetPtr(), token_len) == 0) {
+      break;
+    }
+    m_dwCurPos++;
+  }
+  if (m_dwCurPos == m_dwSize - token_len) {
+    return FALSE;
+  }
+  m_dwCurPos += token_len;
+  return TRUE;
+}
+FX_BOOL CPDF_SimpleParser::SkipWord(const CFX_ByteStringC& token) {
+  while (1) {
+    CFX_ByteStringC word = GetWord();
+    if (word.IsEmpty()) {
+      return FALSE;
+    }
+    if (word == token) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+FX_BOOL CPDF_SimpleParser::FindTagPair(const CFX_ByteStringC& start_token,
+                                       const CFX_ByteStringC& end_token,
+                                       FX_DWORD& start_pos,
+                                       FX_DWORD& end_pos) {
+  if (!start_token.IsEmpty()) {
+    if (!SkipWord(start_token)) {
+      return FALSE;
+    }
+    start_pos = m_dwCurPos;
+  }
+  while (1) {
+    end_pos = m_dwCurPos;
+    CFX_ByteStringC word = GetWord();
+    if (word.IsEmpty()) {
+      return FALSE;
+    }
+    if (word == end_token) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+FX_BOOL CPDF_SimpleParser::FindTagParam(const CFX_ByteStringC& token,
+                                        int nParams) {
+  nParams++;
+  FX_DWORD* pBuf = FX_Alloc(FX_DWORD, nParams);
+  int buf_index = 0;
+  int buf_count = 0;
+  while (1) {
+    pBuf[buf_index++] = m_dwCurPos;
+    if (buf_index == nParams) {
+      buf_index = 0;
+    }
+    buf_count++;
+    if (buf_count > nParams) {
+      buf_count = nParams;
+    }
+    CFX_ByteStringC word = GetWord();
+    if (word.IsEmpty()) {
+      FX_Free(pBuf);
+      return FALSE;
+    }
+    if (word == token) {
+      if (buf_count < nParams) {
+        continue;
+      }
+      m_dwCurPos = pBuf[buf_index];
+      FX_Free(pBuf);
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+CFX_ByteString PDF_NameDecode(const CFX_ByteStringC& bstr) {
+  int size = bstr.GetLength();
+  const FX_CHAR* pSrc = bstr.GetCStr();
+  if (FXSYS_memchr(pSrc, '#', size) == NULL) {
+    return bstr;
+  }
+  CFX_ByteString result;
+  FX_CHAR* pDestStart = result.GetBuffer(size);
+  FX_CHAR* pDest = pDestStart;
+  for (int i = 0; i < size; i++) {
+    if (pSrc[i] == '#' && i < size - 2) {
+      *pDest++ = HexCharToDigit(pSrc[i + 1]) * 16 + HexCharToDigit(pSrc[i + 2]);
+      i += 2;
+    } else {
+      *pDest++ = pSrc[i];
+    }
+  }
+  result.ReleaseBuffer((FX_STRSIZE)(pDest - pDestStart));
+  return result;
+}
+CFX_ByteString PDF_NameDecode(const CFX_ByteString& orig) {
+  if (FXSYS_memchr(orig.c_str(), '#', orig.GetLength()) == NULL) {
+    return orig;
+  }
+  return PDF_NameDecode(CFX_ByteStringC(orig));
+}
+CFX_ByteString PDF_NameEncode(const CFX_ByteString& orig) {
+  uint8_t* src_buf = (uint8_t*)orig.c_str();
+  int src_len = orig.GetLength();
+  int dest_len = 0;
+  int i;
+  for (i = 0; i < src_len; i++) {
+    uint8_t ch = src_buf[i];
+    if (ch >= 0x80 || PDFCharIsWhitespace(ch) || ch == '#' ||
+        PDFCharIsDelimiter(ch)) {
+      dest_len += 3;
+    } else {
+      dest_len++;
+    }
+  }
+  if (dest_len == src_len)
+    return orig;
+
+  CFX_ByteString res;
+  FX_CHAR* dest_buf = res.GetBuffer(dest_len);
+  dest_len = 0;
+  for (i = 0; i < src_len; i++) {
+    uint8_t ch = src_buf[i];
+    if (ch >= 0x80 || PDFCharIsWhitespace(ch) || ch == '#' ||
+        PDFCharIsDelimiter(ch)) {
+      dest_buf[dest_len++] = '#';
+      dest_buf[dest_len++] = "0123456789ABCDEF"[ch / 16];
+      dest_buf[dest_len++] = "0123456789ABCDEF"[ch % 16];
+    } else {
+      dest_buf[dest_len++] = ch;
+    }
+  }
+  dest_buf[dest_len] = 0;
+  res.ReleaseBuffer();
+  return res;
+}
+CFX_ByteTextBuf& operator<<(CFX_ByteTextBuf& buf, const CPDF_Object* pObj) {
+  if (pObj == NULL) {
+    buf << FX_BSTRC(" null");
+    return buf;
+  }
+  switch (pObj->GetType()) {
+    case PDFOBJ_NULL:
+      buf << FX_BSTRC(" null");
+      break;
+    case PDFOBJ_BOOLEAN:
+    case PDFOBJ_NUMBER:
+      buf << " " << pObj->GetString();
+      break;
+    case PDFOBJ_STRING:
+      buf << PDF_EncodeString(pObj->GetString(), pObj->AsString()->IsHex());
+      break;
+    case PDFOBJ_NAME: {
+      CFX_ByteString str = pObj->GetString();
+      buf << FX_BSTRC("/") << PDF_NameEncode(str);
+      break;
+    }
+    case PDFOBJ_REFERENCE: {
+      buf << " " << pObj->AsReference()->GetRefObjNum() << FX_BSTRC(" 0 R ");
+      break;
+    }
+    case PDFOBJ_ARRAY: {
+      const CPDF_Array* p = pObj->AsArray();
+      buf << FX_BSTRC("[");
+      for (FX_DWORD i = 0; i < p->GetCount(); i++) {
+        CPDF_Object* pElement = p->GetElement(i);
+        if (pElement->GetObjNum()) {
+          buf << " " << pElement->GetObjNum() << FX_BSTRC(" 0 R");
+        } else {
+          buf << pElement;
+        }
+      }
+      buf << FX_BSTRC("]");
+      break;
+    }
+    case PDFOBJ_DICTIONARY: {
+      const CPDF_Dictionary* p = pObj->AsDictionary();
+      buf << FX_BSTRC("<<");
+      FX_POSITION pos = p->GetStartPos();
+      while (pos) {
+        CFX_ByteString key;
+        CPDF_Object* pValue = p->GetNextElement(pos, key);
+        buf << FX_BSTRC("/") << PDF_NameEncode(key);
+        if (pValue && pValue->GetObjNum()) {
+          buf << " " << pValue->GetObjNum() << FX_BSTRC(" 0 R ");
+        } else {
+          buf << pValue;
+        }
+      }
+      buf << FX_BSTRC(">>");
+      break;
+    }
+    case PDFOBJ_STREAM: {
+      const CPDF_Stream* p = pObj->AsStream();
+      buf << p->GetDict() << FX_BSTRC("stream\r\n");
+      CPDF_StreamAcc acc;
+      acc.LoadAllData(p, TRUE);
+      buf.AppendBlock(acc.GetData(), acc.GetSize());
+      buf << FX_BSTRC("\r\nendstream");
+      break;
+    }
+    default:
+      ASSERT(FALSE);
+      break;
+  }
+  return buf;
+}
+FX_FLOAT PDF_ClipFloat(FX_FLOAT f) {
+  if (f < 0) {
+    return 0;
+  }
+  if (f > 1.0f) {
+    return 1.0f;
+  }
+  return f;
+}
+static CPDF_Object* SearchNumberNode(CPDF_Dictionary* pNode, int num) {
+  CPDF_Array* pLimits = pNode->GetArray("Limits");
+  if (pLimits &&
+      (num < pLimits->GetInteger(0) || num > pLimits->GetInteger(1))) {
+    return NULL;
+  }
+  CPDF_Array* pNumbers = pNode->GetArray("Nums");
+  if (pNumbers) {
+    FX_DWORD dwCount = pNumbers->GetCount() / 2;
+    for (FX_DWORD i = 0; i < dwCount; i++) {
+      int index = pNumbers->GetInteger(i * 2);
+      if (num == index) {
+        return pNumbers->GetElementValue(i * 2 + 1);
+      }
+      if (index > num) {
+        break;
+      }
+    }
+    return NULL;
+  }
+  CPDF_Array* pKids = pNode->GetArray("Kids");
+  if (pKids == NULL) {
+    return NULL;
+  }
+  for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
+    CPDF_Dictionary* pKid = pKids->GetDict(i);
+    if (pKid == NULL) {
+      continue;
+    }
+    CPDF_Object* pFound = SearchNumberNode(pKid, num);
+    if (pFound) {
+      return pFound;
+    }
+  }
+  return NULL;
+}
+CPDF_Object* CPDF_NumberTree::LookupValue(int num) {
+  return SearchNumberNode(m_pRoot, num);
+}