Apr 302011
 

We are proud to announce a BETA release of the DYMO Label Mobile SDK for iOS. The DYMO Label Mobile SDK for iOS is a complete toolset containing libraries, documentation, and samples that make it easy to add label printing to any iOS app. All DYMO LabelWriter and DYMO LabelManager printers are supported. Using the DYMO Label Mobile SDK in combination with DYMO Label Web SDK allows creating both native and web-based apps for iOS.

Please Note

This release is a BETA and is not supported by DYMO. 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’s first look at the high level architecture for the Mobile SDK to understand how it works. It is similar to the DYMO Web SDK with one difference: instead of using a DYMO Label Javascript library, a native binary library is compiled into an iOS app.

Pasted Graphic 1

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 iOS device and the printer.

Notes:

  • right now DYMO Label Proxy is available for Windows only. So, a printer must be connected to a Windows machine, or be available from Windows machine that 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 a RAW TCP/IP port. This setup might be used to simplify installation (see below).
  • the local network is used for communications between the proxy service and the 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.

Installation

SDK Installation

SDK is available here. It is a zip file, just extract it to any folder. Inside there are following folders:

  • docs– SDK documentation. Contains API reference documentation in two formats:
    • html to be viewed using web browser.
    • com.dymo.label.framework.docset to be integrated with XCode. To install the docset copy it to ~/Library/Developer/Shared/Documentation/DocSets folder.
  • include – header files.
  • lib – static libraries to link with.
  • Samples – sample apps.

Samples were tested in XCode 4.0.2 and XCode 3.2.6 for iOS SDK 3.2 – 4.3.

DYMO Label Proxy 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 Windows 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 software 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.

DYMO Label Proxy 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_thumb

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 the standard “Services” panel of the  “Computer Management” tool can be used.

Service ip-address

Usually the SDK is able to discover the service on the network automatically using Bonjour. But sometimes Bonjour does not work. One common case is when the service and iOS device are on different subnets, e.g. the mobile device is connected to a “wireless” subnet and the service to a “wired” subnet. This is not a problem with the DYMO service, it is how Bonjour works in its default configuration. There are solutions for this problem, but the detailed descriptions are beyond the scope of this post. Some ideas:

In this case it is necessary to know the service’s ip-address to be able to connect to it from iOS device (see below). The configuration utility can help here as well. Service’s ip-address(es) is displayed in Service URIs list.

API Overview

The SDK contains two complete samples that demonstrate most of the functionality the Framework provides. Here we will look at the API from the point of tasks that are necessary to perform to print a label. To print a label the following task usually have to be performed:

  1. discover a printer to print the label on
  2. load label layout
  3. set label data
  4. perform actual printing
  5. Monitor printing progress

Note: these tasks might be performed in a different order, e.g. printer discovery might be performed after the label is prepared for the printing. Though usually printer discovery should be performed first, because it can take some time.

Discover printers

Before something can be printed it is necessary to know on which printer it will be printed. DlfPrinters class is used to perform this task. Printer discovery is an asynchronous operation. The reason is that it requires network communications and various delays and timeouts might happen during the process. So, the Framework follows usual iOS SDK approach, and a delegate object is used to provide a way of receiving async notifications of the discovering progress. The delegate protocol is DlfPrintersDelegate. DlsPrintersDelegate defines two methods, one called when a new printer or printers are discovered, another when some sort of failure happened. So, create an object that implements DlfPrintersDelegate protocol, and initialize DlfPrinters instance by passing the delegate object into initWithDelegate: method. It is possible to reassign the delegate later if necessary using delegate property. To start discovering call refresh method. This method is asynchronous and returns almost immediately. When a printer is found the delegate’s method – printers:(id<IDlfPrinters>)sender didFindPrinters:(NSArray*)printers will be called. Here is a code snippet to demonstrate that:

@interface PrintMeThatLabelAppDelegate :
    NSObject <UIApplicationDelegate, DlfPrintersDelegate>
{
@private
    DlfPrinters* printers_;
}

@end

@implementation PrintMeThatLabelAppDelegate

- (void) applicationDidBecomeActive:(UIApplication*)application
{
    printers_ = [[DlfPrinters alloc] initWithDelegate:self];
    [printers_ refresh];
}

- (void) applicationWillResignActive:(UIApplication*)application
{
    [printers_ release];
}

- (void) printers:(id<IDlfPrinters>)dlfPrinters didFindPrinters:(NSArray*)printers
{
    for (id<IDlfPrinter> printer in printers)
        NSLog(@"%@", printer.name);
}

- (void) printers:(id<IDlfPrinters>)printers didFailWithError:(NSError*)error
{
    NSDictionary* userInfo = error.userInfo;
    NSString* s = [[[NSString alloc] initWithFormat:@"Unable to contact '%@' (%@)",
        [userInfo objectForKey:@"printerUri"],
        [userInfo objectForKey:@"printerLocation"]] autorelease];
    NSLog(@"%@", s);
    UIAlertView* alert = [[[UIAlertView alloc]
        initWithTitle:@"Error"
        message:s delegate:nil
        cancelButtonTitle:@"OK"
        otherButtonTitles:nil] autorelease];
    [alert show];
}

@end

Load Label Layout

A label layout specifies what will printed on a label. The layout contains ‘label objects’ – text, address, barcode, image, etc. Each object has a ‘reference name’, so the object can be referenced programmatically from the SDK. Usually DYMO Label software is used to create a layout and save it as xml file. Because the layout is serialized as a xml document, the layout can be created or modified using any xml editor or even programmatically. This and that blog posts describe label format in great details. To load label layout use [DlfLabel labelWithXml:] method. Usually we will load the layout from a resource file in the app bundle. Something like this:

// obtain a reference to IDlfLabel by loading a label from a file
NSString* labelFile = [[NSBundle mainBundle] pathForResource:@"MyLabel" ofType:@"label"];
NSString* xml = [NSString stringWithContentsOfFile:labelFile encoding:NSUTF8StringEncoding error:NULL];
id<IDlfLabel> label = [DlfLabel labelWithXml:xml];

Set Label Data

Label Layout can already contain data to be printed, e.g. an address. this might be a case when a label is generated dynamically on the server. But usually we will need to set data programmatically based on user’s input. This can be done using two different methods.

The first method allows printing of one label only. Use [IDlfLabel setText:forObject:] method to set text data of Address, Text, Barcode, and CircularText objects. Use setImage:forObject: to set image data for Image objects.

The second method is more universal, and allows printing multiple labels at once. It is implemented using a “label set” concept. Label set is similar to a recordset, a database table. Label set consist of a set of “label records”. Each label record represents data to be printed on one label. Label record itself is conceptually is a dictionary (associative array), where dictionary keys are label object names, and dictionary values are label object data. To manipulate a label set use DlfLabelSet class. Use addRecord method to add a record into a label set. To set object data in the record, use IDlfLabelSetRecord methods. There are methods to add text and image data similar to ones above. Also, there is way to specify formatted/styled text data where each character or line can have different formatting (font size and style).

void PrintWithLabelSet(id<IDlfPrinter> printer)
{
    // open a label
    NSString* labelFile = [[NSBundle mainBundle] pathForResource:@"TextLabel" ofType:@"label"];
    NSString* xml = [NSString stringWithContentsOfFile:labelFile encoding:NSUTF8StringEncoding error:NULL];
    id<IDlfLabel> label = [DlfLabel labelWithXml:xml];

    // create a builder object to populate label set
    DlfLabelSet* labelSet = [[[DlfLabelSet alloc] init] autorelease];

    // label #1
    id<IDlfLabelSetRecord> record = [labelSet addRecord];
    [record setText:@"6x7=42" forObject:@"TEXT"];

    //label #2
    record = [labelSet addRecord];
    [record setTextMarkup:@"font family='Arial' size='36'>6x7=<b>42</b></font>" forObject:@"TEXT"];

    // print label
    [label printOn:printer withParams:nil labelSet:labelSet printJobDelegate:nil];
}

Actual Printing

To start printing call – (id<IDlfPrintJob>) printOn:(id<IDlfPrinter>) printer withParams:(DlfPrintParams*) params labelSet:(DlfLabelSet*)labelSet printJobDelegate:(id<DlfPrintJobDelegate>)delegate method of IDlfLabel

We have to provide four parameters:

  • printer – printer to print the label on. The printer instance can be extracted from DlfPrinters instance initialized previously. This is the only required parameter, all other can be omitted.
  • printParams – printing parameters, like number of copies, print quality, etc. This parameter can be null, in witch case defaults will be used.
  • labelSet – a label set contains data to be printed. If omitted, the data contained by label instance will be printed.
  • delegate – a delegate object to receive status messages about printing progress. this might be omitted as well, if print progress monitoring is not important.

