blob: 9729bab9424888ebb7291d066984690879746eb0 [file] [log] [blame]
// 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 "core/include/fpdfapi/fpdf_parser.h"
#include "core/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++ =
FXSYS_toHexDigit(pSrc[i + 1]) * 16 + FXSYS_toHexDigit(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);
}