Bug 701438 - Margin offset error when printing an image
Summary: Margin offset error when printing an image
Status: RESOLVED FIXED
Alias: None
Product: Ghostscript
Classification: Unclassified
Component: CUPS driver (show other bugs)
Version: 9.27
Hardware: PC Linux
: P4 normal
Assignee: Till Kamppeter
URL: https://github.com/OpenPrinting/cups-...
Keywords:
Depends on:
Blocks:
 
Reported: 2019-08-16 20:08 UTC by Solomon Peachy
Modified: 2019-08-25 10:48 UTC (History)
2 users (show)

See Also:
Customer:
Word Size: ---


Attachments
Image showing the incorrect margin offset (4.75 MB, image/jpeg)
2019-08-16 20:08 UTC, Solomon Peachy
Details
Printer PPD that exhibits the problem. (208.84 KB, application/vnd.cups-ppd)
2019-08-16 20:10 UTC, Solomon Peachy
Details
Source PDF used to demonstrate the problem. (10.33 MB, application/pdf)
2019-08-16 20:11 UTC, Solomon Peachy
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Solomon Peachy 2019-08-16 20:08:51 UTC
Created attachment 18005 [details]
Image showing the incorrect margin offset

I've run into a problem when using the ghostscript-based 'pstopdf' filter out of cups-filters where the page margins got incorrectly offset.  When using the poppler-based 'pdftopdf' filter things were correct.  I reported it there, and after some back and forth, they determined the problem was with ghostscript.

Fedora 30, using their ghostscript-9.26-4 package.

See:   

  https://github.com/OpenPrinting/cups-filters/issues/138#issuecomment-522001386

The printer has a L/R hardware margins, and the PPD has these parameters for the selected image size:

*PageSize w432h648/6x9:         "<</PageSize[460.800 657.600]/ImagingBBox null>>setpagedevice"
*PageRegion w432h648/6x9:       "<</PageSize[460.800 657.600]/ImagingBBox null>>setpagedevice"
*ImageableArea w432h648/6x9:    "9.120 0.000 451.680 657.600"
*PaperDimension w432h648/6x9:   "460.800 657.600"

The command line used was this:

cat test.pdf | PPD=DS620.ppd gs -dQUIET -dPARANOIDSAFER -dNOPAUSE -dBATCH
-dNOINTERPOLATE -dNOMEDIAATTRS -dShowAcroForm -sstdout=%stderr
-sOutputFile=%stdout -sDEVICE=cups -r300x300 -dDEVICEWIDTHPOINTS=460
-dDEVICEHEIGHTPOINTS=657 -dcupsBitsPerColor=8 -dcupsColorOrder=0
-dcupsColorSpace=1 -dcupsCompression=1 -scupsPageSizeName=w432h648
-I/usr/share/cups/fonts -c '<</.HWMargins[9.120000 0.000000 9.119995 0.000000]
/Margins[0 0]>>setpagedevice' -f -_ > test.raster

I will attach the PPD and source PDF.
Comment 1 Solomon Peachy 2019-08-16 20:10:14 UTC
Created attachment 18006 [details]
Printer PPD that exhibits the problem.
Comment 2 Solomon Peachy 2019-08-16 20:11:37 UTC
Created attachment 18007 [details]
Source PDF used to demonstrate the problem.
Comment 3 Solomon Peachy 2019-08-16 20:13:35 UTC
Problem has also been confirmed in Ghostscript 9.27 (on Ubuntu Eoan), so I'm   updating the version field.

Please let me know if there's more info I can supply, but I believe the upstream ticket referenced above should contain all the necessary info to reproduce this.
Comment 4 Ken Sharp 2019-08-17 10:19:13 UTC
I'm leaving this to Till, because HWMargins is (I believe) a device-specific parameter. However, a few observations:

Setting -dDEVCIEIWDHTPOINTS and -dDEVICEHEIGHTPOINTS on their own do not actually set the media size, in effect these set the default media size. If you want a specific media size you must also set -dFIXEDMEDIA. Without that, teh PDF itnerpreter will reset the media on every page to the size of teh MediaBox in the PDF file.

Its not at all obvious to me exactly how this printer works, and there are two ways of looking at the 'unprintable area'. The normal way is that the unprintable area is considered as part of the media; that is, 0,0 is considered to be the lower left of the media, but the printer can't actually print there.
 
