Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Printing mixed content in the correct order #392

Open
lorro opened this issue Dec 5, 2018 · 17 comments
Open

Printing mixed content in the correct order #392

lorro opened this issue Dec 5, 2018 · 17 comments

Comments

@lorro
Copy link

lorro commented Dec 5, 2018

Hi,

I'm encountering a new issue with the following. I want to print an order receipt with a logo, the order details in html and a QR code that contains the order number. In that order. The problem is the QR code get printed before everything else in the header, while I want it displayed in the footer.

I've got it set up like this

 qz.print(config, getReceiptLogo())
   .then(() => {
     return qz.print(config, getReceiptHtml(this.receipt))
    })
    .then(() => {
      return qz.print(config, getReceiptQR(this.receipt))
     })
export function getReceiptLogo () {
  return [
    `\x1B\x61\x31`, // center align
    {
      type: 'raw',
      format: 'image',
      data: 'logo-receipt.jpg',
      options: {
        language: 'ESCPOS',
        dotDensity: 'double'
      }
    }
  ]
}

export function getReceiptHtml (receipt) {
  return [{
    type: 'pixel',
    format: 'html',
    flavor: 'plain',
    data: 'this contains a bunch of html'
  }]
}

export function getReceiptQR (receipt) {
  const qr = receipt.nr

  // QR Dot Size
  const dots = `\x05`
  // QR proprietary size calculation
  const size1 = String.fromCharCode(qr.length + 3)
  const size0 = `\x00`

  // Create the QR Code
  const data = [
    `\x1B\x61\x31`, // center align
    // <!-- BEGIN QR DATA -->
    `\x1D\x28\x6B\x04\x00\x31\x41\x32\x00`,
    `\x1D\x28\x6B\x03\x00\x31\x43${dots}`,
    `\x1D\x28\x6B\x03\x00\x31\x45\x30`,
    `\x1D\x28\x6B${size1}${size0}\x31\x50\x30${qr}`,
    `\x1D\x28\x6B\x03\x00\x31\x51\x30`,
    `\x1D\x28\x6B\x03\x00\x31\x52\x30`
    // <!-- END QR DATA -->
  ]

  return data
}

I've also tried this as setup as I read it was part of the API in version 2.1:

qz.print(config, [getReceiptLogo(), getReceiptHtml(this.receipt), getReceiptQR(this.receipt)])

But that just prints the QR in the header and a bunch of unprocessed html :)

Do you guys have any idea what I could be doing wrong?

Thanks!

@lite1979
Copy link
Contributor

lite1979 commented Dec 5, 2018

@lorro

@tresf and I wrote this sample a while ago (on 2.0), but the concept should be the same;

ESCP (ESCPOS in this case) content will race ahead of HTML content if processed concurrently, as JavaFX has to process the HTML, then the Epson driver has to process the raster image, but the ESCP will go straight to the printer and get processed by the printer's firmware.

Please note: I use "altPrinting: true" for ESCPOS, because on Linux, the TM_BA driver is usually only used for Pixel printing (HTML/Image/PDF). We tried sending mixed content to two different queues (one raw, one pixel), and the ESCPOS content arrived to the printer first every time.

https://gist.github.com/lite1979/30cb2c6d5ea50fd8a59a6effe837e882

@lorro
Copy link
Author

lorro commented Dec 5, 2018

Could you elaborate a bit what is the important difference with my approach what makes that it doesn't work? I'm actually also chaining different 'then' blocks the same as in the provided example.

I'm running it on a Windows environment so I guess the altPrinting thing doesn't apply to my situation?
Is it important I initialize a new config for each print job I add to the queue?

Thanks

@tresf
Copy link
Contributor

tresf commented Dec 5, 2018

I'm running it on a Windows environment so I guess the altPrinting thing doesn't apply to my situation?

Correct sorry for the confusion Lite's example using { altPrinting: true } printing is specific to systems without raw-capable print drivers, such as macOS and Linux. On those systems, two queues are generally required to combine raw + pixel, but they race (CUPS is FIFO and each queue queues separately and racing occurs). You can leave this setting turned on, it won't impact Windows.

Windows has a separate problem. Its spooler thinks it's smarter than you and in doing so, it processes faster jobs over slower jobs. The only way around this is to turn off the local spooling feature. I believe this will fix your problem.

image

Note, with 2.1 we've added a new feature to rasterize the HTML and inject it into the ESC/POS commands. This would also solve the problem and is the reason the API has changed. We don't have the wiki updated yet, but this bug report #19 combined with the 2.1 sample.html will get you further if you'd like to try it out.

