+/*
+ * PaperJam -- Main program
+ *
+ * (c) 2018--2022 Martin Mares <mj@ucw.cz>
+ */
+
#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <cstdio>
+#include <getopt.h>
#include "jam.h"
-#include <qpdf/QPDFWriter.hh>
-
-static QPDF in_pdf;
-static QPDF out_pdf;
+/*** Options ***/
-string page_out::new_resource(const string type)
-{
- return "/" + type + to_string(++res_cnt);
-}
+const char *in_name;
+const char *out_name;
+bool recalc_bbox;
+bool no_auto_transforms;
+int debug_level;
+int debug_indent;
-class in_page : public page {
- QPDFObjectHandle pdf_page;
-public:
- BBox media_box;
- void render(page_out *out, pdf_matrix xform);
- in_page(QPDFObjectHandle inpg, int idx);
-};
+/*** Messages ***/
-in_page::in_page(QPDFObjectHandle inpg, int idx)
+void debug(const char *msg, ...)
{
- pdf_page = inpg;
- index = idx;
- media_box = BBox(inpg.getKey("/MediaBox"));
- width = media_box.width();
- height = media_box.height();
+ if (!debug_level)
+ return;
+ va_list args;
+ va_start(args, msg);
+ fprintf(stderr, "%*s", debug_indent, "");
+ vfprintf(stderr, msg, args);
+ fputc('\n', stderr);
+ va_end(args);
}
-void in_page::render(page_out *out, pdf_matrix xform)
+void warn(const char *msg, ...)
{
- // Convert page to xobject
- QPDFObjectHandle page_copy = page_to_xobject(&out_pdf, out_pdf.copyForeignObject(pdf_page));
- string xobj_res = out->new_resource("XO");
- out->xobjects.replaceKey(xobj_res, out_pdf.makeIndirectObject(page_copy));
-
- pdf_matrix m;
- m.shift(-media_box.x_min, -media_box.y_min);
- m.concat(xform);
-
- out->contents += "q " + m.to_string() + " cm " + xobj_res + " Do Q";
+ va_list args;
+ va_start(args, msg);
+ fprintf(stderr, "Warning: ");
+ vfprintf(stderr, msg, args);
+ fputc('\n', stderr);
+ va_end(args);
}
-static int run_indent;
-
-static void debug_pages(vector<page *> &pages)
+void die(const char *msg, ...)
{
- if (!debug_mode)
- return;
-
- for (auto pg: pages)
- debug("%*sPage #%d: w=%.3f h=%.3f", run_indent, "", pg->index, pg->width, pg->height);
+ va_list args;
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ fputc('\n', stderr);
+ va_end(args);
+ exit(1);
}
-vector<page *> run_command_list(list<cmd *> &cmds, vector<page *> &pages)
+void err(const char *msg, ...)
{
- debug("%*s# Input", run_indent, "");
- debug_pages(pages);
-
- for (auto c: cmds)
- {
- debug("%*s# Executing %s", run_indent, "", c->def->name);
- run_indent += 4;
- pages = c->exec->process(pages);
- run_indent -= 4;
- debug_pages(pages);
- }
-
- return pages;
+ va_list args;
+ va_start(args, msg);
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), msg, args);
+ va_end(args);
+ throw paperjam_error(buf);
}
-static void process(list<cmd *> &cmds, const char *in_name, const char *out_name)
-{
- in_pdf.processFile(in_name);
- in_pdf.pushInheritedAttributesToPage();
- out_pdf.emptyPDF();
-
- vector<QPDFObjectHandle> const &in_pages = in_pdf.getAllPages();
- vector<page *> pages;
+/*** Arguments ***/
- QPDFObjectHandle page_copy = out_pdf.copyForeignObject(in_pages[0]);
+enum opt {
+ OPT_HELP = 256,
+ OPT_VERSION,
+ OPT_NO_AUTO,
+};
- int cnt = 0;
- for (auto inpg: in_pages)
- pages.push_back(new in_page(inpg, ++cnt));
+static const struct option long_opts[] = {
+ { "debug", 0, 0, 'd' },
+ { "help", 0, 0, OPT_HELP },
+ { "version", 0, 0, OPT_VERSION },
+ { "bbox", 0, 0, 'b' },
+ { 0, 0, 0, 0 }
+};
- pages = run_command_list(cmds, pages);
+static const char short_opts[] = "bd";
- for (auto pg: pages)
- {
- page_out out;
- out.resources = QPDFObjectHandle::newDictionary();
- // FIXME: What if the source page requires a broader ProcSet?
- out.resources.replaceKey("/ProcSet", QPDFObjectHandle::parse("[/PDF /Text]"));
- out.xobjects = QPDFObjectHandle::newDictionary();
- out.resources.replaceKey("/XObject", out.xobjects);
- pg->render(&out, pdf_matrix());
-
- QPDFObjectHandle contents = QPDFObjectHandle::newStream(&out_pdf, out.contents);
-
- // Create the page object
- QPDFObjectHandle out_page = out_pdf.makeIndirectObject(QPDFObjectHandle::newDictionary());
- out_page.replaceKey("/Type", QPDFObjectHandle::newName("/Page"));
- out_page.replaceKey("/MediaBox", BBox(pg->width, pg->height).to_array());
- out_page.replaceKey("/Contents", contents);
- out_page.replaceKey("/Resources", out.resources);
- out_pdf.addPage(out_page, false);
- }
+static void usage()
+{
+ printf("Usage: paperjam [<options>] <commands> <in> <out>\n\
+\n\
+Options:\n\
+-b, --bbox Recalculate bounding boxes\n\
+-d, --debug Show debugging messages\n\
+ --no-auto Disable automatic rotation of pages\n\
+\n\
+<command> = <name>(<args>, <named-arg>[=<value>], ...) [<pipeline>]\n\
+<pipeline> = { <stage>, <stage>, ... }\n\
+<stage> = <page> <page> ... [: <commands>]\n\
+\n\
+Example:\n\
+paperjam 'book nup(2, paper=a4)' input.pdf output.pdf\n\
+\n\
+Commands:\n\
+");
+ parser_help();
+}
- // Produce info dictionary
- QPDFObjectHandle trailer = out_pdf.getTrailer();
- QPDFObjectHandle info = trailer.getKey("/Info");
- if (info.isNull())
- {
- info = QPDFObjectHandle::newDictionary();
- trailer.replaceKey("/Info", info);
- }
- else
- assert(info.isDictionary());
- // FIXME: More meta-data
- info.replaceKey("/Producer", unicode_string("PaperJam"));
-
- // Write the output file
- QPDFWriter writer(out_pdf, out_name);
- writer.write();
+static void show_version()
+{
+ printf("PaperJam " VERSION " -- a PDF processor\n");
+ printf("(c) " YEAR " Martin Mares, distributed under GNU GPL 2+\n");
+ printf("Built on " BUILD_DATE " from Git commit " BUILD_COMMIT "\n");
}
int main(int argc, char **argv)
{
- if (argc != 4)
- {
- fprintf(stderr, "Usage: paperjam <commands> <input> <output>\n");
- return 1;
- }
-
- debug_mode = 100;
+ int c;
+ while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
+ switch (c)
+ {
+ case 'b':
+ recalc_bbox = 1;
+ break;
+ case 'd':
+ debug_level++;
+ break;
+ case OPT_VERSION:
+ show_version();
+ return 0;
+ case OPT_HELP:
+ usage();
+ return 0;
+ case OPT_NO_AUTO:
+ no_auto_transforms = true;
+ break;
+ default:
+ exit(1);
+ }
+
+ if (optind + 3 != argc)
+ die("Exactly three positional parameters should be given");
list<cmd *> cmds;
- parse(argv[1], cmds);
+ parse(argv[optind], cmds);
+ in_name = argv[optind+1];
+ out_name = argv[optind+2];
try
{
- process(cmds, argv[2], argv[3]);
+ process(cmds);
}
catch (exception& e)
{