0

I would like to print on a thermal printer from a random list of images but I don't know how.

Currently I have a touchscreen on which I just press a button and it prints an image already defined.

In what language should I write it? Can anyone help me? :D

Actually i have this code :

/**
 * Simple printer application example.
 *
 * This application creates areas that when clicked print a document.
 * During the printing, all the areas are disabled and are enabled
 * only a short time after the print is over (monkey countermeasure).
 * When the printer is offline, the area are also not enabled.
 *
 * The buttons definitions are defined in a JSON file containing an array of ButtonSpec.
 */
function SimplePrinterApp() {
    this.printer = null;
    this.jobid = -1;
    this.container = null;
    this.printAllow = false;
    this.afterPrintTimeout = 2000;
    this.isOpen = undefined;
    this.date = null;
    this.dayPrintCounter = 0;
    this.totalPrintCounter = 0;
    this.clickDisabled = false;
}

SimplePrinterApp.prototype = {
    /**
     * Initialize the application
     * @param {String} url is the URL containing the buttons data (a JSON file)
     */
    initialize: function(url) {
        this.container = document.getElementById("button-container");
        this.body = document.getElementById("body");

        this.mayBeEraseLastPrint();
        this.fetchConfiguration(url);
    },

    loadLastPrint: function() {
        var lastPrint = localStorage.getItem('lastPrint');
        var limit = this.config.printlimit;
        if (lastPrint) {
            var today = this.getTodayDate();
            var data = lastPrint.split(';');
            if (this.config.campaignid != data[3]) {
                this.date = today;
                this.dayPrintCounter = 0;
                this.totalPrintCounter = 0;
            } else {
                if (today != data[0]) {
                    this.date = today;
                    this.dayPrintCounter = 0;
                } else {
                    this.date = data[0];
                    this.dayPrintCounter = parseInt(data[1]);
                }
                this.totalPrintCounter = parseInt(data[2]);
            }
        } else {
            this.date = this.getTodayDate();
            this.dayPrintCounter = 0;
            this.totalPrintCounter = 0;
        }
        if ((this.totalPrintCounter >= limit.total && limit.total >= 0) || (this.dayPrintCounter >= limit.day && limit.day >= 0)) {
            this.onLimitReached();
        } else {
            this.onLimitReset();
        }
    },

    saveLastPrint: function() {
        localStorage.setItem('lastPrint', this.date+";"+this.dayPrintCounter+";"+this.totalPrintCounter+";"+this.config.campaignid);
    },

    mayBeEraseLastPrint: function() {
        var url = new URL(location.href);
        if (url.searchParams.get('reset') !== null) {
            localStorage.removeItem('lastPrint');
        }
    },

    getTodayDate: function() {
        return new Date().toISOString().substring(0, 10);
    },

    /**
     * @private
     * @param {String} url is the URL or name (absolute or relative) containing the button definitions
     */
    fetchConfiguration: function(url) {
        var data = null;

        var xhr = new XMLHttpRequest();

        var thiz = this;
        xhr.addEventListener("readystatechange", function () {
            if (this.readyState === 4 && this.status == 200) {
                try {
                    var config = JSON.parse(this.responseText);
                    thiz.setConfiguration(config);
                } catch(e) {
                    alert(e.message);
                }
            }
        });

        xhr.open("GET", url);
        xhr.setRequestHeader("Accept", "application/json");
        xhr.setRequestHeader("Cache-Control", "no-cache");
        xhr.send(data);
    },

    /**
     * @typedef {Object} ButtonSpec
     * @summary Contains the buttons parameters.
     * @property {String} url is the URL (relative or absolute) of the image to print.
     * @property {String} [class] is the CSS class of the button.
     * @property {Number} [left] is the left position of the button (can be omitted if given in the class).
     * @property {Number} [top] is the top position of the button (can be omitted if given in the class).
     * @property {Number} [width] is the button width (can be omitted if given in the class).
     * @property {Number} [height] is the button height (can be omitted if given in the class).
     */


    setConfiguration: function(config) {
        this.config = config;

        // Time between print
        if (typeof config.printtimeout == "number") {
            this.afterPrintTimeout = config.printtimeout;
        }

        // Set opening time...
        try {
            var opening = this.config.openingtime.split(":");
            this.openingminutes = parseInt(opening[0]) * 60 + parseInt(opening[1]);
        } catch(e) {
            this.openingminutes = 0;
        }

        // Set closing time
        try {
            var closing = this.config.closingtime.split(":");
            this.closingminutes = parseInt(closing[0]) * 60 + parseInt(closing[1]);
        } catch(e) {
            this.closingminutes = 24 * 60;
        }

        this.createButtons(config.buttons);
        this.loadLastPrint();

        // Initialize screen saver
        this.screensaver = new ScreenSaver(config.screensaver.timeout, this.onSetScreenSaver.bind(this));
        this.body.addEventListener("click", this.screensaver.onUserEvent.bind(this.screensaver));
        this.body.addEventListener("touchstart", this.screensaver.onUserEvent.bind(this.screensaver));
        this.container.addEventListener("transitionend", this.onScreenSaverOpacityTransition.bind(this));

        this.onMinuteTick();
    },

    /**
     * Callback invoked when the screensaver needs to be set visible or not.
     * @private
     * @param {boolean} display
     */
    onSetScreenSaver: function(display) {
        if (this.config.screensaver.enable) {
            if (display) {
                this.player.playFolder(this.config.screensaver.folder);
                this.container.classList.add("screensaver");
            } else {
                this.disableClickFor(250); // Avoid a click on a button that may have been touched to disable the screensaver
                this.container.classList.remove("screensaver", "no-touch");
                this.player.stop();
            }
        }
    },

    /**
     * Disable the click on buttons for a certain duration.
     * @param {number} timeout number of milliseconds to wait before enabling the click again
     */
    disableClickFor: function(timeout) {
        this.clickDisabled = true;
        window.setTimeout(function() {
            this.clickDisabled = false;
        }.bind(this), timeout)
    },

    /**
     * Callback called when the opacity transition on the button container is over.
     * It's time to disable the container so that the (hidden) buttons cannot be
     * activated.
     * @param {TransitionEvent} event
     */
    onScreenSaverOpacityTransition: function(event) {
        if (event.target == this.container) {
            if (this.screensaver.isDisplayed()) {
                this.container.classList.add("no-touch");
            }
        }
    },

    /**
     * @private
     * @param {ButtonSpec[]} config is an array of ButtonSpec
     */
    createButtons: function(buttons) {
        for (var i = 0 ; i < buttons.length ; i++) {
            var button = buttons[i];
            var div = document.createElement("div");

            if (button.class !== undefined) {
                div.className = button.class;
            }
            if (button.left !== undefined) {
                div.style.left = button.left + "px";
            }
            if (button.top !== undefined) {
                div.style.top = button.top + "px";
            }
            if (button.width !== undefined) {
                div.style.width = button.width + "px";
            }
            if (button.height !== undefined) {
                div.style.height = button.height + "px";
            }
            if (button.text !== undefined) {
                div.innerText = button.text;
            }

            div.addEventListener("click", this.getClickDelegate(button, div));
            div.addEventListener("animationend", this.getAnimationEndDelegate(div));
            this.container.appendChild(div);
        }
    },

    /**
     * Set the printer object
     * @param {Printer} printer
     */
    setControlInterface: function(iface) {
        this.printer = iface.printer;
        this.logger = iface.logger;
        this.player = iface.player;

        // Register for printer events
        this.printer.addListener(this.onPrinterEvent.bind(this));

        // Get the printer state to enable printing...
        this.printer.getState()
            .then(function(state) {
                this.setPrintAllowed(state.connected && state.online);
            }.bind(this))
            .catch(function() {});

        // Handle contact input in the browser side
        var config = {
            controller: "browser",
            lifetime: "url"
        };

        iface.contact.setInputConfiguration(config)
            .then(function(e) {
                console.log("Contact.setInputConfiguration(" + JSON.stringify(config) + ") => " + JSON.stringify(e));
            })
            .catch(function(e) {
                console.error("Error: Contact.setInputConfiguration(" + JSON.stringify(config) + ") => " + JSON.stringify(e));
            });

        // Get contact input events
        iface.contact.addListener(function(e) {
            this.onInputContactEvent(e);
        }.bind(this));
    },

    /**
     * @private
     * @param {Boolean} allow Is true to allow printing documents.
     * @param {Number} [timeout] The number of milliseconds before executing the action
     */
    setPrintAllowed: function(allow, timeout) {
        var callback = function() {
            if (allow) {
                this.container.classList.remove("disabled");
            } else {
                this.container.classList.add("disabled");
            }
            this.printAllow = allow;
        }.bind(this);

        window.setTimeout(callback, timeout ? timeout : 0);
    },

    /**
     * Convenience method: get an image URL from the button definition
     * @private
     * @param {String} url is an URL or a path relative to the current page.
     */
    getUrl: function(url) {
        if (url.startsWith("http://" || url.startsWith("https://"))) {
            return url;
        }

        return document.URL.substr(0, document.URL.lastIndexOf('/')) + "/" + url;
    },

    /**
     *
     * @param {HTMLElement} div
     * @returns a closure
     */
    getAnimationEndDelegate: function(div) {
        return function() {
            div.classList.remove("animate")
        };
    },

    /**
     * Convenience method: get a function to execute on a click on a button area.
     * @private
     * @param {Object} button is the button definition
     * @param {HTMLElement} div the div element for that button
     */
    getClickDelegate: function(button, div) {
        return function() {
            this.onButtonClick(button, div);
        }.bind(this);
    },

    /**
     * Callback invoked when the user clicks on an area
     * @private
     * @param {Object} button is the button definition
     * @param {HTMLElement} div the div element for that button
     */
    onButtonClick: function(button, div) {
        if (this.clickDisabled) {
            return;
        }

        var printed = this.onPrint(button.url, button.log);

        if (printed && !div.classList.contains('animate')) {
            div.classList.add('animate');
        }
    },

    onPrint: function(url, log) {
        var limit = this.config.printlimit;
        if (this.printAllow && this.printer) {
            if ((this.dayPrintCounter < limit.day || limit.day < 0) && (this.totalPrintCounter < limit.total || limit.total < 0)) {
                this.dayPrintCounter += 1;
                this.totalPrintCounter += 1;
                this.saveLastPrint();
                var url = this.getUrl(url);
                this.logButtonClick(log);
                this.setPrintAllowed(false);
                this.printer
                    .print({ url: url })
                    .then(this.onPrintStart.bind(this))
                    .catch(this.onPrintError.bind(this));

                if ((this.dayPrintCounter >= limit.day && limit.day >= 0) || (this.totalPrintCounter >= limit.total && limit.total >= 0)) {
                    this.onLimitReached();
                }

                return true;
            }
        }
        return false;
    },

    onInputContactEvent: function(e) {
        var name = e.detail.name;
        var contact = this.config.contacts[name];
        if (contact) {
            for (var i = 0 ; i < e.detail.values.length ; i++) {
                var current = contact[i];
                var value = e.detail.values[i];
                if (current) {
                    if (!!current.state !== value && value == true) {
                        if (this.isOpen) {
                            this.onPrint(current.url, current.log);
                        }
                    }
                    current.state = value;
                }
            }
        }
    },

    logButtonClick: function(log) {
        this.log(log ? log : "button clicked");
    },

    /**
     * Generic function to log a message
     */
    log: function(message) {
        if (this.logger != null) {
            this.logger.log(this.config.logfile, this.getTimestamp() + " " + message);
        }
    },

    getTimestamp: function() {
        var now = new Date();
        var twoDigit = function(n) {
            return ("0" + n).slice(-2)
        }

        return now.getFullYear() + "-" + twoDigit(now.getMonth() + 1) + "-" + twoDigit(now.getDate()) + " " +
            twoDigit(now.getHours()) + ":" + twoDigit(now.getMinutes()) + ":" + twoDigit(now.getSeconds());
    },

    /**
     * Callback invoked on any print error.
     * @private
     */
    onPrintError: function() {
        // You can display error message here...
        this.log("Print failure");
        this.setPrintAllowed(true, this.afterPrintTimeout);
    },

    /**
     * Callback invoked when a printing has been done.
     * Does not mean the printing is done, can be done, will be done.
     * Does only gives the job identifier...
     * @param {Number} id is the job identifier
     */
    onPrintStart: function(id) {
        // Print has started, watch printer events...
        this.jobid = id;
    },

    /**
     * Callback invoked when a printer related event is received.
     * @param {PrinterStateEvent|JobStateEvent} event is the event
     */
    onPrinterEvent: function(event) {
        var detail = event.detail;
        switch (detail.type) {
            case 'job-state':
                this.onJobStateEvent(detail.job);
                break;

            case 'printer-state':
                this.onPrinterStateEvent(detail.state);
                break;

            default:
                break;
        }
    },

    /**
     * Callback invoked when a print job event is received.
     * @param {JobState} job is the job that has changed.
     */
    onJobStateEvent: function(job) {
        // Only watch the last printed job (our job)...
        if (job.id == this.jobid) {
            switch(job.state) {
                case 'failed':
                    // Sorry for the convenience...
                    this.onPrintError();
                    break;

                case 'printed':
                    // The document has been successfully printer, say thank you...
                    this.setPrintAllowed(true, this.afterPrintTimeout);
                    break;

                case 'finished':
                    // Can print a new one... or wait a few seconds...
                    break;
            }
        }
    },

    /**
     * Callback invoked when a printer event is received.
     * It tells whether the printer is online, the paper is available...
     * @param {PrinterState} state is the printer state
     */
    onPrinterStateEvent: function(state) {
        if (state.online) {
            // Documents can be printed...
            this.setPrintAllowed(true);
        } else {
            // Display other contents or at least don't allow printing
            this.setPrintAllowed(false);
        }
    },

    /**
     * This method is called every minute and is responsible for calling
     * the onOpeningTime() / onClosingTime() methods.
     */
    onMinuteTick: function() {
        var open = this.isOpenNow();
        if (this.isOpen !== open) {
            this.isOpen = open;
            if (open) {
                this.onOpeningTime();
                var today = this.getTodayDate();
                if (today != this.date) {
                    this.dayPrintCounter = 0;
                    this.date = today;

                    var limitTotal = this.config.printlimit.total;
                    if (this.totalPrintCounter < limitTotal || limitTotal < 0) {
                        this.onLimitReset();
                    }
                }
            } else {
                this.onClosingTime();
            }
        }
        this.setTimeoutTick();
    },

    /**
     * Set a timeout to execute the the opening / close verification every minute.
     * Quite expansive but more reliable when setting the time.
     */
    setTimeoutTick: function() {
        var now = new Date();
        var nextms = 1000 * (61 - (now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds()) % 60);
        window.setTimeout(this.onMinuteTick.bind(this), nextms);
    },

    /**
     * Check whether the app in the opening/closing intervale.
     * @return {Boolean} true if the app is open
     */
    isOpenNow: function() {
        var now = new Date();

        // Number of minutes since midnight
        var nowminutes = now.getHours() * 60 + now.getMinutes();

        if ((nowminutes < this.openingminutes) || (nowminutes >= this.closingminutes))
            return false;

        return true;
    },

    onOpeningTime: function() {
        // Do whatever you want to do on opening time (show buttons...)
        this.container.classList.remove("store-closed");
        this.screensaver.setEnabled(true);
    },

    onClosingTime: function() {
        // Do whatever you want to do on closing time (hide buttons, display something else...)
        this.container.classList.add("store-closed");
        this.screensaver.setEnabled(false);
    },

    onLimitReached: function() {
        // Do whatever you want when the print limit has been reached (hide buttons, display something else...)
        this.container.classList.add('limit-reached');
    },

    onLimitReset: function() {
        // Do whatever you want when the print limit has been reset (show the buttons...) and
        // is is now possible to print.
        this.container.classList.remove('limit-reached');
    }
};


