Sep 242015
 

We apologize for the length of the delay in this release but we ran into a number of unforeseen issues trying to fix a couple of the bigger issues that were brought to our attention. The two issues that we are addressing in this release are problems with HTTPS and Network Printer discovery.

Requirements

Network Printers

This was a big issue. A service running on windows will not see Network/shared printers for a user and there are Microsoft bugs that prevent seeing these printers when the service is run as the current user. With that in mind, we had to re-implement the way the service is run. It is now a service that starts when the user logs in. This Service will appear in a user’s application tray and can be configured from there. This will allow the SDK to see any Network/Shared Printers that the user has installed.

HTTPS

Our JavaScript library now connects to the web service via HTTPS. We have to install a self-signed certificate to allow this to happen. After installation finishes, we will ask the user if they want to open up the default browser to accept the certificate. Once the certificate is accepted then all HTTPS traffic should work as normal. You can install the certificate on other browsers by going to “https://localhost:/DYMO/DLS/Printing/Check”. This is important for the Firefox browser.

New Tray application

The new tray tool has several options:

  • Start/Stop the Service
  • Configure: Which will let you specifically configure a port within the approved range.
  • Diagnose: This will confirm that the service is running and identify the port.

Notes

  • The KB2954953 patch no longers needs to be applied for the service to work
  • We’ve seen some issues where the service needs to run with elevated permissions to see the printers. We are working on this issue.
  • We’ve seen some crashes when the service starts or at the end of the install. We are investigating but you should be able to start the service from the C:\Program Files (x86)\DYMO\DLS Web Service\DYMO.DLS.Printing.Host.exe
  • The Mac version is being tested right now and we hope to have a version released soon.

We appreciate your patience! We are working on this as hard as we can and understand your frustrations with delays. We want to insure that we release a quality product that can meet your demands. Your feedback is valuable to us!

Aug 202015
 

We are proud to announce the Open Beta of the new DYMO Label Framework JavaScript Library 2.0. This solution uses a new communication mechanism that will work on all browsers. This means that we have removed the dependency of browser specific plugins like NPAPI and Active-X. The current DYMO Label Framework API is fully supported.

Requirements

The new init method

Since the new communication protocol needs to discover the port that the DYMO service is listening on, a new method was introduced, dymo.label.framework.init(callback). This method performs a scan of a range of ports to look for the DYMO service. This method needs to finish before any other DYMO Framework API calls are made. To accomplish this, the method takes a callback method that will be called as soon as the dymo.label.framework.init method finishes.

Backward Compatibility Mode

If the dymo.label.framework.init method is not called (i.e. no user code has been changed) then the Framework goes into backward compatibility mode. It will try to scan the first port in the defined range and if it finds the service, the new communication protocol will be used. If it fails, it will fall back to the previous implementation that uses native plugins (NPAPI/Active-X).

Init Example

Typical startup code where a method is subscribed to an onload event:

function startupCode() {
    /* access DYMO Label Framework Library */
}
window.onload = startupCode;

The code should be changed to call a shim that will initialize the DYMO Framework before calling the rest of the startup code.

function startupCode() {
    /* access DYMO Label Framework Library */
}
function frameworkInitShim() {
    dymo.label.framework.init(startupCode); //init, then invoke a callback
}
window.onload = frameworkInitShim;

Tracing

We have added a new tracing feature. This can be used to help debug issues with the new service. The property is called dymo.label.framework.trace. When set to true, it will put tracing messages in the browser developer console. You should set the trace property before calling the dymo.label.framework.init method:

Tracing Example

dymo.label.framework.trace = 1; //true
dymo.label.framework.init(startupCode);

Known Limitations

  • The port number that the service binds to is not user definable. We tried to use a range of ports that are not typically used but there could be port conflicts.
  • HTTPS: Because the service currently uses insecure (http) requests, some browsers may have some issues. For example, Firefox will block insecure XHR requests when a page is loaded through HTTPS. So in order to enable it, the user has to click icon in the address bar and enable insecure requests within a secure connection.
  • As stated in the requirements, a Microsoft patch is typically required on Windows 8 and greater.
  • MacOSX support is not included in this Beta, we are working on it now and we will post more news about this version soon.
  • As with DLS, we do not support Windows Server configurations.

To Do

  • A new version of the DYMO Label Software 8.5.2 will become available. This version will include the new Framework implementation and will be considered the Release version of the new Framework. This is expected to be released near the end of September.
  • Service Configuration (Port Number): Currently, the service will bind to the first available port within a given range. We will add a parameter that will allow a user to assign a particular port within the given range.
  • Investigate the best way to work over HTTPS without issues
  • We are implementing asynchronous versions of existing API methods
  • Take constructive feedback and bug reports from our Beta Customers. We will be releasing new versions of the Beta as we respond to issues found in the field.

Update 08/28/2015

If the Web Service fails to install, try and turn off your Anti-virus software before installing.

 

Update 09/09/2015

We apologize for the delay of the next version of the Beta.  We’ve have been working on full HTTPS support, for which testing is ongoing, we found some issues and want to fix them before releasing.  We’ve also reproduced the service not recognizing networked printers.  We are investigating the possible solutions since it appears to be a limitation of what the service has access to.  We’ll keep you informed of any developments.

