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

Sending commands to printer through Cloud Print #4

Open
svang-app opened this issue Mar 5, 2021 · 14 comments
Open

Sending commands to printer through Cloud Print #4

svang-app opened this issue Mar 5, 2021 · 14 comments

Comments

@svang-app
Copy link

svang-app commented Mar 5, 2021

Hi,

First - Thank you San for your work and sharing. This is very helpful.

We are using Star - MC3 printer and using its CloudPrint facility to send print through the REST API server as we need to print directly on the printer without any intermediate device. After looking through your markup language, I am reluctant to use Star proprietary markup language. We would like to follow a standard, that you are developing.

I ran your sample code and it works fine if I send the print commands directly to my printer in the local subnet using its IP address and port 9100. The prints are as advertised and seen through the designer - which is really very useful to visualize.

But, when I send the same command through the REST API through our API server to the printer, everything prints OK except the line.

Here is the sample code:

printCommands.js file

var doc = `
{width:auto}
|^^^^Pizza Station^^^^|

-
{width:5 *}
|^^2 @^^ |^^Royal Special Pizza |
|   |Olive, Spinach, Asparagus, Tomato  | 
|^^1 @^^ |^^12 inch Subway with basil |
-

-
{width:auto}
Delivery Station
{width:5 *}
|2 @|French Fries |
|1 @|Diet Coke |
-


{code:2012345678903;option:ean,hri}


`;
...
async function ticket() {
	// printer example
	const printer = {
		cpl: 42,
		encoding: 'cp437',
		upsideDown: false,
		gamma: 1.8,
		command: 'starmbcs'
	};
	return receiptline.transform(doc, printer); ;
}
...

The above is called in the REST API GET request sent by the printer to my API server.