I've also tried this as setup as I read it was part of the API in version 2.1:

The API between 2.0 and 2.1 has changed, so it's important any bugs reports stick to one version and one version only -- especially the syntax. 2.0 has no such configuration option type: pixel and prints to 2.1 using the 2.1 syntax MUST must the 2.1 qz-tray.js file. If 2.1

Is it important I initialize a new config for each print job I add to the queue?

No, this is not needed.

But that just prints the QR in the header and a bunch of unprocessed html :)

This suggests that it's stuck in raw mode. This could have several causes but let's get 2.0 working first with the .then(...) logic and the changes to the Windows driver settings and go from there.

@tresf tresf added the question label Dec 5, 2018
@lorro
Copy link
Author

lorro commented Dec 6, 2018

Hi @tresf,

I'm actually using a 2.1.0-RC5 build.

I tried changing the printer drivers to this. The screenshot is in Dutch but the options are ofc the same as your English example ;)
image

When I try printing afterwards unfortunately I get some unclear error :/ (it printed the logo and the QR code, nothing else)
image

P.S. I get the following output in the logs
edit: but apparently the error isn't related to switching to "print directly to the printer" as I got the same error if I switched back :/

[TRACE] 2018-12-06 12:08:32,606 @ qz.ws.PrintSocketClient:?
	Valid signature from <domain>
[ERROR] 2018-12-06 12:08:32,628 @ qz.utils.FileUtilities:?
	Cannot setup file blocked (Shared)
java.io.IOException: Het systeem kan het opgegeven pad niet vinden
	at java.io.WinNTFileSystem.createFileExclusively(Native Method)
	at java.io.File.createNewFile(File.java:1012)
	at qz.utils.FileUtilities.getFile(Unknown Source)
	at qz.auth.Certificate.isBlocked(Unknown Source)
	at qz.ws.PrintSocketClient.allowedFromDialog(Unknown Source)
	at qz.ws.PrintSocketClient.processMessage(Unknown Source)
	at qz.ws.PrintSocketClient.onMessage(Unknown Source)
	at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:70)
	at org.eclipse.jetty.websocket.common.events.annotated.OptionalSessionCallableMethod.call(OptionalSessionCallableMethod.java:68)
	at org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver$2.run(JettyAnnotatedEventDriver.java:210)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
	at java.lang.Thread.run(Thread.java:748)
[ERROR] 2018-12-06 12:08:32,634 @ qz.utils.FileUtilities:?
	Cannot setup file allowed (Shared)
java.io.IOException: Het systeem kan het opgegeven pad niet vinden
	at java.io.WinNTFileSystem.createFileExclusively(Native Method)
	at java.io.File.createNewFile(File.java:1012)
	at qz.utils.FileUtilities.getFile(Unknown Source)
	at qz.auth.Certificate.isSaved(Unknown Source)
	at qz.ws.PrintSocketClient.allowedFromDialog(Unknown Source)
	at qz.ws.PrintSocketClient.processMessage(Unknown Source)
	at qz.ws.PrintSocketClient.onMessage(Unknown Source)
	at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:70)
	at org.eclipse.jetty.websocket.common.events.annotated.OptionalSessionCallableMethod.call(OptionalSessionCallableMethod.java:68)
	at org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver$2.run(JettyAnnotatedEventDriver.java:210)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
	at java.lang.Thread.run(Thread.java:748)
[TRACE] 2018-12-06 12:08:32,639 @ qz.utils.PrintingUtilities:?
	Waiting for processor, 0/7 already in use
[DEBUG] 2018-12-06 12:08:32,640 @ qz.utils.PrintingUtilities:?
	Using qz.printer.action.PrintRaw to print
[DEBUG] 2018-12-06 12:08:32,640 @ qz.printer.PrintServiceMatcher:?
	Searching for PrintService matching Labelprinter
[DEBUG] 2018-12-06 12:08:32,641 @ qz.printer.PrintServiceMatcher:?
	Found 10 printers
[DEBUG] 2018-12-06 12:08:32,642 @ qz.printer.PrintServiceMatcher:?
	Found match: Labelprinter
[DEBUG] 2018-12-06 12:08:32,643 @ qz.utils.PrintingUtilities:?
	Found Resolution: 20300x20300 dphi
[TRACE] 2018-12-06 12:08:32,664 @ qz.printer.action.PrintRaw:?
	Sending print job to printer
[DEBUG] 2018-12-06 12:08:32,780 @ qz.printer.action.PrintRaw:?
	PrintEvent on sun.print.Win32PrintJob@2ed2c9ec