Dec 042014
 

A some may be already aware of, Google has announced that it will phase out support for NPAPI in Chrome with the ultimate removal in Sept 2015.   We use NPAPI in the framework for plugin support for most browsers except for IE which we use an ActiveX plugin.  We are investigating alternatives to NPAPI but we have not come up with a solution yet.

In the mean time before Sept 2015, you will have to possibly change several settings in your Chrome browser in order to use the DYMO Label Framework:

  1. Click on the Chrome Customize button in the upper right of your browser
  2. Find the Settings option
  3. Click on the “Show Advanced Options” link
  4. Click on the “Content Settings” button or browse to chrome://settings/content
  5. Verify that “Allow all sites to run JavaScript” is enabled
  6. Verify that under Plug-ins that “Run Automatically” is selected
    1. You can check in the “Plug-in exceptions” to see if the site you are running the Framework from is set to “Allow” instead of “Block”
  7. Verify that “Unsandboxed plug-in access” is not set to “Do not allow any sites….”

ChromeSettings

 

This should get things working.

Another work around, though less than ideal is to use the extension IE Tab.  This will put a web page into an IE browser tab within Chrome and will use the ActiveX plugin instead of the NPAPI version.  It has options to add a wild card URL that will automatically use the IE Tab when it matches.  We recognize that this is not a valid long term solution.

Update (4/15/2015)
With version 42 of Chrome, Google now disables NPAPI which is required to run our plugin. However, you can manually enable it by typing the following into the Chrome address bar and adjusting the setting:
chrome://flags/#enable-npapi

Update (4/20/2015)
It has been our experience that just enabling NPAPI may not be enough. In some instances, close the chrome browser then uninstall and re-install the DLS software for the plugin to appear in the plugin list: chrome://plugins/
Go here to see if your browser has the framework installed correctly: Check Environement

Update (5/8/2015)
In some situations, a full shutdown and reboot is required after all the steps have been performed for the plugin to appear in Chrome.

Apr 052011
 

We are proud to announce a BETA release of the DYMO Label Web SDK. The DYMO Label Web SDK contains enhancements to DYMO Label software and the DYMO Label Framework JavaScript library to allow printing from a web browser running on any modern computing platform. This includes any desktop browser running on Windows, Mac, or Linux; mobile browsers on iPhone, iPod, iPad, Android, BlackBerry and even on Kindle :)

Please Note

This release is a BETA. It has not been extensively tested outside DYMO and should be used for developer testing only, NOT for production software releases.

Architecture Overview

Before going into details let first look at the high level architecture for the Web SDK to understand how it works.

For the current SDK the architecture looks like this:

SDK1

So, only PC and Mac are supported. DYMO Label software has to be installed on each machine that uses the JavaScript library.

The architecture for the Web SDK is like this:

SDK2

The major piece here is a computer that has a DYMO printer plugged-in and DYMO Label software installed. DYMO Label software contains a service called DYMO Label Proxy that allows communication between a web browser and the printer. Nothing should be installed on any other computing device that accesses the printer. That is the biggest difference from the current SDK where DYMO Label software must be installed on any client computer that needs label printing functionality.

Notes:

  • right now DYMO Label Proxy is available for Windows only. So, a printer should be connected to a Windows machine, or be available from Windows machine has DYMO Label Proxy installed (see below).
  • the printer itself does not have to be connected directly to the computer running DYMO Label Proxy. The printer has to be ACCESSIBLE from that machine. This means that the printer might be really connected to a different machine and be a "shared" network printer. Or the printer might be connected to a DYMO Print Server and be "installed" on the computer using RAW TCP/IP port. This might be used to simplify installation (see below).
  • a local network is used for communications between the proxy service and a mobile device. That means that on the mobile device the Wi-Fi should be turned on and the device should be connected to the network. Using just 3G is not enough.
  • browsers that use an internal “proxy” servers to fetch internet content, e.g. Opera Mini, are not supported. The reason? It is impossible to contact “local network” servers like DYMO Label Proxy from such browsers.

Usage Scenarios

The first scenario is printing from a computing device that DYMO Label cannot be installed on. This includes a mobile device like an iPad or Android as well as a desktop PC running e.g. Linux.

The second scenario is to simplify installation on Windows. Before now DYMO Label has to be installed on a PC you wanted to print from. Not any more :) Let say you want to be able to print to a “shared” printer connected to a different machine. E.g. the printer can be connected to a “server” or someone’s workstation in a common location, like a "front desk". In this case DYMO Label has to be installed only on the "server" and nothing should be installed on your local PC, even drivers.

Installation

The installation is fairly simple. Just install DYMO Label software, that’s it. DYMO Label Proxy service will be installed and run as a part of the installation. DYMO Label Proxy is a service running on port 8631 by default. Because of that there are couple of considerations  that must be taken into account:

  • DYMO Label Software will configure Windows Firewall to open the port 8631 for inbound requests. If a different firewall monitor is installed, you have to configure it manually.
  • The port number may be changed from the default one. Either use the configuration utility included or specify the port manually in the service’s application .config file. In any case don’t forget to update the firewall rules to open the port. Otherwise clients will not be able to connect to the service.
  • The final release to the public will add the ability to select the port number and autostart options for the service during installation.

