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 | 26 | #include <qpdf/Types.h> |
| 27 | 27 | |
| 28 | 28 | #include <cstdint> |
| 29 | +#include <memory> | |
| 29 | 30 | |
| 30 | 31 | class QPDF_Dictionary; |
| 31 | 32 | class QPDFObject; |
| ... | ... | @@ -33,6 +34,7 @@ class QPDFObjectHandle; |
| 33 | 34 | |
| 34 | 35 | namespace qpdf |
| 35 | 36 | { |
| 37 | + class Array; | |
| 36 | 38 | class Dictionary; |
| 37 | 39 | class BaseDictionary; |
| 38 | 40 | ... | ... |
include/qpdf/QPDFObjectHandle.hh
| ... | ... | @@ -1355,6 +1355,7 @@ class QPDFObjectHandle final: public qpdf::BaseHandle |
| 1355 | 1355 | |
| 1356 | 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 | 1359 | inline qpdf::Dictionary as_dictionary(qpdf::typed options = qpdf::typed::any) const; |
| 1359 | 1360 | |
| 1360 | 1361 | private: | ... | ... |
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -777,22 +777,20 @@ QPDFObjectHandle::aitems() |
| 777 | 777 | int |
| 778 | 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 | 788 | QPDFObjectHandle |
| 790 | 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 | 794 | } else { |
| 797 | 795 | objectWarning("returning null for out of bounds array access"); |
| 798 | 796 | QTC::TC("qpdf", "QPDFObjectHandle array bounds"); |
| ... | ... | @@ -808,13 +806,13 @@ QPDFObjectHandle::getArrayItem(int n) const |
| 808 | 806 | bool |
| 809 | 807 | QPDFObjectHandle::isRectangle() const |
| 810 | 808 | { |
| 811 | - if (auto array = asArray()) { | |
| 809 | + if (auto array = as_array(strict)) { | |
| 812 | 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 | 812 | return false; |
| 815 | 813 | } |
| 816 | 814 | } |
| 817 | - return array->size() == 4; | |
| 815 | + return array.size() == 4; | |
| 818 | 816 | } |
| 819 | 817 | return false; |
| 820 | 818 | } |
| ... | ... | @@ -822,13 +820,13 @@ QPDFObjectHandle::isRectangle() const |
| 822 | 820 | bool |
| 823 | 821 | QPDFObjectHandle::isMatrix() const |
| 824 | 822 | { |
| 825 | - if (auto array = asArray()) { | |
| 823 | + if (auto array = as_array(strict)) { | |
| 826 | 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 | 826 | return false; |
| 829 | 827 | } |
| 830 | 828 | } |
| 831 | - return array->size() == 6; | |
| 829 | + return array.size() == 6; | |
| 832 | 830 | } |
| 833 | 831 | return false; |
| 834 | 832 | } |
| ... | ... | @@ -836,13 +834,13 @@ QPDFObjectHandle::isMatrix() const |
| 836 | 834 | QPDFObjectHandle::Rectangle |
| 837 | 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 | 839 | return {}; |
| 842 | 840 | } |
| 843 | 841 | double items[4]; |
| 844 | 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 | 844 | return {}; |
| 847 | 845 | } |
| 848 | 846 | } |
| ... | ... | @@ -858,13 +856,13 @@ QPDFObjectHandle::getArrayAsRectangle() const |
| 858 | 856 | QPDFObjectHandle::Matrix |
| 859 | 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 | 861 | return {}; |
| 864 | 862 | } |
| 865 | 863 | double items[6]; |
| 866 | 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 | 866 | return {}; |
| 869 | 867 | } |
| 870 | 868 | } |
| ... | ... | @@ -876,13 +874,11 @@ QPDFObjectHandle::getArrayAsMatrix() const |
| 876 | 874 | std::vector<QPDFObjectHandle> |
| 877 | 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 | 882 | return {}; |
| 887 | 883 | } |
| 888 | 884 | |
| ... | ... | @@ -891,8 +887,8 @@ QPDFObjectHandle::getArrayAsVector() const |
| 891 | 887 | void |
| 892 | 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 | 892 | objectWarning("ignoring attempt to set out of bounds array item"); |
| 897 | 893 | QTC::TC("qpdf", "QPDFObjectHandle set array bounds"); |
| 898 | 894 | } |
| ... | ... | @@ -904,8 +900,8 @@ QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) |
| 904 | 900 | void |
| 905 | 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 | 905 | } else { |
| 910 | 906 | typeWarning("array", "ignoring attempt to replace items"); |
| 911 | 907 | QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items"); |
| ... | ... | @@ -915,8 +911,8 @@ QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) |
| 915 | 911 | void |
| 916 | 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 | 916 | objectWarning("ignoring attempt to insert out of bounds array item"); |
| 921 | 917 | QTC::TC("qpdf", "QPDFObjectHandle insert array bounds"); |
| 922 | 918 | } |
| ... | ... | @@ -936,8 +932,8 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item) |
| 936 | 932 | void |
| 937 | 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 | 937 | } else { |
| 942 | 938 | typeWarning("array", "ignoring attempt to append item"); |
| 943 | 939 | QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item"); |
| ... | ... | @@ -954,8 +950,8 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item) |
| 954 | 950 | void |
| 955 | 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 | 955 | objectWarning("ignoring attempt to erase out of bounds array item"); |
| 960 | 956 | QTC::TC("qpdf", "QPDFObjectHandle erase array bounds"); |
| 961 | 957 | } |
| ... | ... | @@ -968,8 +964,8 @@ QPDFObjectHandle::eraseItem(int at) |
| 968 | 964 | QPDFObjectHandle |
| 969 | 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 | 969 | eraseItem(at); |
| 974 | 970 | return result; |
| 975 | 971 | } |
| ... | ... | @@ -1344,12 +1340,12 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( |
| 1344 | 1340 | { |
| 1345 | 1341 | all_description = description; |
| 1346 | 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 | 1345 | for (int i = 0; i < n_items; ++i) { |
| 1350 | - QPDFObjectHandle item = array->at(i).second; | |
| 1346 | + QPDFObjectHandle item = array.at(i).second; | |
| 1351 | 1347 | if (item.isStream()) { |
| 1352 | - result.push_back(item); | |
| 1348 | + result.emplace_back(item); | |
| 1353 | 1349 | } else { |
| 1354 | 1350 | QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array"); |
| 1355 | 1351 | warn( |
| ... | ... | @@ -1363,7 +1359,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( |
| 1363 | 1359 | } |
| 1364 | 1360 | } |
| 1365 | 1361 | } else if (isStream()) { |
| 1366 | - result.push_back(*this); | |
| 1362 | + result.emplace_back(*this); | |
| 1367 | 1363 | } else if (!isNull()) { |
| 1368 | 1364 | warn( |
| 1369 | 1365 | getOwningQPDF(), |
| ... | ... | @@ -1993,10 +1989,10 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams) |
| 1993 | 1989 | this->obj = obj->copy(true); |
| 1994 | 1990 | } else if (isArray()) { |
| 1995 | 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 | 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 | 1996 | items.back().makeDirect(visited, stop_at_streams); |
| 2001 | 1997 | } |
| 2002 | 1998 | this->obj = QPDF_Array::create(items); | ... | ... |
libqpdf/QPDF_Array.cc
| 1 | 1 | #include <qpdf/QPDF_Array.hh> |
| 2 | 2 | |
| 3 | 3 | #include <qpdf/JSON_writer.hh> |
| 4 | -#include <qpdf/QPDFObjectHandle.hh> | |
| 4 | +#include <qpdf/QPDFObjectHandle_private.hh> | |
| 5 | 5 | #include <qpdf/QPDFObject_private.hh> |
| 6 | 6 | #include <qpdf/QTC.hh> |
| 7 | 7 | |
| 8 | +using namespace qpdf; | |
| 9 | + | |
| 8 | 10 | static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); |
| 9 | 11 | |
| 10 | 12 | inline void |
| 11 | 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 | 30 | throw std::logic_error( |
| 18 | 31 | "Attempting to add an object from a different QPDF. Use " |
| 19 | 32 | "QPDF::copyForeignObject to add objects from another file."); |
| ... | ... | @@ -184,47 +197,75 @@ QPDF_Array::writeJSON(int json_version, JSON::Writer& p) |
| 184 | 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 | 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 | 229 | if (n < 0 || n >= size()) { |
| 191 | 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 | 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 | 244 | std::vector<QPDFObjectHandle> v; |
| 205 | 245 | v.reserve(size_t(size())); |
| 206 | - for (auto const& item: sp->elements) { | |
| 246 | + for (auto const& item: a->sp->elements) { | |
| 207 | 247 | v.resize(size_t(item.first), null_oh); |
| 208 | 248 | v.emplace_back(item.second); |
| 209 | 249 | } |
| 210 | 250 | v.resize(size_t(size()), null_oh); |
| 211 | 251 | return v; |
| 212 | 252 | } else { |
| 213 | - return {elements.cbegin(), elements.cend()}; | |
| 253 | + return {a->elements.cbegin(), a->elements.cend()}; | |
| 214 | 254 | } |
| 215 | 255 | } |
| 216 | 256 | |
| 217 | 257 | bool |
| 218 | -QPDF_Array::setAt(int at, QPDFObjectHandle const& oh) | |
| 258 | +Array::setAt(int at, QPDFObjectHandle const& oh) | |
| 219 | 259 | { |
| 220 | 260 | if (at < 0 || at >= size()) { |
| 221 | 261 | return false; |
| 222 | 262 | } |
| 263 | + auto a = array(); | |
| 223 | 264 | checkOwnership(oh); |
| 224 | - if (sp) { | |
| 225 | - sp->elements[at] = oh.getObj(); | |
| 265 | + if (a->sp) { | |
| 266 | + a->sp->elements[at] = oh.getObj(); | |
| 226 | 267 | } else { |
| 227 | - elements[size_t(at)] = oh.getObj(); | |
| 268 | + a->elements[size_t(at)] = oh.getObj(); | |
| 228 | 269 | } |
| 229 | 270 | return true; |
| 230 | 271 | } |
| ... | ... | @@ -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 | 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 | 300 | int sz = size(); |
| 247 | 301 | if (at < 0 || at > sz) { |
| 248 | 302 | // As special case, also allow insert beyond the end |
| ... | ... | @@ -251,61 +305,63 @@ QPDF_Array::insert(int at, QPDFObjectHandle const& item) |
| 251 | 305 | push_back(item); |
| 252 | 306 | } else { |
| 253 | 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 | 311 | auto key = (iter++)->first; |
| 258 | 312 | if (key >= at) { |
| 259 | - auto nh = sp->elements.extract(key); | |
| 313 | + auto nh = a->sp->elements.extract(key); | |
| 260 | 314 | ++nh.key(); |
| 261 | - sp->elements.insert(std::move(nh)); | |
| 315 | + a->sp->elements.insert(std::move(nh)); | |
| 262 | 316 | } else { |
| 263 | 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 | 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 | 326 | return true; |
| 273 | 327 | } |
| 274 | 328 | |
| 275 | 329 | void |
| 276 | -QPDF_Array::push_back(QPDFObjectHandle const& item) | |
| 330 | +Array::push_back(QPDFObjectHandle const& item) | |
| 277 | 331 | { |
| 332 | + auto a = array(); | |
| 278 | 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 | 336 | } else { |
| 282 | - elements.push_back(item.getObj()); | |
| 337 | + a->elements.push_back(item.getObj()); | |
| 283 | 338 | } |
| 284 | 339 | } |
| 285 | 340 | |
| 286 | 341 | bool |
| 287 | -QPDF_Array::erase(int at) | |
| 342 | +Array::erase(int at) | |
| 288 | 343 | { |
| 344 | + auto a = array(); | |
| 289 | 345 | if (at < 0 || at >= size()) { |
| 290 | 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 | 351 | if (iter->first == at) { |
| 296 | 352 | iter++; |
| 297 | - sp->elements.erase(at); | |
| 353 | + a->sp->elements.erase(at); | |
| 298 | 354 | } |
| 299 | 355 | |
| 300 | 356 | while (iter != end) { |
| 301 | - auto nh = sp->elements.extract(iter++); | |
| 357 | + auto nh = a->sp->elements.extract(iter++); | |
| 302 | 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 | 363 | } else { |
| 308 | - elements.erase(elements.cbegin() + at); | |
| 364 | + a->elements.erase(a->elements.cbegin() + at); | |
| 309 | 365 | } |
| 310 | 366 | return true; |
| 311 | 367 | } | ... | ... |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| ... | ... | @@ -4,10 +4,40 @@ |
| 4 | 4 | #include <qpdf/QPDFObjectHandle.hh> |
| 5 | 5 | |
| 6 | 6 | #include <qpdf/QPDFObject_private.hh> |
| 7 | +#include <qpdf/QPDF_Array.hh> | |
| 7 | 8 | #include <qpdf/QPDF_Dictionary.hh> |
| 8 | 9 | |
| 9 | 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 | 41 | // BaseDictionary is only used as a base class. It does not contain any methods exposed in the |
| 12 | 42 | // public API. |
| 13 | 43 | class BaseDictionary: public BaseHandle |
| ... | ... | @@ -58,6 +88,19 @@ namespace qpdf |
| 58 | 88 | |
| 59 | 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 | 104 | inline qpdf::Dictionary |
| 62 | 105 | QPDFObjectHandle::as_dictionary(qpdf::typed options) const |
| 63 | 106 | { | ... | ... |
libqpdf/qpdf/QPDF_Array.hh
| 1 | 1 | #ifndef QPDF_ARRAY_HH |
| 2 | 2 | #define QPDF_ARRAY_HH |
| 3 | 3 | |
| 4 | +#include <qpdf/QPDFObjectHandle.hh> | |
| 5 | +#include <qpdf/QPDFObject_private.hh> | |
| 4 | 6 | #include <qpdf/QPDFValue.hh> |
| 5 | 7 | |
| 6 | 8 | #include <map> |
| ... | ... | @@ -25,25 +27,20 @@ class QPDF_Array: public QPDFValue |
| 25 | 27 | void writeJSON(int json_version, JSON::Writer& p) override; |
| 26 | 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 | 30 | private: |
| 31 | + friend class qpdf::Array; | |
| 42 | 32 | QPDF_Array(); |
| 43 | 33 | QPDF_Array(QPDF_Array const&); |
| 44 | 34 | QPDF_Array(std::vector<QPDFObjectHandle> const& items); |
| 45 | 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 | 44 | void checkOwnership(QPDFObjectHandle const& item) const; |
| 48 | 45 | |
| 49 | 46 | std::unique_ptr<Sparse> sp; | ... | ... |
libtests/sparse_array.cc
| 1 | 1 | #include <qpdf/assert_test.h> |
| 2 | 2 | |
| 3 | 3 | #include <qpdf/QPDF.hh> |
| 4 | -#include <qpdf/QPDFObjectHandle.hh> | |
| 4 | +#include <qpdf/QPDFObjectHandle_private.hh> | |
| 5 | 5 | #include <qpdf/QPDFObject_private.hh> |
| 6 | 6 | #include <qpdf/QPDF_Array.hh> |
| 7 | 7 | |
| ... | ... | @@ -11,7 +11,7 @@ int |
| 11 | 11 | main() |
| 12 | 12 | { |
| 13 | 13 | auto obj = QPDF_Array::create({}, true); |
| 14 | - QPDF_Array& a = *obj->as<QPDF_Array>(); | |
| 14 | + auto a = qpdf::Array(obj); | |
| 15 | 15 | |
| 16 | 16 | assert(a.size() == 0); |
| 17 | 17 | |
| ... | ... | @@ -89,15 +89,15 @@ main() |
| 89 | 89 | pdf.emptyPDF(); |
| 90 | 90 | |
| 91 | 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 | 93 | b.setAt(5, pdf.newIndirectNull()); |
| 94 | 94 | b.setAt(7, "[0 1 2 3]"_qpdf); |
| 95 | 95 | assert(b.at(3).second.isNull()); |
| 96 | 96 | assert(b.at(8).second.isNull()); |
| 97 | 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 | 101 | b.at(7).second.setArrayItem(2, "42"_qpdf); |
| 102 | 102 | assert(c->unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]"); |
| 103 | 103 | assert(d->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]"); | ... | ... |