Commit 930eade6d36060b1bdc5934e74952fa6bcfdb07f
1 parent
65ef0bf3
Fix omissions in text appearance generation
When generating appearance streams for variable text annotations, properly handle the cases of there being no appearance dictionary, no appearance stream, or an appearance stream with no BMC..EMC marker.
Showing
7 changed files
with
10225 additions
and
13 deletions
ChangeLog
| 1 | 2019-01-20 Jay Berkenbilt <ejb@ql.org> | 1 | 2019-01-20 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * When generating appearance streams for variable text | ||
| 4 | + annotations, properly handle the cases of there being no | ||
| 5 | + appearance dictionary, no appearance stream, or an appearance | ||
| 6 | + stream with no BMC..EMC marker. | ||
| 7 | + | ||
| 3 | * When flattening annotations, remove annotations from the file | 8 | * When flattening annotations, remove annotations from the file |
| 4 | that don't have appearance streams. These were previously being | 9 | that don't have appearance streams. These were previously being |
| 5 | preserved, but since they are invisible, there is no reason to | 10 | preserved, but since they are invisible, there is no reason to |
libqpdf/QPDFFormFieldObjectHelper.cc
| @@ -506,6 +506,7 @@ class ValueSetter: public QPDFObjectHandle::TokenFilter | @@ -506,6 +506,7 @@ class ValueSetter: public QPDFObjectHandle::TokenFilter | ||
| 506 | { | 506 | { |
| 507 | } | 507 | } |
| 508 | virtual void handleToken(QPDFTokenizer::Token const&); | 508 | virtual void handleToken(QPDFTokenizer::Token const&); |
| 509 | + virtual void handleEOF(); | ||
| 509 | void writeAppearance(); | 510 | void writeAppearance(); |
| 510 | 511 | ||
| 511 | private: | 512 | private: |
| @@ -515,6 +516,7 @@ class ValueSetter: public QPDFObjectHandle::TokenFilter | @@ -515,6 +516,7 @@ class ValueSetter: public QPDFObjectHandle::TokenFilter | ||
| 515 | double tf; | 516 | double tf; |
| 516 | QPDFObjectHandle::Rectangle bbox; | 517 | QPDFObjectHandle::Rectangle bbox; |
| 517 | enum { st_top, st_bmc, st_emc, st_end } state; | 518 | enum { st_top, st_bmc, st_emc, st_end } state; |
| 519 | + bool replaced; | ||
| 518 | }; | 520 | }; |
| 519 | 521 | ||
| 520 | ValueSetter::ValueSetter(std::string const& DA, std::string const& V, | 522 | ValueSetter::ValueSetter(std::string const& DA, std::string const& V, |
| @@ -525,7 +527,8 @@ ValueSetter::ValueSetter(std::string const& DA, std::string const& V, | @@ -525,7 +527,8 @@ ValueSetter::ValueSetter(std::string const& DA, std::string const& V, | ||
| 525 | opt(opt), | 527 | opt(opt), |
| 526 | tf(tf), | 528 | tf(tf), |
| 527 | bbox(bbox), | 529 | bbox(bbox), |
| 528 | - state(st_top) | 530 | + state(st_top), |
| 531 | + replaced(false) | ||
| 529 | { | 532 | { |
| 530 | } | 533 | } |
| 531 | 534 | ||
| @@ -575,8 +578,22 @@ ValueSetter::handleToken(QPDFTokenizer::Token const& token) | @@ -575,8 +578,22 @@ ValueSetter::handleToken(QPDFTokenizer::Token const& token) | ||
| 575 | } | 578 | } |
| 576 | } | 579 | } |
| 577 | 580 | ||
| 578 | -void ValueSetter::writeAppearance() | 581 | +void |
| 582 | +ValueSetter::handleEOF() | ||
| 579 | { | 583 | { |
| 584 | + if (! this->replaced) | ||
| 585 | + { | ||
| 586 | + QTC::TC("qpdf", "QPDFFormFieldObjectHelper replaced BMC at EOF"); | ||
| 587 | + write("/Tx BMC\n"); | ||
| 588 | + writeAppearance(); | ||
| 589 | + } | ||
| 590 | +} | ||
| 591 | + | ||
| 592 | +void | ||
| 593 | +ValueSetter::writeAppearance() | ||
| 594 | +{ | ||
| 595 | + this->replaced = true; | ||
| 596 | + | ||
| 580 | // This code does not take quadding into consideration because | 597 | // This code does not take quadding into consideration because |
| 581 | // doing so requires font metric information, which we don't | 598 | // doing so requires font metric information, which we don't |
| 582 | // have in many cases. | 599 | // have in many cases. |
| @@ -768,6 +785,29 @@ QPDFFormFieldObjectHelper::generateTextAppearance( | @@ -768,6 +785,29 @@ QPDFFormFieldObjectHelper::generateTextAppearance( | ||
| 768 | QPDFAnnotationObjectHelper& aoh) | 785 | QPDFAnnotationObjectHelper& aoh) |
| 769 | { | 786 | { |
| 770 | QPDFObjectHandle AS = aoh.getAppearanceStream("/N"); | 787 | QPDFObjectHandle AS = aoh.getAppearanceStream("/N"); |
| 788 | + if (AS.isNull()) | ||
| 789 | + { | ||
| 790 | + QTC::TC("qpdf", "QPDFFormFieldObjectHelper create AS from scratch"); | ||
| 791 | + QPDFObjectHandle::Rectangle rect = aoh.getRect(); | ||
| 792 | + QPDFObjectHandle::Rectangle bbox( | ||
| 793 | + 0, 0, rect.urx - rect.llx, rect.ury - rect.lly); | ||
| 794 | + QPDFObjectHandle dict = QPDFObjectHandle::parse( | ||
| 795 | + "<< /Resources << /ProcSet [ /PDF /Text ] >>" | ||
| 796 | + " /Type /XObject /Subtype /Form >>"); | ||
| 797 | + dict.replaceKey("/BBox", QPDFObjectHandle::newFromRectangle(bbox)); | ||
| 798 | + AS = QPDFObjectHandle::newStream( | ||
| 799 | + this->oh.getOwningQPDF(), "/Tx BMC\nEMC\n"); | ||
| 800 | + AS.replaceDict(dict); | ||
| 801 | + QPDFObjectHandle AP = aoh.getAppearanceDictionary(); | ||
| 802 | + if (AP.isNull()) | ||
| 803 | + { | ||
| 804 | + QTC::TC("qpdf", "QPDFFormFieldObjectHelper create AP from scratch"); | ||
| 805 | + aoh.getObjectHandle().replaceKey( | ||
| 806 | + "/AP", QPDFObjectHandle::newDictionary()); | ||
| 807 | + AP = aoh.getAppearanceDictionary(); | ||
| 808 | + } | ||
| 809 | + AP.replaceKey("/N", AS); | ||
| 810 | + } | ||
| 771 | if (! AS.isStream()) | 811 | if (! AS.isStream()) |
| 772 | { | 812 | { |
| 773 | aoh.getObjectHandle().warnIfPossible( | 813 | aoh.getObjectHandle().warnIfPossible( |
qpdf/qpdf.testcov
| @@ -422,3 +422,6 @@ qpdf bytes fallback warning 0 | @@ -422,3 +422,6 @@ qpdf bytes fallback warning 0 | ||
| 422 | qpdf invalid utf-8 in auto 0 | 422 | qpdf invalid utf-8 in auto 0 |
| 423 | qpdf input password hex-bytes 0 | 423 | qpdf input password hex-bytes 0 |
| 424 | QPDFPageDocumentHelper ignore annotation with no appearance 0 | 424 | QPDFPageDocumentHelper ignore annotation with no appearance 0 |
| 425 | +QPDFFormFieldObjectHelper create AS from scratch 0 | ||
| 426 | +QPDFFormFieldObjectHelper create AP from scratch 0 | ||
| 427 | +QPDFFormFieldObjectHelper replaced BMC at EOF 0 |
qpdf/qtest/qpdf.test
| @@ -261,18 +261,29 @@ $td->runtest("compare files", | @@ -261,18 +261,29 @@ $td->runtest("compare files", | ||
| 261 | show_ntests(); | 261 | show_ntests(); |
| 262 | # ---------- | 262 | # ---------- |
| 263 | $td->notify("--- Appearance Streams ---"); | 263 | $td->notify("--- Appearance Streams ---"); |
| 264 | -$n_tests += 4; | 264 | +$n_tests += 8; |
| 265 | 265 | ||
| 266 | -$td->runtest("generate appearances and flatten", | ||
| 267 | - {$td->COMMAND => | ||
| 268 | - "qpdf --qdf --no-original-object-ids --static-id" . | ||
| 269 | - " --generate-appearances --flatten-annotations=all" . | ||
| 270 | - " need-appearances.pdf a.pdf"}, | ||
| 271 | - {$td->STRING => "", $td->EXIT_STATUS => 0}, | ||
| 272 | - $td->NORMALIZE_NEWLINES); | ||
| 273 | -$td->runtest("compare files", | ||
| 274 | - {$td->FILE => "a.pdf"}, | ||
| 275 | - {$td->FILE => "appearances-a.pdf"}); | 266 | +foreach my $f ('need-appearances', |
| 267 | + 'need-appearances-more', | ||
| 268 | + 'need-appearances-more2') | ||
| 269 | +{ | ||
| 270 | + $td->runtest("generate appearances and flatten ($f)", | ||
| 271 | + {$td->COMMAND => | ||
| 272 | + "qpdf --qdf --no-original-object-ids --static-id" . | ||
| 273 | + " --generate-appearances --flatten-annotations=all" . | ||
| 274 | + " $f.pdf a.pdf"}, | ||
| 275 | + {$td->STRING => "", $td->EXIT_STATUS => 0}, | ||
| 276 | + $td->NORMALIZE_NEWLINES); | ||
| 277 | + my $exp = 'appearances-a'; | ||
| 278 | + if ($f =~ m/appearances(-.*)$/) | ||
| 279 | + { | ||
| 280 | + $exp .= $1; | ||
| 281 | + } | ||
| 282 | + $exp .= '.pdf'; | ||
| 283 | + $td->runtest("compare files", | ||
| 284 | + {$td->FILE => "a.pdf"}, | ||
| 285 | + {$td->FILE => $exp}); | ||
| 286 | +} | ||
| 276 | 287 | ||
| 277 | $td->runtest("more choices", | 288 | $td->runtest("more choices", |
| 278 | {$td->COMMAND => | 289 | {$td->COMMAND => |
qpdf/qtest/qpdf/appearances-a-more.pdf
0 โ 100644
No preview for this file type
qpdf/qtest/qpdf/appearances-a-more2.pdf
0 โ 100644
No preview for this file type
qpdf/qtest/qpdf/need-appearances-more2.pdf
0 โ 100644
No preview for this file type