From 89bd5627f490a7bfa5c6bd44a8fca98eb9a9ccb5 Mon Sep 17 00:00:00 2001 From: m-holger Date: Sat, 17 Jan 2026 13:40:17 +0000 Subject: [PATCH] Add CI test for use of a copy of a destroyed QPDFJob object --- manual/release-notes.rst | 5 +++-- qpdf/qtest/qpdf/test102.pdf | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qpdf/qtest/qpdfjob.test | 10 +++++++++- qpdf/test_driver.cc | 58 ++++++++++++++++++++++++++++++++++++---------------------- 4 files changed, 149 insertions(+), 25 deletions(-) create mode 100644 qpdf/qtest/qpdf/test102.pdf diff --git a/manual/release-notes.rst b/manual/release-notes.rst index 4aa56c5..1bf220a 100644 --- a/manual/release-notes.rst +++ b/manual/release-notes.rst @@ -17,8 +17,9 @@ more detail. 12.3.1: not yet released - Bug fixes - - Fix a bug causing ``QPDFJob::createQPDF`` to fail if called - from pikepdf. + - Fix failure of ``QPDFJob::run`` and ``QPDFJob::createQPDF`` when + called with a copy of a destroyed ``QPDFJob`` object. This affects + using the job interface from pikepdf. 12.3.0: January 10, 2026 - Release changes diff --git a/qpdf/qtest/qpdf/test102.pdf b/qpdf/qtest/qpdf/test102.pdf new file mode 100644 index 0000000..7b33f19 --- /dev/null +++ b/qpdf/qtest/qpdf/test102.pdf @@ -0,0 +1,101 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +4 0 obj +<< + /Length 5 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +44 +endobj + +%% Original object ID: 6 0 +6 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 5 0 +7 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 8 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000242 00000 n +0000000484 00000 n +0000000583 00000 n +0000000629 00000 n +0000000774 00000 n +trailer << + /Root 1 0 R + /Size 8 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] +>> +startxref +809 +%%EOF diff --git a/qpdf/qtest/qpdfjob.test b/qpdf/qtest/qpdfjob.test index f1ef942..d2187a1 100644 --- a/qpdf/qtest/qpdfjob.test +++ b/qpdf/qtest/qpdfjob.test @@ -44,7 +44,7 @@ my @good_json = ( ["underlay-overlay-password", ""], ["misc-options", ""], ); -my $n_tests = 11 + scalar(@bad_json) + (2 * scalar(@good_json)); +my $n_tests = 13 + scalar(@bad_json) + (2 * scalar(@good_json)); foreach my $i (@bad_json) @@ -130,5 +130,13 @@ else {$td->FILE => "qpdfjob-ctest-wide.pdf", $td->EXIT_STATUS => 0}); } +$td->runtest("Copied QPDFJob", + {$td->COMMAND => "test_driver 102 -"}, + {$td->STRING => "test 102 done\n", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +$td->runtest("check output", + {$td->FILE => "a.pdf"}, + {$td->FILE => "test102.pdf"}); + cleanup(); $td->report($n_tests); diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index d9736d9..cd7c355 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -3584,6 +3584,20 @@ test_101(QPDF& pdf, char const* arg2) test_helper_throws([](QPDF& q) { (void)QPDFPageLabelDocumentHelper::get(q); }); } +static void +test_102(QPDF& pdf, char const* arg2) +{ + // Test using a copy of a QPDFJob object after the original is destroyed + + auto j = std::make_unique(); + j->initializeFromJson( + R"({"inputFile": "minimal.pdf", "outputFile": "a.pdf", "qdf": "", "staticId": ""})"); + QPDFJob j2 = *j; + j = nullptr; + auto q = j2.createQPDF(); + j2.writeQPDF(*q); +} + void runtest(int n, char const* filename1, char const* arg2) { @@ -3591,7 +3605,7 @@ runtest(int n, char const* filename1, char const* arg2) // the test suite to see how the test is invoked to find the file // that the test is supposed to operate on. - std::set ignore_filename = {61, 62, 81, 83, 84, 85, 86, 87, 92, 95, 96, 101}; + std::set ignore_filename = {61, 62, 81, 83, 84, 85, 86, 87, 92, 95, 96, 101, 102}; if (n == 0) { // Throw in some random test cases that don't fit anywhere @@ -3665,27 +3679,27 @@ runtest(int n, char const* filename1, char const* arg2) } std::map test_functions = { - {0, test_0_1}, {1, test_0_1}, {2, test_2}, {3, test_3}, {4, test_4}, - {5, test_5}, {6, test_6}, {7, test_7}, {8, test_8}, {9, test_9}, - {10, test_10}, {11, test_11}, {12, test_12}, {13, test_13}, {14, test_14}, - {15, test_15}, {16, test_16}, {17, test_17}, {18, test_18}, {19, test_19}, - {20, test_20}, {21, test_21}, {22, test_22}, {23, test_23}, {24, test_24}, - {25, test_25}, {26, test_26}, {27, test_27}, {28, test_28}, {29, test_29}, - {30, test_30}, {31, test_31}, {32, test_32}, {33, test_33}, {34, test_34}, - {35, test_35}, {36, test_36}, {37, test_37}, {38, test_38}, {39, test_39}, - {40, test_40}, {41, test_41}, {42, test_42}, {43, test_43}, {44, test_44}, - {45, test_45}, {46, test_46}, {47, test_47}, {48, test_48}, {49, test_49}, - {50, test_50}, {51, test_51}, {52, test_52}, {53, test_53}, {54, test_54}, - {55, test_55}, {56, test_56}, {57, test_57}, {58, test_58}, {59, test_59}, - {60, test_60}, {61, test_61}, {62, test_62}, {63, test_63}, {64, test_64}, - {65, test_65}, {66, test_66}, {67, test_67}, {68, test_68}, {69, test_69}, - {70, test_70}, {71, test_71}, {72, test_72}, {73, test_73}, {74, test_74}, - {75, test_75}, {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79}, - {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83}, {84, test_84}, - {85, test_85}, {86, test_86}, {87, test_87}, {88, test_88}, {89, test_89}, - {90, test_90}, {91, test_91}, {92, test_92}, {93, test_93}, {94, test_94}, - {95, test_95}, {96, test_96}, {97, test_97}, {98, test_98}, {99, test_99}, - {100, test_100}, {101, test_101}}; + {0, test_0_1}, {1, test_0_1}, {2, test_2}, {3, test_3}, {4, test_4}, + {5, test_5}, {6, test_6}, {7, test_7}, {8, test_8}, {9, test_9}, + {10, test_10}, {11, test_11}, {12, test_12}, {13, test_13}, {14, test_14}, + {15, test_15}, {16, test_16}, {17, test_17}, {18, test_18}, {19, test_19}, + {20, test_20}, {21, test_21}, {22, test_22}, {23, test_23}, {24, test_24}, + {25, test_25}, {26, test_26}, {27, test_27}, {28, test_28}, {29, test_29}, + {30, test_30}, {31, test_31}, {32, test_32}, {33, test_33}, {34, test_34}, + {35, test_35}, {36, test_36}, {37, test_37}, {38, test_38}, {39, test_39}, + {40, test_40}, {41, test_41}, {42, test_42}, {43, test_43}, {44, test_44}, + {45, test_45}, {46, test_46}, {47, test_47}, {48, test_48}, {49, test_49}, + {50, test_50}, {51, test_51}, {52, test_52}, {53, test_53}, {54, test_54}, + {55, test_55}, {56, test_56}, {57, test_57}, {58, test_58}, {59, test_59}, + {60, test_60}, {61, test_61}, {62, test_62}, {63, test_63}, {64, test_64}, + {65, test_65}, {66, test_66}, {67, test_67}, {68, test_68}, {69, test_69}, + {70, test_70}, {71, test_71}, {72, test_72}, {73, test_73}, {74, test_74}, + {75, test_75}, {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79}, + {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83}, {84, test_84}, + {85, test_85}, {86, test_86}, {87, test_87}, {88, test_88}, {89, test_89}, + {90, test_90}, {91, test_91}, {92, test_92}, {93, test_93}, {94, test_94}, + {95, test_95}, {96, test_96}, {97, test_97}, {98, test_98}, {99, test_99}, + {100, test_100}, {101, test_101}, {102, test_102}}; auto fn = test_functions.find(n); if (fn == test_functions.end()) { -- libgit2 0.21.4