var printer = require("./printCommands");
app.get('/api/v1/cloudprint', (req, res) => {
    printer.ticket().then((buffer) => {
        res.status(200).type("application/vnd.star.starprnt").send(buffer);
        toPrint = false;
    }).catch((err) => {
        console.log("Getting error in sending commands to the printer", err);
        res.status(200).end();
    });
}

I have tried starsbcs and various supported input methods from Star Link but I was not able to get the line printing properly.

I am attaching here both the prints - one sent directly to the printer (works perfectly) and another sent through REST API using CloudPrint of Star Printer.

sample

Can you please provide tips or hacks for the time being so that I am able to print it correctly?

Thank-You San!

@receiptline
Copy link
Owner

Hello!
Thank you for using receiptline. Please try the following.

  • Set command to match the memory switch setting of mC-Print3

    // command: 'starmbcs'
    command: 'starsbcs'
  • Change the character encoding of express from utf-8 to binary

    // return receiptline.transform(doc, printer);
    return Buffer.from(receiptline.transform(doc, printer), 'binary');

@svang-app
Copy link
Author

Thank YOU, San!

It worked perfectly. I realize that it is mentioned in the document somewhere but I did not realize it until you told me.

However, I also tried the other way as an alternative where I used svg as a source and converted it to the PNG, and then sent that to the Star MC-3 printer through our REST API server. The output from both is attached and I find that the print quality is much better-using PNG as opposed to the raw printer commands. It also looks that the print is little bit expanded when using printer commands. The first picture uses raw commands after using your suggested workaround and the second is using the conversion of SVG to the PNG and the output.

print

This is what I also did with SVG and then the REST API server.

async function ticket() {
	// printer example
	const printer = {
		cpl: 48,
		encoding: 'cp437',
		upsideDown: false,
		gamma: 1.8,
		command: 'svg'
	};
	return receiptline.transform(doc, printer);
}

REST API Server.

const { convert } = require('convert-svg-to-png');
    // Alternate approach of using reciptline - create a SVG first and then convert to PNG 
    printer.ticket().then((buffer) => {
        convert(buffer, {"background": "0xffffff"}).then((png) => {
            res.status(200).type("image/png").send(png);
            toPrint = false;
        });
    });

I am now a little confused about which approach - I should take.

Also, I am going to adopt your standard for receipt printing and am thinking to write some handlebars that can take care of emitting the markup syntax using an input JSON.

Thank again - I wish that all major printer manufacturers adopt this as a standard. I like Star printers and they are very nice in support but your markup language is far better especially the designer.

@receiptline
Copy link
Owner

I think startups tend to print in graphics, but POS systems traditionally print in device fonts for the following reasons

  • Faster printing speed
  • Works well with legacy serial connections
  • Clear characters suitable for business use

As for character visibility, the printing quality of thermal paper is low, so graphics may cause misreading of characters.

  • 1, I or l
  • 0 or O
  • e or c

The SVG created by receiptline uses a web font called Courier Prime.
The visibility of the font is good, but please consider whether it meets the requirements of your system.

By the way, I would like to introduce convert-svg-to-png to some people who just want to print SVG receipt images.

Thank you!

@svang-app
Copy link
Author

Thank YOU, San!

I am running into another issue. Our customer has several Epson TM-220 impact printers. I understand that these printers are as old as 2004 and they are still being used.

I tried receiptline to do tests with an Epson printer that I ordered from ebay to test. If I am able to do the simple prints using RED and Black ribbons - it will solve the problem.

Following your node.js example, I tried the following code.

const receiptline = require('receiptline');
const net = require('net');

var doc = `
{width:auto}
|^Pizza Station^|
{width:5 *}
|2 @ |Royal Special Pizza |
|   |Olive, Spinach, Asparagus, Tomato  | 
|1 @ |12 inch Subway with basil |`;

async function ticket() {
	// printer example
	const printer = {
        "host": "192.168.1.170",
        "port": 9100,
        "cpl": 48,
        "encoding": "cp437",
        "gamma": 1.8,
        "upsideDown": false,
        "spacing": true,
        "cutting": true,
        "command": "escpos"
	};
	const commands = Buffer.from(receiptline.transform(doc, printer), 'binary');

    const host = printer.host;
    const port = printer.port;

    const sock = net.connect(port, host);

    sock.on('connect', () => {
        sock.write(commands);
    });
    sock.on('timeout', () => {
        sock.end();
    });
    sock.on('error', () => {

    });
    sock.setTimeout(60);

}

ticket();

module.exports = {
	ticket
};

It prints but with some extra chars. Your README says that the receiptline supports Epson TM series printers so I was hoping that I could get some help from you.

The printed receipt is as per the picture attached.

I very much appreciate your guidance.

epson-print

@receiptline
Copy link
Owner

Thanks again for using receiptline.
The current implementation only supports thermal printers.
So we will support TM-U220 impact printer by the end of this week.

@svang-app
Copy link
Author

svang-app commented Mar 9, 2021

Thank YOU so much. This will be very useful and I really appreciate your help.

If you support TM-U220 impact printers, you can remove all those features that are not necessary like code, QR Code, images, etc, and the support for USB, etc. The only requirements will be Ethernet and at most serial cables. The markup that is only supported on Thermal printers can be safely ignored and not printed. For example, safely ignore images, etc.

If I could use the same markup, it will make life much easier. I am using handlebars helper functions to abstract the formatting so that the code works seamlessly for Thermal printers and future TM-220 impact printers.

For example: Here is the sample code.

File: lib/index.js
'use strict';
const hb = require('handlebars');

hb.registerHelper('space', function(str) {
    return `||`;
});

hb.registerHelper('center1', function(str) {
    return `|^${str}^|`;
});

hb.registerHelper('leftS', function(str) {
    return `|${str} |`;  
});

hb.registerHelper('title3', function(str) {
    return `|^^^${str}^^^|`;  
});

hb.registerHelper('titleP', function(str) {
  return `{border:line; width:auto}
  |^^^^${str}^^^^|`;  
});

hb.registerHelper('titleS', function(str) {
  return `{border:line; width:auto}
  |${str}|`;  
});

hb.registerHelper('line', function() {
    return `-`;
});

hb.registerHelper('col3receipt', function(qty, name, price) {
    return `{width:5 * 7}
    |^^${qty} @^^ |^^${name} | ^^$${price}^^|`;
});

hb.registerHelper('barcode', function(code) {
    return `{border:space;}
{code:${code}; option:code128,2,72,hri}`;

});

hb.registerHelper('qrcode', function(url, size=4) {
    return `{border:line;}
{code:${url}; option:qrcode,${size},L}`;
});

hb.registerHelper("col1sw", function(col1) {
    return `{border:space; width: *}
|${col1}|`;
});

hb.registerHelper("col1dw", function(col1) {
    return `{border:space; width: *}
|^^${col1}^^|`;
});

hb.registerHelper("col2dw", function(col1w, col1, col2) {
    return `{border:line; width:${col1w} *}
|^^${col1}^^ |^^${col2} |`;
});

hb.registerHelper("col2sw", function(col1w, col1, col2) {
    return `{border: line; width:${col1w} *}
|${col1} |${col2} |`;
});

hb.registerHelper("col3dw", function(col1w, col2w, col1, col2, col3) {
    return `{border:line; width:${col1w} ${col2w} *}
|^^${col1}^^ |^^${col2} | ^^${col3}|`;
});

hb.registerHelper("col3sw", function(col1w, col2w, col1, col2, col3) {
    return `{border:line; width:${col1w} ${col2w} *}
|${col1} |${col2} | ${col3}|`;
});

module.exports = hb;

The sample template

const hb = require("./lib");
const receiptline = require("receiptline");

var data = {
    "orderNum": "BFAG67",
    "url": "https://svang.app",
    "tickets": [
        {
            "stationName": "Pizza Station",
            "primary": true,
            "items": [
                {
                    qty: 1,
                    item: "Royal Special Pizza",
                    modifier: "Add extra tomato, basil, cucumber, black olives, extra salt and pepper"
                },
                {
                    qty: 2,
                    item: "Subway Sandwitch Basil",
                    modifier: "12 inch, extra Guac, Olive Oil, Honey Mustard ranch, Southwest Chipotle"
                }
            ]
        },
        {
            "stationName": "Naan Station",
            "primary": false,
            "items": [
                {
                    qty: 1,
                    item: "Stromboli",
                    modifier: "Strict vegetarian, do not mix, extra cheese, extra tomato"
                }
            ]
        },
        {
            "stationName": "Delivery Station",
            "primary": false,
            "items": [
                {
                    qty: 2,
                    item: "French Fries"
                },
                {
                    qty: 1,
                    item: "2 Litre Soft Drink",
                    modifier: "Diet Coke"
                }
            ]
        }
    ]
}

var source = `
{{#each tickets}}
{{#if primary}}
  {{titleP stationName}}
    {{line}}
    {{col2dw 5 "Qty" "Item"}}
    {{line}}
    {{#each items}}
        {{col2dw 5 qty item}}
        {{#if modifier}}
            {{col2sw 5 " " modifier}}
        {{/if}}
    {{/each}}
{{else}}
  {{titleS stationName}}
  {{line}}
    {{col2sw 5 "Qty" "Item"}}
    {{line}}
    {{#each items}}
        {{col2sw 5 qty item}}
        {{#if modifier}}
            {{col2sw 5 " " modifier}}
        {{/if}}
    {{/each}}
{{/if}}  
{{line}}
{{/each}}
{{line}}
{{barcode orderNum}}
{{line}}
{{qrcode url}}
{{col1sw "Scan QR Code to order online"}}
{{line}}`;

exports.ticketUsingPrintCommands = (dotWidth, cb) => {
    var template = hb.compile(source);
    var ticket = template(data);
    var cpl = Math.floor(dotWidth / 12, 0);
    console.debug("Generated ticket", ticket);
    console.debug("CPL = ", cpl);
    const printer = {
        cpl: cpl,
        encoding: 'cp437',
        upsideDown: false,
        gamma: 1.8,
        command: 'starsbcs' // Make it dynamic for other printers.
    };
    var buffer = Buffer.from(receiptline.transform(ticket, printer), 'binary');
    if (buffer) {
        cb(buffer);
    }
    else {
        cb("Error: We got blank from receiptline.")
    }
}

The buffer - I can feed to the REST API GET request from Star or other cloud-enabled printer or just feed to the IP address:9100 for local or remote printing. This makes life much easier to maintain the code for multiple printers, local or cloud printing.

The only thing that I will request is to allow a color change of the ribbon and the double-width and double-height chars for impact printers. I will be more than happy to do tests for you and give any testing feedback.

I am looking forward for this.

@receiptline
Copy link
Owner

Thank you for waiting.
Version 1.3.0 is now available, please try it with TM-U220.
To print characters in red, please specify "invert".

{width:auto}
|^Pizza Station^|
{width:5 *}
|2 @ |Royal Special Pizza |
|   |`Olive`, `Spinach`, `Asparagus`, `Tomato`  | 
|1 @ |12 inch Subway with basil |
// TM-U220 Font A
const printer = {
    "host": "192.168.1.170",
    "port": 9100,
    "cpl": 33,
    "spacing": true,
    "command": "impact"
};
// TM-U220 Font B
const printer = {
    "host": "192.168.1.170",
    "port": 9100,
    "cpl": 40,
    "spacing": true,
    "command": "impactb"
};

@vikramsvang
Copy link

vikramsvang commented Mar 13, 2021

@receiptline - Thank you San!.

I tested and it worked like a charm. I sincerely appreciate it. Thank you again. Can I send you StarBucks coffee gift card to your email account for your prompt service as a token of appreciation? Please share your email address.

@receiptline
Copy link
Owner

Thank you as well.
I appreciate the thought and I wish you much success.

@per-samuelsson
Copy link

per-samuelsson commented May 25, 2022

@receiptline

I'm currently evaluating receiptline and really enjoy it so far.

My aim is to get it to work with Star webPRNT and Epson ePOS-Print XML. I've accomplished the first (webPRNT) and was very much helped by your answer here @receiptline :

#4 (comment)

Now I'm facing something similar when trying to send the receiptline-document as "raw" data using Epsons XML approach. My suspicion it's caused by poor encoding, or that I don't get if I have to trim something off (like initialization instructions in the linked answer), or both.

Basically, when I'm doing is (psudo-coded) something like:

const document = transform(content, {
  encoding: 'multilingual',
  ...
  command: commands.escpos
});
const bin = Buffer.from(document, 'binary');

const instructions = 
`<epos-print xmlns="http://www.epson-pos.com/schemas/2011/03/epos-print">
  <command>${bin}</command
</epos-print>`;

return `<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>
  ${encodeEscapeSequence(instructions)}
</s:Body></s:Envelope>`;

and POST it to a TM-i-series printer.

Any variation I have tried, I get a "SchemaError", e.g.

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Body>
  <response success="false" code="SchemaError" status="0" battery="0" xmlns="http://www.epson-pos.com/schemas/2011/03/epos-print"/>
</soapenv:Body></soapenv:Envelope>

Any ideas or inputs would be highly appreciated!

@receiptline
Copy link
Owner

Hello!

I have created the following code based on an example using Epson Server Direct Print.
node_modules/receiptline/example/cloud/en_epson_sdp.js

const http = require('http');
const receiptline = require('receiptline');

const printer = {
    cpl: 42,
    encoding: 'multilingual',
    spacing: true,
    command: 'escpos'
};

const order = `{width:*}
^^^Online Order
${new Date().toLocaleString('en')}
{width:4,*}
---
|^^^2|^^Hamburger
|    |Tomato, Onion, Meat sauce, Mayonnaise
|    |\`"~Mustard~
|^^^2|^^Clam chowder
|    |Oyster cracker
---
{code:1234567890; option:code128,2,72,hri}`;

let jobid = 1;

const command = receiptline.transform(order, printer);
// remove ESC @ (initialize printer) GS a 0 (disable automatic status back)
const hex = Buffer.from(command, 'binary').toString('hex', 5);
const xml = `<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Header>
        <parameter xmlns="http://www.epson-pos.com/schemas/2011/03/eposprint">
            <devid>local_printer</devid>
            <timeout>60000</timeout>
            <printjobid>${jobid++}</printjobid>
        </parameter>
    </s:Header>
    <s:Body>
        <epos-print xmlns="http://www.epson-pos.com/schemas/2011/03/epos-print">
            <command>${hex}</command>
        </epos-print>
    </s:Body>
</s:Envelope>`;

const options = {
    host: '192.168.192.168',
    port: 80,
    path: '/cgi-bin/epos/service.cgi',
    method: 'POST',
    headers: {
        'Content-Type': 'text/xml; charset=utf-8',
        'Content-Length': Buffer.byteLength(xml)
    }
};
const req = http.request(options, res => {
    console.log(`${res.statusCode} ${res.statusMessage}`);
    res.on('data', chunk => {
        console.log(chunk.toString());
    });
});
req.on('error', err => {
    console.log(err.message);
});
req.end(xml);

It worked on TM-T88VI.

Thank you!

@per-samuelsson
Copy link

By tweaking some parts with the help of what you wrote above, I finally got it to work now.
I can't thank you enough - you are a hero! 🥇 Thanks!

@JRightmer
Copy link

I have a similar question/issue to the one above. We are printing to the Epson TM printers from a web app using the Epson ePOS SDK for Javascript (version 2.27.0). How would I use receiptline with the SDK?

@receiptline
Copy link
Owner

Hello!

Please use Receipt.js instead of receiptline.
https://github.com/receiptline/receiptjs

<script type="text/javascript" src="receipt.js"></script>
<script type="text/javascript" src="receipt-printer.js"></script>
<script type="text/javascript" src="qrcode-generator/qrcode.js"></script>
const markdown = `^^^RECEIPT

03/18/2024, 12:34:56 PM
Asparagus | 1| 1.00
Broccoli  | 2| 2.00
Carrot    | 3| 3.00
---
^TOTAL | ^6.00`;

const receipt = Receipt.from(markdown, '-p epson -c 42');
const command = await receipt.toCommand();

Thanks!

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

No branches or pull requests

5 participants