Configuration

To configure the service use DYMOLabelProxyServiceConfiguration.exe utility. It lets you change the port number the service is listening to as well as stop/start/restart the service. In addition the utility displays a list of urls that might be used to connect to the service.

image

Changing Port Number

To change the port number enter a new value into “Port Number” field and click “Apply” button. Right now the service is not restarted automatically, so don’t forget to restart it.

Service Control

You can start/stop/restart the service from within the configuration utility. Alternatively standard “Services” panel of the  “Computer Management" tool can be used.

Service URIs

To be able to access the service, the service’s URI should be known on the mobile device. From a programmatic side it is done by calling a framework function addPrinterUri(); we will look at addPrinterUri() later when analyzing a sample application. But what URI to use? Well, it depends on various of factors, like network configuration, a mobile device type, etc.

Bonjour Name

If your device supports Bonjour protocol, i.e. it is a iPhone or iPad, then most likely you can use the first URI in the list, that has a form http://.local:. Here “.local” represents a default Bonjour domain. Even if Bonjour is supported on the mobile device there might be problem if your mobile device and the service are connected to different subnets, e.g. the mobile device is connected to a “wireless” subnet and the service to a “wired” subnet. It is not a problem with the DYMO service, it is how Bonjour works in default configuration. There are solutions for this problem, but the details descriptions are beyond the scope of this post. Some ideas:

DNS Name

If you have a DNS server running on your network, e.g. the PC running the service is in a domain, then most likely you can use a second URI contains just a PC name, like http://.

IP Address

As the last resort you can specify an ip-address. The configuration utility tries to apply some intelligence here and filters ip-addresses. It will show ip-address for active network interfaces only. It will filter VMWare interfaces as well. So, the URI in this case is like http://:

Changes in DYMO Label Framework JavaScript Library

The goal was to modify API in a such way, so existing web applications that use the library should not require major changes. In many cases the only change that needs to be done to an application is to call dymo.label.framework.addPrinterUri() function to “register” the proxy service’s URI, so the framework knows how to communicate with the printer. Below is a short list of changes.

Checking for the Framework Installation

Previously dymo.label.framework.checkEnvironment() function could be used for checking if the library can be used or not. E.g. it checked if the browser is one of the  supported browsers, is DYMO Label software installed or not, etc. Now, because any browser can be used and DYMO Label software is not required to be installed this function is kind of obsolete. It is still a part of the library but it will return a correct result only if running on Windows or Mac assuming you are going to use a Framework dependent parts of the library (see below).

Framework Dependent Functions

Some functions depend on a browser plugin and DYMO Label software being installed to operate. These functions access a local file system to load a label or an image file. If these functions are called when running on a mobile device  an exception will be thrown. It is a good idea to call checkEnviromnent() first to make sure the functions can be called. The functions are dymo.label.framework.openLabelFile() and dymo.label.framework.loadImageAsPngBase64().

Printer Registration

To be able to communicate with the printer , the library needs to know the DYMO Label Proxy service URI. To “register” a printer with the library, use dymo.label.framework.addPrinterUri() function. To “deregester” a printer use dymo.label.framework.removePrinterUri() function. Multiple printers/proxy services can be registered. To register a different service just call addPrinterUri() with a different URI. To deregister all printers use dymo.label.framework.removeAllPrinterUri() function.

Printing Status Checking

When printing from a desktop it might be not necessary to know the print status because the status can be easily accessed from the spooler UI. When printing from a mobile device it is not so easy. So, we added some functions for monitoring print job progress. dymo.label.framework.printLabel2() and label.print2() can be used to create a print job. An object representing a print job is returned by these calls and can be used to monitor  the job progress by calling printJob.getStatus(). There is an utility function to combine both operations into one. It is dymo.label.framework.printLabelAndPollStatus() (and the correspondent label.printAndPollStatus()). Status checking function can be called for any printer, even if the application is running on Windows and  printer is locally connected printer. Status checking for a printer locally connected to a Mac is not implemented in this BETA release.

Sample

Now the fun part Smile Let see how it works in practice. The sample is http://labelwriter.com/software/dls/sdk/samples/js/PrintMeThatLabel/pl.html. When manually typing the url, make sure to use it as-is, because it is case-sensitive. The JavaScript code for the sample is http://labelwriter.com/software/dls/sdk/samples/js/PrintMeThatLabel/PrintMeThatLabel.js

Adding Printer URI

The first thing that has to be done is to add/register printer URI. Type one of the URIs displayed in the configuration utility into the “Printer location(url) field. Make sure to use the full URI, including the protocol scheme. Then click the “Add” button. A list of printers installed on the proxy’s machine should be displayed in the printers drop down list. The click event handler for the “Add” button is as following:

addPrinterUriButton.onclick = function()
{
    try
    {
        var printerUri = printerUriTextBox.value;
        if (!printerUri)
            throw new Error("Specify printer Url");

        dymo.label.framework.addPrinterUri(printerUri, '',
            function()
            {
                settings.printerUris.push(printerUri);
                saveSettings();
                updatePrinters();
            },
            function()
            {
                alert('Unable to connect to "' + printerUri + '"');
            }
        );

    }
    catch(e)
    {
        alert(e.message || e);
    }
}


