Commit 8593b9fdf74e68099a5fe2aa0b6d0b6320781a2d

Authored by m-holger
Committed by Jay Berkenbilt
1 parent 37071065

Add new convenience methods QPDFObjectHandle::isNameAndEquals, etc

Add methods isNameAndEquals, isDictionaryOfType, isStreamOfType
include/qpdf/QPDFObjectHandle.hh
@@ -365,6 +365,22 @@ class QPDFObjectHandle @@ -365,6 +365,22 @@ class QPDFObjectHandle
365 QPDF_DLL 365 QPDF_DLL
366 bool isScalar(); 366 bool isScalar();
367 367
  368 + // True if the object is a name object representing the provided name.
  369 + QPDF_DLL
  370 + bool isNameAndEquals(std::string const& name);
  371 +
  372 + // True if the object is a dictionary of the specified type and
  373 + // subtype, if any.
  374 + QPDF_DLL
  375 + bool isDictionaryOfType(std::string const& type,
  376 + std::string const& subtype = "");
  377 +
  378 + // True if the object is a stream of the specified type and
  379 + // subtype, if any.
  380 + QPDF_DLL
  381 + bool isStreamOfType(std::string const& type,
  382 + std::string const& subtype = "");
  383 +
368 // Public factory methods 384 // Public factory methods
369 385
370 // Wrap an object in an array if it is not already an array. This 386 // Wrap an object in an array if it is not already an array. This
include/qpdf/qpdf-c.h
@@ -676,6 +676,15 @@ extern "C" { @@ -676,6 +676,15 @@ extern "C" {
676 QPDF_BOOL qpdf_oh_is_indirect(qpdf_data qpdf, qpdf_oh oh); 676 QPDF_BOOL qpdf_oh_is_indirect(qpdf_data qpdf, qpdf_oh oh);
677 QPDF_DLL 677 QPDF_DLL
678 QPDF_BOOL qpdf_oh_is_scalar(qpdf_data qpdf, qpdf_oh oh); 678 QPDF_BOOL qpdf_oh_is_scalar(qpdf_data qpdf, qpdf_oh oh);
  679 +
  680 + QPDF_DLL
  681 + QPDF_BOOL qpdf_oh_is_name_and_equals(
  682 + qpdf_data qpdf, qpdf_oh oh, char const* name);
  683 +
  684 + QPDF_DLL
  685 + QPDF_BOOL qpdf_oh_is_dictionary_of_type(
  686 + qpdf_data qpdf, qpdf_oh oh, char const* type, char const* subtype);
  687 +
679 QPDF_DLL 688 QPDF_DLL
680 enum qpdf_object_type_e qpdf_oh_get_type_code(qpdf_data qpdf, qpdf_oh oh); 689 enum qpdf_object_type_e qpdf_oh_get_type_code(qpdf_data qpdf, qpdf_oh oh);
681 QPDF_DLL 690 QPDF_DLL
libqpdf/QPDFObjectHandle.cc
@@ -497,6 +497,34 @@ QPDFObjectHandle::isScalar() @@ -497,6 +497,34 @@ QPDFObjectHandle::isScalar()
497 isOperator() || isInlineImage())); 497 isOperator() || isInlineImage()));
498 } 498 }
499 499
  500 +bool
  501 +QPDFObjectHandle::isNameAndEquals(std::string const& name)
  502 +{
  503 + return isName() && (getName() == name);
  504 +}
  505 +
  506 +bool
  507 +QPDFObjectHandle::isDictionaryOfType(std::string const& type,
  508 + std::string const& subtype)
  509 +{
  510 + if (isDictionary() && getKey("/Type").isNameAndEquals(type))
  511 + {
  512 + return (subtype == "") ||
  513 + (hasKey("/Subtype") && getKey("/Subtype").isNameAndEquals(subtype));
  514 + }
  515 + else
  516 + {
  517 + return false;
  518 + }
  519 +}
  520 +
  521 +bool
  522 +QPDFObjectHandle::isStreamOfType(std::string const& type,
  523 + std::string const& subtype)
  524 +{
  525 + return isStream() && getDict().isDictionaryOfType(type, subtype);
  526 +}
  527 +
