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 | 365 | QPDF_DLL |
| 366 | 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 | 384 | // Public factory methods |
| 369 | 385 | |
| 370 | 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 | 676 | QPDF_BOOL qpdf_oh_is_indirect(qpdf_data qpdf, qpdf_oh oh); |
| 677 | 677 | QPDF_DLL |
| 678 | 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 | 688 | QPDF_DLL |
| 680 | 689 | enum qpdf_object_type_e qpdf_oh_get_type_code(qpdf_data qpdf, qpdf_oh oh); |
| 681 | 690 | QPDF_DLL | ... | ... |
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -497,6 +497,34 @@ QPDFObjectHandle::isScalar() |
| 497 | 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 | 528 | // Bool accessors |
| 501 | 529 | |
| 502 | 530 | bool | ... | ... |
libqpdf/qpdf-c.cc
| ... | ... | @@ -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 | 1172 | qpdf_object_type_e qpdf_oh_get_type_code(qpdf_data qpdf, qpdf_oh oh) |
| 1152 | 1173 | { |
| 1153 | 1174 | return do_with_oh<qpdf_object_type_e>( | ... | ... |
qpdf/qpdf-ctest.c
| ... | ... | @@ -694,12 +694,16 @@ static void test25(char const* infile, |
| 694 | 694 | (strcmp(qpdf_oh_get_utf8_value(qpdf, p_string), "3\xc3\xb7") == 0) && |
| 695 | 695 | (strcmp(qpdf_oh_unparse_binary(qpdf, p_string), "<33f7>") == 0)); |
| 696 | 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 | 698 | assert(strcmp(qpdf_oh_get_type_name(qpdf, p_string), "string") == 0); |
| 698 | 699 | assert(qpdf_oh_is_dictionary(qpdf, p_dict)); |
| 699 | 700 | qpdf_oh p_five = qpdf_oh_get_key(qpdf, p_dict, "/Four"); |
| 700 | 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 | 703 | assert(qpdf_oh_is_or_has_name( |
| 702 | 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 | 707 | assert(qpdf_oh_is_null(qpdf, p_null)); |
| 704 | 708 | assert(qpdf_oh_get_type_code(qpdf, p_null) == ot_null); |
| 705 | 709 | assert(strcmp(qpdf_oh_get_type_name(qpdf, p_null), "null") == 0); |
| ... | ... | @@ -710,10 +714,17 @@ static void test25(char const* infile, |
| 710 | 714 | qpdf_oh_erase_item(qpdf, parsed, 4); |
| 711 | 715 | qpdf_oh_insert_item( |
| 712 | 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 | 719 | qpdf_oh new_dict = qpdf_oh_get_array_item(qpdf, parsed, 2); |
| 715 | 720 | assert(qpdf_oh_has_key(qpdf, new_dict, "/A")); |
| 716 | 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 | 728 | qpdf_oh new_array = qpdf_oh_new_array(qpdf); |
| 718 | 729 | qpdf_oh_replace_or_remove_key( |
| 719 | 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 | 474 | qpdf-c called qpdf_oh_is_stream 0 |
| 475 | 475 | qpdf-c called qpdf_oh_is_indirect 0 |
| 476 | 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 | 479 | qpdf-c called qpdf_oh_get_type_code 0 |
| 478 | 480 | qpdf-c called qpdf_oh_get_type_name 0 |
| 479 | 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 | 1913 | show_ntests(); |
| 1914 | 1914 | # ---------- |
| 1915 | 1915 | $td->notify("--- Type checks ---"); |
| 1916 | -$n_tests += 4; | |
| 1916 | +$n_tests += 5; | |
| 1917 | 1917 | # Whenever object-types.pdf is edited, object-types-os.pdf should be |
| 1918 | 1918 | # regenerated. |
| 1919 | 1919 | $td->runtest("ensure object-types-os is up-to-date", |
| ... | ... | @@ -1937,6 +1937,10 @@ $td->runtest("type checks with object streams", |
| 1937 | 1937 | {$td->FILE => "object-types-os.out", |
| 1938 | 1938 | $td->EXIT_STATUS => 0}, |
| 1939 | 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 | 1946 | $td->notify("--- Coalesce contents ---"); | ... | ... |
qpdf/qtest/qpdf/c-object-handle-creation-out.pdf
| ... | ... | @@ -18,6 +18,8 @@ |
| 18 | 18 | ] |
| 19 | 19 | /C << |
| 20 | 20 | >> |
| 21 | + /Subtype /Marvin | |
| 22 | + /Type /Test | |
| 21 | 23 | >> |
| 22 | 24 | /Type /Catalog |
| 23 | 25 | >> |
| ... | ... | @@ -93,17 +95,17 @@ xref |
| 93 | 95 | 0 8 |
| 94 | 96 | 0000000000 65535 f |
| 95 | 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 | 104 | trailer << |
| 103 | 105 | /Root 1 0 R |
| 104 | 106 | /Size 8 |
| 105 | 107 | /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] |
| 106 | 108 | >> |
| 107 | 109 | startxref |
| 108 | -808 | |
| 110 | +845 | |
| 109 | 111 | %%EOF | ... | ... |
qpdf/test_driver.cc
| ... | ... | @@ -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 | 3133 | void runtest(int n, char const* filename1, char const* arg2) |
| 3101 | 3134 | { |
| 3102 | 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 | 3243 | {68, test_68}, {69, test_69}, {70, test_70}, {71, test_71}, |
| 3211 | 3244 | {72, test_72}, {73, test_73}, {74, test_74}, {75, test_75}, |
| 3212 | 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 | 3249 | auto fn = test_functions.find(n); | ... | ... |