Jul 282011
 

We are proud to announce the release of DYMO Label software version 8.3.1 for Mac. This release adds support for Mac OS X 10.7 (Lion). This release is mandatory, earlier releases will NOT work on 10.7. The release is available from here.

Here is some technical information describing the underlying changes in DLS and the printer drivers:

  1. In 10.7 Apple discontinued the Tioga printer driver architecture which had been in use since the initial release of Mac OS X 10.0. The only driver architecture supported in 10.7 is CUPS. DLS prior to 8.3.1. includes Tioga drivers, while DLS 8.3.1 includes both Tioga (for Mac OS X 10.5 and 10.6) and CUPS (for 10.7) drivers.
  2. Apple discontinued WebKit-style Safari plugins. This does not affect DLS itself, but does affect DYMO SDK users. There are two scenarios where SDK users are affected:
    • If a SDK web application uses DYMO Safari plugin directly. In this case the only solution is to switch to DYMO Label Framework JavaScript library. This should be done NOT by DYMO but by the application developer.
    • If a SDK web application uses DYMO Label Framework JavaScript library. In this case the application developer should update their app to use the just released version 1.2  of the library.

So, if you are a developer integrating DYMO printers on Mac platform, here are the steps to make sure your application works on 10.7:

  • tell users to update to DLS 8.3.1
  • if your application is a web application:
    • switch code from using DYMO Safari plugin to using DYMO Label Framework JavaScript Library
    • make sure to use the latest version of the library available (1.2)

Update:

If you are experiencing problems with installing or using the printer try these steps:

  • Unplug printer’s USB cable
  • (Re)Install DLS 8.3.1
  • Reboot Mac
  • Replug printer’s USB cable

If the problem persists, please contact DYMO Tech Support, http://sites.dymo.com/Support/Pages/ContactForm.aspx.

Update Regarding LabelWriter 3xx Series Printers on Mac OS 10.7

DYMO LabelWriter 3xx series printers are not supported on Mac OS X 10.7.  We dropped support for technical reasons – starting with 10.7 the Tioga driver system that DYMO drivers have always used is no longer supported by Apple. So we switched to CUPS drivers, and these do not work with the older 300 series hardware.

Please contact DYMO customer service for assistance – 1-877-724-8324. Sorry for the inconvenience.

 

Jul 192011
 

We are proud to announce a BETA release of the DYMO Label Mobile SDK for Android. The DYMO Label Mobile SDK for Android is a complete toolset containing libraries, documentation, and samples that make it easy to add label printing to any Android 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 Android.

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

It is essentially the same as the architecture of DYMO Label Mobile SDK for iOS.

DYMOLabelMobileSDKforAndroid

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 an Android 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 JavaDoc format.
  • libs – jars to be included with an application.
  • Samples – sample apps.

Samples were tested with Eclipse Helios and Android SDK r11. The SDK supports Android API level 8 (2.2) and later.

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.

Note: This version of DYMO Label Proxy contains enchantments and bug fixes over the version released for Web and iOS SDKs. To be able to use with Android SDK you must update it to the latest version.

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 the Android 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 the Android 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 those demonstrate most of the functionality the SDK 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 tasks usually have to be performed:

  1. framework initialization
  2. discover a printer to print the label on
  3. load label layout
  4. set label data
  5. perform actual printing
  6. 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.

Framework Initialization

All SDK classes and interfaces are accessed from a top label Framework object. A constructor of Framework class accept Context as a parameter. The context might be either an activity/service or be the application context. The Framework instance can be created inside activity’s onCreate()  handler. Note, that Framework initialization might take some time; so if the activity ca be frequently recreated or if the Framework should be accessed from different activities, it make sense to use some sort of singleton to make sure only one instance of the Framework exists.

import android.content.Context;
import com.dymo.label.framework.Framework;

class PrintLabelManager
{
    static private PrintLabelManager instance_;

    static PrintLabelManager instance(Context context)
    {
        if (instance_ == null)
            instance_ = new PrintLabelManager(context);

        return instance_;
    }

    private Framework framework_;

    private PrintLabelManager(Context context)
    {
        framework_ = new Framework(context.getApplicationContext());
    }

    Framework getFramework()
    {
        return framework_;
    }
}
import com.dymo.label.framework.Framework;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends Activity
{
    private Framework framework_; 

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        setupFramework();
    }

    private void setupFramework()
    {
        framework_ = PrintLabelManager.instance(this).getFramework();
    }
}

 

