// Author: Vitaliy Pavlyuk #include #include #include #include #include #include #include #include #include static char const* version = "1.1"; static char const* whoami = 0; void usage() { std::cerr << "Usage: " << whoami << " -in in_file [-out out_file] [-key key [-val val]?]+\n" << "Modifies/Adds/Removes PDF /Info entries in the in_file\n" << "and stores the result in out_file\n" << "Special mode: " << whoami << " --dump file\n" << "dumps all /Info entries to stdout\n"; exit(2); } void dumpInfoDict(QPDF& pdf, std::ostream& os = std::cout, std::string const& sep = ":\t") { QPDFObjectHandle trailer = pdf.getTrailer(); if (trailer.hasKey("/Info")) { QPDFObjectHandle info = trailer.getKey("/Info"); std::set keys = info.getKeys(); for (std::set::const_iterator it = keys.begin(); keys.end() != it; ++it) { QPDFObjectHandle elt = info.getKey(*it); std::string val; if (false) {} else if (elt.isString()) { val = elt.getStringValue(); } else if (elt.isName()) { val = elt.getName(); } else // according to PDF Spec 1.5, shouldn't happen { val = elt.unparseResolved(); } os << it->substr(1) << sep << val << std::endl; // skip '/' } } } void pdfDumpInfoDict(char const* fname) { try { QPDF pdf; pdf.processFile(fname); dumpInfoDict(pdf); } catch (std::exception& e) { std::cerr << e.what() << std::endl; exit(2); } } int main(int argc, char* argv[]) { bool static_id = false; std::map Keys; if ((whoami = strrchr(argv[0], '/')) == NULL) { whoami = argv[0]; } else { ++whoami; } // For libtool's sake.... if (strncmp(whoami, "lt-", 3) == 0) { whoami += 3; } if ((argc == 2) && (! strcmp(argv[1], "--version")) ) { std::cout << whoami << " version " << version << std::endl; exit(0); } if ((argc == 4) && (! strcmp(argv[1], "--dump")) && (strcmp(argv[2], "-in") == 0) ) { QTC::TC("examples", "pdf-mod-info --dump"); pdfDumpInfoDict(argv[3]); exit(0); } char* fl_in = 0; char* fl_out = 0; char* cur_key = 0; for (int i = 1; i < argc; ++i) { if ((! strcmp(argv[i], "-in")) && (++i < argc)) { fl_in = argv[i]; } else if ((! strcmp(argv[i], "-out")) && (++i < argc)) { fl_out = argv[i]; } else if (! strcmp(argv[i], "--static-id")) // don't document { static_id = true; // this should be used in test suites only } else if ((! strcmp(argv[i], "-key")) && (++i < argc)) { QTC::TC("examples", "pdf-mod-info -key"); cur_key = argv[i]; Keys[cur_key] = ""; } else if ((! strcmp(argv[i], "-val")) && (++i < argc)) { if (cur_key == 0) { QTC::TC("examples", "pdf-mod-info usage wrong val"); usage(); } QTC::TC("examples", "pdf-mod-info -val"); Keys[cur_key] = argv[i]; cur_key = 0; } else { QTC::TC("examples", "pdf-mod-info usage junk"); usage(); } } if (! fl_in) { QTC::TC("examples", "pdf-mod-info no in file"); usage(); } if (! fl_out) { QTC::TC("examples", "pdf-mod-info in-place"); fl_out = fl_in; } if (Keys.size() == 0) { QTC::TC("examples", "pdf-mod-info no keys"); usage(); } try { QPDF file; file.processFile(fl_in); QPDFObjectHandle filetrailer = file.getTrailer(); QPDFObjectHandle fileinfo; for (std::map::const_iterator it = Keys.begin(); Keys.end() != it; ++it) { if (! fileinfo.isInitialized()) { if (filetrailer.hasKey("/Info")) { QTC::TC("examples", "pdf-mod-info has info"); fileinfo = filetrailer.getKey("/Info"); } else { QTC::TC("examples", "pdf-mod-info file no info"); std::map vacant; fileinfo = fileinfo.newDictionary(vacant); filetrailer.replaceKey("/Info", fileinfo); } } if (it->second == "") { fileinfo.removeKey(it->first); } else { QPDFObjectHandle elt = fileinfo.newString(it->second); elt.makeDirect(); fileinfo.replaceKey(it->first, elt); } } std::string fl_tmp = fl_out; fl_tmp += ".tmp"; QPDFWriter w(file, fl_tmp.c_str()); w.setStreamDataMode(QPDFWriter::s_preserve); w.setLinearization(true); w.setStaticID(static_id); w.write(); QUtil::os_wrapper("rename " + fl_tmp + " " + std::string(fl_out), rename(fl_tmp.c_str(), fl_out)); } catch (std::exception& e) { std::cerr << e.what() << std::endl; exit(2); } return 0; }