It checks that something is entered into printer URI field and then calls dymo.label.framework.addPrinterUri() function that does the main job. The signature for addPrinterUri() is

/**
    @param {string} printerUri
    @param {string=} opt_location
    @param {Function=} opt_successCallback
    @param {Function=} opt_errorCallback

    @return {undefined}
*/
dymo.label.framework.addPrinterUri = function(printerUri, opt_location, opt_successCallback, opt_errorCallback)

addPrinterUri() accepts four parameters:

  • printerUri – string URI of the proxy service. It is a required parameter
  • opt_location – optional string representing the printer location. A value passed in this parameter will be used as a part of printer name. If nothing is passed, then the host part of the URI will be used as the location.
  • opt_successCallback – optional callback function called when the service has returned information about available printers. One parameter is passed to the callback – the printerUri of the service.
  • opt_errorCallback – optional callback called when the service cannot be contacted, e.g. the service is down, or the printerURI is invalid. One parameter is passed to the callback – the printerUri of the service.

When addPrinterUri() is called it remembers the printerUri in an internal structures and tries to make a request to the proxy service to obtain information about available printers – model name, printer name, printer type, etc. This request is made asynchronously, so addPrinterUri() returns almost immediately, and callbacks, if provided, are called later when the request is completed. You know, the usual Ajax stuff Smile

For the sample, the printerUri parameter is taken from the printer url field. The opt_location is an empty string, so the host part of the uri will be used as a location. opt_errorCallback displays a simple message box telling the user about connection problem. opt_successCallback remembers the uri, so next time you opens the sample there is no need to reenter the uri (see below for more discussion). After that a printers drop down list is updated by updatePrinters().

function updatePrinters()
{
    // clear first
    removeChildren(printersComboBox);

    printers = dymo.label.framework.getPrinters();

    for (var i = 0; i < printers.length; i++)
    {
        var printerName = printers[i].name;

        // add combo box item
        var option = document.createElement('option');
        option.value = printerName;
        option.appendChild(document.createTextNode(printerName));
        printersComboBox.appendChild(option);

        // select current printer
        if (printerName == settings.currentPrinterName)
            printersComboBox.selectedIndex = i;
    }

    // hide 'add printer url' controls if there are printers to use
    printerSettingsDiv.style.display= printers.length == 0 ? 'block' : 'none';
};


The main item here is a call to dymo.label.framework.getPrinters(). getPrinters() is a synchronous call that uses “cached” printer information returned by the proxy service. Because it is called from a opt_successCallback we are sure that information is really available. It would be a mistake to call  dymo.label.framework.getPrinters() right after dymo.label.framework.addPrinterUri() like this

// Don't do that, will not work
dymo.label.framework.addPrinterUri('http://myservice:8631');
var printers = dymo.label.framework.getPrinters();
// process printers...

In this case the ‘printers’ variable most likely will contain an empty array (or array with local printers only).

Multiple proxy services can be added/registers at the same time. Just type a new service uri and click “Add” button.

To remove/unregester all proxy services, click on “Clear” button

clearPrinterUriButton.onclick = function()
{
    dymo.label.framework.removeAllPrinterUri();
    settings.printerUris = [];
    saveSettings();
    updatePrinters();
}

dymo.label.framework.removeAllPrinterUri() does the main job and the rest updates the UI.

If you want to unregister one proxy service you can call dymo.label.framework.removePrinterUri() with the service uri as a parameter.

/**
    @param {string} printerUri 
    @return {undefined}
*/
dymo.label.framework.removePrinterUri = function(printerUri)

Settings

An utility Settings class is used to store proxy Uris and the last used printer between sessions. In this sample, cookies are used as a persistent storage. In a real application other more reliable mechanisms should be used instead.

// app settings stored between sessions
var Settings = function()
{
    this.currentPrinterName = "";
    this.printerUris = [];
}

// loads settings
Settings.prototype.load = function()
{
    var currentPrinterName = Cookie.get('currentPrinterName');
    var printerUris = Cookie.get('printerUris');

    if (currentPrinterName)
        this.currentPrinterName = currentPrinterName;

    if (printerUris)
        this.printerUris = printerUris.split('|');
}

Settings.prototype.save = function()
{
    Cookie.set('currentPrinterName', this.currentPrinterName, 24*365);
    Cookie.set('printerUris', this.printerUris.join('|'), 24*365);
}

Loading Labels

DYMO Web SDK supports printing on both die-cut labels and tape labels. Label files for these two labels type are similar but different. So, the sample needs two label files for printing depending upon what printer is selected.

// load labels from the server
function loadLabels()
{
    $.get("Address.label", function(labelXml)
    {
        addressLabel = dymo.label.framework.openLabelXml(labelXml);
    }, "text");

    $.get("12mm.label", function(labelXml)
    {
        tapeLabel = dymo.label.framework.openLabelXml(labelXml);
    }, "text");
}

Labels are fetched from files “Address.label” and “12mm.label”. This  files are located on the server in the same folder as other files for the sample. The files are loaded using jQuery’s get() method. After label files are loaded, they are “opened” by using  dymo.label.framework.openLabelXml() function; the resulting “label” objects are stored in addressLabel variable and tapeLabel variable.