500 // Bool accessors 528 // Bool accessors
501 529
502 bool 530 bool
libqpdf/qpdf-c.cc
@@ -1148,6 +1148,27 @@ QPDF_BOOL qpdf_oh_is_number(qpdf_data qpdf, qpdf_oh oh) @@ -1148,6 +1148,27 @@ QPDF_BOOL qpdf_oh_is_number(qpdf_data qpdf, qpdf_oh oh)
1148 }); 1148 });
1149 } 1149 }
1150 1150
  1151 +QPDF_BOOL qpdf_oh_is_name_and_equals(
  1152 + qpdf_data qpdf, qpdf_oh oh, char const* name)
  1153 +{
  1154 + return do_with_oh<QPDF_BOOL>(
  1155 + qpdf, oh, return_false, [name](QPDFObjectHandle& o) {
  1156 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_name_and_equals");
  1157 + return o.isNameAndEquals(name);
  1158 + });
  1159 +}
  1160 +
  1161 +QPDF_BOOL qpdf_oh_is_dictionary_of_type(
  1162 + qpdf_data qpdf, qpdf_oh oh, char const* type, char const* subtype)
  1163 +{
  1164 + auto stype = (subtype == nullptr) ? "" : subtype;
  1165 + return do_with_oh<QPDF_BOOL>(
  1166 + qpdf, oh, return_false, [type, stype](QPDFObjectHandle& o) {
  1167 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_dictionary_of_type");
  1168 + return o.isDictionaryOfType(type, stype);
  1169 + });
  1170 +}
  1171 +
