TrophyCustomer's Canvas is honored with a 2020 InterTech Technology Award! Learn more 

Variable Data Printing

Some products may require the personalization of every printed copy, for example, when you are preparing letters and pre-addressed envelopes for mass mailings (a so-called mail merge), creating wedding invitations, or printing business cards in a corporate style.

The most important benefit of variable data printing (VDP) is getting better connected with your customers by trying to make them feel that you remember every customer personally. The mail merge, for example, makes a letter more personal and helps to increase the response rate and decrease the response time. Making hundreds of similar personalized files manually would be very time-consuming. The technique of variable data printing solves this problem by allowing the creation of one template with placeholders and populating them from a certain data source for each personal copy.

VDP in Customer's Canvas

To personalize a print product by using Customer's Canvas, you can implement the following workflow:

  1. Design a template in either Photoshop, InDesign, or Customer's Canvas itself.
  2. Provide data for personalization and preview the design using this data.
  3. Render print files using the provided data.

Now, let's look at each step in more detail.

Defining Variable Items in Templates

You or your users can create templates with variable fields. Customer's Canvas considers the following elements as variable fields:

  • Placeholders.
  • Elements with a <VAR> marker.
  • Elements with the isVariable property set to true.
  • Parts of text elements surrounded by double curly braces, for example, {{MyVariable}}.

Let's look at several scenarios of preparing templates.

The Designer Creates a Template

A designer creates templates in Photoshop or InDesign. To enable some variable text, you can either use an in-string placeholder or add the <VAR> marker to its layer name. If you assume that the user may want to disable a variable field or surround it with some text, just place the field name with double curly braces. For example: Dear {{FirstName}}, ....

If the structure of the fields should not be changed, you can lock such elements by adding the <LC> marker.

The User Creates a Template with Predefined Fields

Let's assume that your system has a limited number of variable fields and you want the user to create designs by using only these fields. In this case, you can configure the Toolbox so that your users add the predefined fields by clicking specific buttons.

In the following example, the widgets section of clientConfig.json enables two custom buttons in the Toolbox: CardID and Logo.

json
{
    "widgets": {
        "Toolbox": {
            "buttons": [{
                "action": "CustomText",
                "iconClass": "cc-icon-add-text",
                "itemConfig": {
                    "name": "CardID",
                    "text": "1234  ABCD  5678",
                    "isVariable": true,
                    "textPermissions": {
                        "allowChangeText": false
                    }
                }
            },
            {  
                "action": "CustomImage",
                "iconClass": "cc-icon-add-image",
                "itemConfig": {
                    "name": "Logo",
                    "imageUrl": "http://example.com/logo.png",
                    "isVariable": true,  
                    "location": {
                        "originX": "left", "originY": "top",
                        "x": 100, "y": 300
                    }
                }
            }]
        }
    }
}

In this example, we set the isVariable property to true to create variable fields.

The User Creates an Arbitrary Template

In some scenarios, we don't know in advance what fields the user wants to add. Here, you need to instruct them to use regular text elements and enter text with double curly braces. As noted above, such variable fields can be surrounded by other text and even inserted into rich formatted text. Bear in mind that the variable field must have a single style. For example, if part of the field is in italics and another part is bold, Customer's Canvas will not be able to recognize that it is a variable field.

Although your users can easily add text containing double curly braces in the editor, we recommend defining a custom button that creates such a text element, for example:

{
    "action": "CustomText",
    "iconClass": "cc-icon-add-text",
    "itemConfig": {
        "name": "variableField",
        "text": "{{CustomField}}",
        "isVariable": true
    }
}

When you or your users create templates in the Design Editor, these templates are saved as state files, which maintain all information on the product, including images, user changes, fonts, colors, etc. State files are created every time you call Editor.saveProduct() to save a design, or Editor.finishProductDesign() to render a hi-res output. These state files will be used in the next steps.

Providing Data for Personalization

To provide data for personalization, you must send a special REST API request to Customer's Canvas, in which this data will be passed in the JSON format. We will discuss this in more detail later.

Clients usually prefer using XLS or CSV files. In your code, you can convert such files to JSON, for example, by using SheetJS or a similar library. If you use the UI Framework, it already has a dataset widget that allows you to import and visualize these files in the browser.

It is important to instruct your clients in on how you expect their files - which columns should be included in the document and what they should be called. The easiest way is to make the column names (i.e. the first row of their table) the same as the variable field names.

We recommend that you allow users to download an empty Excel file with all the required columns, for example, as implemented in our VDP demo. You can easily do this when you know what variable fields are in the design, but when users define the variable fields themselves, you need to identify which fields the design has. To perform this, you can retrieve a list of variable fields when users create their designs in the editor or after they save their designs on your server.

Another scenario is when users do not provide data but it is requested in a database such as a CRM system. In this case, you also need to convert this data to a compatible JSON structure and send it within a personalization request.

Rendering Personalized Previews

To get a design preview with a certain data set, you can use the Personalization Web API. To render a preview, send the following request to the endpoint ~/api/Preview/GeneratePreview.