Printing and Status Checking

Type some data into the label text box and click “Print” button. The following is the function that does printing. It is a little bit long, but we will look at it part by part.

printButton.onclick = function()
{
    try
    {
        printButton.disabled = true;

        settings.currentPrinterName = printersComboBox.value;

        var text = document.getElementById('labelTextArea').value;

        var printer = printers[settings.currentPrinterName];
        if (!printer)
            throw new Error("Select printer");

        // determine what label to print based on printer type
        var label = null;
        var objName = "";
        if (printer.printerType == "LabelWriterPrinter")
        {
            label = addressLabel;
            objName = "Address";
        }
        else
        {
            label = tapeLabel;
            objName = "Text";
        }

        if (!label)
            throw new Error("Label is not loaded. Wait until is loaded or reload the page");

        // set data
        var labelSet = new dymo.label.framework.LabelSetBuilder();
        labelSet.addRecord().setText(objName, text);

        // print and get status
        var printJob = label.printAndPollStatus(printer.name, null, labelSet.toString(), function(printJob,
        printJobStatus)
        {
            // output status
            var statusStr = 'Job Status: ' + printJobStatus.statusMessage;

            var result = (printJobStatus.status != dymo.label.framework.PrintJobStatus.ProcessingError
                && printJobStatus.status != dymo.label.framework.PrintJobStatus.Finished);

            // reenable when the job is done (either success or fail)
            printButton.disabled = result;

            setTextContent(jobStatusMessageSpan, statusStr);

            return result;

        }, 1000);

        saveSettings();
    }
    catch(e)
    {
        printButton.disabled = false;
        alert(e.message || e);
    }
}

First, the Print button is disabled during printing, just to make things easier. The printer name and text to print is taken from UI elements and saved in printer and text variables correspondingly. If no printer is selected, e.g. no proxy uri has been added, a error saying “Select printer” is displayed.

Depending on the selected printer type a label to print is selected. Also a label object to put data into is selected.

// determine what label to print based on printer type
var label = null;
var objName = "";
if (printer.printerType == "LabelWriterPrinter")
{
    label = addressLabel;
    objName = "Address";
}
else
{
    label = tapeLabel;
    objName = "Text";
}


Then data to be printed is put into a label set object. Usually label sets are used to print multiple labels, but it is perfectly fine to use them to print a single label as well.  In this case the same objects are used regardless of number of labels printed, so the code is easier to adjust.

// set data
var labelSet = new dymo.label.framework.LabelSetBuilder();
labelSet.addRecord().setText(objName, text);


And finally the actual printing

// print and get status
var printJob = label.printAndPollStatus(printer.name, null, labelSet.toString(), function(printJob, printJobStatus)
{
    // output status
    var statusStr = 'Job Status: ' + printJobStatus.statusMessage;

    var result = (printJobStatus.status != dymo.label.framework.PrintJobStatus.ProcessingError
        && printJobStatus.status != dymo.label.framework.PrintJobStatus.Finished);

    // reenable when the job is done (either success or fail)
    printButton.disabled = result;

    setTextContent(jobStatusMessageSpan, statusStr);

    return result;

}, 1000);


The  job is done by the printAndPollStatus() method. This method sends a label and label data to be printed to the selected proxy service, and then periodically checks for the print job status. When a status is available a callback is called with new status information, so the application code can update the UI. printAndPollstatus() has following signature:

/** Prints a label and runs status checking in a loop

    @param {string} printerName
    @param {string} printParamsXml
    @param {string} labelSetXml 
    @param {function(PrintJob, PrintJobStatusInfo): boolean} statusCallback
    @param {number} pollInterval 
    @return {PrintJob}
*/
printAndPollStatus = function(printerName, printParamsXml, labelSetXml, statusCallback, pollInterval)

‘printerName’ is the name of the printer to print on. Usually obtained by calling dymo.label.framework.getPrinters(). printParamsXml – printing parameters, in our case we pass null, so default parameters are used. ‘labelSetXml’ – label data to print. ‘pollInterval’ – interval in milliseconds to ask the proxy service for the job status information. In our case we use an one second interval. ‘statusCallback’ – status callback function called when the service returned status information. the callback takes two parameters; the first is PrintJob object. It can be used to get the status information on demand, without using polling. Also, it might be extended in the future to provide more control over the print job, e.g. an ability to cancel it. The second parameter to the callback is PrintJobStatusInfo class. PrintJobStatusInfo has two properties: statusMessage that contains a status message string; and status that contains status code. The callback has to return a boolean value. If true is returned, than pritnAndPollStatus() will poll the serivce again after the pollInterval passed. If false is returned, then printAndPollStatus() finishes processing. printandPollstatus() returns PrintJob object for the created print job. It has a little of use right now, but may be useful in the future if PrintJob is extended to support more operations.

So, in our sample the callback does the following:

  • a string representing the printing status is assembled by using ‘Job status:’ prefix followed by real status string obtained from statusMessage property.
  • determine if we should continue polling the service for the status or not. We stop polling the service when the status is either dymo.label.framework.PrintJobStatus.ProcessingError, that means something really bad happened and print job has not been even created (e.g. the label has an invalid format, or Windows spooler is not running , or DYMO Label is not installed correctly); or the status is dymo.label.framework.PrintJobStatus.Finished, that means the print job is completed.
  • If no more polling is going to be done, then the “Print” button is enabled again, so another label can be printed
  • and finally the UI is updated to display the status string

