philpem / printer-driver-ptouch

P-Touch PT-series and QL-series printer driver for Linux (under CUPS)
GNU General Public License v2.0
96 stars 23 forks source link

PT-9700PC fails to print on 36mm tape #3

Closed karlp closed 4 years ago

karlp commented 4 years ago

Hi,

I've got a 9700PC, which I've been using as a 9500PC "just fine" However, attempting to print on 36mm tape simply prints me blank labels.

What I've discovered, (painfully) is that I can only print 24mm tall, and that printing to tz-36 is rotated. Anything bigger than this simply prints a blank label of the expected length.

ie, make a label 36mm tall by 50mm long => blank label comes out 50mm long 36mm tall by 36mm long => blank label comes out 36mm long 36mm tall by 24mm long => label comes out rotated 90°, 36mm long, and 24mm printed area

24mm tall labels print just as expected.

I'm printing with lp -d PT-9700PC label.pdf -o ExtraMargin=0mm -o PageSize=tz-36 So I'm not sure how to feed ptexplain to see if I can see any meaningful differences.

https://support.brother.com/g/b/manualtop.aspx?c=us_ot&lang=en&prod=9700eus has an esc command reference for this printer, but I'm not sure how see what escapes are being run to compare either.

getusbprinterid returns DeviceID MFG:Brother;CMD:PT-CBP;MDL:PT-9700PC;CLS:PRINTER; Vendor '1273', Product '8252'

I have the following packages installed ptouch-driver.x86_64 1.5.1-1.fc32 @updates
ptouch-driver-foomatic.noarch 1.5.1-1.fc32 @updates
foomatic.x86_64 4.0.13-11.fc32 @updates
foomatic-db.noarch 4.0-66.20200526.fc32 @updates
foomatic-db-filesystem.noarch 4.0-66.20200526.fc32 @updates
foomatic-db-ppds.noarch 4.0-66.20200526.fc32 @updates

Any advice or pointers greatly appreciated (Printer happily prints on windows)

andreas-gruenbacher commented 4 years ago

Can you attach the 36x50 example? Maybe I can reproduce.

karlp commented 4 years ago

pdf attached label-eg200.pdf

philpem commented 4 years ago

The ESC-P spec for the PT-9700 shows that the maximum printable area is 384 dots, or 27.1mm, centred within the 36mm tape. The print head physically can't print on the outermost 4.5mm of each edge. This differs from other label tapes which

The setting in the TZ 36mm definition is 102.4 PostScript points = 36.12mm - which would create an oversized image.

@karlp, Can you try setting your paper size to 27.1mm instead of 36mm?

andreas-gruenbacher commented 4 years ago

That doesn't make sense ... the page width / height really is supposed to match the actual tape width, and anything outside the printable area will be clipped.

andreas-gruenbacher commented 4 years ago

On my PT-P900W, label-eg200.pdf prints just fine; even the really small QR code works.

Let my try out your PT-9700 printer description to see what ends up in the raster data.

karlp commented 4 years ago

I've tried 27 and 26 and 25 as well, as I read that it was only 27.1mm in the docs too. I'm home now, but will attach pdfs and photos of how they printed for me tomorrow.

andreas-gruenbacher commented 4 years ago

I've attached the raster command and ptexplain files for PT-9700PC and PT-P900W as label-eg200.tar.gz. This shows that the driver screws up; not sure what's going on.

