1// Copyright 2015 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#include <limits>
6#include <string>
7
8#include "core/fpdfapi/parser/cpdf_parser.h"
9#include "core/fpdfapi/parser/cpdf_syntax_parser.h"
10#include "core/fxcrt/fx_extension.h"
11#include "core/fxcrt/fx_stream.h"
12#include "core/fxcrt/retain_ptr.h"
13#include "testing/fx_string_testhelpers.h"
14#include "testing/gtest/include/gtest/gtest.h"
15#include "testing/utils/path_service.h"
16
17// A wrapper class to help test member functions of CPDF_Parser.
18class CPDF_TestParser : public CPDF_Parser {
19 public:
20  CPDF_TestParser() {}
21  ~CPDF_TestParser() {}
22
23  // Setup reading from a file and initial states.
24  bool InitTestFromFile(const char* path) {
25    RetainPtr<IFX_SeekableReadStream> pFileAccess =
26        IFX_SeekableReadStream::CreateFromFilename(path);
27    if (!pFileAccess)
28      return false;
29
30    // For the test file, the header is set at the beginning.
31    m_pSyntax->InitParser(pFileAccess, 0);
32    return true;
33  }
34
35  // Setup reading from a buffer and initial states.
36  bool InitTestFromBuffer(const unsigned char* buffer, size_t len) {
37    // For the test file, the header is set at the beginning.
38    m_pSyntax->InitParser(
39        pdfium::MakeRetain<CFX_BufferSeekableReadStream>(buffer, len), 0);
40    return true;
41  }
42
43 private:
44  // Add test cases here as private friend so that protected members in
45  // CPDF_Parser can be accessed by test cases.
46  // Need to access RebuildCrossRef.
47  FRIEND_TEST(cpdf_parser, RebuildCrossRefCorrectly);
48  FRIEND_TEST(cpdf_parser, RebuildCrossRefFailed);
49  // Need to access LoadCrossRefV4.
50  FRIEND_TEST(cpdf_parser, LoadCrossRefV4);
51};
52
53TEST(cpdf_parser, RebuildCrossRefCorrectly) {
54  CPDF_TestParser parser;
55  std::string test_file;
56  ASSERT_TRUE(PathService::GetTestFilePath("parser_rebuildxref_correct.pdf",
57                                           &test_file));
58  ASSERT_TRUE(parser.InitTestFromFile(test_file.c_str())) << test_file;
59
60  ASSERT_TRUE(parser.RebuildCrossRef());
61  const FX_FILESIZE offsets[] = {0, 15, 61, 154, 296, 374, 450};
62  const uint16_t versions[] = {0, 0, 2, 4, 6, 8, 0};
63  for (size_t i = 0; i < FX_ArraySize(offsets); ++i)
64    EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
65  for (size_t i = 0; i < FX_ArraySize(versions); ++i)
66    EXPECT_EQ(versions[i], parser.m_ObjectInfo[i].gennum);
67}
68
69TEST(cpdf_parser, RebuildCrossRefFailed) {
70  CPDF_TestParser parser;
71  std::string test_file;
72  ASSERT_TRUE(PathService::GetTestFilePath(
73      "parser_rebuildxref_error_notrailer.pdf", &test_file));
74  ASSERT_TRUE(parser.InitTestFromFile(test_file.c_str())) << test_file;
75
76  ASSERT_FALSE(parser.RebuildCrossRef());
77}
78
79TEST(cpdf_parser, LoadCrossRefV4) {
80  {
81    const unsigned char xref_table[] =
82        "xref \n"
83        "0 6 \n"
84        "0000000003 65535 f \n"
85        "0000000017 00000 n \n"
86        "0000000081 00000 n \n"
87        "0000000000 00007 f \n"
88        "0000000331 00000 n \n"
89        "0000000409 00000 n \n"
90        "trail";  // Needed to end cross ref table reading.
91    CPDF_TestParser parser;
92    ASSERT_TRUE(
93        parser.InitTestFromBuffer(xref_table, FX_ArraySize(xref_table)));
94
95    ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
96    const FX_FILESIZE offsets[] = {0, 17, 81, 0, 331, 409};
97    const CPDF_TestParser::ObjectType types[] = {
98        CPDF_TestParser::ObjectType::kFree,
99        CPDF_TestParser::ObjectType::kNotCompressed,
100        CPDF_TestParser::ObjectType::kNotCompressed,
101        CPDF_TestParser::ObjectType::kFree,
102        CPDF_TestParser::ObjectType::kNotCompressed,
103        CPDF_TestParser::ObjectType::kNotCompressed};
104    for (size_t i = 0; i < FX_ArraySize(offsets); ++i) {
105      EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
106      EXPECT_EQ(types[i], parser.m_ObjectInfo[i].type);
107    }
108  }
109  {
110    const unsigned char xref_table[] =
111        "xref \n"
112        "0 1 \n"
113        "0000000000 65535 f \n"
114        "3 1 \n"
115        "0000025325 00000 n \n"
116        "8 2 \n"
117        "0000025518 00002 n \n"
118        "0000025635 00000 n \n"
119        "12 1 \n"
120        "0000025777 00000 n \n"
121        "trail";  // Needed to end cross ref table reading.
122    CPDF_TestParser parser;
123    ASSERT_TRUE(
124        parser.InitTestFromBuffer(xref_table, FX_ArraySize(xref_table)));
125
126    ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
127    const FX_FILESIZE offsets[] = {0, 0,     0,     25325, 0, 0,    0,
128                                   0, 25518, 25635, 0,     0, 25777};
129    const CPDF_TestParser::ObjectType types[] = {
130        CPDF_TestParser::ObjectType::kFree,
131        CPDF_TestParser::ObjectType::kFree,
132        CPDF_TestParser::ObjectType::kFree,
133        CPDF_TestParser::ObjectType::kNotCompressed,
134        CPDF_TestParser::ObjectType::kFree,
135        CPDF_TestParser::ObjectType::kFree,
136        CPDF_TestParser::ObjectType::kFree,
137        CPDF_TestParser::ObjectType::kFree,
138        CPDF_TestParser::ObjectType::kNotCompressed,
139        CPDF_TestParser::ObjectType::kNotCompressed,
140        CPDF_TestParser::ObjectType::kFree,
141        CPDF_TestParser::ObjectType::kFree,
142        CPDF_TestParser::ObjectType::kNotCompressed};
143    for (size_t i = 0; i < FX_ArraySize(offsets); ++i) {
144      EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
145      EXPECT_EQ(types[i], parser.m_ObjectInfo[i].type);
146    }
147  }
148  {
149    const unsigned char xref_table[] =
150        "xref \n"
151        "0 1 \n"
152        "0000000000 65535 f \n"
153        "3 1 \n"
154        "0000025325 00000 n \n"
155        "8 2 \n"
156        "0000000000 65535 f \n"
157        "0000025635 00000 n \n"
158        "12 1 \n"
159        "0000025777 00000 n \n"
160        "trail";  // Needed to end cross ref table reading.
161    CPDF_TestParser parser;
162    ASSERT_TRUE(
163        parser.InitTestFromBuffer(xref_table, FX_ArraySize(xref_table)));
164
165    ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
166    const FX_FILESIZE offsets[] = {0, 0, 0,     25325, 0, 0,    0,
167                                   0, 0, 25635, 0,     0, 25777};
168    const CPDF_TestParser::ObjectType types[] = {
169        CPDF_TestParser::ObjectType::kFree,
170        CPDF_TestParser::ObjectType::kFree,
171        CPDF_TestParser::ObjectType::kFree,
172        CPDF_TestParser::ObjectType::kNotCompressed,
173        CPDF_TestParser::ObjectType::kFree,
174        CPDF_TestParser::ObjectType::kFree,
175        CPDF_TestParser::ObjectType::kFree,
176        CPDF_TestParser::ObjectType::kFree,
177        CPDF_TestParser::ObjectType::kFree,
178        CPDF_TestParser::ObjectType::kNotCompressed,
179        CPDF_TestParser::ObjectType::kFree,
180        CPDF_TestParser::ObjectType::kFree,
181        CPDF_TestParser::ObjectType::kNotCompressed};
182    for (size_t i = 0; i < FX_ArraySize(offsets); ++i) {
183      EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
184      EXPECT_EQ(types[i], parser.m_ObjectInfo[i].type);
185    }
186  }
187  {
188    const unsigned char xref_table[] =
189        "xref \n"
190        "0 7 \n"
191        "0000000002 65535 f \n"
192        "0000000023 00000 n \n"
193        "0000000003 65535 f \n"
194        "0000000004 65535 f \n"
195        "0000000000 65535 f \n"
196        "0000000045 00000 n \n"
197        "0000000179 00000 n \n"
198        "trail";  // Needed to end cross ref table reading.
199    CPDF_TestParser parser;
200    ASSERT_TRUE(
201        parser.InitTestFromBuffer(xref_table, FX_ArraySize(xref_table)));
202
203    ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
204    const FX_FILESIZE offsets[] = {0, 23, 0, 0, 0, 45, 179};
205    const CPDF_TestParser::ObjectType types[] = {
206        CPDF_TestParser::ObjectType::kFree,
207        CPDF_TestParser::ObjectType::kNotCompressed,
208        CPDF_TestParser::ObjectType::kFree,
209        CPDF_TestParser::ObjectType::kFree,
210        CPDF_TestParser::ObjectType::kFree,
211        CPDF_TestParser::ObjectType::kNotCompressed,
212        CPDF_TestParser::ObjectType::kNotCompressed};
213    for (size_t i = 0; i < FX_ArraySize(offsets); ++i) {
214      EXPECT_EQ(offsets[i], parser.m_ObjectInfo[i].pos);
215      EXPECT_EQ(types[i], parser.m_ObjectInfo[i].type);
216    }
217  }
218}
219