1151 qpdf_object_type_e qpdf_oh_get_type_code(qpdf_data qpdf, qpdf_oh oh) 1172 qpdf_object_type_e qpdf_oh_get_type_code(qpdf_data qpdf, qpdf_oh oh)
1152 { 1173 {
1153 return do_with_oh<qpdf_object_type_e>( 1174 return do_with_oh<qpdf_object_type_e>(
qpdf/qpdf-ctest.c
@@ -694,12 +694,16 @@ static void test25(char const* infile, @@ -694,12 +694,16 @@ static void test25(char const* infile,
694 (strcmp(qpdf_oh_get_utf8_value(qpdf, p_string), "3\xc3\xb7") == 0) && 694 (strcmp(qpdf_oh_get_utf8_value(qpdf, p_string), "3\xc3\xb7") == 0) &&
695 (strcmp(qpdf_oh_unparse_binary(qpdf, p_string), "<33f7>") == 0)); 695 (strcmp(qpdf_oh_unparse_binary(qpdf, p_string), "<33f7>") == 0));
696 assert(qpdf_oh_get_type_code(qpdf, p_string) == ot_string); 696 assert(qpdf_oh_get_type_code(qpdf, p_string) == ot_string);
  697 + assert(! qpdf_oh_is_name_and_equals(qpdf, p_string, "3\xf7"));
697 assert(strcmp(qpdf_oh_get_type_name(qpdf, p_string), "string") == 0); 698 assert(strcmp(qpdf_oh_get_type_name(qpdf, p_string), "string") == 0);
698 assert(qpdf_oh_is_dictionary(qpdf, p_dict)); 699 assert(qpdf_oh_is_dictionary(qpdf, p_dict));
699 qpdf_oh p_five = qpdf_oh_get_key(qpdf, p_dict, "/Four"); 700 qpdf_oh p_five = qpdf_oh_get_key(qpdf, p_dict, "/Four");
700 assert(qpdf_oh_is_or_has_name(qpdf, p_five, "/Five")); 701 assert(qpdf_oh_is_or_has_name(qpdf, p_five, "/Five"));
  702 + assert(! qpdf_oh_is_name_and_equals(qpdf, p_five, "/Five"));
701 assert(qpdf_oh_is_or_has_name( 703 assert(qpdf_oh_is_or_has_name(
702 qpdf, qpdf_oh_get_array_item(qpdf, p_five, 0), "/Five")); 704 qpdf, qpdf_oh_get_array_item(qpdf, p_five, 0), "/Five"));
  705 + assert(qpdf_oh_is_name_and_equals(
  706 + qpdf, qpdf_oh_get_array_item(qpdf, p_five, 0), "/Five"));
703 assert(qpdf_oh_is_null(qpdf, p_null)); 707 assert(qpdf_oh_is_null(qpdf, p_null));
704 assert(qpdf_oh_get_type_code(qpdf, p_null) == ot_null); 708 assert(qpdf_oh_get_type_code(qpdf, p_null) == ot_null);
705 assert(strcmp(qpdf_oh_get_type_name(qpdf, p_null), "null") == 0); 709 assert(strcmp(qpdf_oh_get_type_name(qpdf, p_null), "null") == 0);
@@ -710,10 +714,17 @@ static void test25(char const* infile, @@ -710,10 +714,17 @@ static void test25(char const* infile,
710 qpdf_oh_erase_item(qpdf, parsed, 4); 714 qpdf_oh_erase_item(qpdf, parsed, 4);
711 qpdf_oh_insert_item( 715 qpdf_oh_insert_item(
712 qpdf, parsed, 2, 716 qpdf, parsed, 2,
713 - qpdf_oh_parse(qpdf, "<</A 1 /B 2 /C 3 /D 4>>")); 717 + qpdf_oh_parse(
  718 + qpdf, "<</A 1 /B 2 /C 3 /D 4 /Type /Test /Subtype /Marvin>>"));
714 qpdf_oh new_dict = qpdf_oh_get_array_item(qpdf, parsed, 2); 719 qpdf_oh new_dict = qpdf_oh_get_array_item(qpdf, parsed, 2);
715 assert(qpdf_oh_has_key(qpdf, new_dict, "/A")); 720 assert(qpdf_oh_has_key(qpdf, new_dict, "/A"));
716 assert(qpdf_oh_has_key(qpdf, new_dict, "/D")); 721 assert(qpdf_oh_has_key(qpdf, new_dict, "/D"));
  722 + assert(qpdf_oh_is_dictionary_of_type(qpdf, new_dict, "/Test", ""));
  723 + assert(qpdf_oh_is_dictionary_of_type(qpdf, new_dict, "/Test", 0));
  724 + assert(qpdf_oh_is_dictionary_of_type(qpdf, new_dict, "/Test", "/Marvin"));
  725 + assert(! qpdf_oh_is_dictionary_of_type(qpdf, new_dict, "/Test2", ""));
  726 + assert(! qpdf_oh_is_dictionary_of_type(qpdf, new_dict, "/Test", "/M"));
  727 + assert(! qpdf_oh_is_dictionary_of_type(qpdf, new_dict, "", ""));
717 qpdf_oh new_array = qpdf_oh_new_array(qpdf); 728 qpdf_oh new_array = qpdf_oh_new_array(qpdf);
718 qpdf_oh_replace_or_remove_key( 729 qpdf_oh_replace_or_remove_key(
719 qpdf, new_dict, "/A", qpdf_oh_new_null(qpdf)); 730 qpdf, new_dict, "/A", qpdf_oh_new_null(qpdf));
qpdf/qpdf.testcov
@@ -474,6 +474,8 @@ qpdf-c called qpdf_oh_is_dictionary 0 @@ -474,6 +474,8 @@ qpdf-c called qpdf_oh_is_dictionary 0
474 qpdf-c called qpdf_oh_is_stream 0 474 qpdf-c called qpdf_oh_is_stream 0
475 qpdf-c called qpdf_oh_is_indirect 0 475 qpdf-c called qpdf_oh_is_indirect 0
476 qpdf-c called qpdf_oh_is_scalar 0 476 qpdf-c called qpdf_oh_is_scalar 0
  477 +qpdf-c called qpdf_oh_is_name_and_equals 0
  478 +qpdf-c called qpdf_oh_is_dictionary_of_type 0
477 qpdf-c called qpdf_oh_get_type_code 0 479 qpdf-c called qpdf_oh_get_type_code 0
478 qpdf-c called qpdf_oh_get_type_name 0 480 qpdf-c called qpdf_oh_get_type_name 0
479 qpdf-c array to wrap_in_array 0 481 qpdf-c array to wrap_in_array 0
qpdf/qtest/qpdf.test
@@ -1913,7 +1913,7 @@ $td-&gt;runtest(&quot;progress report on small file&quot;, @@ -1913,7 +1913,7 @@ $td-&gt;runtest(&quot;progress report on small file&quot;,
1913 show_ntests(); 1913 show_ntests();
1914 # ---------- 1914 # ----------
1915 $td->notify("--- Type checks ---"); 1915 $td->notify("--- Type checks ---");
1916 -$n_tests += 4; 1916 +$n_tests += 5;
1917 # Whenever object-types.pdf is edited, object-types-os.pdf should be 1917 # Whenever object-types.pdf is edited, object-types-os.pdf should be
1918 # regenerated. 1918 # regenerated.
1919 $td->runtest("ensure object-types-os is up-to-date", 1919 $td->runtest("ensure object-types-os is up-to-date",
@@ -1937,6 +1937,10 @@ $td-&gt;runtest(&quot;type checks with object streams&quot;, @@ -1937,6 +1937,10 @@ $td-&gt;runtest(&quot;type checks with object streams&quot;,
1937 {$td->FILE => "object-types-os.out", 1937 {$td->FILE => "object-types-os.out",
1938 $td->EXIT_STATUS => 0}, 1938 $td->EXIT_STATUS => 0},
1939 $td->NORMALIZE_NEWLINES); 1939 $td->NORMALIZE_NEWLINES);
  1940 +$td->runtest("compound type checks",
  1941 + {$td->COMMAND => "test_driver 82 object-types-os.pdf"},
  1942 + {$td->STRING => "test 82 done\n", $td->EXIT_STATUS => 0},
  1943 + $td->NORMALIZE_NEWLINES);
1940 1944
1941 # ---------- 1945 # ----------
1942 $td->notify("--- Coalesce contents ---"); 1946 $td->notify("--- Coalesce contents ---");
qpdf/qtest/qpdf/c-object-handle-creation-out.pdf
@@ -18,6 +18,8 @@ @@ -18,6 +18,8 @@
18 ] 18 ]
19 /C << 19 /C <<
20 >> 20 >>
  21 + /Subtype /Marvin
  22 + /Type /Test
21 >> 23 >>
22 /Type /Catalog 24 /Type /Catalog
23 >> 25 >>
@@ -93,17 +95,17 @@ xref @@ -93,17 +95,17 @@ xref
93 0 8 95 0 8
94 0000000000 65535 f 96 0000000000 65535 f
95 0000000025 00000 n 97 0000000025 00000 n
96 -0000000240 00000 n  
97 -0000000322 00000 n  
98 -0000000537 00000 n  
99 -0000000636 00000 n  
100 -0000000655 00000 n  
101 -0000000773 00000 n 98 +0000000277 00000 n
  99 +0000000359 00000 n
  100 +0000000574 00000 n
  101 +0000000673 00000 n
  102 +0000000692 00000 n
  103 +0000000810 00000 n
102 trailer << 104 trailer <<
103 /Root 1 0 R 105 /Root 1 0 R
104 /Size 8 106 /Size 8
105 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] 107 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
106 >> 108 >>
107 startxref 109 startxref
108 -808 110 +845
109 %%EOF 111 %%EOF
qpdf/test_driver.cc
@@ -3097,6 +3097,39 @@ static void test_81(QPDF&amp; pdf, char const* arg2) @@ -3097,6 +3097,39 @@ static void test_81(QPDF&amp; pdf, char const* arg2)
3097 } 3097 }
3098 } 3098 }
3099 3099
  3100 +static void test_82(QPDF& pdf, char const* arg2)
  3101 +{
  3102 + // Exercise compound test methods QPDFObjectHandle::isNameAndEquals,
  3103 + // isDictionaryOfType and isStreamOfType
  3104 + auto name = QPDFObjectHandle::newName("/Marvin");
  3105 + auto str = QPDFObjectHandle::newString("/Marvin");
  3106 + assert(name.isNameAndEquals("/Marvin"));
  3107 + assert(! name.isNameAndEquals("Marvin"));
  3108 + assert(! str.isNameAndEquals("/Marvin"));
  3109 + auto dict = QPDFObjectHandle::parse("<</A 1 /Type /Test /Subtype /Marvin>>");
  3110 + assert(dict.isDictionaryOfType( "/Test", ""));
  3111 + assert(dict.isDictionaryOfType("/Test"));
  3112 + assert(dict.isDictionaryOfType("/Test", "/Marvin"));
  3113 + assert(! dict.isDictionaryOfType("/Test2", ""));
  3114 + assert(! dict.isDictionaryOfType("/Test2", "/Marvin"));
  3115 + assert(! dict.isDictionaryOfType("/Test", "/M"));
  3116 + assert(! dict.isDictionaryOfType("", "/Marvin"));
  3117 + assert(! dict.isDictionaryOfType("", ""));
  3118 + dict = QPDFObjectHandle::parse("<</A 1 /Type null /Subtype /Marvin>>");
  3119 + assert(! dict.isDictionaryOfType("/Test"));
  3120 + dict = QPDFObjectHandle::parse("<</A 1 /Type (Test) /Subtype /Marvin>>");
  3121 + assert(! dict.isDictionaryOfType("Test"));
  3122 + dict = QPDFObjectHandle::parse("<</A 1 /Type /Test /Subtype (Marvin)>>");
  3123 + assert(! dict.isDictionaryOfType("Test"));
  3124 + dict = QPDFObjectHandle::parse("<</A 1 /Subtype /Marvin>>");
  3125 + assert(! dict.isDictionaryOfType("/Test", "Marvin"));
  3126 + auto stream = pdf.getObjectByID(1,0);
  3127 + assert(stream.isStreamOfType("/ObjStm"));
  3128 + assert(! stream.isStreamOfType("/Test"));
  3129 + assert(! pdf.getObjectByID(2,0).isStreamOfType("/Pages"));
  3130 +}
  3131 +
  3132 +
3100 void runtest(int n, char const* filename1, char const* arg2) 3133 void runtest(int n, char const* filename1, char const* arg2)
3101 { 3134 {
3102 // Most tests here are crafted to work on specific files. Look at 3135 // Most tests here are crafted to work on specific files. Look at
@@ -3210,7 +3243,7 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -3210,7 +3243,7 @@ void runtest(int n, char const* filename1, char const* arg2)
3210 {68, test_68}, {69, test_69}, {70, test_70}, {71, test_71}, 3243 {68, test_68}, {69, test_69}, {70, test_70}, {71, test_71},
3211 {72, test_72}, {73, test_73}, {74, test_74}, {75, test_75}, 3244 {72, test_72}, {73, test_73}, {74, test_74}, {75, test_75},
3212 {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79}, 3245 {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79},
3213 - {80, test_80}, {81, test_81}, 3246 + {80, test_80}, {81, test_81}, {82, test_82},
3214 }; 3247 };
3215 3248
3216 auto fn = test_functions.find(n); 3249 auto fn = test_functions.find(n);