Commit b3e6d445cbf73da2b00062c3f639c2453041ee41
1 parent
3661f274
Tweak "AndGet" mutator functions again
Remove any ambiguity around whether old or new value is being returned.
Showing
12 changed files
with
101 additions
and
81 deletions
ChangeLog
| 1 | 2022-07-24 Jay Berkenbilt <ejb@ql.org> | 1 | 2022-07-24 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * QPDFObjectHandle: for the methods insertItem, appendItem, | ||
| 4 | + eraseItem, replaceKey, and removeKey, add a corresponding | ||
| 5 | + "AndGetNew" and/or "AndGetOld" methods. The ones that end with | ||
| 6 | + "AndGetNew" return the newly added item. The ones that end with | ||
| 7 | + "AndGetOld" return the old value. The AndGetNew methods make it | ||
| 8 | + possible to create a new object, add it to an array or dictionary, | ||
| 9 | + and get a handle to it all in one line. The AndGetOld methods make | ||
| 10 | + it easier to retrieve an old value when removing or replacing it. | ||
| 11 | + | ||
| 3 | * Thanks to m-holger for doing significant cleanup of private APIs | 12 | * Thanks to m-holger for doing significant cleanup of private APIs |
| 4 | and internals around QPDFObjGen and for significantly improving | 13 | and internals around QPDFObjGen and for significantly improving |
| 5 | the performance of QPDFObjGen -- See #731. This includes a few | 14 | the performance of QPDFObjGen -- See #731. This includes a few |
| @@ -168,16 +177,6 @@ | @@ -168,16 +177,6 @@ | ||
| 168 | 128-bit without AES) an error rather than a warning when | 177 | 128-bit without AES) an error rather than a warning when |
| 169 | --allow-weak-crypto is not specified. Fixes #576. | 178 | --allow-weak-crypto is not specified. Fixes #576. |
| 170 | 179 | ||
| 171 | -2022-04-29 Jay Berkenbilt <ejb@ql.org> | ||
| 172 | - | ||
| 173 | - * QPDFObjectHandle: for the methods insertItem, appendItem, | ||
| 174 | - eraseItem, replaceKey, and removeKey, add a corresponding "AndGet" | ||
| 175 | - method (insertItemAndGet, appendItemAndGet, eraseItemAndGet, | ||
| 176 | - replaceKeyAndGet, and removeKeyAndGet) that returns the newly | ||
| 177 | - inserted, replaced, or removed item. This makes it possible to | ||
| 178 | - create a new object, add it to an array or dictionary, and get a | ||
| 179 | - handle to it all in one line. | ||
| 180 | - | ||
| 181 | 2022-04-24 Jay Berkenbilt <ejb@ql.org> | 180 | 2022-04-24 Jay Berkenbilt <ejb@ql.org> |
| 182 | 181 | ||
| 183 | * Bug fix: "removeAttachment" in the job JSON now takes an array | 182 | * Bug fix: "removeAttachment" in the job JSON now takes an array |
TODO
| @@ -8,29 +8,6 @@ Before Release: | @@ -8,29 +8,6 @@ Before Release: | ||
| 8 | * Stay on top of https://github.com/pikepdf/pikepdf/pull/315 | 8 | * Stay on top of https://github.com/pikepdf/pikepdf/pull/315 |
| 9 | * Release qtest with updates to qtest-driver and copy back into qpdf | 9 | * Release qtest with updates to qtest-driver and copy back into qpdf |
| 10 | 10 | ||
| 11 | -Parent pointer idea: | ||
| 12 | - | ||
| 13 | -* Have replaceKey, removeKey, and eraseItem return the old values. The | ||
| 14 | - comments will clarify the difference between these and the andGet | ||
| 15 | - versions. | ||
| 16 | -* Add std::weak_ptr<QPDFObject> parent to QPDFObject. When adding a | ||
| 17 | - direct object to an array or dictionary, set its parent. When | ||
| 18 | - removing it, clear the parent pointer. | ||
| 19 | -* When a direct object that already has a parent is added to | ||
| 20 | - something, it is a warning and will become an error in qpdf 12. | ||
| 21 | - There needs to be unsafe add methods used by unsafeShallowCopy. | ||
| 22 | - These will add but not modify the parent pointer. | ||
| 23 | - | ||
| 24 | -This allows an object to be moved from one object to another by | ||
| 25 | -removing it, which returns the now orphaned object, and then inserting | ||
| 26 | -it somewhere else. It also doesn't break the pattern of adding a | ||
| 27 | -direct object to something and subsequently mutating it. It just | ||
| 28 | -prevents the same object from being added to more than one thing. | ||
| 29 | - | ||
| 30 | -Note that arrays and dictionaries still need to contain | ||
| 31 | -QPDFObjectHandle because of indirect objects. This only pertains to | ||
| 32 | -direct objects, which are always "resolved" in QPDFObjectHandle. | ||
| 33 | - | ||
| 34 | Next: | 11 | Next: |
| 35 | * JSON v2 fixes | 12 | * JSON v2 fixes |
| 36 | 13 | ||
| @@ -70,6 +47,26 @@ Pending changes: | @@ -70,6 +47,26 @@ Pending changes: | ||
| 70 | about the case of more than 65,536 pages. If the top node has more | 47 | about the case of more than 65,536 pages. If the top node has more |
| 71 | than 256 children, we'll live with it. | 48 | than 256 children, we'll live with it. |
| 72 | 49 | ||
| 50 | +Parent pointer idea: | ||
| 51 | + | ||
| 52 | +* Add std::weak_ptr<QPDFObject> parent to QPDFObject. When adding a | ||
| 53 | + direct object to an array or dictionary, set its parent. When | ||
| 54 | + removing it, clear the parent pointer. | ||
| 55 | +* When a direct object that already has a parent is added to | ||
| 56 | + something, it is a warning and will become an error in qpdf 12. | ||
| 57 | + There needs to be unsafe add methods used by unsafeShallowCopy. | ||
| 58 | + These will add but not modify the parent pointer. | ||
| 59 | + | ||
| 60 | +This allows an object to be moved from one object to another by | ||
| 61 | +removing it, which returns the now orphaned object, and then inserting | ||
| 62 | +it somewhere else. It also doesn't break the pattern of adding a | ||
| 63 | +direct object to something and subsequently mutating it. It just | ||
| 64 | +prevents the same object from being added to more than one thing. | ||
| 65 | + | ||
| 66 | +Note that arrays and dictionaries still need to contain | ||
| 67 | +QPDFObjectHandle because of indirect objects. This only pertains to | ||
| 68 | +direct objects, which are always "resolved" in QPDFObjectHandle. | ||
| 69 | + | ||
| 73 | Soon: Break ground on "Document-level work" | 70 | Soon: Break ground on "Document-level work" |
| 74 | 71 | ||
| 75 | 72 |
include/qpdf/QPDFObjectHandle.hh
| @@ -999,18 +999,15 @@ class QPDFObjectHandle | @@ -999,18 +999,15 @@ class QPDFObjectHandle | ||
| 999 | 999 | ||
| 1000 | // Mutator methods. | 1000 | // Mutator methods. |
| 1001 | 1001 | ||
| 1002 | - // Since qpdf 11: when a mutator object returns QPDFObjectHandle&, | ||
| 1003 | - // it is a reference to the object itself. This makes it possible | ||
| 1004 | - // to use a fluent style. For example: | 1002 | + // Since qpdf 11: for mutators that may add or remove an item, |
| 1003 | + // there are additional versions whose names contain "AndGet" that | ||
| 1004 | + // return the added or removed item. For example: | ||
| 1005 | // | 1005 | // |
| 1006 | - // array.appendItem(i1).appendItem(i2); | ||
| 1007 | - // | ||
| 1008 | - // would append i1 and then i2 to the array. There are also items | ||
| 1009 | - // that end with AndGet and return a QPDFObjectHandle. These | ||
| 1010 | - // return the newly added object. For example: | ||
| 1011 | - // | ||
| 1012 | - // auto new_dict = dict.replaceKeyAndGet( | 1006 | + // auto new_dict = dict.replaceKeyAndGetNew( |
| 1013 | // "/New", QPDFObjectHandle::newDictionary()); | 1007 | // "/New", QPDFObjectHandle::newDictionary()); |
| 1008 | + // | ||
| 1009 | + // auto old_value = dict.replaceKeyAndGetOld( | ||
| 1010 | + // "/New", "(something)"_qpdf); | ||
| 1014 | 1011 | ||
| 1015 | // Recursively copy this object, making it direct. An exception is | 1012 | // Recursively copy this object, making it direct. An exception is |
| 1016 | // thrown if a loop is detected. With allow_streams true, keep | 1013 | // thrown if a loop is detected. With allow_streams true, keep |
| @@ -1036,20 +1033,20 @@ class QPDFObjectHandle | @@ -1036,20 +1033,20 @@ class QPDFObjectHandle | ||
| 1036 | void insertItem(int at, QPDFObjectHandle const& item); | 1033 | void insertItem(int at, QPDFObjectHandle const& item); |
| 1037 | // Like insertItem but return the item that was inserted. | 1034 | // Like insertItem but return the item that was inserted. |
| 1038 | QPDF_DLL | 1035 | QPDF_DLL |
| 1039 | - QPDFObjectHandle insertItemAndGet(int at, QPDFObjectHandle const& item); | 1036 | + QPDFObjectHandle insertItemAndGetNew(int at, QPDFObjectHandle const& item); |
| 1040 | // Append an item to an array. | 1037 | // Append an item to an array. |
| 1041 | QPDF_DLL | 1038 | QPDF_DLL |
| 1042 | void appendItem(QPDFObjectHandle const& item); | 1039 | void appendItem(QPDFObjectHandle const& item); |
| 1043 | // Append an item, and return the newly added item. | 1040 | // Append an item, and return the newly added item. |
| 1044 | QPDF_DLL | 1041 | QPDF_DLL |
| 1045 | - QPDFObjectHandle appendItemAndGet(QPDFObjectHandle const& item); | 1042 | + QPDFObjectHandle appendItemAndGetNew(QPDFObjectHandle const& item); |
| 1046 | // Remove the item at that position, reducing the size of the | 1043 | // Remove the item at that position, reducing the size of the |
| 1047 | // array by one. | 1044 | // array by one. |
| 1048 | QPDF_DLL | 1045 | QPDF_DLL |
| 1049 | void eraseItem(int at); | 1046 | void eraseItem(int at); |
| 1050 | // Erase and item and return the item that was removed. | 1047 | // Erase and item and return the item that was removed. |
| 1051 | QPDF_DLL | 1048 | QPDF_DLL |
| 1052 | - QPDFObjectHandle eraseItemAndGet(int at); | 1049 | + QPDFObjectHandle eraseItemAndGetOld(int at); |
| 1053 | 1050 | ||
| 1054 | // Mutator methods for dictionary objects | 1051 | // Mutator methods for dictionary objects |
| 1055 | 1052 | ||
| @@ -1060,14 +1057,19 @@ class QPDFObjectHandle | @@ -1060,14 +1057,19 @@ class QPDFObjectHandle | ||
| 1060 | // Replace value of key and return the value. | 1057 | // Replace value of key and return the value. |
| 1061 | QPDF_DLL | 1058 | QPDF_DLL |
| 1062 | QPDFObjectHandle | 1059 | QPDFObjectHandle |
| 1063 | - replaceKeyAndGet(std::string const& key, QPDFObjectHandle const& value); | 1060 | + replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value); |
| 1061 | + // Replace value of key and return the old value, or null if the | ||
| 1062 | + // key was previously not present. | ||
| 1063 | + QPDF_DLL | ||
| 1064 | + QPDFObjectHandle | ||
| 1065 | + replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle const& value); | ||
| 1064 | // Remove key, doing nothing if key does not exist. | 1066 | // Remove key, doing nothing if key does not exist. |
| 1065 | QPDF_DLL | 1067 | QPDF_DLL |
| 1066 | void removeKey(std::string const& key); | 1068 | void removeKey(std::string const& key); |
| 1067 | // Remove key and return the old value. If the old value didn't | 1069 | // Remove key and return the old value. If the old value didn't |
| 1068 | // exist, return a null object. | 1070 | // exist, return a null object. |
| 1069 | QPDF_DLL | 1071 | QPDF_DLL |
| 1070 | - QPDFObjectHandle removeKeyAndGet(std::string const& key); | 1072 | + QPDFObjectHandle removeKeyAndGetOld(std::string const& key); |
| 1071 | 1073 | ||
| 1072 | // ABI: Remove in qpdf 12 | 1074 | // ABI: Remove in qpdf 12 |
| 1073 | [[deprecated("use replaceKey -- it does the same thing")]] QPDF_DLL void | 1075 | [[deprecated("use replaceKey -- it does the same thing")]] QPDF_DLL void |
libqpdf/QPDFAcroFormDocumentHelper.cc
| @@ -40,7 +40,7 @@ QPDFAcroFormDocumentHelper::getOrCreateAcroForm() | @@ -40,7 +40,7 @@ QPDFAcroFormDocumentHelper::getOrCreateAcroForm() | ||
| 40 | { | 40 | { |
| 41 | auto acroform = this->qpdf.getRoot().getKey("/AcroForm"); | 41 | auto acroform = this->qpdf.getRoot().getKey("/AcroForm"); |
| 42 | if (!acroform.isDictionary()) { | 42 | if (!acroform.isDictionary()) { |
| 43 | - acroform = this->qpdf.getRoot().replaceKeyAndGet( | 43 | + acroform = this->qpdf.getRoot().replaceKeyAndGetNew( |
| 44 | "/AcroForm", | 44 | "/AcroForm", |
| 45 | this->qpdf.makeIndirectObject(QPDFObjectHandle::newDictionary())); | 45 | this->qpdf.makeIndirectObject(QPDFObjectHandle::newDictionary())); |
| 46 | } | 46 | } |
| @@ -53,8 +53,8 @@ QPDFAcroFormDocumentHelper::addFormField(QPDFFormFieldObjectHelper ff) | @@ -53,8 +53,8 @@ QPDFAcroFormDocumentHelper::addFormField(QPDFFormFieldObjectHelper ff) | ||
| 53 | auto acroform = getOrCreateAcroForm(); | 53 | auto acroform = getOrCreateAcroForm(); |
| 54 | auto fields = acroform.getKey("/Fields"); | 54 | auto fields = acroform.getKey("/Fields"); |
| 55 | if (!fields.isArray()) { | 55 | if (!fields.isArray()) { |
| 56 | - fields = | ||
| 57 | - acroform.replaceKeyAndGet("/Fields", QPDFObjectHandle::newArray()); | 56 | + fields = acroform.replaceKeyAndGetNew( |
| 57 | + "/Fields", QPDFObjectHandle::newArray()); | ||
| 58 | } | 58 | } |
| 59 | fields.appendItem(ff.getObjectHandle()); | 59 | fields.appendItem(ff.getObjectHandle()); |
| 60 | std::set<QPDFObjGen> visited; | 60 | std::set<QPDFObjGen> visited; |
| @@ -854,7 +854,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | @@ -854,7 +854,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | ||
| 854 | } | 854 | } |
| 855 | dr.makeResourcesIndirect(this->qpdf); | 855 | dr.makeResourcesIndirect(this->qpdf); |
| 856 | if (!dr.isIndirect()) { | 856 | if (!dr.isIndirect()) { |
| 857 | - dr = acroform.replaceKeyAndGet( | 857 | + dr = acroform.replaceKeyAndGetNew( |
| 858 | "/DR", this->qpdf.makeIndirectObject(dr)); | 858 | "/DR", this->qpdf.makeIndirectObject(dr)); |
| 859 | } | 859 | } |
| 860 | // Merge the other document's /DR, creating a conflict | 860 | // Merge the other document's /DR, creating a conflict |
| @@ -1076,7 +1076,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | @@ -1076,7 +1076,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | ||
| 1076 | auto apdict = ah.getAppearanceDictionary(); | 1076 | auto apdict = ah.getAppearanceDictionary(); |
| 1077 | std::vector<QPDFObjectHandle> streams; | 1077 | std::vector<QPDFObjectHandle> streams; |
| 1078 | auto replace_stream = [](auto& dict, auto& key, auto& old) { | 1078 | auto replace_stream = [](auto& dict, auto& key, auto& old) { |
| 1079 | - return dict.replaceKeyAndGet(key, old.copyStream()); | 1079 | + return dict.replaceKeyAndGetNew(key, old.copyStream()); |
| 1080 | }; | 1080 | }; |
| 1081 | if (apdict.isDictionary()) { | 1081 | if (apdict.isDictionary()) { |
| 1082 | for (auto& ap: apdict.ditems()) { | 1082 | for (auto& ap: apdict.ditems()) { |
libqpdf/QPDFEFStreamObjectHelper.cc
| @@ -28,7 +28,7 @@ QPDFEFStreamObjectHelper::setParam( | @@ -28,7 +28,7 @@ QPDFEFStreamObjectHelper::setParam( | ||
| 28 | { | 28 | { |
| 29 | auto params = this->oh.getDict().getKey("/Params"); | 29 | auto params = this->oh.getDict().getKey("/Params"); |
| 30 | if (!params.isDictionary()) { | 30 | if (!params.isDictionary()) { |
| 31 | - params = this->oh.getDict().replaceKeyAndGet( | 31 | + params = this->oh.getDict().replaceKeyAndGetNew( |
| 32 | "/Params", QPDFObjectHandle::newDictionary()); | 32 | "/Params", QPDFObjectHandle::newDictionary()); |
| 33 | } | 33 | } |
| 34 | params.replaceKey(pkey, pval); | 34 | params.replaceKey(pkey, pval); |
libqpdf/QPDFEmbeddedFileDocumentHelper.cc
| @@ -62,8 +62,8 @@ QPDFEmbeddedFileDocumentHelper::initEmbeddedFiles() | @@ -62,8 +62,8 @@ QPDFEmbeddedFileDocumentHelper::initEmbeddedFiles() | ||
| 62 | auto root = qpdf.getRoot(); | 62 | auto root = qpdf.getRoot(); |
| 63 | auto names = root.getKey("/Names"); | 63 | auto names = root.getKey("/Names"); |
| 64 | if (!names.isDictionary()) { | 64 | if (!names.isDictionary()) { |
| 65 | - names = | ||
| 66 | - root.replaceKeyAndGet("/Names", QPDFObjectHandle::newDictionary()); | 65 | + names = root.replaceKeyAndGetNew( |
| 66 | + "/Names", QPDFObjectHandle::newDictionary()); | ||
| 67 | } | 67 | } |
| 68 | auto embedded_files = names.getKey("/EmbeddedFiles"); | 68 | auto embedded_files = names.getKey("/EmbeddedFiles"); |
| 69 | if (!embedded_files.isDictionary()) { | 69 | if (!embedded_files.isDictionary()) { |
libqpdf/QPDFJob.cc
| @@ -2098,7 +2098,7 @@ QPDFJob::doUnderOverlayForPage( | @@ -2098,7 +2098,7 @@ QPDFJob::doUnderOverlayForPage( | ||
| 2098 | QPDFObjectHandle resources = dest_page.getAttribute("/Resources", true); | 2098 | QPDFObjectHandle resources = dest_page.getAttribute("/Resources", true); |
| 2099 | if (!resources.isDictionary()) { | 2099 | if (!resources.isDictionary()) { |
| 2100 | QTC::TC("qpdf", "QPDFJob overlay page with no resources"); | 2100 | QTC::TC("qpdf", "QPDFJob overlay page with no resources"); |
| 2101 | - resources = dest_page.getObjectHandle().replaceKeyAndGet( | 2101 | + resources = dest_page.getObjectHandle().replaceKeyAndGetNew( |
| 2102 | "/Resources", QPDFObjectHandle::newDictionary()); | 2102 | "/Resources", QPDFObjectHandle::newDictionary()); |
| 2103 | } | 2103 | } |
| 2104 | for (int from_pageno: pagenos[pageno]) { | 2104 | for (int from_pageno: pagenos[pageno]) { |
libqpdf/QPDFObjectHandle.cc
| @@ -915,7 +915,7 @@ QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) | @@ -915,7 +915,7 @@ QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) | ||
| 915 | } | 915 | } |
| 916 | 916 | ||
| 917 | QPDFObjectHandle | 917 | QPDFObjectHandle |
| 918 | -QPDFObjectHandle::insertItemAndGet(int at, QPDFObjectHandle const& item) | 918 | +QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item) |
| 919 | { | 919 | { |
| 920 | insertItem(at, item); | 920 | insertItem(at, item); |
| 921 | return item; | 921 | return item; |
| @@ -934,7 +934,7 @@ QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) | @@ -934,7 +934,7 @@ QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) | ||
| 934 | } | 934 | } |
| 935 | 935 | ||
| 936 | QPDFObjectHandle | 936 | QPDFObjectHandle |
| 937 | -QPDFObjectHandle::appendItemAndGet(QPDFObjectHandle const& item) | 937 | +QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item) |
| 938 | { | 938 | { |
| 939 | appendItem(item); | 939 | appendItem(item); |
| 940 | return item; | 940 | return item; |
| @@ -957,7 +957,7 @@ QPDFObjectHandle::eraseItem(int at) | @@ -957,7 +957,7 @@ QPDFObjectHandle::eraseItem(int at) | ||
| 957 | } | 957 | } |
| 958 | 958 | ||
| 959 | QPDFObjectHandle | 959 | QPDFObjectHandle |
| 960 | -QPDFObjectHandle::eraseItemAndGet(int at) | 960 | +QPDFObjectHandle::eraseItemAndGetOld(int at) |
| 961 | { | 961 | { |
| 962 | auto result = QPDFObjectHandle::newNull(); | 962 | auto result = QPDFObjectHandle::newNull(); |
| 963 | if (isArray() && (at < getArrayNItems()) && (at >= 0)) { | 963 | if (isArray() && (at < getArrayNItems()) && (at >= 0)) { |
| @@ -1113,7 +1113,8 @@ QPDFObjectHandle::mergeResources( | @@ -1113,7 +1113,8 @@ QPDFObjectHandle::mergeResources( | ||
| 1113 | // subdictionaries just to get this shallow copy | 1113 | // subdictionaries just to get this shallow copy |
| 1114 | // functionality. | 1114 | // functionality. |
| 1115 | QTC::TC("qpdf", "QPDFObjectHandle replace with copy"); | 1115 | QTC::TC("qpdf", "QPDFObjectHandle replace with copy"); |
| 1116 | - this_val = replaceKeyAndGet(rtype, this_val.shallowCopy()); | 1116 | + this_val = |
| 1117 | + replaceKeyAndGetNew(rtype, this_val.shallowCopy()); | ||
| 1117 | } | 1118 | } |
| 1118 | std::map<QPDFObjGen, std::string> og_to_name; | 1119 | std::map<QPDFObjGen, std::string> og_to_name; |
| 1119 | std::set<std::string> rnames; | 1120 | std::set<std::string> rnames; |
| @@ -1242,13 +1243,22 @@ QPDFObjectHandle::replaceKey( | @@ -1242,13 +1243,22 @@ QPDFObjectHandle::replaceKey( | ||
| 1242 | } | 1243 | } |
| 1243 | 1244 | ||
| 1244 | QPDFObjectHandle | 1245 | QPDFObjectHandle |
| 1245 | -QPDFObjectHandle::replaceKeyAndGet( | 1246 | +QPDFObjectHandle::replaceKeyAndGetNew( |
| 1246 | std::string const& key, QPDFObjectHandle const& value) | 1247 | std::string const& key, QPDFObjectHandle const& value) |
| 1247 | { | 1248 | { |
| 1248 | replaceKey(key, value); | 1249 | replaceKey(key, value); |
| 1249 | return value; | 1250 | return value; |
| 1250 | } | 1251 | } |
| 1251 | 1252 | ||
| 1253 | +QPDFObjectHandle | ||
| 1254 | +QPDFObjectHandle::replaceKeyAndGetOld( | ||
| 1255 | + std::string const& key, QPDFObjectHandle const& value) | ||
| 1256 | +{ | ||
| 1257 | + QPDFObjectHandle old = removeKeyAndGetOld(key); | ||
| 1258 | + replaceKey(key, value); | ||
| 1259 | + return old; | ||
| 1260 | +} | ||
| 1261 | + | ||
| 1252 | void | 1262 | void |
| 1253 | QPDFObjectHandle::removeKey(std::string const& key) | 1263 | QPDFObjectHandle::removeKey(std::string const& key) |
| 1254 | { | 1264 | { |
| @@ -1261,7 +1271,7 @@ QPDFObjectHandle::removeKey(std::string const& key) | @@ -1261,7 +1271,7 @@ QPDFObjectHandle::removeKey(std::string const& key) | ||
| 1261 | } | 1271 | } |
| 1262 | 1272 | ||
| 1263 | QPDFObjectHandle | 1273 | QPDFObjectHandle |
| 1264 | -QPDFObjectHandle::removeKeyAndGet(std::string const& key) | 1274 | +QPDFObjectHandle::removeKeyAndGetOld(std::string const& key) |
| 1265 | { | 1275 | { |
| 1266 | auto result = QPDFObjectHandle::newNull(); | 1276 | auto result = QPDFObjectHandle::newNull(); |
| 1267 | if (isDictionary()) { | 1277 | if (isDictionary()) { |
libqpdf/QPDFPageObjectHelper.cc
| @@ -595,7 +595,7 @@ QPDFPageObjectHelper::removeUnreferencedResourcesHelper( | @@ -595,7 +595,7 @@ QPDFPageObjectHelper::removeUnreferencedResourcesHelper( | ||
| 595 | for (auto const& iter: to_filter) { | 595 | for (auto const& iter: to_filter) { |
| 596 | QPDFObjectHandle dict = resources.getKey(iter); | 596 | QPDFObjectHandle dict = resources.getKey(iter); |
| 597 | if (dict.isDictionary()) { | 597 | if (dict.isDictionary()) { |
| 598 | - dict = resources.replaceKeyAndGet(iter, dict.shallowCopy()); | 598 | + dict = resources.replaceKeyAndGetNew(iter, dict.shallowCopy()); |
| 599 | rdicts.push_back(dict); | 599 | rdicts.push_back(dict); |
| 600 | auto keys = dict.getKeys(); | 600 | auto keys = dict.getKeys(); |
| 601 | known_names.insert(keys.begin(), keys.end()); | 601 | known_names.insert(keys.begin(), keys.end()); |
| @@ -1110,8 +1110,8 @@ QPDFPageObjectHelper::copyAnnotations( | @@ -1110,8 +1110,8 @@ QPDFPageObjectHelper::copyAnnotations( | ||
| 1110 | afdh->addAndRenameFormFields(new_fields); | 1110 | afdh->addAndRenameFormFields(new_fields); |
| 1111 | auto annots = this->oh.getKey("/Annots"); | 1111 | auto annots = this->oh.getKey("/Annots"); |
| 1112 | if (!annots.isArray()) { | 1112 | if (!annots.isArray()) { |
| 1113 | - annots = | ||
| 1114 | - this->oh.replaceKeyAndGet("/Annots", QPDFObjectHandle::newArray()); | 1113 | + annots = this->oh.replaceKeyAndGetNew( |
| 1114 | + "/Annots", QPDFObjectHandle::newArray()); | ||
| 1115 | } | 1115 | } |
| 1116 | for (auto const& annot: new_annots) { | 1116 | for (auto const& annot: new_annots) { |
| 1117 | annots.appendItem(annot); | 1117 | annots.appendItem(annot); |
libqpdf/QPDFWriter.cc
| @@ -1571,7 +1571,7 @@ QPDFWriter::unparseObject( | @@ -1571,7 +1571,7 @@ QPDFWriter::unparseObject( | ||
| 1571 | "qpdf", | 1571 | "qpdf", |
| 1572 | "QPDFWriter create Extensions", | 1572 | "QPDFWriter create Extensions", |
| 1573 | this->m->qdf_mode ? 0 : 1); | 1573 | this->m->qdf_mode ? 0 : 1); |
| 1574 | - extensions = object.replaceKeyAndGet( | 1574 | + extensions = object.replaceKeyAndGetNew( |
| 1575 | "/Extensions", QPDFObjectHandle::newDictionary()); | 1575 | "/Extensions", QPDFObjectHandle::newDictionary()); |
| 1576 | } | 1576 | } |
| 1577 | } else if (!have_extensions_other) { | 1577 | } else if (!have_extensions_other) { |
| @@ -2277,7 +2277,7 @@ QPDFWriter::prepareFileForWrite() | @@ -2277,7 +2277,7 @@ QPDFWriter::prepareFileForWrite() | ||
| 2277 | if (oh.isIndirect()) { | 2277 | if (oh.isIndirect()) { |
| 2278 | QTC::TC("qpdf", "QPDFWriter make Extensions direct"); | 2278 | QTC::TC("qpdf", "QPDFWriter make Extensions direct"); |
| 2279 | extensions_indirect = true; | 2279 | extensions_indirect = true; |
| 2280 | - oh = root.replaceKeyAndGet(key, oh.shallowCopy()); | 2280 | + oh = root.replaceKeyAndGetNew(key, oh.shallowCopy()); |
| 2281 | } | 2281 | } |
| 2282 | if (oh.hasKey("/ADBE")) { | 2282 | if (oh.hasKey("/ADBE")) { |
| 2283 | QPDFObjectHandle adbe = oh.getKey("/ADBE"); | 2283 | QPDFObjectHandle adbe = oh.getKey("/ADBE"); |
manual/release-notes.rst
| @@ -163,9 +163,13 @@ For a detailed list of changes, please see the file | @@ -163,9 +163,13 @@ For a detailed list of changes, please see the file | ||
| 163 | - See examples :file:`examples/qpdfjob-save-attachment.cc` and | 163 | - See examples :file:`examples/qpdfjob-save-attachment.cc` and |
| 164 | :file:`examples/qpdfjob-c-save-attachment.cc`. | 164 | :file:`examples/qpdfjob-c-save-attachment.cc`. |
| 165 | 165 | ||
| 166 | - - New methods ``insertItemAndGet``, ``appendItemAndGet``, | ||
| 167 | - ``eraseItemAndGet``, ``replaceKeyAndGet``, and | ||
| 168 | - ``removeKeyAndGet`` return the newly added or removed object. | 166 | + - In ``QPDFObjectHandle``, new methods ``insertItemAndGetNew``, |
| 167 | + ``appendItemAndGetNew``, and ``replaceKeyAndGetNew`` return the | ||
| 168 | + newly added item. New methods ``eraseItemAndGetOld``, | ||
| 169 | + ``replaceKeyAndGetOld``, and ``removeKeyAndGetOld`` return the | ||
| 170 | + item that was just removed or, in the case of | ||
| 171 | + ``replaceKeyAndGetOld``, a ``null`` object if the object was not | ||
| 172 | + previously there. | ||
| 169 | 173 | ||
| 170 | - Add new ``Pipeline`` methods to reduce the amount of casting that is | 174 | - Add new ``Pipeline`` methods to reduce the amount of casting that is |
| 171 | needed: | 175 | needed: |
qpdf/test_driver.cc
| @@ -1095,8 +1095,8 @@ test_27(QPDF& pdf, char const* arg2) | @@ -1095,8 +1095,8 @@ test_27(QPDF& pdf, char const* arg2) | ||
| 1095 | QPDFObjectHandle s2 = QPDFObjectHandle::newStream(&oldpdf, "potato\n"); | 1095 | QPDFObjectHandle s2 = QPDFObjectHandle::newStream(&oldpdf, "potato\n"); |
| 1096 | auto trailer = pdf.getTrailer(); | 1096 | auto trailer = pdf.getTrailer(); |
| 1097 | trailer.replaceKey("/QTest", pdf.copyForeignObject(qtest)); | 1097 | trailer.replaceKey("/QTest", pdf.copyForeignObject(qtest)); |
| 1098 | - auto qtest2 = | ||
| 1099 | - trailer.replaceKeyAndGet("/QTest2", QPDFObjectHandle::newArray()); | 1098 | + auto qtest2 = trailer.replaceKeyAndGetNew( |
| 1099 | + "/QTest2", QPDFObjectHandle::newArray()); | ||
| 1100 | qtest2.appendItem(pdf.copyForeignObject(s1)); | 1100 | qtest2.appendItem(pdf.copyForeignObject(s1)); |
| 1101 | qtest2.appendItem(pdf.copyForeignObject(s2)); | 1101 | qtest2.appendItem(pdf.copyForeignObject(s2)); |
| 1102 | qtest2.appendItem(pdf.copyForeignObject(s3)); | 1102 | qtest2.appendItem(pdf.copyForeignObject(s3)); |
| @@ -3165,15 +3165,23 @@ test_88(QPDF& pdf, char const* arg2) | @@ -3165,15 +3165,23 @@ test_88(QPDF& pdf, char const* arg2) | ||
| 3165 | auto dict = QPDFObjectHandle::newDictionary(); | 3165 | auto dict = QPDFObjectHandle::newDictionary(); |
| 3166 | dict.replaceKey("/One", QPDFObjectHandle::newInteger(1)); | 3166 | dict.replaceKey("/One", QPDFObjectHandle::newInteger(1)); |
| 3167 | dict.replaceKey("/Two", QPDFObjectHandle::newInteger(2)); | 3167 | dict.replaceKey("/Two", QPDFObjectHandle::newInteger(2)); |
| 3168 | - auto three = dict.replaceKeyAndGet("/Three", QPDFObjectHandle::newArray()); | 3168 | + auto three = |
| 3169 | + dict.replaceKeyAndGetNew("/Three", QPDFObjectHandle::newArray()); | ||
| 3169 | three.appendItem("(a)"_qpdf); | 3170 | three.appendItem("(a)"_qpdf); |
| 3170 | three.appendItem("(b)"_qpdf); | 3171 | three.appendItem("(b)"_qpdf); |
| 3171 | - auto newdict = three.appendItemAndGet(QPDFObjectHandle::newDictionary()); | 3172 | + auto newdict = three.appendItemAndGetNew(QPDFObjectHandle::newDictionary()); |
| 3172 | newdict.replaceKey("/Z", "/Y"_qpdf); | 3173 | newdict.replaceKey("/Z", "/Y"_qpdf); |
| 3173 | newdict.replaceKey("/X", "/W"_qpdf); | 3174 | newdict.replaceKey("/X", "/W"_qpdf); |
| 3175 | + dict.replaceKey("/Quack", "[1 2 3]"_qpdf); | ||
| 3176 | + auto quack = dict.replaceKeyAndGetOld("/Quack", "/Moo"_qpdf); | ||
| 3177 | + assert(quack.unparse() == "[ 1 2 3 ]"); | ||
| 3178 | + auto nothing = | ||
| 3179 | + dict.replaceKeyAndGetOld("/NotThere", QPDFObjectHandle::newNull()); | ||
| 3180 | + assert(nothing.isNull()); | ||
| 3174 | assert(dict.unparse() == R"( | 3181 | assert(dict.unparse() == R"( |
| 3175 | << | 3182 | << |
| 3176 | /One 1 | 3183 | /One 1 |
| 3184 | + /Quack /Moo | ||
| 3177 | /Two 2 | 3185 | /Two 2 |
| 3178 | /Three [ (a) (b) << /Z /Y /X /W >> ] | 3186 | /Three [ (a) (b) << /Z /Y /X /W >> ] |
| 3179 | >> | 3187 | >> |
| @@ -3184,7 +3192,7 @@ test_88(QPDF& pdf, char const* arg2) | @@ -3184,7 +3192,7 @@ test_88(QPDF& pdf, char const* arg2) | ||
| 3184 | assert( | 3192 | assert( |
| 3185 | arr.unparse() == | 3193 | arr.unparse() == |
| 3186 | "[ (00) (0) (a) (b) << /Z /Y /X /W >> ]"_qpdf.unparse()); | 3194 | "[ (00) (0) (a) (b) << /Z /Y /X /W >> ]"_qpdf.unparse()); |
| 3187 | - auto new_dict = arr.insertItemAndGet(1, "<< /P /Q /R /S >>"_qpdf); | 3195 | + auto new_dict = arr.insertItemAndGetNew(1, "<< /P /Q /R /S >>"_qpdf); |
| 3188 | arr.eraseItem(2); | 3196 | arr.eraseItem(2); |
| 3189 | arr.eraseItem(0); | 3197 | arr.eraseItem(0); |
| 3190 | assert( | 3198 | assert( |
| @@ -3200,20 +3208,20 @@ test_88(QPDF& pdf, char const* arg2) | @@ -3200,20 +3208,20 @@ test_88(QPDF& pdf, char const* arg2) | ||
| 3200 | assert( | 3208 | assert( |
| 3201 | arr.unparse() == | 3209 | arr.unparse() == |
| 3202 | "[ << /P /Q /T /U >> (a) (b) << /Z /Y /X /W >> ]"_qpdf.unparse()); | 3210 | "[ << /P /Q /T /U >> (a) (b) << /Z /Y /X /W >> ]"_qpdf.unparse()); |
| 3203 | - auto s = arr.eraseItemAndGet(1); | 3211 | + auto s = arr.eraseItemAndGetOld(1); |
| 3204 | assert(s.unparse() == "(a)"); | 3212 | assert(s.unparse() == "(a)"); |
| 3205 | assert( | 3213 | assert( |
| 3206 | arr.unparse() == | 3214 | arr.unparse() == |
| 3207 | "[ << /P /Q /T /U >> (b) << /Z /Y /X /W >> ]"_qpdf.unparse()); | 3215 | "[ << /P /Q /T /U >> (b) << /Z /Y /X /W >> ]"_qpdf.unparse()); |
| 3208 | 3216 | ||
| 3209 | - assert(new_dict.removeKeyAndGet("/M").isNull()); | ||
| 3210 | - assert(new_dict.removeKeyAndGet("/P").unparse() == "/Q"); | 3217 | + assert(new_dict.removeKeyAndGetOld("/M").isNull()); |
| 3218 | + assert(new_dict.removeKeyAndGetOld("/P").unparse() == "/Q"); | ||
| 3211 | assert(new_dict.unparse() == "<< /T /U >>"_qpdf.unparse()); | 3219 | assert(new_dict.unparse() == "<< /T /U >>"_qpdf.unparse()); |
| 3212 | 3220 | ||
| 3213 | // Test errors | 3221 | // Test errors |
| 3214 | - auto arr2 = pdf.getRoot().replaceKeyAndGet("/QTest", "[1 2]"_qpdf); | 3222 | + auto arr2 = pdf.getRoot().replaceKeyAndGetNew("/QTest", "[1 2]"_qpdf); |
| 3215 | arr2.setObjectDescription(&pdf, "test array"); | 3223 | arr2.setObjectDescription(&pdf, "test array"); |
| 3216 | - assert(arr2.eraseItemAndGet(50).isNull()); | 3224 | + assert(arr2.eraseItemAndGetOld(50).isNull()); |
| 3217 | } | 3225 | } |
| 3218 | 3226 | ||
| 3219 | static void | 3227 | static void |