mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
cb769c62e5
This comment expands all tabs using an 8-character tab-width. You should ignore this commit when using git blame or use git blame -w. In the early days, I used to use tabs where possible for indentation, since emacs did this automatically. In recent years, I have switched to only using spaces, which means qpdf source code has been a mixture of spaces and tabs. I have avoided cleaning this up because of not wanting gratuitous whitespaces change to cloud the output of git blame, but I changed my mind after discussing with users who view qpdf source code in editors/IDEs that have other tab widths by default and in light of the fact that I am planning to start applying automatic code formatting soon.
222 lines
5.6 KiB
C++
222 lines
5.6 KiB
C++
// Author: Vitaliy Pavlyuk
|
|
|
|
#include <qpdf/QPDF.hh>
|
|
#include <qpdf/QPDFWriter.hh>
|
|
#include <qpdf/QPDFObjectHandle.hh>
|
|
#include <qpdf/QUtil.hh>
|
|
#include <qpdf/QTC.hh>
|
|
#include <iostream>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
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"))
|
|
{
|
|
for (auto& it: trailer.getKey("/Info").ditems())
|
|
{
|
|
std::string val;
|
|
if (it.second.isString())
|
|
{
|
|
val = it.second.getStringValue();
|
|
}
|
|
else if (it.second.isName())
|
|
{
|
|
val = it.second.getName();
|
|
}
|
|
else // according to PDF Spec 1.5, shouldn't happen
|
|
{
|
|
val = it.second.unparseResolved();
|
|
}
|
|
os << it.first.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<std::string, std::string> Keys;
|
|
|
|
whoami = QUtil::getWhoami(argv[0]);
|
|
|
|
// 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 == 3) && (! strcmp(argv[1], "--dump")))
|
|
{
|
|
QTC::TC("examples", "pdf-mod-info --dump");
|
|
pdfDumpInfoDict(argv[2]);
|
|
exit(0);
|
|
}
|
|
|
|
char* fl_in = 0;
|
|
char* fl_out = 0;
|
|
std::string cur_key;
|
|
|
|
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];
|
|
if (! ((cur_key.length() > 0) && (cur_key.at(0) == '/')))
|
|
{
|
|
cur_key = "/" + cur_key;
|
|
}
|
|
Keys[cur_key] = "";
|
|
}
|
|
else if ((! strcmp(argv[i], "--val")) && (++i < argc))
|
|
{
|
|
if (cur_key.empty())
|
|
{
|
|
QTC::TC("examples", "pdf-mod-info usage wrong val");
|
|
usage();
|
|
}
|
|
QTC::TC("examples", "pdf-mod-info -val");
|
|
Keys[cur_key] = argv[i];
|
|
cur_key.clear();
|
|
}
|
|
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();
|
|
}
|
|
|
|
std::string fl_tmp = fl_out;
|
|
fl_tmp += ".tmp";
|
|
|
|
try
|
|
{
|
|
QPDF file;
|
|
file.processFile(fl_in);
|
|
|
|
QPDFObjectHandle filetrailer = file.getTrailer();
|
|
QPDFObjectHandle fileinfo;
|
|
|
|
for (std::map<std::string, std::string>::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");
|
|
fileinfo = QPDFObjectHandle::newDictionary();
|
|
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);
|
|
}
|
|
}
|
|
QPDFWriter w(file, fl_tmp.c_str());
|
|
w.setStreamDataMode(qpdf_s_preserve);
|
|
w.setLinearization(true);
|
|
w.setStaticID(static_id); // for testing only
|
|
w.write();
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
std::cerr << e.what() << std::endl;
|
|
exit(2);
|
|
}
|
|
|
|
try
|
|
{
|
|
(void) remove(fl_out);
|
|
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;
|
|
}
|