Commit 832d792e4e88b85f4926e1241870de4d6ec2d772
1 parent
1f4771cd
Add CLI support for working with attachments
Showing
21 changed files
with
1809 additions
and
7 deletions
ChangeLog
| 1 | +2021-02-10 Jay Berkenbilt <ejb@ql.org> | |
| 2 | + | |
| 3 | + * Add new command-line arguments for operating on attachments: | |
| 4 | + --list-attachments, --add-attachment, --remove-attachment, | |
| 5 | + --copy-attachments-from. See --help and manual for details. | |
| 6 | + | |
| 1 | 7 | 2021-02-09 Jay Berkenbilt <ejb@ql.org> |
| 2 | 8 | |
| 3 | 9 | * Add methods to QUtil for working with PDF timestamp strings: | ... | ... |
manual/qpdf-manual.xml
| ... | ... | @@ -1801,6 +1801,181 @@ outfile.pdf</option> |
| 1801 | 1801 | </itemizedlist> |
| 1802 | 1802 | </para> |
| 1803 | 1803 | </sect1> |
| 1804 | + <sect1 id="ref.attachments"> | |
| 1805 | + <title>Embedded Files/Attachments Options</title> | |
| 1806 | + <para> | |
| 1807 | + Starting with qpdf 10.2, you can work with file attachments in PDF | |
| 1808 | + files from the command line. The following options are available: | |
| 1809 | + <variablelist> | |
| 1810 | + <varlistentry> | |
| 1811 | + <term><option>--list-attachments</option></term> | |
| 1812 | + <listitem> | |
| 1813 | + <para> | |
| 1814 | + Show the “key” and stream number for embedded | |
| 1815 | + files. With <option>--verbose</option>, additional | |
| 1816 | + information, including preferred file name, description, | |
| 1817 | + dates, and more are also displayed. The key is usually but not | |
| 1818 | + always equal to the file name, and is needed by some of the | |
| 1819 | + other options. | |
| 1820 | + </para> | |
| 1821 | + </listitem> | |
| 1822 | + </varlistentry> | |
| 1823 | + <varlistentry> | |
| 1824 | + <term><option>--show-attachment=<replaceable>key</replaceable></option></term> | |
| 1825 | + <listitem> | |
| 1826 | + <para> | |
| 1827 | + Write the contents of the specified attachment to standard | |
| 1828 | + output as binary data. The key should match one of the keys | |
| 1829 | + shown by <option>--list-attachments</option>. If specified | |
| 1830 | + multiple times, only the last attachment will be shown. | |
| 1831 | + </para> | |
| 1832 | + </listitem> | |
| 1833 | + </varlistentry> | |
| 1834 | + <varlistentry> | |
| 1835 | + <term><option>--add-attachment <replaceable>file</replaceable> <replaceable>options</replaceable> --</option></term> | |
| 1836 | + <listitem> | |
| 1837 | + <para> | |
| 1838 | + Add or replace an attachment with the contents of | |
| 1839 | + <replaceable>file</replaceable>. This may be specified more | |
| 1840 | + than once. The following additional options may appear before | |
| 1841 | + the <literal>--</literal> that ends this option: | |
| 1842 | + <variablelist> | |
| 1843 | + <varlistentry> | |
| 1844 | + <term><option>--key=<replaceable>key</replaceable></option></term> | |
| 1845 | + <listitem> | |
| 1846 | + <para> | |
| 1847 | + The key to use to register the attachment in the embedded | |
| 1848 | + files table. Defaults to the last path element of | |
| 1849 | + <replaceable>file</replaceable>. | |
| 1850 | + </para> | |
| 1851 | + </listitem> | |
| 1852 | + </varlistentry> | |
| 1853 | + <varlistentry> | |
| 1854 | + <term><option>--filename=<replaceable>name</replaceable></option></term> | |
| 1855 | + <listitem> | |
| 1856 | + <para> | |
| 1857 | + The file name to be used for the attachment. This is what is usually | |
| 1858 | + displayed to the user and is the name most graphical PDF | |
| 1859 | + viewers will use when saving a file. It defaults to the | |
| 1860 | + last path element of <replaceable>file</replaceable>. | |
| 1861 | + </para> | |
| 1862 | + </listitem> | |
| 1863 | + </varlistentry> | |
| 1864 | + <varlistentry> | |
| 1865 | + <term><option>--creationdate=<replaceable>date</replaceable></option></term> | |
| 1866 | + <listitem> | |
| 1867 | + <para> | |
| 1868 | + The attachment's creation date in PDF format; defaults to | |
| 1869 | + the current time. The date format is explained below. | |
| 1870 | + </para> | |
| 1871 | + </listitem> | |
| 1872 | + </varlistentry> | |
| 1873 | + <varlistentry> | |
| 1874 | + <term><option>--moddate=<replaceable>date</replaceable></option></term> | |
| 1875 | + <listitem> | |
| 1876 | + <para> | |
| 1877 | + The attachment's modification date in PDF format; defaults | |
| 1878 | + to the current time. The date format is explained below. | |
| 1879 | + </para> | |
| 1880 | + </listitem> | |
| 1881 | + </varlistentry> | |
| 1882 | + <varlistentry> | |
| 1883 | + <term><option>--mimetype=<replaceable>type/subtype</replaceable></option></term> | |
| 1884 | + <listitem> | |
| 1885 | + <para> | |
| 1886 | + The mime type for the attachment, e.g. | |
| 1887 | + <literal>text/plain</literal> or | |
| 1888 | + <literal>application/pdf</literal>. Note that the mimetype | |
| 1889 | + appears in a field called <literal>/Subtype</literal> in | |
| 1890 | + the PDF but actually includes the full type and subtype of | |
| 1891 | + the mime type. | |
| 1892 | + </para> | |
| 1893 | + </listitem> | |
| 1894 | + </varlistentry> | |
| 1895 | + <varlistentry> | |
| 1896 | + <term><option>--description=<replaceable>"text"</replaceable></option></term> | |
| 1897 | + <listitem> | |
| 1898 | + <para> | |
| 1899 | + Descriptive text for the attachment, displayed by some PDF | |
| 1900 | + viewers. | |
| 1901 | + </para> | |
| 1902 | + </listitem> | |
| 1903 | + </varlistentry> | |
| 1904 | + <varlistentry> | |
| 1905 | + <term><option>--replace</option></term> | |
| 1906 | + <listitem> | |
| 1907 | + <para> | |
| 1908 | + Indicates that any existing attachment with the same key | |
| 1909 | + should be replaced by the new attachment. Otherwise, | |
| 1910 | + <command>qpdf</command> gives an error if an attachment | |
| 1911 | + with that key is already present. | |
| 1912 | + </para> | |
| 1913 | + </listitem> | |
| 1914 | + </varlistentry> | |
| 1915 | + </variablelist> | |
| 1916 | + </para> | |
| 1917 | + </listitem> | |
| 1918 | + </varlistentry> | |
| 1919 | + <varlistentry> | |
| 1920 | + <term><option>--remove-attachment=<replaceable>key</replaceable></option></term> | |
| 1921 | + <listitem> | |
| 1922 | + <para> | |
| 1923 | + Remove the specified attachment. This doesn't only remove the | |
| 1924 | + attachment from the embedded files table but also clears out | |
| 1925 | + the file specification. That means that any potential internal | |
| 1926 | + links to the attachment will be broken. This option may be | |
| 1927 | + specified multiple times. Run with <option>--verbose</option> | |
| 1928 | + to see status of the removal. | |
| 1929 | + </para> | |
| 1930 | + </listitem> | |
| 1931 | + </varlistentry> | |
| 1932 | + <varlistentry> | |
| 1933 | + <term><option>--copy-attachments-from <replaceable>file</replaceable> <replaceable>options</replaceable> --</option></term> | |
| 1934 | + <listitem> | |
| 1935 | + <para> | |
| 1936 | + Copy attachments from another file. This may be specified more | |
| 1937 | + than once. The following additional options may appear before | |
| 1938 | + the <literal>--</literal> that ends this option: | |
| 1939 | + <variablelist> | |
| 1940 | + <varlistentry> | |
| 1941 | + <term><option>--password=<replaceable>password</replaceable></option></term> | |
| 1942 | + <listitem> | |
| 1943 | + <para> | |
| 1944 | + If required, the password needed to open | |
| 1945 | + <replaceable>file</replaceable> | |
| 1946 | + </para> | |
| 1947 | + </listitem> | |
| 1948 | + </varlistentry> | |
| 1949 | + <varlistentry> | |
| 1950 | + <term><option>--prefix=<replaceable>prefix</replaceable></option></term> | |
| 1951 | + <listitem> | |
| 1952 | + <para> | |
| 1953 | + Only required if the file from which attachments are being | |
| 1954 | + copied has attachments with keys that conflict with | |
| 1955 | + attachments already in the file. In this case, the | |
| 1956 | + specified prefix will be prepended to each key. This | |
| 1957 | + affects only the key in the embedded files table, not the | |
| 1958 | + file name. The PDF specification doesn't preclude multiple | |
| 1959 | + attachments having the same file name. | |
| 1960 | + </para> | |
| 1961 | + </listitem> | |
| 1962 | + </varlistentry> | |
| 1963 | + </variablelist> | |
| 1964 | + </para> | |
| 1965 | + </listitem> | |
| 1966 | + </varlistentry> | |
| 1967 | + </variablelist> | |
| 1968 | + When a date is required, the date should conform to the PDF date | |
| 1969 | + format specification, which is | |
| 1970 | + <literal>D:</literal><replaceable>yyyymmddhhmmss<z></replaceable>, | |
| 1971 | + where <replaceable><z></replaceable> is either | |
| 1972 | + <literal>Z</literal> for UTC or a timezone offset in the form | |
| 1973 | + <replaceable>-hh'mm'</replaceable> or | |
| 1974 | + <replaceable>+hh'mm'</replaceable>. Examples: | |
| 1975 | + <literal>D:20210207161528-05'00'</literal>, | |
| 1976 | + <literal>D:20210207211528Z</literal>. | |
| 1977 | + </para> | |
| 1978 | + </sect1> | |
| 1804 | 1979 | <sect1 id="ref.advanced-parsing"> |
| 1805 | 1980 | <title>Advanced Parsing Options</title> |
| 1806 | 1981 | <para> |
| ... | ... | @@ -4913,6 +5088,13 @@ print "\n"; |
| 4913 | 5088 | <itemizedlist> |
| 4914 | 5089 | <listitem> |
| 4915 | 5090 | <para> |
| 5091 | + Add new command line options for listing, saving, adding, | |
| 5092 | + removing, and and copying file attachments. See <xref | |
| 5093 | + linkend="ref.attachments"/> for details. | |
| 5094 | + </para> | |
| 5095 | + </listitem> | |
| 5096 | + <listitem> | |
| 5097 | + <para> | |
| 4916 | 5098 | The option |
| 4917 | 5099 | <option>--password-file=<replaceable>filename</replaceable></option> |
| 4918 | 5100 | can now be used to read the decryption password from a file. | ... | ... |
qpdf/qpdf.cc
| ... | ... | @@ -26,6 +26,7 @@ |
| 26 | 26 | #include <qpdf/QPDFExc.hh> |
| 27 | 27 | #include <qpdf/QPDFSystemError.hh> |
| 28 | 28 | #include <qpdf/QPDFCryptoProvider.hh> |
| 29 | +#include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> | |
| 29 | 30 | |
| 30 | 31 | #include <qpdf/QPDFWriter.hh> |
| 31 | 32 | #include <qpdf/QIntC.hh> |
| ... | ... | @@ -95,6 +96,31 @@ struct UnderOverlay |
| 95 | 96 | std::vector<int> repeat_pagenos; |
| 96 | 97 | }; |
| 97 | 98 | |
| 99 | +struct AddAttachment | |
| 100 | +{ | |
| 101 | + AddAttachment() : | |
| 102 | + replace(false) | |
| 103 | + { | |
| 104 | + } | |
| 105 | + | |
| 106 | + std::string path; | |
| 107 | + std::string key; | |
| 108 | + std::string filename; | |
| 109 | + std::string creationdate; | |
| 110 | + std::string moddate; | |
| 111 | + std::string mimetype; | |
| 112 | + std::string description; | |
| 113 | + bool replace; | |
| 114 | +}; | |
| 115 | + | |
| 116 | +struct CopyAttachmentFrom | |
| 117 | +{ | |
| 118 | + std::string path; | |
| 119 | + std::string password; | |
| 120 | + std::string prefix; | |
| 121 | +}; | |
| 122 | + | |
| 123 | + | |
| 98 | 124 | enum remove_unref_e { re_auto, re_yes, re_no }; |
| 99 | 125 | |
| 100 | 126 | struct Options |
| ... | ... | @@ -177,6 +203,7 @@ struct Options |
| 177 | 203 | show_page_images(false), |
| 178 | 204 | collate(false), |
| 179 | 205 | flatten_rotation(false), |
| 206 | + list_attachments(false), | |
| 180 | 207 | json(false), |
| 181 | 208 | check(false), |
| 182 | 209 | optimize_images(false), |
| ... | ... | @@ -282,6 +309,11 @@ struct Options |
| 282 | 309 | bool show_page_images; |
| 283 | 310 | bool collate; |
| 284 | 311 | bool flatten_rotation; |
| 312 | + bool list_attachments; | |
| 313 | + std::string attachment_to_show; | |
| 314 | + std::list<std::string> attachments_to_remove; | |
| 315 | + std::list<AddAttachment> attachments_to_add; | |
| 316 | + std::list<CopyAttachmentFrom> attachments_to_copy; | |
| 285 | 317 | bool json; |
| 286 | 318 | std::set<std::string> json_keys; |
| 287 | 319 | std::set<std::string> json_objects; |
| ... | ... | @@ -758,6 +790,11 @@ class ArgParser |
| 758 | 790 | void argRotate(char* parameter); |
| 759 | 791 | void argCollate(); |
| 760 | 792 | void argFlattenRotation(); |
| 793 | + void argListAttachments(); | |
| 794 | + void argShowAttachment(char* parameter); | |
| 795 | + void argRemoveAttachment(char* parameter); | |
| 796 | + void argAddAttachment(); | |
| 797 | + void argCopyAttachments(); | |
| 761 | 798 | void argStreamData(char* parameter); |
| 762 | 799 | void argCompressStreams(char* parameter); |
| 763 | 800 | void argRecompressFlate(); |
| ... | ... | @@ -838,6 +875,19 @@ class ArgParser |
| 838 | 875 | void argReplaceInput(); |
| 839 | 876 | void argIsEncrypted(); |
| 840 | 877 | void argRequiresPassword(); |
| 878 | + void argAApositional(char* arg); | |
| 879 | + void argAAKey(char* parameter); | |
| 880 | + void argAAFilename(char* parameter); | |
| 881 | + void argAACreationDate(char* parameter); | |
| 882 | + void argAAModDate(char* parameter); | |
| 883 | + void argAAMimeType(char* parameter); | |
| 884 | + void argAADescription(char* parameter); | |
| 885 | + void argAAReplace(); | |
| 886 | + void argEndAddAttachment(); | |
| 887 | + void argCApositional(char* arg); | |
| 888 | + void argCAprefix(char* parameter); | |
| 889 | + void argCApassword(char* parameter); | |
| 890 | + void argEndCopyAttachments(); | |
| 841 | 891 | |
| 842 | 892 | void usage(std::string const& message); |
| 843 | 893 | void checkCompletion(); |
| ... | ... | @@ -874,6 +924,8 @@ class ArgParser |
| 874 | 924 | std::map<std::string, OptionEntry> encrypt128_option_table; |
| 875 | 925 | std::map<std::string, OptionEntry> encrypt256_option_table; |
| 876 | 926 | std::map<std::string, OptionEntry> under_overlay_option_table; |
| 927 | + std::map<std::string, OptionEntry> add_attachment_option_table; | |
| 928 | + std::map<std::string, OptionEntry> copy_attachments_option_table; | |
| 877 | 929 | std::vector<PointerHolder<char> > new_argv; |
| 878 | 930 | std::vector<PointerHolder<char> > bash_argv; |
| 879 | 931 | PointerHolder<char*> argv_ph; |
| ... | ... | @@ -982,6 +1034,13 @@ ArgParser::initOptionTable() |
| 982 | 1034 | {"compress", "preserve", "uncompress", 0}; |
| 983 | 1035 | (*t)["collate"] = oe_bare(&ArgParser::argCollate); |
| 984 | 1036 | (*t)["flatten-rotation"] = oe_bare(&ArgParser::argFlattenRotation); |
| 1037 | + (*t)["list-attachments"] = oe_bare(&ArgParser::argListAttachments); | |
| 1038 | + (*t)["show-attachment"] = oe_requiredParameter( | |
| 1039 | + &ArgParser::argShowAttachment, "attachment-key"); | |
| 1040 | + (*t)["remove-attachment"] = oe_requiredParameter( | |
| 1041 | + &ArgParser::argRemoveAttachment, "attachment-key"); | |
| 1042 | + (*t)["add-attachment"] = oe_bare(&ArgParser::argAddAttachment); | |
| 1043 | + (*t)["copy-attachments-from"] = oe_bare(&ArgParser::argCopyAttachments); | |
| 985 | 1044 | (*t)["stream-data"] = oe_requiredChoices( |
| 986 | 1045 | &ArgParser::argStreamData, stream_data_choices); |
| 987 | 1046 | (*t)["compress-streams"] = oe_requiredChoices( |
| ... | ... | @@ -1129,6 +1188,31 @@ ArgParser::initOptionTable() |
| 1129 | 1188 | (*t)["password"] = oe_requiredParameter( |
| 1130 | 1189 | &ArgParser::argUOpassword, "password"); |
| 1131 | 1190 | (*t)["--"] = oe_bare(&ArgParser::argEndUnderOverlay); |
| 1191 | + | |
| 1192 | + t = &this->add_attachment_option_table; | |
| 1193 | + (*t)[""] = oe_positional(&ArgParser::argAApositional); | |
| 1194 | + (*t)["key"] = oe_requiredParameter( | |
| 1195 | + &ArgParser::argAAKey, "attachment-key"); | |
| 1196 | + (*t)["filename"] = oe_requiredParameter( | |
| 1197 | + &ArgParser::argAAFilename, "filename"); | |
| 1198 | + (*t)["creationdate"] = oe_requiredParameter( | |
| 1199 | + &ArgParser::argAACreationDate, "creation-date"); | |
| 1200 | + (*t)["moddate"] = oe_requiredParameter( | |
| 1201 | + &ArgParser::argAAModDate, "modification-date"); | |
| 1202 | + (*t)["mimetype"] = oe_requiredParameter( | |
| 1203 | + &ArgParser::argAAMimeType, "mime/type"); | |
| 1204 | + (*t)["description"] = oe_requiredParameter( | |
| 1205 | + &ArgParser::argAADescription, "description"); | |
| 1206 | + (*t)["replace"] = oe_bare(&ArgParser::argAAReplace); | |
| 1207 | + (*t)["--"] = oe_bare(&ArgParser::argEndAddAttachment); | |
| 1208 | + | |
| 1209 | + t = &this->copy_attachments_option_table; | |
| 1210 | + (*t)[""] = oe_positional(&ArgParser::argCApositional); | |
| 1211 | + (*t)["prefix"] = oe_requiredParameter( | |
| 1212 | + &ArgParser::argCAprefix, "prefix"); | |
| 1213 | + (*t)["password"] = oe_requiredParameter( | |
| 1214 | + &ArgParser::argCApassword, "password"); | |
| 1215 | + (*t)["--"] = oe_bare(&ArgParser::argEndCopyAttachments); | |
| 1132 | 1216 | } |
| 1133 | 1217 | |
| 1134 | 1218 | void |
| ... | ... | @@ -1361,7 +1445,6 @@ ArgParser::argHelp() |
| 1361 | 1445 | << " --allow-insecure allow the owner password to be empty when the\n" |
| 1362 | 1446 | << " user password is not empty\n" |
| 1363 | 1447 | << "\n" |
| 1364 | - << "\n" | |
| 1365 | 1448 | << " print-opt may be:\n" |
| 1366 | 1449 | << "\n" |
| 1367 | 1450 | << " full allow full printing\n" |
| ... | ... | @@ -1487,6 +1570,55 @@ ArgParser::argHelp() |
| 1487 | 1570 | << " any \"from\" pages have been exhausted\n" |
| 1488 | 1571 | << "\n" |
| 1489 | 1572 | << "\n" |
| 1573 | + << "Embedded Files/Attachments Options\n" | |
| 1574 | + << "----------------------------------\n" | |
| 1575 | + << "\n" | |
| 1576 | + << "These options can be used to work with embedded files, also known as\n" | |
| 1577 | + << "attachments.\n" | |
| 1578 | + << "\n" | |
| 1579 | + << "--list-attachments show key and stream number for embedded files;\n" | |
| 1580 | + << " combine with --verbose for more detailed information\n" | |
| 1581 | + << "--show-attachment=key write the contents of the specified attachment to\n" | |
| 1582 | + << " standard output as binary data\n" | |
| 1583 | + << "--add-attachment file options --\n" | |
| 1584 | + << " add or replace an attachment\n" | |
| 1585 | + << "--remove-attachment=key remove the specified attachment; repeatable\n" | |
| 1586 | + << "--copy-attachments-from file options --\n" | |
| 1587 | + << " copy attachments from another file\n" | |
| 1588 | + << "\n" | |
| 1589 | + << "The \"key\" option is the unique name under which the attachment is registered\n" | |
| 1590 | + << "within the PDF file. You can get this using the --list-attachments option. This\n" | |
| 1591 | + << "is usually the same as the filename, but it doesn't have to be.\n" | |
| 1592 | + << "\n" | |
| 1593 | + << "Options for adding attachments:\n" | |
| 1594 | + << "\n" | |
| 1595 | + << " file path to the file to attach\n" | |
| 1596 | + << " --key=key the name of this in the embedded files table;\n" | |
| 1597 | + << " defaults to the last path element of file\n" | |
| 1598 | + << " --filename=name the file name of the attachment; this is what is\n" | |
| 1599 | + << " usually displayed to the user; defaults to the\n" | |
| 1600 | + << " last path element of file\n" | |
| 1601 | + << " --creationdate=date creation date in PDF format; defaults to the\n" | |
| 1602 | + << " current time\n" | |
| 1603 | + << " --moddate=date modification date in PDF format; defaults to the\n" | |
| 1604 | + << " current time\n" | |
| 1605 | + << " --mimetype=type/subtype mime type of attachment (e.g. application/pdf)\n" | |
| 1606 | + << " --description=\"text\" attachment description\n" | |
| 1607 | + << " --replace replace any existing attachment with the same key\n" | |
| 1608 | + << "\n" | |
| 1609 | + << "Options for copying attachments:\n" | |
| 1610 | + << "\n" | |
| 1611 | + << " file file whose attachments should be copied\n" | |
| 1612 | + << " --password=password password to open the other file, if needed\n" | |
| 1613 | + << " --prefix=prefix a prefix to insert in front of each key;\n" | |
| 1614 | + << " required if needed to ensure each attachment\n" | |
| 1615 | + << " has a unique key\n" | |
| 1616 | + << "\n" | |
| 1617 | + << "Date format: D:yyyymmddhhmmss<z> where <z> is either Z for UTC or a timezone\n" | |
| 1618 | + << "offset in the form -hh'mm' or +hh'mm'.\n" | |
| 1619 | + << "Examples: D:20210207161528-05'00', D:20210207211528Z\n" | |
| 1620 | + << "\n" | |
| 1621 | + << "\n" | |
| 1490 | 1622 | << "Advanced Parsing Options\n" |
| 1491 | 1623 | << "------------------------\n" |
| 1492 | 1624 | << "\n" |
| ... | ... | @@ -1961,6 +2093,40 @@ ArgParser::argFlattenRotation() |
| 1961 | 2093 | } |
| 1962 | 2094 | |
| 1963 | 2095 | void |
| 2096 | +ArgParser::argListAttachments() | |
| 2097 | +{ | |
| 2098 | + o.list_attachments = true; | |
| 2099 | + o.require_outfile = false; | |
| 2100 | +} | |
| 2101 | + | |
| 2102 | +void | |
| 2103 | +ArgParser::argShowAttachment(char* parameter) | |
| 2104 | +{ | |
| 2105 | + o.attachment_to_show = parameter; | |
| 2106 | + o.require_outfile = false; | |
| 2107 | +} | |
| 2108 | + | |
| 2109 | +void | |
| 2110 | +ArgParser::argRemoveAttachment(char* parameter) | |
| 2111 | +{ | |
| 2112 | + o.attachments_to_remove.push_back(parameter); | |
| 2113 | +} | |
| 2114 | + | |
| 2115 | +void | |
| 2116 | +ArgParser::argAddAttachment() | |
| 2117 | +{ | |
| 2118 | + this->option_table = &(this->add_attachment_option_table); | |
| 2119 | + o.attachments_to_add.push_back(AddAttachment()); | |
| 2120 | +} | |
| 2121 | + | |
| 2122 | +void | |
| 2123 | +ArgParser::argCopyAttachments() | |
| 2124 | +{ | |
| 2125 | + this->option_table = &(this->copy_attachments_option_table); | |
| 2126 | + o.attachments_to_copy.push_back(CopyAttachmentFrom()); | |
| 2127 | +} | |
| 2128 | + | |
| 2129 | +void | |
| 1964 | 2130 | ArgParser::argStreamData(char* parameter) |
| 1965 | 2131 | { |
| 1966 | 2132 | o.stream_data_set = true; |
| ... | ... | @@ -2618,6 +2784,134 @@ ArgParser::argRequiresPassword() |
| 2618 | 2784 | } |
| 2619 | 2785 | |
| 2620 | 2786 | void |
| 2787 | +ArgParser::argAApositional(char* arg) | |
| 2788 | +{ | |
| 2789 | + o.attachments_to_add.back().path = arg; | |
| 2790 | +} | |
| 2791 | + | |
| 2792 | +void | |
| 2793 | +ArgParser::argAAKey(char* parameter) | |
| 2794 | +{ | |
| 2795 | + o.attachments_to_add.back().key = parameter; | |
| 2796 | +} | |
| 2797 | + | |
| 2798 | +void | |
| 2799 | +ArgParser::argAAFilename(char* parameter) | |
| 2800 | +{ | |
| 2801 | + o.attachments_to_add.back().filename = parameter; | |
| 2802 | +} | |
| 2803 | + | |
| 2804 | +void | |
| 2805 | +ArgParser::argAACreationDate(char* parameter) | |
| 2806 | +{ | |
| 2807 | + if (! QUtil::pdf_time_to_qpdf_time(parameter)) | |
| 2808 | + { | |
| 2809 | + usage(std::string(parameter) + " is not a valid PDF timestamp"); | |
| 2810 | + } | |
| 2811 | + o.attachments_to_add.back().creationdate = parameter; | |
| 2812 | +} | |
| 2813 | + | |
| 2814 | +void | |
| 2815 | +ArgParser::argAAModDate(char* parameter) | |
| 2816 | +{ | |
| 2817 | + if (! QUtil::pdf_time_to_qpdf_time(parameter)) | |
| 2818 | + { | |
| 2819 | + usage(std::string(parameter) + " is not a valid PDF timestamp"); | |
| 2820 | + } | |
| 2821 | + o.attachments_to_add.back().moddate = parameter; | |
| 2822 | +} | |
| 2823 | + | |
| 2824 | +void | |
| 2825 | +ArgParser::argAAMimeType(char* parameter) | |
| 2826 | +{ | |
| 2827 | + if (strchr(parameter, '/') == nullptr) | |
| 2828 | + { | |
| 2829 | + usage("mime type should be specified as type/subtype"); | |
| 2830 | + } | |
| 2831 | + o.attachments_to_add.back().mimetype = parameter; | |
| 2832 | +} | |
| 2833 | + | |
| 2834 | +void | |
| 2835 | +ArgParser::argAADescription(char* parameter) | |
| 2836 | +{ | |
| 2837 | + o.attachments_to_add.back().description = parameter; | |
| 2838 | +} | |
| 2839 | + | |
| 2840 | +void | |
| 2841 | +ArgParser::argAAReplace() | |
| 2842 | +{ | |
| 2843 | + o.attachments_to_add.back().replace = true; | |
| 2844 | +} | |
| 2845 | + | |
| 2846 | +void | |
| 2847 | +ArgParser::argEndAddAttachment() | |
| 2848 | +{ | |
| 2849 | + static std::string now = QUtil::qpdf_time_to_pdf_time( | |
| 2850 | + QUtil::get_current_qpdf_time()); | |
| 2851 | + this->option_table = &(this->main_option_table); | |
| 2852 | + auto& cur = o.attachments_to_add.back(); | |
| 2853 | + if (cur.path.empty()) | |
| 2854 | + { | |
| 2855 | + usage("add attachment: no path specified"); | |
| 2856 | + } | |
| 2857 | + std::string last_element = cur.path; | |
| 2858 | + size_t pathsep = cur.path.find_last_of("/\\"); | |
| 2859 | + if (pathsep != std::string::npos) | |
| 2860 | + { | |
| 2861 | + last_element = cur.path.substr(pathsep + 1); | |
| 2862 | + if (last_element.empty()) | |
| 2863 | + { | |
| 2864 | + usage("path for --add-attachment may not end" | |
| 2865 | + " with a path separator"); | |
| 2866 | + } | |
| 2867 | + } | |
| 2868 | + if (cur.filename.empty()) | |
| 2869 | + { | |
| 2870 | + cur.filename = last_element; | |
| 2871 | + } | |
| 2872 | + if (cur.key.empty()) | |
| 2873 | + { | |
| 2874 | + cur.key = last_element; | |
| 2875 | + } | |
| 2876 | + if (cur.creationdate.empty()) | |
| 2877 | + { | |
| 2878 | + cur.creationdate = now; | |
| 2879 | + } | |
| 2880 | + if (cur.moddate.empty()) | |
| 2881 | + { | |
| 2882 | + cur.moddate = now; | |
| 2883 | + } | |
| 2884 | +} | |
| 2885 | + | |
| 2886 | +void | |
| 2887 | +ArgParser::argCApositional(char* arg) | |
| 2888 | +{ | |
| 2889 | + o.attachments_to_copy.back().path = arg; | |
| 2890 | +} | |
| 2891 | + | |
| 2892 | +void | |
| 2893 | +ArgParser::argCAprefix(char* parameter) | |
| 2894 | +{ | |
| 2895 | + o.attachments_to_copy.back().prefix = parameter; | |
| 2896 | +} | |
| 2897 | + | |
| 2898 | +void | |
| 2899 | +ArgParser::argCApassword(char* parameter) | |
| 2900 | +{ | |
| 2901 | + o.attachments_to_copy.back().password = parameter; | |
| 2902 | +} | |
| 2903 | + | |
| 2904 | +void | |
| 2905 | +ArgParser::argEndCopyAttachments() | |
| 2906 | +{ | |
| 2907 | + this->option_table = &(this->main_option_table); | |
| 2908 | + if (o.attachments_to_copy.back().path.empty()) | |
| 2909 | + { | |
| 2910 | + usage("copy attachments: no path specified"); | |
| 2911 | + } | |
| 2912 | +} | |
| 2913 | + | |
| 2914 | +void | |
| 2621 | 2915 | ArgParser::handleArgFileArguments() |
| 2622 | 2916 | { |
| 2623 | 2917 | // Support reading arguments from files. Create a new argv. Ensure |
| ... | ... | @@ -3768,6 +4062,66 @@ static void do_show_pages(QPDF& pdf, Options& o) |
| 3768 | 4062 | } |
| 3769 | 4063 | } |
| 3770 | 4064 | |
| 4065 | +static void do_list_attachments(QPDF& pdf, Options& o) | |
| 4066 | +{ | |
| 4067 | + QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 4068 | + if (efdh.hasEmbeddedFiles()) | |
| 4069 | + { | |
| 4070 | + for (auto const& i: efdh.getEmbeddedFiles()) | |
| 4071 | + { | |
| 4072 | + std::string const& key = i.first; | |
| 4073 | + auto efoh = i.second; | |
| 4074 | + std::cout << key << " -> " | |
| 4075 | + << efoh->getEmbeddedFileStream().getObjGen() | |
| 4076 | + << std::endl; | |
| 4077 | + if (o.verbose) | |
| 4078 | + { | |
| 4079 | + auto desc = efoh->getDescription(); | |
| 4080 | + if (! desc.empty()) | |
| 4081 | + { | |
| 4082 | + std::cout << " description: " << desc << std::endl; | |
| 4083 | + } | |
| 4084 | + std::cout << " preferred name: " << efoh->getFilename() | |
| 4085 | + << std::endl; | |
| 4086 | + std::cout << " all names:" << std::endl; | |
| 4087 | + for (auto const& i2: efoh->getFilenames()) | |
| 4088 | + { | |
| 4089 | + std::cout << " " << i2.first << " -> " << i2.second | |
| 4090 | + << std::endl; | |
| 4091 | + } | |
| 4092 | + std::cout << " all data streams:" << std::endl; | |
| 4093 | + for (auto i2: QPDFDictItems(efoh->getEmbeddedFileStreams())) | |
| 4094 | + { | |
| 4095 | + std::cout << " " << i2.first << " -> " | |
| 4096 | + << i2.second.getObjGen() | |
| 4097 | + << std::endl; | |
| 4098 | + } | |
| 4099 | + } | |
| 4100 | + } | |
| 4101 | + } | |
| 4102 | + else | |
| 4103 | + { | |
| 4104 | + std::cout << o.infilename << " has no embedded files" << std::endl; | |
| 4105 | + } | |
| 4106 | +} | |
| 4107 | + | |
| 4108 | +static void do_show_attachment(QPDF& pdf, Options& o, int& exit_code) | |
| 4109 | +{ | |
| 4110 | + QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 4111 | + auto fs = efdh.getEmbeddedFile(o.attachment_to_show); | |
| 4112 | + if (! fs) | |
| 4113 | + { | |
| 4114 | + std::cerr << whoami << ": attachment " << o.attachment_to_show | |
| 4115 | + << " not found" << std::endl; | |
| 4116 | + exit_code = EXIT_ERROR; | |
| 4117 | + return; | |
| 4118 | + } | |
| 4119 | + auto efs = fs->getEmbeddedFileStream(); | |
| 4120 | + QUtil::binary_stdout(); | |
| 4121 | + Pl_StdioFile out("stdout", stdout); | |
| 4122 | + efs.pipeStreamData(&out, 0, qpdf_dl_all); | |
| 4123 | +} | |
| 4124 | + | |
| 3771 | 4125 | static std::set<QPDFObjGen> |
| 3772 | 4126 | get_wanted_json_objects(Options& o) |
| 3773 | 4127 | { |
| ... | ... | @@ -4354,6 +4708,14 @@ static void do_inspection(QPDF& pdf, Options& o) |
| 4354 | 4708 | { |
| 4355 | 4709 | do_show_pages(pdf, o); |
| 4356 | 4710 | } |
| 4711 | + if (o.list_attachments) | |
| 4712 | + { | |
| 4713 | + do_list_attachments(pdf, o); | |
| 4714 | + } | |
| 4715 | + if (! o.attachment_to_show.empty()) | |
| 4716 | + { | |
| 4717 | + do_show_attachment(pdf, o, exit_code); | |
| 4718 | + } | |
| 4357 | 4719 | if ((! pdf.getWarnings().empty()) && (exit_code != EXIT_ERROR)) |
| 4358 | 4720 | { |
| 4359 | 4721 | std::cerr << whoami |
| ... | ... | @@ -4858,7 +5220,106 @@ static void handle_under_overlay(QPDF& pdf, Options& o) |
| 4858 | 5220 | } |
| 4859 | 5221 | } |
| 4860 | 5222 | |
| 4861 | -static void handle_transformations(QPDF& pdf, Options& o) | |
| 5223 | +static void maybe_set_pagemode(QPDF& pdf, std::string const& pagemode) | |
| 5224 | +{ | |
| 5225 | + auto root = pdf.getRoot(); | |
| 5226 | + if (root.getKey("/PageMode").isNull()) | |
| 5227 | + { | |
| 5228 | + root.replaceKey("/PageMode", QPDFObjectHandle::newName(pagemode)); | |
| 5229 | + } | |
| 5230 | +} | |
| 5231 | + | |
| 5232 | +static void add_attachments(QPDF& pdf, Options& o, int& exit_code) | |
| 5233 | +{ | |
| 5234 | + maybe_set_pagemode(pdf, "/UseAttachments"); | |
| 5235 | + QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 5236 | + for (auto const& to_add: o.attachments_to_add) | |
| 5237 | + { | |
| 5238 | + if ((! to_add.replace) && efdh.getEmbeddedFile(to_add.key)) | |
| 5239 | + { | |
| 5240 | + std::cerr << whoami << ": " << pdf.getFilename() | |
| 5241 | + << " already has an attachment with key = " | |
| 5242 | + << to_add.key << "; use --replace to replace" | |
| 5243 | + << " or --key to specificy a different key" | |
| 5244 | + << std::endl; | |
| 5245 | + exit_code = EXIT_ERROR; | |
| 5246 | + continue; | |
| 5247 | + } | |
| 5248 | + | |
| 5249 | + auto fs = QPDFFileSpecObjectHelper::createFileSpec( | |
| 5250 | + pdf, to_add.filename, to_add.path); | |
| 5251 | + if (! to_add.description.empty()) | |
| 5252 | + { | |
| 5253 | + fs.setDescription(to_add.description); | |
| 5254 | + } | |
| 5255 | + auto efs = QPDFEFStreamObjectHelper(fs.getEmbeddedFileStream()); | |
| 5256 | + efs.setCreationDate(to_add.creationdate) | |
| 5257 | + .setModDate(to_add.moddate); | |
| 5258 | + if (! to_add.mimetype.empty()) | |
| 5259 | + { | |
| 5260 | + efs.setSubtype(to_add.mimetype); | |
| 5261 | + } | |
| 5262 | + | |
| 5263 | + efdh.replaceEmbeddedFile(to_add.key, fs); | |
| 5264 | + if (o.verbose) | |
| 5265 | + { | |
| 5266 | + std::cout << whoami << ": attached " << to_add.path | |
| 5267 | + << " as " << to_add.filename | |
| 5268 | + << " with key " << to_add.key << std::endl; | |
| 5269 | + } | |
| 5270 | + } | |
| 5271 | +} | |
| 5272 | + | |
| 5273 | +static void copy_attachments(QPDF& pdf, Options& o, int& exit_code) | |
| 5274 | +{ | |
| 5275 | + maybe_set_pagemode(pdf, "/UseAttachments"); | |
| 5276 | + QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 5277 | + for (auto const& to_copy: o.attachments_to_copy) | |
| 5278 | + { | |
| 5279 | + auto other = process_file( | |
| 5280 | + to_copy.path.c_str(), to_copy.password.c_str(), o); | |
| 5281 | + QPDFEmbeddedFileDocumentHelper other_efdh(*other); | |
| 5282 | + auto other_attachments = other_efdh.getEmbeddedFiles(); | |
| 5283 | + for (auto const& iter: other_attachments) | |
| 5284 | + { | |
| 5285 | + if (o.verbose) | |
| 5286 | + { | |
| 5287 | + std::cout << whoami << ": copying attachments from " | |
| 5288 | + << to_copy.path << std::endl; | |
| 5289 | + } | |
| 5290 | + std::string new_key = to_copy.prefix + iter.first; | |
| 5291 | + if (efdh.getEmbeddedFile(new_key)) | |
| 5292 | + { | |
| 5293 | + exit_code = EXIT_ERROR; | |
| 5294 | + std::cerr << whoami << to_copy.path << " and " | |
| 5295 | + << pdf.getFilename() | |
| 5296 | + << " both have attachments with key " << new_key | |
| 5297 | + << "; use --prefix with --copy-attachments-from" | |
| 5298 | + << " or manually copy individual attachments" | |
| 5299 | + << std::endl; | |
| 5300 | + } | |
| 5301 | + else | |
| 5302 | + { | |
| 5303 | + auto new_fs_oh = pdf.copyForeignObject( | |
| 5304 | + iter.second->getObjectHandle()); | |
| 5305 | + efdh.replaceEmbeddedFile( | |
| 5306 | + new_key, QPDFFileSpecObjectHelper(new_fs_oh)); | |
| 5307 | + if (o.verbose) | |
| 5308 | + { | |
| 5309 | + std::cout << " " << iter.first << " -> " << new_key | |
| 5310 | + << std::endl; | |
| 5311 | + } | |
| 5312 | + } | |
| 5313 | + } | |
| 5314 | + | |
| 5315 | + if ((other->anyWarnings()) && (exit_code == 0)) | |
| 5316 | + { | |
| 5317 | + exit_code = EXIT_WARNING; | |
| 5318 | + } | |
| 5319 | + } | |
| 5320 | +} | |
| 5321 | + | |
| 5322 | +static void handle_transformations(QPDF& pdf, Options& o, int& exit_code) | |
| 4862 | 5323 | { |
| 4863 | 5324 | QPDFPageDocumentHelper dh(pdf); |
| 4864 | 5325 | if (o.externalize_inline_images) |
| ... | ... | @@ -4935,6 +5396,35 @@ static void handle_transformations(QPDF& pdf, Options& o) |
| 4935 | 5396 | { |
| 4936 | 5397 | pdf.getRoot().removeKey("/PageLabels"); |
| 4937 | 5398 | } |
| 5399 | + if (! o.attachments_to_remove.empty()) | |
| 5400 | + { | |
| 5401 | + QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 5402 | + for (auto const& key: o.attachments_to_remove) | |
| 5403 | + { | |
| 5404 | + if (efdh.removeEmbeddedFile(key)) | |
| 5405 | + { | |
| 5406 | + if (o.verbose) | |
| 5407 | + { | |
| 5408 | + std::cout << whoami << | |
| 5409 | + ": removed attachment " << key << std::endl; | |
| 5410 | + } | |
| 5411 | + } | |
| 5412 | + else | |
| 5413 | + { | |
| 5414 | + std::cerr << whoami << | |
| 5415 | + ": attachment " << key << " not found" << std::endl; | |
| 5416 | + exit_code = EXIT_ERROR; | |
| 5417 | + } | |
| 5418 | + } | |
| 5419 | + } | |
| 5420 | + if (! o.attachments_to_add.empty()) | |
| 5421 | + { | |
| 5422 | + add_attachments(pdf, o, exit_code); | |
| 5423 | + } | |
| 5424 | + if (! o.attachments_to_copy.empty()) | |
| 5425 | + { | |
| 5426 | + copy_attachments(pdf, o, exit_code); | |
| 5427 | + } | |
| 4938 | 5428 | } |
| 4939 | 5429 | |
| 4940 | 5430 | static bool should_remove_unreferenced_resources(QPDF& pdf, Options& o) |
| ... | ... | @@ -5854,6 +6344,7 @@ int realmain(int argc, char* argv[]) |
| 5854 | 6344 | Options o; |
| 5855 | 6345 | ArgParser ap(argc, argv, o); |
| 5856 | 6346 | |
| 6347 | + int exit_code = 0; | |
| 5857 | 6348 | try |
| 5858 | 6349 | { |
| 5859 | 6350 | ap.parseOptions(); |
| ... | ... | @@ -5906,7 +6397,7 @@ int realmain(int argc, char* argv[]) |
| 5906 | 6397 | handle_rotations(pdf, o); |
| 5907 | 6398 | } |
| 5908 | 6399 | handle_under_overlay(pdf, o); |
| 5909 | - handle_transformations(pdf, o); | |
| 6400 | + handle_transformations(pdf, o, exit_code); | |
| 5910 | 6401 | |
| 5911 | 6402 | if ((o.outfilename == 0) && (! o.replace_input)) |
| 5912 | 6403 | { |
| ... | ... | @@ -5929,7 +6420,10 @@ int realmain(int argc, char* argv[]) |
| 5929 | 6420 | << std::endl; |
| 5930 | 6421 | } |
| 5931 | 6422 | // Still return with warning code even if warnings were suppressed. |
| 5932 | - return EXIT_WARNING; | |
| 6423 | + if (exit_code == 0) | |
| 6424 | + { | |
| 6425 | + exit_code = EXIT_WARNING; | |
| 6426 | + } | |
| 5933 | 6427 | } |
| 5934 | 6428 | } |
| 5935 | 6429 | catch (std::exception& e) |
| ... | ... | @@ -5938,7 +6432,7 @@ int realmain(int argc, char* argv[]) |
| 5938 | 6432 | return EXIT_ERROR; |
| 5939 | 6433 | } |
| 5940 | 6434 | |
| 5941 | - return 0; | |
| 6435 | + return exit_code; | |
| 5942 | 6436 | } |
| 5943 | 6437 | |
| 5944 | 6438 | #ifdef WINDOWS_WMAIN | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -523,7 +523,7 @@ $td->runtest("page operations on form xobject", |
| 523 | 523 | show_ntests(); |
| 524 | 524 | # ---------- |
| 525 | 525 | $td->notify("--- File Attachments ---"); |
| 526 | -$n_tests += 4; | |
| 526 | +$n_tests += 33; | |
| 527 | 527 | |
| 528 | 528 | open(F, ">auto-txt") or die; |
| 529 | 529 | print F "from file"; |
| ... | ... | @@ -532,16 +532,183 @@ $td->runtest("attachments", |
| 532 | 532 | {$td->COMMAND => "test_driver 76 minimal.pdf auto-txt"}, |
| 533 | 533 | {$td->FILE => "test76.out", $td->EXIT_STATUS => 0}, |
| 534 | 534 | $td->NORMALIZE_NEWLINES); |
| 535 | +$td->runtest("show attachment", | |
| 536 | + {$td->COMMAND => "qpdf --show-attachment=att1 a.pdf"}, | |
| 537 | + {$td->STRING => "from file", $td->EXIT_STATUS => 0}, | |
| 538 | + $td->NORMALIZE_NEWLINES); | |
| 535 | 539 | $td->runtest("check output", |
| 536 | 540 | {$td->FILE => "a.pdf"}, |
| 537 | 541 | {$td->FILE => "test76.pdf"}); |
| 538 | -$td->runtest("attachments", | |
| 542 | +$td->runtest("list attachments", | |
| 543 | + {$td->COMMAND => "qpdf --list-attachments a.pdf"}, | |
| 544 | + {$td->FILE => "test76-list.out", $td->EXIT_STATUS => 0}, | |
| 545 | + $td->NORMALIZE_NEWLINES); | |
| 546 | +$td->runtest("list attachments verbose", | |
| 547 | + {$td->COMMAND => "qpdf --list-attachments --verbose a.pdf"}, | |
| 548 | + {$td->FILE => "test76-list-verbose.out", $td->EXIT_STATUS => 0}, | |
| 549 | + $td->NORMALIZE_NEWLINES); | |
| 550 | +$td->runtest("remove attachment (test_driver)", | |
| 539 | 551 | {$td->COMMAND => "test_driver 77 test76.pdf"}, |
| 540 | 552 | {$td->STRING => "test 77 done\n", $td->EXIT_STATUS => 0}, |
| 541 | 553 | $td->NORMALIZE_NEWLINES); |
| 542 | 554 | $td->runtest("check output", |
| 543 | 555 | {$td->FILE => "a.pdf"}, |
| 544 | 556 | {$td->FILE => "test77.pdf"}); |
| 557 | +$td->runtest("remove attachment (cli)", | |
| 558 | + {$td->COMMAND => "qpdf --remove-attachment=att2 test76.pdf" . | |
| 559 | + " --static-id --qdf --verbose b.pdf"}, | |
| 560 | + {$td->FILE => "remove-attachment.out", $td->EXIT_STATUS => 0}, | |
| 561 | + $td->NORMALIZE_NEWLINES); | |
| 562 | +$td->runtest("check output", | |
| 563 | + {$td->FILE => "b.pdf"}, | |
| 564 | + {$td->FILE => "test77.pdf"}); | |
| 565 | +$td->runtest("show missing attachment", | |
| 566 | + {$td->COMMAND => "qpdf --show-attachment=att2 b.pdf"}, | |
| 567 | + {$td->STRING => "qpdf: attachment att2 not found\n", | |
| 568 | + $td->EXIT_STATUS => 2}, | |
| 569 | + $td->NORMALIZE_NEWLINES); | |
| 570 | +$td->runtest("remove missing attachment", | |
| 571 | + {$td->COMMAND => "qpdf --remove-attachment=att2 b.pdf c.pdf"}, | |
| 572 | + {$td->STRING => "qpdf: attachment att2 not found\n", | |
| 573 | + $td->EXIT_STATUS => 2}, | |
| 574 | + $td->NORMALIZE_NEWLINES); | |
| 575 | + | |
| 576 | +$td->runtest("add attachment: bad creation date", | |
| 577 | + {$td->COMMAND => "qpdf minimal.pdf a.pdf" . | |
| 578 | + " --add-attachment auto-txt --creationdate=potato --"}, | |
| 579 | + {$td->REGEXP => ".*potato is not a valid PDF timestamp.*", | |
| 580 | + $td->EXIT_STATUS => 2}, | |
| 581 | + $td->NORMALIZE_NEWLINES); | |
| 582 | +$td->runtest("add attachment: bad mod date", | |
| 583 | + {$td->COMMAND => "qpdf minimal.pdf a.pdf" . | |
| 584 | + " --add-attachment auto-txt --moddate=potato --"}, | |
| 585 | + {$td->REGEXP => ".*potato is not a valid PDF timestamp.*", | |
| 586 | + $td->EXIT_STATUS => 2}, | |
| 587 | + $td->NORMALIZE_NEWLINES); | |
| 588 | +$td->runtest("add attachment: bad mod date", | |
| 589 | + {$td->COMMAND => "qpdf minimal.pdf a.pdf" . | |
| 590 | + " --add-attachment auto-txt --mimetype=potato --"}, | |
| 591 | + {$td->REGEXP => | |
| 592 | + ".*mime type should be specified as type/subtype.*", | |
| 593 | + $td->EXIT_STATUS => 2}, | |
| 594 | + $td->NORMALIZE_NEWLINES); | |
| 595 | +$td->runtest("add attachment: trailing slash", | |
| 596 | + {$td->COMMAND => "qpdf minimal.pdf a.pdf" . | |
| 597 | + " --add-attachment auto-txt/ --"}, | |
| 598 | + {$td->REGEXP => ".*may not end with a path separator.*", | |
| 599 | + $td->EXIT_STATUS => 2}, | |
| 600 | + $td->NORMALIZE_NEWLINES); | |
| 601 | +$td->runtest("add attachment: trailing slash", | |
| 602 | + {$td->COMMAND => "qpdf minimal.pdf a.pdf" . | |
| 603 | + " --add-attachment --"}, | |
| 604 | + {$td->REGEXP => ".*add attachment: no path specified.*", | |
| 605 | + $td->EXIT_STATUS => 2}, | |
| 606 | + $td->NORMALIZE_NEWLINES); | |
| 607 | + | |
| 608 | +foreach my $i (qw(1 2 3)) | |
| 609 | +{ | |
| 610 | + open(F, ">auto-$i") or die; | |
| 611 | + print F "attachment $i"; | |
| 612 | + close(F); | |
| 613 | +} | |
| 614 | +my @dates = ("--creationdate=D:20210210091359-05'00'", | |
| 615 | + "--moddate=D:20210210141359Z"); | |
| 616 | +$td->runtest("add attachments", | |
| 617 | + {$td->COMMAND => | |
| 618 | + [qw(qpdf minimal.pdf a.pdf --no-original-object-ids), | |
| 619 | + qw(--verbose --static-id --qdf), | |
| 620 | + qw(--add-attachment ./auto-1), @dates, | |
| 621 | + qw(--mimetype=text/plain --), | |
| 622 | + qw(--add-attachment ./auto-2 --key=auto-Two), @dates, '--', | |
| 623 | + qw(--add-attachment ./auto-3 --filename=auto-Three.txt), | |
| 624 | + @dates, '--description=two words', '--']}, | |
| 625 | + {$td->FILE => "add-attachments-1.out", $td->EXIT_STATUS => 0}, | |
| 626 | + $td->NORMALIZE_NEWLINES); | |
| 627 | +$td->runtest("list attachments", | |
| 628 | + {$td->COMMAND => "qpdf --list-attachments a.pdf --verbose"}, | |
| 629 | + {$td->FILE => "list-attachments-1.out", $td->EXIT_STATUS => 0}, | |
| 630 | + $td->NORMALIZE_NEWLINES); | |
| 631 | +$td->runtest("check output", | |
| 632 | + {$td->FILE => "a.pdf"}, | |
| 633 | + {$td->FILE => "add-attachments-1.pdf"}, | |
| 634 | + $td->NORMALIZE_NEWLINES); | |
| 635 | +$td->runtest("add attachments: duplicate", | |
| 636 | + {$td->COMMAND => | |
| 637 | + "qpdf a.pdf b.pdf --verbose --add-attachment ./auto-1 --"}, | |
| 638 | + {$td->FILE => "add-attachments-duplicate.out", | |
| 639 | + $td->EXIT_STATUS => 2}, | |
| 640 | + $td->NORMALIZE_NEWLINES); | |
| 641 | +$td->runtest("add attachments: replace", | |
| 642 | + {$td->COMMAND => | |
| 643 | + [qw(qpdf a.pdf b.pdf --no-original-object-ids), | |
| 644 | + qw(--verbose --static-id --qdf), | |
| 645 | + qw(--add-attachment ./auto-2 --key=auto-1 --replace), | |
| 646 | + @dates, '--']}, | |
| 647 | + {$td->FILE => "add-attachments-2.out", $td->EXIT_STATUS => 0}, | |
| 648 | + $td->NORMALIZE_NEWLINES); | |
| 649 | +$td->runtest("list attachments", | |
| 650 | + {$td->COMMAND => "qpdf --list-attachments b.pdf --verbose"}, | |
| 651 | + {$td->FILE => "list-attachments-3.out", $td->EXIT_STATUS => 0}, | |
| 652 | + $td->NORMALIZE_NEWLINES); | |
| 653 | +$td->runtest("check output", | |
| 654 | + {$td->FILE => "b.pdf"}, | |
| 655 | + {$td->FILE => "add-attachments-2.pdf"}, | |
| 656 | + $td->NORMALIZE_NEWLINES); | |
| 657 | +$td->runtest("copy attachments", | |
| 658 | + {$td->COMMAND => | |
| 659 | + "qpdf --verbose --no-original-object-ids" . | |
| 660 | + " --static-id --qdf minimal.pdf b.pdf" . | |
| 661 | + " --copy-attachments-from a.pdf --"}, | |
| 662 | + {$td->FILE => "copy-attachments-1.out", $td->EXIT_STATUS => 0}, | |
| 663 | + $td->NORMALIZE_NEWLINES); | |
| 664 | +$td->runtest("list attachments", | |
| 665 | + {$td->COMMAND => "qpdf --list-attachments b.pdf --verbose"}, | |
| 666 | + {$td->FILE => "list-attachments-1.out", $td->EXIT_STATUS => 0}, | |
| 667 | + $td->NORMALIZE_NEWLINES); | |
| 668 | +$td->runtest("check output", | |
| 669 | + {$td->FILE => "b.pdf"}, | |
| 670 | + {$td->FILE => "add-attachments-1.pdf"}, | |
| 671 | + $td->NORMALIZE_NEWLINES); | |
| 672 | +$td->runtest("copy attachments: duplicate", | |
| 673 | + {$td->COMMAND => | |
| 674 | + "qpdf --verbose --no-original-object-ids" . | |
| 675 | + " --static-id --qdf a.pdf c.pdf" . | |
| 676 | + " --copy-attachments-from b.pdf --"}, | |
| 677 | + {$td->FILE => "copy-attachments-duplicate.out", | |
| 678 | + $td->EXIT_STATUS => 2}, | |
| 679 | + $td->NORMALIZE_NEWLINES); | |
| 680 | +$td->runtest("copy attachments: prefix", | |
| 681 | + {$td->COMMAND => | |
| 682 | + "qpdf --verbose --no-original-object-ids" . | |
| 683 | + " --static-id --qdf a.pdf c.pdf" . | |
| 684 | + " --copy-attachments-from b.pdf --prefix=1- --"}, | |
| 685 | + {$td->FILE => "copy-attachments-2.out", $td->EXIT_STATUS => 0}, | |
| 686 | + $td->NORMALIZE_NEWLINES); | |
| 687 | +$td->runtest("list attachments", | |
| 688 | + {$td->COMMAND => "qpdf --list-attachments c.pdf --verbose"}, | |
| 689 | + {$td->FILE => "list-attachments-2.out", $td->EXIT_STATUS => 0}, | |
| 690 | + $td->NORMALIZE_NEWLINES); | |
| 691 | +$td->runtest("check output", | |
| 692 | + {$td->FILE => "c.pdf"}, | |
| 693 | + {$td->FILE => "copy-attachments-2.pdf"}, | |
| 694 | + $td->NORMALIZE_NEWLINES); | |
| 695 | +$td->runtest("add attachments: current date", | |
| 696 | + {$td->COMMAND => | |
| 697 | + [qw(qpdf minimal.pdf a.pdf --encrypt u o 256 --), | |
| 698 | + qw(--verbose --add-attachment ./auto-1 --)]}, | |
| 699 | + {$td->FILE => "add-attachments-3.out", $td->EXIT_STATUS => 0}, | |
| 700 | + $td->NORMALIZE_NEWLINES); | |
| 701 | +$td->runtest("list attachments", | |
| 702 | + {$td->COMMAND => | |
| 703 | + "qpdf --password=u --list-attachments a.pdf --verbose"}, | |
| 704 | + {$td->FILE => "list-attachments-4.out", $td->EXIT_STATUS => 0}, | |
| 705 | + $td->NORMALIZE_NEWLINES); | |
| 706 | +# The object to show here is the one in list-attachments-4.out | |
| 707 | +$td->runtest("check dates", | |
| 708 | + {$td->COMMAND => "qpdf --show-object=6 a.pdf --password=u"}, | |
| 709 | + {$td->REGEXP => ".*CreationDate \\(D:\\d+.*ModDate \\(D:\\d+.*", | |
| 710 | + $td->EXIT_STATUS => 0}, | |
| 711 | + $td->NORMALIZE_NEWLINES); | |
| 545 | 712 | |
| 546 | 713 | show_ntests(); |
| 547 | 714 | # ---------- | ... | ... |
qpdf/qtest/qpdf/add-attachments-1.out
0 → 100644
qpdf/qtest/qpdf/add-attachments-1.pdf
0 → 100644
| 1 | +%PDF-1.3 | |
| 2 | +%¿÷¢þ | |
| 3 | +%QDF-1.0 | |
| 4 | + | |
| 5 | +1 0 obj | |
| 6 | +<< | |
| 7 | + /Names << | |
| 8 | + /EmbeddedFiles 2 0 R | |
| 9 | + >> | |
| 10 | + /PageMode /UseAttachments | |
| 11 | + /Pages 3 0 R | |
| 12 | + /Type /Catalog | |
| 13 | +>> | |
| 14 | +endobj | |
| 15 | + | |
| 16 | +2 0 obj | |
| 17 | +<< | |
| 18 | + /Names [ | |
| 19 | + (auto-1) | |
| 20 | + 4 0 R | |
| 21 | + (auto-3) | |
| 22 | + 5 0 R | |
| 23 | + (auto-Two) | |
| 24 | + 6 0 R | |
| 25 | + ] | |
| 26 | +>> | |
| 27 | +endobj | |
| 28 | + | |
| 29 | +3 0 obj | |
| 30 | +<< | |
| 31 | + /Count 1 | |
| 32 | + /Kids [ | |
| 33 | + 7 0 R | |
| 34 | + ] | |
| 35 | + /Type /Pages | |
| 36 | +>> | |
| 37 | +endobj | |
| 38 | + | |
| 39 | +4 0 obj | |
| 40 | +<< | |
| 41 | + /EF << | |
| 42 | + /F 8 0 R | |
| 43 | + /UF 8 0 R | |
| 44 | + >> | |
| 45 | + /F (auto-1) | |
| 46 | + /Type /Filespec | |
| 47 | + /UF (auto-1) | |
| 48 | +>> | |
| 49 | +endobj | |
| 50 | + | |
| 51 | +5 0 obj | |
| 52 | +<< | |
| 53 | + /Desc (two words) | |
| 54 | + /EF << | |
| 55 | + /F 10 0 R | |
| 56 | + /UF 10 0 R | |
| 57 | + >> | |
| 58 | + /F (auto-Three.txt) | |
| 59 | + /Type /Filespec | |
| 60 | + /UF (auto-Three.txt) | |
| 61 | +>> | |
| 62 | +endobj | |
| 63 | + | |
| 64 | +6 0 obj | |
| 65 | +<< | |
| 66 | + /EF << | |
| 67 | + /F 12 0 R | |
| 68 | + /UF 12 0 R | |
| 69 | + >> | |
| 70 | + /F (auto-2) | |
| 71 | + /Type /Filespec | |
| 72 | + /UF (auto-2) | |
| 73 | +>> | |
| 74 | +endobj | |
| 75 | + | |
| 76 | +%% Page 1 | |
| 77 | +7 0 obj | |
| 78 | +<< | |
| 79 | + /Contents 14 0 R | |
| 80 | + /MediaBox [ | |
| 81 | + 0 | |
| 82 | + 0 | |
| 83 | + 612 | |
| 84 | + 792 | |
| 85 | + ] | |
| 86 | + /Parent 3 0 R | |
| 87 | + /Resources << | |
| 88 | + /Font << | |
| 89 | + /F1 16 0 R | |
| 90 | + >> | |
| 91 | + /ProcSet 17 0 R | |
| 92 | + >> | |
| 93 | + /Type /Page | |
| 94 | +>> | |
| 95 | +endobj | |
| 96 | + | |
| 97 | +8 0 obj | |
| 98 | +<< | |
| 99 | + /Params << | |
| 100 | + /CheckSum <a857d18d3fc23ad412122ef040733331> | |
| 101 | + /CreationDate (D:20210210091359-05'00') | |
| 102 | + /ModDate (D:20210210141359Z) | |
| 103 | + /Size 12 | |
| 104 | + /Subtype /text#2fplain | |
| 105 | + >> | |
| 106 | + /Type /EmbeddedFile | |
| 107 | + /Length 9 0 R | |
| 108 | +>> | |
| 109 | +stream | |
| 110 | +attachment 1 | |
| 111 | +endstream | |
| 112 | +endobj | |
| 113 | + | |
| 114 | +%QDF: ignore_newline | |
| 115 | +9 0 obj | |
| 116 | +12 | |
| 117 | +endobj | |
| 118 | + | |
| 119 | +10 0 obj | |
| 120 | +<< | |
| 121 | + /Params << | |
| 122 | + /CheckSum <d6c7ac7cf295ae133fea186cfd068dab> | |
| 123 | + /CreationDate (D:20210210091359-05'00') | |
| 124 | + /ModDate (D:20210210141359Z) | |
| 125 | + /Size 12 | |
| 126 | + >> | |
| 127 | + /Type /EmbeddedFile | |
| 128 | + /Length 11 0 R | |
| 129 | +>> | |
| 130 | +stream | |
| 131 | +attachment 3 | |
| 132 | +endstream | |
| 133 | +endobj | |
| 134 | + | |
| 135 | +%QDF: ignore_newline | |
| 136 | +11 0 obj | |
| 137 | +12 | |
| 138 | +endobj | |
| 139 | + | |
| 140 | +12 0 obj | |
| 141 | +<< | |
| 142 | + /Params << | |
| 143 | + /CheckSum <9f991a5669c47a94f9350f53e3953e57> | |
| 144 | + /CreationDate (D:20210210091359-05'00') | |
| 145 | + /ModDate (D:20210210141359Z) | |
| 146 | + /Size 12 | |
| 147 | + >> | |
| 148 | + /Type /EmbeddedFile | |
| 149 | + /Length 13 0 R | |
| 150 | +>> | |
| 151 | +stream | |
| 152 | +attachment 2 | |
| 153 | +endstream | |
| 154 | +endobj | |
| 155 | + | |
| 156 | +%QDF: ignore_newline | |
| 157 | +13 0 obj | |
| 158 | +12 | |
| 159 | +endobj | |
| 160 | + | |
| 161 | +%% Contents for page 1 | |
| 162 | +14 0 obj | |
| 163 | +<< | |
| 164 | + /Length 15 0 R | |
| 165 | +>> | |
| 166 | +stream | |
| 167 | +BT | |
| 168 | + /F1 24 Tf | |
| 169 | + 72 720 Td | |
| 170 | + (Potato) Tj | |
| 171 | +ET | |
| 172 | +endstream | |
| 173 | +endobj | |
| 174 | + | |
| 175 | +15 0 obj | |
| 176 | +44 | |
| 177 | +endobj | |
| 178 | + | |
| 179 | +16 0 obj | |
| 180 | +<< | |
| 181 | + /BaseFont /Helvetica | |
| 182 | + /Encoding /WinAnsiEncoding | |
| 183 | + /Name /F1 | |
| 184 | + /Subtype /Type1 | |
| 185 | + /Type /Font | |
| 186 | +>> | |
| 187 | +endobj | |
| 188 | + | |
| 189 | +17 0 obj | |
| 190 | +[ | |
| 191 | ||
| 192 | + /Text | |
| 193 | +] | |
| 194 | +endobj | |
| 195 | + | |
| 196 | +xref | |
| 197 | +0 18 | |
| 198 | +0000000000 65535 f | |
| 199 | +0000000025 00000 n | |
| 200 | +0000000149 00000 n | |
| 201 | +0000000257 00000 n | |
| 202 | +0000000329 00000 n | |
| 203 | +0000000439 00000 n | |
| 204 | +0000000587 00000 n | |
| 205 | +0000000709 00000 n | |
| 206 | +0000000904 00000 n | |
| 207 | +0000001199 00000 n | |
| 208 | +0000001218 00000 n | |
| 209 | +0000001488 00000 n | |
| 210 | +0000001508 00000 n | |
| 211 | +0000001778 00000 n | |
| 212 | +0000001821 00000 n | |
| 213 | +0000001922 00000 n | |
| 214 | +0000001942 00000 n | |
| 215 | +0000002061 00000 n | |
| 216 | +trailer << | |
| 217 | + /Root 1 0 R | |
| 218 | + /Size 18 | |
| 219 | + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] | |
| 220 | +>> | |
| 221 | +startxref | |
| 222 | +2097 | |
| 223 | +%%EOF | ... | ... |
qpdf/qtest/qpdf/add-attachments-2.out
0 → 100644
qpdf/qtest/qpdf/add-attachments-2.pdf
0 → 100644
| 1 | +%PDF-1.3 | |
| 2 | +%¿÷¢þ | |
| 3 | +%QDF-1.0 | |
| 4 | + | |
| 5 | +1 0 obj | |
| 6 | +<< | |
| 7 | + /Names << | |
| 8 | + /EmbeddedFiles 2 0 R | |
| 9 | + >> | |
| 10 | + /PageMode /UseAttachments | |
| 11 | + /Pages 3 0 R | |
| 12 | + /Type /Catalog | |
| 13 | +>> | |
| 14 | +endobj | |
| 15 | + | |
| 16 | +2 0 obj | |
| 17 | +<< | |
| 18 | + /Names [ | |
| 19 | + (auto-1) | |
| 20 | + 4 0 R | |
| 21 | + (auto-3) | |
| 22 | + 5 0 R | |
| 23 | + (auto-Two) | |
| 24 | + 6 0 R | |
| 25 | + ] | |
| 26 | +>> | |
| 27 | +endobj | |
| 28 | + | |
| 29 | +3 0 obj | |
| 30 | +<< | |
| 31 | + /Count 1 | |
| 32 | + /Kids [ | |
| 33 | + 7 0 R | |
| 34 | + ] | |
| 35 | + /Type /Pages | |
| 36 | +>> | |
| 37 | +endobj | |
| 38 | + | |
| 39 | +4 0 obj | |
| 40 | +<< | |
| 41 | + /EF << | |
| 42 | + /F 8 0 R | |
| 43 | + /UF 8 0 R | |
| 44 | + >> | |
| 45 | + /F (auto-2) | |
| 46 | + /Type /Filespec | |
| 47 | + /UF (auto-2) | |
| 48 | +>> | |
| 49 | +endobj | |
| 50 | + | |
| 51 | +5 0 obj | |
| 52 | +<< | |
| 53 | + /Desc (two words) | |
| 54 | + /EF << | |
| 55 | + /F 10 0 R | |
| 56 | + /UF 10 0 R | |
| 57 | + >> | |
| 58 | + /F (auto-Three.txt) | |
| 59 | + /Type /Filespec | |
| 60 | + /UF (auto-Three.txt) | |
| 61 | +>> | |
| 62 | +endobj | |
| 63 | + | |
| 64 | +6 0 obj | |
| 65 | +<< | |
| 66 | + /EF << | |
| 67 | + /F 12 0 R | |
| 68 | + /UF 12 0 R | |
| 69 | + >> | |
| 70 | + /F (auto-2) | |
| 71 | + /Type /Filespec | |
| 72 | + /UF (auto-2) | |
| 73 | +>> | |
| 74 | +endobj | |
| 75 | + | |
| 76 | +%% Page 1 | |
| 77 | +7 0 obj | |
| 78 | +<< | |
| 79 | + /Contents 14 0 R | |
| 80 | + /MediaBox [ | |
| 81 | + 0 | |
| 82 | + 0 | |
| 83 | + 612 | |
| 84 | + 792 | |
| 85 | + ] | |
| 86 | + /Parent 3 0 R | |
| 87 | + /Resources << | |
| 88 | + /Font << | |
| 89 | + /F1 16 0 R | |
| 90 | + >> | |
| 91 | + /ProcSet 17 0 R | |
| 92 | + >> | |
| 93 | + /Type /Page | |
| 94 | +>> | |
| 95 | +endobj | |
| 96 | + | |
| 97 | +8 0 obj | |
| 98 | +<< | |
| 99 | + /Params << | |
| 100 | + /CheckSum <9f991a5669c47a94f9350f53e3953e57> | |
| 101 | + /CreationDate (D:20210210091359-05'00') | |
| 102 | + /ModDate (D:20210210141359Z) | |
| 103 | + /Size 12 | |
| 104 | + >> | |
| 105 | + /Type /EmbeddedFile | |
| 106 | + /Length 9 0 R | |
| 107 | +>> | |
| 108 | +stream | |
| 109 | +attachment 2 | |
| 110 | +endstream | |
| 111 | +endobj | |
| 112 | + | |
| 113 | +%QDF: ignore_newline | |
| 114 | +9 0 obj | |
| 115 | +12 | |
| 116 | +endobj | |
| 117 | + | |
| 118 | +10 0 obj | |
| 119 | +<< | |
| 120 | + /Params << | |
| 121 | + /CheckSum <d6c7ac7cf295ae133fea186cfd068dab> | |
| 122 | + /CreationDate (D:20210210091359-05'00') | |
| 123 | + /ModDate (D:20210210141359Z) | |
| 124 | + /Size 12 | |
| 125 | + >> | |
| 126 | + /Type /EmbeddedFile | |
| 127 | + /Length 11 0 R | |
| 128 | +>> | |
| 129 | +stream | |
| 130 | +attachment 3 | |
| 131 | +endstream | |
| 132 | +endobj | |
| 133 | + | |
| 134 | +%QDF: ignore_newline | |
| 135 | +11 0 obj | |
| 136 | +12 | |
| 137 | +endobj | |
| 138 | + | |
| 139 | +12 0 obj | |
| 140 | +<< | |
| 141 | + /Params << | |
| 142 | + /CheckSum <9f991a5669c47a94f9350f53e3953e57> | |
| 143 | + /CreationDate (D:20210210091359-05'00') | |
| 144 | + /ModDate (D:20210210141359Z) | |
| 145 | + /Size 12 | |
| 146 | + >> | |
| 147 | + /Type /EmbeddedFile | |
| 148 | + /Length 13 0 R | |
| 149 | +>> | |
| 150 | +stream | |
| 151 | +attachment 2 | |
| 152 | +endstream | |
| 153 | +endobj | |
| 154 | + | |
| 155 | +%QDF: ignore_newline | |
| 156 | +13 0 obj | |
| 157 | +12 | |
| 158 | +endobj | |
| 159 | + | |
| 160 | +%% Contents for page 1 | |
| 161 | +14 0 obj | |
| 162 | +<< | |
| 163 | + /Length 15 0 R | |
| 164 | +>> | |
| 165 | +stream | |
| 166 | +BT | |
| 167 | + /F1 24 Tf | |
| 168 | + 72 720 Td | |
| 169 | + (Potato) Tj | |
| 170 | +ET | |
| 171 | +endstream | |
| 172 | +endobj | |
| 173 | + | |
| 174 | +15 0 obj | |
| 175 | +44 | |
| 176 | +endobj | |
| 177 | + | |
| 178 | +16 0 obj | |
| 179 | +<< | |
| 180 | + /BaseFont /Helvetica | |
| 181 | + /Encoding /WinAnsiEncoding | |
| 182 | + /Name /F1 | |
| 183 | + /Subtype /Type1 | |
| 184 | + /Type /Font | |
| 185 | +>> | |
| 186 | +endobj | |
| 187 | + | |
| 188 | +17 0 obj | |
| 189 | +[ | |
| 190 | ||
| 191 | + /Text | |
| 192 | +] | |
| 193 | +endobj | |
| 194 | + | |
| 195 | +xref | |
| 196 | +0 18 | |
| 197 | +0000000000 65535 f | |
| 198 | +0000000025 00000 n | |
| 199 | +0000000149 00000 n | |
| 200 | +0000000257 00000 n | |
| 201 | +0000000329 00000 n | |
| 202 | +0000000439 00000 n | |
| 203 | +0000000587 00000 n | |
| 204 | +0000000709 00000 n | |
| 205 | +0000000904 00000 n | |
| 206 | +0000001172 00000 n | |
| 207 | +0000001191 00000 n | |
| 208 | +0000001461 00000 n | |
| 209 | +0000001481 00000 n | |
| 210 | +0000001751 00000 n | |
| 211 | +0000001794 00000 n | |
| 212 | +0000001895 00000 n | |
| 213 | +0000001915 00000 n | |
| 214 | +0000002034 00000 n | |
| 215 | +trailer << | |
| 216 | + /Root 1 0 R | |
| 217 | + /Size 18 | |
| 218 | + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] | |
| 219 | +>> | |
| 220 | +startxref | |
| 221 | +2070 | |
| 222 | +%%EOF | ... | ... |
qpdf/qtest/qpdf/add-attachments-3.out
0 → 100644
qpdf/qtest/qpdf/add-attachments-duplicate.out
0 → 100644
qpdf/qtest/qpdf/copy-attachments-1.out
0 → 100644
qpdf/qtest/qpdf/copy-attachments-2.out
0 → 100644
qpdf/qtest/qpdf/copy-attachments-2.pdf
0 → 100644
| 1 | +%PDF-1.3 | |
| 2 | +%¿÷¢þ | |
| 3 | +%QDF-1.0 | |
| 4 | + | |
| 5 | +1 0 obj | |
| 6 | +<< | |
| 7 | + /Names << | |
| 8 | + /EmbeddedFiles 2 0 R | |
| 9 | + >> | |
| 10 | + /PageMode /UseAttachments | |
| 11 | + /Pages 3 0 R | |
| 12 | + /Type /Catalog | |
| 13 | +>> | |
| 14 | +endobj | |
| 15 | + | |
| 16 | +2 0 obj | |
| 17 | +<< | |
| 18 | + /Names [ | |
| 19 | + (1-auto-1) | |
| 20 | + 4 0 R | |
| 21 | + (1-auto-3) | |
| 22 | + 5 0 R | |
| 23 | + (1-auto-Two) | |
| 24 | + 6 0 R | |
| 25 | + (auto-1) | |
| 26 | + 7 0 R | |
| 27 | + (auto-3) | |
| 28 | + 8 0 R | |
| 29 | + (auto-Two) | |
| 30 | + 9 0 R | |
| 31 | + ] | |
| 32 | +>> | |
| 33 | +endobj | |
| 34 | + | |
| 35 | +3 0 obj | |
| 36 | +<< | |
| 37 | + /Count 1 | |
| 38 | + /Kids [ | |
| 39 | + 10 0 R | |
| 40 | + ] | |
| 41 | + /Type /Pages | |
| 42 | +>> | |
| 43 | +endobj | |
| 44 | + | |
| 45 | +4 0 obj | |
| 46 | +<< | |
| 47 | + /EF << | |
| 48 | + /F 11 0 R | |
| 49 | + /UF 11 0 R | |
| 50 | + >> | |
| 51 | + /F (auto-1) | |
| 52 | + /Type /Filespec | |
| 53 | + /UF (auto-1) | |
| 54 | +>> | |
| 55 | +endobj | |
| 56 | + | |
| 57 | +5 0 obj | |
| 58 | +<< | |
| 59 | + /Desc (two words) | |
| 60 | + /EF << | |
| 61 | + /F 13 0 R | |
| 62 | + /UF 13 0 R | |
| 63 | + >> | |
| 64 | + /F (auto-Three.txt) | |
| 65 | + /Type /Filespec | |
| 66 | + /UF (auto-Three.txt) | |
| 67 | +>> | |
| 68 | +endobj | |
| 69 | + | |
| 70 | +6 0 obj | |
| 71 | +<< | |
| 72 | + /EF << | |
| 73 | + /F 15 0 R | |
| 74 | + /UF 15 0 R | |
| 75 | + >> | |
| 76 | + /F (auto-2) | |
| 77 | + /Type /Filespec | |
| 78 | + /UF (auto-2) | |
| 79 | +>> | |
| 80 | +endobj | |
| 81 | + | |
| 82 | +7 0 obj | |
| 83 | +<< | |
| 84 | + /EF << | |
| 85 | + /F 17 0 R | |
| 86 | + /UF 17 0 R | |
| 87 | + >> | |
| 88 | + /F (auto-1) | |
| 89 | + /Type /Filespec | |
| 90 | + /UF (auto-1) | |
| 91 | +>> | |
| 92 | +endobj | |
| 93 | + | |
| 94 | +8 0 obj | |
| 95 | +<< | |
| 96 | + /Desc (two words) | |
| 97 | + /EF << | |
| 98 | + /F 19 0 R | |
| 99 | + /UF 19 0 R | |
| 100 | + >> | |
| 101 | + /F (auto-Three.txt) | |
| 102 | + /Type /Filespec | |
| 103 | + /UF (auto-Three.txt) | |
| 104 | +>> | |
| 105 | +endobj | |
| 106 | + | |
| 107 | +9 0 obj | |
| 108 | +<< | |
| 109 | + /EF << | |
| 110 | + /F 21 0 R | |
| 111 | + /UF 21 0 R | |
| 112 | + >> | |
| 113 | + /F (auto-2) | |
| 114 | + /Type /Filespec | |
| 115 | + /UF (auto-2) | |
| 116 | +>> | |
| 117 | +endobj | |
| 118 | + | |
| 119 | +%% Page 1 | |
| 120 | +10 0 obj | |
| 121 | +<< | |
| 122 | + /Contents 23 0 R | |
| 123 | + /MediaBox [ | |
| 124 | + 0 | |
| 125 | + 0 | |
| 126 | + 612 | |
| 127 | + 792 | |
| 128 | + ] | |
| 129 | + /Parent 3 0 R | |
| 130 | + /Resources << | |
| 131 | + /Font << | |
| 132 | + /F1 25 0 R | |
| 133 | + >> | |
| 134 | + /ProcSet 26 0 R | |
| 135 | + >> | |
| 136 | + /Type /Page | |
| 137 | +>> | |
| 138 | +endobj | |
| 139 | + | |
| 140 | +11 0 obj | |
| 141 | +<< | |
| 142 | + /Params << | |
| 143 | + /CheckSum <a857d18d3fc23ad412122ef040733331> | |
| 144 | + /CreationDate (D:20210210091359-05'00') | |
| 145 | + /ModDate (D:20210210141359Z) | |
| 146 | + /Size 12 | |
| 147 | + /Subtype /text#2fplain | |
| 148 | + >> | |
| 149 | + /Type /EmbeddedFile | |
| 150 | + /Length 12 0 R | |
| 151 | +>> | |
| 152 | +stream | |
| 153 | +attachment 1 | |
| 154 | +endstream | |
| 155 | +endobj | |
| 156 | + | |
| 157 | +%QDF: ignore_newline | |
| 158 | +12 0 obj | |
| 159 | +12 | |
| 160 | +endobj | |
| 161 | + | |
| 162 | +13 0 obj | |
| 163 | +<< | |
| 164 | + /Params << | |
| 165 | + /CheckSum <d6c7ac7cf295ae133fea186cfd068dab> | |
| 166 | + /CreationDate (D:20210210091359-05'00') | |
| 167 | + /ModDate (D:20210210141359Z) | |
| 168 | + /Size 12 | |
| 169 | + >> | |
| 170 | + /Type /EmbeddedFile | |
| 171 | + /Length 14 0 R | |
| 172 | +>> | |
| 173 | +stream | |
| 174 | +attachment 3 | |
| 175 | +endstream | |
| 176 | +endobj | |
| 177 | + | |
| 178 | +%QDF: ignore_newline | |
| 179 | +14 0 obj | |
| 180 | +12 | |
| 181 | +endobj | |
| 182 | + | |
| 183 | +15 0 obj | |
| 184 | +<< | |
| 185 | + /Params << | |
| 186 | + /CheckSum <9f991a5669c47a94f9350f53e3953e57> | |
| 187 | + /CreationDate (D:20210210091359-05'00') | |
| 188 | + /ModDate (D:20210210141359Z) | |
| 189 | + /Size 12 | |
| 190 | + >> | |
| 191 | + /Type /EmbeddedFile | |
| 192 | + /Length 16 0 R | |
| 193 | +>> | |
| 194 | +stream | |
| 195 | +attachment 2 | |
| 196 | +endstream | |
| 197 | +endobj | |
| 198 | + | |
| 199 | +%QDF: ignore_newline | |
| 200 | +16 0 obj | |
| 201 | +12 | |
| 202 | +endobj | |
| 203 | + | |
| 204 | +17 0 obj | |
| 205 | +<< | |
| 206 | + /Params << | |
| 207 | + /CheckSum <a857d18d3fc23ad412122ef040733331> | |
| 208 | + /CreationDate (D:20210210091359-05'00') | |
| 209 | + /ModDate (D:20210210141359Z) | |
| 210 | + /Size 12 | |
| 211 | + /Subtype /text#2fplain | |
| 212 | + >> | |
| 213 | + /Type /EmbeddedFile | |
| 214 | + /Length 18 0 R | |
| 215 | +>> | |
| 216 | +stream | |
| 217 | +attachment 1 | |
| 218 | +endstream | |
| 219 | +endobj | |
| 220 | + | |
| 221 | +%QDF: ignore_newline | |
| 222 | +18 0 obj | |
| 223 | +12 | |
| 224 | +endobj | |
| 225 | + | |
| 226 | +19 0 obj | |
| 227 | +<< | |
| 228 | + /Params << | |
| 229 | + /CheckSum <d6c7ac7cf295ae133fea186cfd068dab> | |
| 230 | + /CreationDate (D:20210210091359-05'00') | |
| 231 | + /ModDate (D:20210210141359Z) | |
| 232 | + /Size 12 | |
| 233 | + >> | |
| 234 | + /Type /EmbeddedFile | |
| 235 | + /Length 20 0 R | |
| 236 | +>> | |
| 237 | +stream | |
| 238 | +attachment 3 | |
| 239 | +endstream | |
| 240 | +endobj | |
| 241 | + | |
| 242 | +%QDF: ignore_newline | |
| 243 | +20 0 obj | |
| 244 | +12 | |
| 245 | +endobj | |
| 246 | + | |
| 247 | +21 0 obj | |
| 248 | +<< | |
| 249 | + /Params << | |
| 250 | + /CheckSum <9f991a5669c47a94f9350f53e3953e57> | |
| 251 | + /CreationDate (D:20210210091359-05'00') | |
| 252 | + /ModDate (D:20210210141359Z) | |
| 253 | + /Size 12 | |
| 254 | + >> | |
| 255 | + /Type /EmbeddedFile | |
| 256 | + /Length 22 0 R | |
| 257 | +>> | |
| 258 | +stream | |
| 259 | +attachment 2 | |
| 260 | +endstream | |
| 261 | +endobj | |
| 262 | + | |
| 263 | +%QDF: ignore_newline | |
| 264 | +22 0 obj | |
| 265 | +12 | |
| 266 | +endobj | |
| 267 | + | |
| 268 | +%% Contents for page 1 | |
| 269 | +23 0 obj | |
| 270 | +<< | |
| 271 | + /Length 24 0 R | |
| 272 | +>> | |
| 273 | +stream | |
| 274 | +BT | |
| 275 | + /F1 24 Tf | |
| 276 | + 72 720 Td | |
| 277 | + (Potato) Tj | |
| 278 | +ET | |
| 279 | +endstream | |
| 280 | +endobj | |
| 281 | + | |
| 282 | +24 0 obj | |
| 283 | +44 | |
| 284 | +endobj | |
| 285 | + | |
| 286 | +25 0 obj | |
| 287 | +<< | |
| 288 | + /BaseFont /Helvetica | |
| 289 | + /Encoding /WinAnsiEncoding | |
| 290 | + /Name /F1 | |
| 291 | + /Subtype /Type1 | |
| 292 | + /Type /Font | |
| 293 | +>> | |
| 294 | +endobj | |
| 295 | + | |
| 296 | +26 0 obj | |
| 297 | +[ | |
| 298 | ||
| 299 | + /Text | |
| 300 | +] | |
| 301 | +endobj | |
| 302 | + | |
| 303 | +xref | |
| 304 | +0 27 | |
| 305 | +0000000000 65535 f | |
| 306 | +0000000025 00000 n | |
| 307 | +0000000149 00000 n | |
| 308 | +0000000334 00000 n | |
| 309 | +0000000407 00000 n | |
| 310 | +0000000519 00000 n | |
| 311 | +0000000667 00000 n | |
| 312 | +0000000779 00000 n | |
| 313 | +0000000891 00000 n | |
| 314 | +0000001039 00000 n | |
| 315 | +0000001161 00000 n | |
| 316 | +0000001357 00000 n | |
| 317 | +0000001654 00000 n | |
| 318 | +0000001674 00000 n | |
| 319 | +0000001944 00000 n | |
| 320 | +0000001964 00000 n | |
| 321 | +0000002234 00000 n | |
| 322 | +0000002254 00000 n | |
| 323 | +0000002551 00000 n | |
| 324 | +0000002571 00000 n | |
| 325 | +0000002841 00000 n | |
| 326 | +0000002861 00000 n | |
| 327 | +0000003131 00000 n | |
| 328 | +0000003174 00000 n | |
| 329 | +0000003275 00000 n | |
| 330 | +0000003295 00000 n | |
| 331 | +0000003414 00000 n | |
| 332 | +trailer << | |
| 333 | + /Root 1 0 R | |
| 334 | + /Size 27 | |
| 335 | + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] | |
| 336 | +>> | |
| 337 | +startxref | |
| 338 | +3450 | |
| 339 | +%%EOF | ... | ... |
qpdf/qtest/qpdf/copy-attachments-duplicate.out
0 → 100644
| 1 | +qpdf: copying attachments from b.pdf | |
| 2 | +qpdfb.pdf and a.pdf both have attachments with key auto-1; use --prefix with --copy-attachments-from or manually copy individual attachments | |
| 3 | +qpdf: copying attachments from b.pdf | |
| 4 | +qpdfb.pdf and a.pdf both have attachments with key auto-3; use --prefix with --copy-attachments-from or manually copy individual attachments | |
| 5 | +qpdf: copying attachments from b.pdf | |
| 6 | +qpdfb.pdf and a.pdf both have attachments with key auto-Two; use --prefix with --copy-attachments-from or manually copy individual attachments | |
| 7 | +qpdf: wrote file c.pdf | ... | ... |
qpdf/qtest/qpdf/list-attachments-1.out
0 → 100644
| 1 | +auto-1 -> 8,0 | |
| 2 | + preferred name: auto-1 | |
| 3 | + all names: | |
| 4 | + /F -> auto-1 | |
| 5 | + /UF -> auto-1 | |
| 6 | + all data streams: | |
| 7 | + /F -> 8,0 | |
| 8 | + /UF -> 8,0 | |
| 9 | +auto-3 -> 10,0 | |
| 10 | + description: two words | |
| 11 | + preferred name: auto-Three.txt | |
| 12 | + all names: | |
| 13 | + /F -> auto-Three.txt | |
| 14 | + /UF -> auto-Three.txt | |
| 15 | + all data streams: | |
| 16 | + /F -> 10,0 | |
| 17 | + /UF -> 10,0 | |
| 18 | +auto-Two -> 12,0 | |
| 19 | + preferred name: auto-2 | |
| 20 | + all names: | |
| 21 | + /F -> auto-2 | |
| 22 | + /UF -> auto-2 | |
| 23 | + all data streams: | |
| 24 | + /F -> 12,0 | |
| 25 | + /UF -> 12,0 | ... | ... |
qpdf/qtest/qpdf/list-attachments-2.out
0 → 100644
| 1 | +1-auto-1 -> 11,0 | |
| 2 | + preferred name: auto-1 | |
| 3 | + all names: | |
| 4 | + /F -> auto-1 | |
| 5 | + /UF -> auto-1 | |
| 6 | + all data streams: | |
| 7 | + /F -> 11,0 | |
| 8 | + /UF -> 11,0 | |
| 9 | +1-auto-3 -> 13,0 | |
| 10 | + description: two words | |
| 11 | + preferred name: auto-Three.txt | |
| 12 | + all names: | |
| 13 | + /F -> auto-Three.txt | |
| 14 | + /UF -> auto-Three.txt | |
| 15 | + all data streams: | |
| 16 | + /F -> 13,0 | |
| 17 | + /UF -> 13,0 | |
| 18 | +1-auto-Two -> 15,0 | |
| 19 | + preferred name: auto-2 | |
| 20 | + all names: | |
| 21 | + /F -> auto-2 | |
| 22 | + /UF -> auto-2 | |
| 23 | + all data streams: | |
| 24 | + /F -> 15,0 | |
| 25 | + /UF -> 15,0 | |
| 26 | +auto-1 -> 17,0 | |
| 27 | + preferred name: auto-1 | |
| 28 | + all names: | |
| 29 | + /F -> auto-1 | |
| 30 | + /UF -> auto-1 | |
| 31 | + all data streams: | |
| 32 | + /F -> 17,0 | |
| 33 | + /UF -> 17,0 | |
| 34 | +auto-3 -> 19,0 | |
| 35 | + description: two words | |
| 36 | + preferred name: auto-Three.txt | |
| 37 | + all names: | |
| 38 | + /F -> auto-Three.txt | |
| 39 | + /UF -> auto-Three.txt | |
| 40 | + all data streams: | |
| 41 | + /F -> 19,0 | |
| 42 | + /UF -> 19,0 | |
| 43 | +auto-Two -> 21,0 | |
| 44 | + preferred name: auto-2 | |
| 45 | + all names: | |
| 46 | + /F -> auto-2 | |
| 47 | + /UF -> auto-2 | |
| 48 | + all data streams: | |
| 49 | + /F -> 21,0 | |
| 50 | + /UF -> 21,0 | ... | ... |
qpdf/qtest/qpdf/list-attachments-3.out
0 → 100644
| 1 | +auto-1 -> 8,0 | |
| 2 | + preferred name: auto-2 | |
| 3 | + all names: | |
| 4 | + /F -> auto-2 | |
| 5 | + /UF -> auto-2 | |
| 6 | + all data streams: | |
| 7 | + /F -> 8,0 | |
| 8 | + /UF -> 8,0 | |
| 9 | +auto-3 -> 10,0 | |
| 10 | + description: two words | |
| 11 | + preferred name: auto-Three.txt | |
| 12 | + all names: | |
| 13 | + /F -> auto-Three.txt | |
| 14 | + /UF -> auto-Three.txt | |
| 15 | + all data streams: | |
| 16 | + /F -> 10,0 | |
| 17 | + /UF -> 10,0 | |
| 18 | +auto-Two -> 12,0 | |
| 19 | + preferred name: auto-2 | |
| 20 | + all names: | |
| 21 | + /F -> auto-2 | |
| 22 | + /UF -> auto-2 | |
| 23 | + all data streams: | |
| 24 | + /F -> 12,0 | |
| 25 | + /UF -> 12,0 | ... | ... |
qpdf/qtest/qpdf/list-attachments-4.out
0 → 100644
qpdf/qtest/qpdf/remove-attachment.out
0 → 100644
qpdf/qtest/qpdf/test76-list-verbose.out
0 → 100644
| 1 | +att1 -> 8,0 | |
| 2 | + description: some text | |
| 3 | + preferred name: att1.txt | |
| 4 | + all names: | |
| 5 | + /F -> att1.txt | |
| 6 | + /UF -> att1.txt | |
| 7 | + all data streams: | |
| 8 | + /F -> 8,0 | |
| 9 | + /UF -> 8,0 | |
| 10 | +att2 -> 10,0 | |
| 11 | + preferred name: att2.txt | |
| 12 | + all names: | |
| 13 | + /F -> att2.txt | |
| 14 | + /UF -> att2.txt | |
| 15 | + all data streams: | |
| 16 | + /F -> 10,0 | |
| 17 | + /UF -> 10,0 | |
| 18 | +att3 -> 12,0 | |
| 19 | + preferred name: π.txt | |
| 20 | + all names: | |
| 21 | + /F -> att3.txt | |
| 22 | + /UF -> π.txt | |
| 23 | + all data streams: | |
| 24 | + /F -> 12,0 | |
| 25 | + /UF -> 12,0 | ... | ... |
qpdf/qtest/qpdf/test76-list.out
0 → 100644