[DEBUG] 2018-12-06 12:08:32,781 @ qz.printer.action.PrintRaw:?
	PrintEvent on sun.print.Win32PrintJob@2ed2c9ec
[TRACE] 2018-12-06 12:08:32,782 @ qz.printer.action.PrintRaw:?
	Print job received by printer
[INFO] 2018-12-06 12:08:32,783 @ qz.utils.PrintingUtilities:?
	Printing complete
[TRACE] 2018-12-06 12:08:32,784 @ qz.utils.PrintingUtilities:?
	Returning processor back to pool

@lorro
Copy link
Author

lorro commented Dec 6, 2018

I also think rasterizing the HTML wouldn't work for me.
This is my printer config

const config = qz.configs.create('Labelprinter', {
        scaleContent: false,
        rasterize: false,
        margins: {
          right: 0.10,
          left: 0.10
        }
      })

If I didn't set rasterize to false the printed HTML does get extremely blurry and unreadable. When I set it to rasterize: false it is ok.

@tresf
Copy link
Contributor

tresf commented Dec 6, 2018

If I didn't set rasterize to false the printed HTML does get extremely blurry and unreadable.

The config option rasterize: true is for pixel printing. The bug report I linked (#19) was for raw. Although raw will use rasterization under the covers, it does the image to raw conversion on the black pixels only. This may have slightly better results over the pixel technique.

I tried changing the printer drivers to this (and get an unknown error has occurred, please reboot the system)

I'm not sure what that message means. Directly spooling should affect the speed and order of printing , not the reliability of the driver or output. We can install that driver and attempt to reproduce but it will be some time.

@tresf
Copy link
Contributor

tresf commented Dec 7, 2018

@lorro

Here's my working code snippet. This works for me on macOS and Windows (QZ Tray 2.1.0-RC5.6)

The main caveat is that I don't know how to disable auto-cutting on macOS, so all of my macOS receipts have a slice between the HTML and the QR code.

Another caveat, Windows centers the HTML content whereas macOS left-aligns it. I'm not sure why this happens. It's either a nuance with the driver or a bug with the 2.1 HTML printing. Setting up the custom page and HTML document size didn't seem to help but I've left the code commented just incase it comes in handy.

In regards to the Citizen driver not allowing direct printing, that is not typical and should work just fine with most drivers. Below is a screenshot of my results. My testing was done using an Epson TM-T88VI.

/////
///// RAW, altPrinting to overrides CUPS driver
/////
var cfg1 = qz.configs.create("Epson TM-T88VI", { altPrinting: true });
var data1 = [
  '\n\n\n\n\n\n\n\n', // feed
  '\x1B\x61\x31', // center align
  { type: 'raw', format: 'image', data: 'assets/img/image_sample_bw.png', options: { language: 'ESCPOS', dotDensity: 'double' } }
];

/////
///// HTML, disable altPrinting for macOS / Linux
/////
var cfg2 = qz.configs.create("Epson TM-T88VI", { margins: { left: 2.5 /* mm */}, /* size: { width: 80, height: 297 }, */ units: 'mm' } /*, { altPrinting: false }*/);
var data2 = [
  { type: 'pixel', format: 'html', flavor: 'plain', data: '<html><h1><i>HTML STUFF!</i></h1></html>' /*, pageWidth: 80 */ /* mm */ }
];

/////
///// RAW, altPrinting to overrides CUPS driver
/////
var cfg3 = cfg1;
var qr = '123456';
var dots = '\x05'; /**/ var size1 = String.fromCharCode(qr.length + 3); /**/ var size0 = '\x00';
var data3 = [
  '\x1B\x61\x31', // center align
  // <!-- BEGIN QR DATA -->
  '\x1D\x28\x6B\x04\x00\x31\x41\x32\x00',
  '\x1D\x28\x6B\x03\x00\x31\x43' + dots,
  '\x1D\x28\x6B\x03\x00\x31\x45\x30',
  '\x1D\x28\x6B' + size1 + size0 + '\x31\x50\x30' + qr,
  '\x1D\x28\x6B\x03\x00\x31\x51\x30',
  '\x1D\x28\x6B\x03\x00\x31\x52\x30',
  // <!-- END QR DATA -->
 '\n\n\n\n\n\n\n\n' // feed
];

qz.print([cfg1, cfg2, cfg3], [data1, data2, data3]).catch(err => console.error(err));

img_20181206_223432

@lorro
Copy link
Author

lorro commented Dec 7, 2018

Hi @tresf,

thanks for the update.

Just for testing purposes I actually copy/pasted your code sample and just replaced the printer name with mine. Tried printing but that throws the following error.

qz-tray.js?4421:946 Uncaught TypeError: config.getPrinter is not a function
    at Object.print (qz-tray.js?4421:946)
    at VueComponent.sendJobToPrinter (ReceiptPrinter.vue?fef6:90)
    at invoker (vue.esm.js?efeb:2027)
    at HTMLDivElement.fn._withTask.fn._withTask (vue.esm.js?efeb:1826)

Any idea?
That's when I run qz.print([cfg1, cfg2, cfg3], [data1, data2, data3]).catch(err => console.error(err));
When I would create one main single config and pass only that it prints but like before only some unprocessed code (raw and html both). qz.print(cfg1, [data1, data2, data3]).catch(err => console.error(err));

@tresf
Copy link
Contributor

tresf commented Dec 7, 2018

@lorro, that's weird. I was getting that error when I tried to clone my configs (the objects are mutable and I was attempting to use .reconfigure(...) the wrong way). Just a thought I'm not sure.

I assume you're using exactly matched versions of QZ Tray and qz-tray.js?

@lorro
Copy link
Author

lorro commented Dec 7, 2018

Oh fuck, seems I'm still using the 2.0.8 browser plugin.
I pulled that in via npm install.

Is there a way to pull version 2.1.0 through npm?
I get this when trying:

npm ERR! notarget Valid install targets:
npm ERR! notarget 2.0.1-1, 2.0.1-1B, 2.0.1-1C, 2.0.1-1D, 2.0.1-1E, 2.0.1-2, 2.0.3, 2.0.3-1, 2.0.4, 2.0.6, 2.0.6-1, 2.0.7, 2.0.8

@tresf
Copy link
Contributor

tresf commented Dec 7, 2018

2.1.0 will be published to npm only after it reaches stable release. Sorry for the inconvenience.

@lorro
Copy link
Author

lorro commented Dec 7, 2018

Ok I understand. So I just updated the file manually to see what happens now.
Printing is fine now with

qz.print([config, config, config], [
  getReceiptLogo(this.localReceipt), 
  getReceiptHtml(this.localReceipt),
  getReceiptQR(this.localReceipt)
])

But still the QR gets printed above the logo, so that issue remains :)
Unfortunately I can't set the Citizen drivers to "print directly" as that still generated that unclear error :/

