The CUPS Raster output device ("cups", cups/gdevcups.c) supports flipped raster data output for the back sides of the pages when printing duplex, especially used by the HP inkjets which print the back sides backwards when duplexing. It makes use of an extra parameter given by the cupsBackSide PPD keyword described on http://www.cups.org/documentation.php/doc-1.4/spec-ppd.html. To implement this parameter cups/gdevcups.c adapts the CTM via the cups_get_matrix() function, making the CTM not only dependent on things like page size and margins but also on the page number (odd are front sides and even are back sides). This means that the CTM must be recomputed always exactly after the previous page has completed and before the transformation of the current page with the CTM and that also if page size and margins do not change. With PDF code as input this works perfectly, as the PDF interpreter of GS seems to trigger the recomputation of the matrix much more often as the PS interpreter. With PostScript input one still sees that the matrix is often recomputed, but probably too late, after the transfer done with the matrix, as the resulting raster output shows that the old matrix (of page 1) was used for page 2. So what I am looking for is a small change on cups/gdevcups.c which makes the matrix recomputation being triggered at the right moment, like a function call somewhere, a pseudo page size change, ... Do you have any idea? Or did Mike Sweet commit a big design flaw when he implemented this device? In general, not nice from him leaving us with this device, with a lot of bugs, and not taking care of it any more, but recommending to everyone to write CUPS Raster drivers. Thanks in advance for any help.
Looking at the documentation on the cupsBackSide it looks like it provides extensions beyond the PostScript 'Duplex' and 'Tumble'. It may be that the standard 'LeadingEdge' parameter is the missing piece. Unfortunately, the testing of this is difficult since I don't have the stated printer, so I'll just have to try and 'guess' if it is working since it isn't clear (to me) what is meant by: > This means that the CTM must be recomputed always exactly after > the previous page has completed and before the transformation of the current > page with the CTM and that also if page size and margins do not change. in terms of the device calls to 'outputpage' and 'get_initial_matrix' and the PostScript level setpagedevice 'BeginPage' and 'EndPage'. If possible, please provide the output with the following BeginPage and EndPage procs in the pagedevice: << /BeginPage { (BeginPage page ) print =print matrix currentmatrix (, matrix: ) print == flush } /EndPage { (EndPage code=) print dup =print initmatrix matrix currentmatrix (, matrix: ) print == flush exch pop 2 lt } >> setpagedevice
The problem here is that the matrix does not only depend on the options set by the user (duplex, tumble, resolution, ...) but also on whether the page is a front side (odd page) or a back side (even page). So the matrix also needs to be recomputed even if the input PostScript code has no command which changes an option between two pages (probably no "setpagedevice").
See also https://bugs.launchpad.net/hplip/+bug/484928
Here is the stderr output with the modified BeginPage and EndPage procs: BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6525.0] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6525.0] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 1, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] BeginPage page 2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] One sees that for page 2 the matrix did not get switched.
Now I have left in several debug output lines of the CUPS Raster output device (the ones which contain "Processing page " and "matrix"). Now you can see where the output device driver starts to work on a new page (First page is page 1, in contrary to Ghostscript's internal counting starting with 0) and where it recomputed the CTM. One sees clearly that the CUPS output device uses a matrix with a positive 4th component (YY) for the second page but Ghostscript's page device did not overtake it, It continues with the matrix of the first page, with negative YY. INFO: Processing page 1... DEBUG2: matrix = [ 1.389 0.000 0.000 -1.389 -0.000 1100.000 ] DEBUG2: matrix = [ 1.389 0.000 0.000 -1.389 -12.500 1050.000 ] DEBUG2: matrix = [ 1.389 0.000 0.000 -1.389 -25.000 1087.500 ] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] DEBUG2: matrix = [ 1.389 0.000 0.000 -1.389 -25.000 1087.500 ] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] DEBUG2: matrix = [ 1.389 0.000 0.000 -1.389 -25.000 1087.500 ] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] DEBUG2: matrix = [ 1.389 0.000 0.000 -1.389 -25.000 1087.500 ] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] DEBUG2: matrix = [ 1.389 0.000 0.000 -1.389 -25.000 1087.500 ] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] EndPage code=2, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] DEBUG2: matrix = [ 1.389 0.000 0.000 -1.389 -25.000 1087.500 ] BeginPage page 0, matrix: [1.38888884 0.0 0.0 -1.38888884 -25.0 1087.5] DEBUG2: matrix = [ 1.389 0.000 0.000 -1.389 -25.000 1087.500 ] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6525.000 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6525.0] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6525.000 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6525.0] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6525.000 ] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] BeginPage page 0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] EndPage code=0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] INFO: Processing page 2... DEBUG2: matrix = [ 8.333 0.000 0.000 8.333 -150.000 -300.000 ] BeginPage page 1, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 8.333 -150.000 -75.000 ] EndPage code=0, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 8.333 -150.000 -75.000 ] INFO: Processing page 3... DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6708.333 ] BeginPage page 2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -75.000 6708.333 ] DEBUG2: matrix = [ 8.333 0.000 0.000 -8.333 -150.000 6933.333 ] EndPage code=2, matrix: [8.33333302 0.0 0.0 -8.33333302 -150.0 6933.3335]
Thanks, Till. One last request. Please show a 2(or more) page PDF so I can see what that does.
I use the attached PDF as input file and the attached PPD file (with your BeginPage/EndPage redefinitions as a *JobPatchFile) and run export PPD=/etc/cups/ppd/hp990-2.ppd cat frontback.pdf | /usr/lib/cups/filter/pdftops 1 1 1 1 'Duplex=DuplexNoTumble PageSize=A4Duplex' | /usr/lib/cups/filter/pstops 1 1 1 1 'Duplex=DuplexNoTumble PageSize=A4Duplex' | /usr/lib/cups/filter/pstoraster 1 1 1 1 'Duplex=DuplexNoTumble PageSize=A4Duplex' > out.raster 2>log Then I do egrep '\b((Begin|End)Page|Processing page |matrix)\b' log | uniq > importantlog to get the output which I have posted in my previous comment. The result is a file in CUPS Raster format which one can visualize with rasterview from http://www.easysw.com/~mike/rasterview/index.html To show what one can observe in the CUPS Raster output I post some ASCII art here which is from the original mail which Tim Waugh from Red Hat sent to me: Assuming that this is the original document +-----+ +-----+ | /->| | /->| | / | | / | |/ | |/ | +-----+ +-----+ we want to have this in the raster output: +-----+ +-----+ | /->| | /| | / | | / | |/ | |<-/ | +-----+ +-----+ But this is what we get: +-----+ +-----+ | /->| |<-\ | | / | | \ | |/ | | \| +-----+ +-----+ So the Y-axis of the back side is flipped.
Created attachment 5825 [details] frontback.pdf PDF file used as input for the example in the previous comment.
Created attachment 5826 [details] hp990-2.ppd PPD file with the needed duplex setting and the BeginPage/EndPage debug code as *JobPatchFile
Till: Ray gave me a tips about a somewhat similiar bug in PXL - http://bugs.ghostscript.com/show_bug.cgi?id=690948#c2 - the background is that most <<...>> setdevice has no effect on PXL because page parameters can't be altered after the PXL BeginBage instruction is emitted. From what I understand of Ray's comment, it is possible to move most of what's currently in the gs outputpage procedure to fillpage procedure (which currently doesn't exist with pxl) including the PXL BeginBage instruction, thus delaying it to a later stage. Maybe a similiar thing can be done for devcups. (the change is quite invasive, that's why I am not spending much time on 690948 just yet, since only one file is known to affected by it)
Now I am even _more_ confused. The original report stated: > With PDF code as input this works perfectly but the example attached with comments 7 & 8 are a PDF file, and comment 7 says that the output is not "what we want". The pictures go on to show that the back side does not have the Y-axis inverted, which is what 'Tumble' is for, but the options to pdftops and cups specify: Duplex=DuplexNoTumble which seems to contradict the desired 'Tumble' action. Please try with the /Tumble true. Note that I don't know how these filters change from Duplex=DuplexNoTumble to the setpagedevice /Duplex true /Tumble true PostScript standard features.
The "With PDF code as input this works perfectly" means that it works perfectly if PDF is fed directly into the Ghostscript process which uses the CUPS Raster output device. In our example the original PDF file is turned into PostScript by another process (pdftops), then all settings from the PPD file and the BeginPage/EndPage debug code get injected (pstops) and afterwards Ghostscript gets called with PostScript as input and using the "cups" output device to produce CUPS Raster as output. In this situation (and this is the way most Linux distributions handle print jobs) the problem occurs, also if the an applications send PostScript directly the problem occurs. If PDF gets converted directly with export PPD=/etc/cups/ppd/hp990-2.ppd cat frontback.pdf | /usr/lib/cups/filter/pdftoraster 1 1 1 1 'Duplex=DuplexNoTumble PageSize=A4Duplex' > out.raster 2>log The problem does not occur. The HP inkjet printers print the back sides backwards (printing the bottom at first) when duplexing. Therefore the back side must be turned over, which is signaled to the "cups" output device by the cupsBackSide PPD keyword set to "Rotated". The CUPS Raster output (second line of ASCII arts) looks like that DuplexNoTumble and DuplexTumble is the wrong way around but on the paper it comes out correctly. The thing which goes wrong is that there is an extra flip in the Y-axis (compare the second line of ASCII arts with the third line) and this is due to the CTM not being applied in time. See the log output in comment #5.
Please attach the output from the pdftops step so that we have the actual input to Ghostscript.
Created attachment 5832 [details] pdftops-out.ps Output of the pdftops filter step of the command line shown in comment #7.
Created attachment 5833 [details] pstops-out.ps Output of the pstops filter step of the command line in comment #7.
The Ghostscript command line called by the pstoraster CUPS filter forthe example from comment #7 is /usr/bin/gs -dQUIET -dDEBUG -dPARANOIDSAFER -dNOPAUSE -dBATCH -dNOMEDIAATTRS -sDEVICE=cups -sstdout=%stderr -sOUTPUTFILE=%stdout -c -f -_ The input file is supplied via stdin and the output file is sent to stdout.
Fix committed as SVN revision 10625. Please test and report here. Now the y-axis flipping is moved from the matrix to the cups_print_...() functions, where the x-axis flipping already takes place. The matrix does not depend on duplex parameters any more. In addition, the recognition of the used page size was improved, also working whe the margins of the previous page were flipped due to duplex requirements.
Thanks for working on this. It works fine for simple portrait duplexing but fails when trying to print 2-up jobs. I'll attach: 1. 4.pdf, my source document 2. The PPD I'm using (using "PageSetup *Duplex", but "AnySetup *Duplex" also fails in the same way) 3. pdftops.ps, the result of filtering through the CUPS pdftops filter with the options number-up=2 and Duplex=DuplexTumble 4. pstoraster.rast, the result of filtering pdftops.ps through pstoraster with the same options As you can see from the raster file, the reverse side has a great deal of repeated data and weird lines on it, and is no good at all.
Created attachment 5899 [details] 4.pdf
Created attachment 5900 [details] ppd.ppd
Created attachment 5901 [details] pdftops.ps
Created attachment 5902 [details] pstoraster.rast.bz2
It seems that rasterview is somewhat buggy. I also get unusable pages from the second page on in rasterview. On a real printer all was OK for me. Please retry your tests on a real printer.
I tried on a real printer (HP DeskJet 990C) with HPLIP 3.9.8 before posting my feedback here. Could you double-check that the your real print was using the same raster data as rasterview was given?
I can reproduce it by running all filters manually: export PPD=/etc/cups/ppd/hp990-2.ppd echo -en "Page 1\fPage 2\fPage 3\n" | /usr/lib/cups/filter/texttops 1 1 1 1 'cpi=5 lpi=3 Duplex=DuplexNoTumble PageSize=A4.Duplex' | /usr/lib/cups/filter/pstops 1 1 1 1 'page-border=double Duplex=DuplexNoTumble PageSize=A4.Duplex' | /usr/lib/cups/filter/pstoraster 1 1 1 1 'Duplex=DuplexNoTumble PageSize=A4.Duplex' > out.raster 2>log And for testing on a real printer I run in addition cat out.raster | /usr/lib/cups/filter/hpcups 1 1 1 1 'Duplex=DuplexNoTumble PageSize=A4.Duplex' > out.prn 2>log nc -w1 192.168.2.101 9100 < out.prn with the IP being the one of the printer. The printed output is the samy broken output as rasterview shows.
Created attachment 5907 [details] cups/gdevcups.c I have investigated the changes in cups/gdevcups.c. The messed up output is caused when the recomputation of the margins is forced in cups_put_params(), near line 2814 ("if (cups->page != lastpage) { ..."). The recomputation of the margins works, but the memory reallocation (around line 3217) seems to mess up the data. I have attached a slightly modified cups/gdevcups.c trying to force the margin recalculation another way (near line 2814, see debug ouput lines with "XXX"), but this actually does not trigger a margin recalculation. The margin recalculation is needed at every new page as it is possible that the margins are flipped on the back side of the paper (= all even pages) when duplex printing. Ray, how do I get the margins recalculated at the right time and no mess caused by memory reallocation?
Fixed the problem of bitmap data getting messed up. cups_put_params() reallocated memory also if the bitmap size did not change (simply on every margin recalculation). Now it is checked for the size actually changing before reallocation and reallocation is skipped if the size did not change. Committed fixed cups/gdevcups.c to SVN repository, rev 10631.
2-up comes out correctly on my HP PhotoSmart C8100 for me. I used the following commands: export PPD=/etc/cups/ppd/HP-Photosmart-C8100-series.ppd echo -en "Page 1\fPage 2\fPage 3\fPage 4\n" | /usr/lib/cups/filter/texttops 1 1 1 1 'cpi=5 lpi=3 number-up=2 Duplex=DuplexTumble PageSize=A4.Duplex' | /usr/lib/cups/filter/pstops 1 1 1 1 'page-border=double number-up=2 Duplex=DuplexTumble PageSize=A4.Duplex' | /usr/lib/cups/filter/pstoraster 1 1 1 1 'number-up=2 Duplex=DuplexTumble PageSize=A4.Duplex' > out.raster 2>log cat out.raster | /usr/lib/cups/filter/hpcups 1 1 1 1 'number-up=2 Duplex=DuplexTumble PageSize=A4.Duplex' > out.prn 2>log nc -w1 192.168.2.101 9100 < out.prn Note the DuplexTumble, as 2-up makes a landscape document out of the original portrait pages.
This has introduced a regression. When using '-r600x600' (as the turboprint driver does), the raster output is garbled, mainly black. Reported separately as bug #691108.
Fixed in SVN rev 10890.
The remaining problem was that the bitmap memory did not get reallocated when the color depth has changed. This also changes the bitmap size. The fix adds two static variables to track color depth changes. The test case in this bug did not reveal the problem, but the test case of bug #691108.
*** Bug 690392 has been marked as a duplicate of this bug. ***