diff options
Diffstat (limited to 'filter/pdftopdf/pdftopdf_processor.cc')
-rw-r--r-- | filter/pdftopdf/pdftopdf_processor.cc | 363 |
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 ¶m) // {{{ +{ + 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; +} +// }}} |