This method call is asynchronous as well. Right after all necessary data are assembled and printing is started the method returns. Printing progress monitoring can be done using IDlfPrintJob instance returned by the printOn: method (see below).

Print Progress Monitoring

IDlfPrintJob instance returned by printOn:withParams:labelSet:printJobDelegate: call can be used to monitor print progress. Periodically call getStatus: method to retrieve current status information. Note: currently only this “polling” schema is supported; in the future we might add “pushing” data from the printer when it status has changed as well. getStatus: is async call as well; when status data has retrieved from the printer, the delegate method printJob:(id<IDlfPrintJob>) printJob didGetStatus:(id<IDlfPrintJobStatus>) printJobStatus is called. Here is an example:

@interface PrintMeThatLabelAppDelegate : NSObject <UIApplicationDelegate, DlfPrintersDelegate, DlfPrintJobDelegate>
{
@private
    DlfPrinters* printers_;
    id<IDlfPrinter> currentPrinter_;
}

@end

@implementation PrintMeThatLabelAppDelegate

- (IBAction) printAction:(id)sender
{
    // load label
    NSString* labelFile = [[NSBundle mainBundle] pathForResource:@"MyLabel" ofType:@"label"];
    NSString* labelXml = [NSString stringWithContentsOfFile:labelFile encoding:NSUTF8StringEncoding error:NULL];
    id<IDlfLabel> label = [DlfLabel labelWithXml:labelXml];

    // specify label data
    DlfLabelSet* labelSet = [[DlfLabelSet new] autorelease];
    id<IDlfLabelSetRecord> record = [labelSet addRecord];
    [record setText:@"Hello, World!" forObject:@"TEXT"];
    id<IDlfPrintJob> printJob = [[label printOn:currentPrinter_ withParams:nil labelSet:labelSet printJobDelegate:self]
                                retain];

    // start job monitoring
    [(NSObject*)printJob performSelector:@selector(getStatus) withObject:nil afterDelay:1];
}

- (void) printJob:(id<IDlfPrintJob>) printJob didGetStatus:(id<IDlfPrintJobStatus>) printJobStatus
{
    NSLog(@"got print job status: %@", printJobStatus.statusMessage);

    // check status - if not completed then schedule another run, otherwise done with the print job
    DlfJobStatusId statusId = printJobStatus.statusId;
    if (statusId == kDlfPrintJobStatus_ProcessingError || statusId == kDlfPrintJobStatus_Finished)
        [printJob release];
    else
        [(NSObject*)printJob performSelector:@selector(getStatus) withObject:nil afterDelay:1];
}

@end

Setup XCode Project for Using with the SDK

To be able to use SDK libraries to create iOS app a few steps have to be done.  The main (and the only one at the moment) library in the SDK is a static library libDymoLabelFramework.a located in the lib SDK folder. To be able to compile and link  with the library include path to the headers should be specified, as well as the lib path to the library.

  • add a path to header files. Header files are located in DymoSDK/include/DymoLabelFramework folder. So, add the path to SDK/include into “Header Search Paths” Build Settings of the project. After that the headers can be included into *.h or *.m file by using #import “DymoLabelFramework/DymoLabelFramework.h”. Alternatively the include/DymoLabelFramework folder might be copied into the project source folder.  
  • add path to the location of libDymoLabelFramework.a  (DymoSDK/lib) to the “Library Search Paths”
  • add the library libDymoLabelFramework.a into the project. Add it to “Link Binary  With Libraries” group of “Build Phases” tab.libDymo

Conclusion

DYMO Label Mobile SDK for iOS  provides a simple way to add label printing capabilities to any iOS app. Along with DYMO Label Web SDK developers can use DYMO printers from their web-based applications or  from native apps.

Apr 222011
 

This post guides you through the simple steps needed to add your DYMO LabelWriter printer(s) to the Salesforce.com environment so that you can print a contact’s mailing address on a DYMO address label.

The post assumes that you are familiar with Salesforce.com’s CRM software and have a developer account with the company. Consult Salesforce.com user documentation if you have never created a Visualforce page.

The major tasks are:

  • Download and install the appropriate DYMO Label software and SDK
  • Create the Visualforce page that enables you to:
    • · Enumerate a list of client-side printer names
    • · Generate and preview an image for the label
    • · Create an editable multi-line text box with the mailing address for a selected contact
    • · Print the label
    • · Create a Print Label button

When these tasks are completed, your Salesforce.com page will resemble the following example:

pic01

Introduction

Salesforce.com is a Web-based service. As such, Salesforce.com stores and manipulates data on the server side without the need to know much about the client side – your computer. The Salesforce.com server has no knowledge of the peripherals that are connected to your computer. The DYMO Label Framework JavaScript Library fills the gap between your Salesforce.com data and your DYMO LabelWriter printer.

Salesforce.com may be customized for individual customer needs. This post provides JavaScript code examples and describes the Visualforce customization that is required for the CRM software- LabelWriter printer integration.

Download and Install DYMO Label Software

You need to download and install the following software:

DYMO Label v.8.3 (DLS). The latest updates are available for Windows and Mac

· Windows DYMO Label v.8.3 Installer

· Mac DYMO Label v.8.3 Installer

DYMO Label Framework JavaScript library which supports the most popular browsers for Windows and Mac

· DYMO Label Framework JavaScript Library

Creating the Visualforce Page

This post uses the standard Visualforce controller extension to provide access to a current contact’s data. To work with an existing contact’s data, you must specify the contact’s ID in the URL as an input parameter.

To begin, you have to:

  1. Pick one of your contacts (using the Contacts tab).
  2. Create a new URL that enables you to create a new Visualforce page.
  3. Type a page name in the URL, specifying the contact’s ID as an input parameter.

With Salesforce.com running, go to a contact’s Detail page and copy the ID from the browser’s address bar. The following screenshot shows the contact ID for Mr. Sean Forbes.

pic02

Paste the ID into your new URL. The new page URL will resemble the following:

https://c.na5.visual.force.com/apex/PrintAddress?id=0037000000TG8xR’

where PrintAddress is the name of your Visualforce page.

After this URL is typed into the browser’s address bar, the system displays the message:

Page PrintAddress does not exist

To create a page, click the Create Page link beneath this message.

An empty page is created and you can begin to construct the Visualforce page elements described in this post.

Specifying the Controller Extension and Linking the DYMO Label Framework JavaScript Library

Begin by specifying the controller extension and linking the page with the DYMO Label Framework JavaScript Library. Here is the code that does this:

<apex:page id="printAddressPage" standardController="Contact" extensions="PrintAddressExtension">
    <apex:includeScript value="{!$Resource.DymoFramework}"/>
    
    <div style="padding-bottom:6px">
        <apex:outputLink value="{!URLFOR($Action.Contact.View, $CurrentPage.parameters.id)}">
            Back to {!paObject.Contact.FirstName} {!paObject.Contact.LastName} detail page
        </apex:outputLink>
    </div>
    
    <apex:form >
    </apex:form>

</apex:page>

Before including the library in your page, you must add the DYMO Label Framework JavaScript Library as a static resource. Here are the steps:

  1. Create the resource by selecting App Setup > Develop > Static Resource from Salesforce.com.
  2. Provide a name, for example: DymoFramework.
  3. Browse to the folder where the DYMO Label SDK is installed (or where DYMO Framework JavaScript library is downloaded).
  4. Select the DYMO.Label.Framework.js file.

Now the framework library can be referenced on the page as:

<apex:includeScript value="{!$Resource.DymoFramework}"/>

The next part of the code is used as a page controller. You create a singleton:

paObject

to get access to the current contact’s properties as well as to accommodate other methods that the Visualforce page may require.

public class PrintAddressExtension
{
    public PrintAddressExtension(ApexPages.StandardController controller)
    {
    }

    public class PaObject
    {
        private Contact m_contact;
        public PaObject()
        {
            Id id = System.currentPageReference().getParameters().get('id');
            
            m_contact = id == null ? new Contact() :
                [Select Id, FirstName, LastName, MailingStreet, MailingCity, MailingState, MailingCountry,
                   MailingPostalCode FROM Contact WHERE Id = :id];
           
        }
        
        public Contact getContact()
        {
            return m_contact;
        }
        
