Commit 39abb113763a5f3686656cfaa9086ede4495a06d
1 parent
82419ca0
Make all QPDF::Xref_table data members private
Showing
6 changed files
with
118 additions
and
69 deletions
libqpdf/QPDF.cc
| @@ -304,7 +304,7 @@ QPDF::registerStreamFilter( | @@ -304,7 +304,7 @@ QPDF::registerStreamFilter( | ||
| 304 | void | 304 | void |
| 305 | QPDF::setIgnoreXRefStreams(bool val) | 305 | QPDF::setIgnoreXRefStreams(bool val) |
| 306 | { | 306 | { |
| 307 | - m->xref_table.ignore_streams = val; | 307 | + m->xref_table.ignore_streams(val); |
| 308 | } | 308 | } |
| 309 | 309 | ||
| 310 | std::shared_ptr<QPDFLogger> | 310 | std::shared_ptr<QPDFLogger> |
| @@ -342,7 +342,7 @@ void | @@ -342,7 +342,7 @@ void | ||
| 342 | QPDF::setAttemptRecovery(bool val) | 342 | QPDF::setAttemptRecovery(bool val) |
| 343 | { | 343 | { |
| 344 | m->attempt_recovery = val; | 344 | m->attempt_recovery = val; |
| 345 | - m->xref_table.attempt_recovery = val; | 345 | + m->xref_table.attempt_recovery(val); |
| 346 | } | 346 | } |
| 347 | 347 | ||
| 348 | void | 348 | void |
| @@ -496,11 +496,10 @@ QPDF::Xref_table::initialize() | @@ -496,11 +496,10 @@ QPDF::Xref_table::initialize() | ||
| 496 | // 30 characters to leave room for the startxref stuff. | 496 | // 30 characters to leave room for the startxref stuff. |
| 497 | file->seek(0, SEEK_END); | 497 | file->seek(0, SEEK_END); |
| 498 | qpdf_offset_t end_offset = file->tell(); | 498 | qpdf_offset_t end_offset = file->tell(); |
| 499 | - max_offset = end_offset; | ||
| 500 | // Sanity check on object ids. All objects must appear in xref table / stream. In all realistic | 499 | // Sanity check on object ids. All objects must appear in xref table / stream. In all realistic |
| 501 | // scenarios at least 3 bytes are required. | 500 | // scenarios at least 3 bytes are required. |
| 502 | - if (max_id > max_offset / 3) { | ||
| 503 | - max_id = static_cast<int>(max_offset / 3); | 501 | + if (max_id_ > end_offset / 3) { |
| 502 | + max_id_ = static_cast<int>(end_offset / 3); | ||
| 504 | } | 503 | } |
| 505 | qpdf_offset_t start_offset = (end_offset > 1054 ? end_offset - 1054 : 0); | 504 | qpdf_offset_t start_offset = (end_offset > 1054 ? end_offset - 1054 : 0); |
| 506 | PatternFinder sf(qpdf, &QPDF::findStartxref); | 505 | PatternFinder sf(qpdf, &QPDF::findStartxref); |
| @@ -522,7 +521,7 @@ QPDF::Xref_table::initialize() | @@ -522,7 +521,7 @@ QPDF::Xref_table::initialize() | ||
| 522 | throw damaged_pdf(std::string("error reading xref: ") + e.what()); | 521 | throw damaged_pdf(std::string("error reading xref: ") + e.what()); |
| 523 | } | 522 | } |
| 524 | } catch (QPDFExc& e) { | 523 | } catch (QPDFExc& e) { |
| 525 | - if (attempt_recovery) { | 524 | + if (attempt_recovery_) { |
| 526 | reconstruct(e); | 525 | reconstruct(e); |
| 527 | QTC::TC("qpdf", "QPDF reconstructed xref table"); | 526 | QTC::TC("qpdf", "QPDF reconstructed xref table"); |
| 528 | } else { | 527 | } else { |
| @@ -530,13 +529,13 @@ QPDF::Xref_table::initialize() | @@ -530,13 +529,13 @@ QPDF::Xref_table::initialize() | ||
| 530 | } | 529 | } |
| 531 | } | 530 | } |
| 532 | 531 | ||
| 533 | - parsed = true; | 532 | + initialized_ = true; |
| 534 | } | 533 | } |
| 535 | 534 | ||
| 536 | void | 535 | void |
| 537 | QPDF::Xref_table::reconstruct(QPDFExc& e) | 536 | QPDF::Xref_table::reconstruct(QPDFExc& e) |
| 538 | { | 537 | { |
| 539 | - if (reconstructed) { | 538 | + if (reconstructed_) { |
| 540 | // Avoid xref reconstruction infinite loops. This is getting very hard to reproduce because | 539 | // Avoid xref reconstruction infinite loops. This is getting very hard to reproduce because |
| 541 | // qpdf is throwing many fewer exceptions while parsing. Most situations are warnings now. | 540 | // qpdf is throwing many fewer exceptions while parsing. Most situations are warnings now. |
| 542 | throw e; | 541 | throw e; |
| @@ -551,7 +550,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | @@ -551,7 +550,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | ||
| 551 | } | 550 | } |
| 552 | }; | 551 | }; |
| 553 | 552 | ||
| 554 | - reconstructed = true; | 553 | + reconstructed_ = true; |
| 555 | // We may find more objects, which may contain dangling references. | 554 | // We may find more objects, which may contain dangling references. |
| 556 | qpdf.m->fixed_dangling_refs = false; | 555 | qpdf.m->fixed_dangling_refs = false; |
| 557 | 556 | ||
| @@ -584,20 +583,20 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | @@ -584,20 +583,20 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | ||
| 584 | if (t2.isInteger() && read_token(MAX_LEN).isWord("obj")) { | 583 | if (t2.isInteger() && read_token(MAX_LEN).isWord("obj")) { |
| 585 | int obj = QUtil::string_to_int(t1.getValue().c_str()); | 584 | int obj = QUtil::string_to_int(t1.getValue().c_str()); |
| 586 | int gen = QUtil::string_to_int(t2.getValue().c_str()); | 585 | int gen = QUtil::string_to_int(t2.getValue().c_str()); |
| 587 | - if (obj <= max_id) { | 586 | + if (obj <= max_id_) { |
| 588 | insert_reconstructed(obj, token_start, gen); | 587 | insert_reconstructed(obj, token_start, gen); |
| 589 | } else { | 588 | } else { |
| 590 | warn_damaged("ignoring object with impossibly large id " + std::to_string(obj)); | 589 | warn_damaged("ignoring object with impossibly large id " + std::to_string(obj)); |
| 591 | } | 590 | } |
| 592 | } | 591 | } |
| 593 | file->seek(pos, SEEK_SET); | 592 | file->seek(pos, SEEK_SET); |
| 594 | - } else if (!trailer && t1.isWord("trailer")) { | 593 | + } else if (!trailer_ && t1.isWord("trailer")) { |
| 595 | auto pos = file->tell(); | 594 | auto pos = file->tell(); |
| 596 | QPDFObjectHandle t = read_trailer(); | 595 | QPDFObjectHandle t = read_trailer(); |
| 597 | if (!t.isDictionary()) { | 596 | if (!t.isDictionary()) { |
| 598 | // Oh well. It was worth a try. | 597 | // Oh well. It was worth a try. |
| 599 | } else { | 598 | } else { |
| 600 | - trailer = t; | 599 | + trailer_ = t; |
| 601 | } | 600 | } |
| 602 | file->seek(pos, SEEK_SET); | 601 | file->seek(pos, SEEK_SET); |
| 603 | } | 602 | } |
| @@ -606,7 +605,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | @@ -606,7 +605,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | ||
| 606 | } | 605 | } |
| 607 | deleted_objects.clear(); | 606 | deleted_objects.clear(); |
| 608 | 607 | ||
| 609 | - if (!trailer) { | 608 | + if (!trailer_) { |
| 610 | qpdf_offset_t max_offset{0}; | 609 | qpdf_offset_t max_offset{0}; |
| 611 | // If there are any xref streams, take the last one to appear. | 610 | // If there are any xref streams, take the last one to appear. |
| 612 | for (auto const& iter: table) { | 611 | for (auto const& iter: table) { |
| @@ -625,7 +624,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | @@ -625,7 +624,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | ||
| 625 | auto offset = entry.getOffset(); | 624 | auto offset = entry.getOffset(); |
| 626 | if (offset > max_offset) { | 625 | if (offset > max_offset) { |
| 627 | max_offset = offset; | 626 | max_offset = offset; |
| 628 | - trailer = oh.getDict(); | 627 | + trailer_ = oh.getDict(); |
| 629 | } | 628 | } |
| 630 | check_warnings(); | 629 | check_warnings(); |
| 631 | } | 630 | } |
| @@ -640,7 +639,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | @@ -640,7 +639,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | ||
| 640 | } | 639 | } |
| 641 | } | 640 | } |
| 642 | 641 | ||
| 643 | - if (!trailer) { | 642 | + if (!trailer_) { |
| 644 | // We could check the last encountered object to see if it was an xref stream. If so, we | 643 | // We could check the last encountered object to see if it was an xref stream. If so, we |
| 645 | // could try to get the trailer from there. This may make it possible to recover files with | 644 | // could try to get the trailer from there. This may make it possible to recover files with |
| 646 | // bad startxref pointers even when they have object streams. | 645 | // bad startxref pointers even when they have object streams. |
| @@ -653,12 +652,12 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | @@ -653,12 +652,12 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) | ||
| 653 | throw damaged_pdf("unable to find objects while recovering damaged file"); | 652 | throw damaged_pdf("unable to find objects while recovering damaged file"); |
| 654 | } | 653 | } |
| 655 | check_warnings(); | 654 | check_warnings(); |
| 656 | - if (!parsed) { | ||
| 657 | - parsed = true; | 655 | + if (!initialized_) { |
| 656 | + initialized_ = true; | ||
| 658 | qpdf.getAllPages(); | 657 | qpdf.getAllPages(); |
| 659 | check_warnings(); | 658 | check_warnings(); |
| 660 | if (qpdf.m->all_pages.empty()) { | 659 | if (qpdf.m->all_pages.empty()) { |
| 661 | - parsed = false; | 660 | + initialized_ = false; |
| 662 | throw damaged_pdf("unable to find any pages while recovering damaged file"); | 661 | throw damaged_pdf("unable to find any pages while recovering damaged file"); |
| 663 | } | 662 | } |
| 664 | } | 663 | } |
| @@ -730,10 +729,10 @@ QPDF::Xref_table::read(qpdf_offset_t xref_offset) | @@ -730,10 +729,10 @@ QPDF::Xref_table::read(qpdf_offset_t xref_offset) | ||
| 730 | } | 729 | } |
| 731 | } | 730 | } |
| 732 | 731 | ||
| 733 | - if (!trailer) { | 732 | + if (!trailer_) { |
| 734 | throw damaged_pdf("unable to find trailer while reading xref"); | 733 | throw damaged_pdf("unable to find trailer while reading xref"); |
| 735 | } | 734 | } |
| 736 | - int size = trailer.getKey("/Size").getIntValueAsInt(); | 735 | + int size = trailer_.getKey("/Size").getIntValueAsInt(); |
| 737 | int max_obj = 0; | 736 | int max_obj = 0; |
| 738 | if (!table.empty()) { | 737 | if (!table.empty()) { |
| 739 | max_obj = table.rbegin()->first.getObj(); | 738 | max_obj = table.rbegin()->first.getObj(); |
| @@ -967,7 +966,7 @@ QPDF::Xref_table::read_table(qpdf_offset_t xref_offset) | @@ -967,7 +966,7 @@ QPDF::Xref_table::read_table(qpdf_offset_t xref_offset) | ||
| 967 | for (qpdf_offset_t i = obj; i - num < obj; ++i) { | 966 | for (qpdf_offset_t i = obj; i - num < obj; ++i) { |
| 968 | if (i == 0) { | 967 | if (i == 0) { |
| 969 | // This is needed by checkLinearization() | 968 | // This is needed by checkLinearization() |
| 970 | - first_item_offset = file->tell(); | 969 | + first_item_offset_ = file->tell(); |
| 971 | } | 970 | } |
| 972 | // For xref_table, these will always be small enough to be ints | 971 | // For xref_table, these will always be small enough to be ints |
| 973 | qpdf_offset_t f1 = 0; | 972 | qpdf_offset_t f1 = 0; |
| @@ -998,21 +997,21 @@ QPDF::Xref_table::read_table(qpdf_offset_t xref_offset) | @@ -998,21 +997,21 @@ QPDF::Xref_table::read_table(qpdf_offset_t xref_offset) | ||
| 998 | throw qpdf.damagedPDF("", "expected trailer dictionary"); | 997 | throw qpdf.damagedPDF("", "expected trailer dictionary"); |
| 999 | } | 998 | } |
| 1000 | 999 | ||
| 1001 | - if (!trailer) { | ||
| 1002 | - trailer = cur_trailer; | 1000 | + if (!trailer_) { |
| 1001 | + trailer_ = cur_trailer; | ||
| 1003 | 1002 | ||
| 1004 | - if (!trailer.hasKey("/Size")) { | 1003 | + if (!trailer_.hasKey("/Size")) { |
| 1005 | QTC::TC("qpdf", "QPDF trailer lacks size"); | 1004 | QTC::TC("qpdf", "QPDF trailer lacks size"); |
| 1006 | throw qpdf.damagedPDF("trailer", "trailer dictionary lacks /Size key"); | 1005 | throw qpdf.damagedPDF("trailer", "trailer dictionary lacks /Size key"); |
| 1007 | } | 1006 | } |
| 1008 | - if (!trailer.getKey("/Size").isInteger()) { | 1007 | + if (!trailer_.getKey("/Size").isInteger()) { |
| 1009 | QTC::TC("qpdf", "QPDF trailer size not integer"); | 1008 | QTC::TC("qpdf", "QPDF trailer size not integer"); |
| 1010 | throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is not an integer"); | 1009 | throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is not an integer"); |
| 1011 | } | 1010 | } |
| 1012 | } | 1011 | } |
| 1013 | 1012 | ||
| 1014 | if (cur_trailer.hasKey("/XRefStm")) { | 1013 | if (cur_trailer.hasKey("/XRefStm")) { |
| 1015 | - if (ignore_streams) { | 1014 | + if (ignore_streams_) { |
| 1016 | QTC::TC("qpdf", "QPDF ignoring XRefStm in trailer"); | 1015 | QTC::TC("qpdf", "QPDF ignoring XRefStm in trailer"); |
| 1017 | } else { | 1016 | } else { |
| 1018 | if (cur_trailer.getKey("/XRefStm").isInteger()) { | 1017 | if (cur_trailer.getKey("/XRefStm").isInteger()) { |
| @@ -1041,7 +1040,7 @@ QPDF::Xref_table::read_table(qpdf_offset_t xref_offset) | @@ -1041,7 +1040,7 @@ QPDF::Xref_table::read_table(qpdf_offset_t xref_offset) | ||
| 1041 | qpdf_offset_t | 1040 | qpdf_offset_t |
| 1042 | QPDF::Xref_table::read_stream(qpdf_offset_t xref_offset) | 1041 | QPDF::Xref_table::read_stream(qpdf_offset_t xref_offset) |
| 1043 | { | 1042 | { |
| 1044 | - if (!ignore_streams) { | 1043 | + if (!ignore_streams_) { |
| 1045 | QPDFObjGen x_og; | 1044 | QPDFObjGen x_og; |
| 1046 | QPDFObjectHandle xref_obj; | 1045 | QPDFObjectHandle xref_obj; |
| 1047 | try { | 1046 | try { |
| @@ -1237,14 +1236,14 @@ QPDF::Xref_table::process_stream(qpdf_offset_t xref_offset, QPDFObjectHandle& xr | @@ -1237,14 +1236,14 @@ QPDF::Xref_table::process_stream(qpdf_offset_t xref_offset, QPDFObjectHandle& xr | ||
| 1237 | // object record, in which case the generation number appears as the third field. | 1236 | // object record, in which case the generation number appears as the third field. |
| 1238 | if (saw_first_compressed_object) { | 1237 | if (saw_first_compressed_object) { |
| 1239 | if (fields[0] != 2) { | 1238 | if (fields[0] != 2) { |
| 1240 | - uncompressed_after_compressed = true; | 1239 | + uncompressed_after_compressed_ = true; |
| 1241 | } | 1240 | } |
| 1242 | } else if (fields[0] == 2) { | 1241 | } else if (fields[0] == 2) { |
| 1243 | saw_first_compressed_object = true; | 1242 | saw_first_compressed_object = true; |
| 1244 | } | 1243 | } |
| 1245 | if (obj == 0) { | 1244 | if (obj == 0) { |
| 1246 | // This is needed by checkLinearization() | 1245 | // This is needed by checkLinearization() |
| 1247 | - first_item_offset = xref_offset; | 1246 | + first_item_offset_ = xref_offset; |
| 1248 | } else if (fields[0] == 0) { | 1247 | } else if (fields[0] == 0) { |
| 1249 | // Ignore fields[2], which we don't care about in this case. This works around the | 1248 | // Ignore fields[2], which we don't care about in this case. This works around the |
| 1250 | // issue of some PDF files that put invalid values, like -1, here for deleted | 1249 | // issue of some PDF files that put invalid values, like -1, here for deleted |
| @@ -1257,8 +1256,8 @@ QPDF::Xref_table::process_stream(qpdf_offset_t xref_offset, QPDFObjectHandle& xr | @@ -1257,8 +1256,8 @@ QPDF::Xref_table::process_stream(qpdf_offset_t xref_offset, QPDFObjectHandle& xr | ||
| 1257 | } | 1256 | } |
| 1258 | } | 1257 | } |
| 1259 | 1258 | ||
| 1260 | - if (!trailer) { | ||
| 1261 | - trailer = dict; | 1259 | + if (!trailer_) { |
| 1260 | + trailer_ = dict; | ||
| 1262 | } | 1261 | } |
| 1263 | 1262 | ||
| 1264 | if (dict.hasKey("/Prev")) { | 1263 | if (dict.hasKey("/Prev")) { |
| @@ -1283,7 +1282,7 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) | @@ -1283,7 +1282,7 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) | ||
| 1283 | // If there is already an entry for this object and generation in the table, it means that a | 1282 | // If there is already an entry for this object and generation in the table, it means that a |
| 1284 | // later xref table has registered this object. Disregard this one. | 1283 | // later xref table has registered this object. Disregard this one. |
| 1285 | 1284 | ||
| 1286 | - if (obj > max_id) { | 1285 | + if (obj > max_id_) { |
| 1287 | // ignore impossibly large object ids or object ids > Size. | 1286 | // ignore impossibly large object ids or object ids > Size. |
| 1288 | return; | 1287 | return; |
| 1289 | } | 1288 | } |
| @@ -1336,7 +1335,7 @@ QPDF::Xref_table::insert_free(QPDFObjGen og) | @@ -1336,7 +1335,7 @@ QPDF::Xref_table::insert_free(QPDFObjGen og) | ||
| 1336 | void | 1335 | void |
| 1337 | QPDF::Xref_table::insert_reconstructed(int obj, qpdf_offset_t f1, int f2) | 1336 | QPDF::Xref_table::insert_reconstructed(int obj, qpdf_offset_t f1, int f2) |
| 1338 | { | 1337 | { |
| 1339 | - if (!(obj > 0 && obj <= max_id && 0 <= f2 && f2 < 65535)) { | 1338 | + if (!(obj > 0 && obj <= max_id_ && 0 <= f2 && f2 < 65535)) { |
| 1340 | QTC::TC("qpdf", "QPDF xref overwrite invalid objgen"); | 1339 | QTC::TC("qpdf", "QPDF xref overwrite invalid objgen"); |
| 1341 | return; | 1340 | return; |
| 1342 | } | 1341 | } |
| @@ -1385,11 +1384,11 @@ QPDF::Xref_table::show() | @@ -1385,11 +1384,11 @@ QPDF::Xref_table::show() | ||
| 1385 | bool | 1384 | bool |
| 1386 | QPDF::Xref_table::resolve() | 1385 | QPDF::Xref_table::resolve() |
| 1387 | { | 1386 | { |
| 1388 | - bool may_change = !reconstructed; | 1387 | + bool may_change = !reconstructed_; |
| 1389 | for (auto& iter: table) { | 1388 | for (auto& iter: table) { |
| 1390 | if (qpdf.isUnresolved(iter.first)) { | 1389 | if (qpdf.isUnresolved(iter.first)) { |
| 1391 | qpdf.resolve(iter.first); | 1390 | qpdf.resolve(iter.first); |
| 1392 | - if (may_change && reconstructed) { | 1391 | + if (may_change && reconstructed_) { |
| 1393 | return false; | 1392 | return false; |
| 1394 | } | 1393 | } |
| 1395 | } | 1394 | } |
| @@ -1958,7 +1957,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) | @@ -1958,7 +1957,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) | ||
| 1958 | 1957 | ||
| 1959 | int num = QUtil::string_to_int(tnum.getValue().c_str()); | 1958 | int num = QUtil::string_to_int(tnum.getValue().c_str()); |
| 1960 | long long offset = QUtil::string_to_int(toffset.getValue().c_str()); | 1959 | long long offset = QUtil::string_to_int(toffset.getValue().c_str()); |
| 1961 | - if (num > m->xref_table.max_id) { | 1960 | + if (num > m->xref_table.max_id()) { |
| 1962 | continue; | 1961 | continue; |
| 1963 | } | 1962 | } |
| 1964 | if (num == obj_stream_number) { | 1963 | if (num == obj_stream_number) { |
| @@ -2100,7 +2099,7 @@ QPDF::getObjectForParser(int id, int gen, bool parse_pdf) | @@ -2100,7 +2099,7 @@ QPDF::getObjectForParser(int id, int gen, bool parse_pdf) | ||
| 2100 | if (auto iter = m->obj_cache.find(og); iter != m->obj_cache.end()) { | 2099 | if (auto iter = m->obj_cache.find(og); iter != m->obj_cache.end()) { |
| 2101 | return iter->second.object; | 2100 | return iter->second.object; |
| 2102 | } | 2101 | } |
| 2103 | - if (m->xref_table.type(og) || !m->xref_table.parsed) { | 2102 | + if (m->xref_table.type(og) || !m->xref_table.initialized()) { |
| 2104 | return m->obj_cache.insert({og, QPDF_Unresolved::create(this, og)}).first->second.object; | 2103 | return m->obj_cache.insert({og, QPDF_Unresolved::create(this, og)}).first->second.object; |
| 2105 | } | 2104 | } |
| 2106 | if (parse_pdf) { | 2105 | if (parse_pdf) { |
| @@ -2116,8 +2115,9 @@ QPDF::getObjectForJSON(int id, int gen) | @@ -2116,8 +2115,9 @@ QPDF::getObjectForJSON(int id, int gen) | ||
| 2116 | auto [it, inserted] = m->obj_cache.try_emplace(og); | 2115 | auto [it, inserted] = m->obj_cache.try_emplace(og); |
| 2117 | auto& obj = it->second.object; | 2116 | auto& obj = it->second.object; |
| 2118 | if (inserted) { | 2117 | if (inserted) { |
| 2119 | - obj = (m->xref_table.parsed && !m->xref_table.type(og)) ? QPDF_Null::create(this, og) | ||
| 2120 | - : QPDF_Unresolved::create(this, og); | 2118 | + obj = (m->xref_table.initialized() && !m->xref_table.type(og)) |
| 2119 | + ? QPDF_Null::create(this, og) | ||
| 2120 | + : QPDF_Unresolved::create(this, og); | ||
| 2121 | } | 2121 | } |
| 2122 | return obj; | 2122 | return obj; |
| 2123 | } | 2123 | } |
| @@ -2127,7 +2127,7 @@ QPDF::getObject(QPDFObjGen const& og) | @@ -2127,7 +2127,7 @@ QPDF::getObject(QPDFObjGen const& og) | ||
| 2127 | { | 2127 | { |
| 2128 | if (auto it = m->obj_cache.find(og); it != m->obj_cache.end()) { | 2128 | if (auto it = m->obj_cache.find(og); it != m->obj_cache.end()) { |
| 2129 | return {it->second.object}; | 2129 | return {it->second.object}; |
| 2130 | - } else if (m->xref_table.parsed && !m->xref_table.type(og)) { | 2130 | + } else if (m->xref_table.initialized() && !m->xref_table.type(og)) { |
| 2131 | return QPDF_Null::create(); | 2131 | return QPDF_Null::create(); |
| 2132 | } else { | 2132 | } else { |
| 2133 | auto result = m->obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og), -1, -1); | 2133 | auto result = m->obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og), -1, -1); |
| @@ -2524,13 +2524,13 @@ QPDF::getExtensionLevel() | @@ -2524,13 +2524,13 @@ QPDF::getExtensionLevel() | ||
| 2524 | QPDFObjectHandle | 2524 | QPDFObjectHandle |
| 2525 | QPDF::getTrailer() | 2525 | QPDF::getTrailer() |
| 2526 | { | 2526 | { |
| 2527 | - return m->xref_table.trailer; | 2527 | + return m->xref_table.trailer(); |
| 2528 | } | 2528 | } |
| 2529 | 2529 | ||
| 2530 | QPDFObjectHandle | 2530 | QPDFObjectHandle |
| 2531 | QPDF::getRoot() | 2531 | QPDF::getRoot() |
| 2532 | { | 2532 | { |
| 2533 | - QPDFObjectHandle root = m->xref_table.trailer.getKey("/Root"); | 2533 | + QPDFObjectHandle root = m->xref_table.trailer().getKey("/Root"); |
| 2534 | if (!root.isDictionary()) { | 2534 | if (!root.isDictionary()) { |
| 2535 | throw damagedPDF("", 0, "unable to find /Root dictionary"); | 2535 | throw damagedPDF("", 0, "unable to find /Root dictionary"); |
| 2536 | } else if ( | 2536 | } else if ( |
| @@ -2552,7 +2552,7 @@ QPDF::getXRefTable() | @@ -2552,7 +2552,7 @@ QPDF::getXRefTable() | ||
| 2552 | std::map<QPDFObjGen, QPDFXRefEntry> const& | 2552 | std::map<QPDFObjGen, QPDFXRefEntry> const& |
| 2553 | QPDF::getXRefTableInternal() | 2553 | QPDF::getXRefTableInternal() |
| 2554 | { | 2554 | { |
| 2555 | - if (!m->xref_table.parsed) { | 2555 | + if (!m->xref_table.initialized()) { |
| 2556 | throw std::logic_error("QPDF::getXRefTable called before parsing."); | 2556 | throw std::logic_error("QPDF::getXRefTable called before parsing."); |
| 2557 | } | 2557 | } |
| 2558 | 2558 | ||
| @@ -2602,14 +2602,14 @@ QPDF::getCompressibleObjGens() | @@ -2602,14 +2602,14 @@ QPDF::getCompressibleObjGens() | ||
| 2602 | // iterating through the xref table since it avoids preserving orphaned items. | 2602 | // iterating through the xref table since it avoids preserving orphaned items. |
| 2603 | 2603 | ||
| 2604 | // Exclude encryption dictionary, if any | 2604 | // Exclude encryption dictionary, if any |
| 2605 | - QPDFObjectHandle encryption_dict = m->xref_table.trailer.getKey("/Encrypt"); | 2605 | + QPDFObjectHandle encryption_dict = m->xref_table.trailer().getKey("/Encrypt"); |
| 2606 | QPDFObjGen encryption_dict_og = encryption_dict.getObjGen(); | 2606 | QPDFObjGen encryption_dict_og = encryption_dict.getObjGen(); |
| 2607 | 2607 | ||
| 2608 | const size_t max_obj = getObjectCount(); | 2608 | const size_t max_obj = getObjectCount(); |
| 2609 | std::vector<bool> visited(max_obj, false); | 2609 | std::vector<bool> visited(max_obj, false); |
| 2610 | std::vector<QPDFObjectHandle> queue; | 2610 | std::vector<QPDFObjectHandle> queue; |
| 2611 | queue.reserve(512); | 2611 | queue.reserve(512); |
| 2612 | - queue.push_back(m->xref_table.trailer); | 2612 | + queue.push_back(m->xref_table.trailer()); |
| 2613 | std::vector<T> result; | 2613 | std::vector<T> result; |
| 2614 | if constexpr (std::is_same_v<T, QPDFObjGen>) { | 2614 | if constexpr (std::is_same_v<T, QPDFObjGen>) { |
| 2615 | result.reserve(m->obj_cache.size()); | 2615 | result.reserve(m->obj_cache.size()); |
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->xref_table.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->xref_table.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->xref_table.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
| @@ -593,8 +593,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value) | @@ -593,8 +593,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value) | ||
| 593 | this->saw_value = true; | 593 | this->saw_value = true; |
| 594 | // The trailer must be a dictionary, so we can use setNextStateIfDictionary. | 594 | // The trailer must be a dictionary, so we can use setNextStateIfDictionary. |
| 595 | if (setNextStateIfDictionary("trailer.value", value, st_object)) { | 595 | if (setNextStateIfDictionary("trailer.value", value, st_object)) { |
| 596 | - pdf.m->xref_table.trailer = makeObject(value); | ||
| 597 | - setObjectDescription(this->pdf.m->xref_table.trailer, value); | 596 | + pdf.m->xref_table.trailer(makeObject(value)); |
| 598 | } | 597 | } |
| 599 | } else if (key == "stream") { | 598 | } else if (key == "stream") { |
| 600 | // Don't need to set saw_stream here since there's already an error. | 599 | // Don't need to set saw_stream here since there's already an error. |
libqpdf/QPDF_linearization.cc
| @@ -461,11 +461,11 @@ QPDF::checkLinearizationInternal() | @@ -461,11 +461,11 @@ QPDF::checkLinearizationInternal() | ||
| 461 | break; | 461 | break; |
| 462 | } | 462 | } |
| 463 | } | 463 | } |
| 464 | - if (m->file->tell() != m->xref_table.first_item_offset) { | 464 | + if (m->file->tell() != m->xref_table.first_item_offset()) { |
| 465 | QTC::TC("qpdf", "QPDF err /T mismatch"); | 465 | QTC::TC("qpdf", "QPDF err /T mismatch"); |
| 466 | linearizationWarning( | 466 | linearizationWarning( |
| 467 | "space before first xref item (/T) mismatch (computed = " + | 467 | "space before first xref item (/T) mismatch (computed = " + |
| 468 | - std::to_string(m->xref_table.first_item_offset) + | 468 | + std::to_string(m->xref_table.first_item_offset()) + |
| 469 | "; file = " + std::to_string(m->file->tell())); | 469 | "; file = " + std::to_string(m->file->tell())); |
| 470 | } | 470 | } |
| 471 | 471 | ||
| @@ -476,7 +476,7 @@ QPDF::checkLinearizationInternal() | @@ -476,7 +476,7 @@ QPDF::checkLinearizationInternal() | ||
| 476 | // compressed objects are supposed to be at the end of the containing xref section if any object | 476 | // compressed objects are supposed to be at the end of the containing xref section if any object |
| 477 | // streams are in use. | 477 | // streams are in use. |
| 478 | 478 | ||
| 479 | - if (m->xref_table.uncompressed_after_compressed) { | 479 | + if (m->xref_table.uncompressed_after_compressed()) { |
| 480 | linearizationWarning("linearized file contains an uncompressed object after a compressed " | 480 | linearizationWarning("linearized file contains an uncompressed object after a compressed " |
| 481 | "one in a cross-reference stream"); | 481 | "one in a cross-reference stream"); |
| 482 | } | 482 | } |
libqpdf/QPDF_optimization.cc
| @@ -115,13 +115,13 @@ QPDF::optimize_internal( | @@ -115,13 +115,13 @@ QPDF::optimize_internal( | ||
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | // Traverse document-level items | 117 | // Traverse document-level items |
| 118 | - for (auto const& key: m->xref_table.trailer.getKeys()) { | 118 | + for (auto const& key: m->xref_table.trailer().getKeys()) { |
| 119 | if (key == "/Root") { | 119 | if (key == "/Root") { |
| 120 | // handled separately | 120 | // handled separately |
| 121 | } else { | 121 | } else { |
| 122 | updateObjectMaps( | 122 | updateObjectMaps( |
| 123 | ObjUser(ObjUser::ou_trailer_key, key), | 123 | ObjUser(ObjUser::ou_trailer_key, key), |
| 124 | - m->xref_table.trailer.getKey(key), | 124 | + m->xref_table.trailer().getKey(key), |
| 125 | skip_stream_parameters); | 125 | skip_stream_parameters); |
| 126 | } | 126 | } |
| 127 | } | 127 | } |
| @@ -169,7 +169,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys) | @@ -169,7 +169,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys) | ||
| 169 | // values for them. | 169 | // values for them. |
| 170 | std::map<std::string, std::vector<QPDFObjectHandle>> key_ancestors; | 170 | std::map<std::string, std::vector<QPDFObjectHandle>> key_ancestors; |
| 171 | pushInheritedAttributesToPageInternal( | 171 | pushInheritedAttributesToPageInternal( |
| 172 | - m->xref_table.trailer.getKey("/Root").getKey("/Pages"), | 172 | + m->xref_table.trailer().getKey("/Root").getKey("/Pages"), |
| 173 | key_ancestors, | 173 | key_ancestors, |
| 174 | allow_changes, | 174 | allow_changes, |
| 175 | warn_skipped_keys); | 175 | warn_skipped_keys); |
libqpdf/qpdf/QPDF_private.hh
| @@ -19,6 +19,18 @@ class QPDF::Xref_table | @@ -19,6 +19,18 @@ class QPDF::Xref_table | ||
| 19 | void show(); | 19 | void show(); |
| 20 | bool resolve(); | 20 | bool resolve(); |
| 21 | 21 | ||
| 22 | + QPDFObjectHandle | ||
| 23 | + trailer() const | ||
| 24 | + { | ||
| 25 | + return trailer_; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + void | ||
| 29 | + trailer(QPDFObjectHandle&& oh) | ||
| 30 | + { | ||
| 31 | + trailer_ = std::move(oh); | ||
| 32 | + } | ||
| 33 | + | ||
| 22 | // Returns 0 if og is not in table. | 34 | // Returns 0 if og is not in table. |
| 23 | int | 35 | int |
| 24 | type(QPDFObjGen og) const | 36 | type(QPDFObjGen og) const |
| @@ -61,22 +73,47 @@ class QPDF::Xref_table | @@ -61,22 +73,47 @@ class QPDF::Xref_table | ||
| 61 | size_t | 73 | size_t |
| 62 | size() const noexcept | 74 | size() const noexcept |
| 63 | { | 75 | { |
| 64 | - return trailer ? table.size() : 0; | 76 | + return trailer_ ? table.size() : 0; |
| 65 | } | 77 | } |
| 66 | 78 | ||
| 67 | - QPDFObjectHandle trailer; | ||
| 68 | - bool reconstructed{false}; | ||
| 69 | - // Various tables are indexed by object id, with potential size id + 1 | ||
| 70 | - int max_id{std::numeric_limits<int>::max() - 1}; | ||
| 71 | - qpdf_offset_t max_offset{0}; | ||
| 72 | - std::set<int> deleted_objects; | ||
| 73 | - bool ignore_streams{false}; | ||
| 74 | - bool parsed{false}; | ||
| 75 | - bool attempt_recovery{true}; | 79 | + void |
| 80 | + ignore_streams(bool val) noexcept | ||
| 81 | + { | ||
| 82 | + ignore_streams_ = val; | ||
| 83 | + } | ||
| 76 | 84 | ||
| 77 | - // Linearization data | ||
| 78 | - bool uncompressed_after_compressed{false}; | ||
| 79 | - qpdf_offset_t first_item_offset{0}; // actual value from file | 85 | + bool |
| 86 | + initialized() const noexcept | ||
| 87 | + { | ||
| 88 | + return initialized_; | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + void | ||
| 92 | + attempt_recovery(bool val) noexcept | ||
| 93 | + { | ||
| 94 | + attempt_recovery_ = val; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + int | ||
| 98 | + max_id() const noexcept | ||
| 99 | + { | ||
| 100 | + return max_id_; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + // For Linearization | ||
| 104 | + | ||
| 105 | + bool | ||
| 106 | + uncompressed_after_compressed() const noexcept | ||
| 107 | + { | ||
| 108 | + return uncompressed_after_compressed_; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + // Actual value from file | ||
| 112 | + qpdf_offset_t | ||
| 113 | + first_item_offset() const noexcept | ||
| 114 | + { | ||
| 115 | + return first_item_offset_; | ||
| 116 | + } | ||
| 80 | 117 | ||
| 81 | private: | 118 | private: |
| 82 | void read(qpdf_offset_t offset); | 119 | void read(qpdf_offset_t offset); |
| @@ -135,6 +172,19 @@ class QPDF::Xref_table | @@ -135,6 +172,19 @@ class QPDF::Xref_table | ||
| 135 | QPDFTokenizer tokenizer; | 172 | QPDFTokenizer tokenizer; |
| 136 | 173 | ||
| 137 | std::map<QPDFObjGen, QPDFXRefEntry> table; | 174 | std::map<QPDFObjGen, QPDFXRefEntry> table; |
| 175 | + QPDFObjectHandle trailer_; | ||
| 176 | + | ||
| 177 | + bool attempt_recovery_{true}; | ||
| 178 | + bool initialized_{false}; | ||
| 179 | + bool ignore_streams_{false}; | ||
| 180 | + std::set<int> deleted_objects; | ||
| 181 | + bool reconstructed_{false}; | ||
| 182 | + // Various tables are indexed by object id, with potential size id + 1 | ||
| 183 | + int max_id_{std::numeric_limits<int>::max() - 1}; | ||
| 184 | + | ||
| 185 | + // Linearization data | ||
| 186 | + bool uncompressed_after_compressed_{false}; | ||
| 187 | + qpdf_offset_t first_item_offset_{0}; // actual value from file | ||
| 138 | }; | 188 | }; |
| 139 | 189 | ||
| 140 | // Writer class is restricted to QPDFWriter so that only it can call certain methods. | 190 | // Writer class is restricted to QPDFWriter so that only it can call certain methods. |