JavaScript
let inputData = {
    itemsData: {
        "PersonName": { text: "John Wood" }
    },
    productDefinitions: [{
        surfaces: ["invitation"]
    }],
    previewOptions: { maxWidth: 500, maxHeight: 500 }
};
$.ajax({
    url: "https://localhost:44300/api/Preview/GeneratePreview",
    type: "POST",
    headers: { "X-CustomersCanvasAPIKey": "UniqueAPIKey" },
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    data: JSON.stringify(inputData)
});

When your users upload a data set, you can visualize it as a table. If the user selects a row in this table, you can take the cells of this row as variable fields and send them in this request. As an alternative, you can take an arbitrary row from the data set, or even generate it automatically, without binding it to a nested dataset.

Rendering Personalized Print Files

To get a personalized print file, you need to send a similar request to the endpoint ~/api/HiRes/GenerateHiRes, for example:

JavaScript
let inputData = {
    dataSet: {
        surfacesData: [{
            surfaceBinding: {
                surfaceIndexes: [0]
            },
            data: [
                {
                    "PersonName": { text: "John Wood" }
                },
                {
                    "PersonName": { text: "Cristopher Bennet" }
                }
            ]
        }]
    },
    productDefinitions: [{
        surfaces: ["invitation"]
    }]
};
$.ajax({
    url: "https://localhost:44300/api/HiRes/GenerateHiRes",
    type: "POST",
    headers: { "X-CustomersCanvasAPIKey": "UniqueAPIKey" },
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    data: JSON.stringify(inputData)
});

As a result, this data will be saved into a state file, and you will get a URL that links to the personalized design. When you try to download a print file from this link, Customer's Canvas will create a multi-page PDF, where each page (or several pages) corresponds to a single row from the data set.

Note that generating high-resolution print files consumes time and resources. To prevent Customer's Canvas from abusing your server's resources, we recommend either:

  • Setting reasonable limits on the number of rows.
  • Running the rendering on a separate server.

Retrieving a List of Variable Items

When you don't know in advance which fields should be personalized, you need to retrieve their names through either the IFrame API or Web API.

The IFrame API allows you to get the item list at runtime when the Design Editor is open. You can list design elements with the isVariable property set to true and in-string and interpolation placeholders as follows:

JavaScript
let editor = null;
CustomersCanvas.IframeApi.loadEditor(iframe, product)
    .then(function(e){
        editor = e;
    });

async function getVariableItems() {
    editor.getProduct()
        // If we have successfully obtained the product.
        .then(function (product) {
            return product.getVariableItems();
        })
        // If we have successfully obtained an item list.
        .then(function (variableItems) {
            if (variableItems.length)
                console.log(variableItems);
            else
                console.log("This product contains no variable items.");
        });
}

The Web API allows you to get the list of variable items from saved state files. The following request will return an array of variable items.

JavaScript
const userId = "JohnWood";
const stateId = "1f190673-226d-4c31-87b4-0140805cc445";
const url = `https://localhost:44300/api/users/${userId}/states/${stateId}/variables`;

(async() => {
    const response = await fetch(url, {
    method: "GET",
headers: {
    "X-CustomersCanvasAPIKey": "UniqueSecurityKey",
    "Content-Type": "application/json"
}
});

console.log(await response.json());
})()

Rendering Personalized Designs with the Web API

Customer's Canvas provides a Web API to personalize print products and render both proof images and hi-res outputs without loading the editor. In HTTP requests, you can pass new content and properties of variable items, a product definition, and the user identifier under which the links to proofs and hi-res outputs will be saved. You can also specify product themes and pass parameters of proof and hi-res files.

You can use the following endpoints to generate preview images and print files, correspondingly:

  • ~/api/Preview/GeneratePreview
  • ~/api/HiRes/GenerateHiRes

For a detailed description of these requests, you can refer to the Personalized Rendering topic. Now, let's see how you can use itemsData and dataSet in the request payload for personalization.

Using ItemsData

To personalize a single product copy, you can pass new content and properties of variable items in itemsData. For example, you can specify text fields and images as follows:

JavaScript
let itemsData = {
    "Name": {
        text: "John Wood",
        color: "rgb(255, 0, 0)"
    },
    "Logo": {
        image: "http://example.com/logo.svg",
        opacity: 0.8,
        size: {
            width: 100,
            height: 100
        }
    }
};

The following example illustrates how you can use itemsData to personalize state files.

JavaScript
// Set your link to the HiRes controller. This Web API works through the HTTPS protocol.
const controllerURL = "https://localhost:44300/api/HiRes/GenerateHiRes";

// Personalize the Name text field.
async function personalize() {

    let inputData = {
        itemsData: {
            "Name": {
                text: "Cristopher Bennet"
            }
        },
        productDefinitions: ["3f0a09cf-ebdc-41d3-9cea-b6782e822ab2"],
        userId: "default"
    };

    // Make the request to personalize the product.
    $.ajax({
        url: controllerURL,
        type: "POST",
        headers: { "X-CustomersCanvasAPIKey": apiKey },
        dataType: "json",
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify(inputData)
    }).
    fail(function (d) { console.log(d.statusText); }).
    done(function (d) {
        // List URLs that link to hi-res outputs.
        d.forEach(function (links) {
            console.log(links);
        });
    });
}