/**
 * Screen saver manager
 * @param {Number} duration is the duration to wait before starting the screensaver
 * @param {function(boolean)} onDisplay is the callback invoked when the screensaver
 *                                      has to be displayed or hidden.
 */
function ScreenSaver(duration, onDisplay) {
    this.enabled = false;
    this.timeout = null;
    this.duration = duration * 1000;
    this.displayed = undefined; // Boolean, undefined to trigger a first onDisplay() when calling setEnabled() for the first time
    this.onDisplay = onDisplay;
    this._onTimeoutBind = this._onTimeout.bind(this);
}

ScreenSaver.prototype = {
    setEnabled: function(enabled) {
        if (this.enabled !== !!enabled) {
            this.enabled = !!enabled;
        }

        if (this.enabled) {
            this._startTimeout();
        } else {
            this._clearTimeout();
        }

        this._setDisplay(this.enabled && this.displayed);
    },
    onUserEvent: function() {
        this._setDisplay(false);
        this._startTimeout();
    },
    isDisplayed: function() {
        return this.displayed && this.enabled;
    },
    _setDisplay: function(displayed) {
        var value = !!displayed;
        if (this.displayed !== value) {
            this.displayed = value;
            this.onDisplay(value);
        }
    },
    _startTimeout: function() {
        this._clearTimeout(); // Paranoid
        if (this.enabled && this.duration >= 1000) {
            this.timeout = window.setTimeout(this._onTimeoutBind, this.duration);
        }
    },
    _onTimeout: function() {
        this.displayed = true;
        this.onDisplay(true);
    },
    _clearTimeout: function() {
        if (this.timeout != null) {
            window.clearTimeout(this.timeout);
            this.timeout = null;
        }
    }
}

But I don't know what I need to change or what can I simplify

KenS
  • 30,202
  • 3
  • 34
  • 51

0 Answers0