[The PT-9700PC seems to be using the "legacy hires" command, so I've added that to ptexplain (see https://github.com/trialinfo/ptouch-driver) to decode the raster data.]

Can you check if the 9700PC can cope with the P900W raster commands (cat pt-p900w.ptouch > /dev/usb/lp0)?

Are you able to debug rastertoptch yourself?

The raster data for the PT-P900W is in pt-p900w-1.png. You'll notice that the QR code is very jaggy. The PT-9700PC has the same resolution, so it would get the same raster data. Is that a bitmap or vector data in the pdf? If it's a bitmap, we may need to tell Ghostscript to rasterize bitmaps differently at least for this kind of input. I have no idea which Ghostscript options this would require though. If it's vector data, I don't know how to address that problem.

Note that the 9700PC also seems to supports 360x720 dpi printing. I've not had much luck with double-resolution on the PT-P900W: the printer will refuse to print on many types of tapes with that resolution. So that's not a good workaround for the jaggy QR codes, either.

karlp commented 4 years ago

If you give some tips on how to debug rastertoptch, sure. I don't know where in the chain it gets run. "something on the pdf" | rastertoptch | sudo tee /dev/usb/lp0 or something? If you give me some tips on the first part, I can debug it, sure. I'm not really sure what I'm looking for though :)

Here's an image of printing the pt-p900w.ptouch file, along with other sample files.
IMG_20200904_104034 You can see that it prints much closer to the top edge when it prints the pt-900w file. label-eg200.36x27mm.pdf label-eg200.50x36mm.pdf

I'm still working on the reproducing the rotated print with a corresponding pdf or sample. (The printer is currently refusing all prints with a red light on it)

Honestly, jaggy QR codes I hadn't even noticed, print quality was "fine" but i'm not a print nerd. The QR code scans just fine, so that was good enough for me.

karlp commented 4 years ago

I've got

/usr/lib/cups/filter/gstoraster 999 unused unused 1 "" label-eg200.50x36mm.pdf > raster.50x36mm
/rastertoptch -i raster.50x36mm "" | ./ptexplain ""

But I'm pretty sure I'm missing printer options there, as the ptouch file doesn't end up with only zeros like yoru decode?

andreas-gruenbacher commented 4 years ago

If you give some tips on how to debug rastertoptch, sure. I don't know where in the chain it gets run. "something on the pdf" | rastertoptch | sudo tee /dev/usb/lp0 or something? If you give me some tips on the first part, I can debug it, sure. I'm not really sure what I'm looking for though :)

When you set up a printer, the printer description ends up in /etc/cups/ppd/PT-P900W.ppd and similar. This includes a FoomaticRIPCommandLine which defines how the PostScript input is converted into raster commands; it's a simple shell pipeline with substitutions. It's relatively easy to capture the input to gs, the CUPS raster command output of gs, and the ptouch raster commands that rastertoptch produces: simply insert a "tee /tmp/foo.ps" or similar command into the pipeline. To figure out what %B evaluates to, replace the entire command with "echo %B > /tmp/foo.b" and see what you end up with.

Then you can run parts of the pipeline by hand and/or under gdb, for example. Note that the entire %B string is passed to rastertoptch as a single command line argument.

rastertoptch also has a Debug option that you can add to get some additional information.

I'm still working on the reproducing the rotated print with a corresponding pdf or sample.

I'm pretty sure Ghostscript rotates the pages automatically so that the shorter edge ends up in the tape cut-off direction. I don't think this can be turned off. On the other hand, it's highly unlikely you'll actually need to print 36mm wide labels that are shorter than 36mm.

(The printer is currently refusing all prints with a red light on it)

Possible reasons I'm aware of:

Honestly, jaggy QR codes I hadn't even noticed, print quality was "fine" but i'm not a print nerd. The QR code scans just fine, so that was good enough for me.

Okay, something we can look into later perhaps.

karlp commented 4 years ago

ok. some progress. maybe?

I can insert tee commands and get copies of teh raster and the pt files. In both cases, they apear "ok". (they're not all zero for the ptouch files, and "rasterview" shows what i'd expect for the raster file) However, they cause the printer to choke and blink red.

NOthing will print then until I "reset" the printer by manually printing the p900w file you provided. The saved intermediay .pt file will cause it to keep blinking red. (But I'm unsure what is missing at that point)

I'll look at this some more on monday next. I've attached the 24x36mm label (which appears rotated, as you suggested, but doesn't print anyway), the raster, and the pt file.

label-eg200.24x36mm.pdf Screenshot from 2020-09-04 17-23-18

PT PixelXfer=RLE LegacyTransferMode=1 LegacyHires BytesPerLine=48 Align=Center HalfCut Margin=0 noChainPrinting AutoCut

label-eg200.24x36mm.pt.gz

If you know a shell trick to capture %B without destroying the pipeline that woudl be awesome.

I'm assuming my goal here is to have a "valid" raster, and a ptouch output that doesn't print, and going through rastertoptch with the debugger, ideally comparing workign on 24mm tape to busted on 36mm tape?

karlp commented 4 years ago

To clarify, if my cups raster files saved out of the PPD RIP pipeline appear reasonable with rasterview, is it correct to assume that the problem is entirely within rastertoptch?

andreas-gruenbacher commented 4 years ago

I haven't had the time to verify, but if the raster data appear correct, and they seem to be correct for narrower tapes, then it seems very likely that rastertoptch is to blame.

karlp commented 4 years ago

Cool, that's substantially easier to work through vs running the whole cups pipeline :)

karlp commented 4 years ago

Ok, turns out losing %B is because the replacement is only done once, (think sed s/// vs s///g) so if I capture it, it's gone... :) progress :)

Anyway, I believe I've found the cause (and a solution! yay!) The calculation here: https://github.com/philpem/printer-driver-ptouch/blob/master/rastertoptch.c#L1352 appears to rely on either undefined or unspecified integer promotions? Rewriting the equation as in the diff below results in "sane" values for right_padding_bits and printing out on my 36mm tape that matches the cups rasterview data, yay!

diff --git a/rastertoptch.c b/rastertoptch.c
index 865be2e..118b94e 100644
--- a/rastertoptch.c
+++ b/rastertoptch.c
@@ -1344,19 +1351,28 @@ emit_raster_lines (job_options_t* job_options,
     right_spacing_px =
       (header->cupsPageSize[0] - header->cupsImagingBBox[2]) * pt2px [0];
   }
   /* Calculate right_padding_bytes and shift */
   int right_padding_bits;
+  int orig_right_padding_bits;
   if (job_options->align == CENTER) {
     unsigned left_spacing_px =
       header->cupsImagingBBox[0] * pt2px [0];
-    right_padding_bits
+    fprintf(stderr, "Centered job, lspx = %u, bbox0= %f, ptpx0=%f\n", left_spacing_px, header->cupsImagingBBox[0], pt2px[0]);
+    fprintf(stderr, "bpl now %d, cupsW = %u\n", bytes_per_line, cupsWidth);
+    orig_right_padding_bits /* this method is broken! */
       = (bytes_per_line * 8
          - (left_spacing_px + cupsWidth + right_spacing_px)) / 2
       + right_spacing_px;
+    right_padding_bits = bytes_per_line * 8;
+    right_padding_bits -= (left_spacing_px + cupsWidth + right_spacing_px);
+    right_padding_bits /= 2;
+    right_padding_bits += right_spacing_px;
     if (right_padding_bits < 0) right_padding_bits = 0;
   } else
     right_padding_bits = right_spacing_px;
   int right_padding_bytes = right_padding_bits / 8;
+  fprintf(stderr, "r bytes: %d, r bits: %d, r px: %u (original rpbits: %d)\n", right_padding_bytes, right_padding_bits, right_spacing_px, orig_right_padding_bits);
   int shift = right_padding_bits % 8;
   /* If width is not an integral number of bytes, we must shift   */
   /* right if we don't mirror, to ensure printing starts leftmost */

On my system, the original calculations result in right_padding_bits of 2147483585, which blows everything up and results in the blank document. I can file a PR with just that if you like, but I thought I'd share this first, and see if you think more needs to be done, or anything. (I didn't attempt to diagnose which operation was "wrong" but the mix of signed/unsigned operands made me suspicious at the garbage numbers I was getting out.

(I'm now printing on 36mm wide paper size, and it prints to the 27mm area, so that definitely didn't have to be concerned with)

karlp commented 4 years ago

As far as actual driver support, it prints just fine with and without LegacyTransferMode and LegacyHires, but I've not been trying any different resolutions. Per the brother doc for this pritner, it should not need legacytransfermode, even if it works with both.

philpem commented 4 years ago

I've got a horrible feeling about that mix of signed and unsigned integers.

Can you add right_spacing_px to your first fprintfs and attach the resulting debug output? I'd like to see what values are being used in the calculation.

Debug output from the three 'printf's you added, plus the value of right_spacing_px would be plenty.

karlp commented 4 years ago

yeah, some logs in the commit note for what I just added, but I didn't want to add the debug to a commit. (see https://github.com/philpem/printer-driver-ptouch/pull/5/commits/c609cac540bbf114bc9008c5d6be0329f51c0c8d ) I can certainly provide whatever debug prints you like, those were just a sample. I'm not at all confident that my fix is a "final" solution for this though, if this bug exists, I would be inclined to expect similar latent bugs.

similar here

$ ./rastertoptch -i /home/karlp/src/cbms3/desktop/proddle/label-eg200.50x36mm.raster "PT PixelXfer=RLE BytesPerLine=48 Align=Center HalfCut Margin=0 noChainPrinting AutoCut" 
DEBUG: rastertoptch: job options: PT PixelXfer=RLE BytesPerLine=48 Align=Center HalfCut Margin=0 noChainPrinting AutoCut
DEBUG: rastertoptch: PageSize: 102.00x141.00 pt / 35.98x49.74 mm / 510.00x705.00 px
DEBUG: rastertoptch: ImagingBoundingBox: 0.00 0.00 102.00 141.00 pt / 0.00 0.00 35.98 49.74 mm /0.00 0.00 510.00 705.00 px
DEBUG: rastertoptch: HWResolution: 360x360dpi
DEBUG: rastertoptch: Width Height: 510 705
DEBUG: rastertoptch: NegativePrint: 0
Centered job, lspx = 0, bbox0= 0.000000, ptpx0=5.000000
rspx: 0 bpl now 48, cupsW = 510
rpbits, rpbytes, rspx = 0 0 0 (orig: 2147483585)
boundsd checks? shift=-2, shiftp = 0, bpl = 48
INFO: printing page 1, 100% done
andreas-gruenbacher commented 4 years ago

As far as actual driver support, it prints just fine with and without LegacyTransferMode and LegacyHires, but I've not been trying any different resolutions. Per the brother doc for this pritner, it should not need legacytransfermode, even if it works with both.

You should either use TransferMode or LegacyTransferMode, whichever works for this printer. Otherwise, if the printer is in ESC-P or P-touch Template mode, it won't switch into raster mode and it will likely err out. The printer may initially be in raster mode when turning on, but that doesn't mean other applications can't leave it in a different mode.

karlp commented 4 years ago

Compiling with -Wconversion finds this and many other places. I'm afraid I don't have any more time to dedicate to the rest of them however :|

karlp commented 4 years ago

You should either use TransferMode or LegacyTransferMode, whichever works for this printer. Otherwise, if the printer is in ESC-P or P-touch Template mode, it won't switch into raster mode and it will likely err out. The printer may initially be in raster mode when turning on, but that doesn't mean other applications can't leave it in a different mode.

Ok, that's for at the command line, but for the printer xml file, if I don't specify Legacy, it defaults to "TransferMode" right?

andreas-gruenbacher commented 4 years ago

The integer arithmetic in the driver is a total mess, it just turns out to work well enough. Here's a proper fix. I've debugged this with

$ gdb --args ./rastertoptch "PT PixelXfer=RLE LegacyTransferMode=1 LegacyHires BytesPerLine=48 Align=Center HalfCut Margin=0 noChainPrinting AutoCut" -i label-eg200.cups -o label-eg200.ptouch
$ ptexplain -i label-eg200.ptouch

File label-eg200.cups is the cups raster format data produced by the pipeline in PT-9700PC.ppd, captured by putting | tee /tmp/label-eg200.cups in the pipeline before | rastertoptch.

andreas-gruenbacher commented 4 years ago

Ok, that's for at the command line, but for the printer xml file, if I don't specify Legacy, it defaults to "TransferMode" right?

No, it does not. Can we just leave LegacyTransferMode enabled for this printer as it's seemingly been working for its immediate predecessors as well? Thanks.

karlp commented 4 years ago

Re transfer mode: I just went with the documented behaviour from brother, in the document "cv_pt9700_eng_escp_103.pdf" I thought the intent was to match what was documented, where it worked. Given that the documented form works, and is supported as well, is there any reason to explicitly use "legacy" just because it works? Given that it prints either way, I don't realllly care, I was just trying to make this as correct as possible.

Re the signed explosion fix itself, I have no concern about "my" fix, as long as it gets fixed. your command line is pretty much how I was working on this too, when I'd finally understood enough of the cups filters to work out how to invoke all the pieces correctly. I've no desire to be regularly working on this code (sorry) so your own style fix is certainly fine from my side.

karlp commented 4 years ago

(I'd still highly recommend someone who does work on this code compilign with -Wconversion and having a bit of a review though...)

andreas-gruenbacher commented 4 years ago

Ah, I see now that cv_pt9700_eng_escp_103.pdf documents that the printer supports "ESC i a", so the printer description should indeed use the TransferMode option. Thanks for pointing this out.

andreas-gruenbacher commented 4 years ago

(I'd still highly recommend someone who does work on this code compilign with -Wconversion and having a bit of a review though...)

The Foomatic drivers are all going to break in one of the next versions of cups, so any excess time would probably be better spent on lprint. It may still be worth fixing the QR code rendering as it may be a bit tricky to figure out but easy to implement, though.

andreas-gruenbacher commented 4 years ago

On the QR codes: When cups is fed a PDF file, it converts it into PostScript before passing it to Foomatic. The default renderer used is pdftops, which is what's causing the poor QR code rendering. As described in the cups-filters README, the pdftops-renderer option can be used to chose a different renderer on a per-queue or per-job basis. One of the choices is GostScript itself, which leads to much better results:

 lpr -P PT-9700PC -o pdftops-renderer=gs label-eg200.pdf

I'm sceptical if Foomatic allows to set per-queue options, but if it did, it would make sense to switch the renderer for ptouch-driver to GhostScript by default. We're post-processing the output with GhostScript, so passing files through GhostScript twice should do the least damage.

philpem commented 4 years ago

yeah, some logs in the commit note for what I just added, but I didn't want to add the debug to a commit. (see c609cac ) I can certainly provide whatever debug prints you like, those were just a sample. I'm not at all confident that my fix is a "final" solution for this though, if this bug exists, I would be inclined to expect similar latent bugs.

similar here

$ ./rastertoptch -i /home/karlp/src/cbms3/desktop/proddle/label-eg200.50x36mm.raster "PT PixelXfer=RLE BytesPerLine=48 Align=Center HalfCut Margin=0 noChainPrinting AutoCut" 
DEBUG: rastertoptch: job options: PT PixelXfer=RLE BytesPerLine=48 Align=Center HalfCut Margin=0 noChainPrinting AutoCut
DEBUG: rastertoptch: PageSize: 102.00x141.00 pt / 35.98x49.74 mm / 510.00x705.00 px
DEBUG: rastertoptch: ImagingBoundingBox: 0.00 0.00 102.00 141.00 pt / 0.00 0.00 35.98 49.74 mm /0.00 0.00 510.00 705.00 px
DEBUG: rastertoptch: HWResolution: 360x360dpi
DEBUG: rastertoptch: Width Height: 510 705
DEBUG: rastertoptch: NegativePrint: 0
Centered job, lspx = 0, bbox0= 0.000000, ptpx0=5.000000
rspx: 0 bpl now 48, cupsW = 510
rpbits, rpbytes, rspx = 0 0 0 (orig: 2147483585)
boundsd checks? shift=-2, shiftp = 0, bpl = 48
INFO: printing page 1, 100% done

That makes the issue a bit more obvious. cupsWidth (510 pixels) is greater than the printhead width (bytesPerLine*8, or 384 pixels). When the calculation occurs, it goes like this:

bytesperline = 48
LSPX = 0
RSPX = 0
cupsWidth = 510

(48*8 - (0 + 510 + 0)) / 2 = -63

The implicit conversion to unsigned creates the ridiculously large value via bitwise representation.

For me, the correct fix is to check if width > printhead_width and clamp the left and right padding to zero if that's the case. Pretty much what Andreas proposed, actually.

I'll drop a comment on the PR - but essentially if you want to tweak it to follow that pattern (or pull in Andreas's patch from https://github.com/trialinfo/ptouch-driver/commit/9f1152cd2fb234cd6f38b6b491a1dea226922a55), I'm happy to merge it.

(I'd still highly recommend someone who does work on this code compilign with -Wconversion and having a bit of a review though...)

Yeah, I think that's going to be the next step.

To address the 'future of the project' discussion, I don't see why maintenance of ptouch-driver should stop because it might stop working at some indeterminate point in the future. The same can be said of just about any piece of code ever written by humans. Maintain it for now, and perhaps use it as a baseline for writing a driver engine for lprint.

For your QR code issues, @karlp - you may find that including this as a bi-level (black and white) bitmap might improve print quality. It's a bit of a hack, sadly, and also an area where lprint (https://www.msweet.org/lprint) is intended to improve upon CUPS.

karlp commented 4 years ago

Just take Andreas's patch, he's already solved it to your style.

karlp commented 4 years ago

I can confirm that his patch also works, (as expected)

I've also tried printing with -o pdftops-renderer=gs and it definitely results in less jaggy QR, though phones and our barcode scanner don't seem to care much. I'd printed (jaggy) down on 6mm tape and it was still well scannable, though better is always better! IMG_20200908_094356

I've updated my PR to have Andreas's patch, and my printer xml file, so it's "complete" now

philpem commented 4 years ago

Merged #5, which closes this. Many thanks for your report and the debugging work, @karlp . It's very much appreciated! :+1:

karlp commented 4 years ago

Closed by #5