The library provides different functions to do printing and status checking. For printing use print2() method of Label object. It has the same parameters as the print() method and returns a PrintJob object. The PrintJob object then can be used to get the status by calling getStatus() method, which has following signature:

/** 
    Gets a status of the print job
    @param {function(PrintJobStatusInfo)} replyCallback a function called when the status is available
    @return {undefined}
*/
getStatus = function(replyCallback)

In addition to Label class methods, similar functions are defined in dymo.label.framework namespace.

One similar to label.print2()

/** Prints a label and returns a print job object

    @param {string} printerName the printer to print on. A list of printers can be obtained by getPrinters()
    @param {string} printParamsXml printing parameters, like number of copies, print quality, etc. See PrintParams.xsd
    @param {string} labelXml label to print
    @param {string} labelSetXml 
        LabelSet to print. LabelSet is used to print multiple labels with same layout but different data, e.g. multiple 
        addresses. 
        Use LabelSetBuilder to create a LabelSet or construct xml manualy according to LabelSet.xsd
    @return {dymo.label.framework.PrintJob} print job
*/
dymo.label.framework.printLabel2 = function(printerName, printParamsXml, labelXml, labelSetXml)


Another, similar to label.printAndPollStatus()

/** Prints a label and runs status checking in a loop

    @param {string} printerName the printer to print on. A list of printers can be obtained by getPrinters()
    @param {string} printParamsXml printing parameters, like number of copies, print quality, etc. See PrintParams.xsd
    @param {string} labelXml label to print
    @param {string} labelSetXml 
        LabelSet to print. LabelSet is used to print multiple labels with same layout but different data, e.g. multiple 
        addresses. 
        Use LabelSetBuilder to create a LabelSet or construct xml manualy according to LabelSet.xsd
    @param {function(PrintJob, PrintJobStatusInfo): boolean} statusCallback
        Function to be called when a print job status is available.
        To continue polling the status the function should return true, false otherwise 
    @param {number} pollInterval poll interval in milliseconds
    @return {dymo.label.framework.PrintJob}
*/
dymo.label.framework.printLabelAndPollStatus = function(printerName, printParamsXml, labelXml, labelSetXml,
statusCallback, pollInterval)

Conclusion

We are really exited about the Web SDK and hope you can find it useful. Any thoughts, feedback, critics are welcome Smile

Jun 172010
 

In this post we will look at different ways for printing multiple labels from a web application.

Option #1

The first option is just to call the print() method several times. Each call to print() will produce a single label. Between calls you will call other library functions like setObjectText() to update the label’s content. The pseudo-code is like this:

var label = dymo.label.framework.openLabelXml("<?xml>...");

// label #1
label.setObjectText("Text", "Some text");
label.print("DYMO LabelWriter 450 Turbo");

// label #2
label.setObjectText("Text", "Some other text");
label.print("DYMO LabelWriter 450 Turbo");

// label #3
// ...

This option is straightforward but unfortunately is not very efficient. Each call to print() function creates a separate print job. This might be a problem if hundreds of labels are being printed. But more important it might be not fast enough. The reason is that due to the printer hardware design some LabelWriter models might need to reverse feed a label at the beginning of each print job. Thus printing each label in a separate print job might be up to 5-10 times slower that printing all labels in one print job.

The solution is to do printing in one print job, so each label is printed as a single job’s “page”. For this there is option #2.

Option #2 – Printing Using a LabelSet

A “labelset” contains data to be printed on labels. Conceptually it is very similar to a dataset/recordset or a database table. A labelset consists of a set of records. Each record represents data for a single label. A record consists of a set of “fields”. Each field name is a name of an object on the label. A field value is text to be printed for that object. A record can be seen as a dictionary/associative array. The dictionary’s keys are label object names. The dictionary’s values are text for the label object. The dictionary can have only one key, e.g. if there is only one object on the label. But also it can have multiple keys to specify data for different label objects. It is NOT necessary to  specify data for each object on the  label;  if a record does not have an appropriate key the object text will remain as-is (so, the object is a kind of ‘static’ text).

To do multiple label printing two pieces of information need to be defined. One piece defines what to print and the other piece defines how to print. “How to print” means how/where to put data on the label. That is specified by the label file. “What to print” is the actual data. That is specified by a labelset.

One more way to look at multiple label printing from the point of view of the  MVC (Model-View-Controller) design pattern. In this case a labelset is the Model, the data to be processed/printed. A label (file) is the View. It specifies a presentation or layout for the data to be printed. And the library is the Controller, it binds the label and the labelset in print() operation.

LabelSet API

The Labelset API is quite simple. To create a labelset call dymo.label.framework.LabelSetBuilder constructor, like

var labelSet = new dymo.label.framework.LabelSetBuilder();

To add a new record, call the addRecord() method of the LabelSetBuilder object,

var record = labelSet.addRecord();

To set record data, call the setText() method. The first parameter is an object/field name, the second – an object text/field value,