@tresf
Copy link
Contributor

tresf commented Dec 7, 2018

Yeah, this driver goes haywire when I click that radio button. I'm not sure why it does this, but it's not a problem with QZ Tray as far as I can tell. It actually sends my print spooler into panic and pegs it at 25% and the job hangs indefinitely. In some tests, the CITIZEN CT-S310II printer can't be found by QZ Tray and in other tests the queue just immediately goes offline and QZ Tray hangs.

I was able to reproduce the Unknown error has occurred but that was after turning the spooling feature back on. Restarting the print spooler got that message to go away.

At this point, I'm not sure the best recourse. The racing issue appears to be OS related and the only viable workaround that we have is incompatible with the driver you're using. You may find luck by forcing a better driver (such as the Epson TM-T88 series) driver but using a 3rd party driver can be harmful to the hardware.

The last recommendation is to use the HTML->Raw feature which was proposed earlier. To get an understanding of the quality, you can use the ESCP radio button and Raster Print, HTML button on the 2.1 sample.html.

@tresf
Copy link
Contributor

tresf commented Dec 7, 2018

One last item is to use a supported 3rd party driver such as Seagull Scientific. I'm not sure how well this works with raw prints.

Note, we did reach out to Seagull Scientific several times in the past and they were unwilling to talk to us without a subscription, but we've had several clients switch to them for specific needs over the years.

@lorro
Copy link
Author

lorro commented Dec 7, 2018

I'm afraid our printer model (CT-S310II) isn't supported by the Seagull drivers.
It's not in the list. Thanks anyway for the suggestion :)

@tresf
Copy link
Contributor

tresf commented Dec 7, 2018

I'm going to close this out as QZ Tray appears to be functioning fine. Please keep us posted as to your progress.

@tresf tresf closed this as completed Dec 7, 2018
@tresf tresf reopened this Mar 2, 2021
@tresf
Copy link
Contributor

tresf commented Mar 2, 2021

Reopening as this as it may be possible using PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST. We're introducing more win32 direct API calls since #736, so @Vzor- now has this on his radar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants