Commit 46ac3e21b362a8d5c7fd45c9c0384a52fc86fb9e
Committed by
Jay Berkenbilt
1 parent
06b818dc
Add QPDF::getXRefTable()
Showing
7 changed files
with
142 additions
and
1 deletions
include/qpdf/QPDF.hh
| ... | ... | @@ -239,6 +239,8 @@ class QPDF |
| 239 | 239 | QPDFObjectHandle getTrailer(); |
| 240 | 240 | QPDF_DLL |
| 241 | 241 | QPDFObjectHandle getRoot(); |
| 242 | + QPDF_DLL | |
| 243 | + std::map<QPDFObjGen, QPDFXRefEntry> getXRefTable(); | |
| 242 | 244 | |
| 243 | 245 | // Install this object handle as an indirect object and return an |
| 244 | 246 | // indirect reference to it. |
| ... | ... | @@ -1400,6 +1402,7 @@ class QPDF |
| 1400 | 1402 | bool fixed_dangling_refs; |
| 1401 | 1403 | bool immediate_copy_from; |
| 1402 | 1404 | bool in_parse; |
| 1405 | + bool parsed; | |
| 1403 | 1406 | |
| 1404 | 1407 | // Linearization data |
| 1405 | 1408 | qpdf_offset_t first_xref_item_offset; // actual value from file | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -155,6 +155,7 @@ QPDF::Members::Members() : |
| 155 | 155 | fixed_dangling_refs(false), |
| 156 | 156 | immediate_copy_from(false), |
| 157 | 157 | in_parse(false), |
| 158 | + parsed(false), | |
| 158 | 159 | first_xref_item_offset(0), |
| 159 | 160 | uncompressed_after_compressed(false) |
| 160 | 161 | { |
| ... | ... | @@ -431,6 +432,7 @@ QPDF::parse(char const* password) |
| 431 | 432 | |
| 432 | 433 | initializeEncryption(); |
| 433 | 434 | findAttachmentStreams(); |
| 435 | + this->m->parsed = true; | |
| 434 | 436 | } |
| 435 | 437 | |
| 436 | 438 | void |
| ... | ... | @@ -2620,6 +2622,17 @@ QPDF::getRoot() |
| 2620 | 2622 | return root; |
| 2621 | 2623 | } |
| 2622 | 2624 | |
| 2625 | +std::map<QPDFObjGen, QPDFXRefEntry> | |
| 2626 | +QPDF::getXRefTable() | |
| 2627 | +{ | |
| 2628 | + if (! this->m->parsed) | |
| 2629 | + { | |
| 2630 | + throw std::logic_error("QPDF::getXRefTable called before parsing."); | |
| 2631 | + } | |
| 2632 | + | |
| 2633 | + return this->m->xref_table; | |
| 2634 | +} | |
| 2635 | + | |
| 2623 | 2636 | void |
| 2624 | 2637 | QPDF::getObjectStreamData(std::map<int, int>& omap) |
| 2625 | 2638 | { | ... | ... |
qpdf/build.mk
| ... | ... | @@ -6,7 +6,8 @@ BINS_qpdf = \ |
| 6 | 6 | test_pdf_doc_encoding \ |
| 7 | 7 | test_pdf_unicode \ |
| 8 | 8 | test_tokenizer \ |
| 9 | - test_unicode_filenames | |
| 9 | + test_unicode_filenames \ | |
| 10 | + test_xref | |
| 10 | 11 | CBINS_qpdf = qpdf-ctest |
| 11 | 12 | |
| 12 | 13 | TARGETS_qpdf = $(foreach B,$(BINS_qpdf) $(CBINS_qpdf),qpdf/$(OUTPUT_DIR)/$(call binname,$(B))) | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -4019,6 +4019,23 @@ foreach my $i (qw(preserve disable generate)) |
| 4019 | 4019 | |
| 4020 | 4020 | show_ntests(); |
| 4021 | 4021 | # ---------- |
| 4022 | +$td->notify("--- Get XRef Table ---"); | |
| 4023 | +$n_tests += 2; | |
| 4024 | + | |
| 4025 | +$td->runtest("without object streams", | |
| 4026 | + {$td->COMMAND => "test_xref minimal.pdf"}, | |
| 4027 | + {$td->FILE => "minimal-xref.out", | |
| 4028 | + $td->EXIT_STATUS => 0}, | |
| 4029 | + $td->NORMALIZE_NEWLINES); | |
| 4030 | + | |
| 4031 | +$td->runtest("with object streams", | |
| 4032 | + {$td->COMMAND => "test_xref digitally-signed.pdf"}, | |
| 4033 | + {$td->FILE => "digitally-signed-xref.out", | |
| 4034 | + $td->EXIT_STATUS => 0}, | |
| 4035 | + $td->NORMALIZE_NEWLINES); | |
| 4036 | + | |
| 4037 | +show_ntests(); | |
| 4038 | +# ---------- | |
| 4022 | 4039 | $td->notify("--- Large File Tests ---"); |
| 4023 | 4040 | my $nlarge = 1; |
| 4024 | 4041 | if (defined $large_file_test_path) | ... | ... |
qpdf/qtest/qpdf/digitally-signed-xref.out
0 โ 100644
| 1 | +1/0, uncompressed, offset = 64450 (0xfbc2) | |
| 2 | +2/0, uncompressed, offset = 65177 (0xfe99) | |
| 3 | +3/0, uncompressed, offset = 68242 (0x10a92) | |
| 4 | +4/0, uncompressed, offset = 68383 (0x10b1f) | |
| 5 | +5/0, uncompressed, offset = 68538 (0x10bba) | |
| 6 | +6/0, compressed, stream number = 1, stream index = 0 | |
| 7 | +7/0, compressed, stream number = 1, stream index = 1 | |
| 8 | +8/0, compressed, stream number = 1, stream index = 2 | |
| 9 | +9/0, compressed, stream number = 3, stream index = 0 | |
| 10 | +10/0, compressed, stream number = 4, stream index = 0 | |
| 11 | +11/0, uncompressed, offset = 16 (0x10) | |
| 12 | +12/0, uncompressed, offset = 649 (0x289) | |
| 13 | +13/0, uncompressed, offset = 726 (0x2d6) | |
| 14 | +14/0, uncompressed, offset = 900 (0x384) | |
| 15 | +15/0, uncompressed, offset = 1049 (0x419) | |
| 16 | +16/0, uncompressed, offset = 1256 (0x4e8) | |
| 17 | +17/0, uncompressed, offset = 1382 (0x566) | |
| 18 | +18/0, uncompressed, offset = 2411 (0x96b) | |
| 19 | +19/0, uncompressed, offset = 57705 (0xe169) | |
| 20 | +20/0, uncompressed, offset = 59412 (0xe814) | |
| 21 | +21/0, uncompressed, offset = 59506 (0xe872) | |
| 22 | +22/0, uncompressed, offset = 116 (0x74) | |
| 23 | +23/0, compressed, stream number = 19, stream index = 0 | |
| 24 | +24/0, compressed, stream number = 19, stream index = 1 | |
| 25 | +25/0, compressed, stream number = 19, stream index = 2 | |
| 26 | +26/0, compressed, stream number = 19, stream index = 3 | |
| 27 | +27/0, compressed, stream number = 19, stream index = 4 | |
| 28 | +28/0, compressed, stream number = 19, stream index = 5 | |
| 29 | +29/0, compressed, stream number = 19, stream index = 6 | |
| 30 | +30/0, uncompressed, offset = 471 (0x1d7) | ... | ... |
qpdf/qtest/qpdf/minimal-xref.out
0 โ 100644
qpdf/test_xref.cc
0 โ 100644
| 1 | +#include <qpdf/QPDF.hh> | |
| 2 | +#include <qpdf/QPDFObjGen.hh> | |
| 3 | +#include <qpdf/QPDFXRefEntry.hh> | |
| 4 | + | |
| 5 | +#include <iostream> | |
| 6 | +#include <map> | |
| 7 | +#include <string> | |
| 8 | +#include <cstdlib> | |
| 9 | + | |
| 10 | +int main(int argc, char *argv[]) | |
| 11 | +{ | |
| 12 | + if (argc != 2) | |
| 13 | + { | |
| 14 | + std::cerr << "usage: test_xref INPUT.pdf" << std::endl; | |
| 15 | + std::exit(2); | |
| 16 | + } | |
| 17 | + | |
| 18 | + try | |
| 19 | + { | |
| 20 | + QPDF qpdf; | |
| 21 | + qpdf.processFile(argv[1]); | |
| 22 | + | |
| 23 | + std::map<QPDFObjGen, QPDFXRefEntry> xref | |
| 24 | + = qpdf.getXRefTable(); | |
| 25 | + | |
| 26 | + for (std::map<QPDFObjGen, QPDFXRefEntry>::iterator iter = xref.begin(); | |
| 27 | + iter != xref.end(); ++iter) | |
| 28 | + { | |
| 29 | + std::cout | |
| 30 | + << iter->first.getObj() << "/" << iter->first.getGen() | |
| 31 | + << ", "; | |
| 32 | + switch (iter->second.getType()) | |
| 33 | + { | |
| 34 | + case 0: | |
| 35 | + std::cout | |
| 36 | + << "free entry" | |
| 37 | + << std::endl; | |
| 38 | + break; | |
| 39 | + case 1: | |
| 40 | + std::cout | |
| 41 | + << "uncompressed, offset = " | |
| 42 | + << iter->second.getOffset() | |
| 43 | + << " (0x" | |
| 44 | + << std::hex << iter->second.getOffset() << std::dec | |
| 45 | + << ")" | |
| 46 | + << std::endl; | |
| 47 | + break; | |
| 48 | + case 2: | |
| 49 | + std::cout | |
| 50 | + << "compressed, stream number = " | |
| 51 | + << iter->second.getObjStreamNumber() | |
| 52 | + << ", stream index = " | |
| 53 | + << iter->second.getObjStreamIndex() | |
| 54 | + << std::endl; | |
| 55 | + break; | |
| 56 | + default: | |
| 57 | + std::cerr | |
| 58 | + << "unknown" | |
| 59 | + << std::endl; | |
| 60 | + std::exit(2); | |
| 61 | + } | |
| 62 | + } | |
| 63 | + } | |
| 64 | + catch (std::exception& e) | |
| 65 | + { | |
| 66 | + std::cerr << e.what() << std::endl; | |
| 67 | + std::exit(2); | |
| 68 | + } | |
| 69 | + | |
| 70 | + return 0; | |
| 71 | +} | ... | ... |