From 4aaf23aae3695348532e295859f001704a33ebad Mon Sep 17 00:00:00 2001 From: Till Kamppeter Date: Wed, 17 Nov 2021 19:42:32 +0100 Subject: [PATCH] libcupsfilters: Fixed several bugs and did some clean-up in pdftopdf() - Tested and corrected processing according to the "print-scaling" IPP attribute, especially an A4 document on A4 destination page size got shrinked by the width of the unprintable margins. - Also tested and corrected N-up printing with all settings for "print-scaling" and with documents containing pages of different sizes and different orientations. - On multi-page documents check each page (not only the first page) if it is larger than the destination page size and with at least one oversized page apply scale-to-fit/fill to all pages (if "print-scaling"is "auto" or "auto-fit"). - On multi-page documents check each page individually whether it needs to get rotated when determining how it should get cropped. - Make sure that the page->crop() and page->is_landscape() do not modify the /Rotate keys in the PDF. Save the key and restore it before returning. - Cleaned up the code for processing the pages, especially removed code which never gets called, with any of the possible settings for "print-scaling". - For options use the options/num_options data structure which contains both command line options and IPP attributes. - Added more log messages, especially for the "print-scaling" and "ipp-attribute-fidelity" IPP attributes. --- cupsfilters/pdftopdf/pdftopdf.cc | 6 +- cupsfilters/pdftopdf/pdftopdf_processor.cc | 377 ++++++++---------- .../pdftopdf/qpdf_pdftopdf_processor.cc | 5 +- 3 files changed, 183 insertions(+), 205 deletions(-) diff --git a/cupsfilters/pdftopdf/pdftopdf.cc b/cupsfilters/pdftopdf/pdftopdf.cc index 1ff26fd88..de1693441 100644 --- a/cupsfilters/pdftopdf/pdftopdf.cc +++ b/cupsfilters/pdftopdf/pdftopdf.cc @@ -1063,9 +1063,9 @@ pdftopdf(int inputfd, /* I - File descriptor input stream */ doc.iscanceledfunc = iscanceled; doc.iscanceleddata = icd; - getParameters(data,data->num_options,data->options,param,final_content_type,&doc); + getParameters(data, num_options, options, param, final_content_type, &doc); -calculate(data,param,final_content_type); + calculate(data, param, final_content_type); #ifdef DEBUG param.dump(&doc); @@ -1075,7 +1075,7 @@ calculate(data,param,final_content_type); // job through QPDL (so no page management, form flattening, // page size/orientation adjustment, ...) if ((t = cupsGetOption("filter-streaming-mode", - data->num_options, data->options)) != + num_options, options)) != NULL && (strcasecmp(t, "false") && strcasecmp(t, "off") & strcasecmp(t, "no"))) { diff --git a/cupsfilters/pdftopdf/pdftopdf_processor.cc b/cupsfilters/pdftopdf/pdftopdf_processor.cc index 5d273bb73..7b5d2f714 100644 --- a/cupsfilters/pdftopdf/pdftopdf_processor.cc +++ b/cupsfilters/pdftopdf/pdftopdf_processor.cc @@ -192,31 +192,48 @@ bool processPDFTOPDF(PDFTOPDF_Processor &proc,ProcessingParameters ¶m,pdftop } const int numPages=std::max(shuffle.size(),pages.size()); - if(param.autoprint||param.autofit){ + if (doc->logfunc) doc->logfunc(doc->logdata, FILTER_LOGLEVEL_DEBUG, + "pdftopdf: \"print-scaling\" IPP attribute: %s", + (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)) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, FILTER_LOGLEVEL_DEBUG, + "pdftopdf: Page %d too large for output page size, scaling pages to fit.", + i + 1); + document_large = true; + } } - if(w>=pw||h>=ph) + if (param.fidelity && doc->logfunc) + doc->logfunc(doc->logdata, FILTER_LOGLEVEL_DEBUG, + "pdftopdf: \"ipp-attribute-fidelity\" IPP attribute is set, scaling pages to fit."); + + 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; @@ -224,220 +241,178 @@ bool processPDFTOPDF(PDFTOPDF_Processor &proc,ProcessingParameters ¶m,pdftop else param.cropfit = true; } - else{ - if(param.fidelity||document_large) + else + { + if (param.fidelity || document_large) param.fitplot = true; else param.cropfit = true; } } - if(param.fillprint||param.cropfit){ - if (doc->logfunc) doc->logfunc(doc->logdata, FILTER_LOGLEVEL_DEBUG, - "pdftopdf: Cropping input pdf and Enabling " - "fitplot."); - 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++) + if (doc->logfunc) doc->logfunc(doc->logdata, FILTER_LOGLEVEL_DEBUG, + "pdftopdf: Print scaling mode: %s", + (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")))); + + if (param.fillprint || param.cropfit) + { + 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,doc); + 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, doc); } - param.fitplot = 1; } 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; - */ - - for (int iA=0;iAlogfunc) doc->logfunc(doc->logdata, FILTER_LOGLEVEL_CONTROL, - "PAGE: %d %d", 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,doc),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)&&(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++; - } - } else { - param.nup.width=param.page.right-param.page.left; - param.nup.height=param.page.top-param.page.bottom; + 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; + } - double xpos=param.page.left, - ypos=param.page.bottom; // for whole page... TODO from position... + 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; + } - 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; + double xpos = 0, ypos = 0; + if (param.nup.landscape) + { + // pages[iA]->rotate(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.bottom; + ypos = param.page.width - param.page.right; } - 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); + 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... } + } - 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,doc); - } else { - page=pages[shuffle[iA]]; + NupState nupstate(param.nup); + NupPageEdit pgedit; + for (int iA = 0; iA < numPages; iA ++) + { + std::shared_ptr page; + if (shuffle[iA] >= numOrigPages) + // add empty page as filler + page = proc.new_page(param.page.width, param.page.height, doc); + else + page = pages[shuffle[iA]]; + + PageRect rect; + rect = page->getRect(); + //rect.dump(doc); + + 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) + if (doc->logfunc) doc->logfunc(doc->logdata, + FILTER_LOGLEVEL_CONTROL, + "PAGE: %d %d", outputno, + param.copies_to_be_logged); } + curpage = proc.new_page(param.page.width, param.page.height, doc); + outputpage++; + } - 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) - if (doc->logfunc) doc->logfunc(doc->logdata, - FILTER_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno, - param.copies_to_be_logged); - } - curpage=proc.new_page(param.page.width,param.page.height,doc); - outputpage++; - } - if (shuffle[iA]>=numOrigPages) { - continue; - } + 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) - if (doc->logfunc) doc->logfunc(doc->logdata, FILTER_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno, - param.copies_to_be_logged); - } + //pgedit.dump(doc); + } + 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) + if (doc->logfunc) doc->logfunc(doc->logdata, FILTER_LOGLEVEL_CONTROL, + "PAGE: %d %d", outputno, + param.copies_to_be_logged); } if ((param.evenDuplex || !param.oddPages) && (outputno & 1)) { diff --git a/cupsfilters/pdftopdf/qpdf_pdftopdf_processor.cc b/cupsfilters/pdftopdf/qpdf_pdftopdf_processor.cc index 00e29c8e8..a443e9a48 100644 --- a/cupsfilters/pdftopdf/qpdf_pdftopdf_processor.cc +++ b/cupsfilters/pdftopdf/qpdf_pdftopdf_processor.cc @@ -180,6 +180,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,pdftopdf_doc_t *doc) { page.assertInitialized(); + Rotation save_rotate = getRotate(page); if(orientation==ROT_0||orientation==ROT_180) page.replaceOrRemoveKey("/Rotate",makeRotate(ROT_90)); else @@ -238,13 +239,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 @@ -253,6 +255,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.3