Commit 551a953452e54d337bab6a6fa9d5b689f18446a3

Authored by m-holger
Committed by GitHub
2 parents 46425856 6346e664

Merge pull request #1540 from m-holger/oh_name

Create new private-API class Name
libqpdf/QPDFAnnotationObjectHelper.cc
1 #include <qpdf/QPDFAnnotationObjectHelper.hh> 1 #include <qpdf/QPDFAnnotationObjectHelper.hh>
2 2
3 -#include <qpdf/QPDF.hh>  
4 #include <qpdf/QPDFMatrix.hh> 3 #include <qpdf/QPDFMatrix.hh>
  4 +#include <qpdf/QPDFObjectHandle_private.hh>
5 #include <qpdf/QTC.hh> 5 #include <qpdf/QTC.hh>
6 #include <qpdf/QUtil.hh> 6 #include <qpdf/QUtil.hh>
7 7
  8 +using namespace qpdf;
  9 +
8 QPDFAnnotationObjectHelper::QPDFAnnotationObjectHelper(QPDFObjectHandle oh) : 10 QPDFAnnotationObjectHelper::QPDFAnnotationObjectHelper(QPDFObjectHandle oh) :
9 QPDFObjectHelper(oh) 11 QPDFObjectHelper(oh)
10 { 12 {
@@ -31,19 +33,15 @@ QPDFAnnotationObjectHelper::getAppearanceDictionary() @@ -31,19 +33,15 @@ QPDFAnnotationObjectHelper::getAppearanceDictionary()
31 std::string 33 std::string
32 QPDFAnnotationObjectHelper::getAppearanceState() 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 int 40 int
43 QPDFAnnotationObjectHelper::getFlags() 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 QPDFObjectHandle 47 QPDFObjectHandle
libqpdf/QPDFFormFieldObjectHelper.cc
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 #include <qpdf/QUtil.hh> 10 #include <qpdf/QUtil.hh>
11 #include <cstdlib> 11 #include <cstdlib>
12 12
  13 +using namespace qpdf;
  14 +
13 QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper(QPDFObjectHandle oh) : 15 QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper(QPDFObjectHandle oh) :
14 QPDFObjectHelper(oh), 16 QPDFObjectHelper(oh),
15 m(new Members()) 17 m(new Members())
@@ -98,9 +100,8 @@ QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(std::string const&amp; n @@ -98,9 +100,8 @@ QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(std::string const&amp; n
98 std::string 100 std::string
99 QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(std::string const& name) 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 return {}; 106 return {};
106 } 107 }
@@ -245,7 +246,7 @@ QPDFFormFieldObjectHelper::isCheckbox() @@ -245,7 +246,7 @@ QPDFFormFieldObjectHelper::isCheckbox()
245 bool 246 bool
246 QPDFFormFieldObjectHelper::isChecked() 247 QPDFFormFieldObjectHelper::isChecked()
247 { 248 {
248 - return isCheckbox() && getValue().isName() && getValue().getName() != "/Off"; 249 + return isCheckbox() && Name(getValue()) != "/Off";
249 } 250 }
250 251
251 bool 252 bool
@@ -301,25 +302,25 @@ QPDFFormFieldObjectHelper::setFieldAttribute(std::string const&amp; key, std::string @@ -301,25 +302,25 @@ QPDFFormFieldObjectHelper::setFieldAttribute(std::string const&amp; key, std::string
301 void 302 void
302 QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) 303 QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
303 { 304 {
  305 + Name name = value;
304 if (getFieldType() == "/Btn") { 306 if (getFieldType() == "/Btn") {
305 if (isCheckbox()) { 307 if (isCheckbox()) {
306 - if (!value.isName()) { 308 + if (!name) {
307 warn("ignoring attempt to set a checkbox field to a value whose type is not name"); 309 warn("ignoring attempt to set a checkbox field to a value whose type is not name");
308 return; 310 return;
309 } 311 }
310 - std::string name = value.getName();  
311 // Accept any value other than /Off to mean checked. Files have been seen that use 312 // Accept any value other than /Off to mean checked. Files have been seen that use
312 // /1 or other values. 313 // /1 or other values.
313 setCheckBoxValue(name != "/Off"); 314 setCheckBoxValue(name != "/Off");
314 return; 315 return;
315 } 316 }
316 if (isRadioButton()) { 317 if (isRadioButton()) {
317 - if (!value.isName()) { 318 + if (!name) {
318 warn( 319 warn(
319 "ignoring attempt to set a radio button field to an object that is not a name"); 320 "ignoring attempt to set a radio button field to an object that is not a name");
320 return; 321 return;
321 } 322 }
322 - setRadioButtonValue(value); 323 + setRadioButtonValue(name);
323 return; 324 return;
324 } 325 }
325 if (isPushbutton()) { 326 if (isPushbutton()) {
@@ -743,17 +744,18 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao @@ -743,17 +744,18 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao
743 { 744 {
744 QPDFObjectHandle AS = aoh.getAppearanceStream("/N"); 745 QPDFObjectHandle AS = aoh.getAppearanceStream("/N");
745 if (AS.null()) { 746 if (AS.null()) {
746 - QTC::TC("qpdf", "QPDFFormFieldObjectHelper create AS from scratch");  
747 QPDFObjectHandle::Rectangle rect = aoh.getRect(); 747 QPDFObjectHandle::Rectangle rect = aoh.getRect();
748 QPDFObjectHandle::Rectangle bbox(0, 0, rect.urx - rect.llx, rect.ury - rect.lly); 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 AS = QPDFObjectHandle::newStream(oh().getOwningQPDF(), "/Tx BMC\nEMC\n"); 754 AS = QPDFObjectHandle::newStream(oh().getOwningQPDF(), "/Tx BMC\nEMC\n");
753 AS.replaceDict(dict); 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 AP = aoh.getAppearanceDictionary(); 759 AP = aoh.getAppearanceDictionary();
758 } 760 }
759 AP.replaceKey("/N", AS); 761 AP.replaceKey("/N", AS);
@@ -776,7 +778,7 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao @@ -776,7 +778,7 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao
776 std::string DA = getDefaultAppearance(); 778 std::string DA = getDefaultAppearance();
777 std::string V = getValueAsString(); 779 std::string V = getValueAsString();
778 std::vector<std::string> opt; 780 std::vector<std::string> opt;
779 - if (isChoice() && ((getFlags() & ff_ch_combo) == 0)) { 781 + if (isChoice() && (getFlags() & ff_ch_combo) == 0) {
780 opt = getChoices(); 782 opt = getChoices();
781 } 783 }
782 784
@@ -791,29 +793,26 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao @@ -791,29 +793,26 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao
791 std::string font_name = tff.getFontName(); 793 std::string font_name = tff.getFontName();
792 if (!font_name.empty()) { 794 if (!font_name.empty()) {
793 // See if the font is encoded with something we know about. 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 if (!font) { 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 encoder = &QUtil::utf8_to_win_ansi; 814 encoder = &QUtil::utf8_to_win_ansi;
816 - } else if (encoding == "/MacRomanEncoding") { 815 + } else if (Encoding == "/MacRomanEncoding") {
817 encoder = &QUtil::utf8_to_mac_roman; 816 encoder = &QUtil::utf8_to_mac_roman;
818 } 817 }
819 } 818 }
libqpdf/QPDFObjectHandle.cc
@@ -766,14 +766,14 @@ QPDFObjectHandle::isScalar() const @@ -766,14 +766,14 @@ QPDFObjectHandle::isScalar() const
766 bool 766 bool
767 QPDFObjectHandle::isNameAndEquals(std::string const& name) const 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 bool 772 bool
773 QPDFObjectHandle::isDictionaryOfType(std::string const& type, std::string const& subtype) const 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 bool 779 bool
@@ -952,7 +952,33 @@ QPDFObjectHandle::getValueAsReal(std::string&amp; value) const @@ -952,7 +952,33 @@ QPDFObjectHandle::getValueAsReal(std::string&amp; value) const
952 return true; 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 std::string 983 std::string
958 QPDFObjectHandle::getName() const 984 QPDFObjectHandle::getName() const
@@ -961,7 +987,6 @@ QPDFObjectHandle::getName() const @@ -961,7 +987,6 @@ QPDFObjectHandle::getName() const
961 return obj->getStringValue(); 987 return obj->getStringValue();
962 } else { 988 } else {
963 typeWarning("name", "returning dummy name"); 989 typeWarning("name", "returning dummy name");
964 - QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name");  
965 return "/QPDFFakeName"; 990 return "/QPDFFakeName";
966 } 991 }
967 } 992 }
@@ -1695,12 +1720,6 @@ QPDFObjectHandle::newReal(double value, int decimal_places, bool trim_trailing_z @@ -1695,12 +1720,6 @@ QPDFObjectHandle::newReal(double value, int decimal_places, bool trim_trailing_z
1695 } 1720 }
1696 1721
1697 QPDFObjectHandle 1722 QPDFObjectHandle
1698 -QPDFObjectHandle::newName(std::string const& name)  
1699 -{  
1700 - return {QPDFObject::create<QPDF_Name>(name)};  
1701 -}  
1702 -  
1703 -QPDFObjectHandle  
1704 QPDFObjectHandle::newString(std::string const& str) 1723 QPDFObjectHandle::newString(std::string const& str)
1705 { 1724 {
1706 return {QPDFObject::create<QPDF_String>(str)}; 1725 return {QPDFObject::create<QPDF_String>(str)};
libqpdf/QPDFOutlineObjectHelper.cc
@@ -48,26 +48,19 @@ QPDFOutlineObjectHelper::getKids() @@ -48,26 +48,19 @@ QPDFOutlineObjectHelper::getKids()
48 QPDFObjectHandle 48 QPDFObjectHandle
49 QPDFOutlineObjectHelper::getDest() 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 return QPDFObjectHandle::newNull(); 59 return QPDFObjectHandle::newNull();
64 } 60 }
65 -  
66 if (dest.isName() || dest.isString()) { 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 return dest; 64 return dest;
72 } 65 }
73 66
libqpdf/QPDF_Stream.cc
@@ -38,11 +38,10 @@ namespace @@ -38,11 +38,10 @@ namespace
38 setDecodeParms(QPDFObjectHandle decode_parms) final 38 setDecodeParms(QPDFObjectHandle decode_parms) final
39 { 39 {
40 // we only validate here - processing happens in decryptStream 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 for (auto const& [key, value]: dict) { 42 for (auto const& [key, value]: dict) {
43 if (key == "/Type" && 43 if (key == "/Type" &&
44 - (value.null() ||  
45 - (value.isName() && value.getName() == "/CryptFilterDecodeParms"))) { 44 + (value.null() || Name(value) == "/CryptFilterDecodeParms")) {
46 continue; 45 continue;
47 } 46 }
48 if (key == "/Name") { 47 if (key == "/Name") {
@@ -54,7 +53,7 @@ namespace @@ -54,7 +53,7 @@ namespace
54 } 53 }
55 return true; 54 return true;
56 } 55 }
57 - return false; 56 + return decode_parms.null();
58 } 57 }
59 58
60 Pipeline* 59 Pipeline*
@@ -374,7 +373,7 @@ Stream::filterable( @@ -374,7 +373,7 @@ Stream::filterable(
374 auto s = stream(); 373 auto s = stream();
375 // Check filters 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 if (filter_obj.null()) { 378 if (filter_obj.null()) {
380 // No filters 379 // No filters
@@ -387,14 +386,14 @@ Stream::filterable( @@ -387,14 +386,14 @@ Stream::filterable(
387 return false; 386 return false;
388 } 387 }
389 filters.emplace_back(ff()); 388 filters.emplace_back(ff());
390 - } else if (auto array = filter_obj.as_array(strict)) { 389 + } else if (Array array = filter_obj) {
391 // Potentially multiple filters 390 // Potentially multiple filters
392 - for (auto const& item: array) {  
393 - if (!item.isName()) { 391 + for (Name item: array) {
  392 + if (!item) {
394 warn("stream filter type is not name or array"); 393 warn("stream filter type is not name or array");
395 return false; 394 return false;
396 } 395 }
397 - auto ff = s->filter_factory(item.getName()); 396 + auto ff = s->filter_factory(item);
398 if (!ff) { 397 if (!ff) {
399 filters.clear(); 398 filters.clear();
400 return false; 399 return false;
libqpdf/QPDF_encryption.cc
@@ -600,18 +600,17 @@ QPDF::EncryptionData::recover_encryption_key_with_password( @@ -600,18 +600,17 @@ QPDF::EncryptionData::recover_encryption_key_with_password(
600 } 600 }
601 601
602 QPDF::encryption_method_e 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 // Default: /Identity 606 // Default: /Identity
607 return e_none; 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 if (it != crypt_filters.end()) { 610 if (it != crypt_filters.end()) {
612 return it->second; 611 return it->second;
613 } 612 }
614 - if (filter == "/Identity") { 613 + if (cf == "/Identity") {
615 return e_none; 614 return e_none;
616 } 615 }
617 return e_unknown; 616 return e_unknown;
@@ -677,8 +676,7 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf) @@ -677,8 +676,7 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf)
677 throw qpdf.damagedPDF("/Encrypt in trailer dictionary is not a dictionary"); 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 throw unsupported("unsupported encryption filter"); 680 throw unsupported("unsupported encryption filter");
683 } 681 }
684 if (!encryption_dict.getKey("/SubFilter").null()) { 682 if (!encryption_dict.getKey("/SubFilter").null()) {
@@ -766,16 +764,12 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf) @@ -766,16 +764,12 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf)
766 for (auto const& [filter, cdict]: CF.as_dictionary()) { 764 for (auto const& [filter, cdict]: CF.as_dictionary()) {
767 if (cdict.isDictionary()) { 765 if (cdict.isDictionary()) {
768 encryption_method_e method = e_none; 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 method = e_rc4; 769 method = e_rc4;
774 - } else if (method_name == "/AESV2") {  
775 - QTC::TC("qpdf", "QPDF_encryption CFM AESV2"); 770 + } else if (CFM == "/AESV2") {
776 method = e_aes; 771 method = e_aes;
777 - } else if (method_name == "/AESV3") {  
778 - QTC::TC("qpdf", "QPDF_encryption CFM AESV3"); 772 + } else if (CFM == "/AESV3") {
779 method = e_aesv3; 773 method = e_aesv3;
780 } else { 774 } else {
781 // Don't complain now -- maybe we won't need to reference this type. 775 // Don't complain now -- maybe we won't need to reference this type.
@@ -786,9 +780,9 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf) @@ -786,9 +780,9 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; 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 // qpdf does not use this for anything other than informational purposes. This is 786 // qpdf does not use this for anything other than informational purposes. This is
793 // intended to instruct conforming writers on which crypt filter should be used when new 787 // intended to instruct conforming writers on which crypt filter should be used when new
794 // file attachments are added to a PDF file, but qpdf never generates encrypted files 788 // file attachments are added to a PDF file, but qpdf never generates encrypted files
@@ -937,12 +931,7 @@ QPDF::decryptStream( @@ -937,12 +931,7 @@ QPDF::decryptStream(
937 bool is_root_metadata, 931 bool is_root_metadata,
938 std::unique_ptr<Pipeline>& decrypt_pipeline) 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 return; 935 return;
947 } 936 }
948 bool use_aes = false; 937 bool use_aes = false;
@@ -951,26 +940,20 @@ QPDF::decryptStream( @@ -951,26 +940,20 @@ QPDF::decryptStream(
951 std::string method_source = "/StmF from /Encrypt dictionary"; 940 std::string method_source = "/StmF from /Encrypt dictionary";
952 941
953 if (stream_dict.getKey("/Filter").isOrHasName("/Crypt")) { 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 method_source = "stream's Crypt decode parameters"; 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 if (filter.size() == decode.size()) { 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 method_source = "stream's Crypt decode parameters (array)"; 957 method_source = "stream's Crypt decode parameters (array)";
975 } 958 }
976 break; 959 break;
@@ -982,8 +965,7 @@ QPDF::decryptStream( @@ -982,8 +965,7 @@ QPDF::decryptStream(
982 } 965 }
983 966
984 if (method == e_unknown) { 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 method = e_none; 969 method = e_none;
988 } else { 970 } else {
989 method = encp->cf_stream; 971 method = encp->cf_stream;
@@ -1008,13 +990,13 @@ QPDF::decryptStream( @@ -1008,13 +990,13 @@ QPDF::decryptStream(
1008 990
1009 default: 991 default:
1010 // filter local to this stream. 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 // To avoid repeated warnings, reset cf_stream. Assume we'd want to use AES if V == 4. 1000 // To avoid repeated warnings, reset cf_stream. Assume we'd want to use AES if V == 4.
1019 encp->cf_stream = e_aes; 1001 encp->cf_stream = e_aes;
1020 use_aes = true; 1002 use_aes = true;
@@ -1023,11 +1005,9 @@ QPDF::decryptStream( @@ -1023,11 +1005,9 @@ QPDF::decryptStream(
1023 } 1005 }
1024 std::string key = getKeyForObject(encp, og, use_aes); 1006 std::string key = getKeyForObject(encp, og, use_aes);
1025 if (use_aes) { 1007 if (use_aes) {
1026 - QTC::TC("qpdf", "QPDF_encryption aes decode stream");  
1027 decrypt_pipeline = 1008 decrypt_pipeline =
1028 std::make_unique<Pl_AES_PDF>("AES stream decryption", pipeline, false, key); 1009 std::make_unique<Pl_AES_PDF>("AES stream decryption", pipeline, false, key);
1029 } else { 1010 } else {
1030 - QTC::TC("qpdf", "QPDF_encryption rc4 decode stream");  
1031 decrypt_pipeline = std::make_unique<Pl_RC4>("RC4 stream decryption", pipeline, key); 1011 decrypt_pipeline = std::make_unique<Pl_RC4>("RC4 stream decryption", pipeline, key);
1032 } 1012 }
1033 pipeline = decrypt_pipeline.get(); 1013 pipeline = decrypt_pipeline.get();
libqpdf/qpdf/QPDFObjectHandle_private.hh
@@ -324,7 +324,7 @@ namespace qpdf @@ -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 // std::invalid_argument exception. If the object is out of range for the target type, 328 // std::invalid_argument exception. If the object is out of range for the target type,
329 // throw a std::overflow_error or std::underflow_error exception. 329 // throw a std::overflow_error or std::underflow_error exception.
330 template <std::integral T> 330 template <std::integral T>
@@ -391,6 +391,45 @@ namespace qpdf @@ -391,6 +391,45 @@ namespace qpdf
391 // escaping. Return {false, false} if the name is not valid utf-8, otherwise return {true, 391 // escaping. Return {false, false} if the name is not valid utf-8, otherwise return {true,
392 // true} if no characters require or {true, false} if escaping is required. 392 // true} if no characters require or {true, false} if escaping is required.
393 static std::pair<bool, bool> analyzeJSONEncoding(std::string const& name); 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 class Stream final: public BaseHandle 435 class Stream final: public BaseHandle
libqpdf/qpdf/QPDFObject_private.hh
@@ -28,6 +28,7 @@ namespace qpdf @@ -28,6 +28,7 @@ namespace qpdf
28 class BaseDictionary; 28 class BaseDictionary;
29 class Dictionary; 29 class Dictionary;
30 class Integer; 30 class Integer;
  31 + class Name;
31 class Stream; 32 class Stream;
32 } // namespace qpdf 33 } // namespace qpdf
33 34
@@ -138,6 +139,7 @@ class QPDF_Name final @@ -138,6 +139,7 @@ class QPDF_Name final
138 { 139 {
139 friend class QPDFObject; 140 friend class QPDFObject;
140 friend class qpdf::BaseHandle; 141 friend class qpdf::BaseHandle;
  142 + friend class qpdf::Name;
141 143
142 explicit QPDF_Name(std::string name) : 144 explicit QPDF_Name(std::string name) :
143 name(std::move(name)) 145 name(std::move(name))
libqpdf/qpdf/QPDF_private.hh
@@ -7,6 +7,8 @@ @@ -7,6 +7,8 @@
7 #include <qpdf/QPDFObject_private.hh> 7 #include <qpdf/QPDFObject_private.hh>
8 #include <qpdf/QPDFTokenizer_private.hh> 8 #include <qpdf/QPDFTokenizer_private.hh>
9 9
  10 +using namespace qpdf;
  11 +
10 // Writer class is restricted to QPDFWriter so that only it can call certain methods. 12 // Writer class is restricted to QPDFWriter so that only it can call certain methods.
11 class QPDF::Writer 13 class QPDF::Writer
12 { 14 {
@@ -204,7 +206,7 @@ class QPDF::EncryptionParameters @@ -204,7 +206,7 @@ class QPDF::EncryptionParameters
204 } 206 }
205 207
206 void initialize(QPDF& qpdf); 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 private: 211 private:
210 bool encrypted{false}; 212 bool encrypted{false};
qpdf/qpdf.testcov
@@ -96,7 +96,6 @@ QPDF recovered stream length 0 @@ -96,7 +96,6 @@ QPDF recovered stream length 0
96 QPDF found wrong endstream in recovery 0 96 QPDF found wrong endstream in recovery 0
97 QPDF_Stream pipeStreamData with null pipeline 0 97 QPDF_Stream pipeStreamData with null pipeline 0
98 QPDFWriter not recompressing /FlateDecode 0 98 QPDFWriter not recompressing /FlateDecode 0
99 -QPDF_encryption xref stream from encrypted file 0  
100 QPDFJob unable to filter 0 99 QPDFJob unable to filter 0
101 QUtil non-trivial UTF-16 0 100 QUtil non-trivial UTF-16 0
102 QPDF xref overwrite invalid objgen 0 101 QPDF xref overwrite invalid objgen 0
@@ -141,18 +140,12 @@ qpdf-c called qpdf_set_minimum_pdf_version 0 @@ -141,18 +140,12 @@ qpdf-c called qpdf_set_minimum_pdf_version 0
141 qpdf-c called qpdf_force_pdf_version 0 140 qpdf-c called qpdf_force_pdf_version 0
142 qpdf-c called qpdf_init_write multiple times 0 141 qpdf-c called qpdf_init_write multiple times 0
143 QPDF_encryption rc4 decode string 0 142 QPDF_encryption rc4 decode string 0
144 -QPDF_encryption rc4 decode stream 0  
145 QPDFWriter not compressing metadata 0 143 QPDFWriter not compressing metadata 0
146 -QPDF_encryption CFM V2 0  
147 -QPDF_encryption CFM AESV2 0  
148 QPDF_encryption aes decode string 0 144 QPDF_encryption aes decode string 0
149 -QPDF_encryption cleartext metadata 0  
150 -QPDF_encryption aes decode stream 0  
151 QPDFWriter forcing object stream disable 0 145 QPDFWriter forcing object stream disable 0
152 QPDFWriter forced version disabled encryption 0 146 QPDFWriter forced version disabled encryption 0
153 qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0 147 qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0
154 qpdf-c called qpdf_set_static_aes_IV 0 148 qpdf-c called qpdf_set_static_aes_IV 0
155 -QPDF_encryption stream crypt filter 0  
156 QPDF ERR object stream with wrong type 0 149 QPDF ERR object stream with wrong type 0
157 QPDF object gone after xref reconstruction 0 150 QPDF object gone after xref reconstruction 0
158 qpdf-c called qpdf_has_error 0 151 qpdf-c called qpdf_has_error 0
@@ -222,7 +215,6 @@ QPDFWriter remove ADBE 0 @@ -222,7 +215,6 @@ QPDFWriter remove ADBE 0
222 QPDFWriter remove existing Extensions 0 215 QPDFWriter remove existing Extensions 0
223 QPDFWriter preserve ADBE 0 216 QPDFWriter preserve ADBE 0
224 QPDF_encryption skip 0x28 0 217 QPDF_encryption skip 0x28 0
225 -QPDF_encryption CFM AESV3 0  
226 qpdf-c called qpdf_get_pdf_extension_level 0 218 qpdf-c called qpdf_get_pdf_extension_level 0
227 qpdf-c called qpdf_set_r5_encryption_parameters 0 219 qpdf-c called qpdf_set_r5_encryption_parameters 0
228 qpdf-c called qpdf_set_r6_encryption_parameters 0 220 qpdf-c called qpdf_set_r6_encryption_parameters 0
@@ -275,7 +267,6 @@ QPDFParser eof in parse 0 @@ -275,7 +267,6 @@ QPDFParser eof in parse 0
275 QPDFParser eof in parseRemainder 0 267 QPDFParser eof in parseRemainder 0
276 QPDFObjectHandle boolean returning false 0 268 QPDFObjectHandle boolean returning false 0
277 QPDFObjectHandle real returning 0.0 0 269 QPDFObjectHandle real returning 0.0 0
278 -QPDFObjectHandle name returning dummy name 0  
279 QPDFObjectHandle string returning empty string 0 270 QPDFObjectHandle string returning empty string 0
280 QPDFObjectHandle string returning empty utf8 0 271 QPDFObjectHandle string returning empty utf8 0
281 QPDFObjectHandle operator returning fake value 0 272 QPDFObjectHandle operator returning fake value 0
@@ -301,8 +292,6 @@ QPDFFormFieldObjectHelper TU absent 0 @@ -301,8 +292,6 @@ QPDFFormFieldObjectHelper TU absent 0
301 QPDFFormFieldObjectHelper TM absent 0 292 QPDFFormFieldObjectHelper TM absent 0
302 QPDFFormFieldObjectHelper Q present 1 293 QPDFFormFieldObjectHelper Q present 1
303 QPDFFormFieldObjectHelper DA present 1 294 QPDFFormFieldObjectHelper DA present 1
304 -QPDFAnnotationObjectHelper AS present 0  
305 -QPDFAnnotationObjectHelper AS absent 0  
306 QPDFAnnotationObjectHelper AP stream 0 295 QPDFAnnotationObjectHelper AP stream 0
307 QPDFAnnotationObjectHelper AP dictionary 0 296 QPDFAnnotationObjectHelper AP dictionary 0
308 QPDFAnnotationObjectHelper AP sub stream 0 297 QPDFAnnotationObjectHelper AP sub stream 0
@@ -317,9 +306,6 @@ QPDFAcroFormDocumentHelper annotation found 1 @@ -317,9 +306,6 @@ QPDFAcroFormDocumentHelper annotation found 1
317 QPDFJob keep files open n 0 306 QPDFJob keep files open n 0
318 QPDFJob keep files open y 0 307 QPDFJob keep files open y 0
319 QPDFJob automatically set keep files open 1 308 QPDFJob automatically set keep files open 1
320 -QPDFOutlineObjectHelper direct dest 0  
321 -QPDFOutlineObjectHelper action dest 0  
322 -QPDFOutlineObjectHelper named dest 0  
323 QPDFOutlineDocumentHelper string named dest 0 309 QPDFOutlineDocumentHelper string named dest 0
324 QPDFObjectHandle merge top type mismatch 0 310 QPDFObjectHandle merge top type mismatch 0
325 QPDFObjectHandle merge shallow copy 0 311 QPDFObjectHandle merge shallow copy 0
@@ -366,7 +352,6 @@ QPDFJob bytes fallback warning 0 @@ -366,7 +352,6 @@ QPDFJob bytes fallback warning 0
366 QPDFJob invalid utf-8 in auto 0 352 QPDFJob invalid utf-8 in auto 0
367 QPDFJob input password hex-bytes 0 353 QPDFJob input password hex-bytes 0
368 QPDFPageDocumentHelper ignore annotation with no appearance 0 354 QPDFPageDocumentHelper ignore annotation with no appearance 0
369 -QPDFFormFieldObjectHelper create AS from scratch 0  
370 QPDFFormFieldObjectHelper replaced BMC at EOF 0 355 QPDFFormFieldObjectHelper replaced BMC at EOF 0
371 QPDFFormFieldObjectHelper fallback Tf 0 356 QPDFFormFieldObjectHelper fallback Tf 0
372 QPDFPageObjectHelper copy shared attribute 1 357 QPDFPageObjectHelper copy shared attribute 1