        // contact full name
        public String getContactFullName()
        {
            if (m_contact == null)
            {
                system.Debug(logginglevel.ERROR, 'PaObject.m_contact is null');
                return '';
            }
            
            return m_contact.LastName + ', ' + m_contact.FirstName;
        }       
    }

    private paObject m_paObject;
   
    public PaObject getPaObject()
    {
        if (m_paObject == null)
        {
            m_paObject = new PaObject();
            System.debug(logginglevel.INFO, 'singleton PaObject is created');
        }
        return m_paObject;
    }
}

Save the Visualforce page and controller.

The system displays a page with a link:

Back to <Contact> detail page.

Enumerating Printer Names

As mentioned previously, the Salesforce.com server does not know what peripherals are connected to your computer. You must generate a list of the printer names separately on the client side and provide this information to Salesforce.com. You do this by coding a JavaScript routine that enumerates the available printers.

The Visualforce page contains an <apex:selectList> control (selection drop-down combo-box). You leave this control empty when the Visualforce page is first created. The control is created in such a way that you can add items to it later. You will have a global variable, <apex:selectList>, reference to this control.

Here is the piece of code that shows all these elements:

    <apex:form >
    
        <apex:pageBlock id="PrintersBlock" title="Select Printer">
            <apex:selectList id="Printers" size="1" />
        </apex:pageBlock>
    
        <script>
            var PrintersCtrl = document.getElementById("{!$Component.PrintersBlock.Printers}");
        </script>
    </apex:form>

To get access to the generated HTML element, you declare a PrintersCtrl variable and reference it using the full path of nested page blocks. The Salesforce.com syntax is:

{!$Component.PrintersBlock.Printers}

The JavaScript code shown below populates the list. The code uses the onload page event to hook to the procedure of enumerating LabelWriter printers. The enumPrinters function returns a list of available printers. From this function, you call the DYMO Label Framework JavaScript Library method declared as getPrinters(). You reference this method by the namespaces declared in the Library.

dymo.label.framework.getPrinters();

The method returns a list of the names of all available DYMO label printers installed on the client. Since you are going to print an address label, you can only use LabelWriter printers because these printers use die-cut address labels.

The DYMO Label Framework JavaScript Library returns the complete list of printers. You must iterate through the list again and dynamically create OPTION elements filled with only the LabelWriter printer names.

At this point, the reference to the HTML-rendered control becomes handy because you can access to the element (PrintersCtrl ) from JavaScript as:

PrintersCtrl.options.add(option);

Now, find the </apex:form> tag in the previous code example, and add the following code below the tag.

<script type="text/javascript">

    function enumPrinters() {
        var plist = new Array();
        var printers = dymo.label.framework.getPrinters();
        if (printers.length == 0) {
            alert("No DYMO printers are installed. Install DYMO printers.");
        }
        else {
            for (var i = 0; i < printers.length; i++) {
                if (printers[i].printerType == "LabelWriterPrinter")
                    plist[i] = printers[i].name;
            }
        }
        return plist;
    }

    window.onload = new function () {
        var plist = enumPrinters();

        if (plist.length > 0) {
            // populate combo-box control with a list of printers

            for (var i = 0; i < plist.length; i++) {
                var option = document.createElement("OPTION");
                option.text = plist[i];
                option.value = plist[i];
                PrintersCtrl.options.add(option);
            }
        }
    }
</script>

The Salesforce.com page now includes a SELECT control that is populated with the names of DYMO LabelWriter printers.

Updating the Label Preview

Next, you generate an image for the label preview. Some of the work, such as rendering the actual image, needs to be done on the client since only the DYMO Label Framework knows how to do this. The idea is that prior to submitting the page for a redraw, the client side needs to generate an image and save the result in the Visualforce page’s hidden field. When the Visualforce page is redrawn, it will contain the updated image taken from the controller’s member associated with the hidden field. To implement this approach, you introduce other Visualforce UI elements and global variables. Here is the code for this work:

<apex:form>
    <apex:inputhidden id="PreviewImageSrc" value="{!paObject.imageSrc}"/>
  
     <apex:pageBlock id="EditorBlock" title="{!paObject.contactFullName}">
        <div>
            <apex:inputTextarea id="AddressEditor"
                         value="{!paObject.formattedAddress}" rows="4" cols="52"/>
        </div>
        <div>
            <apex:inputCheckbox id="BarcodeCheckbox" selected="{!paObject.printBarcode}"
             	style="vertical-align:middle"/> Print Intelligent Mail Barcode
        </div>
        
        <hr/>
        
        <apex:commandButton id="ButtonUpdate" value="Update" rerender="PreviewPanel"          
        	     onclick="updatePreview('{!paObject.addressLabelXml}')"/>

    </apex:pageBlock>

    <apex:pageBlock id="PrintersBlock" title="Select Printer">
        <apex:selectList id="Printers" size="1" />
    </apex:pageBlock>

    <script>
        var PrintersCtrl = document.getElementById("{!$Component.PrintersBlock.Printers}");
        var AddressEditor = document.getElementById("{!$Component.EditorBlock.AddressEditor}");
        var BarcodeCheckbox = document.getElementById("{!$Component.EditorBlock.BarcodeCheckbox}");
        var PreviewImageSrc = document.getElementById("{!$Component.PreviewImageSrc}");
        var ButtonUpdate = document.getElementById("{!$Component.EditorBlock.ButtonUpdate}");
    </script>  
</apex:form>

<apex:outputpanel id="PreviewPanel">
    <div>
        <apex:image id="previewImage" url="{!paObject.imageSrc}"/>
    </div>   
</apex:outputpanel>

The added UI controls are:

  • A multi-line text editor for modifying the address (if desired).
  • A checkbox that triggers the inclusion of the Intelligent Mail Barcode on the label.
  • An Update button.

The Update button contains the rerender="PreviewPanel" attribute, which refreshes only the preview part of the page. When the Update button is clicked, only <apex:outputpanel> within the Visualforce page is redrawn. (See the Salesforce.com ’s user documentation for more information regarding the partial page refresh.)

Another element this code creates is a hidden field that stores the image preview’s raw data between the time when the update action is invoked and the time when the preview panel is redrawn. The code also contains a reference to the Update button itself because an update action must be invoked when the page is loaded for the first time.

The code hooks to the updatePreview JavaScript function when the Update button is clicked. Similar to the enumPrinters function, this updatePreview JavaScript function calls the DYMO Label Framework Library to open a label template and set the address data in the template’s address object. If you need to make additional modification to the label’s appearance—because of a user request or the data itself—it can be done in this updatePreview function.

In the example shown below, the code prevents the address barcode from appearing in the address.

    function updatePreview(template) {
        try {
            var address = AddressEditor.value;
            var label = dymo.label.framework.openLabelXml(template);

            label.setAddressText(0, address);

            // barcode - show it or not
            if (!BarcodeCheckbox.checked)
                label.setAddressBarcodePosition(0, dymo.label.framework.AddressBarcodePosition.Suppress);

            var pngData = label.render();
            PreviewImageSrc.value = "data:image/png;base64," + pngData;
        }
        catch (e) {
            alert(e.message);
        }
    }

The updatePreview function takes one parameter: an xml string that represents the label template. For simplicity, the example includes the xml template definition as a read-only property in the controller. Alternately, this string could be a static resource.

The call to the DYMO Label Framework Library’s label.render() method returns an image’s raw data as a string. The code assigns this string to the hidden text field (accessible as PreviewImageSrc).

An important thing to remember is that the Visualforce <apex:inputhidden> element requires a corresponding property in the controller. The controller should have a string property called imageSrc.

When the Update button is clicked, the updatePreview function:

  • Gathers all the data from the page controls.
  • Generates the label’s image.
  • Saves the image in the hidden field associated with the controller’s property.
  • Submits a request to redraw part of the page.

When the page is rendered, the Visualforce control <apex:image url="{!paObject.imageSrc}"/> contains an updated image.

To have a preview shown when the page is loaded for the first time, you have to add the next line into the page initialization routine:

ButtonUpdate.click();

Printing the Label

The Visualforce page requires a Print button that is linked to a corresponding JavaScript function attached to an onclick event. The printing function is not much different from the updating the preview function. However, instead of getting the label preview from the label.render() method, the function invokes label.print to print at the desired printer.

Here is the JavaScript print function code:

    function printAddress(template) {
        try {

            var label = dymo.label.framework.openLabelXml(template);
            label.setAddressText(0, AddressEditor.value);

            if (!BarcodeCheckbox.checked)
                label.setAddressBarcodePosition(0, dymo.label.framework.AddressBarcodePosition.Suppress);

            var printer = PrintersCtrl.value;

            label.print(printer);
        }
        catch (e) {
            alert(e.message);
        }
    }

