Aurigma Customer's Canvas SDK - Design Editor IFrame API
Design Editor is a primary component of Customer's Canvas SDK - an application which is used to personalize designs of various print products - from commercial printing and specialties through wide-format printing products. If it inserted to your website through the <iframe>
element.
This module is used to manage the Design Editor through the <iframe>
- load a template, manipulate object model, save and render the result, handle various events.
Getting started
Prerequisites
Before you use this module, you need to install the Design Editor application on a server. It can be either installed on your own server or you can use a hosted version of Customer's Canvas. If you neither have a copy of application nor the account on our servers, contact Aurigma team to request it.
Classic approach vs using this module
It is possible to connect IFrame API library to your website in two ways:
- Classic approach - using the bundled version of IFrame API.
- Modern approach - by importing this module to your frontend app based on Angular/React/Vue/TypeScript/etc.
The first approach is preferable if you stick to the old-school way to develop websites - edit HTML markup and vanilla JS (or maybe with a help of jQuery) directly, without any toolchain to compile the frontend code - no frameworks, WebPack, TypeScript, etc.
In this case, you need to add the following line to the <head>
of a page:
<script id="CcIframeApiScript" src="http://<your_CC_base_URL>/Resources/Generated/IframeApi.js"></script>
Once you do it, you will get the CustomersCanvas.IframeApi
namespace which contains all functions and classes to work with Design Editor. The basic usage looks like this:
<body>
...
<iframe id="editor-iframe">
</iframe>
</body>
...
<script>
document.addEventListener('DOMContentLoaded', async () => {
var product = {...};
var config = {...};
var editor = await CustomersCanvas.IframeApi.loadEditor(
document.getElementById("editor-iframe"), product, config);
...
}
</script>
If you feel that this way to work with the API works fine for you, check out this documentation:
https://customerscanvas.com/dev/editors/iframe-api/reference/code-example.html
Otherwise, stay tuned. The remaining document explains how to add IFrame API and work with it using the second approach, through the modules.
Install design-editor-iframe package
Just run
npm install @aurigma/design-editor-iframe
Once it finishes, you may import its classes and functions and use them in your project.
Load design editor to a page
Let's see how to write the equivalent to the classic approach code snippet you can find above.
First, somewhere in your HTML code, add the <iframe>
element where you are going to load the editor. Let's assume that you add the id
equal to editor-iframe
.
Now, let's use the EditorBuilder
class to create the editor, as follows:
import { EditorBuilder } from "@aurigma/design-editor-iframe/EditorBuilder";
const product = {...};
const config = {...};
const editor = EditorBuilder
.for("https://<your-editor-app-URL>")
.build(document.getElementById("editor-iframe"), product, config);
About product definition and config
Let's take a look at the build()
method parameters a bit closer.
The first one is pretty obvious - it is our <iframe>
where we load the editor. No need to discuss it in details. Let's focus on the remaining ones.
Product Definition
The second one is called the product definition. It describes the design loaded to the editor - what PSD or IDML template to use, what are pages (in terms of Design Editor, surfaces), safety lines (bleed zone), etc. You can even pass the name of the design previously saved by a user.
The structure of a product definition is a quite complicated and described with the IProductDefinition interface. To get a better idea how to work with it, refer to the Configuring Product section of Customer's Canvas documentation.
For the purposes of this quick start, let's demonstrate the simple product definition of a single page design without any safety lines (bleeds):
const product = {
surfaces: [
{
printAreas: [{ designFile: 'samples/test-page' }]
}
]
};
Editor Config
The last parameter is the configuration of the editor. The configuration object is also very large and allows for adjusting aspects of all parts of the editor - the viewer, object inspector, toolbars, asset manager, etc.
You may pass the empty object and in this case, it will use the settings from the Configuration/clientConfig.json file located in the Design Editor app instance. You may use this file file as an example - all keys from it can be copied to your code. However, we would discourage you from modifying the clientConfig.json as it will require you to merge these changes when you install the updated version of the Design Editor. In addition to it, you may find it useful to check out the clientConfig.json file reference in Customer's Canvas documentation.
For this quick start, let's add a couple of config keys which enable the "freehand" mode of the editor, add a shadow around the design in the editor, and hide the object inspector by default:
const config = {
initialMode: 'Advanced',
widgets: {
ObjectInspector: {
isHidden: true
}
},
canvas: {
shadowEnabled: true
}
};
The full example looks like this:
import { EditorBuilder } from "@aurigma/design-editor-iframe/EditorBuilder";
const product = const product = {
surfaces: [
{
printAreas: [{ designFile: 'samples/test-page' }]
}
]
};
const config = {
initialMode: 'Advanced',
widgets: {
ObjectInspector: {
isHidden: true
}
},
canvas: {
shadowEnabled: true
}
};
const editor = EditorBuilder
.for("https://<your-editor-app-URL>")
.build(document.getElementById("editor-iframe"), product, config);
Manipulating the editor
Now we know how to embed the editor to the page, configure it and load some template into it. However, how do we do anything useful with it? Let's explore some of the popular tasks.
Saving the results and receiving images/print files
Let's imagine that the user modified the design. Now you need to save the design on the backend and receive the print files and proof images.
The Editor
has several useful methods:
const result = await editor.saveProduct("some-design-name");
This method saves the state of the editor to a so-called state file on the server. You can pass its name or omit it - in this case, the backend automatically assigns it a unique name (GUID-based).
You receive back the state file name (state id) and the user id (through the editor config).
The state id and user id can be used to construct the URL which renders the result and return a PDF or JPEG version of a design. However, to avoid constructing the URLs yourself, you can use another method instead of saveProduct()
:
const result = await editor.finishProductDesign();
It calls the saveProduct()
inside and returns the extended structure, which includes two important members:
hiResOutputUrls
- an array of URLs which can be used to access the PDF files (depending on the rendering settings - either a single multipage PDF or a separate PDF per surface).proofImageUrls
- a double array for the JPEG or PNG proof images. The first level are surfaces, the second one - the print areas.
Note: This method accepts the optional options object as an argument. See the details in the finishProductDesign reference.
It is important to understand that each call of saveProduct()
or finishProductDesign()
create a new copy of a state file on the server. Although you can pass the same state ID over and over again (and cause the editor to overwrite the results), sometimes you may want to get an image of a design without creating a state file.
In this case, you may use another method:
const result = await editor.getProofImages()
The output is the same as for the finishProductDesigns
, but without the hiResOutputUrls
.
Note: It also accepts its own options. See the details in the getProofImages reference.
To be able to create an image, the editor creates a temporary state. Unlike the regular states, they are not stored permanently, don't save the URLs received from the getProofImages()
in a long-term storage.
Manipulating the design elements
To work with the design elements, we have another package, called @aurigma/design-atoms. It represents to object model for the design - product, surfaces, containers, various kinds of items, etc.
To get an access to the "root" of the Design Atoms object model loaded in the editor, use the following code:
const product = await editor.getProduct();
const root = await product.getProductModel();
This code will serialize the object model inside the iframe, transfers the data to our page, and deserializes it.
If you modify some element, you may use the product.setItem()
method like this:
root
.getAllItems()
.forEach(async (x) => {
switch (true) {
case x.name.toLowerCase() === 'first name':
x.text = 'Ernest';
await product.setItem(x);
break;
case x.name.toLowerCase() === 'last name':
x.text = 'Hemingway';
await product.setItem(x);
break;
}
});
If you want to add a new item, you even don't need a product model. It is enough to use the Product
from IFrame API and use some of its surfaces
to add items to it.
const product = await this.editor.getProduct();
const rect = new RectangleItem(new RectangleF(50, 50, 100, 150));
rect.name = `Item 1`;
rect.fillColor = ColorFactory.createColor('#DAF7A6');
product.currentSurface.insertItem(rect);
Sometimes it is easier to update the entire model in a single method call rather than do it by multiple setItem
s:
const product = await editor.getProduct();
const root = await product.getProductModel();
await product.setProductModel(root);
Important: In the beginning, you may find it a bit confusing that we have two hierarchies with the similar object names - Product, Surfaces, PrintAreas, etc. in both IFrame API module and Design Atoms module. The Design Atoms model represents the design elements itself, while IFrame API members are used send appropriate commands inside the iframe element.
Working with Design Editor events
You may need not just manipulate the editor, but also make reactions on the events which happen in the editor - like the item selection, changes in the design, undoing/redoing changes, dialogs opened/closed, etc.
To do it, you need to subscribe to the events using the editor.subscribe()
method, where you pass the name of the event and the event handler callback, like this:
import { Events } from "PostMessage/RpcTypes";
...
editor.subscribe(Events.SelectedItemsChanged, () => console.log("Selection changed!"));
The RpcTypes exports the Events class which include the constants with all supported event types.
Working with selection
It is quite often when you need to do some actions with the items the user selects in the editor. To understand what items are selected, use this code:
const selection = await editor.getSelectedItems();
const selectedNames = selection.map(x => x.name);
const product = await this.editor.getProduct();
const root = await product.getProductModel();
const selectedItems = root
.getAllItems()
.where(x => selectedNames.indexOf(x.name) !== -1)
.toArray();
After that you can change the items and send it back to the editor as discussed above.
You can also change the selection yourself:
await editor.selectAllItems();
await editor.clearSelection();
And of course, you can select the items from the product model:
const product = await this.editor.getProduct();
const root = await product.getProductModel();
await editor.selectItems(
root
.getAllItems()
.first(x => x.name === 'My Item')
);
What is next?
Once you understand the basics of IFrame API, you may be interested to check out the following resources: