#include #include #include #include #include #include #include // // This example attaches a file to an input file, adds a page to the // beginning of the file that includes a file attachment annotation, // and writes the result to an output file. It also illustrates a // number of new API calls that were added in qpdf 10.2. // static char const* whoami = 0; static void usage(std::string const& msg) { std::cerr << msg << std::endl << std::endl << "Usage: " << whoami << " options" << std::endl << "Options:" << std::endl << " --infile infile.pdf" << std::endl << " --outfile outfile.pdf" << std::endl << " --attachment attachment" << std::endl << " [ --password infile-password ]" << std::endl << " [ --mimetype attachment mime type ]" << std::endl; exit(2); } static void process(char const* infilename, char const* password, char const* attachment, char const* mimetype, char const* outfilename) { QPDF q; q.processFile(infilename, password); // Create an indirect object for the built-in Helvetica font. auto f1 = q.makeIndirectObject( QPDFObjectHandle::parse( "<<" " /Type /Font" " /Subtype /Type1" " /Name /F1" " /BaseFont /Helvetica" " /Encoding /WinAnsiEncoding" ">>")); // Create a resources dictionary with fonts. This uses the new // parse introduced in qpdf 10.2 that takes a QPDF* and allows // indirect object references. auto resources = q.makeIndirectObject( QPDFObjectHandle::parse( &q, "<<" " /Font <<" " /F1 " + f1.unparse() + " >>" ">>")); // Create a file spec. std::string key = QUtil::path_basename(attachment); std::cout << whoami << ": attaching " << attachment << " as " << key << std::endl; auto fs = QPDFFileSpecObjectHelper::createFileSpec(q, key, attachment); if (mimetype) { // Get an embedded file stream and set mimetype auto ef = QPDFEFStreamObjectHelper(fs.getEmbeddedFileStream()); ef.setSubtype(mimetype); } // Add the embedded file at the document level as an attachment. auto efdh = QPDFEmbeddedFileDocumentHelper(q); efdh.replaceEmbeddedFile(key, fs); // Create a file attachment annotation. // Create appearance stream for the attachment. auto ap = QPDFObjectHandle::newStream( &q, "0 10 m\n" "10 0 l\n" "20 10 l\n" "10 0 m\n" "10 20 l\n" "0 0 20 20 re\n" "S\n"); auto apdict = ap.getDict(); apdict.replaceKey("/Resources", QPDFObjectHandle::newDictionary()); apdict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); apdict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Form")); apdict.replaceKey("/BBox", QPDFObjectHandle::parse("[ 0 0 20 20 ]")); auto annot = q.makeIndirectObject( QPDFObjectHandle::parse( &q, "<<" " /AP <<" " /N " + ap.unparse() + " >>" " /Contents " + QPDFObjectHandle::newUnicodeString(attachment).unparse() + " /FS " + fs.getObjectHandle().unparse() + " /NM " + QPDFObjectHandle::newUnicodeString(attachment).unparse() + " /Rect [ 72 700 92 720 ]" " /Subtype /FileAttachment" " /Type /Annot" ">>")); // Generate contents for the page. auto contents = QPDFObjectHandle::newStream( &q, "q\n" "BT\n" " 102 700 Td\n" " /F1 16 Tf\n" " (Here is an attachment.) Tj\n" "ET\n" "Q\n"); // Create the page object. auto page = QPDFObjectHandle::parse( &q, "<<" " /Annots [ " + annot.unparse() + " ]" " /Contents " + contents.unparse() + " /MediaBox [0 0 612 792]" " /Resources " + resources.unparse() + " /Type /Page" ">>"); // Add the page. q.addPage(page, true); QPDFWriter w(q, outfilename); w.setQDFMode(true); w.setSuppressOriginalObjectIDs(true); w.setDeterministicID(true); w.write(); } int main(int argc, char* argv[]) { whoami = QUtil::getWhoami(argv[0]); // For libtool's sake.... if (strncmp(whoami, "lt-", 3) == 0) { whoami += 3; } char const* infilename = 0; char const* password = 0; char const* attachment = 0; char const* outfilename = 0; char const* mimetype = 0; auto check_arg = [](char const* arg, std::string const& msg) { if (arg == nullptr) { usage(msg); } }; for (int i = 1; i < argc; ++i) { char* arg = argv[i]; char* next = argv[i+1]; if (strcmp(arg, "--infile") == 0) { check_arg(next, "--infile takes an argument"); infilename = next; ++i; } else if (strcmp(arg, "--password") == 0) { check_arg(next, "--password takes an argument"); password = next; ++i; } else if (strcmp(arg, "--attachment") == 0) { check_arg(next, "--attachment takes an argument"); attachment = next; ++i; } else if (strcmp(arg, "--outfile") == 0) { check_arg(next, "--outfile takes an argument"); outfilename = next; ++i; } else if (strcmp(arg, "--mimetype") == 0) { check_arg(next, "--mimetype takes an argument"); mimetype = next; ++i; } else { usage("unknown argument " + std::string(arg)); } } if (! (infilename && attachment && outfilename)) { usage("required arguments were not provided"); } try { process(infilename, password, attachment, mimetype, outfilename); } catch (std::exception &e) { std::cerr << whoami << " exception: " << e.what() << std::endl; exit(2); } return 0; }