Creating a Print Label Button

Here is the link to the code examples:

http://www.labelwriter.com/software/dls/sdk/blog_resources/Salesforce.PrintAddress.zip

To make them work, you must add a custom button to the contact’s Detail page.

  1. Select Setup > Customize > Contacts > Buttons and Links.
  2. Click the New button in the Custom Buttons and Links section.

A page similar to the following example appears.

pic03

  1. Provide the requested parameters.
    • · Button Label and Name
    • · Description
    • · Display Type (the type of UI link or button)
    • · Behavior (select Display in existing window with sidebar)
    • · Content Source (select Visualforce Page)

When you have completed and saved this customization work, a Print Label button appears on the Detail page for each of your contacts.

Conclusion

That is all there is to it. I hope this post has demonstrated how easy it is to integrate DYMO LabelWriter printers into a Salesforce.com application.

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

Jul 212010
 

A common question after reviewing the SDK sample applications installed with the SDK installer is “how can I set my own specific data onto the label in a format and layout of my choosing?” To do this you create a label using DLS 8. You can add label objects (Address, Text, Barcode, Image, etc.) onto the label and save it as a ‘template’ for your SDK code to use.

Scenario 1: Adding a TEXT object

First, run DLS 8 and design a label. Go to the Designer tab and edit the label. For this example, add a TEXT object by double-clicking on ‘Text’. You should now have a resizable rectangle on your label (see screen shot below).

Next, double-click on the rectangle to set the text object properties. Select the Advanced tab from the resulting dialog box. In the edit control titled ‘Reference name’ choose a unique name for this object (see screen shot below).

In this sample, I chose to name the object ‘TEXT1’. Your SDK code sets data on this object using this unique name. For instance,

Framework SDK (sample written in VB.NET)

