Commit 8593b9fdf74e68099a5fe2aa0b6d0b6320781a2d
Committed by
Jay Berkenbilt
1 parent
37071065
Add new convenience methods QPDFObjectHandle::isNameAndEquals, etc
Add methods isNameAndEquals, isDictionaryOfType, isStreamOfType
Showing
9 changed files
with
136 additions
and
10 deletions
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->runtest("progress report on small file", | @@ -1913,7 +1913,7 @@ $td->runtest("progress report on small file", | ||
| 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->runtest("type checks with object streams", | @@ -1937,6 +1937,10 @@ $td->runtest("type checks with object streams", | ||
| 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& pdf, char const* arg2) | @@ -3097,6 +3097,39 @@ static void test_81(QPDF& 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); |