Commit 551a953452e54d337bab6a6fa9d5b689f18446a3
Committed by
GitHub
Merge pull request #1540 from m-holger/oh_name
Create new private-API class Name
Showing
10 changed files
with
163 additions
and
147 deletions
libqpdf/QPDFAnnotationObjectHelper.cc
| 1 | 1 | #include <qpdf/QPDFAnnotationObjectHelper.hh> |
| 2 | 2 | |
| 3 | -#include <qpdf/QPDF.hh> | |
| 4 | 3 | #include <qpdf/QPDFMatrix.hh> |
| 4 | +#include <qpdf/QPDFObjectHandle_private.hh> | |
| 5 | 5 | #include <qpdf/QTC.hh> |
| 6 | 6 | #include <qpdf/QUtil.hh> |
| 7 | 7 | |
| 8 | +using namespace qpdf; | |
| 9 | + | |
| 8 | 10 | QPDFAnnotationObjectHelper::QPDFAnnotationObjectHelper(QPDFObjectHandle oh) : |
| 9 | 11 | QPDFObjectHelper(oh) |
| 10 | 12 | { |
| ... | ... | @@ -31,19 +33,15 @@ QPDFAnnotationObjectHelper::getAppearanceDictionary() |
| 31 | 33 | std::string |
| 32 | 34 | QPDFAnnotationObjectHelper::getAppearanceState() |
| 33 | 35 | { |
| 34 | - if (oh().getKey("/AS").isName()) { | |
| 35 | - QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS present"); | |
| 36 | - return oh().getKey("/AS").getName(); | |
| 37 | - } | |
| 38 | - QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS absent"); | |
| 39 | - return ""; | |
| 36 | + Name AS = (*this)["/AS"]; | |
| 37 | + return AS ? AS.value() : ""; | |
| 40 | 38 | } |
| 41 | 39 | |
| 42 | 40 | int |
| 43 | 41 | QPDFAnnotationObjectHelper::getFlags() |
| 44 | 42 | { |
| 45 | - QPDFObjectHandle flags_obj = oh().getKey("/F"); | |
| 46 | - return flags_obj.isInteger() ? flags_obj.getIntValueAsInt() : 0; | |
| 43 | + Integer flags_obj = (*this)["/F"]; | |
| 44 | + return flags_obj ? flags_obj : 0; | |
| 47 | 45 | } |
| 48 | 46 | |
| 49 | 47 | QPDFObjectHandle | ... | ... |
libqpdf/QPDFFormFieldObjectHelper.cc
| ... | ... | @@ -10,6 +10,8 @@ |
| 10 | 10 | #include <qpdf/QUtil.hh> |
| 11 | 11 | #include <cstdlib> |
| 12 | 12 | |
| 13 | +using namespace qpdf; | |
| 14 | + | |
| 13 | 15 | QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper(QPDFObjectHandle oh) : |
| 14 | 16 | QPDFObjectHelper(oh), |
| 15 | 17 | m(new Members()) |
| ... | ... | @@ -98,9 +100,8 @@ QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(std::string const& n |
| 98 | 100 | std::string |
| 99 | 101 | QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(std::string const& name) |
| 100 | 102 | { |
| 101 | - auto fv = getInheritableFieldValue(name); | |
| 102 | - if (fv.isName()) { | |
| 103 | - return fv.getName(); | |
| 103 | + if (Name fv = getInheritableFieldValue(name)) { | |
| 104 | + return fv; | |
| 104 | 105 | } |
| 105 | 106 | return {}; |
| 106 | 107 | } |
| ... | ... | @@ -245,7 +246,7 @@ QPDFFormFieldObjectHelper::isCheckbox() |
| 245 | 246 | bool |
| 246 | 247 | QPDFFormFieldObjectHelper::isChecked() |
| 247 | 248 | { |
| 248 | - return isCheckbox() && getValue().isName() && getValue().getName() != "/Off"; | |
| 249 | + return isCheckbox() && Name(getValue()) != "/Off"; | |
| 249 | 250 | } |
| 250 | 251 | |
| 251 | 252 | bool |
| ... | ... | @@ -301,25 +302,25 @@ QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, std::string |
| 301 | 302 | void |
| 302 | 303 | QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) |
| 303 | 304 | { |
| 305 | + Name name = value; | |
| 304 | 306 | if (getFieldType() == "/Btn") { |
| 305 | 307 | if (isCheckbox()) { |
| 306 | - if (!value.isName()) { | |
| 308 | + if (!name) { | |
| 307 | 309 | warn("ignoring attempt to set a checkbox field to a value whose type is not name"); |
| 308 | 310 | return; |
| 309 | 311 | } |
| 310 | - std::string name = value.getName(); | |
| 311 | 312 | // Accept any value other than /Off to mean checked. Files have been seen that use |
| 312 | 313 | // /1 or other values. |
| 313 | 314 | setCheckBoxValue(name != "/Off"); |
| 314 | 315 | return; |
| 315 | 316 | } |
| 316 | 317 | if (isRadioButton()) { |
| 317 | - if (!value.isName()) { | |
| 318 | + if (!name) { | |
| 318 | 319 | warn( |
| 319 | 320 | "ignoring attempt to set a radio button field to an object that is not a name"); |
| 320 | 321 | return; |
| 321 | 322 | } |
| 322 | - setRadioButtonValue(value); | |
| 323 | + setRadioButtonValue(name); | |
| 323 | 324 | return; |
| 324 | 325 | } |
| 325 | 326 | if (isPushbutton()) { |
| ... | ... | @@ -743,17 +744,18 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper& ao |
| 743 | 744 | { |
| 744 | 745 | QPDFObjectHandle AS = aoh.getAppearanceStream("/N"); |
| 745 | 746 | if (AS.null()) { |
| 746 | - QTC::TC("qpdf", "QPDFFormFieldObjectHelper create AS from scratch"); | |
| 747 | 747 | QPDFObjectHandle::Rectangle rect = aoh.getRect(); |
| 748 | 748 | QPDFObjectHandle::Rectangle bbox(0, 0, rect.urx - rect.llx, rect.ury - rect.lly); |
| 749 | - QPDFObjectHandle dict = QPDFObjectHandle::parse( | |
| 750 | - "<< /Resources << /ProcSet [ /PDF /Text ] >> /Type /XObject /Subtype /Form >>"); | |
| 751 | - dict.replaceKey("/BBox", QPDFObjectHandle::newFromRectangle(bbox)); | |
| 749 | + auto dict = Dictionary( | |
| 750 | + {{"/BBox", QPDFObjectHandle::newFromRectangle(bbox)}, | |
| 751 | + {"/Resources", Dictionary({{"/ProcSet", Array({Name("/PDF"), Name("/Text")})}})}, | |
| 752 | + {"/Type", Name("/XObject")}, | |
| 753 | + {"/Subtype", Name("/Form")}}); | |
| 752 | 754 | AS = QPDFObjectHandle::newStream(oh().getOwningQPDF(), "/Tx BMC\nEMC\n"); |
| 753 | 755 | AS.replaceDict(dict); |
| 754 | - QPDFObjectHandle AP = aoh.getAppearanceDictionary(); | |
| 755 | - if (AP.null()) { | |
| 756 | - aoh.getObjectHandle().replaceKey("/AP", QPDFObjectHandle::newDictionary()); | |
| 756 | + Dictionary AP = aoh.getAppearanceDictionary(); | |
| 757 | + if (!AP) { | |
| 758 | + aoh.getObjectHandle().replaceKey("/AP", Dictionary::empty()); | |
| 757 | 759 | AP = aoh.getAppearanceDictionary(); |
| 758 | 760 | } |
| 759 | 761 | AP.replaceKey("/N", AS); |
| ... | ... | @@ -776,7 +778,7 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper& ao |
| 776 | 778 | std::string DA = getDefaultAppearance(); |
| 777 | 779 | std::string V = getValueAsString(); |
| 778 | 780 | std::vector<std::string> opt; |
| 779 | - if (isChoice() && ((getFlags() & ff_ch_combo) == 0)) { | |
| 781 | + if (isChoice() && (getFlags() & ff_ch_combo) == 0) { | |
| 780 | 782 | opt = getChoices(); |
| 781 | 783 | } |
| 782 | 784 | |
| ... | ... | @@ -791,29 +793,26 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper& ao |
| 791 | 793 | std::string font_name = tff.getFontName(); |
| 792 | 794 | if (!font_name.empty()) { |
| 793 | 795 | // See if the font is encoded with something we know about. |
| 794 | - QPDFObjectHandle resources = AS.getDict().getKey("/Resources"); | |
| 795 | - QPDFObjectHandle font = getFontFromResource(resources, font_name); | |
| 796 | - bool found_font_in_dr = false; | |
| 796 | + Dictionary resources = AS.getDict()["/Resources"]; | |
| 797 | + Dictionary font = getFontFromResource(resources, font_name); | |
| 797 | 798 | if (!font) { |
| 798 | - QPDFObjectHandle dr = getDefaultResources(); | |
| 799 | - font = getFontFromResource(dr, font_name); | |
| 800 | - found_font_in_dr = font.isDictionary(); | |
| 801 | - } | |
| 802 | - if (found_font_in_dr && resources.isDictionary()) { | |
| 803 | - if (resources.isIndirect()) { | |
| 804 | - resources = resources.getQPDF().makeIndirectObject(resources.shallowCopy()); | |
| 805 | - AS.getDict().replaceKey("/Resources", resources); | |
| 799 | + font = getFontFromResource(getDefaultResources(), font_name); | |
| 800 | + if (resources) { | |
| 801 | + if (resources.indirect()) { | |
| 802 | + resources = resources.qpdf()->makeIndirectObject(resources.copy()); | |
| 803 | + AS.getDict().replaceKey("/Resources", resources); | |
| 804 | + } | |
| 805 | + // Use mergeResources to force /Font to be local | |
| 806 | + QPDFObjectHandle res = resources; | |
| 807 | + res.mergeResources(Dictionary({{"/Font", Dictionary::empty()}})); | |
| 808 | + res.getKey("/Font").replaceKey(font_name, font); | |
| 806 | 809 | } |
| 807 | - // Use mergeResources to force /Font to be local | |
| 808 | - resources.mergeResources("<< /Font << >> >>"_qpdf); | |
| 809 | - resources.getKey("/Font").replaceKey(font_name, font); | |
| 810 | 810 | } |
| 811 | 811 | |
| 812 | - if (font.isDictionary() && font.getKey("/Encoding").isName()) { | |
| 813 | - std::string encoding = font.getKey("/Encoding").getName(); | |
| 814 | - if (encoding == "/WinAnsiEncoding") { | |
| 812 | + if (Name Encoding = font["/Encoding"]) { | |
| 813 | + if (Encoding == "/WinAnsiEncoding") { | |
| 815 | 814 | encoder = &QUtil::utf8_to_win_ansi; |
| 816 | - } else if (encoding == "/MacRomanEncoding") { | |
| 815 | + } else if (Encoding == "/MacRomanEncoding") { | |
| 817 | 816 | encoder = &QUtil::utf8_to_mac_roman; |
| 818 | 817 | } |
| 819 | 818 | } | ... | ... |
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -766,14 +766,14 @@ QPDFObjectHandle::isScalar() const |
| 766 | 766 | bool |
| 767 | 767 | QPDFObjectHandle::isNameAndEquals(std::string const& name) const |
| 768 | 768 | { |
| 769 | - return isName() && (getName() == name); | |
| 769 | + return Name(*this) == name; | |
| 770 | 770 | } |
| 771 | 771 | |
| 772 | 772 | bool |
| 773 | 773 | QPDFObjectHandle::isDictionaryOfType(std::string const& type, std::string const& subtype) const |
| 774 | 774 | { |
| 775 | - return isDictionary() && (type.empty() || getKey("/Type").isNameAndEquals(type)) && | |
| 776 | - (subtype.empty() || getKey("/Subtype").isNameAndEquals(subtype)); | |
| 775 | + return isDictionary() && (type.empty() || Name((*this)["/Type"]) == type) && | |
| 776 | + (subtype.empty() || Name((*this)["/Subtype"]) == subtype); | |
| 777 | 777 | } |
| 778 | 778 | |
| 779 | 779 | bool |
| ... | ... | @@ -952,7 +952,33 @@ QPDFObjectHandle::getValueAsReal(std::string& value) const |
| 952 | 952 | return true; |
| 953 | 953 | } |
| 954 | 954 | |
| 955 | -// Name accessors | |
| 955 | +// Name methods | |
| 956 | + | |
| 957 | +QPDFObjectHandle | |
| 958 | +QPDFObjectHandle::newName(std::string const& name) | |
| 959 | +{ | |
| 960 | + return {QPDFObject::create<QPDF_Name>(name)}; | |
| 961 | +} | |
| 962 | + | |
| 963 | +Name::Name(std::string const& name) : | |
| 964 | + BaseHandle(QPDFObject::create<QPDF_Name>(name)) | |
| 965 | +{ | |
| 966 | +} | |
| 967 | + | |
| 968 | +Name::Name(std::string&& name) : | |
| 969 | + BaseHandle(QPDFObject::create<QPDF_Name>(std::move(name))) | |
| 970 | +{ | |
| 971 | +} | |
| 972 | + | |
| 973 | +std::string const& | |
| 974 | +Name::value() const | |
| 975 | +{ | |
| 976 | + auto* n = as<QPDF_Name>(); | |
| 977 | + if (!n) { | |
| 978 | + throw invalid_error("Name"); | |
| 979 | + } | |
| 980 | + return n->name; | |
| 981 | +} | |
| 956 | 982 | |
| 957 | 983 | std::string |
| 958 | 984 | QPDFObjectHandle::getName() const |
| ... | ... | @@ -961,7 +987,6 @@ QPDFObjectHandle::getName() const |
| 961 | 987 | return obj->getStringValue(); |
| 962 | 988 | } else { |
| 963 | 989 | typeWarning("name", "returning dummy name"); |
| 964 | - QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name"); | |
| 965 | 990 | return "/QPDFFakeName"; |
| 966 | 991 | } |
| 967 | 992 | } |
| ... | ... | @@ -1695,12 +1720,6 @@ QPDFObjectHandle::newReal(double value, int decimal_places, bool trim_trailing_z |
| 1695 | 1720 | } |
| 1696 | 1721 | |
| 1697 | 1722 | QPDFObjectHandle |
| 1698 | -QPDFObjectHandle::newName(std::string const& name) | |
| 1699 | -{ | |
| 1700 | - return {QPDFObject::create<QPDF_Name>(name)}; | |
| 1701 | -} | |
| 1702 | - | |
| 1703 | -QPDFObjectHandle | |
| 1704 | 1723 | QPDFObjectHandle::newString(std::string const& str) |
| 1705 | 1724 | { |
| 1706 | 1725 | return {QPDFObject::create<QPDF_String>(str)}; | ... | ... |
libqpdf/QPDFOutlineObjectHelper.cc
| ... | ... | @@ -48,26 +48,19 @@ QPDFOutlineObjectHelper::getKids() |
| 48 | 48 | QPDFObjectHandle |
| 49 | 49 | QPDFOutlineObjectHelper::getDest() |
| 50 | 50 | { |
| 51 | - QPDFObjectHandle dest; | |
| 52 | - QPDFObjectHandle A; | |
| 53 | - if (oh().hasKey("/Dest")) { | |
| 54 | - QTC::TC("qpdf", "QPDFOutlineObjectHelper direct dest"); | |
| 55 | - dest = oh().getKey("/Dest"); | |
| 56 | - } else if ( | |
| 57 | - (A = oh().getKey("/A")).isDictionary() && A.getKey("/S").isName() && | |
| 58 | - (A.getKey("/S").getName() == "/GoTo") && A.hasKey("/D")) { | |
| 59 | - QTC::TC("qpdf", "QPDFOutlineObjectHelper action dest"); | |
| 60 | - dest = A.getKey("/D"); | |
| 51 | + auto dest = (*this)["/Dest"]; | |
| 52 | + if (dest.null()) { | |
| 53 | + auto const& A = (*this)["/A"]; | |
| 54 | + if (Name(A["/S"]) == "/GoTo") { | |
| 55 | + dest = A["/D"]; | |
| 56 | + } | |
| 61 | 57 | } |
| 62 | - if (!dest) { | |
| 58 | + if (dest.null()) { | |
| 63 | 59 | return QPDFObjectHandle::newNull(); |
| 64 | 60 | } |
| 65 | - | |
| 66 | 61 | if (dest.isName() || dest.isString()) { |
| 67 | - QTC::TC("qpdf", "QPDFOutlineObjectHelper named dest"); | |
| 68 | - dest = m->dh.resolveNamedDest(dest); | |
| 62 | + return m->dh.resolveNamedDest(dest); | |
| 69 | 63 | } |
| 70 | - | |
| 71 | 64 | return dest; |
| 72 | 65 | } |
| 73 | 66 | ... | ... |
libqpdf/QPDF_Stream.cc
| ... | ... | @@ -38,11 +38,10 @@ namespace |
| 38 | 38 | setDecodeParms(QPDFObjectHandle decode_parms) final |
| 39 | 39 | { |
| 40 | 40 | // we only validate here - processing happens in decryptStream |
| 41 | - if (auto dict = decode_parms.as_dictionary(optional)) { | |
| 41 | + if (Dictionary dict = decode_parms) { | |
| 42 | 42 | for (auto const& [key, value]: dict) { |
| 43 | 43 | if (key == "/Type" && |
| 44 | - (value.null() || | |
| 45 | - (value.isName() && value.getName() == "/CryptFilterDecodeParms"))) { | |
| 44 | + (value.null() || Name(value) == "/CryptFilterDecodeParms")) { | |
| 46 | 45 | continue; |
| 47 | 46 | } |
| 48 | 47 | if (key == "/Name") { |
| ... | ... | @@ -54,7 +53,7 @@ namespace |
| 54 | 53 | } |
| 55 | 54 | return true; |
| 56 | 55 | } |
| 57 | - return false; | |
| 56 | + return decode_parms.null(); | |
| 58 | 57 | } |
| 59 | 58 | |
| 60 | 59 | Pipeline* |
| ... | ... | @@ -374,7 +373,7 @@ Stream::filterable( |
| 374 | 373 | auto s = stream(); |
| 375 | 374 | // Check filters |
| 376 | 375 | |
| 377 | - auto filter_obj = s->stream_dict.getKey("/Filter"); | |
| 376 | + auto const& filter_obj = s->stream_dict["/Filter"]; | |
| 378 | 377 | |
| 379 | 378 | if (filter_obj.null()) { |
| 380 | 379 | // No filters |
| ... | ... | @@ -387,14 +386,14 @@ Stream::filterable( |
| 387 | 386 | return false; |
| 388 | 387 | } |
| 389 | 388 | filters.emplace_back(ff()); |
| 390 | - } else if (auto array = filter_obj.as_array(strict)) { | |
| 389 | + } else if (Array array = filter_obj) { | |
| 391 | 390 | // Potentially multiple filters |
| 392 | - for (auto const& item: array) { | |
| 393 | - if (!item.isName()) { | |
| 391 | + for (Name item: array) { | |
| 392 | + if (!item) { | |
| 394 | 393 | warn("stream filter type is not name or array"); |
| 395 | 394 | return false; |
| 396 | 395 | } |
| 397 | - auto ff = s->filter_factory(item.getName()); | |
| 396 | + auto ff = s->filter_factory(item); | |
| 398 | 397 | if (!ff) { |
| 399 | 398 | filters.clear(); |
| 400 | 399 | return false; | ... | ... |
libqpdf/QPDF_encryption.cc
| ... | ... | @@ -600,18 +600,17 @@ QPDF::EncryptionData::recover_encryption_key_with_password( |
| 600 | 600 | } |
| 601 | 601 | |
| 602 | 602 | QPDF::encryption_method_e |
| 603 | -QPDF::EncryptionParameters::interpretCF(QPDFObjectHandle const& cf) const | |
| 603 | +QPDF::EncryptionParameters::interpretCF(Name const& cf) const | |
| 604 | 604 | { |
| 605 | - if (!cf.isName()) { | |
| 605 | + if (!cf) { | |
| 606 | 606 | // Default: /Identity |
| 607 | 607 | return e_none; |
| 608 | 608 | } |
| 609 | - std::string filter = cf.getName(); | |
| 610 | - auto it = crypt_filters.find(filter); | |
| 609 | + auto it = crypt_filters.find(cf); | |
| 611 | 610 | if (it != crypt_filters.end()) { |
| 612 | 611 | return it->second; |
| 613 | 612 | } |
| 614 | - if (filter == "/Identity") { | |
| 613 | + if (cf == "/Identity") { | |
| 615 | 614 | return e_none; |
| 616 | 615 | } |
| 617 | 616 | return e_unknown; |
| ... | ... | @@ -677,8 +676,7 @@ QPDF::EncryptionParameters::initialize(QPDF& qpdf) |
| 677 | 676 | throw qpdf.damagedPDF("/Encrypt in trailer dictionary is not a dictionary"); |
| 678 | 677 | } |
| 679 | 678 | |
| 680 | - if (!(encryption_dict.getKey("/Filter").isName() && | |
| 681 | - (encryption_dict.getKey("/Filter").getName() == "/Standard"))) { | |
| 679 | + if (Name(encryption_dict["/Filter"]) != "/Standard") { | |
| 682 | 680 | throw unsupported("unsupported encryption filter"); |
| 683 | 681 | } |
| 684 | 682 | if (!encryption_dict.getKey("/SubFilter").null()) { |
| ... | ... | @@ -766,16 +764,12 @@ QPDF::EncryptionParameters::initialize(QPDF& qpdf) |
| 766 | 764 | for (auto const& [filter, cdict]: CF.as_dictionary()) { |
| 767 | 765 | if (cdict.isDictionary()) { |
| 768 | 766 | encryption_method_e method = e_none; |
| 769 | - if (cdict.getKey("/CFM").isName()) { | |
| 770 | - std::string method_name = cdict.getKey("/CFM").getName(); | |
| 771 | - if (method_name == "/V2") { | |
| 772 | - QTC::TC("qpdf", "QPDF_encryption CFM V2"); | |
| 767 | + if (Name const& CFM = cdict["/CFM"]) { | |
| 768 | + if (CFM == "/V2") { | |
| 773 | 769 | method = e_rc4; |
| 774 | - } else if (method_name == "/AESV2") { | |
| 775 | - QTC::TC("qpdf", "QPDF_encryption CFM AESV2"); | |
| 770 | + } else if (CFM == "/AESV2") { | |
| 776 | 771 | method = e_aes; |
| 777 | - } else if (method_name == "/AESV3") { | |
| 778 | - QTC::TC("qpdf", "QPDF_encryption CFM AESV3"); | |
| 772 | + } else if (CFM == "/AESV3") { | |
| 779 | 773 | method = e_aesv3; |
| 780 | 774 | } else { |
| 781 | 775 | // Don't complain now -- maybe we won't need to reference this type. |
| ... | ... | @@ -786,9 +780,9 @@ QPDF::EncryptionParameters::initialize(QPDF& qpdf) |
| 786 | 780 | } |
| 787 | 781 | } |
| 788 | 782 | |
| 789 | - cf_stream = interpretCF(encryption_dict.getKey("/StmF")); | |
| 790 | - cf_string = interpretCF(encryption_dict.getKey("/StrF")); | |
| 791 | - if (auto EFF = encryption_dict.getKey("/EFF"); EFF.isName()) { | |
| 783 | + cf_stream = interpretCF(encryption_dict["/StmF"]); | |
| 784 | + cf_string = interpretCF(encryption_dict["/StrF"]); | |
| 785 | + if (Name const& EFF = encryption_dict["/EFF"]) { | |
| 792 | 786 | // qpdf does not use this for anything other than informational purposes. This is |
| 793 | 787 | // intended to instruct conforming writers on which crypt filter should be used when new |
| 794 | 788 | // file attachments are added to a PDF file, but qpdf never generates encrypted files |
| ... | ... | @@ -937,12 +931,7 @@ QPDF::decryptStream( |
| 937 | 931 | bool is_root_metadata, |
| 938 | 932 | std::unique_ptr<Pipeline>& decrypt_pipeline) |
| 939 | 933 | { |
| 940 | - std::string type; | |
| 941 | - if (stream_dict.getKey("/Type").isName()) { | |
| 942 | - type = stream_dict.getKey("/Type").getName(); | |
| 943 | - } | |
| 944 | - if (type == "/XRef") { | |
| 945 | - QTC::TC("qpdf", "QPDF_encryption xref stream from encrypted file"); | |
| 934 | + if (Name(stream_dict["/Type"]) == "/XRef") { | |
| 946 | 935 | return; |
| 947 | 936 | } |
| 948 | 937 | bool use_aes = false; |
| ... | ... | @@ -951,26 +940,20 @@ QPDF::decryptStream( |
| 951 | 940 | std::string method_source = "/StmF from /Encrypt dictionary"; |
| 952 | 941 | |
| 953 | 942 | if (stream_dict.getKey("/Filter").isOrHasName("/Crypt")) { |
| 954 | - if (stream_dict.getKey("/DecodeParms").isDictionary()) { | |
| 955 | - QPDFObjectHandle decode_parms = stream_dict.getKey("/DecodeParms"); | |
| 956 | - if (decode_parms.isDictionaryOfType("/CryptFilterDecodeParms")) { | |
| 957 | - QTC::TC("qpdf", "QPDF_encryption stream crypt filter"); | |
| 958 | - method = encp->interpretCF(decode_parms.getKey("/Name")); | |
| 943 | + if (Dictionary decode_parms = stream_dict["/DecodeParms"]) { | |
| 944 | + if (Name(decode_parms["/Type"]) == "/CryptFilterDecodeParms") { | |
| 945 | + method = encp->interpretCF(decode_parms["/Name"]); | |
| 959 | 946 | method_source = "stream's Crypt decode parameters"; |
| 960 | 947 | } |
| 961 | - } else if ( | |
| 962 | - stream_dict.getKey("/DecodeParms").isArray() && | |
| 963 | - stream_dict.getKey("/Filter").isArray()) { | |
| 964 | - auto filter = stream_dict.getKey("/Filter"); | |
| 965 | - auto decode = stream_dict.getKey("/DecodeParms"); | |
| 948 | + } else { | |
| 949 | + Array filter = stream_dict["/Filter"]; | |
| 950 | + Array decode = stream_dict.getKey("/DecodeParms"); | |
| 966 | 951 | if (filter.size() == decode.size()) { |
| 967 | - int i = 0; | |
| 968 | - for (auto const& item: filter.as_array()) { | |
| 969 | - if (item.isNameAndEquals("/Crypt")) { | |
| 970 | - auto crypt_params = decode.getArrayItem(i); | |
| 971 | - if (crypt_params.isDictionary() && | |
| 972 | - crypt_params.getKey("/Name").isName()) { | |
| 973 | - method = encp->interpretCF(crypt_params.getKey("/Name")); | |
| 952 | + size_t i = 0; | |
| 953 | + for (Name item: filter) { | |
| 954 | + if (item == "/Crypt") { | |
| 955 | + if (Name name = decode[i]["/Name"]) { | |
| 956 | + method = encp->interpretCF(name); | |
| 974 | 957 | method_source = "stream's Crypt decode parameters (array)"; |
| 975 | 958 | } |
| 976 | 959 | break; |
| ... | ... | @@ -982,8 +965,7 @@ QPDF::decryptStream( |
| 982 | 965 | } |
| 983 | 966 | |
| 984 | 967 | if (method == e_unknown) { |
| 985 | - if ((!encp->encrypt_metadata) && is_root_metadata) { | |
| 986 | - QTC::TC("qpdf", "QPDF_encryption cleartext metadata"); | |
| 968 | + if (!encp->encrypt_metadata && is_root_metadata) { | |
| 987 | 969 | method = e_none; |
| 988 | 970 | } else { |
| 989 | 971 | method = encp->cf_stream; |
| ... | ... | @@ -1008,13 +990,13 @@ QPDF::decryptStream( |
| 1008 | 990 | |
| 1009 | 991 | default: |
| 1010 | 992 | // filter local to this stream. |
| 1011 | - qpdf_for_warning.warn(QPDFExc( | |
| 1012 | - qpdf_e_damaged_pdf, | |
| 1013 | - file->getName(), | |
| 1014 | - "", | |
| 1015 | - file->getLastOffset(), | |
| 1016 | - "unknown encryption filter for streams (check " + method_source + | |
| 1017 | - "); streams may be decrypted improperly")); | |
| 993 | + qpdf_for_warning.warn( | |
| 994 | + {qpdf_e_damaged_pdf, | |
| 995 | + file->getName(), | |
| 996 | + "", | |
| 997 | + file->getLastOffset(), | |
| 998 | + "unknown encryption filter for streams (check " + method_source + | |
| 999 | + "); streams may be decrypted improperly"}); | |
| 1018 | 1000 | // To avoid repeated warnings, reset cf_stream. Assume we'd want to use AES if V == 4. |
| 1019 | 1001 | encp->cf_stream = e_aes; |
| 1020 | 1002 | use_aes = true; |
| ... | ... | @@ -1023,11 +1005,9 @@ QPDF::decryptStream( |
| 1023 | 1005 | } |
| 1024 | 1006 | std::string key = getKeyForObject(encp, og, use_aes); |
| 1025 | 1007 | if (use_aes) { |
| 1026 | - QTC::TC("qpdf", "QPDF_encryption aes decode stream"); | |
| 1027 | 1008 | decrypt_pipeline = |
| 1028 | 1009 | std::make_unique<Pl_AES_PDF>("AES stream decryption", pipeline, false, key); |
| 1029 | 1010 | } else { |
| 1030 | - QTC::TC("qpdf", "QPDF_encryption rc4 decode stream"); | |
| 1031 | 1011 | decrypt_pipeline = std::make_unique<Pl_RC4>("RC4 stream decryption", pipeline, key); |
| 1032 | 1012 | } |
| 1033 | 1013 | pipeline = decrypt_pipeline.get(); | ... | ... |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| ... | ... | @@ -324,7 +324,7 @@ namespace qpdf |
| 324 | 324 | { |
| 325 | 325 | } |
| 326 | 326 | |
| 327 | - // Return the integer value. If the object is not a valid integer, throw a | |
| 327 | + // Return the integer value. If the object is not a valid Integer, throw a | |
| 328 | 328 | // std::invalid_argument exception. If the object is out of range for the target type, |
| 329 | 329 | // throw a std::overflow_error or std::underflow_error exception. |
| 330 | 330 | template <std::integral T> |
| ... | ... | @@ -391,6 +391,45 @@ namespace qpdf |
| 391 | 391 | // escaping. Return {false, false} if the name is not valid utf-8, otherwise return {true, |
| 392 | 392 | // true} if no characters require or {true, false} if escaping is required. |
| 393 | 393 | static std::pair<bool, bool> analyzeJSONEncoding(std::string const& name); |
| 394 | + | |
| 395 | + Name() = default; | |
| 396 | + Name(Name const&) = default; | |
| 397 | + Name(Name&&) = default; | |
| 398 | + Name& operator=(Name const&) = default; | |
| 399 | + Name& operator=(Name&&) = default; | |
| 400 | + ~Name() = default; | |
| 401 | + | |
| 402 | + explicit Name(std::string const&); | |
| 403 | + explicit Name(std::string&&); | |
| 404 | + | |
| 405 | + Name(QPDFObjectHandle const& oh) : | |
| 406 | + BaseHandle(oh.type_code() == ::ot_name ? oh : QPDFObjectHandle()) | |
| 407 | + { | |
| 408 | + } | |
| 409 | + | |
| 410 | + Name(QPDFObjectHandle&& oh) : | |
| 411 | + BaseHandle(oh.type_code() == ::ot_name ? std::move(oh) : QPDFObjectHandle()) | |
| 412 | + { | |
| 413 | + } | |
| 414 | + | |
| 415 | + // Return the name value. If the object is not a valid Name, throw a | |
| 416 | + // std::invalid_argument exception. | |
| 417 | + operator std::string() const& | |
| 418 | + { | |
| 419 | + return value(); | |
| 420 | + } | |
| 421 | + | |
| 422 | + // Return the integer value. If the object is not a valid integer, throw a | |
| 423 | + // std::invalid_argument exception. | |
| 424 | + std::string const& value() const; | |
| 425 | + | |
| 426 | + // Return true if object value is equal to the 'rhs' value. Return false if the object is | |
| 427 | + // not a valid Name. | |
| 428 | + friend bool | |
| 429 | + operator==(Name const& lhs, std::string_view rhs) | |
| 430 | + { | |
| 431 | + return lhs && lhs.value() == rhs; | |
| 432 | + } | |
| 394 | 433 | }; |
| 395 | 434 | |
| 396 | 435 | class Stream final: public BaseHandle | ... | ... |
libqpdf/qpdf/QPDFObject_private.hh
| ... | ... | @@ -28,6 +28,7 @@ namespace qpdf |
| 28 | 28 | class BaseDictionary; |
| 29 | 29 | class Dictionary; |
| 30 | 30 | class Integer; |
| 31 | + class Name; | |
| 31 | 32 | class Stream; |
| 32 | 33 | } // namespace qpdf |
| 33 | 34 | |
| ... | ... | @@ -138,6 +139,7 @@ class QPDF_Name final |
| 138 | 139 | { |
| 139 | 140 | friend class QPDFObject; |
| 140 | 141 | friend class qpdf::BaseHandle; |
| 142 | + friend class qpdf::Name; | |
| 141 | 143 | |
| 142 | 144 | explicit QPDF_Name(std::string name) : |
| 143 | 145 | name(std::move(name)) | ... | ... |
libqpdf/qpdf/QPDF_private.hh
| ... | ... | @@ -7,6 +7,8 @@ |
| 7 | 7 | #include <qpdf/QPDFObject_private.hh> |
| 8 | 8 | #include <qpdf/QPDFTokenizer_private.hh> |
| 9 | 9 | |
| 10 | +using namespace qpdf; | |
| 11 | + | |
| 10 | 12 | // Writer class is restricted to QPDFWriter so that only it can call certain methods. |
| 11 | 13 | class QPDF::Writer |
| 12 | 14 | { |
| ... | ... | @@ -204,7 +206,7 @@ class QPDF::EncryptionParameters |
| 204 | 206 | } |
| 205 | 207 | |
| 206 | 208 | void initialize(QPDF& qpdf); |
| 207 | - encryption_method_e interpretCF(QPDFObjectHandle const& cf) const; | |
| 209 | + encryption_method_e interpretCF(Name const& cf) const; | |
| 208 | 210 | |
| 209 | 211 | private: |
| 210 | 212 | bool encrypted{false}; | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -96,7 +96,6 @@ QPDF recovered stream length 0 |
| 96 | 96 | QPDF found wrong endstream in recovery 0 |
| 97 | 97 | QPDF_Stream pipeStreamData with null pipeline 0 |
| 98 | 98 | QPDFWriter not recompressing /FlateDecode 0 |
| 99 | -QPDF_encryption xref stream from encrypted file 0 | |
| 100 | 99 | QPDFJob unable to filter 0 |
| 101 | 100 | QUtil non-trivial UTF-16 0 |
| 102 | 101 | QPDF xref overwrite invalid objgen 0 |
| ... | ... | @@ -141,18 +140,12 @@ qpdf-c called qpdf_set_minimum_pdf_version 0 |
| 141 | 140 | qpdf-c called qpdf_force_pdf_version 0 |
| 142 | 141 | qpdf-c called qpdf_init_write multiple times 0 |
| 143 | 142 | QPDF_encryption rc4 decode string 0 |
| 144 | -QPDF_encryption rc4 decode stream 0 | |
| 145 | 143 | QPDFWriter not compressing metadata 0 |
| 146 | -QPDF_encryption CFM V2 0 | |
| 147 | -QPDF_encryption CFM AESV2 0 | |
| 148 | 144 | QPDF_encryption aes decode string 0 |
| 149 | -QPDF_encryption cleartext metadata 0 | |
| 150 | -QPDF_encryption aes decode stream 0 | |
| 151 | 145 | QPDFWriter forcing object stream disable 0 |
| 152 | 146 | QPDFWriter forced version disabled encryption 0 |
| 153 | 147 | qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0 |
| 154 | 148 | qpdf-c called qpdf_set_static_aes_IV 0 |
| 155 | -QPDF_encryption stream crypt filter 0 | |
| 156 | 149 | QPDF ERR object stream with wrong type 0 |
| 157 | 150 | QPDF object gone after xref reconstruction 0 |
| 158 | 151 | qpdf-c called qpdf_has_error 0 |
| ... | ... | @@ -222,7 +215,6 @@ QPDFWriter remove ADBE 0 |
| 222 | 215 | QPDFWriter remove existing Extensions 0 |
| 223 | 216 | QPDFWriter preserve ADBE 0 |
| 224 | 217 | QPDF_encryption skip 0x28 0 |
| 225 | -QPDF_encryption CFM AESV3 0 | |
| 226 | 218 | qpdf-c called qpdf_get_pdf_extension_level 0 |
| 227 | 219 | qpdf-c called qpdf_set_r5_encryption_parameters 0 |
| 228 | 220 | qpdf-c called qpdf_set_r6_encryption_parameters 0 |
| ... | ... | @@ -275,7 +267,6 @@ QPDFParser eof in parse 0 |
| 275 | 267 | QPDFParser eof in parseRemainder 0 |
| 276 | 268 | QPDFObjectHandle boolean returning false 0 |
| 277 | 269 | QPDFObjectHandle real returning 0.0 0 |
| 278 | -QPDFObjectHandle name returning dummy name 0 | |
| 279 | 270 | QPDFObjectHandle string returning empty string 0 |
| 280 | 271 | QPDFObjectHandle string returning empty utf8 0 |
| 281 | 272 | QPDFObjectHandle operator returning fake value 0 |
| ... | ... | @@ -301,8 +292,6 @@ QPDFFormFieldObjectHelper TU absent 0 |
| 301 | 292 | QPDFFormFieldObjectHelper TM absent 0 |
| 302 | 293 | QPDFFormFieldObjectHelper Q present 1 |
| 303 | 294 | QPDFFormFieldObjectHelper DA present 1 |
| 304 | -QPDFAnnotationObjectHelper AS present 0 | |
| 305 | -QPDFAnnotationObjectHelper AS absent 0 | |
| 306 | 295 | QPDFAnnotationObjectHelper AP stream 0 |
| 307 | 296 | QPDFAnnotationObjectHelper AP dictionary 0 |
| 308 | 297 | QPDFAnnotationObjectHelper AP sub stream 0 |
| ... | ... | @@ -317,9 +306,6 @@ QPDFAcroFormDocumentHelper annotation found 1 |
| 317 | 306 | QPDFJob keep files open n 0 |
| 318 | 307 | QPDFJob keep files open y 0 |
| 319 | 308 | QPDFJob automatically set keep files open 1 |
| 320 | -QPDFOutlineObjectHelper direct dest 0 | |
| 321 | -QPDFOutlineObjectHelper action dest 0 | |
| 322 | -QPDFOutlineObjectHelper named dest 0 | |
| 323 | 309 | QPDFOutlineDocumentHelper string named dest 0 |
| 324 | 310 | QPDFObjectHandle merge top type mismatch 0 |
| 325 | 311 | QPDFObjectHandle merge shallow copy 0 |
| ... | ... | @@ -366,7 +352,6 @@ QPDFJob bytes fallback warning 0 |
| 366 | 352 | QPDFJob invalid utf-8 in auto 0 |
| 367 | 353 | QPDFJob input password hex-bytes 0 |
| 368 | 354 | QPDFPageDocumentHelper ignore annotation with no appearance 0 |
| 369 | -QPDFFormFieldObjectHelper create AS from scratch 0 | |
| 370 | 355 | QPDFFormFieldObjectHelper replaced BMC at EOF 0 |
| 371 | 356 | QPDFFormFieldObjectHelper fallback Tf 0 |
| 372 | 357 | QPDFPageObjectHelper copy shared attribute 1 | ... | ... |