Discover Printers

Before something can be printed it is necessary to know on which printer it will be printed. Printer discovery is an asynchronous operation. The reason is that it requires network communications and various delays and timeouts might happen during the process. To handle async notifications of the discovering progress a PrintersListener object should be assigned to the Framework object. PrintersListener defines two methods, one called when a new printer or printers are discovered, another when some sort of failure happened. To assign the listener use setPrintersListeners() method. To start discovering call startRefreshPrinters() method. This method is asynchronous and returns almost immediately. When a printer is found the listener’s method newPrintersFound() will be called. The list of available printers can be obtained by the Framework.getPrinters() or by  NewPrintersFoundEvent.getPrinters() methods. If the Proxy service could not be contacted during the discovering, e.g. it went offline, the printerLookupFailure() method will be called. Here is a code snippet to demonstrate that:

public class MainActivity extends Activity
{
    private void setupFramework()
    {
        framework_ = PrintLabelManager.instance(this).getFramework();
        framework_.setPrintersListener(new PrintersListener()
        {
            @Override
            public void printerLookupFailure(PrinterLookupFailureEvent event)
            {
                final String message = String.format("Unable to contact '%s' (%s)", event.getPrinterUri(),
                    event.getLocation());
                Log.e("Print Label", message);

                MainActivity.this.runOnUiThread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
                    }
                });
            }

            @Override
            public void newPrintersFound(final NewPrintersFoundEvent event)
            {
                runOnUiThread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        // output all discovered printers to the log
                        Iterable<Printer> printers = event.getPrinters();
                        for (Printer p : printers)
                            Log.i("Print Label", p.getName());
                    }
                });
            }
        });
    }
}

Please note, the listener’s methods are called from a non-UI thread. So, if you need to perform any UI related operations that must be called from the UI thread, don’t forget to use Activity.runOnUiThread() or View.postDelayed() methods.

Unlike iOS API there is no way to automatically determine when printer discovering should be stopped. It is important to stop it manually by calling stopRefreshPrinters()method. If stopRefreshPrinters() is not called this may lead to an extensive battery drain (underneath implementation uses Bonjour, that uses multicast network requests, and the requests use a lot of power). Call stopRefreshPrinters() after a timeout and when the activity is been stopped.

    // refreshes printers and update currently selected one
    private void refreshPrinters()
    {

        framework_.startRefreshPrinters();

        // / stop refreshing in 5 seconds
        labelContentEditText_.postDelayed(new CheckedRunnable()
        {
            @Override
            public void run2()
            {
                Log.i("PrintLabel", "----------- Stopping printers lookup after 5 seconds");
                framework_.stopRefreshPrinters();

                // usually this is called from newPrintersFound() handler
                // newPrintersFound will not be called if all printers are gone 
                updateCurrentPrinter(framework_.getPrinters());
            }
        }, 5000);
    }

 

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 one of Framework.openLabel() methods. Usually we will load the layout from an asset. Something like this:

    Label label = framework_.openLabel(this, "Address.label");

This assumes, that the Android project  has “Address.label” file in the assets folder.

image

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 Label.setObjectText(String objectName, String objectText) method to set text data of Address, Text, Barcode, and CircularText objects. Use setObjectImage(String objectName, Bitmap image) to set image data for Image objects.

The second method is more universal, and it 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 LabelSet interface. Use addRecord method to add a record into a label set. To set object data in the record, use LabelSetRecord 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). To create a LabelSet use Framework.createLabelSet() method.

public class MainActivity extends Activity
{
    void printWithLabelSet(Printer printer)
    {
        // open a label
        Label label = framework_.openLabel(this, "Address.label");

        // create a label set
        LabelSet labelSet = framework_.createLabelSet();

        // label #1
        LabelSetRecord record = labelSet.addRecord();
        record.setText("TEXT", "6x7=42");

        //label #2
        record = labelSet.addRecord();
        record.setTextMarkup("TEXT", "font family='Arial' size='36'>6x7=<b>42</b></font>");

        // print label
        label.print(printer, labelSet);
    }
}

Actual Printing

To start printing call Label.print() methods. There are several  overrides, the most generic one is print(Printer printer, PrintParams printParams, LabelSet labelSet)

