From 207314fc3dbcfde829f5d0082c6a23ad695d34b9 Mon Sep 17 00:00:00 2001 From: Till Kamppeter Date: Tue, 1 Feb 2022 23:00:46 -0300 Subject: [PATCH] pdftopdf: Fixed print-scaling and N-up for asymmetric margins and files with differently-sized pages (manually backported commit 4aaf23aae3695348532e295859f001704a33ebad and commit 31dfcae961ca737b7166cd6a3e7d4a30cd19f9e8) --- NEWS | 8 + filter/pdftopdf/pdftopdf_processor.cc | 362 ++++++++++----------- filter/pdftopdf/qpdf_pdftopdf_processor.cc | 9 +- 3 files changed, 189 insertions(+), 190 deletions(-) diff --git a/filter/pdftopdf/pdftopdf_processor.cc b/filter/pdftopdf/pdftopdf_processor.cc index 6d2d32ff5..e297a246e 100644 --- a/filter/pdftopdf/pdftopdf_processor.cc +++ b/filter/pdftopdf/pdftopdf_processor.cc @@ -175,31 +175,46 @@ bool processPDFTOPDF(PDFTOPDF_Processor &proc,ProcessingParameters ¶m) // {{ } const int numPages=std::max(shuffle.size(),pages.size()); + fprintf(stderr, "DEBUG: pdftopdf: \"print-scaling\" IPP attribute: %s\n", + (param.autoprint ? "auto" : + (param.autofit ? "auto-fit" : + (param.fitplot ? "fit" : + (param.fillprint ? "fill" : + (param.cropfit ? "none" : + "Not defined, should never happen")))))); + if(param.autoprint||param.autofit){ bool margin_defined = true; bool document_large = false; int pw = param.page.right-param.page.left; int ph = param.page.top-param.page.bottom; - int w=0,h=0; - Rotation tempRot=param.orientation; - PageRect r= pages[0]->getRect(); - w = r.width; - h = r.height; - if(tempRot==ROT_90||tempRot==ROT_270) + if ((param.page.width == pw) && (param.page.height == ph)) + margin_defined = false; + + for (int i = 0; i < (int)pages.size(); i ++) { - std::swap(w,h); + PageRect r = pages[i]->getRect(); + int w = r.width; + int h = r.height; + if ((w > param.page.width || h > param.page.height) && + (h > param.page.width || w > param.page.height)) + { + fprintf(stderr, + "DEBUG: pdftopdf: Page %d too large for output page size, scaling pages to fit.\n", + i + 1); + document_large = true; + } } - if(w>=pw||h>=ph) + if (param.fidelity) + fprintf(stderr, + "DEBUG: pdftopdf: \"ipp-attribute-fidelity\" IPP attribute is set, scaling pages to fit.\n"); + + if (param.autoprint) { - document_large = true; - } - if((param.page.width==pw)&& - (param.page.height==ph)) - margin_defined = false; - if(param.autoprint){ - if(param.fidelity||document_large) { - if(margin_defined) + if (param.fidelity || document_large) + { + if (margin_defined) param.fitplot = true; else param.fillprint = true; @@ -215,204 +230,177 @@ bool processPDFTOPDF(PDFTOPDF_Processor &proc,ProcessingParameters ¶m) // {{ } } + fprintf(stderr, "DEBUG: pdftopdf: Print scaling mode: %s\n", + (param.fitplot ? + "Scale to fit printable area" : + (param.fillprint ? + "Scale to fill page and crop" : + (param.cropfit ? + "Do not scale, center, crop if needed" : + "Not defined, should never happen")))); + + // In Crop mode we do not scale the original document, it should keep the + // exact same size. With N-Up it should be scaled to fit exacly the halves, + // quarters, ... of the sheet, regardless of unprintable margins. + // Therefore we remove the unprintable margins to do all the math without + // them. + if (param.cropfit) + { + param.page.left = 0; + param.page.bottom = 0; + param.page.right = param.page.width; + param.page.top = param.page.height; + } + if(param.fillprint||param.cropfit){ - fprintf(stderr,"[DEBUG]: Cropping input pdf and Enabling fitplot.\n"); - if(param.noOrientation&&pages.size()) - { - bool land = pages[0]->is_landscape(param.orientation); - if(land) - param.orientation = param.normal_landscape; - } for(int i=0;i<(int)pages.size();i++) { std::shared_ptr page = pages[i]; - page->crop(param.page,param.orientation,param.xpos,param.ypos,!param.cropfit); + Rotation orientation = param.orientation; + if (param.noOrientation && + page->is_landscape(param.orientation)) + orientation = param.normal_landscape; + page->crop(param.page, orientation, param.xpos, param.ypos, + !param.cropfit); } - param.fitplot = 1; + if (param.fillprint) + param.fitplot = true; } std::shared_ptr 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; - */ + if ((param.nup.nupX == 1) && (param.nup.nupY == 1) && !param.fitplot) + { + param.nup.width = param.page.width; + param.nup.height = param.page.height; + } + else + { + param.nup.width = param.page.right - param.page.left; + param.nup.height = param.page.top - param.page.bottom; + } - for (int iA=0;iArotate(param.normal_landscape); + param.orientation = param.orientation + param.normal_landscape; + // TODO? better + if (param.nup.nupX != 1 || param.nup.nupY != 1 || param.fitplot) + { + xpos = param.page.height - param.page.top; + ypos = param.page.left; + } + std::swap(param.page.width, param.page.height); + std::swap(param.nup.width, param.nup.height); + } + else + { + if (param.nup.nupX != 1 || param.nup.nupY != 1 || param.fitplot) + { + xpos = param.page.left; + ypos = param.page.bottom; // for whole page... TODO from position... + } + } - if (shuffle[iA]>=numOrigPages) { - // add empty page as filler - proc.add_page(proc.new_page(param.page.width,param.page.height),param.reverse); + NupState nupstate(param.nup); + NupPageEdit pgedit; + for (int iA=0;iA 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; + rect = page->getRect(); + //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++; - 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); + if (param.page_logging == 1) + fprintf(stderr, "PAGE: %d %d\n", outputno, + param.copies_to_be_logged); } - - // place border - if ((param.border!=BorderType::NONE)&&(iAgetRect(); - - 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++; + curpage=proc.new_page(param.page.width,param.page.height); + outputpage++; } - } 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); + if (shuffle[iA]>=numOrigPages) { + continue; } - NupState nupstate(param.nup); - NupPageEdit pgedit; - for (int iA=0;iA 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.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.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 { - if(param.cropfit){ - double xpos2 = (param.page.right-param.page.left-(page->getRect().width))/2; - double ypos2 = (param.page.top-param.page.bottom-(page->getRect().height))/2; - if(param.orientation==ROT_270||param.orientation==ROT_90) - { - xpos2 = (param.page.right-param.page.left-(page->getRect().height))/2; - ypos2 = (param.page.top-param.page.bottom-(page->getRect().width))/2; - curpage->add_subpage(page,ypos2+param.page.bottom,xpos2+param.page.left,1); - }else{ - curpage->add_subpage(page,xpos2+param.page.left,ypos2+param.page.bottom,1); - } - } - else - curpage->add_subpage(page,pgedit.xpos+xpos,pgedit.ypos+ypos,pgedit.scale); + if (param.cropfit) + { + if ((param.nup.nupX == 1) && (param.nup.nupY == 1)) + { + double xpos2, ypos2; + if (param.orientation == ROT_270 || param.orientation == ROT_90) + { + xpos2 = (param.page.width - (page->getRect().height)) / 2; + ypos2 = (param.page.height - (page->getRect().width)) / 2; + curpage->add_subpage(page, ypos2 + xpos, xpos2 + ypos, 1); + } + else + { + xpos2 = (param.page.width - (page->getRect().width)) / 2; + ypos2 = (param.page.height - (page->getRect().height)) / 2; + curpage->add_subpage(page, xpos2 + xpos, ypos2 + ypos, 1); + } } + else + curpage->add_subpage(page,pgedit.xpos+xpos,pgedit.ypos+ypos,pgedit.scale); + } + else + curpage->add_subpage(page, pgedit.xpos + xpos, pgedit.ypos + ypos, + pgedit.scale); #ifdef DEBUG - if (auto dbg=dynamic_cast(curpage.get())) { - // dbg->debug(pgedit.sub,xpos,ypos); - } + if (auto dbg=dynamic_cast(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); + // 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)) { diff --git a/filter/pdftopdf/qpdf_pdftopdf_processor.cc b/filter/pdftopdf/qpdf_pdftopdf_processor.cc index d06ca5f77..621c76880 100644 --- a/filter/pdftopdf/qpdf_pdftopdf_processor.cc +++ b/filter/pdftopdf/qpdf_pdftopdf_processor.cc @@ -179,6 +179,7 @@ void QPDF_PDFTOPDF_PageHandle::add_border_rect(const PageRect &_rect,BorderType Rotation QPDF_PDFTOPDF_PageHandle::crop(const PageRect &cropRect,Rotation orientation,Position xpos,Position ypos,bool scale) { page.assertInitialized(); + Rotation save_rotate = getRotate(page); if(orientation==ROT_0||orientation==ROT_180) page.replaceOrRemoveKey("/Rotate",makeRotate(ROT_90)); else @@ -209,8 +210,8 @@ Rotation QPDF_PDFTOPDF_PageHandle::crop(const PageRect &cropRect,Rotation orient } } else{ - final_w = std::min(width,pageWidth); - final_h = std::min(height,pageHeight); + final_w = pageWidth; + final_h = pageHeight; } fprintf(stderr,"After Cropping: %lf %lf %lf %lf\n",width,height,final_w,final_h); double posw = (width-final_w)/2, @@ -235,13 +236,14 @@ Rotation QPDF_PDFTOPDF_PageHandle::crop(const PageRect &cropRect,Rotation orient //Cropping. // TODO: Borders are covered by the image. buffer space? page.replaceKey("/TrimBox",makeBox(currpage.left,currpage.bottom,currpage.right,currpage.top)); - page.replaceOrRemoveKey("/Rotate",makeRotate(ROT_0)); + page.replaceOrRemoveKey("/Rotate",makeRotate(save_rotate)); return getRotate(page); } bool QPDF_PDFTOPDF_PageHandle::is_landscape(Rotation orientation) { page.assertInitialized(); + Rotation save_rotate = getRotate(page); if(orientation==ROT_0||orientation==ROT_180) page.replaceOrRemoveKey("/Rotate",makeRotate(ROT_90)); else @@ -250,6 +252,7 @@ bool QPDF_PDFTOPDF_PageHandle::is_landscape(Rotation orientation) PageRect currpage= getBoxAsRect(getTrimBox(page)); double width = currpage.right-currpage.left; double height = currpage.top-currpage.bottom; + page.replaceOrRemoveKey("/Rotate",makeRotate(save_rotate)); if(width>height) return true; return false; -- 2.47.1