Commit 3b6b32130627e337ba58e4b6dbfde21aebbac2fc
Committed by
GitHub
Merge pull request #1586 from m-holger/ffoh
Refactor QPDFFormFieldObjectHelper
Showing
5 changed files
with
368 additions
and
191 deletions
libqpdf/QPDFFormFieldObjectHelper.cc
| ... | ... | @@ -18,6 +18,8 @@ using namespace qpdf; |
| 18 | 18 | |
| 19 | 19 | using FormField = qpdf::impl::FormField; |
| 20 | 20 | |
| 21 | +const QPDFObjectHandle FormField::null_oh; | |
| 22 | + | |
| 21 | 23 | class QPDFFormFieldObjectHelper::Members: public FormField |
| 22 | 24 | { |
| 23 | 25 | public: |
| ... | ... | @@ -48,93 +50,71 @@ QPDFFormFieldObjectHelper::isNull() |
| 48 | 50 | QPDFFormFieldObjectHelper |
| 49 | 51 | QPDFFormFieldObjectHelper::getParent() |
| 50 | 52 | { |
| 51 | - return {Null::if_null(m->getParent().oh())}; | |
| 52 | -} | |
| 53 | - | |
| 54 | -FormField | |
| 55 | -FormField::getParent() | |
| 56 | -{ | |
| 57 | - return {oh()["/Parent"]}; // maybe null | |
| 53 | + return {Null::if_null(m->Parent().oh())}; | |
| 58 | 54 | } |
| 59 | 55 | |
| 60 | 56 | QPDFFormFieldObjectHelper |
| 61 | 57 | QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different) |
| 62 | 58 | { |
| 63 | - return Null::if_null(m->getTopLevelField(is_different).oh()); | |
| 59 | + return Null::if_null(m->root_field(is_different).oh()); | |
| 64 | 60 | } |
| 65 | 61 | |
| 66 | 62 | FormField |
| 67 | -FormField::getTopLevelField(bool* is_different) | |
| 63 | +FormField::root_field(bool* is_different) | |
| 68 | 64 | { |
| 65 | + if (is_different) { | |
| 66 | + *is_different = false; | |
| 67 | + } | |
| 69 | 68 | if (!obj) { |
| 70 | 69 | return {}; |
| 71 | 70 | } |
| 72 | - auto top_field = oh(); | |
| 71 | + auto rf = *this; | |
| 72 | + size_t depth = 0; // Don't bother with loop detection until depth becomes suspicious | |
| 73 | 73 | QPDFObjGen::set seen; |
| 74 | - while (seen.add(top_field) && !top_field.getKeyIfDict("/Parent").null()) { | |
| 75 | - top_field = top_field.getKey("/Parent"); | |
| 74 | + while (rf.Parent() && (++depth < 10 || seen.add(rf))) { | |
| 75 | + rf = rf.Parent(); | |
| 76 | 76 | if (is_different) { |
| 77 | 77 | *is_different = true; |
| 78 | 78 | } |
| 79 | 79 | } |
| 80 | - return {top_field}; | |
| 81 | -} | |
| 82 | - | |
| 83 | -QPDFObjectHandle | |
| 84 | -FormField::getFieldFromAcroForm(std::string const& name) | |
| 85 | -{ | |
| 86 | - QPDFObjectHandle result = QPDFObjectHandle::newNull(); | |
| 87 | - // Fields are supposed to be indirect, so this should work. | |
| 88 | - QPDF* q = oh().getOwningQPDF(); | |
| 89 | - if (!q) { | |
| 90 | - return result; | |
| 91 | - } | |
| 92 | - auto acroform = q->getRoot().getKey("/AcroForm"); | |
| 93 | - if (!acroform.isDictionary()) { | |
| 94 | - return result; | |
| 95 | - } | |
| 96 | - return acroform.getKey(name); | |
| 80 | + return rf; | |
| 97 | 81 | } |
| 98 | 82 | |
| 99 | 83 | QPDFObjectHandle |
| 100 | 84 | QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name) |
| 101 | 85 | { |
| 102 | - return m->getInheritableFieldValue(name); | |
| 86 | + return Null::if_null(m->inheritable_value<QPDFObjectHandle>(name)); | |
| 103 | 87 | } |
| 104 | 88 | |
| 105 | -QPDFObjectHandle | |
| 106 | -FormField::getInheritableFieldValue(std::string const& name) | |
| 89 | +QPDFObjectHandle const& | |
| 90 | +FormField::inherited(std::string const& name, bool acroform) const | |
| 107 | 91 | { |
| 108 | - QPDFObjectHandle node = oh(); | |
| 109 | - if (!node.isDictionary()) { | |
| 110 | - return QPDFObjectHandle::newNull(); | |
| 92 | + if (!obj) { | |
| 93 | + return null_oh; | |
| 111 | 94 | } |
| 112 | - QPDFObjectHandle result(node.getKey(name)); | |
| 113 | - if (result.null()) { | |
| 114 | - QPDFObjGen::set seen; | |
| 115 | - while (seen.add(node) && node.hasKey("/Parent")) { | |
| 116 | - node = node.getKey("/Parent"); | |
| 117 | - result = node.getKey(name); | |
| 118 | - if (!result.null()) { | |
| 119 | - return result; | |
| 120 | - } | |
| 95 | + auto node = *this; | |
| 96 | + QPDFObjGen::set seen; | |
| 97 | + size_t depth = 0; // Don't bother with loop detection until depth becomes suspicious | |
| 98 | + while (node.Parent() && (++depth < 10 || seen.add(node))) { | |
| 99 | + node = node.Parent(); | |
| 100 | + if (auto const& result = node[name]) { | |
| 101 | + return {result}; | |
| 121 | 102 | } |
| 122 | 103 | } |
| 123 | - return result; | |
| 104 | + return acroform ? from_AcroForm(name) : null_oh; | |
| 124 | 105 | } |
| 125 | 106 | |
| 126 | 107 | std::string |
| 127 | 108 | QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(std::string const& name) |
| 128 | 109 | { |
| 129 | - return m->getInheritableFieldValueAsString(name); | |
| 110 | + return m->inheritable_string(name); | |
| 130 | 111 | } |
| 131 | 112 | |
| 132 | 113 | std::string |
| 133 | -FormField::getInheritableFieldValueAsString(std::string const& name) | |
| 114 | +FormField::inheritable_string(std::string const& name) const | |
| 134 | 115 | { |
| 135 | - auto fv = getInheritableFieldValue(name); | |
| 136 | - if (fv.isString()) { | |
| 137 | - return fv.getUTF8Value(); | |
| 116 | + if (auto fv = inheritable_value<String>(name)) { | |
| 117 | + return fv.utf8_value(); | |
| 138 | 118 | } |
| 139 | 119 | return {}; |
| 140 | 120 | } |
| ... | ... | @@ -142,13 +122,7 @@ FormField::getInheritableFieldValueAsString(std::string const& name) |
| 142 | 122 | std::string |
| 143 | 123 | QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(std::string const& name) |
| 144 | 124 | { |
| 145 | - return m->getInheritableFieldValueAsName(name); | |
| 146 | -} | |
| 147 | - | |
| 148 | -std::string | |
| 149 | -FormField::getInheritableFieldValueAsName(std::string const& name) | |
| 150 | -{ | |
| 151 | - if (Name fv = getInheritableFieldValue(name)) { | |
| 125 | + if (auto fv = m->inheritable_value<Name>(name)) { | |
| 152 | 126 | return fv; |
| 153 | 127 | } |
| 154 | 128 | return {}; |
| ... | ... | @@ -157,35 +131,33 @@ FormField::getInheritableFieldValueAsName(std::string const& name) |
| 157 | 131 | std::string |
| 158 | 132 | QPDFFormFieldObjectHelper::getFieldType() |
| 159 | 133 | { |
| 160 | - return m->getFieldType(); | |
| 161 | -} | |
| 162 | - | |
| 163 | -std::string | |
| 164 | -FormField::getFieldType() | |
| 165 | -{ | |
| 166 | - return getInheritableFieldValueAsName("/FT"); | |
| 134 | + if (auto ft = m->FT()) { | |
| 135 | + return ft; | |
| 136 | + } | |
| 137 | + return {}; | |
| 167 | 138 | } |
| 168 | 139 | |
| 169 | 140 | std::string |
| 170 | 141 | QPDFFormFieldObjectHelper::getFullyQualifiedName() |
| 171 | 142 | { |
| 172 | - return m->getFullyQualifiedName(); | |
| 143 | + return m->fully_qualified_name(); | |
| 173 | 144 | } |
| 174 | 145 | |
| 175 | 146 | std::string |
| 176 | -FormField::getFullyQualifiedName() | |
| 147 | +FormField::fully_qualified_name() const | |
| 177 | 148 | { |
| 178 | 149 | std::string result; |
| 179 | - QPDFObjectHandle node = oh(); | |
| 150 | + auto node = *this; | |
| 180 | 151 | QPDFObjGen::set seen; |
| 181 | - while (!node.null() && seen.add(node)) { | |
| 182 | - if (node.getKey("/T").isString()) { | |
| 152 | + size_t depth = 0; // Don't bother with loop detection until depth becomes suspicious | |
| 153 | + while (node && (++depth < 10 || seen.add(node))) { | |
| 154 | + if (auto T = node.T()) { | |
| 183 | 155 | if (!result.empty()) { |
| 184 | - result = "." + result; | |
| 156 | + result.insert(0, 1, '.'); | |
| 185 | 157 | } |
| 186 | - result = node.getKey("/T").getUTF8Value() + result; | |
| 158 | + result.insert(0, T.utf8_value()); | |
| 187 | 159 | } |
| 188 | - node = node.getKey("/Parent"); | |
| 160 | + node = node.Parent(); | |
| 189 | 161 | } |
| 190 | 162 | return result; |
| 191 | 163 | } |
| ... | ... | @@ -193,131 +165,110 @@ FormField::getFullyQualifiedName() |
| 193 | 165 | std::string |
| 194 | 166 | QPDFFormFieldObjectHelper::getPartialName() |
| 195 | 167 | { |
| 196 | - return m->getPartialName(); | |
| 168 | + return m->partial_name(); | |
| 197 | 169 | } |
| 198 | 170 | |
| 199 | 171 | std::string |
| 200 | -FormField::getPartialName() | |
| 172 | +FormField::partial_name() const | |
| 201 | 173 | { |
| 202 | - std::string result; | |
| 203 | - if (oh().getKey("/T").isString()) { | |
| 204 | - result = oh().getKey("/T").getUTF8Value(); | |
| 174 | + if (auto pn = T()) { | |
| 175 | + return pn.utf8_value(); | |
| 205 | 176 | } |
| 206 | - return result; | |
| 177 | + return {}; | |
| 207 | 178 | } |
| 208 | 179 | |
| 209 | 180 | std::string |
| 210 | 181 | QPDFFormFieldObjectHelper::getAlternativeName() |
| 211 | 182 | { |
| 212 | - return m->getAlternativeName(); | |
| 183 | + return m->alternative_name(); | |
| 213 | 184 | } |
| 214 | 185 | |
| 215 | 186 | std::string |
| 216 | -FormField::getAlternativeName() | |
| 187 | +FormField::alternative_name() const | |
| 217 | 188 | { |
| 218 | - if (oh().getKey("/TU").isString()) { | |
| 219 | - QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU present"); | |
| 220 | - return oh().getKey("/TU").getUTF8Value(); | |
| 189 | + if (auto an = TU()) { | |
| 190 | + return an.utf8_value(); | |
| 221 | 191 | } |
| 222 | - QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU absent"); | |
| 223 | - return getFullyQualifiedName(); | |
| 192 | + return fully_qualified_name(); | |
| 224 | 193 | } |
| 225 | 194 | |
| 226 | 195 | std::string |
| 227 | 196 | QPDFFormFieldObjectHelper::getMappingName() |
| 228 | 197 | { |
| 229 | - return m->getMappingName(); | |
| 198 | + return m->mapping_name(); | |
| 230 | 199 | } |
| 231 | 200 | |
| 232 | 201 | std::string |
| 233 | -FormField::getMappingName() | |
| 202 | +FormField::mapping_name() const | |
| 234 | 203 | { |
| 235 | - if (oh().getKey("/TM").isString()) { | |
| 236 | - QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM present"); | |
| 237 | - return oh().getKey("/TM").getUTF8Value(); | |
| 204 | + if (auto mn = TM()) { | |
| 205 | + return mn.utf8_value(); | |
| 238 | 206 | } |
| 239 | - QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM absent"); | |
| 240 | - return getAlternativeName(); | |
| 207 | + return alternative_name(); | |
| 241 | 208 | } |
| 242 | 209 | |
| 243 | 210 | QPDFObjectHandle |
| 244 | 211 | QPDFFormFieldObjectHelper::getValue() |
| 245 | 212 | { |
| 246 | - return m->getValue(); | |
| 247 | -} | |
| 248 | - | |
| 249 | -QPDFObjectHandle | |
| 250 | -FormField::getValue() | |
| 251 | -{ | |
| 252 | - return getInheritableFieldValue("/V"); | |
| 213 | + return Null::if_null(m->V<QPDFObjectHandle>()); | |
| 253 | 214 | } |
| 254 | 215 | |
| 255 | 216 | std::string |
| 256 | 217 | QPDFFormFieldObjectHelper::getValueAsString() |
| 257 | 218 | { |
| 258 | - return getInheritableFieldValueAsString("/V"); | |
| 219 | + return m->value(); | |
| 259 | 220 | } |
| 260 | 221 | |
| 261 | 222 | std::string |
| 262 | -FormField::getValueAsString() | |
| 223 | +FormField::value() const | |
| 263 | 224 | { |
| 264 | - return getInheritableFieldValueAsString("/V"); | |
| 225 | + return inheritable_string("/V"); | |
| 265 | 226 | } |
| 266 | 227 | |
| 267 | 228 | QPDFObjectHandle |
| 268 | 229 | QPDFFormFieldObjectHelper::getDefaultValue() |
| 269 | 230 | { |
| 270 | - return m->getDefaultValue(); | |
| 271 | -} | |
| 272 | - | |
| 273 | -QPDFObjectHandle | |
| 274 | -FormField::getDefaultValue() | |
| 275 | -{ | |
| 276 | - return getInheritableFieldValue("/DV"); | |
| 231 | + return Null::if_null(m->DV()); | |
| 277 | 232 | } |
| 278 | 233 | |
| 279 | 234 | std::string |
| 280 | 235 | QPDFFormFieldObjectHelper::getDefaultValueAsString() |
| 281 | 236 | { |
| 282 | - return m->getDefaultValueAsString(); | |
| 237 | + return m->default_value(); | |
| 283 | 238 | } |
| 284 | 239 | |
| 285 | 240 | std::string |
| 286 | -FormField::getDefaultValueAsString() | |
| 241 | +FormField::default_value() const | |
| 287 | 242 | { |
| 288 | - return getInheritableFieldValueAsString("/DV"); | |
| 243 | + return inheritable_string("/DV"); | |
| 289 | 244 | } |
| 290 | 245 | |
| 291 | 246 | QPDFObjectHandle |
| 292 | 247 | QPDFFormFieldObjectHelper::getDefaultResources() |
| 293 | 248 | { |
| 294 | - return m->getDefaultResources(); | |
| 249 | + return Null::if_null(m->getDefaultResources()); | |
| 295 | 250 | } |
| 296 | 251 | |
| 297 | 252 | QPDFObjectHandle |
| 298 | 253 | FormField::getDefaultResources() |
| 299 | 254 | { |
| 300 | - return getFieldFromAcroForm("/DR"); | |
| 255 | + return from_AcroForm("/DR"); | |
| 301 | 256 | } |
| 302 | 257 | |
| 303 | 258 | std::string |
| 304 | 259 | QPDFFormFieldObjectHelper::getDefaultAppearance() |
| 305 | 260 | { |
| 306 | - return m->getDefaultAppearance(); | |
| 261 | + return m->default_appearance(); | |
| 307 | 262 | } |
| 308 | 263 | |
| 309 | 264 | std::string |
| 310 | -FormField::getDefaultAppearance() | |
| 265 | +FormField::default_appearance() const | |
| 311 | 266 | { |
| 312 | - auto value = getInheritableFieldValue("/DA"); | |
| 313 | - bool looked_in_acroform = false; | |
| 314 | - if (!value.isString()) { | |
| 315 | - value = getFieldFromAcroForm("/DA"); | |
| 316 | - looked_in_acroform = true; | |
| 267 | + if (auto DA = inheritable_value<String>("/DA")) { | |
| 268 | + return DA.utf8_value(); | |
| 317 | 269 | } |
| 318 | - if (value.isString()) { | |
| 319 | - QTC::TC("qpdf", "QPDFFormFieldObjectHelper DA present", looked_in_acroform ? 0 : 1); | |
| 320 | - return value.getUTF8Value(); | |
| 270 | + if (String DA = from_AcroForm("/DA")) { | |
| 271 | + return DA.utf8_value(); | |
| 321 | 272 | } |
| 322 | 273 | return {}; |
| 323 | 274 | } |
| ... | ... | @@ -331,10 +282,10 @@ QPDFFormFieldObjectHelper::getQuadding() |
| 331 | 282 | int |
| 332 | 283 | FormField::getQuadding() |
| 333 | 284 | { |
| 334 | - QPDFObjectHandle fv = getInheritableFieldValue("/Q"); | |
| 285 | + auto fv = inheritable_value<QPDFObjectHandle>("/Q"); | |
| 335 | 286 | bool looked_in_acroform = false; |
| 336 | 287 | if (!fv.isInteger()) { |
| 337 | - fv = getFieldFromAcroForm("/Q"); | |
| 288 | + fv = from_AcroForm("/Q"); | |
| 338 | 289 | looked_in_acroform = true; |
| 339 | 290 | } |
| 340 | 291 | if (fv.isInteger()) { |
| ... | ... | @@ -353,7 +304,7 @@ QPDFFormFieldObjectHelper::getFlags() |
| 353 | 304 | int |
| 354 | 305 | FormField::getFlags() |
| 355 | 306 | { |
| 356 | - QPDFObjectHandle f = getInheritableFieldValue("/Ff"); | |
| 307 | + auto f = inheritable_value<QPDFObjectHandle>("/Ff"); | |
| 357 | 308 | return f.isInteger() ? f.getIntValueAsInt() : 0; |
| 358 | 309 | } |
| 359 | 310 | |
| ... | ... | @@ -366,7 +317,7 @@ QPDFFormFieldObjectHelper::isText() |
| 366 | 317 | bool |
| 367 | 318 | FormField::isText() |
| 368 | 319 | { |
| 369 | - return getFieldType() == "/Tx"; | |
| 320 | + return FT() == "/Tx"; | |
| 370 | 321 | } |
| 371 | 322 | |
| 372 | 323 | bool |
| ... | ... | @@ -378,7 +329,7 @@ QPDFFormFieldObjectHelper::isCheckbox() |
| 378 | 329 | bool |
| 379 | 330 | FormField::isCheckbox() |
| 380 | 331 | { |
| 381 | - return getFieldType() == "/Btn" && (getFlags() & (ff_btn_radio | ff_btn_pushbutton)) == 0; | |
| 332 | + return FT() == "/Btn" && (getFlags() & (ff_btn_radio | ff_btn_pushbutton)) == 0; | |
| 382 | 333 | } |
| 383 | 334 | |
| 384 | 335 | bool |
| ... | ... | @@ -390,7 +341,7 @@ QPDFFormFieldObjectHelper::isChecked() |
| 390 | 341 | bool |
| 391 | 342 | FormField::isChecked() |
| 392 | 343 | { |
| 393 | - return isCheckbox() && Name(getValue()) != "/Off"; | |
| 344 | + return isCheckbox() && V<Name>() != "/Off"; | |
| 394 | 345 | } |
| 395 | 346 | |
| 396 | 347 | bool |
| ... | ... | @@ -402,7 +353,7 @@ QPDFFormFieldObjectHelper::isRadioButton() |
| 402 | 353 | bool |
| 403 | 354 | FormField::isRadioButton() |
| 404 | 355 | { |
| 405 | - return getFieldType() == "/Btn" && (getFlags() & ff_btn_radio) == ff_btn_radio; | |
| 356 | + return FT() == "/Btn" && (getFlags() & ff_btn_radio) == ff_btn_radio; | |
| 406 | 357 | } |
| 407 | 358 | |
| 408 | 359 | bool |
| ... | ... | @@ -414,7 +365,7 @@ QPDFFormFieldObjectHelper::isPushbutton() |
| 414 | 365 | bool |
| 415 | 366 | FormField::isPushbutton() |
| 416 | 367 | { |
| 417 | - return getFieldType() == "/Btn" && (getFlags() & ff_btn_pushbutton) == ff_btn_pushbutton; | |
| 368 | + return FT() == "/Btn" && (getFlags() & ff_btn_pushbutton) == ff_btn_pushbutton; | |
| 418 | 369 | } |
| 419 | 370 | |
| 420 | 371 | bool |
| ... | ... | @@ -426,7 +377,7 @@ QPDFFormFieldObjectHelper::isChoice() |
| 426 | 377 | bool |
| 427 | 378 | FormField::isChoice() |
| 428 | 379 | { |
| 429 | - return getFieldType() == "/Ch"; | |
| 380 | + return FT() == "/Ch"; | |
| 430 | 381 | } |
| 431 | 382 | |
| 432 | 383 | std::vector<std::string> |
| ... | ... | @@ -442,7 +393,7 @@ FormField::getChoices() |
| 442 | 393 | return {}; |
| 443 | 394 | } |
| 444 | 395 | std::vector<std::string> result; |
| 445 | - for (auto const& item: getInheritableFieldValue("/Opt").as_array()) { | |
| 396 | + for (auto const& item: inheritable_value<Array>("/Opt")) { | |
| 446 | 397 | if (item.isString()) { |
| 447 | 398 | result.emplace_back(item.getUTF8Value()); |
| 448 | 399 | } else if (item.size() == 2) { |
| ... | ... | @@ -489,7 +440,7 @@ void |
| 489 | 440 | FormField::setV(QPDFObjectHandle value, bool need_appearances) |
| 490 | 441 | { |
| 491 | 442 | Name name = value; |
| 492 | - if (getFieldType() == "/Btn") { | |
| 443 | + if (FT() == "/Btn") { | |
| 493 | 444 | if (isCheckbox()) { |
| 494 | 445 | if (!name) { |
| 495 | 446 | warn("ignoring attempt to set a checkbox field to a value whose type is not name"); |
| ... | ... | @@ -652,10 +603,9 @@ QPDFFormFieldObjectHelper::generateAppearance(QPDFAnnotationObjectHelper& aoh) |
| 652 | 603 | void |
| 653 | 604 | FormField::generateAppearance(QPDFAnnotationObjectHelper& aoh) |
| 654 | 605 | { |
| 655 | - std::string ft = getFieldType(); | |
| 656 | 606 | // Ignore field types we don't know how to generate appearances for. Button fields don't really |
| 657 | 607 | // need them -- see code in QPDFAcroFormDocumentHelper::generateAppearancesIfNeeded. |
| 658 | - if ((ft == "/Tx") || (ft == "/Ch")) { | |
| 608 | + if (FT() == "/Tx" || FT() == "/Ch") { | |
| 659 | 609 | generateTextAppearance(aoh); |
| 660 | 610 | } |
| 661 | 611 | } |
| ... | ... | @@ -973,8 +923,8 @@ FormField::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) |
| 973 | 923 | return; |
| 974 | 924 | } |
| 975 | 925 | QPDFObjectHandle::Rectangle bbox = bbox_obj.getArrayAsRectangle(); |
| 976 | - std::string DA = getDefaultAppearance(); | |
| 977 | - std::string V = getValueAsString(); | |
| 926 | + std::string DA = default_appearance(); | |
| 927 | + std::string V = value(); | |
| 978 | 928 | std::vector<std::string> opt; |
| 979 | 929 | if (isChoice() && (getFlags() & ff_ch_combo) == 0) { |
| 980 | 930 | opt = getChoices(); | ... | ... |
libqpdf/qpdf/FormField.hh
| ... | ... | @@ -32,57 +32,264 @@ namespace qpdf::impl |
| 32 | 32 | { |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | - // Return the field's parent. A form field object helper whose underlying object is null is | |
| 36 | - // returned if there is no parent. This condition may be tested by calling isNull(). | |
| 37 | - FormField getParent(); | |
| 38 | - | |
| 39 | - // Return the top-level field for this field. Typically this will be the field itself or its | |
| 40 | - // parent. If is_different is provided, it is set to true if the top-level field is | |
| 41 | - // different from the field itself; otherwise it is set to false. | |
| 42 | - FormField getTopLevelField(bool* is_different = nullptr); | |
| 43 | - | |
| 44 | - // Get a field value, possibly inheriting the value from an ancestor node. | |
| 45 | - QPDFObjectHandle getInheritableFieldValue(std::string const& name); | |
| 46 | - | |
| 47 | - // Get an inherited field value as a string. If it is not a string, silently return the | |
| 48 | - // empty string. | |
| 49 | - std::string getInheritableFieldValueAsString(std::string const& name); | |
| 50 | - | |
| 51 | - // Get an inherited field value of type name as a string representing the name. If it is not | |
| 52 | - // a name, silently return the empty string. | |
| 53 | - std::string getInheritableFieldValueAsName(std::string const& name); | |
| 54 | - | |
| 55 | - // Returns the value of /FT if present, otherwise returns the empty string. | |
| 56 | - std::string getFieldType(); | |
| 35 | + /// Retrieves the /Parent form field of the current field. | |
| 36 | + /// | |
| 37 | + /// This function accesses the parent field in the hierarchical structure of form fields, if | |
| 38 | + /// it exists. The parent is determined based on the /Parent attribute in the field | |
| 39 | + /// dictionary. | |
| 40 | + /// | |
| 41 | + /// @return A FormField object representing the parent field. If the current field has no | |
| 42 | + /// parent, an empty FormField object is returned. | |
| 43 | + FormField | |
| 44 | + Parent() | |
| 45 | + { | |
| 46 | + return {get("/Parent")}; | |
| 47 | + } | |
| 57 | 48 | |
| 58 | - std::string getFullyQualifiedName(); | |
| 49 | + /// @brief Returns the top-level field associated with the current field. | |
| 50 | + /// | |
| 51 | + /// The function traverses the hierarchy of parent fields to identify the highest-level | |
| 52 | + /// field in the tree. Typically, this will be the current field itself unless it has a | |
| 53 | + /// parent field. Optionally, it can indicate whether the top-level field is different from | |
| 54 | + /// the current field. | |
| 55 | + /// | |
| 56 | + /// @param is_different A pointer to a boolean that, if provided, will be set to true if the | |
| 57 | + /// top-level field differs from the current field; otherwise, it will be set to | |
| 58 | + /// false. | |
| 59 | + /// | |
| 60 | + /// @return The top-level field in the form field hierarchy. | |
| 61 | + FormField root_field(bool* is_different = nullptr); | |
| 62 | + | |
| 63 | + /// @brief Retrieves the inherited value of the specified attribute. | |
| 64 | + /// | |
| 65 | + /// @param name The name of the attribute to retrieve. | |
| 66 | + /// @param acroform If true, checks the document's /AcroForm dictionary for the attribute | |
| 67 | + /// if it is not found in the field hierarchy. | |
| 68 | + /// | |
| 69 | + /// @return A constant reference to the QPDFObjectHandle representing the value of the | |
| 70 | + /// specified attribute, if found. If the attribute is not found in the field | |
| 71 | + /// hierarchy or the /AcroForm dictionary (when `acroform` is true), returns a | |
| 72 | + /// reference to a static null object handle. | |
| 73 | + QPDFObjectHandle const& inherited(std::string const& name, bool acroform = false) const; | |
| 74 | + | |
| 75 | + /// @brief Retrieves the value of a specified field, accounting for inheritance through the | |
| 76 | + /// hierarchy of ancestor nodes in the form field tree. | |
| 77 | + /// | |
| 78 | + /// This function attempts to retrieve the value of the specified field. If the `inherit` | |
| 79 | + /// parameter is set to `true` and the field value is not found at the current level, the | |
| 80 | + /// method traverses up the parent hierarchy to find the value. The traversal stops when a | |
| 81 | + /// value is found, when the root node is reached, or when a loop detection mechanism | |
| 82 | + /// prevents further traversal. | |
| 83 | + /// | |
| 84 | + /// @tparam T The return type of the field value. | |
| 85 | + /// @param name The name of the field to retrieve the value for. | |
| 86 | + /// @param inherit If set to `true`, the function will attempt to retrieve the value by | |
| 87 | + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. | |
| 88 | + /// @return Returns the field's value if found; otherwise, returns a default-constructed | |
| 89 | + /// value of type `T`. | |
| 90 | + template <class T> | |
| 91 | + T | |
| 92 | + inheritable_value(std::string const& name, bool inherit = true, bool acroform = false) const | |
| 93 | + { | |
| 94 | + if (auto& v = get(name)) { | |
| 95 | + return {v}; | |
| 96 | + } | |
| 97 | + return {inherit ? inherited(name, acroform) : null_oh}; | |
| 98 | + } | |
| 59 | 99 | |
| 60 | - std::string getPartialName(); | |
| 100 | + /// @brief Retrieves an inherited field string attribute as a string. | |
| 101 | + /// | |
| 102 | + /// @param name The name of the field for which the value is to be retrieved. | |
| 103 | + /// @return The inherited field value as a UTF-8 encoded string, or an empty string if the | |
| 104 | + /// value does not exist or is not of String type. | |
| 105 | + std::string inheritable_string(std::string const& name) const; | |
| 106 | + | |
| 107 | + /// @brief Retrieves the field type (/FT attribute). | |
| 108 | + /// | |
| 109 | + /// @param inherit If set to `true`, the function will attempt to retrieve the value by | |
| 110 | + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. | |
| 111 | + /// @return Returns the field type if found; otherwise, returns a default-constructed | |
| 112 | + /// `Name`. | |
| 113 | + Name | |
| 114 | + FT(bool inherit = true) const | |
| 115 | + { | |
| 116 | + return inheritable_value<Name>("/FT"); | |
| 117 | + } | |
| 61 | 118 | |
| 62 | - // Return the alternative field name (/TU), which is the field name intended to be presented | |
| 63 | - // to users. If not present, fall back to the fully qualified name. | |
| 64 | - std::string getAlternativeName(); | |
| 119 | + /// @brief Retrieves the partial field name (/T attribute). | |
| 120 | + /// | |
| 121 | + /// @return Returns the partial field name if found; otherwise, returns a | |
| 122 | + /// default-constructed `String`. | |
| 123 | + String | |
| 124 | + T() const | |
| 125 | + { | |
| 126 | + return {get("/T")}; | |
| 127 | + } | |
| 65 | 128 | |
| 66 | - // Return the mapping field name (/TM). If not present, fall back to the alternative name, | |
| 67 | - // then to the partial name. | |
| 68 | - std::string getMappingName(); | |
| 129 | + /// @brief Retrieves the alternative name (/TU attribute). | |
| 130 | + /// | |
| 131 | + /// @return Returns the alternative name if found; otherwise, returns a default-constructed | |
| 132 | + /// `String`. | |
| 133 | + String | |
| 134 | + TU() const | |
| 135 | + { | |
| 136 | + return {get("/TU")}; | |
| 137 | + } | |
| 69 | 138 | |
| 70 | - QPDFObjectHandle getValue(); | |
| 139 | + /// @brief Retrieves the mapping name (/TM attribute). | |
| 140 | + /// | |
| 141 | + /// @return Returns the mapping name if found; otherwise, returns a default-constructed | |
| 142 | + /// `String`. | |
| 143 | + String | |
| 144 | + TM() const | |
| 145 | + { | |
| 146 | + return {get("/TM")}; | |
| 147 | + } | |
| 71 | 148 | |
| 72 | - // Return the field's value as a string. If this is called with a field whose value is not a | |
| 73 | - std::string getValueAsString(); | |
| 149 | + /// @brief Retrieves the fully qualified name of the form field. | |
| 150 | + /// | |
| 151 | + /// This method constructs the fully qualified name of the form field by traversing through | |
| 152 | + /// its parent hierarchy. The fully qualified name is constructed by concatenating the /T | |
| 153 | + /// (field name) attribute of each parent node with periods as separators, starting from the | |
| 154 | + /// root of the hierarchy. | |
| 155 | + /// | |
| 156 | + /// If the field has no parent hierarchy, the result will simply be the /T attribute of the | |
| 157 | + /// current field. In cases of potential circular references, loop detection is applied. | |
| 158 | + /// | |
| 159 | + /// @return A string representing the fully qualified name of the field. | |
| 160 | + std::string fully_qualified_name() const; | |
| 161 | + | |
| 162 | + /// @brief Retrieves the partial name (/T attribute) of the form field. | |
| 163 | + /// | |
| 164 | + /// This method returns the value of the field's /T attribute, which is the partial name | |
| 165 | + /// used to identify the field within its parent hierarchy. If the attribute is not set, an | |
| 166 | + /// empty string is returned. | |
| 167 | + /// | |
| 168 | + /// @return A string representing the partial name of the field in UTF-8 encoding, or an | |
| 169 | + /// empty string if the /T attribute is not present. | |
| 170 | + std::string partial_name() const; | |
| 171 | + | |
| 172 | + /// @brief Retrieves the alternative name for the form field. | |
| 173 | + /// | |
| 174 | + /// This method attempts to return the alternative name (/TU) of the form field, which is | |
| 175 | + /// the field name intended to be presented, to users as a UTF-8 string, if it exists. If | |
| 176 | + /// the alternative name is not present, the method falls back to the fully qualified name | |
| 177 | + /// of the form field. | |
| 178 | + /// | |
| 179 | + /// @return The alternative name of the form field as a string, or the | |
| 180 | + /// fully qualified name if the alternative name is unavailable. | |
| 181 | + std::string alternative_name() const; | |
| 182 | + | |
| 183 | + /// @brief Retrieves the mapping field name (/TM) for the form field. | |
| 184 | + /// | |
| 185 | + /// If the mapping name (/TM) is present, it is returned as a UTF-8 string. If not, it falls | |
| 186 | + /// back to the 'alternative name', which is obtained using the `alternative_name()` method. | |
| 187 | + /// | |
| 188 | + /// @return The mapping field name (/TM) as a UTF-8 string or the alternative name if the | |
| 189 | + /// mapping name is absent. | |
| 190 | + std::string mapping_name() const; | |
| 191 | + | |
| 192 | + /// @brief Retrieves the field value (`/V` attribute) of a specified field, accounting for | |
| 193 | + /// inheritance through thehierarchy of ancestor nodes in the form field tree. | |
| 194 | + /// | |
| 195 | + /// This function attempts to retrieve the `/V` attribute. If the `inherit` | |
| 196 | + /// parameter is set to `true` and the `/V` is not found at the current level, the | |
| 197 | + /// method traverses up the parent hierarchy to find the value. The traversal stops when | |
| 198 | + /// `/V` is found, when the root node is reached, or when a loop detection mechanism | |
| 199 | + /// prevents further traversal. | |
| 200 | + /// | |
| 201 | + /// @tparam T The return type. | |
| 202 | + /// @param inherit If set to `true`, the function will attempt to retrieve `/V` by | |
| 203 | + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. | |
| 204 | + /// @return Returns the field's value if found; otherwise, returns a default-constructed | |
| 205 | + /// value of type `T`. | |
| 206 | + template <class T> | |
| 207 | + T | |
| 208 | + V(bool inherit = true) const | |
| 209 | + { | |
| 210 | + return inheritable_value<T>("/V", inherit); | |
| 211 | + } | |
| 74 | 212 | |
| 75 | - QPDFObjectHandle getDefaultValue(); | |
| 213 | + /// @brief Retrieves the field value (`/V` attribute) of a specified field, accounting for | |
| 214 | + /// inheritance through the hierarchy of ancestor nodes in the form field tree. | |
| 215 | + /// | |
| 216 | + /// This function attempts to retrieve the `/V` attribute. If the `inherit` | |
| 217 | + /// parameter is set to `true` and the `/V` is not found at the current level, the | |
| 218 | + /// method traverses up the parent hierarchy to find the value. The traversal stops when | |
| 219 | + /// `/V` is found, when the root node is reached, or when a loop detection mechanism | |
| 220 | + /// prevents further traversal. | |
| 221 | + /// | |
| 222 | + /// @param inherit If set to `true`, the function will attempt to retrieve `/V` by | |
| 223 | + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. | |
| 224 | + /// @return Returns the field's value if found; otherwise, returns a default-constructed | |
| 225 | + /// object handle. | |
| 226 | + QPDFObjectHandle const& | |
| 227 | + V(bool inherit = true) const | |
| 228 | + { | |
| 229 | + if (auto& v = get("/V")) { | |
| 230 | + return v; | |
| 231 | + } | |
| 232 | + return {inherit ? inherited("/V") : null_oh}; | |
| 233 | + } | |
| 76 | 234 | |
| 77 | - // Return the field's default value as a string. If this is called with a field whose value | |
| 78 | - // is not a string, the empty string will be silently returned. | |
| 79 | - std::string getDefaultValueAsString(); | |
| 235 | + /// @brief Retrieves the field value `/V` attribute of the form field, considering | |
| 236 | + /// inheritance, if the value is a String. | |
| 237 | + /// | |
| 238 | + /// This function extracts the value of the form field, accounting for potential inheritance | |
| 239 | + /// through the form hierarchy. It returns the value if it is a String, and an empty string | |
| 240 | + /// otherwise. | |
| 241 | + /// | |
| 242 | + /// @return A string containing the actual or inherited `/V` attribute of the form field, or | |
| 243 | + /// an empty string if the value is not present or not a String. | |
| 244 | + std::string value() const; | |
| 245 | + | |
| 246 | + /// @brief Retrieves the field default value (`/DV` attribute) of a specified field, | |
| 247 | + /// accounting for inheritance through the hierarchy of ancestor nodes in the form | |
| 248 | + /// field tree. | |
| 249 | + /// | |
| 250 | + /// This function attempts to retrieve the `/DV` attribute. If the `inherit` parameter is | |
| 251 | + /// set to `true` and the `/DV` is not found at the current level, the method traverses up | |
| 252 | + /// the parent hierarchy to find the value. The traversal stops when | |
| 253 | + /// `/DV` is found, when the root node is reached, or when a loop detection mechanism | |
| 254 | + /// prevents further traversal. | |
| 255 | + /// | |
| 256 | + /// @tparam T The return type. | |
| 257 | + /// @param inherit If set to `true`, the function will attempt to retrieve `/DV` by | |
| 258 | + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. | |
| 259 | + /// @return Returns the field's default value if found; otherwise, returns a | |
| 260 | + /// default-constructed value of type `T`. | |
| 261 | + QPDFObjectHandle const& | |
| 262 | + DV(bool inherit = true) const | |
| 263 | + { | |
| 264 | + if (auto& v = get("/DV")) { | |
| 265 | + return v; | |
| 266 | + } | |
| 267 | + return {inherit ? inherited("/DV") : null_oh}; | |
| 268 | + } | |
| 80 | 269 | |
| 81 | - // Return the default appearance string, taking inheritance from the field tree into | |
| 82 | - // account. Returns the empty string if the default appearance string is not available | |
| 83 | - // (because it's erroneously absent or because this is not a variable text field). If not | |
| 84 | - // found in the field hierarchy, look in /AcroForm. | |
| 85 | - std::string getDefaultAppearance(); | |
| 270 | + /// @brief Retrieves the default value `/DV` attribute of the form field, considering | |
| 271 | + /// inheritance, if the default value is a String. | |
| 272 | + /// | |
| 273 | + /// This function extracts the default value of the form field, accounting for potential | |
| 274 | + /// inheritance through the form hierarchy. It returns the value if it is a String, and an | |
| 275 | + /// empty string otherwise. | |
| 276 | + /// | |
| 277 | + /// @return A string containing the actual or inherited `/V` attribute of the form field, or | |
| 278 | + /// an empty string if the value is not present or not a String. | |
| 279 | + std::string default_value() const; | |
| 280 | + | |
| 281 | + /// @brief Returns the default appearance string for the form field, considering inheritance | |
| 282 | + /// from the field tree hierarchy and the document's /AcroForm dictionary. | |
| 283 | + /// | |
| 284 | + /// This method retrieves the field's /DA (default appearance) attribute. If the attribute | |
| 285 | + /// is not directly available, it checks the parent fields in the hierarchy for an inherited | |
| 286 | + /// value. If no value is found in the field hierarchy, it attempts to retrieve the /DA | |
| 287 | + /// attribute from the document's /AcroForm dictionary. The method returns an empty string | |
| 288 | + /// if no default appearance string is available or applicable. | |
| 289 | + /// | |
| 290 | + /// @return A string representing the default appearance, or an empty string if | |
| 291 | + /// no value is found. | |
| 292 | + std::string default_appearance() const; | |
| 86 | 293 | |
| 87 | 294 | // Return the default resource dictionary for the field. This comes not from the field but |
| 88 | 295 | // from the document-level /AcroForm dictionary. While several PDF generates put a /DR key |
| ... | ... | @@ -154,12 +361,29 @@ namespace qpdf::impl |
| 154 | 361 | void generateAppearance(QPDFAnnotationObjectHelper&); |
| 155 | 362 | |
| 156 | 363 | private: |
| 157 | - QPDFObjectHandle getFieldFromAcroForm(std::string const& name); | |
| 364 | + /// @brief Retrieves an entry from the document's /AcroForm dictionary using the specified | |
| 365 | + /// name. | |
| 366 | + /// | |
| 367 | + /// The method accesses the AcroForm dictionary within the root object of the PDF document. | |
| 368 | + /// If the the AcroForm dictionary contains the given field name, it retrieves the | |
| 369 | + /// corresponding entry. Otherwise, it returns a default-constructed object handle. | |
| 370 | + /// | |
| 371 | + /// @param name The name of the form field to retrieve. | |
| 372 | + /// @return A object handle corresponding to the specified name within the AcroForm | |
| 373 | + /// dictionary. | |
| 374 | + QPDFObjectHandle const& | |
| 375 | + from_AcroForm(std::string const& name) const | |
| 376 | + { | |
| 377 | + return {qpdf() ? qpdf()->getRoot()["/AcroForm"][name] : null_oh}; | |
| 378 | + } | |
| 379 | + | |
| 158 | 380 | void setRadioButtonValue(QPDFObjectHandle name); |
| 159 | 381 | void setCheckBoxValue(bool value); |
| 160 | 382 | void generateTextAppearance(QPDFAnnotationObjectHelper&); |
| 161 | 383 | QPDFObjectHandle |
| 162 | 384 | getFontFromResource(QPDFObjectHandle resources, std::string const& font_name); |
| 385 | + | |
| 386 | + static const QPDFObjectHandle null_oh; | |
| 163 | 387 | }; |
| 164 | 388 | } // namespace qpdf::impl |
| 165 | 389 | ... | ... |
libtests/objects.cc
| ... | ... | @@ -90,7 +90,6 @@ test_0(QPDF& pdf, char const* arg2) |
| 90 | 90 | assert_compare_numbers(UINT_MAX, t.getKey("/Q3").getUIntValueAsUInt()); |
| 91 | 91 | } |
| 92 | 92 | |
| 93 | - | |
| 94 | 93 | static void |
| 95 | 94 | test_1(QPDF& pdf, char const* arg2) |
| 96 | 95 | { |
| ... | ... | @@ -121,7 +120,7 @@ test_1(QPDF& pdf, char const* arg2) |
| 121 | 120 | |
| 122 | 121 | bool thrown = false; |
| 123 | 122 | try { |
| 124 | - i.at("/A"); | |
| 123 | + i.at("/A"); | |
| 125 | 124 | } catch (std::runtime_error const&) { |
| 126 | 125 | thrown = true; |
| 127 | 126 | } |
| ... | ... | @@ -161,7 +160,9 @@ runtest(int n, char const* filename1, char const* arg2) |
| 161 | 160 | // the test suite to see how the test is invoked to find the file |
| 162 | 161 | // that the test is supposed to operate on. |
| 163 | 162 | |
| 164 | - std::set<int> ignore_filename = {1,}; | |
| 163 | + std::set<int> ignore_filename = { | |
| 164 | + 1, | |
| 165 | + }; | |
| 165 | 166 | |
| 166 | 167 | QPDF pdf; |
| 167 | 168 | std::shared_ptr<char> file_buf; |
| ... | ... | @@ -175,7 +176,8 @@ runtest(int n, char const* filename1, char const* arg2) |
| 175 | 176 | } |
| 176 | 177 | |
| 177 | 178 | std::map<int, void (*)(QPDF&, char const*)> test_functions = { |
| 178 | - {0, test_0}, {1, test_1}, | |
| 179 | + {0, test_0}, | |
| 180 | + {1, test_1}, | |
| 179 | 181 | }; |
| 180 | 182 | |
| 181 | 183 | auto fn = test_functions.find(n); | ... | ... |
manual/release-notes.rst
| ... | ... | @@ -23,6 +23,12 @@ more detail. |
| 23 | 23 | not work on some older Linux distributions. If you need support |
| 24 | 24 | for an older distribution, please use version 12.2.0 or below. |
| 25 | 25 | |
| 26 | + - Bug fixes | |
| 27 | + | |
| 28 | + - Set `is_different` flag in `QPDFFormFieldObjectHelper::getTopLevelField` to | |
| 29 | + false if the field is a top-level field. Previously the flag was only set | |
| 30 | + if the field is a top-level field. | |
| 31 | + | |
| 26 | 32 | - Library Enhancements |
| 27 | 33 | |
| 28 | 34 | - Add ``QPDFNameTreeObjectHelper`` and ``QPDFNumberTreeObjectHelper`` | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -174,12 +174,7 @@ QPDFObjectHandle dictionary empty map for asMap 0 |
| 174 | 174 | QPDFObjectHandle numeric non-numeric 0 |
| 175 | 175 | QPDFObjectHandle erase array bounds 0 |
| 176 | 176 | qpdf-c called qpdf_check_pdf 0 |
| 177 | -QPDFFormFieldObjectHelper TU present 0 | |
| 178 | -QPDFFormFieldObjectHelper TM present 0 | |
| 179 | -QPDFFormFieldObjectHelper TU absent 0 | |
| 180 | -QPDFFormFieldObjectHelper TM absent 0 | |
| 181 | 177 | QPDFFormFieldObjectHelper Q present 1 |
| 182 | -QPDFFormFieldObjectHelper DA present 1 | |
| 183 | 178 | QPDFAcroFormDocumentHelper field found 1 |
| 184 | 179 | QPDFAcroFormDocumentHelper annotation found 1 |
| 185 | 180 | QPDFJob automatically set keep files open 1 | ... | ... |