summaryrefslogtreecommitdiff
path: root/filter/pdftopdf/pdftopdf_processor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'filter/pdftopdf/pdftopdf_processor.cc')
-rw-r--r--filter/pdftopdf/pdftopdf_processor.cc363
1 files changed, 363 insertions, 0 deletions
diff --git a/filter/pdftopdf/pdftopdf_processor.cc b/filter/pdftopdf/pdftopdf_processor.cc
new file mode 100644
index 000000000..697eb4ab4
--- /dev/null
+++ b/filter/pdftopdf/pdftopdf_processor.cc
@@ -0,0 +1,363 @@
+#include "pdftopdf_processor.h"
+#include "qpdf_pdftopdf_processor.h"
+#include <stdio.h>
+#include <assert.h>
+#include <numeric>
+
+void BookletMode_dump(BookletMode bkm) // {{{
+{
+ static const char *bstr[3]={"Off","On","Shuffle-Only"};
+ if ((bkm<BOOKLET_OFF) || (bkm>BOOKLET_JUSTSHUFFLE)) {
+ fprintf(stderr,"(bad booklet mode: %d)",bkm);
+ } else {
+ fputs(bstr[bkm],stderr);
+ }
+}
+// }}}
+
+bool ProcessingParameters::withPage(int outno) const // {{{
+{
+ if (outno%2 == 0) { // 1-based
+ if (!evenPages) {
+ return false;
+ }
+ } else if (!oddPages) {
+ return false;
+ }
+ return pageRange.contains(outno);
+}
+// }}}
+
+void ProcessingParameters::dump() const // {{{
+{
+ fprintf(stderr,"jobId: %d, numCopies: %d\n",
+ jobId,numCopies);
+ fprintf(stderr,"user: %s, title: %s\n",
+ (user)?user:"(null)",(title)?title:"(null)");
+ fprintf(stderr,"fitplot: %s\n",
+ (fitplot)?"true":"false");
+
+ page.dump();
+
+ fprintf(stderr,"Rotation(CCW): ");
+ Rotation_dump(orientation);
+ fprintf(stderr,"\n");
+
+ fprintf(stderr,"paper_is_landscape: %s\n",
+ (paper_is_landscape)?"true":"false");
+
+ fprintf(stderr,"duplex: %s\n",
+ (duplex)?"true":"false");
+
+ fprintf(stderr,"Border: ");
+ BorderType_dump(border);
+ fprintf(stderr,"\n");
+
+ nup.dump();
+
+ fprintf(stderr,"reverse: %s\n",
+ (reverse)?"true":"false");
+
+ fprintf(stderr,"evenPages: %s, oddPages: %s\n",
+ (evenPages)?"true":"false",
+ (oddPages)?"true":"false");
+
+ fprintf(stderr,"page range: ");
+ pageRange.dump();
+
+ fprintf(stderr,"mirror: %s\n",
+ (mirror)?"true":"false");
+
+ fprintf(stderr,"Position: ");
+ Position_dump(xpos,Axis::X);
+ fprintf(stderr,"/");
+ Position_dump(ypos,Axis::Y);
+ fprintf(stderr,"\n");
+
+ fprintf(stderr,"collate: %s\n",
+ (collate)?"true":"false");
+
+ fprintf(stderr,"evenDuplex: %s\n",
+ (evenDuplex)?"true":"false");
+
+ fprintf(stderr,"pageLabel: %s\n",
+ pageLabel.empty () ? "(none)" : pageLabel.c_str());
+
+ fprintf(stderr,"bookletMode: ");
+ BookletMode_dump(booklet);
+ fprintf(stderr,"\nbooklet signature: %d\n",
+ bookSignature);
+
+ fprintf(stderr,"autoRotate: %s\n",
+ (autoRotate)?"true":"false");
+
+ fprintf(stderr,"emitJCL: %s\n",
+ (emitJCL)?"true":"false");
+ fprintf(stderr,"deviceCopies: %d\n",
+ deviceCopies);
+ fprintf(stderr,"deviceReverse: %s\n",
+ (deviceReverse)?"true":"false");
+ fprintf(stderr,"deviceCollate: %s\n",
+ (deviceCollate)?"true":"false");
+ fprintf(stderr,"setDuplex: %s\n",
+ (setDuplex)?"true":"false");
+}
+// }}}
+
+
+PDFTOPDF_Processor *PDFTOPDF_Factory::processor()
+{
+ return new QPDF_PDFTOPDF_Processor();
+}
+
+// (1-based)
+// 9: [*] [1] [2] [*] [*] [3] [4] [9] [8] [5] [6] [7] -> signature = 12 = 3*4 = ((9+3)/4)*4
+// 1 2 3 4 5 6 7 8 9 10 11 12
+// NOTE: psbook always fills the sig completely (results in completely white pages (4-set), depending on the input)
+
+// empty pages must be added for output values >=numPages
+std::vector<int> bookletShuffle(int numPages,int signature) // {{{
+{
+ if (signature<0) {
+ signature=(numPages+3)&~0x3;
+ }
+ assert(signature%4==0);
+
+ std::vector<int> ret;
+ ret.reserve(numPages+signature-1);
+
+ int curpage=0;
+ while (curpage<numPages) {
+ // as long as pages to be done -- i.e. multiple times the signature
+ int firstpage=curpage,
+ lastpage=curpage+signature-1;
+ // one signature
+ while (firstpage<lastpage) {
+ ret.push_back(lastpage--);
+ ret.push_back(firstpage++);
+ ret.push_back(firstpage++);
+ ret.push_back(lastpage--);
+ }
+ curpage+=signature;
+ }
+ return ret;
+}
+// }}}
+
+bool processPDFTOPDF(PDFTOPDF_Processor &proc,ProcessingParameters &param) // {{{
+{
+ if (!proc.check_print_permissions()) {
+ fprintf(stderr,"Not allowed to print\n");
+ return false;
+ }
+
+ if (param.autoRotate) {
+ const bool dst_lscape =
+ (param.paper_is_landscape ==
+ ((param.orientation == ROT_0) || (param.orientation == ROT_180)));
+ proc.autoRotateAll(dst_lscape,param.normal_landscape);
+ }
+
+ std::vector<std::shared_ptr<PDFTOPDF_PageHandle>> pages=proc.get_pages();
+ const int numOrigPages=pages.size();
+
+ // TODO FIXME? elsewhere
+ std::vector<int> shuffle;
+ if (param.booklet!=BOOKLET_OFF) {
+ shuffle=bookletShuffle(numOrigPages,param.bookSignature);
+ if (param.booklet==BOOKLET_ON) { // override options
+ // TODO? specifically "sides=two-sided-short-edge" / DuplexTumble
+ // param.duplex=true;
+ // param.setDuplex=true; ? currently done in setFinalPPD()
+ NupParameters::preset(2,param.nup); // TODO?! better
+ }
+ } else { // 0 1 2 3 ...
+ shuffle.resize(numOrigPages);
+ std::iota(shuffle.begin(),shuffle.end(),0);
+ }
+ const int numPages=std::max(shuffle.size(),pages.size());
+
+ std::shared_ptr<PDFTOPDF_PageHandle> curpage;
+ int outputpage=0;
+ int outputno=0;
+
+ if ((param.nup.nupX==1)&&(param.nup.nupY==1)&&(!param.fitplot)) {
+ // TODO? fitplot also without xobject?
+ /*
+ param.nup.width=param.page.width;
+ param.nup.height=param.page.height;
+ */
+
+ for (int iA=0;iA<numPages;iA++) {
+ if (!param.withPage(iA+1)) {
+ continue;
+ }
+
+ // Log page in /var/log/cups/page_log
+ if (param.page_logging == 1)
+ fprintf(stderr, "PAGE: %d %d\n", iA + 1, param.copies_to_be_logged);
+
+ if (shuffle[iA]>=numOrigPages) {
+ // add empty page as filler
+ proc.add_page(proc.new_page(param.page.width,param.page.height),param.reverse);
+ outputno++;
+ continue; // no border, etc.
+ }
+ auto page=pages[shuffle[iA]];
+
+ page->rotate(param.orientation);
+
+ if (param.mirror) {
+ page->mirror();
+ }
+
+ if (!param.pageLabel.empty()) {
+ page->add_label(param.page, param.pageLabel);
+ }
+
+ // place border
+ if ((param.border!=BorderType::NONE)&&(iA<numOrigPages)) {
+#if 0 // would be nice, but is not possible
+ PageRect rect=page->getRect();
+
+ rect.left+=param.page.left;
+ rect.bottom+=param.page.bottom;
+ rect.top-=param.page.top;
+ rect.right-=param.page.right;
+ // width,height not needed for add_border_rect (FIXME?)
+
+ page->add_border_rect(rect,param.border,1.0);
+#else // this is what pstops does
+ page->add_border_rect(param.page,param.border,1.0);
+#endif
+ }
+
+ proc.add_page(page,param.reverse); // reverse -> insert at beginning
+ outputno++;
+ }
+ } else {
+ param.nup.width=param.page.right-param.page.left;
+ param.nup.height=param.page.top-param.page.bottom;
+
+ double xpos=param.page.left,
+ ypos=param.page.bottom; // for whole page... TODO from position...
+
+ const bool origls=param.nup.landscape;
+ if ((param.orientation==ROT_90)||(param.orientation==ROT_270)) {
+ std::swap(param.nup.nupX,param.nup.nupY);
+ param.nup.landscape=!param.nup.landscape;
+ param.orientation=param.orientation-param.normal_landscape;
+ }
+ if (param.nup.landscape) {
+ // pages[iA]->rotate(param.normal_landscape);
+ param.orientation=param.orientation+param.normal_landscape;
+ // TODO? better
+ xpos=param.page.bottom;
+ ypos=param.page.width - param.page.right;
+ std::swap(param.page.width,param.page.height);
+ std::swap(param.nup.width,param.nup.height);
+ }
+
+ NupState nupstate(param.nup);
+ NupPageEdit pgedit;
+ for (int iA=0;iA<numPages;iA++) {
+ std::shared_ptr<PDFTOPDF_PageHandle> page;
+ if (shuffle[iA]>=numOrigPages) {
+ // add empty page as filler
+ page=proc.new_page(param.page.width,param.page.height);
+ } else {
+ page=pages[shuffle[iA]];
+ }
+
+ PageRect rect;
+ if (param.fitplot) {
+ rect=page->getRect();
+ } else {
+ rect.width=param.page.width;
+ rect.height=param.page.height;
+
+ // TODO? better
+ if (origls) {
+ std::swap(rect.width,rect.height);
+ }
+
+ rect.left=0;
+ rect.bottom=0;
+ rect.right=rect.width;
+ rect.top=rect.height;
+ }
+ // rect.dump();
+
+ bool newPage=nupstate.nextPage(rect.width,rect.height,pgedit);
+ if (newPage) {
+ if ((curpage)&&(param.withPage(outputpage))) {
+ curpage->rotate(param.orientation);
+ if (param.mirror) {
+ curpage->mirror();
+ // TODO? update rect? --- not needed any more
+ }
+ proc.add_page(curpage,param.reverse); // reverse -> insert at beginning
+ // Log page in /var/log/cups/page_log
+ outputno++;
+ if (param.page_logging == 1)
+ fprintf(stderr, "PAGE: %d %d\n", outputno,
+ param.copies_to_be_logged);
+ }
+ curpage=proc.new_page(param.page.width,param.page.height);
+ outputpage++;
+ }
+ if (shuffle[iA]>=numOrigPages) {
+ continue;
+ }
+
+ if (param.border!=BorderType::NONE) {
+ // TODO FIXME... border gets cutted away, if orignal page had wrong size
+ // page->"uncrop"(rect); // page->setMedia()
+ // Note: currently "fixed" in add_subpage(...&rect);
+ page->add_border_rect(rect,param.border,1.0/pgedit.scale);
+ }
+
+ if (!param.pageLabel.empty()) {
+ page->add_label(param.page, param.pageLabel);
+ }
+
+ if (!param.fitplot) {
+ curpage->add_subpage(page,pgedit.xpos+xpos,pgedit.ypos+ypos,pgedit.scale,&rect);
+ } else {
+ curpage->add_subpage(page,pgedit.xpos+xpos,pgedit.ypos+ypos,pgedit.scale);
+ }
+
+#ifdef DEBUG
+ if (auto dbg=dynamic_cast<QPDF_PDFTOPDF_PageHandle *>(curpage.get())) {
+ // dbg->debug(pgedit.sub,xpos,ypos);
+ }
+#endif
+
+ // pgedit.dump();
+ }
+ if ((curpage)&&(param.withPage(outputpage))) {
+ curpage->rotate(param.orientation);
+ if (param.mirror) {
+ curpage->mirror();
+ }
+ proc.add_page(curpage,param.reverse); // reverse -> insert at beginning
+ // Log page in /var/log/cups/page_log
+ outputno ++;
+ if (param.page_logging == 1)
+ fprintf(stderr, "PAGE: %d %d\n", outputno, param.copies_to_be_logged);
+ }
+ }
+
+ if ((param.evenDuplex || !param.oddPages) && (outputno & 1)) {
+ // need to output empty page to not confuse duplex
+ proc.add_page(proc.new_page(param.page.width,param.page.height),param.reverse);
+ // Log page in /var/log/cups/page_log
+ if (param.page_logging == 1)
+ fprintf(stderr, "PAGE: %d %d\n", outputno + 1, param.copies_to_be_logged);
+ }
+
+ proc.multiply(param.numCopies,param.collate);
+
+ return true;
+}
+// }}}