record.setText("Description", itemDescription);
record.setText("ItemCode", itemCode);

To print a labelset, just pass it as a third argument to print() method (the first argument is a parameter name, and the second argument is printing parameters; you can use empty string to specify default printing parameters),

label.print("DYMO LabelWriter 450 Turbo", '', labelSet);

Samples

To demonstrate using LabelSet we will look at two samples. Both of them use Google Docs spreadsheet as a data source. They take a spreadsheet data using Google Data JSON API and convert to appropriate labelset. The spreadsheet itself is displayed on a web page as well, so it is more clear what data are printed. Although for these samples spreadsheets are read-only it is possible to make them “editable”, so “live” data will be printed.

Sample #1 – Addresses

A spreadsheet for this sample contains address data. Each part of the address – name, street, city, state, zip – is in it’s own column. So, each spreadsheet row contains a full address, and specifies data for one label. The sample page is here, the java script code is here.

Upon page load a request is made to get the spreadsheet’s data:

function loadSpreadSheetData()
{
    removeOldJSONScriptNodes();

    var script = document.createElement('script');

    script.setAttribute('src', 'http://spreadsheets.google.
                        com/feeds/list/tSaHQOgyWYZb6mUPGgrsOGg/1/public/values?alt=json-in-script&callback=window.
                        _loadSpreadSheetDataCallback');
    script.setAttribute('id', 'printScript');
    script.setAttribute('type', 'text/javascript');
    document.documentElement.firstChild.appendChild(script);
};

This is done by dynamically creating a <script> element to avoid cross-domain security restrictions. Upon request completion a callback function is called; the callback gets passed the returned spreadsheet’s data in json format to a function that generates a labelset and saves it in labelSet variable.

function loadSpreadSheetDataCallback(json)
{
    labelSet = createLabelSet(json);
};

createLabelSet() function iterates through all spreadsheet rows, and for each row it creates one labelset record. Each record contain only one “field”, because the label we going to print contains only one address object named “Address”.

function createLabelSet(json)
{
    var labelSet = new dymo.label.framework.LabelSetBuilder();

    for (var i = 0; i < json.feed.entry.length; ++i)
    {
        var entry = json.feed.entry[i];

        var name = entry.gsx$name.$t;
        var street = entry.gsx$streetaddress.$t;
        var city = entry.gsx$city.$t;
        var state = entry.gsx$state.$t;
        var zip = entry.gsx$zip.$t;

        var address = name + 'n' + street + 'n' + city + ', ' + state + ' ' + zip;

        var record = labelSet.addRecord();
        record.setText("Address", address);
    }

    return labelSet;
}

As you see, the function is pretty straightforward. The most complex part is to get the spreadsheet’s columns values (name, street, city, etc variables) and combine them into one address block (address variable).

A label file is fetched from the server on the page loading as well. The label is saved in the “label” variable. We used jQuery library to fetch the label xml, but any other AJAX toolkit library can be used as well.

 function loadLabel()
 {
     // use jQuery API to load label
     $.get("Address.label", function(labelXml)
     {
         label = dymo.label.framework.openLabelXml(labelXml);
     }, "text");
 }

The final step is printing itself. It is done from Print button’s onclick handler.

printButton.onclick = function()
{
    try
    {
        if (!label)
            throw "Label is not loaded";

        if (!labelSet)
            throw "Label data is not loaded";

        label.print(printersSelect.value, '', labelSet);
    }
    catch (e)
    {
        alert(e.message || e);
    }
};

For simplicity the sample throws an exception if the label or labelset is still being loaded. If everything is OK, the printing itself is as simple as calling the print() method. There should be three labels printed with the following content:

img1 img2 img3
Sample #2 – Inventory List

The second sample is very similar to the first one. The only difference is the data itself. For this sample the data is an inventory list with two columns. The first column is a textual description of an inventory item. The second is the item’s “code”/”id”. Instead of combining different columns into one field (as in the first sample), these two column are used independently to provide data for two different objects on the label. The “ItemDescription” column provides data for the text object, while the “ItemCode” column provides data for the barcode object. So, each record in the labelset will have two “fields”, one for each object.  This is done by calling setText() method twice.

function createLabelSet(json)
{
    var labelSet = new dymo.label.framework.LabelSetBuilder();

    for (var i = 0; i < json.feed.entry.length; ++i)
    {
        var entry = json.feed.entry[i];

        var itemDescription = entry.gsx$itemdescription.$t;
        var itemCode = entry.gsx$itemcode.$t;

        var record = labelSet.addRecord();
        record.setText("Description", itemDescription);
        record.setText("ItemCode", itemCode);
    }

    return labelSet;
}

The full code is available here. Four labels should be printed:

img10 img11 img12 img13

Conclusion

We have looked at two different ways to print multiple labels. The second one that uses a “labelset” concept is a recommended way to print multiple labels. You could even use it to print a single label, in this case you will not have to change your code much if you need to print multiple labels in the future. The data for labels might come from different sources, .e.g. entered directly by user, read from a corporate database, or fetched from a third-party provider like Google Docs as in the blog’s  samples.

In the next post we will look at various ways to format text data on a label, e.g. how to specify different font size, font style, etc on character-by-character basis.

Jun 152010
 

Introduction

Developing web/browser-based applications might be a hard task. One of the difficulties is that an application is a client-server application. The application consists of the two parts, one runs on the server side inside a web server, another runs on the client side inside a web browser. If a part of the application does not communicate with the external world, e.g. it performs some computation, it might be irrelevant or not very important to understand where it’s running, the server side or the client side. But if the part is communicating with the external world, e.g. it performs printing, it is extremely important to understand the rules and requirements of such communications. To complicate life, modern IDEs and frameworks provide high-level tools and these often hide/abstract core interaction logic between the browser and the server.

The goal for this post is to analyze possible use cases/scenarios related to label printing in web applications. For each scenario the necessary software, installation location, usefulness, etc will be discussed. After reading the post, the readers should be able to answer questions like «Why does my application work great on a development machine, but stop working/crashes/does nothing after deployment?» if awaken in the middle of the night; or even better if such questions will never arise :)

