Commit b5a5780019a5b75465671a23f55772a20b007115
1 parent
a3f693c8
Make Xref_table an inner class of QPDF::Objects
Showing
9 changed files
with
522 additions
and
489 deletions
include/qpdf/QPDF.hh
| @@ -734,7 +734,6 @@ class QPDF | @@ -734,7 +734,6 @@ class QPDF | ||
| 734 | class ParseGuard; | 734 | class ParseGuard; |
| 735 | class Pipe; | 735 | class Pipe; |
| 736 | class JobSetter; | 736 | class JobSetter; |
| 737 | - class Xref_table; | ||
| 738 | 737 | ||
| 739 | // For testing only -- do not add to DLL | 738 | // For testing only -- do not add to DLL |
| 740 | static bool test_json_validators(); | 739 | static bool test_json_validators(); |
| @@ -811,7 +810,7 @@ class QPDF | @@ -811,7 +810,7 @@ class QPDF | ||
| 811 | void optimize( | 810 | void optimize( |
| 812 | QPDFWriter::ObjTable const& obj, | 811 | QPDFWriter::ObjTable const& obj, |
| 813 | std::function<int(QPDFObjectHandle&)> skip_stream_parameters); | 812 | std::function<int(QPDFObjectHandle&)> skip_stream_parameters); |
| 814 | - void optimize(Xref_table const& obj); | 813 | + void optimize(Objects const& obj); |
| 815 | 814 | ||
| 816 | // Get lists of all objects in order according to the part of a linearized file that they belong | 815 | // Get lists of all objects in order according to the part of a linearized file that they belong |
| 817 | // to. | 816 | // to. |
| @@ -904,7 +903,7 @@ class QPDF | @@ -904,7 +903,7 @@ class QPDF | ||
| 904 | QPDFObjectHandle | 903 | QPDFObjectHandle |
| 905 | getUncompressedObject(QPDFObjectHandle&, std::map<int, int> const& object_stream_data); | 904 | getUncompressedObject(QPDFObjectHandle&, std::map<int, int> const& object_stream_data); |
| 906 | QPDFObjectHandle getUncompressedObject(QPDFObjectHandle&, QPDFWriter::ObjTable const& obj); | 905 | QPDFObjectHandle getUncompressedObject(QPDFObjectHandle&, QPDFWriter::ObjTable const& obj); |
| 907 | - QPDFObjectHandle getUncompressedObject(QPDFObjectHandle&, Xref_table const& obj); | 906 | + QPDFObjectHandle getUncompressedObject(QPDFObjectHandle&, Objects const& obj); |
| 908 | int lengthNextN(int first_object, int n); | 907 | int lengthNextN(int first_object, int n); |
| 909 | void | 908 | void |
| 910 | checkHPageOffset(std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj); | 909 | checkHPageOffset(std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj); |
| @@ -950,7 +949,7 @@ class QPDF | @@ -950,7 +949,7 @@ class QPDF | ||
| 950 | std::function<int(QPDFObjectHandle&)> skip_stream_parameters); | 949 | std::function<int(QPDFObjectHandle&)> skip_stream_parameters); |
| 951 | void filterCompressedObjects(std::map<int, int> const& object_stream_data); | 950 | void filterCompressedObjects(std::map<int, int> const& object_stream_data); |
| 952 | void filterCompressedObjects(QPDFWriter::ObjTable const& object_stream_data); | 951 | void filterCompressedObjects(QPDFWriter::ObjTable const& object_stream_data); |
| 953 | - void filterCompressedObjects(Xref_table const& object_stream_data); | 952 | + void filterCompressedObjects(Objects const& object_stream_data); |
| 954 | 953 | ||
| 955 | // JSON import | 954 | // JSON import |
| 956 | void importJSON(std::shared_ptr<InputSource>, bool must_be_complete); | 955 | void importJSON(std::shared_ptr<InputSource>, bool must_be_complete); |
libqpdf/QPDF.cc
| @@ -185,8 +185,7 @@ QPDF::Members::Members(QPDF& qpdf) : | @@ -185,8 +185,7 @@ QPDF::Members::Members(QPDF& qpdf) : | ||
| 185 | file_sp(new InvalidInputSource(no_input_name)), | 185 | file_sp(new InvalidInputSource(no_input_name)), |
| 186 | file(file_sp.get()), | 186 | file(file_sp.get()), |
| 187 | encp(new EncryptionParameters), | 187 | encp(new EncryptionParameters), |
| 188 | - objects(qpdf, this), | ||
| 189 | - xref_table(qpdf, objects, file) | 188 | + objects(qpdf, this, file) |
| 190 | { | 189 | { |
| 191 | } | 190 | } |
| 192 | 191 | ||
| @@ -279,7 +278,7 @@ QPDF::emptyPDF() | @@ -279,7 +278,7 @@ QPDF::emptyPDF() | ||
| 279 | { | 278 | { |
| 280 | m->pdf_version = "1.3"; | 279 | m->pdf_version = "1.3"; |
| 281 | m->no_input_name = "empty PDF"; | 280 | m->no_input_name = "empty PDF"; |
| 282 | - m->xref_table.initialize_empty(); | 281 | + m->objects.xref_table().initialize_empty(); |
| 283 | } | 282 | } |
| 284 | 283 | ||
| 285 | void | 284 | void |
| @@ -292,7 +291,7 @@ QPDF::registerStreamFilter( | @@ -292,7 +291,7 @@ QPDF::registerStreamFilter( | ||
| 292 | void | 291 | void |
| 293 | QPDF::setIgnoreXRefStreams(bool val) | 292 | QPDF::setIgnoreXRefStreams(bool val) |
| 294 | { | 293 | { |
| 295 | - m->xref_table.ignore_streams(val); | 294 | + m->objects.xref_table().ignore_streams(val); |
| 296 | } | 295 | } |
| 297 | 296 | ||
| 298 | std::shared_ptr<QPDFLogger> | 297 | std::shared_ptr<QPDFLogger> |
| @@ -330,7 +329,7 @@ void | @@ -330,7 +329,7 @@ void | ||
| 330 | QPDF::setAttemptRecovery(bool val) | 329 | QPDF::setAttemptRecovery(bool val) |
| 331 | { | 330 | { |
| 332 | m->attempt_recovery = val; | 331 | m->attempt_recovery = val; |
| 333 | - m->xref_table.attempt_recovery(val); | 332 | + m->objects.xref_table().attempt_recovery(val); |
| 334 | } | 333 | } |
| 335 | 334 | ||
| 336 | void | 335 | void |
| @@ -424,9 +423,9 @@ QPDF::parse(char const* password) | @@ -424,9 +423,9 @@ QPDF::parse(char const* password) | ||
| 424 | m->pdf_version = "1.2"; | 423 | m->pdf_version = "1.2"; |
| 425 | } | 424 | } |
| 426 | 425 | ||
| 427 | - m->xref_table.initialize(); | 426 | + m->objects.xref_table().initialize(); |
| 428 | initializeEncryption(); | 427 | initializeEncryption(); |
| 429 | - if (m->xref_table.size() > 0 && !getRoot().getKey("/Pages").isDictionary()) { | 428 | + if (m->objects.xref_table().size() > 0 && !getRoot().getKey("/Pages").isDictionary()) { |
| 430 | // QPDFs created from JSON have an empty xref table and no root object yet. | 429 | // QPDFs created from JSON have an empty xref table and no root object yet. |
| 431 | throw damagedPDF("", 0, "unable to find page tree"); | 430 | throw damagedPDF("", 0, "unable to find page tree"); |
| 432 | } | 431 | } |
| @@ -469,7 +468,7 @@ QPDF::warn( | @@ -469,7 +468,7 @@ QPDF::warn( | ||
| 469 | void | 468 | void |
| 470 | QPDF::showXRefTable() | 469 | QPDF::showXRefTable() |
| 471 | { | 470 | { |
| 472 | - m->xref_table.show(); | 471 | + m->objects.xref_table().show(); |
| 473 | } | 472 | } |
| 474 | 473 | ||
| 475 | // Ensure all objects in the pdf file, including those in indirect references, appear in the object | 474 | // Ensure all objects in the pdf file, including those in indirect references, appear in the object |
| @@ -480,9 +479,9 @@ QPDF::fixDanglingReferences(bool force) | @@ -480,9 +479,9 @@ QPDF::fixDanglingReferences(bool force) | ||
| 480 | if (m->fixed_dangling_refs) { | 479 | if (m->fixed_dangling_refs) { |
| 481 | return; | 480 | return; |
| 482 | } | 481 | } |
| 483 | - if (!m->xref_table.resolve()) { | 482 | + if (!m->objects.xref_table().resolve()) { |
| 484 | QTC::TC("qpdf", "QPDF fix dangling triggered xref reconstruction"); | 483 | QTC::TC("qpdf", "QPDF fix dangling triggered xref reconstruction"); |
| 485 | - m->xref_table.resolve(); | 484 | + m->objects.xref_table().resolve(); |
| 486 | } | 485 | } |
| 487 | m->fixed_dangling_refs = true; | 486 | m->fixed_dangling_refs = true; |
| 488 | } | 487 | } |
| @@ -578,7 +577,7 @@ QPDF::getObject(QPDFObjGen const& og) | @@ -578,7 +577,7 @@ QPDF::getObject(QPDFObjGen const& og) | ||
| 578 | { | 577 | { |
| 579 | if (auto it = m->objects.obj_cache.find(og); it != m->objects.obj_cache.end()) { | 578 | if (auto it = m->objects.obj_cache.find(og); it != m->objects.obj_cache.end()) { |
| 580 | return {it->second.object}; | 579 | return {it->second.object}; |
| 581 | - } else if (m->xref_table.initialized() && !m->xref_table.type(og)) { | 580 | + } else if (m->objects.xref_table().initialized() && !m->objects.xref_table().type(og)) { |
| 582 | return QPDF_Null::create(); | 581 | return QPDF_Null::create(); |
| 583 | } else { | 582 | } else { |
| 584 | auto result = m->objects.obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og)); | 583 | auto result = m->objects.obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og)); |
| @@ -945,13 +944,13 @@ QPDF::getExtensionLevel() | @@ -945,13 +944,13 @@ QPDF::getExtensionLevel() | ||
| 945 | QPDFObjectHandle | 944 | QPDFObjectHandle |
| 946 | QPDF::getTrailer() | 945 | QPDF::getTrailer() |
| 947 | { | 946 | { |
| 948 | - return m->xref_table.trailer(); | 947 | + return m->objects.trailer(); |
| 949 | } | 948 | } |
| 950 | 949 | ||
| 951 | QPDFObjectHandle | 950 | QPDFObjectHandle |
| 952 | QPDF::getRoot() | 951 | QPDF::getRoot() |
| 953 | { | 952 | { |
| 954 | - QPDFObjectHandle root = m->xref_table.trailer().getKey("/Root"); | 953 | + auto root = m->objects.trailer().getKey("/Root"); |
| 955 | if (!root.isDictionary()) { | 954 | if (!root.isDictionary()) { |
| 956 | throw damagedPDF("", 0, "unable to find /Root dictionary"); | 955 | throw damagedPDF("", 0, "unable to find /Root dictionary"); |
| 957 | } else if ( | 956 | } else if ( |
| @@ -967,10 +966,10 @@ QPDF::getRoot() | @@ -967,10 +966,10 @@ QPDF::getRoot() | ||
| 967 | std::map<QPDFObjGen, QPDFXRefEntry> | 966 | std::map<QPDFObjGen, QPDFXRefEntry> |
| 968 | QPDF::getXRefTable() | 967 | QPDF::getXRefTable() |
| 969 | { | 968 | { |
| 970 | - if (!m->xref_table.initialized()) { | 969 | + if (!m->objects.xref_table().initialized()) { |
| 971 | throw std::logic_error("QPDF::getXRefTable called before parsing."); | 970 | throw std::logic_error("QPDF::getXRefTable called before parsing."); |
| 972 | } | 971 | } |
| 973 | - return m->xref_table.as_map(); | 972 | + return m->objects.xref_table().as_map(); |
| 974 | } | 973 | } |
| 975 | 974 | ||
| 976 | bool | 975 | bool |
libqpdf/QPDF_encryption.cc
| @@ -727,7 +727,7 @@ QPDF::initializeEncryption() | @@ -727,7 +727,7 @@ QPDF::initializeEncryption() | ||
| 727 | // at /Encrypt again. Otherwise, things could go wrong if someone mutates the encryption | 727 | // at /Encrypt again. Otherwise, things could go wrong if someone mutates the encryption |
| 728 | // dictionary. | 728 | // dictionary. |
| 729 | 729 | ||
| 730 | - if (!m->xref_table.trailer().hasKey("/Encrypt")) { | 730 | + if (!m->objects.trailer().hasKey("/Encrypt")) { |
| 731 | return; | 731 | return; |
| 732 | } | 732 | } |
| 733 | 733 | ||
| @@ -736,7 +736,7 @@ QPDF::initializeEncryption() | @@ -736,7 +736,7 @@ QPDF::initializeEncryption() | ||
| 736 | m->encp->encrypted = true; | 736 | m->encp->encrypted = true; |
| 737 | 737 | ||
| 738 | std::string id1; | 738 | std::string id1; |
| 739 | - QPDFObjectHandle id_obj = m->xref_table.trailer().getKey("/ID"); | 739 | + QPDFObjectHandle id_obj = m->objects.trailer().getKey("/ID"); |
| 740 | if ((id_obj.isArray() && (id_obj.getArrayNItems() == 2) && id_obj.getArrayItem(0).isString())) { | 740 | if ((id_obj.isArray() && (id_obj.getArrayNItems() == 2) && id_obj.getArrayItem(0).isString())) { |
| 741 | id1 = id_obj.getArrayItem(0).getStringValue(); | 741 | id1 = id_obj.getArrayItem(0).getStringValue(); |
| 742 | } else { | 742 | } else { |
| @@ -745,7 +745,7 @@ QPDF::initializeEncryption() | @@ -745,7 +745,7 @@ QPDF::initializeEncryption() | ||
| 745 | warn(damagedPDF("trailer", "invalid /ID in trailer dictionary")); | 745 | warn(damagedPDF("trailer", "invalid /ID in trailer dictionary")); |
| 746 | } | 746 | } |
| 747 | 747 | ||
| 748 | - QPDFObjectHandle encryption_dict = m->xref_table.trailer().getKey("/Encrypt"); | 748 | + QPDFObjectHandle encryption_dict = m->objects.trailer().getKey("/Encrypt"); |
| 749 | if (!encryption_dict.isDictionary()) { | 749 | if (!encryption_dict.isDictionary()) { |
| 750 | throw damagedPDF("/Encrypt in trailer dictionary is not a dictionary"); | 750 | throw damagedPDF("/Encrypt in trailer dictionary is not a dictionary"); |
| 751 | } | 751 | } |
libqpdf/QPDF_json.cc
| @@ -582,7 +582,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value) | @@ -582,7 +582,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value) | ||
| 582 | this->saw_value = true; | 582 | this->saw_value = true; |
| 583 | // The trailer must be a dictionary, so we can use setNextStateIfDictionary. | 583 | // The trailer must be a dictionary, so we can use setNextStateIfDictionary. |
| 584 | if (setNextStateIfDictionary("trailer.value", value, st_object)) { | 584 | if (setNextStateIfDictionary("trailer.value", value, st_object)) { |
| 585 | - pdf.m->xref_table.trailer(makeObject(value)); | 585 | + pdf.m->objects.xref_table().trailer(makeObject(value)); |
| 586 | } | 586 | } |
| 587 | } else if (key == "stream") { | 587 | } else if (key == "stream") { |
| 588 | // Don't need to set saw_stream here since there's already an error. | 588 | // Don't need to set saw_stream here since there's already an error. |
| @@ -776,7 +776,7 @@ QPDF::createFromJSON(std::shared_ptr<InputSource> is) | @@ -776,7 +776,7 @@ QPDF::createFromJSON(std::shared_ptr<InputSource> is) | ||
| 776 | { | 776 | { |
| 777 | m->pdf_version = "1.3"; | 777 | m->pdf_version = "1.3"; |
| 778 | m->no_input_name = is->getName(); | 778 | m->no_input_name = is->getName(); |
| 779 | - m->xref_table.initialize_json(); | 779 | + m->objects.xref_table().initialize_json(); |
| 780 | importJSON(is, true); | 780 | importJSON(is, true); |
| 781 | } | 781 | } |
| 782 | 782 |
libqpdf/QPDF_linearization.cc
| @@ -288,8 +288,8 @@ QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) | @@ -288,8 +288,8 @@ QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) | ||
| 288 | QPDFObjGen og; | 288 | QPDFObjGen og; |
| 289 | QPDFObjectHandle H = | 289 | QPDFObjectHandle H = |
| 290 | objects().read(false, offset, "linearization hint stream", QPDFObjGen(0, 0), og, false); | 290 | objects().read(false, offset, "linearization hint stream", QPDFObjGen(0, 0), og, false); |
| 291 | - qpdf_offset_t min_end_offset = m->xref_table.end_before_space(og); | ||
| 292 | - qpdf_offset_t max_end_offset = m->xref_table.end_after_space(og); | 291 | + qpdf_offset_t min_end_offset = m->objects.xref_table().end_before_space(og); |
| 292 | + qpdf_offset_t max_end_offset = m->objects.xref_table().end_after_space(og); | ||
| 293 | if (!H.isStream()) { | 293 | if (!H.isStream()) { |
| 294 | throw damagedPDF("linearization dictionary", "hint table is not a stream"); | 294 | throw damagedPDF("linearization dictionary", "hint table is not a stream"); |
| 295 | } | 295 | } |
| @@ -303,8 +303,8 @@ QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) | @@ -303,8 +303,8 @@ QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) | ||
| 303 | auto length_og = Hdict.getKey("/Length").getObjGen(); | 303 | auto length_og = Hdict.getKey("/Length").getObjGen(); |
| 304 | if (length_og.isIndirect()) { | 304 | if (length_og.isIndirect()) { |
| 305 | QTC::TC("qpdf", "QPDF hint table length indirect"); | 305 | QTC::TC("qpdf", "QPDF hint table length indirect"); |
| 306 | - min_end_offset = m->xref_table.end_before_space(length_og); | ||
| 307 | - max_end_offset = m->xref_table.end_after_space(length_og); | 306 | + min_end_offset = m->objects.xref_table().end_before_space(length_og); |
| 307 | + max_end_offset = m->objects.xref_table().end_after_space(length_og); | ||
| 308 | } else { | 308 | } else { |
| 309 | QTC::TC("qpdf", "QPDF hint table length direct"); | 309 | QTC::TC("qpdf", "QPDF hint table length direct"); |
| 310 | } | 310 | } |
| @@ -441,7 +441,7 @@ QPDF::checkLinearizationInternal() | @@ -441,7 +441,7 @@ QPDF::checkLinearizationInternal() | ||
| 441 | for (size_t i = 0; i < toS(npages); ++i) { | 441 | for (size_t i = 0; i < toS(npages); ++i) { |
| 442 | QPDFObjectHandle const& page = pages.at(i); | 442 | QPDFObjectHandle const& page = pages.at(i); |
| 443 | QPDFObjGen og(page.getObjGen()); | 443 | QPDFObjGen og(page.getObjGen()); |
| 444 | - if (m->xref_table.type(og) == 2) { | 444 | + if (m->objects.xref_table().type(og) == 2) { |
| 445 | linearizationWarning( | 445 | linearizationWarning( |
| 446 | "page dictionary for page " + std::to_string(i) + " is compressed"); | 446 | "page dictionary for page " + std::to_string(i) + " is compressed"); |
| 447 | } | 447 | } |
| @@ -457,11 +457,11 @@ QPDF::checkLinearizationInternal() | @@ -457,11 +457,11 @@ QPDF::checkLinearizationInternal() | ||
| 457 | break; | 457 | break; |
| 458 | } | 458 | } |
| 459 | } | 459 | } |
| 460 | - if (m->file->tell() != m->xref_table.first_item_offset()) { | 460 | + if (m->file->tell() != m->objects.xref_table().first_item_offset()) { |
| 461 | QTC::TC("qpdf", "QPDF err /T mismatch"); | 461 | QTC::TC("qpdf", "QPDF err /T mismatch"); |
| 462 | linearizationWarning( | 462 | linearizationWarning( |
| 463 | "space before first xref item (/T) mismatch (computed = " + | 463 | "space before first xref item (/T) mismatch (computed = " + |
| 464 | - std::to_string(m->xref_table.first_item_offset()) + | 464 | + std::to_string(m->objects.xref_table().first_item_offset()) + |
| 465 | "; file = " + std::to_string(m->file->tell())); | 465 | "; file = " + std::to_string(m->file->tell())); |
| 466 | } | 466 | } |
| 467 | 467 | ||
| @@ -472,7 +472,7 @@ QPDF::checkLinearizationInternal() | @@ -472,7 +472,7 @@ QPDF::checkLinearizationInternal() | ||
| 472 | // compressed objects are supposed to be at the end of the containing xref section if any object | 472 | // compressed objects are supposed to be at the end of the containing xref section if any object |
| 473 | // streams are in use. | 473 | // streams are in use. |
| 474 | 474 | ||
| 475 | - if (m->xref_table.uncompressed_after_compressed()) { | 475 | + if (m->objects.xref_table().uncompressed_after_compressed()) { |
| 476 | linearizationWarning("linearized file contains an uncompressed object after a compressed " | 476 | linearizationWarning("linearized file contains an uncompressed object after a compressed " |
| 477 | "one in a cross-reference stream"); | 477 | "one in a cross-reference stream"); |
| 478 | } | 478 | } |
| @@ -481,8 +481,8 @@ QPDF::checkLinearizationInternal() | @@ -481,8 +481,8 @@ QPDF::checkLinearizationInternal() | ||
| 481 | // make changes. If it has to, then the file is not properly linearized. We use the xref table | 481 | // make changes. If it has to, then the file is not properly linearized. We use the xref table |
| 482 | // to figure out which objects are compressed and which are uncompressed. | 482 | // to figure out which objects are compressed and which are uncompressed. |
| 483 | 483 | ||
| 484 | - optimize(m->xref_table); | ||
| 485 | - calculateLinearizationData(m->xref_table); | 484 | + optimize(m->objects); |
| 485 | + calculateLinearizationData(m->objects); | ||
| 486 | 486 | ||
| 487 | // E: offset of end of first page -- Implementation note 123 says Acrobat includes on extra | 487 | // E: offset of end of first page -- Implementation note 123 says Acrobat includes on extra |
| 488 | // object here by mistake. pdlin fails to place thumbnail images in section 9, so when | 488 | // object here by mistake. pdlin fails to place thumbnail images in section 9, so when |
| @@ -499,8 +499,8 @@ QPDF::checkLinearizationInternal() | @@ -499,8 +499,8 @@ QPDF::checkLinearizationInternal() | ||
| 499 | qpdf_offset_t max_E = -1; | 499 | qpdf_offset_t max_E = -1; |
| 500 | for (auto const& oh: m->part6) { | 500 | for (auto const& oh: m->part6) { |
| 501 | QPDFObjGen og(oh.getObjGen()); | 501 | QPDFObjGen og(oh.getObjGen()); |
| 502 | - auto before = m->xref_table.end_before_space(og); | ||
| 503 | - auto after = m->xref_table.end_after_space(og); | 502 | + auto before = m->objects.xref_table().end_before_space(og); |
| 503 | + auto after = m->objects.xref_table().end_after_space(og); | ||
| 504 | if (before <= 0) { | 504 | if (before <= 0) { |
| 505 | // All objects have to have been dereferenced to be classified. | 505 | // All objects have to have been dereferenced to be classified. |
| 506 | throw std::logic_error("linearization part6 object not in cache"); | 506 | throw std::logic_error("linearization part6 object not in cache"); |
| @@ -533,7 +533,7 @@ QPDF::maxEnd(ObjUser const& ou) | @@ -533,7 +533,7 @@ QPDF::maxEnd(ObjUser const& ou) | ||
| 533 | } | 533 | } |
| 534 | qpdf_offset_t end = 0; | 534 | qpdf_offset_t end = 0; |
| 535 | for (auto const& og: m->obj_user_to_objects[ou]) { | 535 | for (auto const& og: m->obj_user_to_objects[ou]) { |
| 536 | - auto e = m->xref_table.end_after_space(og); | 536 | + auto e = m->objects.xref_table().end_after_space(og); |
| 537 | if (e <= 0) { | 537 | if (e <= 0) { |
| 538 | stopOnError("unknown object referenced in object user table"); | 538 | stopOnError("unknown object referenced in object user table"); |
| 539 | } | 539 | } |
| @@ -545,13 +545,14 @@ QPDF::maxEnd(ObjUser const& ou) | @@ -545,13 +545,14 @@ QPDF::maxEnd(ObjUser const& ou) | ||
| 545 | qpdf_offset_t | 545 | qpdf_offset_t |
| 546 | QPDF::getLinearizationOffset(QPDFObjGen const& og) | 546 | QPDF::getLinearizationOffset(QPDFObjGen const& og) |
| 547 | { | 547 | { |
| 548 | - switch (m->xref_table.type(og)) { | 548 | + switch (m->objects.xref_table().type(og)) { |
| 549 | case 1: | 549 | case 1: |
| 550 | - return m->xref_table.offset(og); | 550 | + return m->objects.xref_table().offset(og); |
| 551 | 551 | ||
| 552 | case 2: | 552 | case 2: |
| 553 | // For compressed objects, return the offset of the object stream that contains them. | 553 | // For compressed objects, return the offset of the object stream that contains them. |
| 554 | - return getLinearizationOffset(QPDFObjGen(m->xref_table.stream_number(og.getObj()), 0)); | 554 | + return getLinearizationOffset( |
| 555 | + QPDFObjGen(m->objects.xref_table().stream_number(og.getObj()), 0)); | ||
| 555 | 556 | ||
| 556 | default: | 557 | default: |
| 557 | stopOnError("getLinearizationOffset called for xref entry not of type 1 or 2"); | 558 | stopOnError("getLinearizationOffset called for xref entry not of type 1 or 2"); |
| @@ -571,13 +572,13 @@ QPDF::getUncompressedObject(QPDFObjectHandle& obj, std::map<int, int> const& obj | @@ -571,13 +572,13 @@ QPDF::getUncompressedObject(QPDFObjectHandle& obj, std::map<int, int> const& obj | ||
| 571 | } | 572 | } |
| 572 | 573 | ||
| 573 | QPDFObjectHandle | 574 | QPDFObjectHandle |
| 574 | -QPDF::getUncompressedObject(QPDFObjectHandle& obj, Xref_table const& xref) | 575 | +QPDF::getUncompressedObject(QPDFObjectHandle& obj, Objects const& objects) |
| 575 | { | 576 | { |
| 576 | auto og = obj.getObjGen(); | 577 | auto og = obj.getObjGen(); |
| 577 | - if (obj.isNull() || xref.type(og) != 2) { | 578 | + if (obj.isNull() || objects.xref_table().type(og) != 2) { |
| 578 | return obj; | 579 | return obj; |
| 579 | } | 580 | } |
| 580 | - return getObject(xref.stream_number(og.getObj()), 0); | 581 | + return getObject(objects.xref_table().stream_number(og.getObj()), 0); |
| 581 | } | 582 | } |
| 582 | 583 | ||
| 583 | QPDFObjectHandle | 584 | QPDFObjectHandle |
| @@ -597,7 +598,7 @@ QPDF::lengthNextN(int first_object, int n) | @@ -597,7 +598,7 @@ QPDF::lengthNextN(int first_object, int n) | ||
| 597 | int length = 0; | 598 | int length = 0; |
| 598 | for (int i = 0; i < n; ++i) { | 599 | for (int i = 0; i < n; ++i) { |
| 599 | QPDFObjGen og(first_object + i, 0); | 600 | QPDFObjGen og(first_object + i, 0); |
| 600 | - auto end = m->xref_table.end_after_space(og); | 601 | + auto end = m->objects.xref_table().end_after_space(og); |
| 601 | if (end <= 0) { | 602 | if (end <= 0) { |
| 602 | linearizationWarning( | 603 | linearizationWarning( |
| 603 | "no xref table entry for " + std::to_string(first_object + i) + " 0"); | 604 | "no xref table entry for " + std::to_string(first_object + i) + " 0"); |
| @@ -627,7 +628,7 @@ QPDF::checkHPageOffset( | @@ -627,7 +628,7 @@ QPDF::checkHPageOffset( | ||
| 627 | int npages = toI(pages.size()); | 628 | int npages = toI(pages.size()); |
| 628 | qpdf_offset_t table_offset = adjusted_offset(m->page_offset_hints.first_page_offset); | 629 | qpdf_offset_t table_offset = adjusted_offset(m->page_offset_hints.first_page_offset); |
| 629 | QPDFObjGen first_page_og(pages.at(0).getObjGen()); | 630 | QPDFObjGen first_page_og(pages.at(0).getObjGen()); |
| 630 | - if (m->xref_table.type(first_page_og) == 0) { | 631 | + if (m->objects.xref_table().type(first_page_og) == 0) { |
| 631 | stopOnError("supposed first page object is not known"); | 632 | stopOnError("supposed first page object is not known"); |
| 632 | } | 633 | } |
| 633 | qpdf_offset_t offset = getLinearizationOffset(first_page_og); | 634 | qpdf_offset_t offset = getLinearizationOffset(first_page_og); |
| @@ -638,7 +639,7 @@ QPDF::checkHPageOffset( | @@ -638,7 +639,7 @@ QPDF::checkHPageOffset( | ||
| 638 | for (int pageno = 0; pageno < npages; ++pageno) { | 639 | for (int pageno = 0; pageno < npages; ++pageno) { |
| 639 | QPDFObjGen page_og(pages.at(toS(pageno)).getObjGen()); | 640 | QPDFObjGen page_og(pages.at(toS(pageno)).getObjGen()); |
| 640 | int first_object = page_og.getObj(); | 641 | int first_object = page_og.getObj(); |
| 641 | - if (m->xref_table.type(page_og) == 0) { | 642 | + if (m->objects.xref_table().type(page_og) == 0) { |
| 642 | stopOnError("unknown object in page offset hint table"); | 643 | stopOnError("unknown object in page offset hint table"); |
| 643 | } | 644 | } |
| 644 | offset = getLinearizationOffset(page_og); | 645 | offset = getLinearizationOffset(page_og); |
| @@ -760,7 +761,7 @@ QPDF::checkHSharedObject(std::vector<QPDFObjectHandle> const& pages, std::map<in | @@ -760,7 +761,7 @@ QPDF::checkHSharedObject(std::vector<QPDFObjectHandle> const& pages, std::map<in | ||
| 760 | cur_object = so.first_shared_obj; | 761 | cur_object = so.first_shared_obj; |
| 761 | 762 | ||
| 762 | QPDFObjGen og(cur_object, 0); | 763 | QPDFObjGen og(cur_object, 0); |
| 763 | - if (m->xref_table.type(og) == 0) { | 764 | + if (m->objects.xref_table().type(og) == 0) { |
| 764 | stopOnError("unknown object in shared object hint table"); | 765 | stopOnError("unknown object in shared object hint table"); |
| 765 | } | 766 | } |
| 766 | qpdf_offset_t offset = getLinearizationOffset(og); | 767 | qpdf_offset_t offset = getLinearizationOffset(og); |
| @@ -811,7 +812,7 @@ QPDF::checkHOutlines() | @@ -811,7 +812,7 @@ QPDF::checkHOutlines() | ||
| 811 | return; | 812 | return; |
| 812 | } | 813 | } |
| 813 | QPDFObjGen og(outlines.getObjGen()); | 814 | QPDFObjGen og(outlines.getObjGen()); |
| 814 | - if (m->xref_table.type(og) == 0) { | 815 | + if (m->objects.xref_table().type(og) == 0) { |
| 815 | stopOnError("unknown object in outlines hint table"); | 816 | stopOnError("unknown object in outlines hint table"); |
| 816 | } | 817 | } |
| 817 | qpdf_offset_t offset = getLinearizationOffset(og); | 818 | qpdf_offset_t offset = getLinearizationOffset(og); |
| @@ -1158,7 +1159,7 @@ QPDF::calculateLinearizationData(T const& object_stream_data) | @@ -1158,7 +1159,7 @@ QPDF::calculateLinearizationData(T const& object_stream_data) | ||
| 1158 | // Map all page objects to the containing object stream. This should be a no-op in a | 1159 | // Map all page objects to the containing object stream. This should be a no-op in a |
| 1159 | // properly linearized file. | 1160 | // properly linearized file. |
| 1160 | for (auto oh: getAllPages()) { | 1161 | for (auto oh: getAllPages()) { |
| 1161 | - pages.push_back(getUncompressedObject(oh, object_stream_data)); | 1162 | + pages.emplace_back(getUncompressedObject(oh, object_stream_data)); |
| 1162 | } | 1163 | } |
| 1163 | } | 1164 | } |
| 1164 | int npages = toI(pages.size()); | 1165 | int npages = toI(pages.size()); |
libqpdf/QPDF_objects.cc
| @@ -24,6 +24,9 @@ | @@ -24,6 +24,9 @@ | ||
| 24 | #include <qpdf/QTC.hh> | 24 | #include <qpdf/QTC.hh> |
| 25 | #include <qpdf/QUtil.hh> | 25 | #include <qpdf/QUtil.hh> |
| 26 | 26 | ||
| 27 | +using Objects = QPDF::Objects; | ||
| 28 | +using Xref_table = Objects::Xref_table; | ||
| 29 | + | ||
| 27 | namespace | 30 | namespace |
| 28 | { | 31 | { |
| 29 | class InvalidInputSource final: public InputSource | 32 | class InvalidInputSource final: public InputSource |
| @@ -98,7 +101,7 @@ QPDF::findStartxref() | @@ -98,7 +101,7 @@ QPDF::findStartxref() | ||
| 98 | } | 101 | } |
| 99 | 102 | ||
| 100 | void | 103 | void |
| 101 | -QPDF::Xref_table::initialize_empty() | 104 | +Xref_table::initialize_empty() |
| 102 | { | 105 | { |
| 103 | initialized_ = true; | 106 | initialized_ = true; |
| 104 | trailer_ = QPDFObjectHandle::newDictionary(); | 107 | trailer_ = QPDFObjectHandle::newDictionary(); |
| @@ -114,7 +117,7 @@ QPDF::Xref_table::initialize_empty() | @@ -114,7 +117,7 @@ QPDF::Xref_table::initialize_empty() | ||
| 114 | } | 117 | } |
| 115 | 118 | ||
| 116 | void | 119 | void |
| 117 | -QPDF::Xref_table::initialize_json() | 120 | +Xref_table::initialize_json() |
| 118 | { | 121 | { |
| 119 | initialized_ = true; | 122 | initialized_ = true; |
| 120 | table.resize(1); | 123 | table.resize(1); |
| @@ -123,7 +126,7 @@ QPDF::Xref_table::initialize_json() | @@ -123,7 +126,7 @@ QPDF::Xref_table::initialize_json() | ||
| 123 | } | 126 | } |
| 124 | 127 | ||
| 125 | void | 128 | void |
| 126 | -QPDF::Xref_table::initialize() | 129 | +Xref_table::initialize() |
| 127 | { | 130 | { |
| 128 | // PDF spec says %%EOF must be found within the last 1024 bytes of/ the file. We add an extra | 131 | // PDF spec says %%EOF must be found within the last 1024 bytes of/ the file. We add an extra |
| 129 | // 30 characters to leave room for the startxref stuff. | 132 | // 30 characters to leave room for the startxref stuff. |
| @@ -166,7 +169,7 @@ QPDF::Xref_table::initialize() | @@ -166,7 +169,7 @@ QPDF::Xref_table::initialize() | ||
| 166 | } | 169 | } |
| 167 | 170 | ||
| 168 | void | 171 | void |
| 169 | -QPDF::Xref_table::reconstruct(QPDFExc& e) | 172 | +Xref_table::reconstruct(QPDFExc& e) |
| 170 | { | 173 | { |
| 171 | if (reconstructed_) { | 174 | if (reconstructed_) { |
| 172 | // Avoid xref reconstruction infinite loops. This is getting very hard to reproduce because | 175 | // Avoid xref reconstruction infinite loops. This is getting very hard to reproduce because |
| @@ -318,7 +321,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | @@ -318,7 +321,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | ||
| 318 | } | 321 | } |
| 319 | 322 | ||
| 320 | void | 323 | void |
| 321 | -QPDF::Xref_table::read(qpdf_offset_t xref_offset) | 324 | +Xref_table::read(qpdf_offset_t xref_offset) |
| 322 | { | 325 | { |
| 323 | std::map<int, int> free_table; | 326 | std::map<int, int> free_table; |
| 324 | std::set<qpdf_offset_t> visited; | 327 | std::set<qpdf_offset_t> visited; |
| @@ -392,8 +395,8 @@ QPDF::Xref_table::read(qpdf_offset_t xref_offset) | @@ -392,8 +395,8 @@ QPDF::Xref_table::read(qpdf_offset_t xref_offset) | ||
| 392 | // entries, including missing entries before the last actual entry. | 395 | // entries, including missing entries before the last actual entry. |
| 393 | } | 396 | } |
| 394 | 397 | ||
| 395 | -QPDF::Xref_table::Subsection | ||
| 396 | -QPDF::Xref_table::subsection(std::string const& line) | 398 | +Xref_table::Subsection |
| 399 | +Xref_table::subsection(std::string const& line) | ||
| 397 | { | 400 | { |
| 398 | auto terminate = [this]() -> void { | 401 | auto terminate = [this]() -> void { |
| 399 | QTC::TC("qpdf", "QPDF invalid xref"); | 402 | QTC::TC("qpdf", "QPDF invalid xref"); |
| @@ -447,10 +450,10 @@ QPDF::Xref_table::subsection(std::string const& line) | @@ -447,10 +450,10 @@ QPDF::Xref_table::subsection(std::string const& line) | ||
| 447 | return {obj, count, file->getLastOffset() + toI(p - start)}; | 450 | return {obj, count, file->getLastOffset() + toI(p - start)}; |
| 448 | } | 451 | } |
| 449 | 452 | ||
| 450 | -std::vector<QPDF::Xref_table::Subsection> | ||
| 451 | -QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) | 453 | +std::vector<Xref_table::Subsection> |
| 454 | +Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) | ||
| 452 | { | 455 | { |
| 453 | - std::vector<QPDF::Xref_table::Subsection> result; | 456 | + std::vector<Xref_table::Subsection> result; |
| 454 | file->seek(start, SEEK_SET); | 457 | file->seek(start, SEEK_SET); |
| 455 | 458 | ||
| 456 | while (true) { | 459 | while (true) { |
| @@ -475,12 +478,12 @@ QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) | @@ -475,12 +478,12 @@ QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) | ||
| 475 | 478 | ||
| 476 | // Optimistically read and parse all subsection headers. If an error is encountered return the | 479 | // Optimistically read and parse all subsection headers. If an error is encountered return the |
| 477 | // result of bad_subsections. | 480 | // result of bad_subsections. |
| 478 | -std::vector<QPDF::Xref_table::Subsection> | ||
| 479 | -QPDF::Xref_table::subsections(std::string& line) | 481 | +std::vector<Xref_table::Subsection> |
| 482 | +Xref_table::subsections(std::string& line) | ||
| 480 | { | 483 | { |
| 481 | auto recovery_offset = file->tell(); | 484 | auto recovery_offset = file->tell(); |
| 482 | try { | 485 | try { |
| 483 | - std::vector<QPDF::Xref_table::Subsection> result; | 486 | + std::vector<Xref_table::Subsection> result; |
| 484 | 487 | ||
| 485 | while (true) { | 488 | while (true) { |
| 486 | line.assign(50, '\0'); | 489 | line.assign(50, '\0'); |
| @@ -507,7 +510,7 @@ QPDF::Xref_table::subsections(std::string& line) | @@ -507,7 +510,7 @@ QPDF::Xref_table::subsections(std::string& line) | ||
| 507 | 510 | ||
| 508 | // Returns (success, f1, f2, type). | 511 | // Returns (success, f1, f2, type). |
| 509 | std::tuple<bool, qpdf_offset_t, int, char> | 512 | std::tuple<bool, qpdf_offset_t, int, char> |
| 510 | -QPDF::Xref_table::read_bad_entry() | 513 | +Xref_table::read_bad_entry() |
| 511 | { | 514 | { |
| 512 | qpdf_offset_t f1{0}; | 515 | qpdf_offset_t f1{0}; |
| 513 | int f2{0}; | 516 | int f2{0}; |
| @@ -592,7 +595,7 @@ QPDF::Xref_table::read_bad_entry() | @@ -592,7 +595,7 @@ QPDF::Xref_table::read_bad_entry() | ||
| 592 | // Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return | 595 | // Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return |
| 593 | // result. Returns (success, f1, f2, type). | 596 | // result. Returns (success, f1, f2, type). |
| 594 | std::tuple<bool, qpdf_offset_t, int, char> | 597 | std::tuple<bool, qpdf_offset_t, int, char> |
| 595 | -QPDF::Xref_table::read_entry() | 598 | +Xref_table::read_entry() |
| 596 | { | 599 | { |
| 597 | qpdf_offset_t f1{0}; | 600 | qpdf_offset_t f1{0}; |
| 598 | int f2{0}; | 601 | int f2{0}; |
| @@ -651,7 +654,7 @@ QPDF::Xref_table::read_entry() | @@ -651,7 +654,7 @@ QPDF::Xref_table::read_entry() | ||
| 651 | 654 | ||
| 652 | // Read a single cross-reference table section and associated trailer. | 655 | // Read a single cross-reference table section and associated trailer. |
| 653 | qpdf_offset_t | 656 | qpdf_offset_t |
| 654 | -QPDF::Xref_table::process_section(qpdf_offset_t xref_offset) | 657 | +Xref_table::process_section(qpdf_offset_t xref_offset) |
| 655 | { | 658 | { |
| 656 | file->seek(xref_offset, SEEK_SET); | 659 | file->seek(xref_offset, SEEK_SET); |
| 657 | std::string line; | 660 | std::string line; |
| @@ -738,7 +741,7 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset) | @@ -738,7 +741,7 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset) | ||
| 738 | 741 | ||
| 739 | // Read a single cross-reference stream. | 742 | // Read a single cross-reference stream. |
| 740 | qpdf_offset_t | 743 | qpdf_offset_t |
| 741 | -QPDF::Xref_table::read_stream(qpdf_offset_t xref_offset) | 744 | +Xref_table::read_stream(qpdf_offset_t xref_offset) |
| 742 | { | 745 | { |
| 743 | if (!ignore_streams_) { | 746 | if (!ignore_streams_) { |
| 744 | QPDFObjGen x_og; | 747 | QPDFObjGen x_og; |
| @@ -762,8 +765,7 @@ QPDF::Xref_table::read_stream(qpdf_offset_t xref_offset) | @@ -762,8 +765,7 @@ QPDF::Xref_table::read_stream(qpdf_offset_t xref_offset) | ||
| 762 | 765 | ||
| 763 | // Return the entry size of the xref stream and the processed W array. | 766 | // Return the entry size of the xref stream and the processed W array. |
| 764 | std::pair<int, std::array<int, 3>> | 767 | std::pair<int, std::array<int, 3>> |
| 765 | -QPDF::Xref_table::process_W( | ||
| 766 | - QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged) | 768 | +Xref_table::process_W(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged) |
| 767 | { | 769 | { |
| 768 | auto W_obj = dict.getKey("/W"); | 770 | auto W_obj = dict.getKey("/W"); |
| 769 | if (!(W_obj.isArray() && W_obj.getArrayNItems() >= 3 && W_obj.getArrayItem(0).isInteger() && | 771 | if (!(W_obj.isArray() && W_obj.getArrayNItems() >= 3 && W_obj.getArrayItem(0).isInteger() && |
| @@ -794,7 +796,7 @@ QPDF::Xref_table::process_W( | @@ -794,7 +796,7 @@ QPDF::Xref_table::process_W( | ||
| 794 | // Validate Size entry and return the maximum number of entries that the xref stream can contain and | 796 | // Validate Size entry and return the maximum number of entries that the xref stream can contain and |
| 795 | // the value of the Size entry. | 797 | // the value of the Size entry. |
| 796 | std::pair<int, size_t> | 798 | std::pair<int, size_t> |
| 797 | -QPDF::Xref_table::process_Size( | 799 | +Xref_table::process_Size( |
| 798 | QPDFObjectHandle& dict, int entry_size, std::function<QPDFExc(std::string_view)> damaged) | 800 | QPDFObjectHandle& dict, int entry_size, std::function<QPDFExc(std::string_view)> damaged) |
| 799 | { | 801 | { |
| 800 | // Number of entries is limited by the highest possible object id and stream size. | 802 | // Number of entries is limited by the highest possible object id and stream size. |
| @@ -818,7 +820,7 @@ QPDF::Xref_table::process_Size( | @@ -818,7 +820,7 @@ QPDF::Xref_table::process_Size( | ||
| 818 | 820 | ||
| 819 | // Return the number of entries of the xref stream and the processed Index array. | 821 | // Return the number of entries of the xref stream and the processed Index array. |
| 820 | std::pair<int, std::vector<std::pair<int, int>>> | 822 | std::pair<int, std::vector<std::pair<int, int>>> |
| 821 | -QPDF::Xref_table::process_Index( | 823 | +Xref_table::process_Index( |
| 822 | QPDFObjectHandle& dict, int max_num_entries, std::function<QPDFExc(std::string_view)> damaged) | 824 | QPDFObjectHandle& dict, int max_num_entries, std::function<QPDFExc(std::string_view)> damaged) |
| 823 | { | 825 | { |
| 824 | auto size = dict.getKey("/Size").getIntValueAsInt(); | 826 | auto size = dict.getKey("/Size").getIntValueAsInt(); |
| @@ -885,7 +887,7 @@ QPDF::Xref_table::process_Index( | @@ -885,7 +887,7 @@ QPDF::Xref_table::process_Index( | ||
| 885 | } | 887 | } |
| 886 | 888 | ||
| 887 | qpdf_offset_t | 889 | qpdf_offset_t |
| 888 | -QPDF::Xref_table::process_stream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) | 890 | +Xref_table::process_stream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) |
| 889 | { | 891 | { |
| 890 | auto damaged = [this, xref_offset](std::string_view msg) -> QPDFExc { | 892 | auto damaged = [this, xref_offset](std::string_view msg) -> QPDFExc { |
| 891 | return qpdf.damagedPDF("xref stream", xref_offset, msg.data()); | 893 | return qpdf.damagedPDF("xref stream", xref_offset, msg.data()); |
| @@ -978,7 +980,7 @@ QPDF::Xref_table::process_stream(qpdf_offset_t xref_offset, QPDFObjectHandle& xr | @@ -978,7 +980,7 @@ QPDF::Xref_table::process_stream(qpdf_offset_t xref_offset, QPDFObjectHandle& xr | ||
| 978 | } | 980 | } |
| 979 | 981 | ||
| 980 | void | 982 | void |
| 981 | -QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) | 983 | +Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) |
| 982 | { | 984 | { |
| 983 | // Populate the xref table in such a way that the first reference to an object that we see, | 985 | // Populate the xref table in such a way that the first reference to an object that we see, |
| 984 | // which is the one in the latest xref table in which it appears, is the one that gets stored. | 986 | // which is the one in the latest xref table in which it appears, is the one that gets stored. |
| @@ -1040,7 +1042,7 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) | @@ -1040,7 +1042,7 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) | ||
| 1040 | } | 1042 | } |
| 1041 | 1043 | ||
| 1042 | void | 1044 | void |
| 1043 | -QPDF::Xref_table::insert_free(QPDFObjGen og) | 1045 | +Xref_table::insert_free(QPDFObjGen og) |
| 1044 | { | 1046 | { |
| 1045 | // At the moment we are processing the updates last to first and therefore the gen doesn't | 1047 | // At the moment we are processing the updates last to first and therefore the gen doesn't |
| 1046 | // matter as long as it > 0 to distinguish it from an uninitialized entry. This will need to be | 1048 | // matter as long as it > 0 to distinguish it from an uninitialized entry. This will need to be |
| @@ -1055,7 +1057,7 @@ QPDF::Xref_table::insert_free(QPDFObjGen og) | @@ -1055,7 +1057,7 @@ QPDF::Xref_table::insert_free(QPDFObjGen og) | ||
| 1055 | } | 1057 | } |
| 1056 | 1058 | ||
| 1057 | QPDFObjGen | 1059 | QPDFObjGen |
| 1058 | -QPDF::Xref_table::at_offset(qpdf_offset_t offset) const noexcept | 1060 | +Xref_table::at_offset(qpdf_offset_t offset) const noexcept |
| 1059 | { | 1061 | { |
| 1060 | int id = 0; | 1062 | int id = 0; |
| 1061 | int gen = 0; | 1063 | int gen = 0; |
| @@ -1075,7 +1077,7 @@ QPDF::Xref_table::at_offset(qpdf_offset_t offset) const noexcept | @@ -1075,7 +1077,7 @@ QPDF::Xref_table::at_offset(qpdf_offset_t offset) const noexcept | ||
| 1075 | } | 1077 | } |
| 1076 | 1078 | ||
| 1077 | std::map<QPDFObjGen, QPDFXRefEntry> | 1079 | std::map<QPDFObjGen, QPDFXRefEntry> |
| 1078 | -QPDF::Xref_table::as_map() const | 1080 | +Xref_table::as_map() const |
| 1079 | { | 1081 | { |
| 1080 | std::map<QPDFObjGen, QPDFXRefEntry> result; | 1082 | std::map<QPDFObjGen, QPDFXRefEntry> result; |
| 1081 | int i{0}; | 1083 | int i{0}; |
| @@ -1099,7 +1101,7 @@ QPDF::Xref_table::as_map() const | @@ -1099,7 +1101,7 @@ QPDF::Xref_table::as_map() const | ||
| 1099 | } | 1101 | } |
| 1100 | 1102 | ||
| 1101 | void | 1103 | void |
| 1102 | -QPDF::Xref_table::show() | 1104 | +Xref_table::show() |
| 1103 | { | 1105 | { |
| 1104 | auto& cout = *qpdf.m->log->getInfo(); | 1106 | auto& cout = *qpdf.m->log->getInfo(); |
| 1105 | int i = -1; | 1107 | int i = -1; |
| @@ -1128,7 +1130,7 @@ QPDF::Xref_table::show() | @@ -1128,7 +1130,7 @@ QPDF::Xref_table::show() | ||
| 1128 | // Resolve all objects in the xref table. If this triggers a xref table reconstruction abort and | 1130 | // Resolve all objects in the xref table. If this triggers a xref table reconstruction abort and |
| 1129 | // return false. Otherwise return true. | 1131 | // return false. Otherwise return true. |
| 1130 | bool | 1132 | bool |
| 1131 | -QPDF::Xref_table::resolve() | 1133 | +Xref_table::resolve() |
| 1132 | { | 1134 | { |
| 1133 | bool may_change = !reconstructed_; | 1135 | bool may_change = !reconstructed_; |
| 1134 | int i = -1; | 1136 | int i = -1; |
| @@ -1159,7 +1161,7 @@ QPDF::getAllObjects() | @@ -1159,7 +1161,7 @@ QPDF::getAllObjects() | ||
| 1159 | } | 1161 | } |
| 1160 | 1162 | ||
| 1161 | QPDFObjectHandle | 1163 | QPDFObjectHandle |
| 1162 | -QPDF::Xref_table::read_trailer() | 1164 | +Xref_table::read_trailer() |
| 1163 | { | 1165 | { |
| 1164 | qpdf_offset_t offset = file->tell(); | 1166 | qpdf_offset_t offset = file->tell(); |
| 1165 | bool empty = false; | 1167 | bool empty = false; |
| @@ -1177,7 +1179,7 @@ QPDF::Xref_table::read_trailer() | @@ -1177,7 +1179,7 @@ QPDF::Xref_table::read_trailer() | ||
| 1177 | } | 1179 | } |
| 1178 | 1180 | ||
| 1179 | QPDFObjectHandle | 1181 | QPDFObjectHandle |
| 1180 | -QPDF::Objects::read_object(std::string const& description, QPDFObjGen og) | 1182 | +Objects::read_object(std::string const& description, QPDFObjGen og) |
| 1181 | { | 1183 | { |
| 1182 | qpdf.setLastObjectDescription(description, og); | 1184 | qpdf.setLastObjectDescription(description, og); |
| 1183 | qpdf_offset_t offset = m->file->tell(); | 1185 | qpdf_offset_t offset = m->file->tell(); |
| @@ -1209,7 +1211,7 @@ QPDF::Objects::read_object(std::string const& description, QPDFObjGen og) | @@ -1209,7 +1211,7 @@ QPDF::Objects::read_object(std::string const& description, QPDFObjGen og) | ||
| 1209 | 1211 | ||
| 1210 | // After reading stream dictionary and stream keyword, read rest of stream. | 1212 | // After reading stream dictionary and stream keyword, read rest of stream. |
| 1211 | void | 1213 | void |
| 1212 | -QPDF::Objects::read_stream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) | 1214 | +Objects::read_stream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) |
| 1213 | { | 1215 | { |
| 1214 | validate_stream_line_end(object, og, offset); | 1216 | validate_stream_line_end(object, og, offset); |
| 1215 | 1217 | ||
| @@ -1250,8 +1252,7 @@ QPDF::Objects::read_stream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_ | @@ -1250,8 +1252,7 @@ QPDF::Objects::read_stream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_ | ||
| 1250 | } | 1252 | } |
| 1251 | 1253 | ||
| 1252 | void | 1254 | void |
| 1253 | -QPDF::Objects::validate_stream_line_end( | ||
| 1254 | - QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) | 1255 | +Objects::validate_stream_line_end(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) |
| 1255 | { | 1256 | { |
| 1256 | // The PDF specification states that the word "stream" should be followed by either a carriage | 1257 | // The PDF specification states that the word "stream" should be followed by either a carriage |
| 1257 | // return and a newline or by a newline alone. It specifically disallowed following it by a | 1258 | // return and a newline or by a newline alone. It specifically disallowed following it by a |
| @@ -1302,7 +1303,7 @@ QPDF::Objects::validate_stream_line_end( | @@ -1302,7 +1303,7 @@ QPDF::Objects::validate_stream_line_end( | ||
| 1302 | } | 1303 | } |
| 1303 | 1304 | ||
| 1304 | QPDFObjectHandle | 1305 | QPDFObjectHandle |
| 1305 | -QPDF::Objects::readObjectInStream(std::shared_ptr<InputSource>& input, int obj) | 1306 | +Objects::readObjectInStream(std::shared_ptr<InputSource>& input, int obj) |
| 1306 | { | 1307 | { |
| 1307 | m->last_object_description.erase(7); // last_object_description starts with "object " | 1308 | m->last_object_description.erase(7); // last_object_description starts with "object " |
| 1308 | m->last_object_description += std::to_string(obj); | 1309 | m->last_object_description += std::to_string(obj); |
| @@ -1332,7 +1333,7 @@ QPDF::findEndstream() | @@ -1332,7 +1333,7 @@ QPDF::findEndstream() | ||
| 1332 | } | 1333 | } |
| 1333 | 1334 | ||
| 1334 | size_t | 1335 | size_t |
| 1335 | -QPDF::Objects::recover_stream_length( | 1336 | +Objects::recover_stream_length( |
| 1336 | std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset) | 1337 | std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset) |
| 1337 | { | 1338 | { |
| 1338 | // Try to reconstruct stream length by looking for endstream or endobj | 1339 | // Try to reconstruct stream length by looking for endstream or endobj |
| @@ -1351,7 +1352,7 @@ QPDF::Objects::recover_stream_length( | @@ -1351,7 +1352,7 @@ QPDF::Objects::recover_stream_length( | ||
| 1351 | 1352 | ||
| 1352 | if (length) { | 1353 | if (length) { |
| 1353 | // Make sure this is inside this object | 1354 | // Make sure this is inside this object |
| 1354 | - auto found = m->xref_table.at_offset(stream_offset + toO(length)); | 1355 | + auto found = xref.at_offset(stream_offset + toO(length)); |
| 1355 | if (found == QPDFObjGen() || found == og) { | 1356 | if (found == QPDFObjGen() || found == og) { |
| 1356 | // If we are trying to recover an XRef stream the xref table will not contain and | 1357 | // If we are trying to recover an XRef stream the xref table will not contain and |
| 1357 | // won't contain any entries, therefore we cannot check the found length. Otherwise we | 1358 | // won't contain any entries, therefore we cannot check the found length. Otherwise we |
| @@ -1376,7 +1377,7 @@ QPDF::Objects::recover_stream_length( | @@ -1376,7 +1377,7 @@ QPDF::Objects::recover_stream_length( | ||
| 1376 | } | 1377 | } |
| 1377 | 1378 | ||
| 1378 | QPDFObjectHandle | 1379 | QPDFObjectHandle |
| 1379 | -QPDF::Objects::read( | 1380 | +Objects::read( |
| 1380 | bool try_recovery, | 1381 | bool try_recovery, |
| 1381 | qpdf_offset_t offset, | 1382 | qpdf_offset_t offset, |
| 1382 | std::string const& description, | 1383 | std::string const& description, |
| @@ -1455,10 +1456,10 @@ QPDF::Objects::read( | @@ -1455,10 +1456,10 @@ QPDF::Objects::read( | ||
| 1455 | } catch (QPDFExc& e) { | 1456 | } catch (QPDFExc& e) { |
| 1456 | if (try_recovery) { | 1457 | if (try_recovery) { |
| 1457 | // Try again after reconstructing xref table | 1458 | // Try again after reconstructing xref table |
| 1458 | - m->xref_table.reconstruct(e); | ||
| 1459 | - if (m->xref_table.type(exp_og) == 1) { | 1459 | + xref.reconstruct(e); |
| 1460 | + if (xref.type(exp_og) == 1) { | ||
| 1460 | QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset"); | 1461 | QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset"); |
| 1461 | - return read(false, m->xref_table.offset(exp_og), description, exp_og, og, false); | 1462 | + return read(false, xref.offset(exp_og), description, exp_og, og, false); |
| 1462 | } else { | 1463 | } else { |
| 1463 | QTC::TC("qpdf", "QPDF object gone after xref reconstruction"); | 1464 | QTC::TC("qpdf", "QPDF object gone after xref reconstruction"); |
| 1464 | qpdf.warn(qpdf.damagedPDF( | 1465 | qpdf.warn(qpdf.damagedPDF( |
| @@ -1498,7 +1499,7 @@ QPDF::Objects::read( | @@ -1498,7 +1499,7 @@ QPDF::Objects::read( | ||
| 1498 | } | 1499 | } |
| 1499 | } | 1500 | } |
| 1500 | qpdf_offset_t end_after_space = m->file->tell(); | 1501 | qpdf_offset_t end_after_space = m->file->tell(); |
| 1501 | - if (skip_cache_if_in_xref && m->xref_table.type(og)) { | 1502 | + if (skip_cache_if_in_xref && xref.type(og)) { |
| 1502 | // Ordinarily, an object gets read here when resolved through xref table or stream. In | 1503 | // Ordinarily, an object gets read here when resolved through xref table or stream. In |
| 1503 | // the special case of the xref stream and linearization hint tables, the offset comes | 1504 | // the special case of the xref stream and linearization hint tables, the offset comes |
| 1504 | // from another source. For the specific case of xref streams, the xref stream is read | 1505 | // from another source. For the specific case of xref streams, the xref stream is read |
| @@ -1526,8 +1527,7 @@ QPDF::Objects::read( | @@ -1526,8 +1527,7 @@ QPDF::Objects::read( | ||
| 1526 | // could use !check_og in place of skip_cache_if_in_xref. | 1527 | // could use !check_og in place of skip_cache_if_in_xref. |
| 1527 | QTC::TC("qpdf", "QPDF skipping cache for known unchecked object"); | 1528 | QTC::TC("qpdf", "QPDF skipping cache for known unchecked object"); |
| 1528 | } else { | 1529 | } else { |
| 1529 | - m->xref_table.linearization_offsets( | ||
| 1530 | - toS(og.getObj()), end_before_space, end_after_space); | 1530 | + xref.linearization_offsets(toS(og.getObj()), end_before_space, end_after_space); |
| 1531 | update_table(og, oh.getObj()); | 1531 | update_table(og, oh.getObj()); |
| 1532 | } | 1532 | } |
| 1533 | } | 1533 | } |
| @@ -1536,7 +1536,7 @@ QPDF::Objects::read( | @@ -1536,7 +1536,7 @@ QPDF::Objects::read( | ||
| 1536 | } | 1536 | } |
| 1537 | 1537 | ||
| 1538 | QPDFObject* | 1538 | QPDFObject* |
| 1539 | -QPDF::Objects::resolve(QPDFObjGen og) | 1539 | +Objects::resolve(QPDFObjGen og) |
| 1540 | { | 1540 | { |
| 1541 | if (!unresolved(og)) { | 1541 | if (!unresolved(og)) { |
| 1542 | return obj_cache[og].object.get(); | 1542 | return obj_cache[og].object.get(); |
| @@ -1553,19 +1553,19 @@ QPDF::Objects::resolve(QPDFObjGen og) | @@ -1553,19 +1553,19 @@ QPDF::Objects::resolve(QPDFObjGen og) | ||
| 1553 | ResolveRecorder rr(&qpdf, og); | 1553 | ResolveRecorder rr(&qpdf, og); |
| 1554 | 1554 | ||
| 1555 | try { | 1555 | try { |
| 1556 | - switch (m->xref_table.type(og)) { | 1556 | + switch (xref.type(og)) { |
| 1557 | case 0: | 1557 | case 0: |
| 1558 | break; | 1558 | break; |
| 1559 | case 1: | 1559 | case 1: |
| 1560 | { | 1560 | { |
| 1561 | // Object stored in cache by readObjectAtOffset | 1561 | // Object stored in cache by readObjectAtOffset |
| 1562 | QPDFObjGen a_og; | 1562 | QPDFObjGen a_og; |
| 1563 | - QPDFObjectHandle oh = read(true, m->xref_table.offset(og), "", og, a_og, false); | 1563 | + QPDFObjectHandle oh = read(true, xref.offset(og), "", og, a_og, false); |
| 1564 | } | 1564 | } |
| 1565 | break; | 1565 | break; |
| 1566 | 1566 | ||
| 1567 | case 2: | 1567 | case 2: |
| 1568 | - resolveObjectsInStream(m->xref_table.stream_number(og.getObj())); | 1568 | + resolveObjectsInStream(xref.stream_number(og.getObj())); |
| 1569 | break; | 1569 | break; |
| 1570 | 1570 | ||
| 1571 | default: | 1571 | default: |
| @@ -1591,7 +1591,7 @@ QPDF::Objects::resolve(QPDFObjGen og) | @@ -1591,7 +1591,7 @@ QPDF::Objects::resolve(QPDFObjGen og) | ||
| 1591 | } | 1591 | } |
| 1592 | 1592 | ||
| 1593 | void | 1593 | void |
| 1594 | -QPDF::Objects::resolveObjectsInStream(int obj_stream_number) | 1594 | +Objects::resolveObjectsInStream(int obj_stream_number) |
| 1595 | { | 1595 | { |
| 1596 | if (m->resolved_object_streams.count(obj_stream_number)) { | 1596 | if (m->resolved_object_streams.count(obj_stream_number)) { |
| 1597 | return; | 1597 | return; |
| @@ -1642,7 +1642,7 @@ QPDF::Objects::resolveObjectsInStream(int obj_stream_number) | @@ -1642,7 +1642,7 @@ QPDF::Objects::resolveObjectsInStream(int obj_stream_number) | ||
| 1642 | 1642 | ||
| 1643 | int num = QUtil::string_to_int(tnum.getValue().c_str()); | 1643 | int num = QUtil::string_to_int(tnum.getValue().c_str()); |
| 1644 | long long offset = QUtil::string_to_int(toffset.getValue().c_str()); | 1644 | long long offset = QUtil::string_to_int(toffset.getValue().c_str()); |
| 1645 | - if (num > m->xref_table.max_id()) { | 1645 | + if (num > xref.max_id()) { |
| 1646 | continue; | 1646 | continue; |
| 1647 | } | 1647 | } |
| 1648 | if (num == obj_stream_number) { | 1648 | if (num == obj_stream_number) { |
| @@ -1674,8 +1674,7 @@ QPDF::Objects::resolveObjectsInStream(int obj_stream_number) | @@ -1674,8 +1674,7 @@ QPDF::Objects::resolveObjectsInStream(int obj_stream_number) | ||
| 1674 | m->last_object_description += "object "; | 1674 | m->last_object_description += "object "; |
| 1675 | for (auto const& iter: offsets) { | 1675 | for (auto const& iter: offsets) { |
| 1676 | QPDFObjGen og(iter.first, 0); | 1676 | QPDFObjGen og(iter.first, 0); |
| 1677 | - if (m->xref_table.type(og) == 2 && | ||
| 1678 | - m->xref_table.stream_number(og.getObj()) == obj_stream_number) { | 1677 | + if (xref.type(og) == 2 && xref.stream_number(og.getObj()) == obj_stream_number) { |
| 1679 | int offset = iter.second; | 1678 | int offset = iter.second; |
| 1680 | input->seek(offset, SEEK_SET); | 1679 | input->seek(offset, SEEK_SET); |
| 1681 | QPDFObjectHandle oh = readObjectInStream(input, iter.first); | 1680 | QPDFObjectHandle oh = readObjectInStream(input, iter.first); |
| @@ -1687,7 +1686,7 @@ QPDF::Objects::resolveObjectsInStream(int obj_stream_number) | @@ -1687,7 +1686,7 @@ QPDF::Objects::resolveObjectsInStream(int obj_stream_number) | ||
| 1687 | } | 1686 | } |
| 1688 | 1687 | ||
| 1689 | void | 1688 | void |
| 1690 | -QPDF::Objects::update_table(QPDFObjGen og, const std::shared_ptr<QPDFObject>& object) | 1689 | +Objects::update_table(QPDFObjGen og, const std::shared_ptr<QPDFObject>& object) |
| 1691 | { | 1690 | { |
| 1692 | object->setObjGen(&qpdf, og); | 1691 | object->setObjGen(&qpdf, og); |
| 1693 | if (cached(og)) { | 1692 | if (cached(og)) { |
| @@ -1699,19 +1698,19 @@ QPDF::Objects::update_table(QPDFObjGen og, const std::shared_ptr<QPDFObject>& ob | @@ -1699,19 +1698,19 @@ QPDF::Objects::update_table(QPDFObjGen og, const std::shared_ptr<QPDFObject>& ob | ||
| 1699 | } | 1698 | } |
| 1700 | 1699 | ||
| 1701 | bool | 1700 | bool |
| 1702 | -QPDF::Objects::cached(QPDFObjGen og) | 1701 | +Objects::cached(QPDFObjGen og) |
| 1703 | { | 1702 | { |
| 1704 | return obj_cache.count(og) != 0; | 1703 | return obj_cache.count(og) != 0; |
| 1705 | } | 1704 | } |
| 1706 | 1705 | ||
| 1707 | bool | 1706 | bool |
| 1708 | -QPDF::Objects::unresolved(QPDFObjGen og) | 1707 | +Objects::unresolved(QPDFObjGen og) |
| 1709 | { | 1708 | { |
| 1710 | return !cached(og) || obj_cache[og].object->isUnresolved(); | 1709 | return !cached(og) || obj_cache[og].object->isUnresolved(); |
| 1711 | } | 1710 | } |
| 1712 | 1711 | ||
| 1713 | QPDFObjGen | 1712 | QPDFObjGen |
| 1714 | -QPDF::Objects::next_id() | 1713 | +Objects::next_id() |
| 1715 | { | 1714 | { |
| 1716 | int max_objid = toI(qpdf.getObjectCount()); | 1715 | int max_objid = toI(qpdf.getObjectCount()); |
| 1717 | if (max_objid == std::numeric_limits<int>::max()) { | 1716 | if (max_objid == std::numeric_limits<int>::max()) { |
| @@ -1721,7 +1720,7 @@ QPDF::Objects::next_id() | @@ -1721,7 +1720,7 @@ QPDF::Objects::next_id() | ||
| 1721 | } | 1720 | } |
| 1722 | 1721 | ||
| 1723 | QPDFObjectHandle | 1722 | QPDFObjectHandle |
| 1724 | -QPDF::Objects::make_indirect(std::shared_ptr<QPDFObject> const& obj) | 1723 | +Objects::make_indirect(std::shared_ptr<QPDFObject> const& obj) |
| 1725 | { | 1724 | { |
| 1726 | QPDFObjGen next{next_id()}; | 1725 | QPDFObjGen next{next_id()}; |
| 1727 | obj_cache[next] = ObjCache(obj); | 1726 | obj_cache[next] = ObjCache(obj); |
| @@ -1729,14 +1728,14 @@ QPDF::Objects::make_indirect(std::shared_ptr<QPDFObject> const& obj) | @@ -1729,14 +1728,14 @@ QPDF::Objects::make_indirect(std::shared_ptr<QPDFObject> const& obj) | ||
| 1729 | } | 1728 | } |
| 1730 | 1729 | ||
| 1731 | std::shared_ptr<QPDFObject> | 1730 | std::shared_ptr<QPDFObject> |
| 1732 | -QPDF::Objects::get_for_parser(int id, int gen, bool parse_pdf) | 1731 | +Objects::get_for_parser(int id, int gen, bool parse_pdf) |
| 1733 | { | 1732 | { |
| 1734 | // This method is called by the parser and therefore must not resolve any objects. | 1733 | // This method is called by the parser and therefore must not resolve any objects. |
| 1735 | auto og = QPDFObjGen(id, gen); | 1734 | auto og = QPDFObjGen(id, gen); |
| 1736 | if (auto iter = obj_cache.find(og); iter != obj_cache.end()) { | 1735 | if (auto iter = obj_cache.find(og); iter != obj_cache.end()) { |
| 1737 | return iter->second.object; | 1736 | return iter->second.object; |
| 1738 | } | 1737 | } |
| 1739 | - if (m->xref_table.type(og) || !m->xref_table.initialized()) { | 1738 | + if (xref.type(og) || !xref.initialized()) { |
| 1740 | return obj_cache.insert({og, QPDF_Unresolved::create(&qpdf, og)}).first->second.object; | 1739 | return obj_cache.insert({og, QPDF_Unresolved::create(&qpdf, og)}).first->second.object; |
| 1741 | } | 1740 | } |
| 1742 | if (parse_pdf) { | 1741 | if (parse_pdf) { |
| @@ -1746,15 +1745,14 @@ QPDF::Objects::get_for_parser(int id, int gen, bool parse_pdf) | @@ -1746,15 +1745,14 @@ QPDF::Objects::get_for_parser(int id, int gen, bool parse_pdf) | ||
| 1746 | } | 1745 | } |
| 1747 | 1746 | ||
| 1748 | std::shared_ptr<QPDFObject> | 1747 | std::shared_ptr<QPDFObject> |
| 1749 | -QPDF::Objects::get_for_json(int id, int gen) | 1748 | +Objects::get_for_json(int id, int gen) |
| 1750 | { | 1749 | { |
| 1751 | auto og = QPDFObjGen(id, gen); | 1750 | auto og = QPDFObjGen(id, gen); |
| 1752 | auto [it, inserted] = obj_cache.try_emplace(og); | 1751 | auto [it, inserted] = obj_cache.try_emplace(og); |
| 1753 | auto& obj = it->second.object; | 1752 | auto& obj = it->second.object; |
| 1754 | if (inserted) { | 1753 | if (inserted) { |
| 1755 | - obj = (m->xref_table.initialized() && !m->xref_table.type(og)) | ||
| 1756 | - ? QPDF_Null::create(&qpdf, og) | ||
| 1757 | - : QPDF_Unresolved::create(&qpdf, og); | 1754 | + obj = (xref.initialized() && !xref.type(og)) ? QPDF_Null::create(&qpdf, og) |
| 1755 | + : QPDF_Unresolved::create(&qpdf, og); | ||
| 1758 | } | 1756 | } |
| 1759 | return obj; | 1757 | return obj; |
| 1760 | } | 1758 | } |
| @@ -1770,7 +1768,7 @@ QPDF::replaceObject(QPDFObjGen const& og, QPDFObjectHandle oh) | @@ -1770,7 +1768,7 @@ QPDF::replaceObject(QPDFObjGen const& og, QPDFObjectHandle oh) | ||
| 1770 | } | 1768 | } |
| 1771 | 1769 | ||
| 1772 | void | 1770 | void |
| 1773 | -QPDF::Objects::erase(QPDFObjGen og) | 1771 | +Objects::erase(QPDFObjGen og) |
| 1774 | { | 1772 | { |
| 1775 | if (auto cached = obj_cache.find(og); cached != obj_cache.end()) { | 1773 | if (auto cached = obj_cache.find(og); cached != obj_cache.end()) { |
| 1776 | // Take care of any object handles that may be floating around. | 1774 | // Take care of any object handles that may be floating around. |
| @@ -1790,11 +1788,11 @@ QPDF::swapObjects(QPDFObjGen const& og1, QPDFObjGen const& og2) | @@ -1790,11 +1788,11 @@ QPDF::swapObjects(QPDFObjGen const& og1, QPDFObjGen const& og2) | ||
| 1790 | } | 1788 | } |
| 1791 | 1789 | ||
| 1792 | size_t | 1790 | size_t |
| 1793 | -QPDF::Objects::table_size() | 1791 | +Objects::table_size() |
| 1794 | { | 1792 | { |
| 1795 | // If obj_cache is dense, accommodate all object in tables,else accommodate only original | 1793 | // If obj_cache is dense, accommodate all object in tables,else accommodate only original |
| 1796 | // objects. | 1794 | // objects. |
| 1797 | - auto max_xref = toI(m->xref_table.size()); | 1795 | + auto max_xref = toI(xref.size()); |
| 1798 | if (max_xref > 0) { | 1796 | if (max_xref > 0) { |
| 1799 | --max_xref; | 1797 | --max_xref; |
| 1800 | } | 1798 | } |
| @@ -1813,20 +1811,20 @@ QPDF::Objects::table_size() | @@ -1813,20 +1811,20 @@ QPDF::Objects::table_size() | ||
| 1813 | } | 1811 | } |
| 1814 | 1812 | ||
| 1815 | std::vector<QPDFObjGen> | 1813 | std::vector<QPDFObjGen> |
| 1816 | -QPDF::Objects::compressible_vector() | 1814 | +Objects::compressible_vector() |
| 1817 | { | 1815 | { |
| 1818 | return compressible<QPDFObjGen>(); | 1816 | return compressible<QPDFObjGen>(); |
| 1819 | } | 1817 | } |
| 1820 | 1818 | ||
| 1821 | std::vector<bool> | 1819 | std::vector<bool> |
| 1822 | -QPDF::Objects::compressible_set() | 1820 | +Objects::compressible_set() |
| 1823 | { | 1821 | { |
| 1824 | return compressible<bool>(); | 1822 | return compressible<bool>(); |
| 1825 | } | 1823 | } |
| 1826 | 1824 | ||
| 1827 | template <typename T> | 1825 | template <typename T> |
| 1828 | std::vector<T> | 1826 | std::vector<T> |
| 1829 | -QPDF::Objects::compressible() | 1827 | +Objects::compressible() |
| 1830 | { | 1828 | { |
| 1831 | // Return a list of objects that are allowed to be in object streams. Walk through the objects | 1829 | // Return a list of objects that are allowed to be in object streams. Walk through the objects |
| 1832 | // by traversing the document from the root, including a traversal of the pages tree. This | 1830 | // by traversing the document from the root, including a traversal of the pages tree. This |
| @@ -1835,14 +1833,14 @@ QPDF::Objects::compressible() | @@ -1835,14 +1833,14 @@ QPDF::Objects::compressible() | ||
| 1835 | // iterating through the xref table since it avoids preserving orphaned items. | 1833 | // iterating through the xref table since it avoids preserving orphaned items. |
| 1836 | 1834 | ||
| 1837 | // Exclude encryption dictionary, if any | 1835 | // Exclude encryption dictionary, if any |
| 1838 | - QPDFObjectHandle encryption_dict = m->xref_table.trailer().getKey("/Encrypt"); | 1836 | + QPDFObjectHandle encryption_dict = trailer().getKey("/Encrypt"); |
| 1839 | QPDFObjGen encryption_dict_og = encryption_dict.getObjGen(); | 1837 | QPDFObjGen encryption_dict_og = encryption_dict.getObjGen(); |
| 1840 | 1838 | ||
| 1841 | const size_t max_obj = qpdf.getObjectCount(); | 1839 | const size_t max_obj = qpdf.getObjectCount(); |
| 1842 | std::vector<bool> visited(max_obj, false); | 1840 | std::vector<bool> visited(max_obj, false); |
| 1843 | std::vector<QPDFObjectHandle> queue; | 1841 | std::vector<QPDFObjectHandle> queue; |
| 1844 | queue.reserve(512); | 1842 | queue.reserve(512); |
| 1845 | - queue.push_back(m->xref_table.trailer()); | 1843 | + queue.emplace_back(trailer()); |
| 1846 | std::vector<T> result; | 1844 | std::vector<T> result; |
| 1847 | if constexpr (std::is_same_v<T, QPDFObjGen>) { | 1845 | if constexpr (std::is_same_v<T, QPDFObjGen>) { |
| 1848 | result.reserve(obj_cache.size()); | 1846 | result.reserve(obj_cache.size()); |
libqpdf/QPDF_optimization.cc
| @@ -79,9 +79,9 @@ QPDF::optimize( | @@ -79,9 +79,9 @@ QPDF::optimize( | ||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | void | 81 | void |
| 82 | -QPDF::optimize(QPDF::Xref_table const& xref) | 82 | +QPDF::optimize(QPDF::Objects const& objects) |
| 83 | { | 83 | { |
| 84 | - optimize_internal(xref, false, nullptr); | 84 | + optimize_internal(objects, false, nullptr); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | template <typename T> | 87 | template <typename T> |
| @@ -121,13 +121,13 @@ QPDF::optimize_internal( | @@ -121,13 +121,13 @@ QPDF::optimize_internal( | ||
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | // Traverse document-level items | 123 | // Traverse document-level items |
| 124 | - for (auto const& key: m->xref_table.trailer().getKeys()) { | 124 | + for (auto const& key: m->objects.trailer().getKeys()) { |
| 125 | if (key == "/Root") { | 125 | if (key == "/Root") { |
| 126 | // handled separately | 126 | // handled separately |
| 127 | } else { | 127 | } else { |
| 128 | updateObjectMaps( | 128 | updateObjectMaps( |
| 129 | ObjUser(ObjUser::ou_trailer_key, key), | 129 | ObjUser(ObjUser::ou_trailer_key, key), |
| 130 | - m->xref_table.trailer().getKey(key), | 130 | + m->objects.trailer().getKey(key), |
| 131 | skip_stream_parameters); | 131 | skip_stream_parameters); |
| 132 | } | 132 | } |
| 133 | } | 133 | } |
| @@ -175,7 +175,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys) | @@ -175,7 +175,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys) | ||
| 175 | // values for them. | 175 | // values for them. |
| 176 | std::map<std::string, std::vector<QPDFObjectHandle>> key_ancestors; | 176 | std::map<std::string, std::vector<QPDFObjectHandle>> key_ancestors; |
| 177 | pushInheritedAttributesToPageInternal( | 177 | pushInheritedAttributesToPageInternal( |
| 178 | - m->xref_table.trailer().getKey("/Root").getKey("/Pages"), | 178 | + m->objects.trailer().getKey("/Root").getKey("/Pages"), |
| 179 | key_ancestors, | 179 | key_ancestors, |
| 180 | allow_changes, | 180 | allow_changes, |
| 181 | warn_skipped_keys); | 181 | warn_skipped_keys); |
| @@ -450,8 +450,9 @@ QPDF::filterCompressedObjects(QPDFWriter::ObjTable const& obj) | @@ -450,8 +450,9 @@ QPDF::filterCompressedObjects(QPDFWriter::ObjTable const& obj) | ||
| 450 | } | 450 | } |
| 451 | 451 | ||
| 452 | void | 452 | void |
| 453 | -QPDF::filterCompressedObjects(QPDF::Xref_table const& xref) | 453 | +QPDF::filterCompressedObjects(QPDF::Objects const& objects) |
| 454 | { | 454 | { |
| 455 | + auto const& xref = objects.xref_table(); | ||
| 455 | if (!xref.object_streams()) { | 456 | if (!xref.object_streams()) { |
| 456 | return; | 457 | return; |
| 457 | } | 458 | } |
libqpdf/qpdf/QPDF_objects.hh
| @@ -3,17 +3,410 @@ | @@ -3,17 +3,410 @@ | ||
| 3 | 3 | ||
| 4 | #include <qpdf/QPDF.hh> | 4 | #include <qpdf/QPDF.hh> |
| 5 | 5 | ||
| 6 | +#include <variant> | ||
| 7 | + | ||
| 6 | // The Objects class is responsible for keeping track of all objects belonging to a QPDF instance, | 8 | // The Objects class is responsible for keeping track of all objects belonging to a QPDF instance, |
| 7 | // including loading it from an input source when required. | 9 | // including loading it from an input source when required. |
| 8 | class QPDF::Objects | 10 | class QPDF::Objects |
| 9 | { | 11 | { |
| 10 | public: | 12 | public: |
| 11 | - Objects(QPDF& qpdf, QPDF::Members* m) : | 13 | + // Xref_table encapsulates the pdf's xref table and trailer. |
| 14 | + class Xref_table | ||
| 15 | + { | ||
| 16 | + public: | ||
| 17 | + Xref_table(Objects& objects) : | ||
| 18 | + qpdf(objects.qpdf), | ||
| 19 | + objects(objects), | ||
| 20 | + file(objects.file) | ||
| 21 | + { | ||
| 22 | + tokenizer.allowEOF(); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + void initialize(); | ||
| 26 | + void initialize_empty(); | ||
| 27 | + void initialize_json(); | ||
| 28 | + void reconstruct(QPDFExc& e); | ||
| 29 | + void show(); | ||
| 30 | + bool resolve(); | ||
| 31 | + | ||
| 32 | + QPDFObjectHandle | ||
| 33 | + trailer() noexcept | ||
| 34 | + { | ||
| 35 | + return trailer_; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + QPDFObjectHandle const& | ||
| 39 | + trailer() const noexcept | ||
| 40 | + { | ||
| 41 | + return trailer_; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + void | ||
| 45 | + trailer(QPDFObjectHandle&& oh) | ||
| 46 | + { | ||
| 47 | + trailer_ = std::move(oh); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + // Returns 0 if og is not in table. | ||
| 51 | + size_t | ||
| 52 | + type(QPDFObjGen og) const | ||
| 53 | + { | ||
| 54 | + int id = og.getObj(); | ||
| 55 | + if (id < 1 || static_cast<size_t>(id) >= table.size()) { | ||
| 56 | + return 0; | ||
| 57 | + } | ||
| 58 | + auto& e = table[static_cast<size_t>(id)]; | ||
| 59 | + return e.gen() == og.getGen() ? e.type() : 0; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + // Returns 0 if og is not in table. | ||
| 63 | + size_t | ||
| 64 | + type(size_t id) const noexcept | ||
| 65 | + { | ||
| 66 | + if (id >= table.size()) { | ||
| 67 | + return 0; | ||
| 68 | + } | ||
| 69 | + return table[id].type(); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + // Returns 0 if og is not in table. | ||
| 73 | + qpdf_offset_t | ||
| 74 | + offset(QPDFObjGen og) const noexcept | ||
| 75 | + { | ||
| 76 | + int id = og.getObj(); | ||
| 77 | + if (id < 1 || static_cast<size_t>(id) >= table.size()) { | ||
| 78 | + return 0; | ||
| 79 | + } | ||
| 80 | + return table[static_cast<size_t>(id)].offset(); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + // Returns 0 if id is not in table. | ||
| 84 | + int | ||
| 85 | + stream_number(int id) const noexcept | ||
| 86 | + { | ||
| 87 | + if (id < 1 || static_cast<size_t>(id) >= table.size()) { | ||
| 88 | + return 0; | ||
| 89 | + } | ||
| 90 | + return table[static_cast<size_t>(id)].stream_number(); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + int | ||
| 94 | + stream_index(int id) const noexcept | ||
| 95 | + { | ||
| 96 | + if (id < 1 || static_cast<size_t>(id) >= table.size()) { | ||
| 97 | + return 0; | ||
| 98 | + } | ||
| 99 | + return table[static_cast<size_t>(id)].stream_index(); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + QPDFObjGen at_offset(qpdf_offset_t offset) const noexcept; | ||
| 103 | + | ||
| 104 | + std::map<QPDFObjGen, QPDFXRefEntry> as_map() const; | ||
| 105 | + | ||
| 106 | + bool | ||
| 107 | + object_streams() const noexcept | ||
| 108 | + { | ||
| 109 | + return object_streams_; | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + // Return a vector of object id and stream number for each compressed object. | ||
| 113 | + std::vector<std::pair<unsigned int, int>> | ||
| 114 | + compressed_objects() const | ||
| 115 | + { | ||
| 116 | + if (!initialized()) { | ||
| 117 | + throw std::logic_error("Xref_table::compressed_objects called before parsing."); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + std::vector<std::pair<unsigned int, int>> result; | ||
| 121 | + result.reserve(table.size()); | ||
| 122 | + | ||
| 123 | + unsigned int i{0}; | ||
| 124 | + for (auto const& item: table) { | ||
| 125 | + if (item.type() == 2) { | ||
| 126 | + result.emplace_back(i, item.stream_number()); | ||
| 127 | + } | ||
| 128 | + ++i; | ||
| 129 | + } | ||
| 130 | + return result; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + // Temporary access to underlying table size | ||
| 134 | + size_t | ||
| 135 | + size() const noexcept | ||
| 136 | + { | ||
| 137 | + return table.size(); | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + void | ||
| 141 | + ignore_streams(bool val) noexcept | ||
| 142 | + { | ||
| 143 | + ignore_streams_ = val; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + bool | ||
| 147 | + initialized() const noexcept | ||
| 148 | + { | ||
| 149 | + return initialized_; | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + void | ||
| 153 | + attempt_recovery(bool val) noexcept | ||
| 154 | + { | ||
| 155 | + attempt_recovery_ = val; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + int | ||
| 159 | + max_id() const noexcept | ||
| 160 | + { | ||
| 161 | + return max_id_; | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + // For Linearization | ||
| 165 | + | ||
| 166 | + qpdf_offset_t | ||
| 167 | + end_after_space(QPDFObjGen og) | ||
| 168 | + { | ||
| 169 | + auto& e = entry(toS(og.getObj())); | ||
| 170 | + switch (e.type()) { | ||
| 171 | + case 1: | ||
| 172 | + return e.end_after_space_; | ||
| 173 | + case 2: | ||
| 174 | + { | ||
| 175 | + auto es = entry(toS(e.stream_number())); | ||
| 176 | + return es.type() == 1 ? es.end_after_space_ : 0; | ||
| 177 | + } | ||
| 178 | + default: | ||
| 179 | + return 0; | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + qpdf_offset_t | ||
| 184 | + end_before_space(QPDFObjGen og) | ||
| 185 | + { | ||
| 186 | + auto& e = entry(toS(og.getObj())); | ||
| 187 | + switch (e.type()) { | ||
| 188 | + case 1: | ||
| 189 | + return e.end_before_space_; | ||
| 190 | + case 2: | ||
| 191 | + { | ||
| 192 | + auto es = entry(toS(e.stream_number())); | ||
| 193 | + return es.type() == 1 ? es.end_before_space_ : 0; | ||
| 194 | + } | ||
| 195 | + default: | ||
| 196 | + return 0; | ||
| 197 | + } | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + void | ||
| 201 | + linearization_offsets(size_t id, qpdf_offset_t before, qpdf_offset_t after) | ||
| 202 | + { | ||
| 203 | + if (type(id)) { | ||
| 204 | + table[id].end_before_space_ = before; | ||
| 205 | + table[id].end_after_space_ = after; | ||
| 206 | + } | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + bool | ||
| 210 | + uncompressed_after_compressed() const noexcept | ||
| 211 | + { | ||
| 212 | + return uncompressed_after_compressed_; | ||
| 213 | + } | ||
| 214 | + | ||
| 215 | + // Actual value from file | ||
| 216 | + qpdf_offset_t | ||
| 217 | + first_item_offset() const noexcept | ||
| 218 | + { | ||
| 219 | + return first_item_offset_; | ||
| 220 | + } | ||
| 221 | + | ||
| 222 | + private: | ||
| 223 | + // Object, count, offset of first entry | ||
| 224 | + typedef std::tuple<int, int, qpdf_offset_t> Subsection; | ||
| 225 | + | ||
| 226 | + struct Uncompressed | ||
| 227 | + { | ||
| 228 | + Uncompressed(qpdf_offset_t offset) : | ||
| 229 | + offset(offset) | ||
| 230 | + { | ||
| 231 | + } | ||
| 232 | + qpdf_offset_t offset; | ||
| 233 | + }; | ||
| 234 | + | ||
| 235 | + struct Compressed | ||
| 236 | + { | ||
| 237 | + Compressed(int stream_number, int stream_index) : | ||
| 238 | + stream_number(stream_number), | ||
| 239 | + stream_index(stream_index) | ||
| 240 | + { | ||
| 241 | + } | ||
| 242 | + int stream_number{0}; | ||
| 243 | + int stream_index{0}; | ||
| 244 | + }; | ||
| 245 | + | ||
| 246 | + typedef std::variant<std::monostate, Uncompressed, Compressed> Xref; | ||
| 247 | + | ||
| 248 | + struct Entry | ||
| 249 | + { | ||
| 250 | + Entry() = default; | ||
| 251 | + | ||
| 252 | + Entry(int gen, Xref entry) : | ||
| 253 | + gen_(gen), | ||
| 254 | + entry(entry) | ||
| 255 | + { | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + int | ||
| 259 | + gen() const noexcept | ||
| 260 | + { | ||
| 261 | + return gen_; | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + size_t | ||
| 265 | + type() const noexcept | ||
| 266 | + { | ||
| 267 | + return entry.index(); | ||
| 268 | + } | ||
| 269 | + | ||
| 270 | + qpdf_offset_t | ||
| 271 | + offset() const noexcept | ||
| 272 | + { | ||
| 273 | + return type() == 1 ? std::get<1>(entry).offset : 0; | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + int | ||
| 277 | + stream_number() const noexcept | ||
| 278 | + { | ||
| 279 | + return type() == 2 ? std::get<2>(entry).stream_number : 0; | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + int | ||
| 283 | + stream_index() const noexcept | ||
| 284 | + { | ||
| 285 | + return type() == 2 ? std::get<2>(entry).stream_index : 0; | ||
| 286 | + } | ||
| 287 | + | ||
| 288 | + int gen_{0}; | ||
| 289 | + Xref entry; | ||
| 290 | + qpdf_offset_t end_before_space_{0}; | ||
| 291 | + qpdf_offset_t end_after_space_{0}; | ||
| 292 | + }; | ||
| 293 | + | ||
| 294 | + Entry& | ||
| 295 | + entry(size_t id) | ||
| 296 | + { | ||
| 297 | + return id < table.size() ? table[id] : table[0]; | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + void read(qpdf_offset_t offset); | ||
| 301 | + | ||
| 302 | + // Methods to parse tables | ||
| 303 | + qpdf_offset_t process_section(qpdf_offset_t offset); | ||
| 304 | + std::vector<Subsection> subsections(std::string& line); | ||
| 305 | + std::vector<Subsection> bad_subsections(std::string& line, qpdf_offset_t offset); | ||
| 306 | + Subsection subsection(std::string const& line); | ||
| 307 | + std::tuple<bool, qpdf_offset_t, int, char> read_entry(); | ||
| 308 | + std::tuple<bool, qpdf_offset_t, int, char> read_bad_entry(); | ||
| 309 | + | ||
| 310 | + // Methods to parse streams | ||
| 311 | + qpdf_offset_t read_stream(qpdf_offset_t offset); | ||
| 312 | + qpdf_offset_t process_stream(qpdf_offset_t offset, QPDFObjectHandle& xref_stream); | ||
| 313 | + std::pair<int, std::array<int, 3>> | ||
| 314 | + process_W(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged); | ||
| 315 | + std::pair<int, size_t> process_Size( | ||
| 316 | + QPDFObjectHandle& dict, | ||
| 317 | + int entry_size, | ||
| 318 | + std::function<QPDFExc(std::string_view)> damaged); | ||
| 319 | + std::pair<int, std::vector<std::pair<int, int>>> process_Index( | ||
| 320 | + QPDFObjectHandle& dict, | ||
| 321 | + int max_num_entries, | ||
| 322 | + std::function<QPDFExc(std::string_view)> damaged); | ||
| 323 | + | ||
| 324 | + QPDFObjectHandle read_trailer(); | ||
| 325 | + | ||
| 326 | + QPDFTokenizer::Token | ||
| 327 | + read_token(size_t max_len = 0) | ||
| 328 | + { | ||
| 329 | + return tokenizer.readToken(*file, "", true, max_len); | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + // Methods to insert table entries | ||
| 333 | + void insert(int obj, int f0, qpdf_offset_t f1, int f2); | ||
| 334 | + void insert_free(QPDFObjGen); | ||
| 335 | + | ||
| 336 | + QPDFExc | ||
| 337 | + damaged_pdf(std::string const& msg) | ||
| 338 | + { | ||
| 339 | + return qpdf.damagedPDF("", 0, msg); | ||
| 340 | + } | ||
| 341 | + | ||
| 342 | + QPDFExc | ||
| 343 | + damaged_table(std::string const& msg) | ||
| 344 | + { | ||
| 345 | + return qpdf.damagedPDF("xref table", msg); | ||
| 346 | + } | ||
| 347 | + | ||
| 348 | + void | ||
| 349 | + warn_damaged(std::string const& msg) | ||
| 350 | + { | ||
| 351 | + qpdf.warn(damaged_pdf(msg)); | ||
| 352 | + } | ||
| 353 | + | ||
| 354 | + QPDF& qpdf; | ||
| 355 | + QPDF::Objects& objects; | ||
| 356 | + InputSource* const& file; | ||
| 357 | + QPDFTokenizer tokenizer; | ||
| 358 | + | ||
| 359 | + std::vector<Entry> table; | ||
| 360 | + QPDFObjectHandle trailer_; | ||
| 361 | + | ||
| 362 | + bool attempt_recovery_{true}; | ||
| 363 | + bool initialized_{false}; | ||
| 364 | + bool ignore_streams_{false}; | ||
| 365 | + bool reconstructed_{false}; | ||
| 366 | + bool object_streams_{false}; | ||
| 367 | + // Before the xref table is initialized, max_id_ is an upper bound on the possible object | ||
| 368 | + // ids that could be present in the PDF file. Once the trailer has been read, max_id_ is set | ||
| 369 | + // to the value of /Size. If the file is damaged, max_id_ becomes the maximum object id in | ||
| 370 | + // the xref table after reconstruction. | ||
| 371 | + int max_id_{std::numeric_limits<int>::max() - 1}; | ||
| 372 | + | ||
| 373 | + // Linearization data | ||
| 374 | + bool uncompressed_after_compressed_{false}; | ||
| 375 | + qpdf_offset_t first_item_offset_{0}; // actual value from file | ||
| 376 | + }; // Xref_table; | ||
| 377 | + | ||
| 378 | + Objects(QPDF& qpdf, QPDF::Members* m, InputSource* const& file) : | ||
| 12 | qpdf(qpdf), | 379 | qpdf(qpdf), |
| 13 | - m(m) | 380 | + file(file), |
| 381 | + m(m), | ||
| 382 | + xref(*this) | ||
| 14 | { | 383 | { |
| 15 | } | 384 | } |
| 16 | 385 | ||
| 386 | + Xref_table& | ||
| 387 | + xref_table() noexcept | ||
| 388 | + { | ||
| 389 | + return xref; | ||
| 390 | + } | ||
| 391 | + | ||
| 392 | + Xref_table const& | ||
| 393 | + xref_table() const noexcept | ||
| 394 | + { | ||
| 395 | + return xref; | ||
| 396 | + } | ||
| 397 | + | ||
| 398 | + QPDFObjectHandle | ||
| 399 | + trailer() noexcept | ||
| 400 | + { | ||
| 401 | + return xref.trailer(); | ||
| 402 | + } | ||
| 403 | + | ||
| 404 | + QPDFObjectHandle const& | ||
| 405 | + trailer() const noexcept | ||
| 406 | + { | ||
| 407 | + return xref.trailer(); | ||
| 408 | + } | ||
| 409 | + | ||
| 17 | std::map<QPDFObjGen, ObjCache> obj_cache; | 410 | std::map<QPDFObjGen, ObjCache> obj_cache; |
| 18 | 411 | ||
| 19 | QPDFObjectHandle readObjectInStream(std::shared_ptr<InputSource>& input, int obj); | 412 | QPDFObjectHandle readObjectInStream(std::shared_ptr<InputSource>& input, int obj); |
| @@ -42,8 +435,6 @@ class QPDF::Objects | @@ -42,8 +435,6 @@ class QPDF::Objects | ||
| 42 | size_t table_size(); | 435 | size_t table_size(); |
| 43 | 436 | ||
| 44 | private: | 437 | private: |
| 45 | - friend class QPDF::Xref_table; | ||
| 46 | - | ||
| 47 | void erase(QPDFObjGen og); | 438 | void erase(QPDFObjGen og); |
| 48 | bool cached(QPDFObjGen og); | 439 | bool cached(QPDFObjGen og); |
| 49 | bool unresolved(QPDFObjGen og); | 440 | bool unresolved(QPDFObjGen og); |
| @@ -55,7 +446,9 @@ class QPDF::Objects | @@ -55,7 +446,9 @@ class QPDF::Objects | ||
| 55 | std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset); | 446 | std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset); |
| 56 | 447 | ||
| 57 | QPDF& qpdf; | 448 | QPDF& qpdf; |
| 449 | + InputSource* const& file; | ||
| 58 | QPDF::Members* m; | 450 | QPDF::Members* m; |
| 451 | + Xref_table xref; | ||
| 59 | }; // Objects | 452 | }; // Objects |
| 60 | 453 | ||
| 61 | #endif // QPDF_OBJECTS_HH | 454 | #endif // QPDF_OBJECTS_HH |
libqpdf/qpdf/QPDF_private.hh
| @@ -7,363 +7,6 @@ | @@ -7,363 +7,6 @@ | ||
| 7 | 7 | ||
| 8 | #include <variant> | 8 | #include <variant> |
| 9 | 9 | ||
| 10 | -// Xref_table encapsulates the pdf's xref table and trailer. | ||
| 11 | -class QPDF::Xref_table | ||
| 12 | -{ | ||
| 13 | - public: | ||
| 14 | - Xref_table(QPDF& qpdf, QPDF::Objects& objects, InputSource* const& file) : | ||
| 15 | - qpdf(qpdf), | ||
| 16 | - objects(objects), | ||
| 17 | - file(file) | ||
| 18 | - { | ||
| 19 | - tokenizer.allowEOF(); | ||
| 20 | - } | ||
| 21 | - | ||
| 22 | - void initialize(); | ||
| 23 | - void initialize_empty(); | ||
| 24 | - void initialize_json(); | ||
| 25 | - void reconstruct(QPDFExc& e); | ||
| 26 | - void show(); | ||
| 27 | - bool resolve(); | ||
| 28 | - | ||
| 29 | - QPDFObjectHandle | ||
| 30 | - trailer() const | ||
| 31 | - { | ||
| 32 | - return trailer_; | ||
| 33 | - } | ||
| 34 | - | ||
| 35 | - void | ||
| 36 | - trailer(QPDFObjectHandle&& oh) | ||
| 37 | - { | ||
| 38 | - trailer_ = std::move(oh); | ||
| 39 | - } | ||
| 40 | - | ||
| 41 | - // Returns 0 if og is not in table. | ||
| 42 | - size_t | ||
| 43 | - type(QPDFObjGen og) const | ||
| 44 | - { | ||
| 45 | - int id = og.getObj(); | ||
| 46 | - if (id < 1 || static_cast<size_t>(id) >= table.size()) { | ||
| 47 | - return 0; | ||
| 48 | - } | ||
| 49 | - auto& e = table[static_cast<size_t>(id)]; | ||
| 50 | - return e.gen() == og.getGen() ? e.type() : 0; | ||
| 51 | - } | ||
| 52 | - | ||
| 53 | - // Returns 0 if og is not in table. | ||
| 54 | - size_t | ||
| 55 | - type(size_t id) const noexcept | ||
| 56 | - { | ||
| 57 | - if (id >= table.size()) { | ||
| 58 | - return 0; | ||
| 59 | - } | ||
| 60 | - return table[id].type(); | ||
| 61 | - } | ||
| 62 | - | ||
| 63 | - // Returns 0 if og is not in table. | ||
| 64 | - qpdf_offset_t | ||
| 65 | - offset(QPDFObjGen og) const noexcept | ||
| 66 | - { | ||
| 67 | - int id = og.getObj(); | ||
| 68 | - if (id < 1 || static_cast<size_t>(id) >= table.size()) { | ||
| 69 | - return 0; | ||
| 70 | - } | ||
| 71 | - return table[static_cast<size_t>(id)].offset(); | ||
| 72 | - } | ||
| 73 | - | ||
| 74 | - // Returns 0 if id is not in table. | ||
| 75 | - int | ||
| 76 | - stream_number(int id) const noexcept | ||
| 77 | - { | ||
| 78 | - if (id < 1 || static_cast<size_t>(id) >= table.size()) { | ||
| 79 | - return 0; | ||
| 80 | - } | ||
| 81 | - return table[static_cast<size_t>(id)].stream_number(); | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | - int | ||
| 85 | - stream_index(int id) const noexcept | ||
| 86 | - { | ||
| 87 | - if (id < 1 || static_cast<size_t>(id) >= table.size()) { | ||
| 88 | - return 0; | ||
| 89 | - } | ||
| 90 | - return table[static_cast<size_t>(id)].stream_index(); | ||
| 91 | - } | ||
| 92 | - | ||
| 93 | - QPDFObjGen at_offset(qpdf_offset_t offset) const noexcept; | ||
| 94 | - | ||
| 95 | - std::map<QPDFObjGen, QPDFXRefEntry> as_map() const; | ||
| 96 | - | ||
| 97 | - bool | ||
| 98 | - object_streams() const noexcept | ||
| 99 | - { | ||
| 100 | - return object_streams_; | ||
| 101 | - } | ||
| 102 | - | ||
| 103 | - // Return a vector of object id and stream number for each compressed object. | ||
| 104 | - std::vector<std::pair<unsigned int, int>> | ||
| 105 | - compressed_objects() const | ||
| 106 | - { | ||
| 107 | - if (!initialized()) { | ||
| 108 | - throw std::logic_error("Xref_table::compressed_objects called before parsing."); | ||
| 109 | - } | ||
| 110 | - | ||
| 111 | - std::vector<std::pair<unsigned int, int>> result; | ||
| 112 | - result.reserve(table.size()); | ||
| 113 | - | ||
| 114 | - unsigned int i{0}; | ||
| 115 | - for (auto const& item: table) { | ||
| 116 | - if (item.type() == 2) { | ||
| 117 | - result.emplace_back(i, item.stream_number()); | ||
| 118 | - } | ||
| 119 | - ++i; | ||
| 120 | - } | ||
| 121 | - return result; | ||
| 122 | - } | ||
| 123 | - | ||
| 124 | - // Temporary access to underlying table size | ||
| 125 | - size_t | ||
| 126 | - size() const noexcept | ||
| 127 | - { | ||
| 128 | - return table.size(); | ||
| 129 | - } | ||
| 130 | - | ||
| 131 | - void | ||
| 132 | - ignore_streams(bool val) noexcept | ||
| 133 | - { | ||
| 134 | - ignore_streams_ = val; | ||
| 135 | - } | ||
| 136 | - | ||
| 137 | - bool | ||
| 138 | - initialized() const noexcept | ||
| 139 | - { | ||
| 140 | - return initialized_; | ||
| 141 | - } | ||
| 142 | - | ||
| 143 | - void | ||
| 144 | - attempt_recovery(bool val) noexcept | ||
| 145 | - { | ||
| 146 | - attempt_recovery_ = val; | ||
| 147 | - } | ||
| 148 | - | ||
| 149 | - int | ||
| 150 | - max_id() const noexcept | ||
| 151 | - { | ||
| 152 | - return max_id_; | ||
| 153 | - } | ||
| 154 | - | ||
| 155 | - // For Linearization | ||
| 156 | - | ||
| 157 | - qpdf_offset_t | ||
| 158 | - end_after_space(QPDFObjGen og) | ||
| 159 | - { | ||
| 160 | - auto& e = entry(toS(og.getObj())); | ||
| 161 | - switch (e.type()) { | ||
| 162 | - case 1: | ||
| 163 | - return e.end_after_space_; | ||
| 164 | - case 2: | ||
| 165 | - { | ||
| 166 | - auto es = entry(toS(e.stream_number())); | ||
| 167 | - return es.type() == 1 ? es.end_after_space_ : 0; | ||
| 168 | - } | ||
| 169 | - default: | ||
| 170 | - return 0; | ||
| 171 | - } | ||
| 172 | - } | ||
| 173 | - | ||
| 174 | - qpdf_offset_t | ||
| 175 | - end_before_space(QPDFObjGen og) | ||
| 176 | - { | ||
| 177 | - auto& e = entry(toS(og.getObj())); | ||
| 178 | - switch (e.type()) { | ||
| 179 | - case 1: | ||
| 180 | - return e.end_before_space_; | ||
| 181 | - case 2: | ||
| 182 | - { | ||
| 183 | - auto es = entry(toS(e.stream_number())); | ||
| 184 | - return es.type() == 1 ? es.end_before_space_ : 0; | ||
| 185 | - } | ||
| 186 | - default: | ||
| 187 | - return 0; | ||
| 188 | - } | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - void | ||
| 192 | - linearization_offsets(size_t id, qpdf_offset_t before, qpdf_offset_t after) | ||
| 193 | - { | ||
| 194 | - if (type(id)) { | ||
| 195 | - table[id].end_before_space_ = before; | ||
| 196 | - table[id].end_after_space_ = after; | ||
| 197 | - } | ||
| 198 | - } | ||
| 199 | - | ||
| 200 | - bool | ||
| 201 | - uncompressed_after_compressed() const noexcept | ||
| 202 | - { | ||
| 203 | - return uncompressed_after_compressed_; | ||
| 204 | - } | ||
| 205 | - | ||
| 206 | - // Actual value from file | ||
| 207 | - qpdf_offset_t | ||
| 208 | - first_item_offset() const noexcept | ||
| 209 | - { | ||
| 210 | - return first_item_offset_; | ||
| 211 | - } | ||
| 212 | - | ||
| 213 | - private: | ||
| 214 | - // Object, count, offset of first entry | ||
| 215 | - typedef std::tuple<int, int, qpdf_offset_t> Subsection; | ||
| 216 | - | ||
| 217 | - struct Uncompressed | ||
| 218 | - { | ||
| 219 | - Uncompressed(qpdf_offset_t offset) : | ||
| 220 | - offset(offset) | ||
| 221 | - { | ||
| 222 | - } | ||
| 223 | - qpdf_offset_t offset; | ||
| 224 | - }; | ||
| 225 | - | ||
| 226 | - struct Compressed | ||
| 227 | - { | ||
| 228 | - Compressed(int stream_number, int stream_index) : | ||
| 229 | - stream_number(stream_number), | ||
| 230 | - stream_index(stream_index) | ||
| 231 | - { | ||
| 232 | - } | ||
| 233 | - int stream_number{0}; | ||
| 234 | - int stream_index{0}; | ||
| 235 | - }; | ||
| 236 | - | ||
| 237 | - typedef std::variant<std::monostate, Uncompressed, Compressed> Xref; | ||
| 238 | - | ||
| 239 | - struct Entry | ||
| 240 | - { | ||
| 241 | - Entry() = default; | ||
| 242 | - | ||
| 243 | - Entry(int gen, Xref entry) : | ||
| 244 | - gen_(gen), | ||
| 245 | - entry(entry) | ||
| 246 | - { | ||
| 247 | - } | ||
| 248 | - | ||
| 249 | - int | ||
| 250 | - gen() const noexcept | ||
| 251 | - { | ||
| 252 | - return gen_; | ||
| 253 | - } | ||
| 254 | - | ||
| 255 | - size_t | ||
| 256 | - type() const noexcept | ||
| 257 | - { | ||
| 258 | - return entry.index(); | ||
| 259 | - } | ||
| 260 | - | ||
| 261 | - qpdf_offset_t | ||
| 262 | - offset() const noexcept | ||
| 263 | - { | ||
| 264 | - return type() == 1 ? std::get<1>(entry).offset : 0; | ||
| 265 | - } | ||
| 266 | - | ||
| 267 | - int | ||
| 268 | - stream_number() const noexcept | ||
| 269 | - { | ||
| 270 | - return type() == 2 ? std::get<2>(entry).stream_number : 0; | ||
| 271 | - } | ||
| 272 | - | ||
| 273 | - int | ||
| 274 | - stream_index() const noexcept | ||
| 275 | - { | ||
| 276 | - return type() == 2 ? std::get<2>(entry).stream_index : 0; | ||
| 277 | - } | ||
| 278 | - | ||
| 279 | - int gen_{0}; | ||
| 280 | - Xref entry; | ||
| 281 | - qpdf_offset_t end_before_space_{0}; | ||
| 282 | - qpdf_offset_t end_after_space_{0}; | ||
| 283 | - }; | ||
| 284 | - | ||
| 285 | - Entry& | ||
| 286 | - entry(size_t id) | ||
| 287 | - { | ||
| 288 | - return id < table.size() ? table[id] : table[0]; | ||
| 289 | - } | ||
| 290 | - | ||
| 291 | - void read(qpdf_offset_t offset); | ||
| 292 | - | ||
| 293 | - // Methods to parse tables | ||
| 294 | - qpdf_offset_t process_section(qpdf_offset_t offset); | ||
| 295 | - std::vector<Subsection> subsections(std::string& line); | ||
| 296 | - std::vector<Subsection> bad_subsections(std::string& line, qpdf_offset_t offset); | ||
| 297 | - Subsection subsection(std::string const& line); | ||
| 298 | - std::tuple<bool, qpdf_offset_t, int, char> read_entry(); | ||
| 299 | - std::tuple<bool, qpdf_offset_t, int, char> read_bad_entry(); | ||
| 300 | - | ||
| 301 | - // Methods to parse streams | ||
| 302 | - qpdf_offset_t read_stream(qpdf_offset_t offset); | ||
| 303 | - qpdf_offset_t process_stream(qpdf_offset_t offset, QPDFObjectHandle& xref_stream); | ||
| 304 | - std::pair<int, std::array<int, 3>> | ||
| 305 | - process_W(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged); | ||
| 306 | - std::pair<int, size_t> process_Size( | ||
| 307 | - QPDFObjectHandle& dict, int entry_size, std::function<QPDFExc(std::string_view)> damaged); | ||
| 308 | - std::pair<int, std::vector<std::pair<int, int>>> process_Index( | ||
| 309 | - QPDFObjectHandle& dict, | ||
| 310 | - int max_num_entries, | ||
| 311 | - std::function<QPDFExc(std::string_view)> damaged); | ||
| 312 | - | ||
| 313 | - QPDFObjectHandle read_trailer(); | ||
| 314 | - | ||
| 315 | - QPDFTokenizer::Token | ||
| 316 | - read_token(size_t max_len = 0) | ||
| 317 | - { | ||
| 318 | - return tokenizer.readToken(*file, "", true, max_len); | ||
| 319 | - } | ||
| 320 | - | ||
| 321 | - // Methods to insert table entries | ||
| 322 | - void insert(int obj, int f0, qpdf_offset_t f1, int f2); | ||
| 323 | - void insert_free(QPDFObjGen); | ||
| 324 | - | ||
| 325 | - QPDFExc | ||
| 326 | - damaged_pdf(std::string const& msg) | ||
| 327 | - { | ||
| 328 | - return qpdf.damagedPDF("", 0, msg); | ||
| 329 | - } | ||
| 330 | - | ||
| 331 | - QPDFExc | ||
| 332 | - damaged_table(std::string const& msg) | ||
| 333 | - { | ||
| 334 | - return qpdf.damagedPDF("xref table", msg); | ||
| 335 | - } | ||
| 336 | - | ||
| 337 | - void | ||
| 338 | - warn_damaged(std::string const& msg) | ||
| 339 | - { | ||
| 340 | - qpdf.warn(damaged_pdf(msg)); | ||
| 341 | - } | ||
| 342 | - | ||
| 343 | - QPDF& qpdf; | ||
| 344 | - QPDF::Objects& objects; | ||
| 345 | - InputSource* const& file; | ||
| 346 | - QPDFTokenizer tokenizer; | ||
| 347 | - | ||
| 348 | - std::vector<Entry> table; | ||
| 349 | - QPDFObjectHandle trailer_; | ||
| 350 | - | ||
| 351 | - bool attempt_recovery_{true}; | ||
| 352 | - bool initialized_{false}; | ||
| 353 | - bool ignore_streams_{false}; | ||
| 354 | - bool reconstructed_{false}; | ||
| 355 | - bool object_streams_{false}; | ||
| 356 | - // Before the xref table is initialized, max_id_ is an upper bound on the possible object ids | ||
| 357 | - // that could be present in the PDF file. Once the trailer has been read, max_id_ is set to the | ||
| 358 | - // value of /Size. If the file is damaged, max_id_ becomes the maximum object id in the xref | ||
| 359 | - // table after reconstruction. | ||
| 360 | - int max_id_{std::numeric_limits<int>::max() - 1}; | ||
| 361 | - | ||
| 362 | - // Linearization data | ||
| 363 | - bool uncompressed_after_compressed_{false}; | ||
| 364 | - qpdf_offset_t first_item_offset_{0}; // actual value from file | ||
| 365 | -}; | ||
| 366 | - | ||
| 367 | // StreamCopier class is restricted to QPDFObjectHandle so it can copy stream data. | 10 | // StreamCopier class is restricted to QPDFObjectHandle so it can copy stream data. |
| 368 | class QPDF::StreamCopier | 11 | class QPDF::StreamCopier |
| 369 | { | 12 | { |
| @@ -740,7 +383,6 @@ class QPDF::Members | @@ -740,7 +383,6 @@ class QPDF::Members | ||
| 740 | std::shared_ptr<EncryptionParameters> encp; | 383 | std::shared_ptr<EncryptionParameters> encp; |
| 741 | std::string pdf_version; | 384 | std::string pdf_version; |
| 742 | Objects objects; | 385 | Objects objects; |
| 743 | - Xref_table xref_table; | ||
| 744 | std::set<QPDFObjGen> resolving; | 386 | std::set<QPDFObjGen> resolving; |
| 745 | std::vector<QPDFObjectHandle> all_pages; | 387 | std::vector<QPDFObjectHandle> all_pages; |
| 746 | bool invalid_page_found{false}; | 388 | bool invalid_page_found{false}; |
| @@ -901,10 +543,10 @@ class QPDF::Writer | @@ -901,10 +543,10 @@ class QPDF::Writer | ||
| 901 | return qpdf.objects().compressible_set(); | 543 | return qpdf.objects().compressible_set(); |
| 902 | } | 544 | } |
| 903 | 545 | ||
| 904 | - static Xref_table const& | 546 | + static Objects::Xref_table const& |
| 905 | getXRefTable(QPDF& qpdf) | 547 | getXRefTable(QPDF& qpdf) |
| 906 | { | 548 | { |
| 907 | - return qpdf.m->xref_table; | 549 | + return qpdf.objects().xref_table(); |
| 908 | } | 550 | } |
| 909 | 551 | ||
| 910 | static size_t | 552 | static size_t |