Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cpdf_parser_unittest.cpp
Go to the documentation of this file.
1// Copyright 2015 The PDFium Authors
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 "core/fpdfapi/parser/cpdf_parser.h"
6
7#include <limits>
8#include <memory>
9#include <ostream>
10#include <string>
11#include <utility>
12#include <vector>
13
14#include "core/fpdfapi/parser/cpdf_dictionary.h"
15#include "core/fpdfapi/parser/cpdf_linearized_header.h"
16#include "core/fpdfapi/parser/cpdf_object.h"
17#include "core/fpdfapi/parser/cpdf_syntax_parser.h"
18#include "core/fxcrt/cfx_read_only_span_stream.h"
19#include "core/fxcrt/fx_extension.h"
20#include "core/fxcrt/fx_stream.h"
21#include "core/fxcrt/retain_ptr.h"
22#include "testing/gmock/include/gmock/gmock.h"
23#include "testing/gtest/include/gtest/gtest.h"
24#include "testing/utils/path_service.h"
25
26using testing::ElementsAre;
27using testing::Pair;
28using testing::Return;
29
30namespace {
31
32CPDF_CrossRefTable::ObjectInfo GetObjInfo(const CPDF_Parser& parser,
33 uint32_t obj_num) {
34 const auto* info = parser.GetCrossRefTable()->GetObjectInfo(obj_num);
35 return info ? *info : CPDF_CrossRefTable::ObjectInfo();
36}
37
38class TestObjectsHolder final : public CPDF_Parser::ParsedObjectsHolder {
39 public:
40 TestObjectsHolder() = default;
41 ~TestObjectsHolder() override = default;
42
43 // CPDF_Parser::ParsedObjectsHolder:
44 bool TryInit() override { return true; }
45 MOCK_METHOD1(ParseIndirectObject, RetainPtr<CPDF_Object>(uint32_t objnum));
46};
47
48} // namespace
49
50// Test-only helper to support Gmock. Cannot be in an anonymous namespace.
52 const CPDF_CrossRefTable::ObjectInfo& rhs) {
53 if (lhs.type != rhs.type) {
54 return false;
55 }
56
57 if (lhs.gennum != rhs.gennum) {
58 return false;
59 }
60
61 switch (lhs.type) {
63 return true;
65 return lhs.pos == rhs.pos;
67 return lhs.archive.obj_num == rhs.archive.obj_num &&
68 lhs.archive.obj_index == rhs.archive.obj_index;
70 return false;
71 }
72}
73
74// Test-only helper to let Gmock pretty-print `info`. Cannot be in an anonymous
75// namespace.
76std::ostream& operator<<(std::ostream& os,
77 const CPDF_CrossRefTable::ObjectInfo& info) {
78 os << "(";
79 switch (info.type) {
81 os << "Free object";
82 break;
84 os << "Normal object, pos: " << info.pos
85 << ", obj_stream=" << info.is_object_stream_flag;
86 break;
88 os << "Compressed object, archive obj_num: " << info.archive.obj_num
89 << ", archive obj_index: " << info.archive.obj_index;
90 break;
92 os << "Null object";
93 break;
94 }
95 os << ", gennum: " << info.gennum << ")";
96 return os;
97}
98
99// A wrapper class to help test member functions of CPDF_Parser.
100class CPDF_TestParser final : public CPDF_Parser {
101 public:
103 ~CPDF_TestParser() = default;
104
105 // Setup reading from a file and initial states.
106 bool InitTestFromFile(const char* path) {
107 RetainPtr<IFX_SeekableReadStream> pFileAccess =
108 IFX_SeekableReadStream::CreateFromFilename(path);
109 if (!pFileAccess)
110 return false;
111
112 // For the test file, the header is set at the beginning.
113 SetSyntaxParserForTesting(
114 std::make_unique<CPDF_SyntaxParser>(std::move(pFileAccess)));
115 return true;
116 }
117
118 // Setup reading from a buffer and initial states.
119 bool InitTestFromBufferWithOffset(pdfium::span<const uint8_t> buffer,
120 FX_FILESIZE header_offset) {
121 SetSyntaxParserForTesting(CPDF_SyntaxParser::CreateForTesting(
122 pdfium::MakeRetain<CFX_ReadOnlySpanStream>(buffer), header_offset));
123 return true;
124 }
125
126 bool InitTestFromBuffer(pdfium::span<const uint8_t> buffer) {
127 return InitTestFromBufferWithOffset(buffer, 0 /*header_offset*/);
128 }
129
130 // Expose protected CPDF_Parser methods for testing.
131 using CPDF_Parser::LoadCrossRefV4;
132 using CPDF_Parser::ParseLinearizedHeader;
133 using CPDF_Parser::ParseStartXRef;
134 using CPDF_Parser::RebuildCrossRef;
135 using CPDF_Parser::StartParseInternal;
136
137 TestObjectsHolder& object_holder() { return object_holder_; }
138
139 private:
140 TestObjectsHolder object_holder_;
141};
142
144 CPDF_TestParser parser;
145 std::string test_file =
146 PathService::GetTestFilePath("parser_rebuildxref_correct.pdf");
147 ASSERT_FALSE(test_file.empty());
148 ASSERT_TRUE(parser.InitTestFromFile(test_file.c_str())) << test_file;
149
150 ASSERT_TRUE(parser.RebuildCrossRef());
151 const FX_FILESIZE offsets[] = {0, 15, 61, 154, 296, 374, 450};
152 const uint16_t versions[] = {0, 0, 2, 4, 6, 8, 0};
153 for (size_t i = 0; i < std::size(offsets); ++i)
154 EXPECT_EQ(offsets[i], GetObjInfo(parser, i).pos);
155 for (size_t i = 0; i < std::size(versions); ++i)
156 EXPECT_EQ(versions[i], GetObjInfo(parser, i).gennum);
157
158 const CPDF_CrossRefTable* cross_ref_table = parser.GetCrossRefTable();
159 ASSERT_TRUE(cross_ref_table);
160 EXPECT_EQ(0u, cross_ref_table->trailer_object_number());
161}
162
164 CPDF_TestParser parser;
165 std::string test_file =
166 PathService::GetTestFilePath("parser_rebuildxref_error_notrailer.pdf");
167 ASSERT_FALSE(test_file.empty());
168 ASSERT_TRUE(parser.InitTestFromFile(test_file.c_str())) << test_file;
169
170 ASSERT_FALSE(parser.RebuildCrossRef());
171}
172
174 {
175 static const unsigned char kXrefTable[] =
176 "xref \n"
177 "0 6 \n"
178 "0000000003 65535 f \n"
179 "0000000017 00000 n \n"
180 "0000000081 00000 n \n"
181 "0000000000 00007 f \n"
182 "0000000331 00000 n \n"
183 "0000000409 00000 n \n"
184 "trail"; // Needed to end cross ref table reading.
185 CPDF_TestParser parser;
186 ASSERT_TRUE(parser.InitTestFromBuffer(kXrefTable));
187
188 ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
189 static const FX_FILESIZE kOffsets[] = {0, 17, 81, 0, 331, 409};
190 static const CPDF_CrossRefTable::ObjectType kTypes[] = {
197 static_assert(std::size(kOffsets) == std::size(kTypes),
198 "kOffsets / kTypes size mismatch");
199 for (size_t i = 0; i < std::size(kOffsets); ++i) {
200 EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
201 EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
202 }
203 }
204 {
205 static const unsigned char kXrefTable[] =
206 "xref \n"
207 "0 1 \n"
208 "0000000000 65535 f \n"
209 "3 1 \n"
210 "0000025325 00000 n \n"
211 "8 2 \n"
212 "0000025518 00002 n \n"
213 "0000025635 00000 n \n"
214 "12 1 \n"
215 "0000025777 00000 n \n"
216 "trail"; // Needed to end cross ref table reading.
217 CPDF_TestParser parser;
218 ASSERT_TRUE(parser.InitTestFromBuffer(kXrefTable));
219
220 ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
221 static const FX_FILESIZE kOffsets[] = {0, 0, 0, 25325, 0, 0, 0,
222 0, 25518, 25635, 0, 0, 25777};
223 static const CPDF_CrossRefTable::ObjectType kTypes[] = {
237 static_assert(std::size(kOffsets) == std::size(kTypes),
238 "kOffsets / kTypes size mismatch");
239 for (size_t i = 0; i < std::size(kOffsets); ++i) {
240 EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
241 EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
242 }
243 }
244 {
245 static const unsigned char kXrefTable[] =
246 "xref \n"
247 "0 1 \n"
248 "0000000000 65535 f \n"
249 "3 1 \n"
250 "0000025325 00000 n \n"
251 "8 2 \n"
252 "0000000000 65535 f \n"
253 "0000025635 00000 n \n"
254 "12 1 \n"
255 "0000025777 00000 n \n"
256 "trail"; // Needed to end cross ref table reading.
257 CPDF_TestParser parser;
258 ASSERT_TRUE(parser.InitTestFromBuffer(kXrefTable));
259
260 ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
261 static const FX_FILESIZE kOffsets[] = {0, 0, 0, 25325, 0, 0, 0,
262 0, 0, 25635, 0, 0, 25777};
263 static const CPDF_CrossRefTable::ObjectType kTypes[] = {
277 static_assert(std::size(kOffsets) == std::size(kTypes),
278 "kOffsets / kTypes size mismatch");
279 for (size_t i = 0; i < std::size(kOffsets); ++i) {
280 EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
281 EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
282 }
283 }
284 {
285 static const unsigned char kXrefTable[] =
286 "xref \n"
287 "0 7 \n"
288 "0000000002 65535 f \n"
289 "0000000023 00000 n \n"
290 "0000000003 65535 f \n"
291 "0000000004 65535 f \n"
292 "0000000000 65535 f \n"
293 "0000000045 00000 n \n"
294 "0000000179 00000 n \n"
295 "trail"; // Needed to end cross ref table reading.
296 CPDF_TestParser parser;
297 ASSERT_TRUE(parser.InitTestFromBuffer(kXrefTable));
298
299 ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
300 static const FX_FILESIZE kOffsets[] = {0, 23, 0, 0, 0, 45, 179};
301 static const CPDF_CrossRefTable::ObjectType kTypes[] = {
309 static_assert(std::size(kOffsets) == std::size(kTypes),
310 "kOffsets / kTypes size mismatch");
311 for (size_t i = 0; i < std::size(kOffsets); ++i) {
312 EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos);
313 EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type);
314 }
315 }
316 {
317 // Regression test for https://crbug.com/945624 - Make sure the parser
318 // can correctly handle table sizes that are multiples of the read size,
319 // which is 1024.
320 std::string xref_table = "xref \n 0 2048 \n";
321 xref_table.reserve(41000);
322 for (int i = 0; i < 2048; ++i) {
323 char buffer[21];
324 snprintf(buffer, sizeof(buffer), "%010d 00000 n \n", i + 1);
325 xref_table += buffer;
326 }
327 xref_table += "trail"; // Needed to end cross ref table reading.
328 CPDF_TestParser parser;
329 ASSERT_TRUE(parser.InitTestFromBuffer(
330 pdfium::make_span(reinterpret_cast<const uint8_t*>(xref_table.c_str()),
331 xref_table.size())));
332
333 ASSERT_TRUE(parser.LoadCrossRefV4(0, false));
334 for (size_t i = 0; i < 2048; ++i) {
335 EXPECT_EQ(static_cast<int>(i) + 1, GetObjInfo(parser, i).pos);
337 GetObjInfo(parser, i).type);
338 }
339 }
340}
341
343 CPDF_TestParser parser;
344 std::string test_file =
345 PathService::GetTestFilePath("annotation_stamp_with_ap.pdf");
346 ASSERT_FALSE(test_file.empty());
347 ASSERT_TRUE(parser.InitTestFromFile(test_file.c_str())) << test_file;
348
349 EXPECT_EQ(100940, parser.ParseStartXRef());
350 RetainPtr<CPDF_Object> cross_ref_v5_obj =
351 parser.ParseIndirectObjectAt(100940, 0);
352 ASSERT_TRUE(cross_ref_v5_obj);
353 EXPECT_EQ(75u, cross_ref_v5_obj->GetObjNum());
354}
355
357 static constexpr FX_FILESIZE kTestHeaderOffset = 765;
358 std::string test_file =
359 PathService::GetTestFilePath("annotation_stamp_with_ap.pdf");
360 ASSERT_FALSE(test_file.empty());
361 RetainPtr<IFX_SeekableReadStream> pFileAccess =
362 IFX_SeekableReadStream::CreateFromFilename(test_file.c_str());
363 ASSERT_TRUE(pFileAccess);
364
365 std::vector<unsigned char> data(pFileAccess->GetSize() + kTestHeaderOffset);
366 ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(
367 pdfium::make_span(data).subspan(kTestHeaderOffset), 0));
368 CPDF_TestParser parser;
369 parser.InitTestFromBufferWithOffset(data, kTestHeaderOffset);
370
371 EXPECT_EQ(100940, parser.ParseStartXRef());
372 RetainPtr<CPDF_Object> cross_ref_v5_obj =
373 parser.ParseIndirectObjectAt(100940, 0);
374 ASSERT_TRUE(cross_ref_v5_obj);
375 EXPECT_EQ(75u, cross_ref_v5_obj->GetObjNum());
376}
377
379 static constexpr FX_FILESIZE kTestHeaderOffset = 765;
380 std::string test_file = PathService::GetTestFilePath("linearized.pdf");
381 ASSERT_FALSE(test_file.empty());
382 RetainPtr<IFX_SeekableReadStream> pFileAccess =
383 IFX_SeekableReadStream::CreateFromFilename(test_file.c_str());
384 ASSERT_TRUE(pFileAccess);
385
386 std::vector<unsigned char> data(pFileAccess->GetSize() + kTestHeaderOffset);
387 ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(
388 pdfium::make_span(data).subspan(kTestHeaderOffset), 0));
389
390 CPDF_TestParser parser;
391 parser.InitTestFromBufferWithOffset(data, kTestHeaderOffset);
392 EXPECT_TRUE(parser.ParseLinearizedHeader());
393
394 const CPDF_CrossRefTable* cross_ref_table = parser.GetCrossRefTable();
395 ASSERT_TRUE(cross_ref_table);
396 EXPECT_EQ(0u, cross_ref_table->trailer_object_number());
397}
398
400 const unsigned char kData[] =
401 "%PDF1-7 0 obj <</Size 2 /W [0 0 0]\n>>\n"
402 "stream\n"
403 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
404 "endstream\n"
405 "endobj\n"
406 "startxref\n"
407 "6\n"
408 "%%EOF\n";
409 CPDF_TestParser parser;
410 ASSERT_TRUE(parser.InitTestFromBuffer(kData));
412 ASSERT_TRUE(parser.GetCrossRefTable());
413 EXPECT_EQ(0u, parser.GetCrossRefTable()->objects_info().size());
414}
415
416class ParserXRefTest : public testing::Test {
417 public:
418 ParserXRefTest() = default;
420
421 // testing::Test:
423 // Satisfy CPDF_Parser's checks, so the test data below can concentrate on
424 // the /XRef stream and avoid also providing other valid dictionaries.
425 dummy_root_ = pdfium::MakeRetain<CPDF_Dictionary>();
426 EXPECT_CALL(parser().object_holder(), ParseIndirectObject)
427 .WillRepeatedly(Return(dummy_root_));
428 }
429
430 CPDF_TestParser& parser() { return parser_; }
431
432 private:
433 RetainPtr<CPDF_Dictionary> dummy_root_;
434 CPDF_TestParser parser_;
435};
436
438 // Since /Index starts at 4194303, the object number will go past
439 // `kMaxObjectNumber`.
440 static_assert(CPDF_Parser::kMaxObjectNumber == 4194304,
441 "Unexpected kMaxObjectNumber");
442 const unsigned char kData[] =
443 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
444 "7 0 obj <<\n"
445 " /Filter /ASCIIHexDecode\n"
446 " /Index [4194303 3]\n"
447 " /Root 1 0 R\n"
448 " /Size 4194306\n"
449 " /W [1 1 1]\n"
450 ">>\n"
451 "stream\n"
452 "01 00 00\n"
453 "01 0F 00\n"
454 "01 12 00\n"
455 "endstream\n"
456 "endobj\n"
457 "startxref\n"
458 "14\n"
459 "%%EOF\n";
460 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
461 EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
462 EXPECT_FALSE(parser().xref_table_rebuilt());
463 ASSERT_TRUE(parser().GetCrossRefTable());
464 const auto& objects_info = parser().GetCrossRefTable()->objects_info();
465
466 // This should be the only object from table. Subsequent objects have object
467 // numbers that are too big.
468 CPDF_CrossRefTable::ObjectInfo only_valid_object = {
470
471 // TODO(thestig): Should the xref table contain object 4194305?
472 // Consider reworking CPDF_Parser's object representation to avoid having to
473 // store this placeholder object.
474 CPDF_CrossRefTable::ObjectInfo placeholder_object = {
476
477 EXPECT_THAT(objects_info, ElementsAre(Pair(4194303, only_valid_object),
478 Pair(4194305, placeholder_object)));
479}
480
482 // 0xFF in the first object in the xref object stream is invalid.
483 const unsigned char kData[] =
484 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
485 "7 0 obj <<\n"
486 " /Filter /ASCIIHexDecode\n"
487 " /Root 1 0 R\n"
488 " /Size 3\n"
489 " /W [1 1 1]\n"
490 ">>\n"
491 "stream\n"
492 "02 FF 00\n"
493 "01 0F 00\n"
494 "01 12 00\n"
495 "endstream\n"
496 "endobj\n"
497 "startxref\n"
498 "14\n"
499 "%%EOF\n";
500 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
501 EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
502 EXPECT_FALSE(parser().xref_table_rebuilt());
503
504 const CPDF_CrossRefTable* cross_ref_table = parser().GetCrossRefTable();
505 ASSERT_TRUE(cross_ref_table);
506 EXPECT_EQ(7u, cross_ref_table->trailer_object_number());
507 const auto& objects_info = cross_ref_table->objects_info();
508
509 // The expectation is for the parser to skip over the first object, and
510 // continue parsing the remaining objects. So these are the second and third
511 // objects.
512 CPDF_CrossRefTable::ObjectInfo expected_objects[2] = {
515
516 EXPECT_THAT(objects_info, ElementsAre(Pair(1, expected_objects[0]),
517 Pair(2, expected_objects[1])));
518}
519
521 // The XRef object is a dictionary and not a stream.
522 const unsigned char kData[] =
523 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
524 "7 0 obj <<\n"
525 " /Filter /ASCIIHexDecode\n"
526 " /Root 1 0 R\n"
527 " /Size 3\n"
528 " /W [1 1 1]\n"
529 ">>\n"
530 "endobj\n"
531 "startxref\n"
532 "14\n"
533 "%%EOF\n";
534
535 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
536 EXPECT_EQ(CPDF_Parser::FORMAT_ERROR, parser().StartParseInternal());
537}
538
540 // The /Prev value is an absolute offset, so it should never be negative.
541 const unsigned char kData[] =
542 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
543 "7 0 obj <<\n"
544 " /Filter /ASCIIHexDecode\n"
545 " /Root 1 0 R\n"
546 " /Size 3\n"
547 " /W [1 1 1]\n"
548 " /Prev -1\n"
549 ">>\n"
550 "stream\n"
551 "02 FF 00\n"
552 "01 0F 00\n"
553 "01 12 00\n"
554 "endstream\n"
555 "endobj\n"
556 "startxref\n"
557 "14\n"
558 "%%EOF\n";
559
560 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
561 EXPECT_EQ(CPDF_Parser::FORMAT_ERROR, parser().StartParseInternal());
562}
563
565 // The /Size value should never be negative.
566 const unsigned char kData[] =
567 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
568 "7 0 obj <<\n"
569 " /Filter /ASCIIHexDecode\n"
570 " /Root 1 0 R\n"
571 " /Size 3\n"
572 " /W [1 1 1]\n"
573 " /Size -1\n"
574 ">>\n"
575 "stream\n"
576 "02 FF 00\n"
577 "01 0F 00\n"
578 "01 12 00\n"
579 "endstream\n"
580 "endobj\n"
581 "startxref\n"
582 "14\n"
583 "%%EOF\n";
584
585 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
586 EXPECT_EQ(CPDF_Parser::FORMAT_ERROR, parser().StartParseInternal());
587}
588
590 // The /W array needs to have at least 3 values.
591 const unsigned char kData[] =
592 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
593 "7 0 obj <<\n"
594 " /Filter /ASCIIHexDecode\n"
595 " /Root 1 0 R\n"
596 " /Size 3\n"
597 " /W [1 1]\n"
598 ">>\n"
599 "stream\n"
600 "02 FF 00\n"
601 "01 0F 00\n"
602 "01 12 00\n"
603 "endstream\n"
604 "endobj\n"
605 "startxref\n"
606 "14\n"
607 "%%EOF\n";
608
609 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
610
611 // StartParseInternal() succeeded not because XRef parsing succeeded, but
612 // because RebuildCrossRef() got lucky with the data stream. Therefore, don't
613 // bother checking the garbage output.
614 EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
615 EXPECT_TRUE(parser().xref_table_rebuilt());
616}
617
619 // When the first /W array entry is 0, it implies the objects are all of the
620 // normal type.
621 const unsigned char kData[] =
622 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
623 "7 0 obj <<\n"
624 " /Filter /ASCIIHexDecode\n"
625 " /Root 1 0 R\n"
626 " /Size 2\n"
627 " /W [0 1 1]\n"
628 ">>\n"
629 "stream\n"
630 "0F 00\n"
631 "12 00\n"
632 "endstream\n"
633 "endobj\n"
634 "startxref\n"
635 "14\n"
636 "%%EOF\n";
637
638 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
639 EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
640 EXPECT_FALSE(parser().xref_table_rebuilt());
641 ASSERT_TRUE(parser().GetCrossRefTable());
642 const auto& objects_info = parser().GetCrossRefTable()->objects_info();
643
644 CPDF_CrossRefTable::ObjectInfo expected_result[2] = {
647
648 EXPECT_THAT(objects_info, ElementsAre(Pair(0, expected_result[0]),
649 Pair(1, expected_result[1])));
650}
651
653 // The /Index specifies objects (2), (4, 5), (80, 81, 82).
654 const unsigned char kData[] =
655 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
656 "7 0 obj <<\n"
657 " /Filter /ASCIIHexDecode\n"
658 " /Root 1 0 R\n"
659 " /Size 83\n"
660 " /Index [2 1 4 2 80 3]\n"
661 " /W [1 1 1]\n"
662 ">>\n"
663 "stream\n"
664 "01 00 00\n"
665 "01 0F 00\n"
666 "01 12 00\n"
667 "01 20 00\n"
668 "01 22 00\n"
669 "01 25 00\n"
670 "endstream\n"
671 "endobj\n"
672 "startxref\n"
673 "14\n"
674 "%%EOF\n";
675
676 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
677 EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
678 EXPECT_FALSE(parser().xref_table_rebuilt());
679 ASSERT_TRUE(parser().GetCrossRefTable());
680 const auto& objects_info = parser().GetCrossRefTable()->objects_info();
681
682 CPDF_CrossRefTable::ObjectInfo expected_result[6] = {
689
690 EXPECT_THAT(
691 objects_info,
692 ElementsAre(Pair(2, expected_result[0]), Pair(4, expected_result[1]),
693 Pair(5, expected_result[2]), Pair(80, expected_result[3]),
694 Pair(81, expected_result[4]), Pair(82, expected_result[5])));
695}
696
698 // The /Index specifies objects (2, 3), (3). AKA the sub-sections overlap.
699 const unsigned char kData[] =
700 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
701 "7 0 obj <<\n"
702 " /Filter /ASCIIHexDecode\n"
703 " /Root 1 0 R\n"
704 " /Size 4\n"
705 " /Index [2 2 3 1]\n"
706 " /W [1 1 1]\n"
707 ">>\n"
708 "stream\n"
709 "01 00 00\n"
710 "01 0F 00\n"
711 "01 12 00\n"
712 "endstream\n"
713 "endobj\n"
714 "startxref\n"
715 "14\n"
716 "%%EOF\n";
717
718 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
719 EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
720 EXPECT_FALSE(parser().xref_table_rebuilt());
721 ASSERT_TRUE(parser().GetCrossRefTable());
722 const auto& objects_info = parser().GetCrossRefTable()->objects_info();
723
724 CPDF_CrossRefTable::ObjectInfo expected_result[2] = {
726 // Since the /Index does not follow the spec, this is one of the 2
727 // possible values that a parser can come up with.
729
730 EXPECT_THAT(objects_info, ElementsAre(Pair(2, expected_result[0]),
731 Pair(3, expected_result[1])));
732}
733
735 // The /Index specifies objects (3, 4), (2), which is not in ascending order.
736 const unsigned char kData[] =
737 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
738 "7 0 obj <<\n"
739 " /Filter /ASCIIHexDecode\n"
740 " /Root 1 0 R\n"
741 " /Size 5\n"
742 " /Index [3 2 2 1]\n"
743 " /W [1 1 1]\n"
744 ">>\n"
745 "stream\n"
746 "01 00 00\n"
747 "01 0F 00\n"
748 "01 12 00\n"
749 "endstream\n"
750 "endobj\n"
751 "startxref\n"
752 "14\n"
753 "%%EOF\n";
754
755 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
756 EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
757 EXPECT_FALSE(parser().xref_table_rebuilt());
758 ASSERT_TRUE(parser().GetCrossRefTable());
759 const auto& objects_info = parser().GetCrossRefTable()->objects_info();
760
761 // Although the /Index does not follow the spec, the parser tolerates it.
762 CPDF_CrossRefTable::ObjectInfo expected_result[3] = {
766
767 EXPECT_THAT(objects_info, ElementsAre(Pair(2, expected_result[0]),
768 Pair(3, expected_result[1]),
769 Pair(4, expected_result[2])));
770}
771
773 // The /Index specifies objects (2), (80, 81), so the /Size should be 82,
774 // but is actually 81.
775 const unsigned char kData[] =
776 "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
777 "7 0 obj <<\n"
778 " /Filter /ASCIIHexDecode\n"
779 " /Root 1 0 R\n"
780 " /Size 81\n"
781 " /Index [2 1 80 2]\n"
782 " /W [1 1 1]\n"
783 ">>\n"
784 "stream\n"
785 "01 00 00\n"
786 "01 0F 00\n"
787 "01 12 00\n"
788 "endstream\n"
789 "endobj\n"
790 "startxref\n"
791 "14\n"
792 "%%EOF\n";
793
794 ASSERT_TRUE(parser().InitTestFromBuffer(kData));
795 EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
796 EXPECT_FALSE(parser().xref_table_rebuilt());
797 ASSERT_TRUE(parser().GetCrossRefTable());
798 const auto& objects_info = parser().GetCrossRefTable()->objects_info();
799
800 const CPDF_CrossRefTable::ObjectInfo expected_result[3] = {
804
805 EXPECT_THAT(objects_info, ElementsAre(Pair(2, expected_result[0]),
806 Pair(80, expected_result[1]),
807 Pair(81, expected_result[2])));
808}
const ObjectInfo * GetObjectInfo(uint32_t obj_num) const
uint32_t trailer_object_number() const
bool RebuildCrossRef()
static constexpr uint32_t kMaxObjectNumber
Definition cpdf_parser.h:57
Error StartParseInternal()
const CPDF_CrossRefTable * GetCrossRefTable() const
FX_FILESIZE ParseStartXRef()
bool LoadCrossRefV4(FX_FILESIZE pos, bool bSkip)
bool InitTestFromBufferWithOffset(pdfium::span< const uint8_t > buffer, FX_FILESIZE header_offset)
bool InitTestFromFile(const char *path)
bool InitTestFromBuffer(pdfium::span< const uint8_t > buffer)
TestObjectsHolder & object_holder()
~CPDF_TestParser()=default
~ParserXRefTest() override=default
CPDF_TestParser & parser()
ParserXRefTest()=default
static std::string GetTestFilePath(const std::string &file_name)
bool operator==(const CPDF_CrossRefTable::ObjectInfo &lhs, const CPDF_CrossRefTable::ObjectInfo &rhs)
TEST_F(ParserXRefTest, XrefObjectIndicesTooBig)
TEST(FXCRYPT, MD5GenerateEmtpyData)
#define FX_FILESIZE
Definition fx_types.h:19