Commit 306f0efa56b3227e9015bdf3b0d84b68bb9687a3
1 parent
a03c6863
Refactor QPDF_Array
Move all array-specific methods to new class qpdf::Array.
Showing
7 changed files
with
203 additions
and
108 deletions
include/qpdf/ObjectHandle.hh
| @@ -26,6 +26,7 @@ | @@ -26,6 +26,7 @@ | ||
| 26 | #include <qpdf/Types.h> | 26 | #include <qpdf/Types.h> |
| 27 | 27 | ||
| 28 | #include <cstdint> | 28 | #include <cstdint> |
| 29 | +#include <memory> | ||
| 29 | 30 | ||
| 30 | class QPDF_Dictionary; | 31 | class QPDF_Dictionary; |
| 31 | class QPDFObject; | 32 | class QPDFObject; |
| @@ -33,6 +34,7 @@ class QPDFObjectHandle; | @@ -33,6 +34,7 @@ class QPDFObjectHandle; | ||
| 33 | 34 | ||
| 34 | namespace qpdf | 35 | namespace qpdf |
| 35 | { | 36 | { |
| 37 | + class Array; | ||
| 36 | class Dictionary; | 38 | class Dictionary; |
| 37 | class BaseDictionary; | 39 | class BaseDictionary; |
| 38 | 40 |
include/qpdf/QPDFObjectHandle.hh
| @@ -1355,6 +1355,7 @@ class QPDFObjectHandle final: public qpdf::BaseHandle | @@ -1355,6 +1355,7 @@ class QPDFObjectHandle final: public qpdf::BaseHandle | ||
| 1355 | 1355 | ||
| 1356 | void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const; | 1356 | void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const; |
| 1357 | 1357 | ||
| 1358 | + inline qpdf::Array as_array(qpdf::typed options = qpdf::typed::any) const; | ||
| 1358 | inline qpdf::Dictionary as_dictionary(qpdf::typed options = qpdf::typed::any) const; | 1359 | inline qpdf::Dictionary as_dictionary(qpdf::typed options = qpdf::typed::any) const; |
| 1359 | 1360 | ||
| 1360 | private: | 1361 | private: |
libqpdf/QPDFObjectHandle.cc
| @@ -777,22 +777,20 @@ QPDFObjectHandle::aitems() | @@ -777,22 +777,20 @@ QPDFObjectHandle::aitems() | ||
| 777 | int | 777 | int |
| 778 | QPDFObjectHandle::getArrayNItems() const | 778 | QPDFObjectHandle::getArrayNItems() const |
| 779 | { | 779 | { |
| 780 | - if (auto array = asArray()) { | ||
| 781 | - return array->size(); | ||
| 782 | - } else { | ||
| 783 | - typeWarning("array", "treating as empty"); | ||
| 784 | - QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); | ||
| 785 | - return 0; | 780 | + if (auto array = as_array(strict)) { |
| 781 | + return array.size(); | ||
| 786 | } | 782 | } |
| 783 | + typeWarning("array", "treating as empty"); | ||
| 784 | + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); | ||
| 785 | + return 0; | ||
| 787 | } | 786 | } |
| 788 | 787 | ||
| 789 | QPDFObjectHandle | 788 | QPDFObjectHandle |
| 790 | QPDFObjectHandle::getArrayItem(int n) const | 789 | QPDFObjectHandle::getArrayItem(int n) const |
| 791 | { | 790 | { |
| 792 | - if (auto array = asArray()) { | ||
| 793 | - auto result = array->at(n); | ||
| 794 | - if (result.first) { | ||
| 795 | - return result.second; | 791 | + if (auto array = as_array(strict)) { |
| 792 | + if (auto const [success, oh] = array.at(n); success) { | ||
| 793 | + return oh; | ||
| 796 | } else { | 794 | } else { |
| 797 | objectWarning("returning null for out of bounds array access"); | 795 | objectWarning("returning null for out of bounds array access"); |
| 798 | QTC::TC("qpdf", "QPDFObjectHandle array bounds"); | 796 | QTC::TC("qpdf", "QPDFObjectHandle array bounds"); |
| @@ -808,13 +806,13 @@ QPDFObjectHandle::getArrayItem(int n) const | @@ -808,13 +806,13 @@ QPDFObjectHandle::getArrayItem(int n) const | ||
| 808 | bool | 806 | bool |
| 809 | QPDFObjectHandle::isRectangle() const | 807 | QPDFObjectHandle::isRectangle() const |
| 810 | { | 808 | { |
| 811 | - if (auto array = asArray()) { | 809 | + if (auto array = as_array(strict)) { |
| 812 | for (int i = 0; i < 4; ++i) { | 810 | for (int i = 0; i < 4; ++i) { |
| 813 | - if (auto item = array->at(i).second; !item.isNumber()) { | 811 | + if (auto item = array.at(i).second; !item.isNumber()) { |
| 814 | return false; | 812 | return false; |
| 815 | } | 813 | } |
| 816 | } | 814 | } |
| 817 | - return array->size() == 4; | 815 | + return array.size() == 4; |
| 818 | } | 816 | } |
| 819 | return false; | 817 | return false; |
| 820 | } | 818 | } |
| @@ -822,13 +820,13 @@ QPDFObjectHandle::isRectangle() const | @@ -822,13 +820,13 @@ QPDFObjectHandle::isRectangle() const | ||
| 822 | bool | 820 | bool |
| 823 | QPDFObjectHandle::isMatrix() const | 821 | QPDFObjectHandle::isMatrix() const |
| 824 | { | 822 | { |
| 825 | - if (auto array = asArray()) { | 823 | + if (auto array = as_array(strict)) { |
| 826 | for (int i = 0; i < 6; ++i) { | 824 | for (int i = 0; i < 6; ++i) { |
| 827 | - if (auto item = array->at(i).second; !item.isNumber()) { | 825 | + if (auto item = array.at(i).second; !item.isNumber()) { |
| 828 | return false; | 826 | return false; |
| 829 | } | 827 | } |
| 830 | } | 828 | } |
| 831 | - return array->size() == 6; | 829 | + return array.size() == 6; |
| 832 | } | 830 | } |
| 833 | return false; | 831 | return false; |
| 834 | } | 832 | } |
| @@ -836,13 +834,13 @@ QPDFObjectHandle::isMatrix() const | @@ -836,13 +834,13 @@ QPDFObjectHandle::isMatrix() const | ||
| 836 | QPDFObjectHandle::Rectangle | 834 | QPDFObjectHandle::Rectangle |
| 837 | QPDFObjectHandle::getArrayAsRectangle() const | 835 | QPDFObjectHandle::getArrayAsRectangle() const |
| 838 | { | 836 | { |
| 839 | - if (auto array = asArray()) { | ||
| 840 | - if (array->size() != 4) { | 837 | + if (auto array = as_array(strict)) { |
| 838 | + if (array.size() != 4) { | ||
| 841 | return {}; | 839 | return {}; |
| 842 | } | 840 | } |
| 843 | double items[4]; | 841 | double items[4]; |
| 844 | for (int i = 0; i < 4; ++i) { | 842 | for (int i = 0; i < 4; ++i) { |
| 845 | - if (auto item = array->at(i).second; !item.getValueAsNumber(items[i])) { | 843 | + if (auto item = array.at(i).second; !item.getValueAsNumber(items[i])) { |
| 846 | return {}; | 844 | return {}; |
| 847 | } | 845 | } |
| 848 | } | 846 | } |
| @@ -858,13 +856,13 @@ QPDFObjectHandle::getArrayAsRectangle() const | @@ -858,13 +856,13 @@ QPDFObjectHandle::getArrayAsRectangle() const | ||
| 858 | QPDFObjectHandle::Matrix | 856 | QPDFObjectHandle::Matrix |
| 859 | QPDFObjectHandle::getArrayAsMatrix() const | 857 | QPDFObjectHandle::getArrayAsMatrix() const |
| 860 | { | 858 | { |
| 861 | - if (auto array = asArray()) { | ||
| 862 | - if (array->size() != 6) { | 859 | + if (auto array = as_array(strict)) { |
| 860 | + if (array.size() != 6) { | ||
| 863 | return {}; | 861 | return {}; |
| 864 | } | 862 | } |
| 865 | double items[6]; | 863 | double items[6]; |
| 866 | for (int i = 0; i < 6; ++i) { | 864 | for (int i = 0; i < 6; ++i) { |
| 867 | - if (auto item = array->at(i).second; !item.getValueAsNumber(items[i])) { | 865 | + if (auto item = array.at(i).second; !item.getValueAsNumber(items[i])) { |
| 868 | return {}; | 866 | return {}; |
| 869 | } | 867 | } |
| 870 | } | 868 | } |
| @@ -876,13 +874,11 @@ QPDFObjectHandle::getArrayAsMatrix() const | @@ -876,13 +874,11 @@ QPDFObjectHandle::getArrayAsMatrix() const | ||
| 876 | std::vector<QPDFObjectHandle> | 874 | std::vector<QPDFObjectHandle> |
| 877 | QPDFObjectHandle::getArrayAsVector() const | 875 | QPDFObjectHandle::getArrayAsVector() const |
| 878 | { | 876 | { |
| 879 | - auto array = asArray(); | ||
| 880 | - if (array) { | ||
| 881 | - return array->getAsVector(); | ||
| 882 | - } else { | ||
| 883 | - typeWarning("array", "treating as empty"); | ||
| 884 | - QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector"); | 877 | + if (auto array = as_array(strict)) { |
| 878 | + return array.getAsVector(); | ||
| 885 | } | 879 | } |
| 880 | + typeWarning("array", "treating as empty"); | ||
| 881 | + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector"); | ||
| 886 | return {}; | 882 | return {}; |
| 887 | } | 883 | } |
| 888 | 884 | ||
| @@ -891,8 +887,8 @@ QPDFObjectHandle::getArrayAsVector() const | @@ -891,8 +887,8 @@ QPDFObjectHandle::getArrayAsVector() const | ||
| 891 | void | 887 | void |
| 892 | QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) | 888 | QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) |
| 893 | { | 889 | { |
| 894 | - if (auto array = asArray()) { | ||
| 895 | - if (!array->setAt(n, item)) { | 890 | + if (auto array = as_array(strict)) { |
| 891 | + if (!array.setAt(n, item)) { | ||
| 896 | objectWarning("ignoring attempt to set out of bounds array item"); | 892 | objectWarning("ignoring attempt to set out of bounds array item"); |
| 897 | QTC::TC("qpdf", "QPDFObjectHandle set array bounds"); | 893 | QTC::TC("qpdf", "QPDFObjectHandle set array bounds"); |
| 898 | } | 894 | } |
| @@ -904,8 +900,8 @@ QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) | @@ -904,8 +900,8 @@ QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) | ||
| 904 | void | 900 | void |
| 905 | QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) | 901 | QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) |
| 906 | { | 902 | { |
| 907 | - if (auto array = asArray()) { | ||
| 908 | - array->setFromVector(items); | 903 | + if (auto array = as_array(strict)) { |
| 904 | + array.setFromVector(items); | ||
| 909 | } else { | 905 | } else { |
| 910 | typeWarning("array", "ignoring attempt to replace items"); | 906 | typeWarning("array", "ignoring attempt to replace items"); |
| 911 | QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items"); | 907 | QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items"); |
| @@ -915,8 +911,8 @@ QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) | @@ -915,8 +911,8 @@ QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) | ||
| 915 | void | 911 | void |
| 916 | QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) | 912 | QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) |
| 917 | { | 913 | { |
| 918 | - if (auto array = asArray()) { | ||
| 919 | - if (!array->insert(at, item)) { | 914 | + if (auto array = as_array(strict)) { |
| 915 | + if (!array.insert(at, item)) { | ||
| 920 | objectWarning("ignoring attempt to insert out of bounds array item"); | 916 | objectWarning("ignoring attempt to insert out of bounds array item"); |
| 921 | QTC::TC("qpdf", "QPDFObjectHandle insert array bounds"); | 917 | QTC::TC("qpdf", "QPDFObjectHandle insert array bounds"); |
| 922 | } | 918 | } |
| @@ -936,8 +932,8 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item) | @@ -936,8 +932,8 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item) | ||
| 936 | void | 932 | void |
| 937 | QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) | 933 | QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) |
| 938 | { | 934 | { |
| 939 | - if (auto array = asArray()) { | ||
| 940 | - array->push_back(item); | 935 | + if (auto array = as_array(strict)) { |
| 936 | + array.push_back(item); | ||
| 941 | } else { | 937 | } else { |
| 942 | typeWarning("array", "ignoring attempt to append item"); | 938 | typeWarning("array", "ignoring attempt to append item"); |
| 943 | QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item"); | 939 | QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item"); |
| @@ -954,8 +950,8 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item) | @@ -954,8 +950,8 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item) | ||
| 954 | void | 950 | void |
| 955 | QPDFObjectHandle::eraseItem(int at) | 951 | QPDFObjectHandle::eraseItem(int at) |
| 956 | { | 952 | { |
| 957 | - if (auto array = asArray()) { | ||
| 958 | - if (!array->erase(at)) { | 953 | + if (auto array = as_array(strict)) { |
| 954 | + if (!array.erase(at)) { | ||
| 959 | objectWarning("ignoring attempt to erase out of bounds array item"); | 955 | objectWarning("ignoring attempt to erase out of bounds array item"); |
| 960 | QTC::TC("qpdf", "QPDFObjectHandle erase array bounds"); | 956 | QTC::TC("qpdf", "QPDFObjectHandle erase array bounds"); |
| 961 | } | 957 | } |
| @@ -968,8 +964,8 @@ QPDFObjectHandle::eraseItem(int at) | @@ -968,8 +964,8 @@ QPDFObjectHandle::eraseItem(int at) | ||
| 968 | QPDFObjectHandle | 964 | QPDFObjectHandle |
| 969 | QPDFObjectHandle::eraseItemAndGetOld(int at) | 965 | QPDFObjectHandle::eraseItemAndGetOld(int at) |
| 970 | { | 966 | { |
| 971 | - auto array = asArray(); | ||
| 972 | - auto result = (array && at < array->size() && at >= 0) ? array->at(at).second : newNull(); | 967 | + auto array = as_array(strict); |
| 968 | + auto result = (array && at < array.size() && at >= 0) ? array.at(at).second : newNull(); | ||
| 973 | eraseItem(at); | 969 | eraseItem(at); |
| 974 | return result; | 970 | return result; |
| 975 | } | 971 | } |
| @@ -1344,12 +1340,12 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( | @@ -1344,12 +1340,12 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( | ||
| 1344 | { | 1340 | { |
| 1345 | all_description = description; | 1341 | all_description = description; |
| 1346 | std::vector<QPDFObjectHandle> result; | 1342 | std::vector<QPDFObjectHandle> result; |
| 1347 | - if (auto array = asArray()) { | ||
| 1348 | - int n_items = array->size(); | 1343 | + if (auto array = as_array(strict)) { |
| 1344 | + int n_items = array.size(); | ||
| 1349 | for (int i = 0; i < n_items; ++i) { | 1345 | for (int i = 0; i < n_items; ++i) { |
| 1350 | - QPDFObjectHandle item = array->at(i).second; | 1346 | + QPDFObjectHandle item = array.at(i).second; |
| 1351 | if (item.isStream()) { | 1347 | if (item.isStream()) { |
| 1352 | - result.push_back(item); | 1348 | + result.emplace_back(item); |
| 1353 | } else { | 1349 | } else { |
| 1354 | QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array"); | 1350 | QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array"); |
| 1355 | warn( | 1351 | warn( |
| @@ -1363,7 +1359,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( | @@ -1363,7 +1359,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( | ||
| 1363 | } | 1359 | } |
| 1364 | } | 1360 | } |
| 1365 | } else if (isStream()) { | 1361 | } else if (isStream()) { |
| 1366 | - result.push_back(*this); | 1362 | + result.emplace_back(*this); |
| 1367 | } else if (!isNull()) { | 1363 | } else if (!isNull()) { |
| 1368 | warn( | 1364 | warn( |
| 1369 | getOwningQPDF(), | 1365 | getOwningQPDF(), |
| @@ -1993,10 +1989,10 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams) | @@ -1993,10 +1989,10 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams) | ||
| 1993 | this->obj = obj->copy(true); | 1989 | this->obj = obj->copy(true); |
| 1994 | } else if (isArray()) { | 1990 | } else if (isArray()) { |
| 1995 | std::vector<QPDFObjectHandle> items; | 1991 | std::vector<QPDFObjectHandle> items; |
| 1996 | - auto array = asArray(); | ||
| 1997 | - int n = array->size(); | 1992 | + auto array = as_array(strict); |
| 1993 | + int n = array.size(); | ||
| 1998 | for (int i = 0; i < n; ++i) { | 1994 | for (int i = 0; i < n; ++i) { |
| 1999 | - items.push_back(array->at(i).second); | 1995 | + items.emplace_back(array.at(i).second); |
| 2000 | items.back().makeDirect(visited, stop_at_streams); | 1996 | items.back().makeDirect(visited, stop_at_streams); |
| 2001 | } | 1997 | } |
| 2002 | this->obj = QPDF_Array::create(items); | 1998 | this->obj = QPDF_Array::create(items); |
libqpdf/QPDF_Array.cc
| 1 | #include <qpdf/QPDF_Array.hh> | 1 | #include <qpdf/QPDF_Array.hh> |
| 2 | 2 | ||
| 3 | #include <qpdf/JSON_writer.hh> | 3 | #include <qpdf/JSON_writer.hh> |
| 4 | -#include <qpdf/QPDFObjectHandle.hh> | 4 | +#include <qpdf/QPDFObjectHandle_private.hh> |
| 5 | #include <qpdf/QPDFObject_private.hh> | 5 | #include <qpdf/QPDFObject_private.hh> |
| 6 | #include <qpdf/QTC.hh> | 6 | #include <qpdf/QTC.hh> |
| 7 | 7 | ||
| 8 | +using namespace qpdf; | ||
| 9 | + | ||
| 8 | static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); | 10 | static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); |
| 9 | 11 | ||
| 10 | inline void | 12 | inline void |
| 11 | QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const | 13 | QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const |
| 12 | { | 14 | { |
| 13 | - if (auto obj = item.getObjectPtr()) { | ||
| 14 | - if (qpdf) { | ||
| 15 | - if (auto item_qpdf = obj->getQPDF()) { | ||
| 16 | - if (qpdf != item_qpdf) { | 15 | + // This is only called from QPDF_Array::setFromVector, which in turn is only called from create. |
| 16 | + // At his point qpdf is a nullptr and therefore the ownership check reduces to an uninitialized | ||
| 17 | + // check | ||
| 18 | + if (!item.getObjectPtr()) { | ||
| 19 | + throw std::logic_error("Attempting to add an uninitialized object to a QPDF_Array."); | ||
| 20 | + } | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +inline void | ||
| 24 | +Array::checkOwnership(QPDFObjectHandle const& item) const | ||
| 25 | +{ | ||
| 26 | + if (auto o = item.getObjectPtr()) { | ||
| 27 | + if (auto pdf = obj->getQPDF()) { | ||
| 28 | + if (auto item_qpdf = o->getQPDF()) { | ||
| 29 | + if (pdf != item_qpdf) { | ||
| 17 | throw std::logic_error( | 30 | throw std::logic_error( |
| 18 | "Attempting to add an object from a different QPDF. Use " | 31 | "Attempting to add an object from a different QPDF. Use " |
| 19 | "QPDF::copyForeignObject to add objects from another file."); | 32 | "QPDF::copyForeignObject to add objects from another file."); |
| @@ -184,47 +197,75 @@ QPDF_Array::writeJSON(int json_version, JSON::Writer& p) | @@ -184,47 +197,75 @@ QPDF_Array::writeJSON(int json_version, JSON::Writer& p) | ||
| 184 | p.writeEnd(']'); | 197 | p.writeEnd(']'); |
| 185 | } | 198 | } |
| 186 | 199 | ||
| 200 | +QPDF_Array* | ||
| 201 | +Array::array() const | ||
| 202 | +{ | ||
| 203 | + if (obj) { | ||
| 204 | + if (auto a = obj->as<QPDF_Array>()) { | ||
| 205 | + return a; | ||
| 206 | + } | ||
| 207 | + } | ||
| 208 | + throw std::runtime_error("Expected an array but found a non-array object"); | ||
| 209 | + return nullptr; // unreachable | ||
| 210 | +} | ||
| 211 | + | ||
| 212 | +QPDFObjectHandle | ||
| 213 | +Array::null() const | ||
| 214 | +{ | ||
| 215 | + return null_oh; | ||
| 216 | +} | ||
| 217 | + | ||
| 218 | +int | ||
| 219 | +Array::size() const | ||
| 220 | +{ | ||
| 221 | + auto a = array(); | ||
| 222 | + return a->sp ? a->sp->size : int(a->elements.size()); | ||
| 223 | +} | ||
| 224 | + | ||
| 187 | std::pair<bool, QPDFObjectHandle> | 225 | std::pair<bool, QPDFObjectHandle> |
| 188 | -QPDF_Array::at(int n) const noexcept | 226 | +Array::at(int n) const |
| 189 | { | 227 | { |
| 228 | + auto a = array(); | ||
| 190 | if (n < 0 || n >= size()) { | 229 | if (n < 0 || n >= size()) { |
| 191 | return {false, {}}; | 230 | return {false, {}}; |
| 192 | - } else if (sp) { | ||
| 193 | - auto const& iter = sp->elements.find(n); | ||
| 194 | - return {true, iter == sp->elements.end() ? null_oh : (*iter).second}; | ||
| 195 | - } else { | ||
| 196 | - return {true, elements[size_t(n)]}; | ||
| 197 | } | 231 | } |
| 232 | + if (!a->sp) { | ||
| 233 | + return {true, a->elements[size_t(n)]}; | ||
| 234 | + } | ||
| 235 | + auto const& iter = a->sp->elements.find(n); | ||
| 236 | + return {true, iter == a->sp->elements.end() ? null() : iter->second}; | ||
| 198 | } | 237 | } |
| 199 | 238 | ||
| 200 | std::vector<QPDFObjectHandle> | 239 | std::vector<QPDFObjectHandle> |
| 201 | -QPDF_Array::getAsVector() const | 240 | +Array::getAsVector() const |
| 202 | { | 241 | { |
| 203 | - if (sp) { | 242 | + auto a = array(); |
| 243 | + if (a->sp) { | ||
| 204 | std::vector<QPDFObjectHandle> v; | 244 | std::vector<QPDFObjectHandle> v; |
| 205 | v.reserve(size_t(size())); | 245 | v.reserve(size_t(size())); |
| 206 | - for (auto const& item: sp->elements) { | 246 | + for (auto const& item: a->sp->elements) { |
| 207 | v.resize(size_t(item.first), null_oh); | 247 | v.resize(size_t(item.first), null_oh); |
| 208 | v.emplace_back(item.second); | 248 | v.emplace_back(item.second); |
| 209 | } | 249 | } |
| 210 | v.resize(size_t(size()), null_oh); | 250 | v.resize(size_t(size()), null_oh); |
| 211 | return v; | 251 | return v; |
| 212 | } else { | 252 | } else { |
| 213 | - return {elements.cbegin(), elements.cend()}; | 253 | + return {a->elements.cbegin(), a->elements.cend()}; |
| 214 | } | 254 | } |
| 215 | } | 255 | } |
| 216 | 256 | ||
| 217 | bool | 257 | bool |
| 218 | -QPDF_Array::setAt(int at, QPDFObjectHandle const& oh) | 258 | +Array::setAt(int at, QPDFObjectHandle const& oh) |
| 219 | { | 259 | { |
| 220 | if (at < 0 || at >= size()) { | 260 | if (at < 0 || at >= size()) { |
| 221 | return false; | 261 | return false; |
| 222 | } | 262 | } |
| 263 | + auto a = array(); | ||
| 223 | checkOwnership(oh); | 264 | checkOwnership(oh); |
| 224 | - if (sp) { | ||
| 225 | - sp->elements[at] = oh.getObj(); | 265 | + if (a->sp) { |
| 266 | + a->sp->elements[at] = oh.getObj(); | ||
| 226 | } else { | 267 | } else { |
| 227 | - elements[size_t(at)] = oh.getObj(); | 268 | + a->elements[size_t(at)] = oh.getObj(); |
| 228 | } | 269 | } |
| 229 | return true; | 270 | return true; |
| 230 | } | 271 | } |
| @@ -240,9 +281,22 @@ QPDF_Array::setFromVector(std::vector<QPDFObjectHandle> const& v) | @@ -240,9 +281,22 @@ QPDF_Array::setFromVector(std::vector<QPDFObjectHandle> const& v) | ||
| 240 | } | 281 | } |
| 241 | } | 282 | } |
| 242 | 283 | ||
| 284 | +void | ||
| 285 | +Array::setFromVector(std::vector<QPDFObjectHandle> const& v) | ||
| 286 | +{ | ||
| 287 | + auto a = array(); | ||
| 288 | + a->elements.resize(0); | ||
| 289 | + a->elements.reserve(v.size()); | ||
| 290 | + for (auto const& item: v) { | ||
| 291 | + checkOwnership(item); | ||
| 292 | + a->elements.push_back(item.getObj()); | ||
| 293 | + } | ||
| 294 | +} | ||
| 295 | + | ||
| 243 | bool | 296 | bool |
| 244 | -QPDF_Array::insert(int at, QPDFObjectHandle const& item) | 297 | +Array::insert(int at, QPDFObjectHandle const& item) |
| 245 | { | 298 | { |
| 299 | + auto a = array(); | ||
| 246 | int sz = size(); | 300 | int sz = size(); |
| 247 | if (at < 0 || at > sz) { | 301 | if (at < 0 || at > sz) { |
| 248 | // As special case, also allow insert beyond the end | 302 | // As special case, also allow insert beyond the end |
| @@ -251,61 +305,63 @@ QPDF_Array::insert(int at, QPDFObjectHandle const& item) | @@ -251,61 +305,63 @@ QPDF_Array::insert(int at, QPDFObjectHandle const& item) | ||
| 251 | push_back(item); | 305 | push_back(item); |
| 252 | } else { | 306 | } else { |
| 253 | checkOwnership(item); | 307 | checkOwnership(item); |
| 254 | - if (sp) { | ||
| 255 | - auto iter = sp->elements.crbegin(); | ||
| 256 | - while (iter != sp->elements.crend()) { | 308 | + if (a->sp) { |
| 309 | + auto iter = a->sp->elements.crbegin(); | ||
| 310 | + while (iter != a->sp->elements.crend()) { | ||
| 257 | auto key = (iter++)->first; | 311 | auto key = (iter++)->first; |
| 258 | if (key >= at) { | 312 | if (key >= at) { |
| 259 | - auto nh = sp->elements.extract(key); | 313 | + auto nh = a->sp->elements.extract(key); |
| 260 | ++nh.key(); | 314 | ++nh.key(); |
| 261 | - sp->elements.insert(std::move(nh)); | 315 | + a->sp->elements.insert(std::move(nh)); |
| 262 | } else { | 316 | } else { |
| 263 | break; | 317 | break; |
| 264 | } | 318 | } |
| 265 | } | 319 | } |
| 266 | - sp->elements[at] = item.getObj(); | ||
| 267 | - ++sp->size; | 320 | + a->sp->elements[at] = item.getObj(); |
| 321 | + ++a->sp->size; | ||
| 268 | } else { | 322 | } else { |
| 269 | - elements.insert(elements.cbegin() + at, item.getObj()); | 323 | + a->elements.insert(a->elements.cbegin() + at, item.getObj()); |
| 270 | } | 324 | } |
| 271 | } | 325 | } |
| 272 | return true; | 326 | return true; |
| 273 | } | 327 | } |
| 274 | 328 | ||
| 275 | void | 329 | void |
| 276 | -QPDF_Array::push_back(QPDFObjectHandle const& item) | 330 | +Array::push_back(QPDFObjectHandle const& item) |
| 277 | { | 331 | { |
| 332 | + auto a = array(); | ||
| 278 | checkOwnership(item); | 333 | checkOwnership(item); |
| 279 | - if (sp) { | ||
| 280 | - sp->elements[(sp->size)++] = item.getObj(); | 334 | + if (a->sp) { |
| 335 | + a->sp->elements[(a->sp->size)++] = item.getObj(); | ||
| 281 | } else { | 336 | } else { |
| 282 | - elements.push_back(item.getObj()); | 337 | + a->elements.push_back(item.getObj()); |
| 283 | } | 338 | } |
| 284 | } | 339 | } |
| 285 | 340 | ||
| 286 | bool | 341 | bool |
| 287 | -QPDF_Array::erase(int at) | 342 | +Array::erase(int at) |
| 288 | { | 343 | { |
| 344 | + auto a = array(); | ||
| 289 | if (at < 0 || at >= size()) { | 345 | if (at < 0 || at >= size()) { |
| 290 | return false; | 346 | return false; |
| 291 | } | 347 | } |
| 292 | - if (sp) { | ||
| 293 | - auto end = sp->elements.end(); | ||
| 294 | - if (auto iter = sp->elements.lower_bound(at); iter != end) { | 348 | + if (a->sp) { |
| 349 | + auto end = a->sp->elements.end(); | ||
| 350 | + if (auto iter = a->sp->elements.lower_bound(at); iter != end) { | ||
| 295 | if (iter->first == at) { | 351 | if (iter->first == at) { |
| 296 | iter++; | 352 | iter++; |
| 297 | - sp->elements.erase(at); | 353 | + a->sp->elements.erase(at); |
| 298 | } | 354 | } |
| 299 | 355 | ||
| 300 | while (iter != end) { | 356 | while (iter != end) { |
| 301 | - auto nh = sp->elements.extract(iter++); | 357 | + auto nh = a->sp->elements.extract(iter++); |
| 302 | --nh.key(); | 358 | --nh.key(); |
| 303 | - sp->elements.insert(std::move(nh)); | 359 | + a->sp->elements.insert(std::move(nh)); |
| 304 | } | 360 | } |
| 305 | } | 361 | } |
| 306 | - --(sp->size); | 362 | + --(a->sp->size); |
| 307 | } else { | 363 | } else { |
| 308 | - elements.erase(elements.cbegin() + at); | 364 | + a->elements.erase(a->elements.cbegin() + at); |
| 309 | } | 365 | } |
| 310 | return true; | 366 | return true; |
| 311 | } | 367 | } |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| @@ -4,10 +4,40 @@ | @@ -4,10 +4,40 @@ | ||
| 4 | #include <qpdf/QPDFObjectHandle.hh> | 4 | #include <qpdf/QPDFObjectHandle.hh> |
| 5 | 5 | ||
| 6 | #include <qpdf/QPDFObject_private.hh> | 6 | #include <qpdf/QPDFObject_private.hh> |
| 7 | +#include <qpdf/QPDF_Array.hh> | ||
| 7 | #include <qpdf/QPDF_Dictionary.hh> | 8 | #include <qpdf/QPDF_Dictionary.hh> |
| 8 | 9 | ||
| 9 | namespace qpdf | 10 | namespace qpdf |
| 10 | { | 11 | { |
| 12 | + class Array final: public BaseHandle | ||
| 13 | + { | ||
| 14 | + public: | ||
| 15 | + explicit Array(std::shared_ptr<QPDFObject> const& obj) : | ||
| 16 | + BaseHandle(obj) | ||
| 17 | + { | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + explicit Array(std::shared_ptr<QPDFObject>&& obj) : | ||
| 21 | + BaseHandle(std::move(obj)) | ||
| 22 | + { | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + int size() const; | ||
| 26 | + std::pair<bool, QPDFObjectHandle> at(int n) const; | ||
| 27 | + bool setAt(int at, QPDFObjectHandle const& oh); | ||
| 28 | + bool insert(int at, QPDFObjectHandle const& item); | ||
| 29 | + void push_back(QPDFObjectHandle const& item); | ||
| 30 | + bool erase(int at); | ||
| 31 | + | ||
| 32 | + std::vector<QPDFObjectHandle> getAsVector() const; | ||
| 33 | + void setFromVector(std::vector<QPDFObjectHandle> const& items); | ||
| 34 | + | ||
| 35 | + private: | ||
| 36 | + QPDF_Array* array() const; | ||
| 37 | + void checkOwnership(QPDFObjectHandle const& item) const; | ||
| 38 | + QPDFObjectHandle null() const; | ||
| 39 | + }; | ||
| 40 | + | ||
| 11 | // BaseDictionary is only used as a base class. It does not contain any methods exposed in the | 41 | // BaseDictionary is only used as a base class. It does not contain any methods exposed in the |
| 12 | // public API. | 42 | // public API. |
| 13 | class BaseDictionary: public BaseHandle | 43 | class BaseDictionary: public BaseHandle |
| @@ -58,6 +88,19 @@ namespace qpdf | @@ -58,6 +88,19 @@ namespace qpdf | ||
| 58 | 88 | ||
| 59 | } // namespace qpdf | 89 | } // namespace qpdf |
| 60 | 90 | ||
| 91 | +inline qpdf::Array | ||
| 92 | +QPDFObjectHandle::as_array(qpdf::typed options) const | ||
| 93 | +{ | ||
| 94 | + if (options & qpdf::error) { | ||
| 95 | + assertType("array", false); | ||
| 96 | + } | ||
| 97 | + if (options & qpdf::any_flag || type_code() == ::ot_array || | ||
| 98 | + (options & qpdf::optional && type_code() == ::ot_null)) { | ||
| 99 | + return qpdf::Array(obj); | ||
| 100 | + } | ||
| 101 | + return qpdf::Array({}); | ||
| 102 | +} | ||
| 103 | + | ||
| 61 | inline qpdf::Dictionary | 104 | inline qpdf::Dictionary |
| 62 | QPDFObjectHandle::as_dictionary(qpdf::typed options) const | 105 | QPDFObjectHandle::as_dictionary(qpdf::typed options) const |
| 63 | { | 106 | { |
libqpdf/qpdf/QPDF_Array.hh
| 1 | #ifndef QPDF_ARRAY_HH | 1 | #ifndef QPDF_ARRAY_HH |
| 2 | #define QPDF_ARRAY_HH | 2 | #define QPDF_ARRAY_HH |
| 3 | 3 | ||
| 4 | +#include <qpdf/QPDFObjectHandle.hh> | ||
| 5 | +#include <qpdf/QPDFObject_private.hh> | ||
| 4 | #include <qpdf/QPDFValue.hh> | 6 | #include <qpdf/QPDFValue.hh> |
| 5 | 7 | ||
| 6 | #include <map> | 8 | #include <map> |
| @@ -25,25 +27,20 @@ class QPDF_Array: public QPDFValue | @@ -25,25 +27,20 @@ class QPDF_Array: public QPDFValue | ||
| 25 | void writeJSON(int json_version, JSON::Writer& p) override; | 27 | void writeJSON(int json_version, JSON::Writer& p) override; |
| 26 | void disconnect() override; | 28 | void disconnect() override; |
| 27 | 29 | ||
| 28 | - int | ||
| 29 | - size() const noexcept | ||
| 30 | - { | ||
| 31 | - return sp ? sp->size : int(elements.size()); | ||
| 32 | - } | ||
| 33 | - std::pair<bool, QPDFObjectHandle> at(int n) const noexcept; | ||
| 34 | - bool setAt(int n, QPDFObjectHandle const& oh); | ||
| 35 | - std::vector<QPDFObjectHandle> getAsVector() const; | ||
| 36 | - void setFromVector(std::vector<QPDFObjectHandle> const& items); | ||
| 37 | - bool insert(int at, QPDFObjectHandle const& item); | ||
| 38 | - void push_back(QPDFObjectHandle const& item); | ||
| 39 | - bool erase(int at); | ||
| 40 | - | ||
| 41 | private: | 30 | private: |
| 31 | + friend class qpdf::Array; | ||
| 42 | QPDF_Array(); | 32 | QPDF_Array(); |
| 43 | QPDF_Array(QPDF_Array const&); | 33 | QPDF_Array(QPDF_Array const&); |
| 44 | QPDF_Array(std::vector<QPDFObjectHandle> const& items); | 34 | QPDF_Array(std::vector<QPDFObjectHandle> const& items); |
| 45 | QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); | 35 | QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); |
| 46 | 36 | ||
| 37 | + int | ||
| 38 | + size() const | ||
| 39 | + { | ||
| 40 | + return sp ? sp->size : int(elements.size()); | ||
| 41 | + } | ||
| 42 | + void setFromVector(std::vector<QPDFObjectHandle> const& items); | ||
| 43 | + | ||
| 47 | void checkOwnership(QPDFObjectHandle const& item) const; | 44 | void checkOwnership(QPDFObjectHandle const& item) const; |
| 48 | 45 | ||
| 49 | std::unique_ptr<Sparse> sp; | 46 | std::unique_ptr<Sparse> sp; |
libtests/sparse_array.cc
| 1 | #include <qpdf/assert_test.h> | 1 | #include <qpdf/assert_test.h> |
| 2 | 2 | ||
| 3 | #include <qpdf/QPDF.hh> | 3 | #include <qpdf/QPDF.hh> |
| 4 | -#include <qpdf/QPDFObjectHandle.hh> | 4 | +#include <qpdf/QPDFObjectHandle_private.hh> |
| 5 | #include <qpdf/QPDFObject_private.hh> | 5 | #include <qpdf/QPDFObject_private.hh> |
| 6 | #include <qpdf/QPDF_Array.hh> | 6 | #include <qpdf/QPDF_Array.hh> |
| 7 | 7 | ||
| @@ -11,7 +11,7 @@ int | @@ -11,7 +11,7 @@ int | ||
| 11 | main() | 11 | main() |
| 12 | { | 12 | { |
| 13 | auto obj = QPDF_Array::create({}, true); | 13 | auto obj = QPDF_Array::create({}, true); |
| 14 | - QPDF_Array& a = *obj->as<QPDF_Array>(); | 14 | + auto a = qpdf::Array(obj); |
| 15 | 15 | ||
| 16 | assert(a.size() == 0); | 16 | assert(a.size() == 0); |
| 17 | 17 | ||
| @@ -89,15 +89,15 @@ main() | @@ -89,15 +89,15 @@ main() | ||
| 89 | pdf.emptyPDF(); | 89 | pdf.emptyPDF(); |
| 90 | 90 | ||
| 91 | obj = QPDF_Array::create({10, "null"_qpdf.getObj()}, true); | 91 | obj = QPDF_Array::create({10, "null"_qpdf.getObj()}, true); |
| 92 | - QPDF_Array& b = *obj->as<QPDF_Array>(); | 92 | + auto b = qpdf::Array(obj); |
| 93 | b.setAt(5, pdf.newIndirectNull()); | 93 | b.setAt(5, pdf.newIndirectNull()); |
| 94 | b.setAt(7, "[0 1 2 3]"_qpdf); | 94 | b.setAt(7, "[0 1 2 3]"_qpdf); |
| 95 | assert(b.at(3).second.isNull()); | 95 | assert(b.at(3).second.isNull()); |
| 96 | assert(b.at(8).second.isNull()); | 96 | assert(b.at(8).second.isNull()); |
| 97 | assert(b.at(5).second.isIndirect()); | 97 | assert(b.at(5).second.isIndirect()); |
| 98 | - assert(b.unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]"); | ||
| 99 | - auto c = b.copy(true); | ||
| 100 | - auto d = b.copy(false); | 98 | + assert(obj->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]"); |
| 99 | + auto c = obj->copy(true); | ||
| 100 | + auto d = obj->copy(false); | ||
| 101 | b.at(7).second.setArrayItem(2, "42"_qpdf); | 101 | b.at(7).second.setArrayItem(2, "42"_qpdf); |
| 102 | assert(c->unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]"); | 102 | assert(c->unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]"); |
| 103 | assert(d->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]"); | 103 | assert(d->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]"); |