Different Scenarios

In context of label printing the important questions are:

  • Where is the printer connected? On the server or on the client/user machines?
  • Where does the printing happen? On the web server or on the browser?

So, we have four different scenarios.

Printer Connection

User PC

Server

Print From

Browser

Scenario #1

Scenario #4

Web Server

Scenario #3

Scenario #2

Lets look at each scenario in detail.

Scenario #1

In this scenario the printer is connected to the client, and printing itself is done on the client/browser. It is a «typical» scenario because is it a classic web-application scenario. The server might be thousand kilometers away; the server cannot access and does not know anything about local label printers installed on users machines. Note: the printer might be not connected directly to the user machine, it might be a «shared» printer on a local network. It is still can be considered as a «local» printer, because it is available from client machine. The point here is that the printer is available from client machine but NOT available from the web server machine.

Server Requirements

There are no special requirements for the server. It might run on any operating system, use any web-server software (like Apache or IIS), etc. Very likely that it will host one or more label files (.label) to be available on the clients.

Client Requirements

Because the printer is connected to the client/user machine, the printer drivers must be installed on the client. Anything else is optional but the most convenient way to do printing is to use the DYMO Label Framework JavaScript Library. In this case DYMO Label software should be installed on the client as well.

Hints for Developers

There should not be code in any language but JavaScript that accesses printing functionality, e.g. there should not be any ASP.NET event handlers those try to create DYMO.Label.Framework or DYMO.DymoAddIn objects.

Load a label file from the server using any AJAX library.

Scenario #2

In this scenario a printer is connected to the server. Printing itself is initiated on the server side as well. Possible types of labeling applications using this scenario are:

  • Some sort of “hard copy” logging. E.g. each operation/transaction is printed on a continuous label roll, like a receipt.
  • In intranet the label printer might be the only one available on the local network, so it might be suitable to connect it on the server.

Server Requirements

Because the printer is connected to the server, printer drivers must be installed on the server. Anything else is optional. If the server is running on Windows the most convenient way to do printing is to use DYMO Label Framework. In this case DYMO Label software should be installed as well. If the server is running on Linux, then you would probably use some PDF library to generate label content then print it using the CUPS API.

Client Requirements

In this case no special/additional software is needed on the client.

Hints for Developers

Check you have no JavaScript code accessing any print related functionality.

Scenario #3

In this scenario the printer is connected locally on the user’s machine but the printing itself is initiated on the web server side. It is really usable only for intranet, because server has to have a connection to a user (all users) printer(s).

Server Requirements

The printer is not physically connected to the server, but printer drivers must be installed on the server because the server has to have a “queue” for the remote client printer. Anything else is optional. If the server is running on Windows the most convenient way to do printing is to use DYMO Label Framework. In this case DYMO Label software should be installed as well. If the server is running on Linux, then you would probably use some PDF library to generate label content then print it using CUPS API.

Client Requirements

Because printer is connected to the client, printer drivers should be installed on the client. Also, the printer itself should be “shared”, so the server can make connect to it and can send print jobs.

Hints for Developers

Check you have no JavaScript code accessing any print related functionality.

Scenario #4

In this scenario the printer is connected to the server, but the printing is initiated from the client. Again it is only usable in intranet/local network setups. Even though, it is hard to think about any good use cases here, because scenario #1 or #2 would be more applicable here. Try to avoid this setup.

Hybrid Scenarios

Of course, some installation might require hybrid scenarios. E.g. your application developed using scenario #1 and works great. Later you decided to add a label preview feature, so user can look at the label before printing. Unfortunately you have to support IE6. But the label preview feature does not in IE6 (it uses data URLs, not supported by IE6). In this case you could modify your setup and install DYMO Label Framework on the server. In this case a label preview can be easily generated on the server and be passed to the client as a regular image URL, not as a data URL.

Conclusion

Developing a web application might be a hard task but it doesn’t have to be. Try to think about your application deployment requirements in advance. Based on that, determine what scenario is the best fit for the application. After that you can think about what should be installed on the server and  client machines and what tools/libraries to use for development.

If you run into a problem, try to determine what scenario your application is using. Knowing that you will know what should be installed in the server and client machines. You might need to rewrite some code if it is placed in a wrong place, that is why it is better to think about it in advance. Don’t hesitate to contact DYMO – we are always glad to help.