]> git.ipfire.org Git - thirdparty/cups-filters.git/commitdiff
pdftopdf: Fixed print-scaling and N-up for asymmetric margins and files with differen...
authorTill Kamppeter <till.kamppeter@gmail.com>
Wed, 2 Feb 2022 02:00:46 +0000 (23:00 -0300)
committerTill Kamppeter <till.kamppeter@gmail.com>
Wed, 2 Feb 2022 02:00:46 +0000 (23:00 -0300)
(manually backported commit 4aaf23aae3695348532e295859f001704a33ebad
                 and commit 31dfcae961ca737b7166cd6a3e7d4a30cd19f9e8)

NEWS
filter/pdftopdf/pdftopdf_processor.cc
filter/pdftopdf/qpdf_pdftopdf_processor.cc

diff --git a/NEWS b/NEWS
index 32cfee237c941c4ceaba9b63ecc98df3593d5265..0d0ce186e7e938899b3ce7189896db08a0e8d4e3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,14 @@
 NEWS - OpenPrinting CUPS Filters v1.28.11 - 2022-01-15
 ------------------------------------------------------
 
+CHANGES IN V1.28.12
+
+       - pdftopdf: Fixed all combinations of print-scaling and
+         number-up for printers with asymmetric margins (top !=
+         bottom or left != right) and for input files containing
+         pages with different sizes and/or orientations. Fixes
+         backported from 2.x branch.
+
 CHANGES IN V1.28.11
 
        - libcupsfilters: Let PPD generator take default ColorModel
index 6d2d32ff58f3eb1d9725513708490aa1b343b685..e297a246e97707a997cdd56096eeb4d1f651eea6 100644 (file)
@@ -175,31 +175,46 @@ bool processPDFTOPDF(PDFTOPDF_Processor &proc,ProcessingParameters &param) // {{
   }
   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 &param) // {{
     }
   }
 
+  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<PDFTOPDF_PageHandle> 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<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;
-    */
+  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;iA<numPages;iA++) {
-      if (!param.withPage(iA+1)) {
-        continue;
-      }
+  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;
+  }
 
-      // 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);
+  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.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<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;
+    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)&&(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++;
+      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<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.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<QPDF_PDFTOPDF_PageHandle *>(curpage.get())) {
-       // dbg->debug(pgedit.sub,xpos,ypos);
-      }
+    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);
+    // 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)) {
index d06ca5f7736c4925bcc1cdded6b09b45db3a4c99..621c768801a8a2041f9202e27f13bf2d3f03ef15 100644 (file)
@@ -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;