We have to provide three parameters:

  • printer – printer to print the label on. The printer instance can be obtained from a list of available printers populated during printer discovering process described above. 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.

This method call is asynchronous as well. Right after all necessary data are assembled the method returns. Printing progress monitoring can be done using PrintJob instance returned by the print() method (see below).

Print Progress Monitoring

PrintJob instance returned by the Label.print() 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. getStatus() is an async call as well; when the status data has been retrieved from the printer, the PrintJobListener method statusReceived(PrintJobStatusReceivedEvent event) is called. Here is an example:

public class MainActivity extends Activity
{
    private void printButtonClick()
    {
        try
        {
            //... open label, assemble print data, etc 

            // print with default parameters
            final PrintJob job = label.print(currentPrinter);

            final Runnable getStatus = new CheckedRunnable()
            {
                @Override
                public void run2()
                {
                    job.getStatus();
                }
            };

            // an executor service to ask for the status in the background 
            final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

            // set the listener to be called when the status received
            job.setOnStatusListener(new PrintJobListener()
            {

                @Override
                public void statusReceived(PrintJobStatusReceivedEvent event)
                {
                    try
                    {
                        final PrintJobStatus jobStatus = event.getPrintJobStatus();
                        final JobStatusId statusId = jobStatus.getStatusId();
                        final String statusMessage = jobStatus.getStatusMessage();
                        Log.i("job status", statusMessage);

                        switch (statusId)
                        {
                        case ProcessingError:
                            // bad - unrecoverable printing error, do not ask status again
                            break;

                        case Finished:
                            // done - printing is done, do not ask status again
                            break;

                        default:
                            // OK - printing is in progress, ask the status after one second delay
                            scheduler.schedule(getStatus, 1, TimeUnit.SECONDS);
                            break;
                        }

                    }
                    catch (Exception e)
                    {
                        handleException(e);
                    }
                }
            });

            // schedule first status request in a second
            scheduler.schedule(getStatus, 1, TimeUnit.SECONDS);

        } catch (Exception e)
        {
            handleException(e);
        }
    }
}

Setup an Eclipse Project to Use SDK

Add Jars into a classpath

To use classes from the SDK two jars must be added to a class path (both can be found in the libs folder). The first one is DymoLabelFramework.jar – it contains the SDK classes. The second one is jmdns.jar. It contains Bonjour implementation used for printer discovering. JmDNS is an open source implementation of Bonjour/multicast-DNS for Java. The version bundled with the SDK is JmDNS 3.4.0 and can be downloaded from http://sourceforge.net/projects/jmdns/

Check Android SDK Target Version

The DymoLabelFramework internally uses some classes available only starting from Android SDK API level 8 (Android 2.2, Froyo). Make sure the project targets Android 2.2 or later.

Set Proper Permissions

The SDK requires two permissions to be set in AndroidManifest.xml. The first is android.permission.INTERNET, so the SDK be communicate with the Proxy service. The second is android.permission.CHANGE_WIFI_MULTICAST_STATE that is required to be able to use Bonjour for printer discovering.

Note: samples applications use another one, android.permission.ACCESS_NETWORK_STATE. It is uses to determine Wi-Fi status and ask a user to turn Wi-Fi on if necessary because otherwise no printers can be discovered. It is strictly to be a little bit more user-friendly, and is NOT required.

Setup an Ant Build Script to Use SDK

Use Android SDK tools to generate/update initial build.xml file.

Check Android SDK target version in default.properties file, so target=android-8 (or later).

Specify path where the jars are located in build.properties by using jar.libs.dir  property. E.g. for the sample projects jar.libs.dir=../../libs.

Note: to compile sample projects from the command line you have to specify path to the Android SDK in the local.properties file by using sdk.dir property.

Conclusion

DYMO Label Mobile SDK for Android provides a simple way to add label printing capabilities to any Android 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.

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.

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.

Mar 232010
 

DYMO Label Software v.8 has a lot of compelling features from a developer’s point of view. One important change is the new label file format.

In most cases knowing the file format is not necessary because DYMO Label Software (DLS) can be used to design a label visually. However, there are advanced scenarios where knowing the file format can benefit applications that use the DYMO Label SDK. Some of these advanced scenarios are:

  • Using features not available through the DYMO Label Software UI
  • Using features not available through the DYMO Label SDK
  • Generating label files on the fly
  • Creating label files for custom labels

In a series of blog posts we will describe the internals of label file format.