In that case, if you start printing a full page, then the left margin (for example) would go missing because the pritner couldn't pritn there. But the remainder would render correctly. So the RIP must shrink the content slightly, and shift the image right so that the rendered image fits into the imageable area, and begins exactly at the left edge of the imageble area. In effect it *creates* a white margin around the original page content, shrinking that content to fit between teh margins.


But it looks to me, from the output, that this printer doesn't actually print in the non-imageable area. It starts printing some distance (9 points ?) from the left of the media. It looks (again, guessing) that the PDF file aalready includes this as a white margin. So by starting to print wehat happens is that we get the white margin of the unprintable area, then teh white margin at the left of the image, then the rest of the image, etc.

So here the RIP is required to *subtract* the white margin from the content. In effect, this means the page is printed at full size, but shifted left/down so that the white margin disappears.

Or possibly I misunderstand the goal, not being familiar with the exact printer here.

Anyway I'd suggest a command line more like :

-dDEVICEWIDTHPOINTS=443 -dDEVICEHEIGHTPOINTS=585 -dFIXEDMEDIA -c"<</PageOffset [-9.12 0]>> setpagedevice"

That will shift the content left by 9.12 points, which (in effect) removes the majority of the white space at the left. Assuming the printer then starts printing from the start of the printable area, it will print 1846 pixels of data (at 300 dpi). So you'll get the unprintable area, then the content (starting from 9 points into the original).
Comment 5 Solomon Peachy 2019-08-17 12:51:58 UTC
The image is a little misleading, as it already includes a white border.  However, it is perfectly sized for the printer's imageable area for a 6x8" print.

For this family of of thermal dyesub printers, the reported imageable area is slightly larger than the print media (~2mm on each side) to allow for full-bleed (and media alignment variation) but the actual print head is slightly wider, yielding a few mm to either side that is not printable. Nonetheless the printer requires the RIP (Gutenprint) to send over this "margin" data over.