Private Label As DYMO.Label.Framework.ILabel
Label = DYMO.Label.Framework.Framework.Open("c:Documents and SettingsAll 
UsersDocumentsDYMO LabelLabel FilesTestLabel.label")
Label.SetObjectText("TEXT1", "Testing, testing")
Label.Print("DYMO LabelWriter 450 Turbo")

DLS SDK API (sample written in C#)

myDymoAddin = new DymoAddIn();
myLabel = new DymoLabels();
if (myDymoAddin.Open(@"c:Documents and SettingsAll UsersDocumentsDYMO 
LabelLabel FilesTestLabel.label"))
{
    myLabel.SetField("TEXT1", "Testing, testing");
    myDymoAddin.StartPrintJob();
    myDymoAddin.Print(1, FALSE);
    myDymoAddin.EndPrintJob();
}

Output:

This sets the text, “Testing, testing”, onto the TEXT object which you named TEXT1 on your label. The exact same process would work for creating an ADDRESS object or a BARCODE object. You would simply refer to any other object you create by the name assigned in the Properties dialog (“TEXT1” in our example) and change the corresponding line of code which sets the data onto this object. For example, if I had added a barcode object and assinged it the name ‘MYBARCODE’, my call to set the data for this barcode object might resemble the following:

myLabel.SetField("MYBARCODE", "97820315");

See Scenario 2 (below) for greater detail about adding a barcode object.

Scenario 2: Adding a BARCODE object

If instead, you would like to add a barcode object (either in addition to the TEXT object from Scenario 1 or all by itself), again, run DLS 8 to add this object to your label file template. Go to the Designer tab and edit the label. Add a BARCODE object by double-clicking on ‘Barcode’. You now have a resizable rectangle on your label (see screen shot below).

When you size the barcode object make sure its large enough to hold the barcode for your data! Next, double-click on the rectangle to set the barcode object properties. Select the Advanced tab from the resulting dialog box. In the edit control titled ‘Reference name’ choose a unique name for this object (see screen shot below).

In this sample, I chose to name the control ‘TEST_BARCODE’. Your SDK code sets data on this object using this unique name.  Next, click on the ‘General’ tab of the properties dialog box. You are now presented with options to change the barcode symbology (see screen shot below).

The code sample below will set the barcode data to ‘9876543’ for the particular label printed.

Framework SDK (sample written in VB.NET)

Private Label As DYMO.Label.Framework.ILabel
Label = DYMO.Label.Framework.Framework.Open("c:Documents and SettingsAll 
UsersDocumentsDYMO LabelLabel FilesTestLabel.label")
Label.SetObjectText("TEST_BARCODE", "9876543")
Label.Print("DYMO LabelWriter 450 Turbo")

DLS SDK API (sample written in C#)

myDymoAddin = new DymoAddIn();
myLabel = new DymoLabels();
if (myDymoAddin.Open(@"c:Documents and SettingsAll UsersDocumentsDYMO 
LabelLabel FilesTestLabel.label"))
{
    myLabel.SetField("TEST_BARCODE", "9876543");
    myDymoAddin.StartPrintJob();
    myDymoAddin.Print(1, FALSE);
    myDymoAddin.EndPrintJob();
}

Output:

Conclusion

The format of the label (objects found within the label and their locations) printed from your SDK application can easily be edited using DLS 8. The data passed to the objects is controlled through the SDK application itself. Using the SetField() functionality you can set your desired data into the formatted label template with total control.

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 112010
 

An earlier post showed how simple is to add label printing capabilities into a web application by using DYMO Label Framework JavaScript Library. Now we will explore some advanced features. One such feature is generating a label preview. The complete sample is available on http://labelwriter.com/software/dls/sdk/samples/js/PreviewAndPrintLabel/PreviewAndPrintLabel.html; JavaScript code on http://labelwriter.com/software/dls/sdk/samples/js/PreviewAndPrintLabel/PreviewAndPrintLabel.js. The sample allows previewing an Address label loaded from the server. The content of the address can be edited in a TextArea element. The label preview is dynamically updated using data from the TextArea element. After the address is entered, the label can be printed on a printer selected from a list of available printers.

Loading the Label

Label layout information is stored using xml, so it is simple to operate it in the browser. The sample uses the jQuery library to load xml from the server though any other library can be used to do that. On the server the label is stored as a regular file “Address.label”.

function loadLabelFromWeb()
{
    // use jQuery API to load label
    $.get("Address.label", function(labelXml)
    {
        label = dymo.label.framework.openLabelXml(labelXml);
        // check that label has an address object
        if (label.getAddressObjectCount() == 0)
        {
            alert("Selected label does not have an address object on it. Select another label");
            return;
        }

        updatePreview();
        addressTextArea.value = getAddress();
        printButton.disabled = false;
        addressTextArea.disabled = false;
    }, "text");
}

After label xml is downloaded from the server it is “opened” and stored in label variable by calling dymo.label.framework.openLabelXml() function.

Generating Preview

After the label is loaded its preview is generated by calling render() method. render() method returns preview data as a base64-encoded png stream, so it can be used as a data url for <img>.

function updatePreview()
{
    if (!label)
        return;

    var pngData = label.render();

    var labelImage = document.getElementById('labelImage');
    labelImage.src = "data:image/png;base64," + pngData;
}

Please note that not all browsers support data urls, e.g. IE6 and IE7 do not support them at all, IE8 has a limitation of 32 Kb for an url, so not all labels can fit into this limit. But it is always possible to generate preview on the server if needed.

Getting Address Data

To initialize TextArea element with address information from the label the getAddressText() method can be used. Because we know/assume that our label has only one address object on it we address it as an object with index ‘0’.

function getAddress()
{
    if (!label || label.getAddressObjectCount() == 0)
        return "";

    return label.getAddressText(0);
}

Setting Address Data

To update the label with a new address entered in TextArea element. setAddressText() can be used.

function setAddress(address)
{
    if (!label || label.getAddressObjectCount() == 0)
        return;

    return label.setAddressText(0, address);
}

Printing

Printing is as simple as calling the print() method with the printer name selected from printers combo box.

label.print(printersSelect.value);

Summary

In this post we looked at how label previewing can be added into your web application. In the next post we will see how multiple labels can be printed.

Jun 022010
 

This blog post will show how easy is to add label printing capabilities to your web application. This is possible because of the JavaScript library that is a part of new DYMO Label Framework.

Prerequisites

To be able to use the library DYMO Label software should be installed on the client machines. On Windows version 8.3 or later is required.  Version 8.3.1.1332 is available on http://download.dymo.com/download/Win/DLS8Setup.8.3.1.1332.exe On Mac version 8.2.2.1173 or later is required. Version 8.2.2.1173 is available on http://www.labelwriter.com/software/dls/mac/DLS8Setup.8.2.2.1173.dmg

The Sample

The complete sample is available on http://www.labelwriter.com/software/dls/sdk/samples/js/PrintLabel/PrintLabel.html

The code in the sample is in http://www.labelwriter.com/software/dls/sdk/samples/js/PrintLabel/PrintLabel.js

The sample is minimalistic. It just contains a text area element to specify a text to be printed on a label and a button that triggers printing.

DYMOLabelFramework.js

The first thing should be done for any web project that uses the Framework is to include the library’s code, so it is available for scripts on the page. It is done like this:

<script src="http://labelwriter.com/software/dls/sdk/js/DYMO.Label.Framework.latest.js"
        type="text/javascript" charset="UTF-8"> </script>

This will include the latest version of the library. The only other hosted version for now is http://labelwriter.com/software/dls/sdk/js/DYMO.Label.Framework.1.0.beta.js

In the future other versions will be available as well. It is OK to host the file on your own web-server.

Print a Label

All printing code is in the printButton.onclick event handler assigned in PrintLabel.js. The print task contains three major steps: specifying label layout to print, setting data to print, selecting printer to print on, and actual printing.

Specify Label Layout to Print

Before a label can be printed we should specify what is the label, what objects it contains, what are their positions, etc. It is done by “opening” a label. The easiest way is to put the xml string describing the label right into openLabelXml() function. The easiest way to obtain the xml is to design the label using DYMO Label software, saving it into a file, then pasting file content into the js code.

var labelXml = '
 <DieCutLabel Version="8.0" Units="twips">
 <PaperOrientation>Landscape</PaperOrientation>
 <Id>Address</Id>
 <PaperName>30252 Address</PaperName>
 <DrawCommands/>
 <ObjectInfo>
 <TextObject>
 <Name>Text</Name>
 <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
 <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
 <LinkedObjectName></LinkedObjectName>
 <Rotation>Rotation0</Rotation>
 <IsMirrored>False</IsMirrored>
 <IsVariable>True</IsVariable>
 <HorizontalAlignment>Left</HorizontalAlignment>
 <VerticalAlignment>Middle</VerticalAlignment>
 <TextFitMode>ShrinkToFit</TextFitMode>
 <UseFullFontHeight>True</UseFullFontHeight>
 <Verticalized>False</Verticalized>
 <StyledText/>
 </TextObject>
 <Bounds X="332" Y="150" Width="4455" Height="1260" />
 </ObjectInfo>
 </DieCutLabel>';
var label = dymo.label.framework.openLabelXml(labelXml);

In a real web application you would probably load the label definition xml from the server using some AJAX library instead of specifying it directly in a js file.

Setting Data to Print

The next step is to specify data to print on the label. This is easy:

label.setObjectText("Text", textTextArea.value);

The code sets the content for the object named “Text” to whatever is typed in the text area field on the page. Note: the library supports setting formatted/styled text as well. This ability will be reviewed in a different blog post.

Selecting the Printer to Print on

The printer should be selected from a list of installed printers. For this sample we choose the first LabelWriter printer:

var printers = dymo.label.framework.getPrinters();
if (printers.length == 0)
    throw "No DYMO printers are installed. Install DYMO printers.";

var printerName = "";
for (var i = 0; i < printers.length; ++i)
{
    var printer = printers[i];
    if (printer.printerType == "LabelWriterPrinter")
    {
        printerName = printer.name;
        break;
    }
}

Actual Printing

If the printer name is known the printing is simple:

label.print(printerName);

Conclusion

As you can see, label printing is not hard if DYMO Label Framework is being used. The Framework has a lot of other useful features, like multiple label printing, specifying image data for a label, specifying text styles, etc. These features will be reviewed in later posts.

Apr 302010
 

In the last blog, I started out with a simple label printing application using the DLS SDK. If you are planning on using the SDK, you’ll need to become familiar with two objects that will do the bulk of label printing work in your application. The first one the DymoAddin object, the other is the DymoLabels object. We will examine on the DymoAddin object in this post.

Background

The DymoAddin object got its name from the time when DYMO needed to create addins for 3rd party applications. Instead of re-writing all the label printing code in a separate library, then using the library to implement the 3rd party addins (like the DLS MS Office Addins, DLS ACT! Addin, DLS QuickBooks Addin), we simply added a COM interface to the DLS application so we can leverage the label printing functionality from the DLS application. The first interface exposed was called the IDymoAddin interface, and the object that implemented the interface was called the DymoAddin object.

There are many methods exposed in the IDymoAddin interface, however, only a part of it is really intended for the SDK. The methods that don’t fall in this group are there because we (i.e. DYMO) needed it to implement some functionality for the addins we write for other applications. Call it lack of planning or oversight, these methods and properties that are really meant for internal DYMO use are lumped together in the same IDymoAddin interface that is used for SDK applications. For the most part, you should avoid using these methods and properties in your application. But as you sit down to understand the DLS SDK and design your own application, you’ll likely find that these methods and properties are too specialized and are not needed for your SDK application.

But what are these functions and methods that you shouldn’t use? Instead of listing them here, I will instead focus on the methods that are meant to be used by SDK applications.

Main Functionality

In the last blog, I outlined the three main concepts used in the DLS SDK. The first is the concept of a label file. The main functionality of the DymoAddin object is to implement the IDymoAddin interface, which provides operations you can perform on a label file, including opening, saving, and printing a label file. Because printing is part of the functionality, the IDymoAddin interface also provides methods to list and select DYMO printers for use in your SDK application. I will explore these methods and their usage (in C#) below.

File Operations

The Open(), Save(), and SaveAs() methods combined to give you the ability to open and save labels on your computer’s local file system. The same operations can be used on network storage if the storage location is addressable using UNC (i.e. Universal Naming Convention).

The following example shows how to open a label file and then save it with a different file name:

 1 private void FileOperatoinsExample()
 2 {
 3     DymoAddInClass _dymoAddin = new DymoAddInClass();
 4 
 5     // open a label file using absolute path, relative path
 6     // or UNC (for network accessible locations)
 7     if (_dymoAddin.Open(@"..LabelsSample.label"))
 8     {
 9         _dymoAddin.SaveAs(@"..LabelsSampleCopy.label");
10     }
11 
12     if (_dymoAddin.Open(@"C:UsersckhsuLabelsSample.label"))
13     {
14         _dymoAddin.SaveAs(@"C:UsersckhsulabelsSampleCopy.label");
15     }
16 
17     if (_dymoAddin.Open(@"\File ServerSharedLabelsSample.label"))
18     {
19         _dymoAddin.SaveAs(@"\File ServerSharedLabelsSampleCopy.label");
20     }
21 }
22 

Web Applications

Sometimes the label file you need is located in the cloud and you need to access it using an URL. The OpenURL() method is created for this purpose.

Proxy Server and Proxy Bypass

The OpenURL() method is implemented using WinInet. This means that the user’s proxy settings in IE is used when calling the OpenURL(). But depending on your application, this might not be desirable. You can use the following methods and properties to control proxy settings used for the OpenURL() method. These settings are good for the lifetime of the DymoAddinObject, but are not persistent beyond that. So if you free and create another instance of the DymoAddinObject, you will need to setup the custom proxy settings again for the next OpenURL call.

  • ProxyBypass property – set to True to bypass all proxy servers. When this property is set to True, it overrides all other proxy settings.
  • SetupProxySettings() method – setup custom proxy settings.
  • ClearProxySettings() method – clear all proxy settings and revert back to using IE’s proxy settings.

The following example shows how to open a label file using an URL.

 1 private void URLFileOperatoinsExample()
 2 {
 3     DymoAddInClass _dymoAddin = new DymoAddInClass();
 4 
 5     // use custom proxy setup.
 6     // by default, the OpenURL() method uses the same proxy
 7     // settings as IE
 8     _dymoAddin.proxyBypass = false;
 9     _dymoAddin.SetupProxySettings("http", "dymo_proxy_server", 80, "*.internal.dymo.*", "", "");
10 
11     // open a label file using URL
12     if (_dymoAddin.OpenURL("http://www.mylabelprinter.com/labels/shipping.lwl"))
13     {
14         _dymoAddin.SaveAs(@"LabelsSampleCopy.label");
15     }
16 }
17 

Database Operations

If your application uses a database to store label files, you can use the OpenStream() and SaveStream() methods to open and save label files using memory buffers.

The following example shows how to open and save label files into memory buffers:

 1 private void OpenAndSaveStreamExample()
 2 {
 3     DymoAddInClass _dymoAddin = new DymoAddInClass();
 4 
 5     // this is the buffer that will hold an array of bytes
 6     object buf = null;
 7 
 8     if (_dymoAddin.Open(System.IO.Path.Combine(
 9                             System.IO.Directory.GetCurrentDirectory(),
10                             @"DatabaseLabel.label")))
11     {
12         // get an array of bytes from Save Stream
13         buf = _dymoAddin.SaveStream();
14 
15         // at this point, you can store buf as a blob
16         // in your database
17         byte[] blob = (byte[])buf;
18     }
19 
20     // to open a label file from a blob in your database
21     // first, read the blob into into an array of bytes
22     // then pass the array of bytes to OpenStream
23     System.IO.MemoryStream ms = new System.IO.MemoryStream();
24 
25     // the following line is psuedo code aimed to illustrate how one
26     // might read a blob into an array of bytes
27     !!!Database.Table("Labels Table").Field("Shipping Label").SaveToStream(ms);
28 
29     // at this point, you can read the bytes
30     _dymoAddin.OpenStream(ms.GetBuffer());
31 }
32 

Printer Related Operations

The DLS SDK (and therefore your SDK applications) can only be used with LabelWriter printers. Because of this, we added printer listing and selection methods to filter out printers that are not compatible with the DLS SDK so your application will not need extra code to deal with printer compatibility and selection issues.

The following example shows how to get a list of LabelWriter printers installed on the computer, and selects a printer named “My LabelWriter” to print a label file:

 1 private void SelectPrinterAndPrintLabelExample()
 2 {
 3     DymoAddInClass _dymoAddin = new DymoAddInClass();
 4 
 5     string[] printers = _dymoAddin.GetDymoPrinters().Split(new char[] { '|' });
 6     foreach (var printer in printers)
 7     {
 8         // find the printer we want to print to
 9         if (printer == "My LabelWriter")
10         {
11             // select it first
12             _dymoAddin.SelectPrinter(printer);
13 
14             // then print the currently open label
15             if (_dymoAddin.IsTwinTurboPrinter(printer))
16             {
17                 // print one copy of label to the left tray
18                 _dymoAddin.Print2(1, false, 0);
19             }
20             else
21             {
22                 // print one copy of label to the only tray
23                 _dymoAddin.Print(1, false);
24             }
25             break;
26         }
27     }
28 }
29 

A new method worth mentioning is the IsPrinterOnline() method, which return the online/offline status of a printer.

Conclusion

Although the DymoAddin object’s name may not invoke any ideas of the functionality it provides, it is an important part of the because of the rich features it provides. I hope this blogs gives you a good idea of the DymoAddin object’s capabilities and how to best use the capabilities for your specific application.

Apr 032010
 

So you’ve read somewhere that DYMO LabelWriter printers come with a software developer’s kit, commonly referred to as the DLS SDK. But you might be wondering how DLS SDK can help you and how much time you have to spend learning this SDK before you can start using it. Hopefully this blog entry gives you an idea on how easy it is to get started and also help answer some commonly asked questions by developers.

Before we get too far, let me first explain what the DLS SDK is: it is a programming interface designed specifically for label printing. Using the programming interface (API), one can build a label printing application with minimum amount of work.

Create a label printing application

The following section walks you through the steps of creating a label printing application using the DLS SDK. At the end of the section, you should have an understanding of the basic concepts and the working parts of the DLS SDK.

Step 1. Get the tools you need.

  1. The latest DLS 8 Installer (build 996). It’s important to install the latest version.
  2. The latest DLS SDK Samples. The samples installers installs documentation and technical references to the DLS SDK along with samples to give you an idea of the types of application you can create using the SDK.
  3. Visual Studio 2005 or 2008. I will be using VS2008, but what I do there can easily be done in VS2005.
  4. A PC with a LabelWriter printer connected to it and the latest DLS 8 software installed.

Step 2. Understanding the basic concepts in the DLS SDK

  1. Label File: A label file is a document that contains address, text, barcode, and/or images that you want to print on a label. You can perform various operations on a label file, like Open, Print, SaveAs, etc.
  2. Label Objects: The address, text, and barcode data that are stored inside a label file are logically separated into different label objects. Each object has its own set of properties that control how the object is printed on the label, and the DLS SDK provides access to programmatically create label objects and change label object properties.
  3. Data: The data is a general term for data (i.e. text, address, or image) that you would like to print on a label. The data source can be from user input, a file, or a database.

The DLS SDK ties these three concepts together to enable you to start printing labels in your application with a few lines of code.

Step 3. Defining your application.

To keep the blog short, I will define a simple application that lets users type in a list of to-dos for the day, and print out the list on a label to take with them for the day.

Step 4. Designing a label the application will print (download it here).

  1. start DLS 8, select a shipping label, then change the label orientation to portrait
  2. add a date-time label object on the label
  3. add a text object as header on the label
  4. add a text object as the area to list to-do’s on the label
  5. name the to-do’s text object “todos”
  6. save the label

This video shows you the steps to create the above label.

Step 5. Create an SDK application project in VS2008 (download it here). The important things to note are:

  • make sure the project is targeted for x86 platform. The reason is because the current DSL SDK works only in 32-bit mode (i.e. x86), so if your application is targeted for x64 or AnyCPU, you will not be able to use the DLS SDK.
  • Add a reference to the “DLS 7 Compatibility COM Type Library” in the project. Doing so allows you to use the COM objects that are available in the DLS SDK.

This video shows you the steps to setup the above project.

Step 6. Adding code to take in a list of to-do items from the user, then printing it on label (see listing below).

DymoAddinClass and DymoLabelsClass are the two main objects available from the High-Level COM interface in the DLS SDK. Combined, they provide methods and properties for you to open a label file, set data on label objects in the label file, and print the label. Keep in mind that the DLS SDK is much more extensive than just these two objects, and we will take time to explore them in future blog entries.

1 private void printBtn_Click(object sender, EventArgs e)
2 {
3     DymoAddInClass _dymoAddin = new DymoAddInClass();
4     DymoLabelsClass _dymoLabel = new DymoLabelsClass();
5 
6     // open the to-do-list label we created earlier
7     if (_dymoAddin.Open(@"......To-Do's List.label"))
8     {
9         // this call returns a list of objects on the label
10         string[] objNames = _dymoLabel.GetObjectNames(false).Split(new Char [] {'|'});
11 
12         // verify that our text object is on the label
13         if (objNames.Contains("todos"))
14         {
15             // take the to-do's list entered by the user
16             // and put in on the label
17             if (_dymoLabel.SetField("todos", todoEdit.Text))
18             {
19                 // let's print to the first LabelWriter available
20                 // on the pc, if the printer is a TwiTurbo, print
21                 // to the left tray
22                 string[] printers = _dymoAddin.GetDymoPrinters().Split(new char[] { '|' });
23                 if (printers.Count() > 0)
24                 {
25                     if (_dymoAddin.SelectPrinter(printers[0]))
26                     {
27                         if (_dymoAddin.IsTwinTurboPrinter(printers[0]))
28                         {
29                             _dymoAddin.Print2(1, false, 0);
30                         }
31                         else
32                         {
33                             _dymoAddin.Print(1, false);
34                         }
35                     }
36                 }
37             }
38         }
39     }
40 }
41 
(you can download the complete project here).

Done! As you can see, it didn’t take much to create a simple application that prints labels using the DLS SDK

Mar 242010
 

In a previous post we looked at the new label file format used by DYMO Label v.8 software (DLS) from a bird’s eye view. Now it’s a time to look a little closer. The most important thing in a label file are label objects. Label objects contain data and formatting properties and define the label’s content. All object types and most of their properties are available through the DLS UI and are pretty straightforward. In this post we will briefly describe all label objects and will  look at most important properties as well as at properties not available through the UI.

Let’s look at  this label file. It contains all the different object types supported by DLS. If you open the label in DLS it will look like this:

UndestandingLabelFileFormat-AllObjects.label
Listing 1: UndestandingLabelFileFormat-AllObjects.label
001 <?xml version="1.0" encoding="utf-8"?>
002 <DieCutLabel Version="8.0" Units="twips">
003     <PaperOrientation>Landscape</PaperOrientation>
004     <Id>LargeShipping</Id>
005     <PaperName>30256 Shipping</PaperName>
006     <DrawCommands>
007         <RoundRectangle X="0" Y="0" Width="3331" Height="5715" Rx="270" Ry="270" />
008     </DrawCommands>
009     <ObjectInfo>
010         <TextObject>
011             <Name>BarcodeText</Name>
012             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
013             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
014             <LinkedObjectName></LinkedObjectName>
015             <Rotation>Rotation0</Rotation>
016             <IsMirrored>False</IsMirrored>
017             <IsVariable>False</IsVariable>
018             <HorizontalAlignment>Left</HorizontalAlignment>
019             <VerticalAlignment>Top</VerticalAlignment>
020             <TextFitMode>AlwaysFit</TextFitMode>
021             <UseFullFontHeight>False</UseFullFontHeight>
022             <Verticalized>False</Verticalized>
023             <StyledText>
024                 <Element>
025                     <String>BARCODE</String>
026                     <Attributes>
027                         <Font Family="Arial" Size="12" Bold="False" Italic="False"
028                                      Underline="False" Strikeout="False" />
029                         <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
030                     </Attributes>
031                 </Element>
032             </StyledText>
033         </TextObject>
034         <Bounds X="336" Y="132.900024414063" Width="2265" Height="345" />
035     </ObjectInfo>
036     <ObjectInfo>
037         <ShapeObject>
038             <Name>Shape</Name>
039             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
040             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
041             <LinkedObjectName></LinkedObjectName>
042             <Rotation>Rotation0</Rotation>
043             <IsMirrored>False</IsMirrored>
044             <IsVariable>False</IsVariable>
045             <ShapeType>Ellipse</ShapeType>
046             <LineWidth>45</LineWidth>
047             <LineAlignment>Center</LineAlignment>
048             <FillColor Alpha="0" Red="255" Green="255" Blue="255" />
049         </ShapeObject>
050         <Bounds X="3004" Y="2187.89990234375" Width="2670" Height="570" />
051     </ObjectInfo>
052     <ObjectInfo>
053         <AddressObject>
054             <Name>Address</Name>
055             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
056             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
057             <LinkedObjectName></LinkedObjectName>
058             <Rotation>Rotation0</Rotation>
059             <IsMirrored>False</IsMirrored>
060             <IsVariable>True</IsVariable>
061             <HorizontalAlignment>Left</HorizontalAlignment>
062             <VerticalAlignment>Top</VerticalAlignment>
063             <TextFitMode>ShrinkToFit</TextFitMode>
064             <UseFullFontHeight>True</UseFullFontHeight>
065             <Verticalized>False</Verticalized>
066             <StyledText>
067                 <Element>
068                     <String>DYMO
069 </String>
070                     <Attributes>
071                         <Font Family="Arial" Size="12" Bold="True" Italic="False"
072                                      Underline="False" Strikeout="False" />
073                         <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
074                     </Attributes>
075                 </Element>
076                 <Element>
077                     <String>828 SAN PABLO AVE
078 </String>
079                     <Attributes>
080                         <Font Family="Arial" Size="12" Bold="False" Italic="True"
081                                      Underline="False" Strikeout="False" />
082                         <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
083                     </Attributes>
084                 </Element>
085                 <Element>
086                     <String>ALBANY CA 94706-1567</String>
087                     <Attributes>
088                         <Font Family="Arial" Size="12" Bold="False" Italic="False"
089                                      Underline="True" Strikeout="False" />
090                         <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
091                     </Attributes>
092                 </Element>
093             </StyledText>
094             <ShowBarcodeFor9DigitZipOnly>True</ShowBarcodeFor9DigitZipOnly>
095             <BarcodePosition>BelowAddress</BarcodePosition>
096             <LineFonts>
097                 <Font Family="Arial" Size="12" Bold="True" Italic="False"
098                              Underline="False" Strikeout="False" />
099                 <Font Family="Arial" Size="12" Bold="False" Italic="True"
100                              Underline="False" Strikeout="False" />
101                 <Font Family="Arial" Size="12" Bold="False" Italic="False"
102                              Underline="True" Strikeout="False" />
103             </LineFonts>
104         </AddressObject>
105         <Bounds X="336" Y="885" Width="4380" Height="960" />
106     </ObjectInfo>
107     <ObjectInfo>
108         <CircularTextObject>
109             <Name>CURVED-TEXT</Name>
110             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
111             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
112             <LinkedObjectName></LinkedObjectName>
113             <Rotation>Rotation0</Rotation>
114             <IsMirrored>False</IsMirrored>
115             <IsVariable>False</IsVariable>
116             <Text>Double-click to enter text</Text>
117             <Font Family="Arial" Size="12" Bold="False" Italic="False" Underline="False"
118                          Strikeout="False" />
119             <StartAngle>0</StartAngle>
120             <Mode>ArcTextTop</Mode>
121             <CircleAlignment>CenterAtTop</CircleAlignment>
122             <TextAlignment>Center</TextAlignment>
123             <VerticalAlignment>Middle</VerticalAlignment>
124         </CircularTextObject>
125         <Bounds X="4204" Y="2739" Width="1470" Height="510" />
126     </ObjectInfo>
127     <ObjectInfo>
128         <BarcodeObject>
129             <Name>Barcode</Name>
130             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
131             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
132             <LinkedObjectName>BarcodeText</LinkedObjectName>
133             <Rotation>Rotation0</Rotation>
134             <IsMirrored>False</IsMirrored>
135             <IsVariable>True</IsVariable>
136             <Text>BARCODE</Text>
137             <Type>Code128Auto</Type>
138             <Size>Medium</Size>
139             <TextPosition>Bottom</TextPosition>
140             <TextFont Family="Arial" Size="8" Bold="False" Italic="False"
141                              Underline="False" Strikeout="False" />
142             <CheckSumFont Family="Arial" Size="8" Bold="False" Italic="False"
143                                  Underline="False" Strikeout="False" />
144             <TextEmbedding>None</TextEmbedding>
145             <ECLevel>0</ECLevel>
146             <HorizontalAlignment>Center</HorizontalAlignment>
147             <QuietZonesPadding Left="0" Top="0" Right="0" Bottom="0" />
148         </BarcodeObject>
149         <Bounds X="2524" Y="150" Width="3150" Height="720" />
150     </ObjectInfo>
151     <ObjectInfo>
152         <DateTimeObject>
153             <Name>DateTime</Name>
154             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
155             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
156             <LinkedObjectName>Counter</LinkedObjectName>
157             <Rotation>Rotation0</Rotation>
158             <IsMirrored>False</IsMirrored>
159             <IsVariable>False</IsVariable>
160             <HorizontalAlignment>Left</HorizontalAlignment>
161             <VerticalAlignment>Top</VerticalAlignment>
162             <TextFitMode>ShrinkToFit</TextFitMode>
163             <UseFullFontHeight>True</UseFullFontHeight>
164             <Verticalized>False</Verticalized>
165             <DateTimeFormat>WeekdayLongMonthDayLongYear</DateTimeFormat>
166             <Font Family="Arial" Size="12" Bold="False" Italic="False" Underline="False"
167                          Strikeout="False" />
168             <PreText>Pre Text 001</PreText>
169             <PostText></PostText>
170             <IncludeTime>False</IncludeTime>
171             <Use24HourFormat>False</Use24HourFormat>
172         </DateTimeObject>
173         <Bounds X="381" Y="2982.89990234375" Width="2880" Height="165" />
174     </ObjectInfo>
175     <ObjectInfo>
176         <CounterObject>
177             <Name>Counter</Name>
178             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
179             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
180             <LinkedObjectName></LinkedObjectName>
181             <Rotation>Rotation0</Rotation>
182             <IsMirrored>False</IsMirrored>
183             <IsVariable>False</IsVariable>
184             <HorizontalAlignment>Left</HorizontalAlignment>
185             <VerticalAlignment>Top</VerticalAlignment>
186             <TextFitMode>ShrinkToFit</TextFitMode>
187             <UseFullFontHeight>True</UseFullFontHeight>
188             <Verticalized>False</Verticalized>
189             <Font Family="Arial" Size="12" Bold="False" Italic="False" Underline="False"
190                          Strikeout="False" />
191             <PreText>Pre Text </PreText>
192             <PostText></PostText>
193             <Start>1</Start>
194             <Current>1</Current>
195             <Increment>1</Increment>
196             <FormatWidth>3</FormatWidth>
197             <UseLeadingZeros>True</UseLeadingZeros>
198         </CounterObject>
199         <Bounds X="336" Y="2589" Width="2160" Height="330" />
200     </ObjectInfo>
201     <ObjectInfo>
202         <ImageObject>
203             <Name>Graphic</Name>
204             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
205             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
206             <LinkedObjectName></LinkedObjectName>
207             <Rotation>Rotation0</Rotation>
208             <IsMirrored>False</IsMirrored>
209             <IsVariable>False</IsVariable>
210             <Image>iVBORw0KGgoAAAANSUhEUgAAAYgAAAF2CAYAAAB02w9PAAAACXBIWXMAAA7DAAAOwwHHb6hkAA
507             B/w9ijrFQSeOvwgAAAABJRU5ErkJggg==</Image>
508             <ScaleMode>Uniform</ScaleMode>
509             <BorderWidth>0</BorderWidth>
510             <BorderColor Alpha="255" Red="0" Green="0" Blue="0" />
511             <HorizontalAlignment>Center</HorizontalAlignment>
512             <VerticalAlignment>Center</VerticalAlignment>
513         </ImageObject>
514         <Bounds X="4860" Y="852.900024414063" Width="691.203186035156" Height="615" />
515     </ObjectInfo>
516     <ObjectInfo>
517         <ImageObject>
518             <Name>Graphic-Clone</Name>
519             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
520             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
521             <LinkedObjectName>Graphic</LinkedObjectName>
522             <Rotation>Rotation0</Rotation>
523             <IsMirrored>False</IsMirrored>
524             <IsVariable>False</IsVariable>
525             <ImageLocation/>
526             <ScaleMode>Uniform</ScaleMode>
527             <BorderWidth>0</BorderWidth>
528             <BorderColor Alpha="255" Red="0" Green="0" Blue="0" />
529             <HorizontalAlignment>Center</HorizontalAlignment>
530             <VerticalAlignment>Center</VerticalAlignment>
531         </ImageObject>
532         <Bounds X="5091.28344726562" Y="1602.90002441406" Width="582.716552734375"
533                   Height="390" />
534     </ObjectInfo>
535     <ObjectInfo>
536         <ImageObject>
537             <Name>GoogleLogo</Name>
538             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
539             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
540             <LinkedObjectName></LinkedObjectName>
541             <Rotation>Rotation0</Rotation>
542             <IsMirrored>False</IsMirrored>
543             <IsVariable>False</IsVariable>
544             <ImageLocation>http://www.google.com/intl/en_ALL/images/logo.gif</ImageLocation>
546             <ScaleMode>Uniform</ScaleMode>
547             <BorderWidth>0</BorderWidth>
548             <BorderColor Alpha="255" Red="0" Green="0" Blue="0" />
549             <HorizontalAlignment>Center</HorizontalAlignment>
550             <VerticalAlignment>Center</VerticalAlignment>
551         </ImageObject>
552         <Bounds X="3465" Y="2292.90002441406" Width="1905.34887695313" Height="375" />
553     </ObjectInfo>
554 </DieCutLabel>

Note: lines ##211-506 contain image data and were deleted because the real image content is irrelevant to our discussion.

Common Properties

Name
011             <Name>BarcodeText</Name>

Name is one of the most important properties. It serves as a unique identifier for an object. Usually it is used to refer to the object from code that uses the DYMO Label SDK. There are no restrictions how to name the object. The name can contain any Unicode character and the name length has no limit. Still, it is a good practice to only use alpha-numeric characters and keep the name length short. It is not required to assign a name to the object – if the object is not going to be referenced it is perfectly OK to have empty name. It is also OK (but not very useful) to have two or more objects with the same name. The DYMO Label Runtime will use the first object found with a specified name. There is no guarantee which object with the same name will be used. Names are case sensitive, so “Name”, “NAME”, and “name” are treated as different names.

LinkedObjectName
128         <BarcodeObject>
129             <Name>Barcode</Name>
132             <LinkedObjectName>BarcodeText</LinkedObjectName>

LinkedObjectName property defines a link where you can content from a different object as content for the current object. For example, look at line #124. It specifies that the text for the barcode object named “Barcode” should be taken from an object named “BarcodeText”. In the DYMO Label application it is only possible to link a barcode object to a text object. But in code it is possible to link any objects by specifying LinkedObjectName property directly in the label file.

517         <ImageObject>
518             <Name>Graphic-Clone</Name>
521             <LinkedObjectName>Graphic</LinkedObjectName>

Line #432 directs the Runtime to get image data for the graphic object named “Graphic-Clone” from the graphic object named “Graphic”. As a result the label file contains only one copy of the image data: that significantly decreases its size.

Text Object

The Text object displays a block of text.

StyledText

The StyledText property specifies the object’s text and text style(s). the following attributes are available on a per character basis:

  • font name
  • font size
  • font style: bold, italic, underline, strikeout
  • color (character foreground color)
066             <StyledText>
067                 <Element>
068                     <String>DYMO
069 </String>
070                     <Attributes>
071                         <Font Family="Arial" Size="12" Bold="True" Italic="False"
072                                      Underline="False" Strikeout="False" />
073                         <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
074                     </Attributes>
075                 </Element>
076                 <Element>
077                     <String>828 SAN PABLO AVE
078 </String>
079                     <Attributes>
080                         <Font Family="Arial" Size="12" Bold="False" Italic="True"
081                                      Underline="False" Strikeout="False" />
082                         <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
083                     </Attributes>
084                 </Element>
085                 <Element>
086                     <String>ALBANY CA 94706-1567</String>
087                     <Attributes>
088                         <Font Family="Arial" Size="12" Bold="False" Italic="False"
089                                      Underline="True" Strikeout="False" />
090                         <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
091                     </Attributes>
092                 </Element>
093             </StyledText>

The example above specifies that the text contains three lines, with each line formatted differently. The Element tag specifies a range of characters with the same formatting. The String tag specifies text/characters, while the Attributes tag specifies text formatting attributes.

Adding Line Breaks

To include a new line separator just put a new line/carriage return symbol(s) into the String tag. The text specified by above StyledText will be displayed like this:

DYMO
828 SAN PABLO AVE
ALBANY CA 94706-1567
UseFullFontHeight
021             <UseFullFontHeight>False</UseFullFontHeight>

The UseFullFontHeight property can be used to increase font size to the maximum possible size when shrink-to-fit or auto-fit are applied to the object. When UseFullFontHeight is True (the default) the line height is calculated based on the font metrics. So, the height is the same regardless of the text content. When UseFullFontHeight is False the line height is calculated based on the height of each character’s “black box” (an area where glyph outline is drawn). Because usually a character’s “black box” height is smaller than its font height a bigger font size can be used to draw text.  This property might be useful in scenarios where printable area is limited but you want to make the text as large as possible, e.g. for tape labels. For example, below is the same label with only UseFullFontHeight property changed.

Address Object

The Address object is similar to the Text object. It is used to display address information. If the address is a valid US address then the Address object can also display and Intelligent Mail barcode.

Barcode Object

The Barcode object is used to display barcodes on the label.

Image Object

The Image object is used to display bitmap images on the label.

Image

The Image property specifies image data. Image data is encoded using base64 encoding and the image data itself is in PNG format.

ImageLocation
544             <ImageLocation>http://www.google.com/intl/en_ALL/images/logo.gif</ImageLocation>

The ImageLocation property is another way to specify image data. In this case image data is not stored inside the label but is loaded on demand from the specified URI. The URI can be a path to a local file, e.g. “C:UsersUserNameDocumentsMyImage.png” or “http://”, “https://” or “file://” URLs. The URI can point not only to PNG files but to any image file type supported by the DYMO Label Software (BMP, JPG, GIF, TIF, PNG). ImageLocation can be used in scenario when there is a set of label files that should share same image, e.g. a company logo.

Note: the specified URI will be accessed on demand, basically when a label is being printed or previewed. If the URI specifies a remote resource there can be a delay or even an exception if the URI is inaccessible.

Date and Time Object

The Date and Time object is a special kind of the Text object designed to display current date and/or time.

Counter Object

The Counter object is a special kind of the Text object designed to print auto-incremented numbers. Every time the label is printed the Current property of the Counter object is updated based on the Increment property. So, the next time the label is printed the new updated value for the Counter will be used.

Note: To preserve the value Current property between an application runs (so, the next time application runs the Counter continues from its last value), the label should be saved when the application exits. To avoid unnecessary label modifications we do not recommend to use Counter objects in SDK applications. Use Text object instead and format its text according to the application’s logic. The “current” counter value can be fetched for example from a database or from a web-server in a way the application fully controls and understands.

Shape Object

The Shape object displays simple geometric figures like rectangles and ellipses as well as vertical and horizontal lines.

Circular Text Object

The Circular Text object display text on a curve. Usually it is used on CD/DVD labels.