In DYMO Label v.8 we made several improvements to make using label files easier:

  • The label file format is cross-platform; both Windows and Mac versions of DYMO Label Software use the same file format and shared the same built-in layouts. Because file format is the same it is easier to create applications target different platform, e.g. web-based applications.
  • The label file format is XML-based. So the content of a label can be examined by just looking at the label’s XML or by using any XML tool. Also, this allows using any XML API to manipulate label content directly in case the corresponding functionality is unavailable in the DYMO Label SDK or direct manipulation is more convenient in this particular case.
  • The label file format is the same for die-cut labels such as Address or Shipping and for tape/continuous labels such as 12-mm or 19-mm tapes. Strictly speaking the format is not the same but both formats shared some common parts, so it is still easier to use.

So, let look at a sample label that in DYMO Label Software looks like

01 xml version="1.0" encoding="utf-8"?>
02 <DieCutLabel Version="8.0" Units="twips">
03     <PaperOrientation>Landscape</PaperOrientation>
04     <Id>Address</Id>
05     <PaperName>30252 Address</PaperName>
06     <DrawCommands>
07         <RoundRectangle X="0" Y="0" Width="1581" Height="5040" Rx="270" Ry="270" />
08     </DrawCommands>
09     <ObjectInfo>
10         <TextObject>
11             <Name>Text</Name>
12             <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
13             <BackColor Alpha="0" Red="255" Green="255" Blue="255" />
14             <LinkedObjectName></LinkedObjectName>
15             <Rotation>Rotation0</Rotation>
16             <IsMirrored>False</IsMirrored>
17             <IsVariable>True</IsVariable>
18             <HorizontalAlignment>Center</HorizontalAlignment>
19             <VerticalAlignment>Middle</VerticalAlignment>
20             <TextFitMode>ShrinkToFit</TextFitMode>
21             <UseFullFontHeight>True</UseFullFontHeight>
22             <Verticalized>False</Verticalized>
23             <StyledText>
24                 <Element>
25                     <String>Click here to enter text</String>
26                     <Attributes>
27                         <Font Family="Arial" Size="20" Bold="False" Italic="False" Underline="False" Strikeout="False" />
28                         <ForeColor Alpha="255" Red="0" Green="0" Blue="0" />
29                     </Attributes>
30                 </Element>
31             </StyledText>
32         </TextObject>
33         <Bounds X="331" Y="150" Width="4560" Height="1343" />
34     </ObjectInfo>
35 </DieCutLabel>
02 <DieCutLabel Version="8.0" Units="twips">

Line #2 specifies label type as well as version and measurement units. The label type is DieCutLabel, one of two label types currently supported by DYMO Label. DieCutLabel as well as other label type ContinuousLabel will be described with details in a separate blog post. Version=”8.0″ specifies file format version. This version is independent from the version of DYMO Label Software. Currently the only valid version for label file is “8.0” for all versions of DYMO Label Software v.8 up to the latest version, 8.2.1.913. Units=”twips” specifies measurement units used in the file for values like object position or border width. The only supported units for now are “twips” where 1 ‘twip’ is 1/1440 of inch. Other units might be added in future version of DYMO Label. “Twips” are used basically because of “historical” reasons; previous versions of DYMO Label Software used this unit.

03     <PaperOrientation>Landscape</PaperOrientation>
05     <PaperName>30252 Address</PaperName>

Lines #3, #5 specify paper to use to print the label. In this case it is “30352 Address” paper in Landscape orientation. Paper name should be a paper defined by printer driver.

04     <Id>Address</Id>

Line #4 specifies a label ID – a unique identifier for a label type. Basically this ID is used by DYMO Label Software to lookup additional business-related information about the label like available packaging, buy links, etc. It is OK to have ID to be set to empty string if the label is not going to be used from DYMO Label Software.

06     <DrawCommands>
07         <RoundRectangle X="0" Y="0" Width="1581" Height="5040" Rx="270" Ry="270" />
08     </DrawCommands>

Lines ##6-8 specify commands used to draw label’s shape, in this case a rounded rectangle. Again, if a label is created on the fly and intended to be printed only, the commands can be omitted.

Lines ## 9-34 specify position and properties for the only text object on the label. More information about objects will be provided in following posts.

The complete xml schema for the label file format is available on http://www.labelwriter.com/software/dls/sdk/LabelFile.xsd