mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
Add transformAnnotations and fix flattenRotations to use it
This commit is contained in:
parent
a76decd2d5
commit
a9ae8cadc6
@ -1,5 +1,11 @@
|
||||
2021-02-21 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* Bug fix: --flatten-rotation now applies the required
|
||||
transformation to annotations on the page.
|
||||
|
||||
* Add QPDFAcroFormDocumentHelper::transformAnnotations to apply a
|
||||
transformation to a group of annotations.
|
||||
|
||||
* Add QPDFObjGen::unparse()
|
||||
|
||||
* Add QPDFObjectHandle::copyStream() for making a copy of a stream
|
||||
|
@ -113,6 +113,10 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
|
||||
QPDF_DLL
|
||||
void addFormField(QPDFFormFieldObjectHelper);
|
||||
|
||||
// Remove fields from the fields array
|
||||
QPDF_DLL
|
||||
void removeFormFields(std::set<QPDFObjGen> const&);
|
||||
|
||||
// Return a vector of all terminal fields in a document. Terminal
|
||||
// fields are fields that have no children that are also fields.
|
||||
// Terminal fields may still have children that are annotations.
|
||||
@ -174,6 +178,32 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
|
||||
QPDF_DLL
|
||||
void generateAppearancesIfNeeded();
|
||||
|
||||
// Note: this method works on all annotations, not just ones with
|
||||
// associated fields. For each annotation in old_annots, apply the
|
||||
// given transformation matrix to create a new annotation. New
|
||||
// annotations are appended to new_annots. If the annotation is
|
||||
// associated with a form field, a new form field is created that
|
||||
// points to the new annotation and is appended to new_fields, and
|
||||
// the old field is added to old_fields.
|
||||
//
|
||||
// old_annots may belong to a different QPDF object. In that case,
|
||||
// you should pass in from_qpdf, and copyForeignObject will be
|
||||
// called automatically. If this is the case, for efficiency, you
|
||||
// may pass in a QPDFAcroFormDocumentHelper for the other file to
|
||||
// avoid the expensive process of creating one for each call to
|
||||
// transformAnnotations. New fields and annotations are not added
|
||||
// to the document or pages. You have to do that yourself after
|
||||
// calling transformAnnotations.
|
||||
QPDF_DLL
|
||||
void transformAnnotations(
|
||||
QPDFObjectHandle old_annots,
|
||||
std::vector<QPDFObjectHandle>& new_annots,
|
||||
std::vector<QPDFObjectHandle>& new_fields,
|
||||
std::set<QPDFObjGen>& old_fields,
|
||||
QPDFMatrix const& cm,
|
||||
QPDF* from_qpdf = nullptr,
|
||||
QPDFAcroFormDocumentHelper* from_afdh = nullptr);
|
||||
|
||||
private:
|
||||
void analyze();
|
||||
void traverseField(QPDFObjectHandle field,
|
||||
|
@ -40,6 +40,13 @@ class QPDFAnnotationObjectHelper: public QPDFObjectHelper
|
||||
// This class provides helper methods for annotations. More
|
||||
// functionality will likely be added in the future.
|
||||
|
||||
// Some functionality for annotations is also implemented in
|
||||
// QPDFAcroFormDocumentHelper and QPDFFormFieldObjectHelper. In
|
||||
// some cases, functions defined there work for other annotations
|
||||
// besides widget annotations, but they are implemented with form
|
||||
// fields so that they can properly handle form fields when
|
||||
// needed.
|
||||
|
||||
// Return the subtype of the annotation as a string (e.g.
|
||||
// "/Widget"). Returns the empty string if the subtype (which is
|
||||
// required by the spec) is missing.
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include <qpdf/QPDFObjectHandle.hh>
|
||||
#include <functional>
|
||||
|
||||
class QPDFAcroFormDocumentHelper;
|
||||
|
||||
class QPDFPageObjectHelper: public QPDFObjectHelper
|
||||
{
|
||||
// This is a helper class for page objects, but as of qpdf 10.1,
|
||||
@ -323,9 +325,15 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
|
||||
// various page bounding boxes (/MediaBox, etc.) so that the page
|
||||
// will have the same semantics. This can be useful to work around
|
||||
// problems with PDF applications that can't properly handle
|
||||
// rotated pages.
|
||||
// rotated pages. If a QPDFAcroFormDocumentHelper is provided, it
|
||||
// will be used for resolving any form fields that have to be
|
||||
// rotated. If not, one will be created inside the function, which
|
||||
// is less efficient.
|
||||
QPDF_DLL
|
||||
void flattenRotation();
|
||||
// ABI: merge versions and make afdh default to nullptr
|
||||
QPDF_DLL
|
||||
void flattenRotation(QPDFAcroFormDocumentHelper* afdh);
|
||||
|
||||
private:
|
||||
static bool
|
||||
|
@ -54,6 +54,50 @@ QPDFAcroFormDocumentHelper::addFormField(QPDFFormFieldObjectHelper ff)
|
||||
ff.getObjectHandle(), QPDFObjectHandle::newNull(), 0, visited);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFAcroFormDocumentHelper::removeFormFields(
|
||||
std::set<QPDFObjGen> const& to_remove)
|
||||
{
|
||||
auto acroform = this->qpdf.getRoot().getKey("/AcroForm");
|
||||
if (! acroform.isDictionary())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto fields = acroform.getKey("/Fields");
|
||||
if (! fields.isArray())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const& og: to_remove)
|
||||
{
|
||||
auto annotations = this->m->field_to_annotations.find(og);
|
||||
if (annotations != this->m->field_to_annotations.end())
|
||||
{
|
||||
for (auto aoh: annotations->second)
|
||||
{
|
||||
this->m->annotation_to_field.erase(
|
||||
aoh.getObjectHandle().getObjGen());
|
||||
}
|
||||
this->m->field_to_annotations.erase(og);
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
while (i < fields.getArrayNItems())
|
||||
{
|
||||
auto field = fields.getArrayItem(i);
|
||||
if (to_remove.count(field.getObjGen()))
|
||||
{
|
||||
fields.eraseItem(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<QPDFFormFieldObjectHelper>
|
||||
QPDFAcroFormDocumentHelper::getFormFields()
|
||||
{
|
||||
@ -350,3 +394,273 @@ QPDFAcroFormDocumentHelper::generateAppearancesIfNeeded()
|
||||
}
|
||||
setNeedAppearances(false);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFAcroFormDocumentHelper::transformAnnotations(
|
||||
QPDFObjectHandle old_annots,
|
||||
std::vector<QPDFObjectHandle>& new_annots,
|
||||
std::vector<QPDFObjectHandle>& new_fields,
|
||||
std::set<QPDFObjGen>& old_fields,
|
||||
QPDFMatrix const& cm,
|
||||
QPDF* from_qpdf,
|
||||
QPDFAcroFormDocumentHelper* from_afdh)
|
||||
{
|
||||
PointerHolder<QPDFAcroFormDocumentHelper> afdhph;
|
||||
if (! from_qpdf)
|
||||
{
|
||||
// Assume these are from the same QPDF.
|
||||
from_qpdf = &this->qpdf;
|
||||
from_afdh = this;
|
||||
}
|
||||
else if ((from_qpdf != &this->qpdf) && (! from_afdh))
|
||||
{
|
||||
afdhph = new QPDFAcroFormDocumentHelper(*from_qpdf);
|
||||
from_afdh = afdhph.getPointer();
|
||||
}
|
||||
bool foreign = (from_qpdf != &this->qpdf);
|
||||
|
||||
std::set<QPDFObjGen> added_new_fields;
|
||||
|
||||
// This helper prevents us from copying the same object
|
||||
// multiple times.
|
||||
std::map<QPDFObjGen, QPDFObjectHandle> copied_objects;
|
||||
auto maybe_copy_object = [&](QPDFObjectHandle& to_copy) {
|
||||
auto og = to_copy.getObjGen();
|
||||
if (copied_objects.count(og))
|
||||
{
|
||||
to_copy = copied_objects[og];
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
to_copy = this->qpdf.makeIndirectObject(to_copy.shallowCopy());
|
||||
copied_objects[og] = to_copy;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
for (auto annot: QPDFArrayItems(old_annots))
|
||||
{
|
||||
if (annot.isStream())
|
||||
{
|
||||
annot.warnIfPossible("ignoring annotation that's a stream");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make copies of annotations and fields down to the
|
||||
// appearance streams, preserving all internal referential
|
||||
// integrity. When the incoming annotations are from a
|
||||
// different file, we first copy them locally. Then, whether
|
||||
// local or foreign, we copy them again so that if we bring
|
||||
// the same annotation in multiple times (e.g. overlaying a
|
||||
// foreign page onto multiple local pages or a local page onto
|
||||
// multiple other local pages), we don't create annotations
|
||||
// that are referenced in more than one place. If we did that,
|
||||
// the effect of applying transformations would be cumulative,
|
||||
// which is definitely not what we want. Besides, annotations
|
||||
// and fields are not intended to be referenced in multiple
|
||||
// places.
|
||||
|
||||
// Determine if this annotation is attached to a form field.
|
||||
// If so, the annotation may be the same object as the form
|
||||
// field, or the form field may have the annotation as a kid.
|
||||
// In either case, we have to walk up the field structure to
|
||||
// find the top-level field. Within one iteration through a
|
||||
// set of annotations, we don't want to copy the same item
|
||||
// more than once. For example, suppose we have field A with
|
||||
// kids B, C, and D, each of which has annotations BA, CA, and
|
||||
// DA. When we get to BA, we will find that BA is a kid of B
|
||||
// which is under A. When we do a copyForeignObject of A, it
|
||||
// will also copy everything else because of the indirect
|
||||
// references. When we clone BA, we will want to clone A and
|
||||
// then update A's clone's kid to point B's clone and B's
|
||||
// clone's parent to point to A's clone. The same thing holds
|
||||
// for annotatons. Next, when we get to CA, we will again
|
||||
// discover that A is the top, but we don't want to re-copy A.
|
||||
// We want CA's clone to be linked to the same clone as BA's.
|
||||
// Failure to do this will break up things like radio button
|
||||
// groups, which all have to kids of the same parent.
|
||||
|
||||
auto ffield = from_afdh->getFieldForAnnotation(annot);
|
||||
auto ffield_oh = ffield.getObjectHandle();
|
||||
QPDFObjectHandle top_field;
|
||||
bool have_field = false;
|
||||
bool have_parent = false;
|
||||
if (ffield_oh.isStream())
|
||||
{
|
||||
ffield_oh.warnIfPossible("ignoring form field that's a stream");
|
||||
}
|
||||
else if ((! ffield_oh.isNull()) && (! ffield_oh.isIndirect()))
|
||||
{
|
||||
ffield_oh.warnIfPossible("ignoring form field not indirect");
|
||||
}
|
||||
else if (! ffield_oh.isNull())
|
||||
{
|
||||
// A field and its associated annotation can be the same
|
||||
// object. This matters because we don't want to clone the
|
||||
// annotation and field separately in this case.
|
||||
have_field = true;
|
||||
// Find the top-level field. It may be the field itself.
|
||||
top_field = ffield_oh;
|
||||
std::set<QPDFObjGen> seen;
|
||||
while (! top_field.getKey("/Parent").isNull())
|
||||
{
|
||||
top_field = top_field.getKey("/Parent");
|
||||
have_parent = true;
|
||||
auto og = top_field.getObjGen();
|
||||
if (seen.count(og))
|
||||
{
|
||||
break;
|
||||
}
|
||||
seen.insert(og);
|
||||
}
|
||||
if (foreign)
|
||||
{
|
||||
// copyForeignObject returns the same value if called
|
||||
// multiple times with the same field. Create/retrieve
|
||||
// the local copy of the original field. This pulls
|
||||
// over everything the field references including
|
||||
// annotations and appearance streams, but it's
|
||||
// harmless to call copyForeignObject on them too.
|
||||
// They will already be copied, so we'll get the right
|
||||
// object back.
|
||||
|
||||
top_field = this->qpdf.copyForeignObject(top_field);
|
||||
ffield_oh = this->qpdf.copyForeignObject(ffield_oh);
|
||||
}
|
||||
old_fields.insert(top_field.getObjGen());
|
||||
|
||||
// Traverse the field, copying kids, and preserving
|
||||
// integrity.
|
||||
std::list<QPDFObjectHandle> queue;
|
||||
if (maybe_copy_object(top_field))
|
||||
{
|
||||
queue.push_back(top_field);
|
||||
}
|
||||
seen.clear();
|
||||
while (! queue.empty())
|
||||
{
|
||||
QPDFObjectHandle obj = queue.front();
|
||||
queue.pop_front();
|
||||
auto orig_og = obj.getObjGen();
|
||||
if (seen.count(orig_og))
|
||||
{
|
||||
// loop
|
||||
break;
|
||||
}
|
||||
seen.insert(orig_og);
|
||||
auto parent = obj.getKey("/Parent");
|
||||
if (parent.isIndirect())
|
||||
{
|
||||
auto parent_og = parent.getObjGen();
|
||||
if (copied_objects.count(parent_og))
|
||||
{
|
||||
obj.replaceKey("/Parent", copied_objects[parent_og]);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.warnIfPossible(
|
||||
"while traversing field " +
|
||||
obj.getObjGen().unparse() +
|
||||
", found parent (" + parent_og.unparse() +
|
||||
") that had not been seen, indicating likely"
|
||||
" invalid field structure");
|
||||
}
|
||||
}
|
||||
auto kids = obj.getKey("/Kids");
|
||||
if (kids.isArray())
|
||||
{
|
||||
for (int i = 0; i < kids.getArrayNItems(); ++i)
|
||||
{
|
||||
auto kid = kids.getArrayItem(i);
|
||||
if (maybe_copy_object(kid))
|
||||
{
|
||||
kids.setArrayItem(i, kid);
|
||||
queue.push_back(kid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now switch to copies. We already switched for top_field
|
||||
maybe_copy_object(ffield_oh);
|
||||
ffield = QPDFFormFieldObjectHelper(ffield_oh);
|
||||
}
|
||||
|
||||
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper copy annotation",
|
||||
(have_field ? 1 : 0) | (foreign ? 2 : 0));
|
||||
if (have_field)
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper field with parent",
|
||||
(have_parent ? 1 : 0) | (foreign ? 2 : 0));
|
||||
}
|
||||
if (foreign)
|
||||
{
|
||||
annot = this->qpdf.copyForeignObject(annot);
|
||||
}
|
||||
maybe_copy_object(annot);
|
||||
|
||||
// Now we have copies, so we can safely mutate.
|
||||
if (have_field && ! added_new_fields.count(top_field.getObjGen()))
|
||||
{
|
||||
new_fields.push_back(top_field);
|
||||
added_new_fields.insert(top_field.getObjGen());
|
||||
}
|
||||
new_annots.push_back(annot);
|
||||
|
||||
// Identify and copy any appearance streams
|
||||
|
||||
auto ah = QPDFAnnotationObjectHelper(annot);
|
||||
auto apdict = ah.getAppearanceDictionary();
|
||||
std::vector<QPDFObjectHandle> streams;
|
||||
auto replace_stream = [](auto& dict, auto& key, auto& old) {
|
||||
auto new_stream = old.copyStream();
|
||||
dict.replaceKey(key, new_stream);
|
||||
return new_stream;
|
||||
};
|
||||
if (apdict.isDictionary())
|
||||
{
|
||||
for (auto& ap: QPDFDictItems(apdict))
|
||||
{
|
||||
if (ap.second.isStream())
|
||||
{
|
||||
streams.push_back(
|
||||
replace_stream(apdict, ap.first, ap.second));
|
||||
}
|
||||
else if (ap.second.isDictionary())
|
||||
{
|
||||
for (auto& ap2: QPDFDictItems(ap.second))
|
||||
{
|
||||
if (ap2.second.isStream())
|
||||
{
|
||||
streams.push_back(
|
||||
replace_stream(
|
||||
ap.second, ap2.first, ap2.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we can safely mutate the annotation and its appearance
|
||||
// streams.
|
||||
for (auto& stream: streams)
|
||||
{
|
||||
auto omatrix = stream.getDict().getKey("/Matrix");
|
||||
QPDFMatrix apcm;
|
||||
if (omatrix.isArray())
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFAcroFormDocumentHelper modify ap matrix");
|
||||
auto m1 = omatrix.getArrayAsMatrix();
|
||||
apcm = QPDFMatrix(m1);
|
||||
}
|
||||
apcm.concat(cm);
|
||||
auto new_matrix = QPDFObjectHandle::newFromMatrix(apcm);
|
||||
stream.getDict().replaceKey("/Matrix", new_matrix);
|
||||
}
|
||||
auto rect = cm.transformRectangle(
|
||||
annot.getKey("/Rect").getArrayAsRectangle());
|
||||
annot.replaceKey(
|
||||
"/Rect", QPDFObjectHandle::newFromRectangle(rect));
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <qpdf/QPDFExc.hh>
|
||||
#include <qpdf/QPDFMatrix.hh>
|
||||
#include <qpdf/QIntC.hh>
|
||||
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
|
||||
|
||||
class ContentProvider: public QPDFObjectHandle::StreamDataProvider
|
||||
{
|
||||
@ -1080,6 +1081,12 @@ QPDFPageObjectHelper::placeFormXObject(
|
||||
|
||||
void
|
||||
QPDFPageObjectHelper::flattenRotation()
|
||||
{
|
||||
flattenRotation(nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
|
||||
{
|
||||
QPDF* qpdf = this->oh.getOwningQPDF();
|
||||
if (! qpdf)
|
||||
@ -1206,4 +1213,26 @@ QPDFPageObjectHelper::flattenRotation()
|
||||
QTC::TC("qpdf", "QPDFPageObjectHelper flatten inherit rotate");
|
||||
this->oh.replaceKey("/Rotate", QPDFObjectHandle::newInteger(0));
|
||||
}
|
||||
|
||||
QPDFObjectHandle annots = this->oh.getKey("/Annots");
|
||||
if (annots.isArray())
|
||||
{
|
||||
std::vector<QPDFObjectHandle> new_annots;
|
||||
std::vector<QPDFObjectHandle> new_fields;
|
||||
std::set<QPDFObjGen> old_fields;
|
||||
PointerHolder<QPDFAcroFormDocumentHelper> afdhph;
|
||||
if (! afdh)
|
||||
{
|
||||
afdhph = new QPDFAcroFormDocumentHelper(*qpdf);
|
||||
afdh = afdhph.getPointer();
|
||||
}
|
||||
afdh->transformAnnotations(
|
||||
annots, new_annots, new_fields, old_fields, cm);
|
||||
afdh->removeFormFields(old_fields);
|
||||
for (auto const& f: new_fields)
|
||||
{
|
||||
afdh->addFormField(QPDFFormFieldObjectHelper(f));
|
||||
}
|
||||
this->oh.replaceKey("/Annots", QPDFObjectHandle::newArray(new_annots));
|
||||
}
|
||||
}
|
||||
|
@ -5313,6 +5313,13 @@ print "\n";
|
||||
dictionary if needed.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add method
|
||||
<function>QPDFAcroFormDocumentHelper::transformAnnotations</function>,
|
||||
which applies a transformation to each annotation on a page.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add <function>QUtil::path_basename</function> to return the
|
||||
@ -5341,6 +5348,12 @@ print "\n";
|
||||
Bug Fixes
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
The <option>--flatten-rotations</option> option applies
|
||||
transformations to any annotations that may be on the page.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If a form XObject lacks a resources dictionary, consider any
|
||||
|
14
qpdf/qpdf.cc
14
qpdf/qpdf.cc
@ -5362,6 +5362,13 @@ static void copy_attachments(QPDF& pdf, Options& o, int& exit_code)
|
||||
static void handle_transformations(QPDF& pdf, Options& o, int& exit_code)
|
||||
{
|
||||
QPDFPageDocumentHelper dh(pdf);
|
||||
PointerHolder<QPDFAcroFormDocumentHelper> afdh;
|
||||
auto make_afdh = [&]() {
|
||||
if (! afdh.getPointer())
|
||||
{
|
||||
afdh = new QPDFAcroFormDocumentHelper(pdf);
|
||||
}
|
||||
};
|
||||
if (o.externalize_inline_images)
|
||||
{
|
||||
std::vector<QPDFPageObjectHelper> pages = dh.getAllPages();
|
||||
@ -5408,8 +5415,8 @@ static void handle_transformations(QPDF& pdf, Options& o, int& exit_code)
|
||||
}
|
||||
if (o.generate_appearances)
|
||||
{
|
||||
QPDFAcroFormDocumentHelper afdh(pdf);
|
||||
afdh.generateAppearancesIfNeeded();
|
||||
make_afdh();
|
||||
afdh->generateAppearancesIfNeeded();
|
||||
}
|
||||
if (o.flatten_annotations)
|
||||
{
|
||||
@ -5427,9 +5434,10 @@ static void handle_transformations(QPDF& pdf, Options& o, int& exit_code)
|
||||
}
|
||||
if (o.flatten_rotation)
|
||||
{
|
||||
make_afdh();
|
||||
for (auto& page: dh.getAllPages())
|
||||
{
|
||||
page.flattenRotation();
|
||||
page.flattenRotation(afdh.getPointer());
|
||||
}
|
||||
}
|
||||
if (o.remove_page_labels)
|
||||
|
@ -572,3 +572,6 @@ qpdf password file 0
|
||||
QPDFFileSpecObjectHelper empty compat_name 0
|
||||
QPDFFileSpecObjectHelper non-empty compat_name 0
|
||||
QPDFPageObjectHelper flatten inherit rotate 0
|
||||
QPDFAcroFormDocumentHelper copy annotation 1
|
||||
QPDFAcroFormDocumentHelper field with parent 1
|
||||
QPDFAcroFormDocumentHelper modify ap matrix 0
|
||||
|
@ -2248,7 +2248,7 @@ $td->runtest("explicit keep files open = n",
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Rotate Pages ---");
|
||||
$n_tests += 8;
|
||||
$n_tests += 18;
|
||||
# Do absolute, positive, and negative on ranges that include
|
||||
# inherited and non-inherited.
|
||||
# Pages 11-15 inherit /Rotate 90
|
||||
@ -2290,6 +2290,49 @@ $td->runtest("check output",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "inherited-flattened.pdf"});
|
||||
|
||||
foreach my $angle (qw(90 180 270))
|
||||
{
|
||||
$td->runtest("rotate annotations",
|
||||
{$td->COMMAND =>
|
||||
"qpdf --static-id --qdf --rotate=$angle" .
|
||||
" --flatten-rotation --no-original-object-ids" .
|
||||
" form-fields-and-annotations.pdf a.pdf"},
|
||||
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||
$td->runtest("check output (flatten $angle)",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "annotations-rotated-$angle.pdf"});
|
||||
}
|
||||
|
||||
# The file form-fields-and-annotations-shared.pdf contains some
|
||||
# annotations that appear in multiple pages /Annots, some non-shared
|
||||
# things that share appearance streams, some form fields appear on
|
||||
# multiple pages, and an indirect /Annotations array. It is out of
|
||||
# spec in several ways but still works in most viewers. These test
|
||||
# make sure we don't make anything worse and also end up exercising
|
||||
# some cases of things being copied more than once, though we also
|
||||
# exercise that with legitimate test cases using overlay.
|
||||
|
||||
$td->runtest("shared annotations 1 page",
|
||||
{$td->COMMAND =>
|
||||
"qpdf --qdf --no-original-object-ids --static-id" .
|
||||
" --rotate=90:1 form-fields-and-annotations-shared.pdf" .
|
||||
" a.pdf --flatten-rotation"},
|
||||
{$td->STRING => "", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("check output",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "rotated-shared-annotations-1.pdf"});
|
||||
$td->runtest("shared annotations 2 pages",
|
||||
{$td->COMMAND =>
|
||||
"qpdf --qdf --no-original-object-ids --static-id" .
|
||||
" --rotate=90:1,2 form-fields-and-annotations-shared.pdf" .
|
||||
" a.pdf --flatten-rotation"},
|
||||
{$td->STRING => "", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("check output",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "rotated-shared-annotations-2.pdf"});
|
||||
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Flatten Form/Annotations ---");
|
||||
|
1052
qpdf/qtest/qpdf/annotations-rotated-180.pdf
Normal file
1052
qpdf/qtest/qpdf/annotations-rotated-180.pdf
Normal file
File diff suppressed because it is too large
Load Diff
1052
qpdf/qtest/qpdf/annotations-rotated-270.pdf
Normal file
1052
qpdf/qtest/qpdf/annotations-rotated-270.pdf
Normal file
File diff suppressed because it is too large
Load Diff
1052
qpdf/qtest/qpdf/annotations-rotated-90.pdf
Normal file
1052
qpdf/qtest/qpdf/annotations-rotated-90.pdf
Normal file
File diff suppressed because it is too large
Load Diff
1132
qpdf/qtest/qpdf/form-fields-and-annotations-shared.pdf
Normal file
1132
qpdf/qtest/qpdf/form-fields-and-annotations-shared.pdf
Normal file
File diff suppressed because it is too large
Load Diff
942
qpdf/qtest/qpdf/form-fields-and-annotations.pdf
Normal file
942
qpdf/qtest/qpdf/form-fields-and-annotations.pdf
Normal file
@ -0,0 +1,942 @@
|
||||
%PDF-1.6
|
||||
%¿÷¢þ
|
||||
%QDF-1.0
|
||||
|
||||
1 0 obj
|
||||
<<
|
||||
/AcroForm <<
|
||||
/DR 2 0 R
|
||||
/Fields [
|
||||
3 0 R
|
||||
4 0 R
|
||||
5 0 R
|
||||
]
|
||||
>>
|
||||
/Names <<
|
||||
/EmbeddedFiles 6 0 R
|
||||
>>
|
||||
/Pages 7 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<<
|
||||
/Font <<
|
||||
/F1 8 0 R
|
||||
>>
|
||||
>>
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N 9 0 R
|
||||
>>
|
||||
/DA (0 0.4 0 rg /F1 18 Tf)
|
||||
/DR 2 0 R
|
||||
/DV ()
|
||||
/FT /Tx
|
||||
/Ff 0
|
||||
/Rect [
|
||||
72
|
||||
470.774
|
||||
190.8
|
||||
484.922
|
||||
]
|
||||
/Subtype /Widget
|
||||
/T (Text Box 1)
|
||||
/Type /Annot
|
||||
/V (Formy field)
|
||||
>>
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N 11 0 R
|
||||
>>
|
||||
/DA (0 0.4 0 rg /F1 18 Tf)
|
||||
/DR 2 0 R
|
||||
/DV ()
|
||||
/FT /Tx
|
||||
/Ff 0
|
||||
/Rect [
|
||||
372
|
||||
330.774
|
||||
386.148
|
||||
470.374
|
||||
]
|
||||
/Subtype /Widget
|
||||
/T (Text Box 1)
|
||||
/Type /Annot
|
||||
/V (Rot-ccw field)
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<<
|
||||
/DV /1
|
||||
/FT /Btn
|
||||
/Ff 49152
|
||||
/Kids [
|
||||
13 0 R
|
||||
14 0 R
|
||||
15 0 R
|
||||
]
|
||||
/T (r1)
|
||||
/V /2
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<<
|
||||
/Names [
|
||||
(attachment1.txt)
|
||||
16 0 R
|
||||
]
|
||||
>>
|
||||
endobj
|
||||
|
||||
7 0 obj
|
||||
<<
|
||||
/Count 1
|
||||
/Kids [
|
||||
17 0 R
|
||||
]
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
8 0 obj
|
||||
<<
|
||||
/BaseFont /Courier
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
9 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
-2.826
|
||||
118.8
|
||||
11.322
|
||||
]
|
||||
/Resources 2 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 10 0 R
|
||||
>>
|
||||
stream
|
||||
/Tx BMC
|
||||
q
|
||||
BT
|
||||
/F1 18 Tf
|
||||
(Formy field) Tj
|
||||
ET
|
||||
Q
|
||||
EMC
|
||||
endstream
|
||||
endobj
|
||||
|
||||
10 0 obj
|
||||
53
|
||||
endobj
|
||||
|
||||
11 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
-2.826
|
||||
140.4
|
||||
11.322
|
||||
]
|
||||
/Matrix [
|
||||
0
|
||||
1
|
||||
-1
|
||||
0
|
||||
0
|
||||
0
|
||||
]
|
||||
/Resources 2 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 12 0 R
|
||||
>>
|
||||
stream
|
||||
/Tx BMC
|
||||
q
|
||||
BT
|
||||
/F1 18 Tf
|
||||
(Rot-ccw field) Tj
|
||||
ET
|
||||
Q
|
||||
EMC
|
||||
endstream
|
||||
endobj
|
||||
|
||||
12 0 obj
|
||||
55
|
||||
endobj
|
||||
|
||||
13 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N <<
|
||||
/1 18 0 R
|
||||
/Off 20 0 R
|
||||
>>
|
||||
>>
|
||||
/AS /1
|
||||
/DA (0.18039 0.20392 0.21176 rg /ZaDi 0 Tf)
|
||||
/DR <<
|
||||
/Font <<
|
||||
/ZaDi 22 0 R
|
||||
>>
|
||||
>>
|
||||
/F 4
|
||||
/FT /Btn
|
||||
/MK <<
|
||||
/CA (l)
|
||||
>>
|
||||
/Parent 5 0 R
|
||||
/Rect [
|
||||
152.749
|
||||
648.501
|
||||
164.801
|
||||
660.549
|
||||
]
|
||||
/Subtype /Widget
|
||||
/Type /Annot
|
||||
>>
|
||||
endobj
|
||||
|
||||
14 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N <<
|
||||
/2 23 0 R
|
||||
/Off 25 0 R
|
||||
>>
|
||||
>>
|
||||
/AS /2
|
||||
/DA (0.18039 0.20392 0.21176 rg /ZaDi 0 Tf)
|
||||
/DR <<
|
||||
/Font <<
|
||||
/ZaDi 22 0 R
|
||||
>>
|
||||
>>
|
||||
/F 4
|
||||
/FT /Btn
|
||||
/MK <<
|
||||
/CA (l)
|
||||
>>
|
||||
/Parent 5 0 R
|
||||
/Rect [
|
||||
152.749
|
||||
627.301
|
||||
164.801
|
||||
639.349
|
||||
]
|
||||
/Subtype /Widget
|
||||
/Type /Annot
|
||||
>>
|
||||
endobj
|
||||
|
||||
15 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N <<
|
||||
/3 27 0 R
|
||||
/Off 29 0 R
|
||||
>>
|
||||
>>
|
||||
/AS /3
|
||||
/DA (0.18039 0.20392 0.21176 rg /ZaDi 0 Tf)
|
||||
/DR <<
|
||||
/Font <<
|
||||
/ZaDi 22 0 R
|
||||
>>
|
||||
>>
|
||||
/F 4
|
||||
/FT /Btn
|
||||
/MK <<
|
||||
/CA (l)
|
||||
>>
|
||||
/Parent 5 0 R
|
||||
/Rect [
|
||||
151.399
|
||||
606.501
|
||||
163.451
|
||||
618.549
|
||||
]
|
||||
/Subtype /Widget
|
||||
/Type /Annot
|
||||
>>
|
||||
endobj
|
||||
|
||||
16 0 obj
|
||||
<<
|
||||
/EF <<
|
||||
/F 31 0 R
|
||||
/UF 31 0 R
|
||||
>>
|
||||
/F (attachment1.txt)
|
||||
/Type /Filespec
|
||||
/UF (attachment1.txt)
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Page 1
|
||||
17 0 obj
|
||||
<<
|
||||
/Annots [
|
||||
33 0 R
|
||||
3 0 R
|
||||
34 0 R
|
||||
4 0 R
|
||||
35 0 R
|
||||
36 0 R
|
||||
37 0 R
|
||||
38 0 R
|
||||
13 0 R
|
||||
14 0 R
|
||||
15 0 R
|
||||
]
|
||||
/Contents 39 0 R
|
||||
/MediaBox [
|
||||
0
|
||||
0
|
||||
612
|
||||
792
|
||||
]
|
||||
/Parent 7 0 R
|
||||
/Resources 2 0 R
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
|
||||
18 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
12.05
|
||||
12.05
|
||||
]
|
||||
/Resources 41 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 19 0 R
|
||||
>>
|
||||
stream
|
||||
/Tx BMC
|
||||
q BT
|
||||
0.18039 0.20392 0.21176 rg /ZaDi 12.05 Tf
|
||||
0 0 Td
|
||||
ET
|
||||
Q
|
||||
1 0 0 rg
|
||||
6 8.4 m 7.35 8.4 8.45 7.35 8.45 6 c
|
||||
8.45 4.65 7.35 3.55 6 3.55 c
|
||||
4.65 3.55 3.6 4.65 3.6 6 c
|
||||
3.6 7.35 4.65 8.4 6 8.4 c f*
|
||||
|
||||
EMC
|
||||
endstream
|
||||
endobj
|
||||
|
||||
19 0 obj
|
||||
202
|
||||
endobj
|
||||
|
||||
20 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
12.05
|
||||
12.05
|
||||
]
|
||||
/Resources 41 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 21 0 R
|
||||
>>
|
||||
stream
|
||||
/Tx BMC
|
||||
EMC
|
||||
endstream
|
||||
endobj
|
||||
|
||||
21 0 obj
|
||||
12
|
||||
endobj
|
||||
|
||||
22 0 obj
|
||||
<<
|
||||
/BaseFont /ZapfDingbats
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
23 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
12.05
|
||||
12.05
|
||||
]
|
||||
/Resources 41 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 24 0 R
|
||||
>>
|
||||
stream
|
||||
/Tx BMC
|
||||
q BT
|
||||
0.18039 0.20392 0.21176 rg /ZaDi 12.05 Tf
|
||||
0 0 Td
|
||||
ET
|
||||
Q
|
||||
0 1 0 rg
|
||||
6 8.4 m 7.35 8.4 8.45 7.35 8.45 6 c
|
||||
8.45 4.65 7.35 3.55 6 3.55 c
|
||||
4.65 3.55 3.6 4.65 3.6 6 c
|
||||
3.6 7.35 4.65 8.4 6 8.4 c f*
|
||||
|
||||
EMC
|
||||
endstream
|
||||
endobj
|
||||
|
||||
24 0 obj
|
||||
202
|
||||
endobj
|
||||
|
||||
25 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
12.05
|
||||
12.05
|
||||
]
|
||||
/Resources 41 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 26 0 R
|
||||
>>
|
||||
stream
|
||||
/Tx BMC
|
||||
EMC
|
||||
endstream
|
||||
endobj
|
||||
|
||||
26 0 obj
|
||||
12
|
||||
endobj
|
||||
|
||||
27 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
12.05
|
||||
12.05
|
||||
]
|
||||
/Resources 41 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 28 0 R
|
||||
>>
|
||||
stream
|
||||
/Tx BMC
|
||||
q BT
|
||||
0.18039 0.20392 0.21176 rg /ZaDi 12.05 Tf
|
||||
0 0 Td
|
||||
ET
|
||||
Q
|
||||
0 0 1 rg
|
||||
6 8.4 m 7.35 8.4 8.45 7.35 8.45 6 c
|
||||
8.45 4.65 7.35 3.55 6 3.55 c
|
||||
4.65 3.55 3.6 4.65 3.6 6 c
|
||||
3.6 7.35 4.65 8.4 6 8.4 c f*
|
||||
|
||||
EMC
|
||||
endstream
|
||||
endobj
|
||||
|
||||
28 0 obj
|
||||
202
|
||||
endobj
|
||||
|
||||
29 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
12.05
|
||||
12.05
|
||||
]
|
||||
/Resources 41 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 30 0 R
|
||||
>>
|
||||
stream
|
||||
/Tx BMC
|
||||
EMC
|
||||
endstream
|
||||
endobj
|
||||
|
||||
30 0 obj
|
||||
12
|
||||
endobj
|
||||
|
||||
31 0 obj
|
||||
<<
|
||||
/Params <<
|
||||
/CheckSum <80a33fc110b5a7b8b4d58b8d57e814bc>
|
||||
/Size 22
|
||||
/Subtype /text#2fplain
|
||||
>>
|
||||
/Type /EmbeddedFile
|
||||
/Length 32 0 R
|
||||
>>
|
||||
stream
|
||||
content of attachment
|
||||
endstream
|
||||
endobj
|
||||
|
||||
32 0 obj
|
||||
22
|
||||
endobj
|
||||
|
||||
33 0 obj
|
||||
<<
|
||||
/A <<
|
||||
/S /URI
|
||||
/URI (https://www.qbilt.org/)
|
||||
>>
|
||||
/Border [
|
||||
0
|
||||
0
|
||||
.4
|
||||
]
|
||||
/C [
|
||||
.8
|
||||
.6
|
||||
.6
|
||||
]
|
||||
/H /I
|
||||
/Rect [
|
||||
72
|
||||
501.832
|
||||
374.4
|
||||
520.696
|
||||
]
|
||||
/Subtype /Link
|
||||
/Type /Annot
|
||||
>>
|
||||
endobj
|
||||
|
||||
34 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N 42 0 R
|
||||
>>
|
||||
/Contents (attachment1.txt)
|
||||
/FS 16 0 R
|
||||
/NM (attachment1.txt)
|
||||
/Rect [
|
||||
72
|
||||
400
|
||||
92
|
||||
420
|
||||
]
|
||||
/Subtype /FileAttachment
|
||||
/Type /Annot
|
||||
>>
|
||||
endobj
|
||||
|
||||
35 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N 44 0 R
|
||||
>>
|
||||
/DA ()
|
||||
/Rect [
|
||||
72
|
||||
350
|
||||
92
|
||||
360
|
||||
]
|
||||
/Subtype /FreeText
|
||||
/Type /Annot
|
||||
>>
|
||||
endobj
|
||||
|
||||
36 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N 46 0 R
|
||||
>>
|
||||
/DA ()
|
||||
/Rect [
|
||||
102
|
||||
350
|
||||
112
|
||||
370
|
||||
]
|
||||
/Subtype /FreeText
|
||||
/Type /Annot
|
||||
>>
|
||||
endobj
|
||||
|
||||
37 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N 48 0 R
|
||||
>>
|
||||
/DA ()
|
||||
/Rect [
|
||||
122
|
||||
350
|
||||
142
|
||||
360
|
||||
]
|
||||
/Subtype /FreeText
|
||||
/Type /Annot
|
||||
>>
|
||||
endobj
|
||||
|
||||
38 0 obj
|
||||
<<
|
||||
/AP <<
|
||||
/N 50 0 R
|
||||
>>
|
||||
/DA ()
|
||||
/Rect [
|
||||
152
|
||||
350
|
||||
162
|
||||
370
|
||||
]
|
||||
/Subtype /FreeText
|
||||
/Type /Annot
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Contents for page 1
|
||||
39 0 obj
|
||||
<<
|
||||
/Length 40 0 R
|
||||
>>
|
||||
stream
|
||||
q
|
||||
1 1 .7 rg
|
||||
.5 .5 0 RG
|
||||
72 470.77 118.8 14.15 re
|
||||
B
|
||||
Q
|
||||
q
|
||||
0 .5 .5 RG
|
||||
0 1 1 rg
|
||||
372 330.77 14.15 139.4 re
|
||||
B
|
||||
Q
|
||||
q
|
||||
1 0 0 RG
|
||||
72 310 20 10 re
|
||||
72 310 5 10 re
|
||||
S
|
||||
0 1 0 RG
|
||||
102 310 10 20 re
|
||||
102 310 10 5 re
|
||||
S
|
||||
0 0 1 RG
|
||||
122 310 20 10 re
|
||||
137 310 5 10 re
|
||||
S
|
||||
0.5 0 1 RG
|
||||
152 310 10 20 re
|
||||
152 325 10 5 re
|
||||
S
|
||||
10 w
|
||||
0.14 .33 .18 RG
|
||||
5 5 602 782 re
|
||||
S
|
||||
Q
|
||||
BT
|
||||
/F1 16 Tf
|
||||
20.6 TL
|
||||
170 650 Td
|
||||
(radio button 1) Tj
|
||||
(radio button 2) '
|
||||
(radio button 3) '
|
||||
1 0 0 1 72 546 Tm
|
||||
/F1 20 Tf
|
||||
(Thick green border surrounds page.) Tj
|
||||
0 -40 Td
|
||||
/F1 24 Tf
|
||||
0 0 1 rg
|
||||
(https://www.qbilt.org) Tj
|
||||
/F1 12 Tf
|
||||
1 0 0 1 202 474 Tm
|
||||
(<- Formy field in yellow) Tj
|
||||
1 0 0 1 392 410 Tm
|
||||
14.4 TL
|
||||
(<- Rot-ccw field) Tj
|
||||
(with "Rot" at bottom) '
|
||||
(and text going up) '
|
||||
0 g
|
||||
1 0 0 1 102 405 Tm
|
||||
(Arrow to the left points down.) Tj
|
||||
1 0 0 1 182 310 Tm
|
||||
(<- Drawn rectangles appear below annotations.) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
|
||||
40 0 obj
|
||||
874
|
||||
endobj
|
||||
|
||||
41 0 obj
|
||||
<<
|
||||
/Font 52 0 R
|
||||
/ProcSet [
|
||||
/PDF
|
||||
/Text
|
||||
]
|
||||
>>
|
||||
endobj
|
||||
|
||||
42 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
20
|
||||
20
|
||||
]
|
||||
/Resources <<
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 43 0 R
|
||||
>>
|
||||
stream
|
||||
0 10 m
|
||||
10 0 l
|
||||
20 10 l
|
||||
10 0 m
|
||||
10 20 l
|
||||
0 0 20 20 re
|
||||
S
|
||||
endstream
|
||||
endobj
|
||||
|
||||
43 0 obj
|
||||
52
|
||||
endobj
|
||||
|
||||
44 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
20
|
||||
10
|
||||
]
|
||||
/Resources 2 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 45 0 R
|
||||
>>
|
||||
stream
|
||||
1 0 0 RG
|
||||
0 0 20 10 re
|
||||
0 0 5 10 re
|
||||
S
|
||||
endstream
|
||||
endobj
|
||||
|
||||
45 0 obj
|
||||
36
|
||||
endobj
|
||||
|
||||
46 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
20
|
||||
10
|
||||
]
|
||||
/Matrix [
|
||||
0
|
||||
1
|
||||
-1
|
||||
0
|
||||
0
|
||||
0
|
||||
]
|
||||
/Resources 2 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 47 0 R
|
||||
>>
|
||||
stream
|
||||
0 1 0 RG
|
||||
0 0 20 10 re
|
||||
0 0 5 10 re
|
||||
S
|
||||
endstream
|
||||
endobj
|
||||
|
||||
47 0 obj
|
||||
36
|
||||
endobj
|
||||
|
||||
48 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
20
|
||||
10
|
||||
]
|
||||
/Matrix [
|
||||
-1
|
||||
0
|
||||
0
|
||||
-1
|
||||
0
|
||||
0
|
||||
]
|
||||
/Resources 2 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 49 0 R
|
||||
>>
|
||||
stream
|
||||
0 0 1 RG
|
||||
0 0 20 10 re
|
||||
0 0 5 10 re
|
||||
S
|
||||
endstream
|
||||
endobj
|
||||
|
||||
49 0 obj
|
||||
36
|
||||
endobj
|
||||
|
||||
50 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
20
|
||||
10
|
||||
]
|
||||
/Matrix [
|
||||
0
|
||||
-1
|
||||
1
|
||||
0
|
||||
0
|
||||
0
|
||||
]
|
||||
/Resources 2 0 R
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 51 0 R
|
||||
>>
|
||||
stream
|
||||
0.5 0 1 RG
|
||||
0 0 20 10 re
|
||||
0 0 5 10 re
|
||||
S
|
||||
endstream
|
||||
endobj
|
||||
|
||||
51 0 obj
|
||||
38
|
||||
endobj
|
||||
|
||||
52 0 obj
|
||||
<<
|
||||
/ZaDi 22 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 53
|
||||
0000000000 65535 f
|
||||
0000000025 00000 n
|
||||
0000000211 00000 n
|
||||
0000000263 00000 n
|
||||
0000000506 00000 n
|
||||
0000000755 00000 n
|
||||
0000000874 00000 n
|
||||
0000000944 00000 n
|
||||
0000001017 00000 n
|
||||
0000001121 00000 n
|
||||
0000001335 00000 n
|
||||
0000001355 00000 n
|
||||
0000001625 00000 n
|
||||
0000001645 00000 n
|
||||
0000001997 00000 n
|
||||
0000002349 00000 n
|
||||
0000002701 00000 n
|
||||
0000002842 00000 n
|
||||
0000003114 00000 n
|
||||
0000003473 00000 n
|
||||
0000003494 00000 n
|
||||
0000003663 00000 n
|
||||
0000003683 00000 n
|
||||
0000003764 00000 n
|
||||
0000004123 00000 n
|
||||
0000004144 00000 n
|
||||
0000004313 00000 n
|
||||
0000004333 00000 n
|
||||
0000004692 00000 n
|
||||
0000004713 00000 n
|
||||
0000004882 00000 n
|
||||
0000004902 00000 n
|
||||
0000005110 00000 n
|
||||
0000005130 00000 n
|
||||
0000005374 00000 n
|
||||
0000005578 00000 n
|
||||
0000005718 00000 n
|
||||
0000005860 00000 n
|
||||
0000006002 00000 n
|
||||
0000006167 00000 n
|
||||
0000007098 00000 n
|
||||
0000007119 00000 n
|
||||
0000007193 00000 n
|
||||
0000007397 00000 n
|
||||
0000007417 00000 n
|
||||
0000007603 00000 n
|
||||
0000007623 00000 n
|
||||
0000007862 00000 n
|
||||
0000007882 00000 n
|
||||
0000008122 00000 n
|
||||
0000008142 00000 n
|
||||
0000008383 00000 n
|
||||
0000008403 00000 n
|
||||
trailer <<
|
||||
/Root 1 0 R
|
||||
/Size 53
|
||||
/ID [<a2f146daeb6d814a742556489dab9882><7b639c67bfc16b5e891fa5468aac3a14>]
|
||||
>>
|
||||
startxref
|
||||
8441
|
||||
%%EOF
|
1894
qpdf/qtest/qpdf/rotated-shared-annotations-1.pdf
Normal file
1894
qpdf/qtest/qpdf/rotated-shared-annotations-1.pdf
Normal file
File diff suppressed because it is too large
Load Diff
2705
qpdf/qtest/qpdf/rotated-shared-annotations-2.pdf
Normal file
2705
qpdf/qtest/qpdf/rotated-shared-annotations-2.pdf
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user