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.