To personalize a number of products in a single request, you can pass a number of templates in the productDefinitions array.

By default, the HiRes controller renders products to a single multipage PDF file. To enable the output to separate files, you need to configure hiResOptions. You can pass these options to the rendering request as follows:

JavaScript
let inputData = {
    hiResOptions: {
        renderingConfig:{
            hiResOutputToSeparateFiles: true
        }
    },
    itemsData: {
    ...
};

Using DataSet

DataSet provides a more flexible approach than itemsData and allows you to use products as templates. First, you can pass not only single but also multiple itemsData instances in a data array. Another advantage is the surfaceBinding property that defines pages to be rendered. This property creates a new product based on the template specified in productDefinitions, the listed pages, and the personalization data.

For example, you can render three personalized copies of a Badge.idml template as follows:

JavaScript
let inputData = {
    "productDefinitions": [{
        "surfaces": { "file": "Badge" }
    }],
    "dataSet": {
        "surfacesData": [{
            "surfaceBinding": {
                "surfaceIndexes": [0]
            },
            "data": [{
                "Name": {
                    "text": "John Wood"
                }
            },
            {
                "Name": {
                    "text": "Cristopher Bennet"
                }
            },
            {
                "Name": {
                    "text": "Alex Ford"
                }
            }
            ]
        }]
    },
    ...
};

Here, we take the first page from Badge.idml and create a three-page PDF file. The pages of this file will contain personalized copies for John Wood, Cristopher Bennet, and Alex Ford.

Personalization Sample

You can download a sample and extract the vdp.html file into the root folder of your Design Editor and clientConfig.json into the \Configuration\ subfolder.

To make this sample work, in the \Configuration\AppSettings.config file, specify ApiSecurityKey with the same value as you use in vdp.html.

This sample allows you to open an empty product and add predefined text fields from the Toolbox. The personalization procedure includes the following steps:

  1. Saving the product to a state file.
  2. Getting a variable item list from this product.
  3. Personalizing variable items through the Web API.

As a result, you will get URLs that link to proof images.

You can also contact our support team to get a VDP demo package based on this Web API.

Rendering Personalized Designs in the Design Atoms Framework

Using the Web API is the main but not the only way to personalize print products in the Customer's Canvas SDK.

When rendering products in the Design Atoms Framework, you can also enable VDP and use ItemsData and DataSet for personalization.

The following example illustrates how you can render a two-page product on the back end.

C#
// Define two variable data sets in a list of ItemsData.
var surfaceData = new List<ItemsData> { new ItemsData
    {
        RootItems = new ItemDataCollection
        {
            {"Name", new ItemData {Text = "Cristopher Bennet"}},
            {"Photo", new ItemData {Image = "https://example.com/images/c.bennet.jpg"}}
        }
    }, new ItemsData
    {
        RootItems = new ItemDataCollection
        {
            {"Name", new ItemData {Text = "John Wood"}},
            {"Photo", new ItemData {Image = "https://example.com/images/j.wood.jpg"}}
        }
    }
};

// Specify surfaces that need to be rendered with variable data sets.
var vdpData = new DataSet()
{
    SurfacesData = new SurfaceDataCollection()
};

// Bind the first and second product pages with the two data sets.
// As a result, it will render two pages for each of the data sets.
vdpData.SurfacesData.Add(new SurfaceData {
            SurfaceBinding = new SurfaceBinding(new[] { 0, 1 }),
            Data = surfaceData
});

// Render personalized pages to the file cache.
var renderer = Injector.Instance.Resolve<IProductRenderer>();
var cacheIDs = renderer.RenderHiRes(product, vdpData, RenderingConfig.GetDefault());

For more details, see the topic about Rendering Products in the Design Atoms Framework.

Using a VDP Demo

You can also try our VDP demo and use it for your VDP tasks. This front-end application is based on the UI Framework and uses the Web API approach to personalize a badge. To get this demo package, contact our support team.

This package contains the following files:

The VDP demo package.

Just copy index.html and vdp.json to your webserver. No matter what server you are using - IIS, Nginx, Apache, Node.js, or others, it will work just fine. To run this demo, you need a working instance of the Design Editor. Copy the id-card-design.psd and id-card-mockup.psd files to the asset folders of your Design Editor: ..\assets\designs and ..\assets\mockups, correspondingly.

In a real-life situation, you will need to integrate the content of index.html to your page and change stub settings. For example, you must specify the URL of your Design Editor in urlCC. You can refer to comments in this file for more details.

In this demo, the UI Framework helps you build the user interface and implement the following steps:

  1. Creating a design (from scratch or based on a PSD or IDML template).

    Creating a design in the Variable data printing tool.

  2. Uploading user data as an XLS file or typing it in on the page.

    Editing their data in the Variable data printing tool.

  3. Rendering the personalized design and obtaining links to printable PDF files.

    Getting a state ID in the Variable data printing tool.

Note

If you need a desktop solution for variable data printing, check out our VDP Tool.

See Also

Manual

Downloads