(Imageable area with 6" media is 1864px wide, but the printer requires a 1920px wide raster line.  The RIP, Gutenprint, handles this fine)
Comment 6 Till Kamppeter 2019-08-22 21:05:12 UTC
For me it seems to work all correctly here.

The input PDF file has the following bounding box:

gs -sDEVICE=bbox test.pdf
GPL Ghostscript 9.27 (2019-04-04)
Copyright (C) 2018 Artifex Software, Inc.  All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
Processing pages 1 through 1.
Page 1
%%BoundingBox: 0 0 443 585
%%HiResBoundingBox: 0.000000 0.000000 442.565986 584.639982

The total width of the page according to the PPD is 460.8 pt. Independent of the width of the unprintable margins the PDF file will be placed on the left edge of the page width, making it being de-centered to the left, which we are seeing here.

Generally, if you print such a file with CUPS you should supply the "-o fit-to-page", "-o fill", or "-o crop-to-fit" command line options. This way the not perfectly matching input file will be centered on the page (and also scaled depending on which option you use). This centering and scaling is done by cups-filters modifying the input PDF file appropriately.

Another point is printer driver design. Your somewhat off-standard driver design can easily confuse users. If a user follows the printer specs or the page size name in the PPD (paper is 6 inches wide) he could easily create PDFs with a page width of 6 inches and the printout gets de-centered because the PDF gets aligned to the left border of the wider, virtual page defined by the driver. A GUI photo application will probably print correctly as it gets the PPD (or does a get-printer-attributes IPP request) and from there knows the page width needed by the driver and scales the photo appropriately. But do not let the GUI app add a border to your photo, as it gets a larger then the actual page width and the actual page height and so your printout will have narrower borders at the left and the right than at the top and the bottom.

To make sure that all works correctly and as the user expects the PaperDimension in the PPD should be the exact size of the paper, it should not have any overspray add-on. If you need to overspray for borderless printing, you need to work with the BorderlessScalingFactor of the CUPS Raster format which allows for blowing up the bitmap somewhat. Please ask Mike Sweet on how to exactly use that in a printer driver (post on CUPS mailing list).
Comment 7 Solomon Peachy 2019-08-22 21:43:45 UTC
I'm sorry, but how exactly can this considered resolved?

>The total width of the page according to the PPD is 460.8 pt. Independent of the width of the unprintable margins the PDF file will be placed on the left edge of the page width, making it being de-centered to the left, which we are seeing here.

I suppose I should have made this explicit, the image out of gs2pdf is offset to the *right* rather than the left.  The corner I annotated is the lower-left corner.  

> Generally, if you print such a file with CUPS you should supply the "-o fit-to-page", "-o fill", or "-o crop-to-fit" command line options. This way the not perfectly matching input file will be centered on the page (and also scaled depending on which option you use). This centering and scaling is done by cups-filters modifying the input PDF file appropriately.

The input file is a pixel-perfect fit for the 6x8 imageable area.

... but I digress.

In the cups-filters bug you said the problem was with ghostscript, and you provided information to use to pass it over here.  I did that.  Now here, you're claiming that ghostscript is correct and the problem is actually with the PPD?

With identical PPD, input and explicit configuration options, the ghostscript-based gs2pdf filter generates different output from the poppler-based pdf2pdf fitler.  Additionally, the image2raster filter generates output that matches the pdf2pdf filter.  The odd one out here is gs2pdf.  The logical conclusion is that gs2pdf is broken, or at least has a different default option somewhere.

Meanwhile, gutenprint has been generating PPDs in this manner (ie page sizes with non-printable margins) pretty much as long as it's had a PPD generator (ie well over a decade) and this specific printer family (DNP) has been in widely-deployed production with these non-printable margins for over five years, printing from the command line and random desktop applications.  It's only when 'gs2pdf' is involved that things go wonky.

So, yeah.  I'm quite confused here.
Comment 8 Till Kamppeter 2019-08-23 19:42:15 UTC
In further investigations I have seen that for your file of size 6x8 inches Ghostscript picked the 6x9 paper size in error whereas pdftoraster (Poppler) picked no size at all and ended up considering the size custom. Due to the fact that the printer does not support custom sizes according to the PPD there were no margins defined for a custom size and therefore zero margins used, leading to pdftoraster centering your file correctly by coincidence.

Now as a first approach of Ghostscript not picking wrong sizes I have reduced the tolerances for comparing paper dimensions to 1%, in commit 2d6bb6e69.

Now Ghostscript does the same as pdftoraster, not finding a correct paper size and ending up with a custom size and so also without margins getting the image centered by accident.

Further improvements to come.
Comment 9 Solomon Peachy 2019-08-24 01:34:42 UTC
Hmm.  So what you're saying is the fact that Gutenprint ever "worked" with a PDF flow was a happy accident.  I can believe that, but what's not clear is if there's a technical problem with how Gutenprint generates or reports its PageSize/etc definitions.

(The 5.3.x Gutenprint series included a major rework of how it handled paper definitions.  This included support for PageSizes in units other than integer points.  For some printers, this resulted in non-integer PageSizes being specified in the PPD.  As far as we can determine, all of this is perfectly legal...)

All that said, if a specific PageSize is requested by the user, it seems inappropriate for GS to implicitly select a different one.  Indeed, it's not clear why GS wasn't matching on the requested size when the input image should have perfectly fit within the requested size's printable area.  Is it possible there's a rounding error resulting from the non-integer PageSize?

Thanks,
Comment 10 Till Kamppeter 2019-08-24 21:28:09 UTC
Fixed in commit 863d77f73, commit 2d6bb6e69, and commit 3e09cede82 in the GIT repository of Ghostscript.

In total I have done the following improvements for finding the correct page size of the PPD file for the input page:

    - Before each page gets rendered its size is compared to the page sizes
      of the PPD file to select the actual page size for the output. Reduced
      the tolerances from 7% for the long edge and 5% for the short edge to
      1%, as before easily wrong sizes got picked.

    - Do not try to match by page size name, the input files do not
      contain page size names.

    - Do not only match the input page size against the actual media sizes
      reported by the PPD but also to the imageable areas. Media size match
      is preferred but if there is no media size match, we accept also
      a match of the imageable area. In case of a match of the imageable
      area we position the input page in the imageable area of the output
      page.

    - More debug output (when building with CUPS_DEBUG). Now the process
      of page size matching can easily get followed.

    - In case of a custom page size (no match with PPD sizes) the page
      size name in the CUPS Raster output is a correct "Custom.XXXxYYY"
      and not the default page size name.

For me it handles your example and some general tests correctly now.

Equivalent improvements in pdftoraster (Poppler-based) will follow.
Comment 11 Till Kamppeter 2019-08-25 10:48:53 UTC
There was one little oversight, now it is complete (commit 3283e6d5ce3).

Fixed in commit 863d77f73, commit 2d6bb6e69, commit 3e09cede82, and commit 3283e6d5ce3 in the GIT repository of Ghostscript.