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_serial.h"
8CFDF_Document::CFDF_Document() : CPDF_IndirectObjects(NULL)
9{
10    m_pRootDict = NULL;
11    m_pFile = NULL;
12    m_bOwnFile = FALSE;
13}
14CFDF_Document::~CFDF_Document()
15{
16    if (m_bOwnFile && m_pFile) {
17        m_pFile->Release();
18    }
19}
20CFDF_Document* CFDF_Document::CreateNewDoc()
21{
22    CFDF_Document* pDoc = FX_NEW CFDF_Document;
23    pDoc->m_pRootDict = FX_NEW CPDF_Dictionary;
24    pDoc->AddIndirectObject(pDoc->m_pRootDict);
25    CPDF_Dictionary* pFDFDict = FX_NEW CPDF_Dictionary;
26    pDoc->m_pRootDict->SetAt(FX_BSTRC("FDF"), pFDFDict);
27    return pDoc;
28}
29CFDF_Document* CFDF_Document::ParseFile(FX_LPCSTR file_path)
30{
31    return CFDF_Document::ParseFile(FX_CreateFileRead(file_path), TRUE);
32}
33CFDF_Document* CFDF_Document::ParseFile(FX_LPCWSTR file_path)
34{
35    return CFDF_Document::ParseFile(FX_CreateFileRead(file_path), TRUE);
36}
37CFDF_Document* CFDF_Document::ParseFile(IFX_FileRead *pFile, FX_BOOL bOwnFile)
38{
39    if (!pFile) {
40        return NULL;
41    }
42    CFDF_Document* pDoc = FX_NEW CFDF_Document;
43    pDoc->ParseStream(pFile, bOwnFile);
44    if (pDoc->m_pRootDict == NULL) {
45        delete pDoc;
46        return NULL;
47    }
48    return pDoc;
49}
50CFDF_Document* CFDF_Document::ParseMemory(FX_LPCBYTE pData, FX_DWORD size)
51{
52    return CFDF_Document::ParseFile(FX_CreateMemoryStream((FX_LPBYTE)pData, size), TRUE);
53}
54void CFDF_Document::ParseStream(IFX_FileRead *pFile, FX_BOOL bOwnFile)
55{
56    m_pFile = pFile;
57    m_bOwnFile = bOwnFile;
58    CPDF_SyntaxParser parser;
59    parser.InitParser(m_pFile, 0);
60    while (1) {
61        FX_BOOL bNumber;
62        CFX_ByteString word = parser.GetNextWord(bNumber);
63        if (bNumber) {
64            FX_DWORD objnum = FXSYS_atoi(word);
65            word = parser.GetNextWord(bNumber);
66            if (!bNumber) {
67                break;
68            }
69            word = parser.GetNextWord(bNumber);
70            if (word != FX_BSTRC("obj")) {
71                break;
72            }
73            CPDF_Object* pObj = parser.GetObject(this, objnum, 0, FALSE);
74            if (pObj == NULL) {
75                break;
76            }
77            InsertIndirectObject(objnum, pObj);
78            word = parser.GetNextWord(bNumber);
79            if (word != FX_BSTRC("endobj")) {
80                break;
81            }
82        } else {
83            if (word != FX_BSTRC("trailer")) {
84                break;
85            }
86            CPDF_Dictionary* pMainDict = (CPDF_Dictionary*)parser.GetObject(this, 0, 0, 0);
87            if (pMainDict == NULL || pMainDict->GetType() != PDFOBJ_DICTIONARY) {
88                break;
89            }
90            m_pRootDict = pMainDict->GetDict(FX_BSTRC("Root"));
91            pMainDict->Release();
92            break;
93        }
94    }
95}
96FX_BOOL CFDF_Document::WriteBuf(CFX_ByteTextBuf& buf) const
97{
98    if (m_pRootDict == NULL) {
99        return FALSE;
100    }
101    buf << FX_BSTRC("%FDF-1.2\r\n");
102    FX_POSITION pos = m_IndirectObjs.GetStartPosition();
103    while(pos) {
104        size_t objnum;
105        CPDF_Object* pObj;
106        m_IndirectObjs.GetNextAssoc(pos, (FX_LPVOID&)objnum, (FX_LPVOID&)pObj);
107        buf << (FX_DWORD)objnum << FX_BSTRC(" 0 obj\r\n") << pObj << FX_BSTRC("\r\nendobj\r\n\r\n");
108    }
109    buf << FX_BSTRC("trailer\r\n<</Root ") << m_pRootDict->GetObjNum() << FX_BSTRC(" 0 R>>\r\n%%EOF\r\n");
110    return TRUE;
111}
112CFX_WideString CFDF_Document::GetWin32Path() const
113{
114    CPDF_Object* pFileSpec = m_pRootDict->GetDict(FX_BSTRC("FDF"))->GetElementValue(FX_BSTRC("F"));
115    if (pFileSpec == NULL) {
116        return CFX_WideString();
117    }
118    if (pFileSpec->GetType() == PDFOBJ_STRING) {
119        return FPDF_FileSpec_GetWin32Path(m_pRootDict->GetDict(FX_BSTRC("FDF")));
120    }
121    return FPDF_FileSpec_GetWin32Path(pFileSpec);
122}
123FX_BOOL CFDF_Document::WriteFile(FX_LPCSTR file_path) const
124{
125    IFX_FileWrite *pFile = FX_CreateFileWrite(file_path);
126    if (!pFile) {
127        return FALSE;
128    }
129    FX_BOOL bRet = WriteFile(pFile);
130    pFile->Release();
131    return bRet;
132}
133FX_BOOL CFDF_Document::WriteFile(FX_LPCWSTR file_path) const
134{
135    IFX_FileWrite *pFile = FX_CreateFileWrite(file_path);
136    if (!pFile) {
137        return FALSE;
138    }
139    FX_BOOL bRet = WriteFile(pFile);
140    pFile->Release();
141    return bRet;
142}
143FX_BOOL CFDF_Document::WriteFile(IFX_FileWrite *pFile) const
144{
145    CFX_ByteTextBuf buf;
146    WriteBuf(buf);
147    FX_BOOL bRet = pFile->WriteBlock(buf.GetBuffer(), buf.GetSize());
148    if (bRet) {
149        pFile->Flush();
150    }
151    return bRet;
152}
153static CFX_WideString ChangeSlash(FX_LPCWSTR str)
154{
155    CFX_WideString result;
156    while (*str) {
157        if (*str == '\\') {
158            result += '/';
159        } else if (*str == '/') {
160            result += '\\';
161        } else {
162            result += *str;
163        }
164        str ++;
165    }
166    return result;
167}
168void FPDF_FileSpec_SetWin32Path(CPDF_Object* pFileSpec, const CFX_WideString& filepath)
169{
170    CFX_WideString result;
171    if (filepath.GetLength() > 1 && filepath[1] == ':') {
172        result = L"/";
173        result += filepath[0];
174        if (filepath[2] != '\\') {
175            result += '/';
176        }
177        result += ChangeSlash((FX_LPCWSTR)filepath + 2);
178    } else if (filepath.GetLength() > 1 && filepath[0] == '\\' && filepath[1] == '\\') {
179        result = ChangeSlash((FX_LPCWSTR)filepath + 1);
180    } else {
181        result = ChangeSlash(filepath);
182    }
183    if (pFileSpec->GetType() == PDFOBJ_STRING) {
184        pFileSpec->SetString(CFX_ByteString::FromUnicode(result));
185    } else if (pFileSpec->GetType() == PDFOBJ_DICTIONARY) {
186        ((CPDF_Dictionary*)pFileSpec)->SetAtString(FX_BSTRC("F"), CFX_ByteString::FromUnicode(result));
187        ((CPDF_Dictionary*)pFileSpec)->SetAtString(FX_BSTRC("UF"), PDF_EncodeText(result));
188        ((CPDF_Dictionary*)pFileSpec)->RemoveAt(FX_BSTRC("FS"));
189    }
190}
191CFX_WideString	FPDF_FileSpec_GetWin32Path(const CPDF_Object* pFileSpec)
192{
193    CFX_WideString wsFileName;
194    if (pFileSpec->GetType() == PDFOBJ_DICTIONARY) {
195        CPDF_Dictionary* pDict = (CPDF_Dictionary*)pFileSpec;
196        wsFileName = pDict->GetUnicodeText(FX_BSTRC("UF"));
197        if (wsFileName.IsEmpty()) {
198            wsFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("F")));
199        }
200        if (pDict->GetString(FX_BSTRC("FS")) == FX_BSTRC("URL")) {
201            return wsFileName;
202        }
203        if (wsFileName.IsEmpty() && pDict->KeyExist(FX_BSTRC("DOS"))) {
204            wsFileName = CFX_WideString::FromLocal(pDict->GetString(FX_BSTRC("DOS")));
205        }
206    } else {
207        wsFileName = CFX_WideString::FromLocal(pFileSpec->GetString());
208    }
209    if (wsFileName[0] != '/') {
210        return ChangeSlash(wsFileName);
211    }
212    if (wsFileName[2] == '/') {
213        CFX_WideString result;
214        result += wsFileName[1];
215        result += ':';
216        result += ChangeSlash(((FX_LPCWSTR)wsFileName) + 2);
217        return result;
218    } else {
219        CFX_WideString result;
220        result += '\\';
221        result += ChangeSlash(wsFileName);
222        return result;
223    }
224}
225