2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 02:49:00 +00:00

Add basic appearance stream generation

This commit is contained in:
Jay Berkenbilt 2019-01-03 19:09:00 -05:00
parent 02281632cc
commit 158156d506
20 changed files with 31076 additions and 394 deletions

View File

@ -1,5 +1,28 @@
2019-01-03 Jay Berkenbilt <ejb@ql.org>
* Fix behavior of form field value setting to handle the following
cases:
- Strings are always written as UTF-16
- Check boxes and radio buttons are handled properly with
synchronization of values and appearance states
* Define constants in qpdf/Constants.h for interpretation of
annotation and form field flags
* Add QPDFAnnotationObjectHelper::getFlags
* Add many new methods to QPDFFormFieldObjectHelper for querying
flags and field types
* Add new methods for appearance stream generation. See comments
in QPDFFormFieldObjectHelper.hh for generateAppearance() for a
description of limitations.
- QPDFAcroFormDocumentHelper::generateAppearancesIfNeeded
- QPDFFormFieldObjectHelper::generateAppearance
* Bug fix: when writing form field values, always write string
values encoded as UTF-16.
* Add method QUtil::utf8_to_ascii, which returns an ASCII string
for a UTF-8 string, replacing out-of-range characters with a
specified substitute.

View File

@ -157,6 +157,16 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
QPDF_DLL
void setNeedAppearances(bool);
// If /NeedAppearances is false, do nothing. Otherwise generate
// appearance streams for all widget annotations that need them.
// See comments in QPDFFormFieldObjectHelper.hh for
// generateAppearance for limitations. For checkbox and radio
// button fields, this code ensures that appearance state is
// consistent with the field's value and uses any pre-existing
// appearance streams.
QPDF_DLL
void generateAppearancesIfNeeded();
private:
void analyze();
void traverseField(QPDFObjectHandle field,

View File

@ -31,6 +31,8 @@
#include <qpdf/DLL.h>
#include <vector>
class QPDFAnnotationObjectHelper;
class QPDFFormFieldObjectHelper: public QPDFObjectHelper
{
public:
@ -179,9 +181,21 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper
QPDF_DLL
void setV(std::string const& utf8_value, bool need_appearances = true);
// Update the appearance stream for this field. Note that qpdf's
// abilitiy to generate appearance streams is limited. We only
// generate appearance streams for streams of type text or choice.
// The appearance uses the default parameters provided in the
// file, and it only supports ASCII characters. Quadding is
// currently ignored. While this functionality is limited, it
// should do a decent job on properly constructed PDF files when
// field values are restricted to ASCII characters.
QPDF_DLL
void generateAppearance(QPDFAnnotationObjectHelper&);
private:
void setRadioButtonValue(QPDFObjectHandle name);
void setCheckBoxValue(bool value);
void generateTextAppearance(QPDFAnnotationObjectHelper&);
class Members
{

View File

@ -285,3 +285,48 @@ QPDFAcroFormDocumentHelper::setNeedAppearances(bool val)
acroform.removeKey("/NeedAppearances");
}
}
void
QPDFAcroFormDocumentHelper::generateAppearancesIfNeeded()
{
if (! getNeedAppearances())
{
return;
}
QPDFPageDocumentHelper pdh(this->qpdf);
std::vector<QPDFPageObjectHelper> pages = pdh.getAllPages();
for (std::vector<QPDFPageObjectHelper>::iterator page_iter =
pages.begin();
page_iter != pages.end(); ++page_iter)
{
std::vector<QPDFAnnotationObjectHelper> annotations =
getWidgetAnnotationsForPage(*page_iter);
for (std::vector<QPDFAnnotationObjectHelper>::iterator annot_iter =
annotations.begin();
annot_iter != annotations.end(); ++annot_iter)
{
QPDFAnnotationObjectHelper& aoh = *annot_iter;
QPDFFormFieldObjectHelper ffh =
getFieldForAnnotation(aoh);
if (ffh.getFieldType() == "/Btn")
{
// Rather than generating appearances for button
// fields, rely on what's already there. Just make
// sure /AS is consistent with /V, which we can do by
// resetting the value of the field back to itself.
// This code is referenced in a comment in
// QPDFFormFieldObjectHelper::generateAppearance.
if (ffh.isRadioButton() || ffh.isCheckbox())
{
ffh.setV(ffh.getValue());
}
}
else
{
ffh.generateAppearance(aoh);
}
}
}
setNeedAppearances(false);
}

View File

@ -1,6 +1,10 @@
#include <qpdf/QPDFFormFieldObjectHelper.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
#include <qpdf/QPDFAnnotationObjectHelper.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/Pl_QPDFTokenizer.hh>
#include <stdlib.h>
QPDFFormFieldObjectHelper::Members::~Members()
{
@ -313,7 +317,15 @@ QPDFFormFieldObjectHelper::setV(
}
return;
}
setFieldAttribute("/V", value);
if (value.isString())
{
setFieldAttribute(
"/V", QPDFObjectHandle::newUnicodeString(value.getUTF8Value()));
}
else
{
setFieldAttribute("/V", value);
}
if (need_appearances)
{
QPDF* qpdf = this->oh.getOwningQPDF();
@ -470,3 +482,290 @@ QPDFFormFieldObjectHelper::setCheckBoxValue(bool value)
QTC::TC("qpdf", "QPDFFormFieldObjectHelper set checkbox AS");
annot.replaceKey("/AS", name);
}
void
QPDFFormFieldObjectHelper::generateAppearance(QPDFAnnotationObjectHelper& aoh)
{
std::string ft = getFieldType();
// Ignore field types we don't know how to generate appearances
// for. Button fields don't really need them -- see code in
// QPDFAcroFormDocumentHelper::generateAppearancesIfNeeded.
if ((ft == "/Tx") || (ft == "/Ch"))
{
generateTextAppearance(aoh);
}
}
class ValueSetter: public QPDFObjectHandle::TokenFilter
{
public:
ValueSetter(std::string const& DA, std::string const& V,
std::vector<std::string> const& opt, double tf,
QPDFObjectHandle::Rectangle const& bbox);
virtual ~ValueSetter()
{
}
virtual void handleToken(QPDFTokenizer::Token const&);
void writeAppearance();
private:
std::string DA;
std::string V;
std::vector<std::string> opt;
double tf;
QPDFObjectHandle::Rectangle bbox;
enum { st_top, st_bmc, st_emc, st_end } state;
};
ValueSetter::ValueSetter(std::string const& DA, std::string const& V,
std::vector<std::string> const& opt, double tf,
QPDFObjectHandle::Rectangle const& bbox) :
DA(DA),
V(V),
opt(opt),
tf(tf),
bbox(bbox),
state(st_top)
{
}
void
ValueSetter::handleToken(QPDFTokenizer::Token const& token)
{
QPDFTokenizer::token_type_e ttype = token.getType();
std::string value = token.getValue();
bool do_replace = false;
switch (state)
{
case st_top:
writeToken(token);
if ((ttype == QPDFTokenizer::tt_word) && (value == "BMC"))
{
state = st_bmc;
}
break;
case st_bmc:
if ((ttype == QPDFTokenizer::tt_space) ||
(ttype == QPDFTokenizer::tt_comment))
{
writeToken(token);
}
else
{
state = st_emc;
}
// fall through to emc
case st_emc:
if ((ttype == QPDFTokenizer::tt_word) && (value == "EMC"))
{
do_replace = true;
state = st_end;
}
break;
case st_end:
writeToken(token);
break;
}
if (do_replace)
{
writeAppearance();
}
}
void ValueSetter::writeAppearance()
{
// This code does not take quadding into consideration because
// doing so requires font metric information, which we don't
// have in many cases.
double tfh = 1.2 * tf;
int dx = 1;
// Write one or more lines, centered vertically, possibly with
// one row highlighted.
size_t max_rows = static_cast<size_t>((bbox.ury - bbox.lly) / tfh);
bool highlight = false;
size_t highlight_idx = 0;
std::vector<std::string> lines;
if (opt.empty() || (max_rows < 2))
{
lines.push_back(V);
}
else
{
// Figure out what rows to write
size_t nopt = opt.size();
size_t found_idx = 0;
bool found = false;
for (found_idx = 0; found_idx < nopt; ++found_idx)
{
if (opt.at(found_idx) == V)
{
found = true;
break;
}
}
if (found)
{
// Try to make the found item the second one, but
// adjust for under/overflow.
int wanted_first = found_idx - 1;
int wanted_last = found_idx + max_rows - 2;
QTC::TC("qpdf", "QPDFFormFieldObjectHelper list found");
while (wanted_first < 0)
{
QTC::TC("qpdf", "QPDFFormFieldObjectHelper list first too low");
++wanted_first;
++wanted_last;
}
while (wanted_last >= static_cast<int>(nopt))
{
QTC::TC("qpdf", "QPDFFormFieldObjectHelper list last too high");
if (wanted_first > 0)
{
--wanted_first;
}
--wanted_last;
}
highlight = true;
highlight_idx = found_idx - wanted_first;
for (int i = wanted_first; i <= wanted_last; ++i)
{
lines.push_back(opt.at(i));
}
}
else
{
QTC::TC("qpdf", "QPDFFormFieldObjectHelper list not found");
// include our value and the first n-1 rows
highlight_idx = 0;
highlight = true;
lines.push_back(V);
for (size_t i = 0; ((i < nopt) && (i < (max_rows - 1))); ++i)
{
lines.push_back(opt.at(i));
}
}
}
// Write the lines centered vertically, highlighting if needed
size_t nlines = lines.size();
double dy = bbox.ury - ((bbox.ury - bbox.lly - (nlines * tfh)) / 2.0);
write(DA + "\nq\n");
if (highlight)
{
write("q\n0.85 0.85 0.85 rg\n" +
QUtil::int_to_string(bbox.llx) + " " +
QUtil::double_to_string(bbox.lly + dy -
(tfh * (highlight_idx + 1))) + " " +
QUtil::int_to_string(bbox.urx - bbox.llx) + " " +
QUtil::double_to_string(tfh) +
" re f\nQ\n");
}
dy += 0.2 * tf;
for (size_t i = 0; i < nlines; ++i)
{
dy -= tfh;
write("BT\n" +
QUtil::int_to_string(bbox.llx + dx) + " " +
QUtil::double_to_string(bbox.lly + dy) + " Td\n" +
QPDFObjectHandle::newString(lines.at(i)).unparse() +
" Tj\nET\n");
}
write("Q\nEMC");
}
class TfFinder: public QPDFObjectHandle::TokenFilter
{
public:
TfFinder();
virtual ~TfFinder()
{
}
virtual void handleToken(QPDFTokenizer::Token const&);
double getTf();
private:
double tf;
double last_num;
};
TfFinder::TfFinder() :
tf(11.0),
last_num(0.0)
{
}
void
TfFinder::handleToken(QPDFTokenizer::Token const& token)
{
QPDFTokenizer::token_type_e ttype = token.getType();
std::string value = token.getValue();
switch (ttype)
{
case QPDFTokenizer::tt_integer:
case QPDFTokenizer::tt_real:
last_num = strtod(value.c_str(), 0);
break;
case QPDFTokenizer::tt_word:
if ((value == "Tf") &&
(last_num > 1.0) &&
(last_num < 1000.0))
{
// These ranges are arbitrary but keep us from doing
// insane things or suffering from over/underflow
tf = last_num;
}
break;
default:
break;
}
}
double
TfFinder::getTf()
{
return this->tf;
}
void
QPDFFormFieldObjectHelper::generateTextAppearance(
QPDFAnnotationObjectHelper& aoh)
{
QPDFObjectHandle AS = aoh.getAppearanceStream("/N");
if (! AS.isStream())
{
aoh.getObjectHandle().warnIfPossible(
"unable to get normal appearance stream for update");
return;
}
QPDFObjectHandle bbox_obj = AS.getDict().getKey("/BBox");
if (! bbox_obj.isRectangle())
{
aoh.getObjectHandle().warnIfPossible(
"unable to get appearance stream bounding box");
return;
}
QPDFObjectHandle::Rectangle bbox = bbox_obj.getArrayAsRectangle();
std::string DA = getDefaultAppearance();
std::string V = QUtil::utf8_to_ascii(getValueAsString());
TfFinder tff;
Pl_QPDFTokenizer tok("tf", &tff);
tok.write(QUtil::unsigned_char_pointer(DA.c_str()), DA.length());
tok.finish();
double tf = tff.getTf();
std::vector<std::string> opt;
if (isChoice() && ((getFlags() & ff_ch_combo) == 0))
{
opt = getChoices();
}
AS.addTokenFilter(new ValueSetter(DA, V, opt, tf, bbox));
}

View File

@ -106,6 +106,7 @@ struct Options
flatten_annotations(false),
flatten_annotations_required(0),
flatten_annotations_forbidden(an_invisible | an_hidden),
generate_appearances(false),
show_npages(false),
deterministic_id(false),
static_id(false),
@ -181,6 +182,7 @@ struct Options
bool flatten_annotations;
int flatten_annotations_required;
int flatten_annotations_forbidden;
bool generate_appearances;
std::string min_version;
std::string force_version;
bool show_npages;
@ -570,6 +572,7 @@ class ArgParser
void argLinearizePass1(char* parameter);
void argCoalesceContents();
void argFlattenAnnotations(char* parameter);
void argGenerateAppearances();
void argMinVersion(char* parameter);
void argForceVersion(char* parameter);
void argSplitPages(char* parameter);
@ -771,6 +774,8 @@ ArgParser::initOptionTable()
char const* flatten_choices[] = {"all", "print", "screen", 0};
(*t)["flatten-annotations"] = oe_requiredChoices(
&ArgParser::argFlattenAnnotations, flatten_choices);
(*t)["generate-appearances"] =
oe_bare(&ArgParser::argGenerateAppearances);
(*t)["min-version"] = oe_requiredParameter(
&ArgParser::argMinVersion, "version");
(*t)["force-version"] = oe_requiredParameter(
@ -1233,6 +1238,12 @@ ArgParser::argFlattenAnnotations(char* parameter)
}
}
void
ArgParser::argGenerateAppearances()
{
o.generate_appearances = true;
}
void
ArgParser::argMinVersion(char* parameter)
{
@ -1877,18 +1888,28 @@ familiar with the PDF file format or who are PDF developers.\n\
--flatten-annotations=option\n\
incorporate rendering of annotations into page\n\
contents including those for interactive form\n\
fields\n\
fields; may also want --generate-appearances\n\
--generate-appearances generate appearance streams for form fields\n\
--qdf turns on \"QDF mode\" (below)\n\
--linearize-pass1=file write intermediate pass of linearized file\n\
for debugging\n\
--min-version=version sets the minimum PDF version of the output file\n\
--force-version=version forces this to be the PDF version of the output file\n\
\n\
Options for --flatten-annotations are all, print, or screen. If the\n\
option is print, only annotations marked as print are included. If the\n\
option is screen, options marked as \"no view\" are excluded.\n\
Otherwise, annotations are flattened regardless of the presence of\n\
print or NoView flags.\n\
Options for --flatten-annotations are all, print, or screen. If the option\n\
is print, only annotations marked as print are included. If the option is\n\
screen, options marked as \"no view\" are excluded. Otherwise, annotations\n\
are flattened regardless of the presence of print or NoView flags. It is\n\
common for PDF files to have a flag set that appearance streams need to be\n\
regenerated. This happens when someone changes a form value with software\n\
that does not know how to render the new value. qpdf will not flatten form\n\
fields in files like this. If you get this warning, you have two choices:\n\
either use qpdf's --generate-appearances flag to tell qpdf to go ahead and\n\
regenerate appearances, or use some other tool to generate the appearances.\n\
qpdf does a pretty good job with most forms when only ASCII characters are\n\
used in form field values, but if your form fields contain other\n\
characters, rich text, or are other than left justified, you will get\n\
better results first saving with other software.\n\
\n\
Version numbers may be expressed as major.minor.extension-level, so 1.7.3\n\
means PDF version 1.7 at extension level 3.\n\
@ -3356,6 +3377,11 @@ static void do_inspection(QPDF& pdf, Options& o)
static void handle_transformations(QPDF& pdf, Options& o)
{
QPDFPageDocumentHelper dh(pdf);
if (o.generate_appearances)
{
QPDFAcroFormDocumentHelper afdh(pdf);
afdh.generateAppearancesIfNeeded();
}
if (o.flatten_annotations)
{
dh.flattenAnnotations(o.flatten_annotations_required,

View File

@ -398,3 +398,7 @@ QPDFFormFieldObjectHelper checkbox kid widget 0
QPDFObjectHandle broken radio button 0
QPDFFormFieldObjectHelper set checkbox AS 0
QPDFObjectHandle broken checkbox 0
QPDFFormFieldObjectHelper list not found 0
QPDFFormFieldObjectHelper list found 0
QPDFFormFieldObjectHelper list first too low 0
QPDFFormFieldObjectHelper list last too high 0

View File

@ -232,6 +232,57 @@ $td->runtest("compare files",
{$td->FILE => "a.pdf"},
{$td->FILE => "button-set-broken-out.pdf"});
show_ntests();
# ----------
$td->notify("--- Appearance Streams ---");
$n_tests += 4;
$td->runtest("generate appearances and flatten",
{$td->COMMAND =>
"qpdf --qdf --no-original-object-ids --static-id" .
" --generate-appearances --flatten-annotations=all" .
" need-appearances.pdf a.pdf"},
{$td->STRING => "", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("compare files",
{$td->FILE => "a.pdf"},
{$td->FILE => "appearances-a.pdf"});
$td->runtest("more choices",
{$td->COMMAND =>
"qpdf --qdf --no-original-object-ids --static-id" .
" --generate-appearances" .
" more-choices.pdf b.pdf"},
{$td->STRING => "", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
# b.pdf still has forms
$td->runtest("compare files",
{$td->FILE => "b.pdf"},
{$td->FILE => "appearances-b.pdf"});
my @choice_values = qw(1 2 11 12 quack);
$n_tests += 3 * scalar(@choice_values);
foreach my $i (@choice_values)
{
# b.pdf was generated by qpdf and needs appearances
# test_driver 52 writes a.pdf
$td->runtest("set value to $i",
{$td->COMMAND => "test_driver 52 b.pdf $i"},
{$td->STRING => "setting list1 value\ntest 52 done\n",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("regenerate appearances",
{$td->COMMAND =>
"qpdf --qdf --no-original-object-ids --static-id" .
" --generate-appearances" .
" a.pdf b.pdf"},
{$td->STRING => "", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("compare files",
{$td->FILE => "b.pdf"},
{$td->FILE => "appearances-$i.pdf"});
}
show_ntests();
# ----------
$td->notify("--- Stream Replacement Tests ---");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -321,7 +321,7 @@
"parent": null,
"partialname": "list1",
"quadding": 0,
"value": "five"
"value": "six"
},
{
"alternativename": "drop1",

File diff suppressed because it is too large Load Diff

View File

@ -273,7 +273,7 @@ endobj
/Subtype /Widget
/T (list1)
/Type /Annot
/V <feff0066006900760065>
/V <feff007300690078>
>>
endobj
@ -3617,199 +3617,199 @@ xref
0000002295 00000 n
0000002426 00000 n
0000002780 00000 n
0000003211 00000 n
0000003687 00000 n
0000004108 00000 n
0000004579 00000 n
0000005130 00000 n
0000005204 00000 n
0000005354 00000 n
0000005444 00000 n
0000005611 00000 n
0000005631 00000 n
0000005995 00000 n
0000006361 00000 n
0000006727 00000 n
0000006895 00000 n
0000006915 00000 n
0000007153 00000 n
0000007173 00000 n
0000007254 00000 n
0000007422 00000 n
0000007442 00000 n
0000007680 00000 n
0000007700 00000 n
0000007868 00000 n
0000007888 00000 n
0000008126 00000 n
0000008146 00000 n
0000008512 00000 n
0000008876 00000 n
0000009242 00000 n
0000009411 00000 n
0000009431 00000 n
0000009632 00000 n
0000009652 00000 n
0000009853 00000 n
0000009873 00000 n
0000010076 00000 n
0000010096 00000 n
0000010295 00000 n
0000010315 00000 n
0000010479 00000 n
0000010538 00000 n
0000010580 00000 n
0000015406 00000 n
0000015451 00000 n
0000015579 00000 n
0000015599 00000 n
0000016822 00000 n
0000016843 00000 n
0000017635 00000 n
0000018037 00000 n
0000018488 00000 n
0000020405 00000 n
0000020775 00000 n
0000022689 00000 n
0000023065 00000 n
0000023086 00000 n
0000023254 00000 n
0000023274 00000 n
0000023650 00000 n
0000023671 00000 n
0000023839 00000 n
0000023859 00000 n
0000024235 00000 n
0000024256 00000 n
0000024424 00000 n
0000024444 00000 n
0000024820 00000 n
0000024841 00000 n
0000025009 00000 n
0000025029 00000 n
0000025405 00000 n
0000025426 00000 n
0000025594 00000 n
0000025614 00000 n
0000025990 00000 n
0000026011 00000 n
0000026179 00000 n
0000026199 00000 n
0000026531 00000 n
0000026644 00000 n
0000026740 00000 n
0000026853 00000 n
0000026949 00000 n
0000027062 00000 n
0000027158 00000 n
0000027254 00000 n
0000027350 00000 n
0000027446 00000 n
0000027542 00000 n
0000027639 00000 n
0000027753 00000 n
0000027850 00000 n
0000027947 00000 n
0000028044 00000 n
0000028141 00000 n
0000028238 00000 n
0000028335 00000 n
0000028432 00000 n
0000028529 00000 n
0000028626 00000 n
0000028723 00000 n
0000028837 00000 n
0000028934 00000 n
0000029031 00000 n
0000029128 00000 n
0000029225 00000 n
0000029322 00000 n
0000029419 00000 n
0000029516 00000 n
0000029613 00000 n
0000029710 00000 n
0000029807 00000 n
0000029921 00000 n
0000030018 00000 n
0000030115 00000 n
0000030212 00000 n
0000030332 00000 n
0000030429 00000 n
0000030526 00000 n
0000030623 00000 n
0000030720 00000 n
0000030817 00000 n
0000030937 00000 n
0000031035 00000 n
0000031133 00000 n
0000031231 00000 n
0000031329 00000 n
0000031427 00000 n
0000031525 00000 n
0000031623 00000 n
0000031721 00000 n
0000031819 00000 n
0000031917 00000 n
0000032015 00000 n
0000032113 00000 n
0000032211 00000 n
0000032309 00000 n
0000032407 00000 n
0000032652 00000 n
0000033413 00000 n
0000033435 00000 n
0000033651 00000 n
0000033895 00000 n
0000034536 00000 n
0000034558 00000 n
0000034773 00000 n
0000034830 00000 n
0000034887 00000 n
0000034944 00000 n
0000035001 00000 n
0000035058 00000 n
0000035115 00000 n
0000035172 00000 n
0000035229 00000 n
0000035286 00000 n
0000035343 00000 n
0000035400 00000 n
0000035457 00000 n
0000035514 00000 n
0000035571 00000 n
0000035628 00000 n
0000035685 00000 n
0000035742 00000 n
0000035799 00000 n
0000035856 00000 n
0000035913 00000 n
0000035970 00000 n
0000036027 00000 n
0000036084 00000 n
0000036141 00000 n
0000036198 00000 n
0000036255 00000 n
0000036312 00000 n
0000036369 00000 n
0000036426 00000 n
0000036483 00000 n
0000036540 00000 n
0000036597 00000 n
0000036654 00000 n
0000036711 00000 n
0000036768 00000 n
0000036825 00000 n
0000036882 00000 n
0000036939 00000 n
0000036996 00000 n
0000037053 00000 n
0000037110 00000 n
0000037167 00000 n
0000037224 00000 n
0000037281 00000 n
0000053563 00000 n
0000053587 00000 n
0000064773 00000 n
0000003207 00000 n
0000003683 00000 n
0000004104 00000 n
0000004575 00000 n
0000005126 00000 n
0000005200 00000 n
0000005350 00000 n
0000005440 00000 n
0000005607 00000 n
0000005627 00000 n
0000005991 00000 n
0000006357 00000 n
0000006723 00000 n
0000006891 00000 n
0000006911 00000 n
0000007149 00000 n
0000007169 00000 n
0000007250 00000 n
0000007418 00000 n
0000007438 00000 n
0000007676 00000 n
0000007696 00000 n
0000007864 00000 n
0000007884 00000 n
0000008122 00000 n
0000008142 00000 n
0000008508 00000 n
0000008872 00000 n
0000009238 00000 n
0000009407 00000 n
0000009427 00000 n
0000009628 00000 n
0000009648 00000 n
0000009849 00000 n
0000009869 00000 n
0000010072 00000 n
0000010092 00000 n
0000010291 00000 n
0000010311 00000 n
0000010475 00000 n
0000010534 00000 n
0000010576 00000 n
0000015402 00000 n
0000015447 00000 n
0000015575 00000 n
0000015595 00000 n
0000016818 00000 n
0000016839 00000 n
0000017631 00000 n
0000018033 00000 n
0000018484 00000 n
0000020401 00000 n
0000020771 00000 n
0000022685 00000 n
0000023061 00000 n
0000023082 00000 n
0000023250 00000 n
0000023270 00000 n
0000023646 00000 n
0000023667 00000 n
0000023835 00000 n
0000023855 00000 n
0000024231 00000 n
0000024252 00000 n
0000024420 00000 n
0000024440 00000 n
0000024816 00000 n
0000024837 00000 n
0000025005 00000 n
0000025025 00000 n
0000025401 00000 n
0000025422 00000 n
0000025590 00000 n
0000025610 00000 n
0000025986 00000 n
0000026007 00000 n
0000026175 00000 n
0000026195 00000 n
0000026527 00000 n
0000026640 00000 n
0000026736 00000 n
0000026849 00000 n
0000026945 00000 n
0000027058 00000 n
0000027154 00000 n
0000027250 00000 n
0000027346 00000 n
0000027442 00000 n
0000027538 00000 n
0000027635 00000 n
0000027749 00000 n
0000027846 00000 n
0000027943 00000 n
0000028040 00000 n
0000028137 00000 n
0000028234 00000 n
0000028331 00000 n
0000028428 00000 n
0000028525 00000 n
0000028622 00000 n
0000028719 00000 n
0000028833 00000 n
0000028930 00000 n
0000029027 00000 n
0000029124 00000 n
0000029221 00000 n
0000029318 00000 n
0000029415 00000 n
0000029512 00000 n
0000029609 00000 n
0000029706 00000 n
0000029803 00000 n
0000029917 00000 n
0000030014 00000 n
0000030111 00000 n
0000030208 00000 n
0000030328 00000 n
0000030425 00000 n
0000030522 00000 n
0000030619 00000 n
0000030716 00000 n
0000030813 00000 n
0000030933 00000 n
0000031031 00000 n
0000031129 00000 n
0000031227 00000 n
0000031325 00000 n
0000031423 00000 n
0000031521 00000 n
0000031619 00000 n
0000031717 00000 n
0000031815 00000 n
0000031913 00000 n
0000032011 00000 n
0000032109 00000 n
0000032207 00000 n
0000032305 00000 n
0000032403 00000 n
0000032648 00000 n
0000033409 00000 n
0000033431 00000 n
0000033647 00000 n
0000033891 00000 n
0000034532 00000 n
0000034554 00000 n
0000034769 00000 n
0000034826 00000 n
0000034883 00000 n
0000034940 00000 n
0000034997 00000 n
0000035054 00000 n
0000035111 00000 n
0000035168 00000 n
0000035225 00000 n
0000035282 00000 n
0000035339 00000 n
0000035396 00000 n
0000035453 00000 n
0000035510 00000 n
0000035567 00000 n
0000035624 00000 n
0000035681 00000 n
0000035738 00000 n
0000035795 00000 n
0000035852 00000 n
0000035909 00000 n
0000035966 00000 n
0000036023 00000 n
0000036080 00000 n
0000036137 00000 n
0000036194 00000 n
0000036251 00000 n
0000036308 00000 n
0000036365 00000 n
0000036422 00000 n
0000036479 00000 n
0000036536 00000 n
0000036593 00000 n
0000036650 00000 n
0000036707 00000 n
0000036764 00000 n
0000036821 00000 n
0000036878 00000 n
0000036935 00000 n
0000036992 00000 n
0000037049 00000 n
0000037106 00000 n
0000037163 00000 n
0000037220 00000 n
0000037277 00000 n
0000053559 00000 n
0000053583 00000 n
0000064769 00000 n
trailer <<
/DocChecksum /CC322E136FE95DECF8BC297B1A9B2C2E
/Info 2 0 R
@ -3818,5 +3818,5 @@ trailer <<
/ID [<f8abc47bb1df544a0df9c15a75ef0046><31415926535897932384626433832795>]
>>
startxref
64797
64793
%%EOF

View File

@ -273,7 +273,7 @@ endobj
/Subtype /Widget
/T (list1)
/Type /Annot
/V <feff0066006900760065>
/V <feff007300690078>
>>
endobj
@ -3579,195 +3579,195 @@ xref
0000002295 00000 n
0000002426 00000 n
0000002780 00000 n
0000003211 00000 n
0000003687 00000 n
0000004108 00000 n
0000004579 00000 n
0000005006 00000 n
0000005145 00000 n
0000005295 00000 n
0000005385 00000 n
0000005552 00000 n
0000005572 00000 n
0000005936 00000 n
0000006302 00000 n
0000006668 00000 n
0000006836 00000 n
0000006856 00000 n
0000007094 00000 n
0000007114 00000 n
0000007195 00000 n
0000007363 00000 n
0000007383 00000 n
0000007621 00000 n
0000007641 00000 n
0000007809 00000 n
0000007829 00000 n
0000008067 00000 n
0000008087 00000 n
0000008453 00000 n
0000008817 00000 n
0000009183 00000 n
0000009352 00000 n
0000009372 00000 n
0000009573 00000 n
0000009593 00000 n
0000009794 00000 n
0000009814 00000 n
0000010017 00000 n
0000010037 00000 n
0000010236 00000 n
0000010279 00000 n
0000015105 00000 n
0000015127 00000 n
0000015911 00000 n
0000016312 00000 n
0000016763 00000 n
0000018680 00000 n
0000019050 00000 n
0000020964 00000 n
0000021340 00000 n
0000021361 00000 n
0000021529 00000 n
0000021549 00000 n
0000021925 00000 n
0000021946 00000 n
0000022114 00000 n
0000022134 00000 n
0000022510 00000 n
0000022531 00000 n
0000022699 00000 n
0000022719 00000 n
0000023095 00000 n
0000023116 00000 n
0000023284 00000 n
0000023304 00000 n
0000023680 00000 n
0000023701 00000 n
0000023869 00000 n
0000023889 00000 n
0000024265 00000 n
0000024286 00000 n
0000024454 00000 n
0000024474 00000 n
0000024587 00000 n
0000024683 00000 n
0000024796 00000 n
0000024892 00000 n
0000025005 00000 n
0000025101 00000 n
0000025197 00000 n
0000025293 00000 n
0000025389 00000 n
0000025485 00000 n
0000025581 00000 n
0000025694 00000 n
0000025790 00000 n
0000025886 00000 n
0000025982 00000 n
0000026078 00000 n
0000026174 00000 n
0000026270 00000 n
0000026367 00000 n
0000026464 00000 n
0000026561 00000 n
0000026658 00000 n
0000026772 00000 n
0000026869 00000 n
0000026966 00000 n
0000027063 00000 n
0000027160 00000 n
0000027257 00000 n
0000027354 00000 n
0000027451 00000 n
0000027548 00000 n
0000027645 00000 n
0000027742 00000 n
0000027856 00000 n
0000027953 00000 n
0000028050 00000 n
0000028147 00000 n
0000028267 00000 n
0000028364 00000 n
0000028461 00000 n
0000028558 00000 n
0000028655 00000 n
0000028752 00000 n
0000028872 00000 n
0000028970 00000 n
0000029068 00000 n
0000029166 00000 n
0000029264 00000 n
0000029362 00000 n
0000029460 00000 n
0000029558 00000 n
0000029656 00000 n
0000029754 00000 n
0000029852 00000 n
0000029950 00000 n
0000030048 00000 n
0000030146 00000 n
0000030244 00000 n
0000030342 00000 n
0000030587 00000 n
0000031348 00000 n
0000031370 00000 n
0000031586 00000 n
0000031830 00000 n
0000032471 00000 n
0000032493 00000 n
0000032708 00000 n
0000032765 00000 n
0000032822 00000 n
0000032879 00000 n
0000032936 00000 n
0000032993 00000 n
0000033050 00000 n
0000033107 00000 n
0000033164 00000 n
0000033221 00000 n
0000033278 00000 n
0000033335 00000 n
0000033392 00000 n
0000033449 00000 n
0000033506 00000 n
0000033563 00000 n
0000033620 00000 n
0000033677 00000 n
0000033734 00000 n
0000033791 00000 n
0000033848 00000 n
0000033905 00000 n
0000033962 00000 n
0000034019 00000 n
0000034076 00000 n
0000034133 00000 n
0000034190 00000 n
0000034247 00000 n
0000034304 00000 n
0000034361 00000 n
0000034418 00000 n
0000034475 00000 n
0000034532 00000 n
0000034589 00000 n
0000034646 00000 n
0000034703 00000 n
0000034760 00000 n
0000034817 00000 n
0000034874 00000 n
0000034931 00000 n
0000034988 00000 n
0000035045 00000 n
0000035102 00000 n
0000035159 00000 n
0000035216 00000 n
0000051498 00000 n
0000051522 00000 n
0000062708 00000 n
0000062732 00000 n
0000063067 00000 n
0000063210 00000 n
0000064435 00000 n
0000003207 00000 n
0000003683 00000 n
0000004104 00000 n
0000004575 00000 n
0000005002 00000 n
0000005141 00000 n
0000005291 00000 n
0000005381 00000 n
0000005548 00000 n
0000005568 00000 n
0000005932 00000 n
0000006298 00000 n
0000006664 00000 n
0000006832 00000 n
0000006852 00000 n
0000007090 00000 n
0000007110 00000 n
0000007191 00000 n
0000007359 00000 n
0000007379 00000 n
0000007617 00000 n
0000007637 00000 n
0000007805 00000 n
0000007825 00000 n
0000008063 00000 n
0000008083 00000 n
0000008449 00000 n
0000008813 00000 n
0000009179 00000 n
0000009348 00000 n
0000009368 00000 n
0000009569 00000 n
0000009589 00000 n
0000009790 00000 n
0000009810 00000 n
0000010013 00000 n
0000010033 00000 n
0000010232 00000 n
0000010275 00000 n
0000015101 00000 n
0000015123 00000 n
0000015907 00000 n
0000016308 00000 n
0000016759 00000 n
0000018676 00000 n
0000019046 00000 n
0000020960 00000 n
0000021336 00000 n
0000021357 00000 n
0000021525 00000 n
0000021545 00000 n
0000021921 00000 n
0000021942 00000 n
0000022110 00000 n
0000022130 00000 n
0000022506 00000 n
0000022527 00000 n
0000022695 00000 n
0000022715 00000 n
0000023091 00000 n
0000023112 00000 n
0000023280 00000 n
0000023300 00000 n
0000023676 00000 n
0000023697 00000 n
0000023865 00000 n
0000023885 00000 n
0000024261 00000 n
0000024282 00000 n
0000024450 00000 n
0000024470 00000 n
0000024583 00000 n
0000024679 00000 n
0000024792 00000 n
0000024888 00000 n
0000025001 00000 n
0000025097 00000 n
0000025193 00000 n
0000025289 00000 n
0000025385 00000 n
0000025481 00000 n
0000025577 00000 n
0000025690 00000 n
0000025786 00000 n
0000025882 00000 n
0000025978 00000 n
0000026074 00000 n
0000026170 00000 n
0000026266 00000 n
0000026363 00000 n
0000026460 00000 n
0000026557 00000 n
0000026654 00000 n
0000026768 00000 n
0000026865 00000 n
0000026962 00000 n
0000027059 00000 n
0000027156 00000 n
0000027253 00000 n
0000027350 00000 n
0000027447 00000 n
0000027544 00000 n
0000027641 00000 n
0000027738 00000 n
0000027852 00000 n
0000027949 00000 n
0000028046 00000 n
0000028143 00000 n
0000028263 00000 n
0000028360 00000 n
0000028457 00000 n
0000028554 00000 n
0000028651 00000 n
0000028748 00000 n
0000028868 00000 n
0000028966 00000 n
0000029064 00000 n
0000029162 00000 n
0000029260 00000 n
0000029358 00000 n
0000029456 00000 n
0000029554 00000 n
0000029652 00000 n
0000029750 00000 n
0000029848 00000 n
0000029946 00000 n
0000030044 00000 n
0000030142 00000 n
0000030240 00000 n
0000030338 00000 n
0000030583 00000 n
0000031344 00000 n
0000031366 00000 n
0000031582 00000 n
0000031826 00000 n
0000032467 00000 n
0000032489 00000 n
0000032704 00000 n
0000032761 00000 n
0000032818 00000 n
0000032875 00000 n
0000032932 00000 n
0000032989 00000 n
0000033046 00000 n
0000033103 00000 n
0000033160 00000 n
0000033217 00000 n
0000033274 00000 n
0000033331 00000 n
0000033388 00000 n
0000033445 00000 n
0000033502 00000 n
0000033559 00000 n
0000033616 00000 n
0000033673 00000 n
0000033730 00000 n
0000033787 00000 n
0000033844 00000 n
0000033901 00000 n
0000033958 00000 n
0000034015 00000 n
0000034072 00000 n
0000034129 00000 n
0000034186 00000 n
0000034243 00000 n
0000034300 00000 n
0000034357 00000 n
0000034414 00000 n
0000034471 00000 n
0000034528 00000 n
0000034585 00000 n
0000034642 00000 n
0000034699 00000 n
0000034756 00000 n
0000034813 00000 n
0000034870 00000 n
0000034927 00000 n
0000034984 00000 n
0000035041 00000 n
0000035098 00000 n
0000035155 00000 n
0000035212 00000 n
0000051494 00000 n
0000051518 00000 n
0000062704 00000 n
0000062728 00000 n
0000063063 00000 n
0000063206 00000 n
0000064431 00000 n
trailer <<
/DocChecksum /CC322E136FE95DECF8BC297B1A9B2C2E
/Info 2 0 R
@ -3776,5 +3776,5 @@ trailer <<
/ID [<f8abc47bb1df544a0df9c15a75ef0046><45201f7a345625a01ccb53b240a8ba8d>]
>>
startxref
64457
64453
%%EOF

View File

@ -1820,6 +1820,32 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
else if (n == 52)
{
// This test just sets a field value for appearance stream
// generating testing.
QPDFObjectHandle acroform = pdf.getRoot().getKey("/AcroForm");
QPDFObjectHandle fields = acroform.getKey("/Fields");
int n = fields.getArrayNItems();
for (int i = 0; i < n; ++i)
{
QPDFObjectHandle field = fields.getArrayItem(i);
QPDFObjectHandle T = field.getKey("/T");
if (! T.isString())
{
continue;
}
std::string Tval = T.getUTF8Value();
if (Tval == "list1")
{
std::cout << "setting list1 value\n";
QPDFFormFieldObjectHelper foh(field);
foh.setV(QPDFObjectHandle::newString(arg2));
}
}
QPDFWriter w(pdf, "a.pdf");
w.write();
}
else
{
throw std::runtime_error(std::string("invalid test ") +