Custom Integration

(Custom UI)

It’s highly recommended to begin with the iframe integration as there are no downsides and CSS customization is easy.

If at any point you feel that the iframe integration lacks some functionality or you require a drastically different UI you can always switch to a Custom Integration.

All the work you have done to make the iframe look like your website will still work, since custom integration is doing the exact same thing as the iframe - except you can customize much more because you have access to the entire UI code.

In Buyer Mode, Printess is reduced to a pure view container which will not expose any UI besides the set editable area. All controls and inputs must be provided by your website. This gives you full control of how your website looks but you have to handle selection-change and page-change callbacks to update your UI, which in return needs to update Printess properties via the js-API.

Have a look at our GitHub repository here: https://github.com/PrintessEditor/getting-started

You can also test drive the custom integration here: https://printesseditor.github.io/getting-started/

Custom integration comes with a uiHelper.js file which does all of the UI work you are already familiar with from the iframe integration.

uiHelper.js uses the Printess-JS-API to communicate with the Editor.

WARNING! While working with the custom integration you need to keep your code up to date to have a working Editor. If you change the code we recommend forking and performing regular updates. To ensure that your Editor stays in sync with the corresponding uiHelper code and does not suddenly stop working, you must use our freeze versions which are intended to stay change free.

For each freeze version there is a specific matching branch in the getting started GitHub repository. In GitHub, select Branches to see the available freeze versions (see screenshot below). This lets you change versions in a planned way within your schedule.

Github branches

So for the freeze version 0.9.4 one will need to use: getting started branch 0.9.4 + the Editor URL https://editor.printess.com/v/0.9.4/

For the freeze version 0.9.3 one will need to use: getting started branch 0.9.3 + the Editor URL https://editor.printess.com/v/0.9.3/

Typescript

If you’re using typescript you’ll find a printess-editor.d.ts file in the repro which contains all types for the Printess object. You also find uiHelper.ts (the typescript version of our uiHelper) which uses the “d.ts” file.

JS-API Reference Full reference can be found here.

Loading Printess

Be aware that Printess itself is loaded after the web component polyfills. So first we need to load webcomponentjs from the Printess CDN.

We also need to load the bootstrap bundle for combo-boxes and off canvas popups. Then bootstrap.css and bs-layout.css need to be loaded.

Finally, we need just to load the uiHelper.js, which contains all of the UI logic.

Please have a careful look at the samples integration to get an idea of how the Printess API callbacks work together to redraw the UI elements.

 <!-- Bootstrap -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js"
  integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4"
  crossorigin="anonymous"></script>

<!-- Load polyfills -->
<script src="https://editor.printess.com/node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>

<!-- Load uiHelper --->
<script src="custom-ui/uiHelper.js"></script>

Now we wait for WebComponentsReady before loading Printess itself from our CDN.

<script>
  window.WebComponents = window.WebComponents || {
    waitFor(cb) {
      addEventListener('WebComponentsReady', cb);
    },
  };
  let printess;
  let api;
  WebComponents.waitFor(async () => {
    printess = await import(
      'https://editor.printess.com/printess-editor/printess-editor.js'
    );

    api = printess.attachPrintess({
      resourcePath: 'https://editor.printess.com/printess-editor', // needs to be always set
      domain: 'api.printess.com',
      div: document.getElementById('printess'),
      basketId: 'CurrentShopBasketId',
      shopUserId: 'CurrentShopCustomerId',
      token: 'YOUR SHOP-TOKEN',
      showBuyerSide: true,
      templateName: 'Retro Sign',
      templateVersion: 'published'
    });
  });
</script>

The attachPrintess call initializes Printess then passes the authentication token and name of the Template to be loaded. See all parameters here: JS-API

JS-API Reference

The return value of the attachPrintess() call holds a reference to the Printess JS-API, your direct link to the Printess Editor. In our example, the variable is named api. You can read the full JS-API documentation here: JS-API

ResourcePath and Domain

resourcePath: 'https://editor.printess.com/printess-editor',
domain: 'api.printess.com'

Please be aware that you’ll need to tell Printess the path to its resource files (Web-Assembly and Default Fonts) in a separate property resourcePath. Please do not change this value!

The domain should also remain unchanged. It only needs to be changed if you are using a private Printess cloud.

div

div: document.getElementById('printess')

In the div property you need to pass a div-element which the Printess Editor will attach to. Printess is intended to have as much space as possible, so it is highly recommended to not leave space on the left or right sides (especially on mobile).

Token

 token: 'YOUR TOKEN'

token should be set to a Shop-Token which points to your Printess account. You can get this token once you are logged in to the Printess Editor -> Account Menu -> API-Token. You’ll see 2 different tokens in the dialog. Please always use the Shop-Token.

showBuyerSide

showBuyerSide: true

showBuyerSide is a boolean which forces Printess to jump directly to the Buyer Side. If you use the shop-token, Printess will always show the Buyer Side - no matter what you pass in that property.

templateName

templateName: 'Sign'
templateVersion: 'published' // Can be draft or published (default) depending on which version of the document should get used

templateName is required and specifies the name of the Template to load.
templateName can also take the save-token you received from saveJson() and load it directly.

basketId and shopUserId

To enable your customer to upload images and to save/load their work you need to pass at minimum a BasketId to Printess on attachPrintess().

Optionally you can pass a shopUserId to make Printess store in the context of the current customer (user). Now when the customer uploads an image it will be stored under the shopUserId. So if the customer returns later they will see all of their previously uploaded images.

{
  "basketId": "CurrentShopBasketId",
  "shopUserId": "CurrentShopCustomerId"
}

We are working on a method to assign an existing basketId to a shopUserId in the case the user logs in after they have already designed their artwork. This wil ensure that even with late sign in or user creation, all existing uploaded images are assigned to that customer.

Merge Templates

mergeTemplates: [{
  templateName: "motive1",
  documentName: "Document", // optional, source-document used for merging. If not supplied Primary or first Document is used. !!! This is the source, not the target document, you can set the target-document in the designer by enabling "Is Merge Target" in the document menu. !!! 
  templateVersion: "published", // Can be draft or published (default) depending on which version of the document should get used
  spreadIndex: 0 // [optional]
}];

A commonly used approach is to have a Master Template with the final dimensions, general Buyer Side settings, and all available colors and fonts. And then having multiple Artwork Templates which the customer can select directly from the shop catalog.

Example: Let’s say the Master Template is named “Card” and the selected motive is named “Motive1”. When you show the Printess Editor you can now load “Card” via the templateName property and merge in the document Document from the “Motive1” Artwork Template via the mergeTemplates property.
With spreadIndex you can define the spread number (zero-based) that the merged Template is placed on.

Additional Note: Keep in mind that on a facing-page document each pair of pages counts only as a single spread.

Custom Translations

Printess will automatically show the Buyer Side UI in the current user’s browser language. Translations are managed/modified in the account portal. Read more about translations and Buyer Side language here.

If a specific language is desired (e.g. if a buyer should be able to set his preferred language, (regardless of local browser language) one can use the translationKey with a language key (window.navigator.language). Specific language keys like en-US will fall back to en in case no specific language is defined in the account portal. The default value is auto which lets the browser language determine the Editor language.

For debugging or if you’d like to change a certain value across all languages, you can provide a custom translation table to the attachPrintess call. Available keys can be found in the translation.json on the Printess Github Repo.

You can modify the values to the given keys to your needs by adding them to your translation table. If no custom translation for a specific key is found, Printess will fall back to the default translation.

translations: {
  "custom": {
    "name": "Your Name" // you can also access this translation in documents with ${gl("custom.name")}
  }
}

In the uiHelper you need to call the printess.gl() function and pass through the keys for your translation as strings chained together with a dot (e.g. “custom.name”), to receive “Your Name”.

Using Custom Translations for Changing Labels and Icons

The custom translation table can also be used to change Buyer Side UI icons and text. For example, we can use the translation keys for “ui.infoFrame” to change the desktop label Form Field setting:

translations: {
  "ui": {
    "infoFrame": "Important Information"
  }
}

This changes the text in the Buyer Side information frame from “Info” to “Important Information”.

You can use the translation key to find all available UI element keys and easily change them to best suit your product.

All available icons and their respective names can be found here.

Offensive Language

To prevent the use of offensive language in customizable texts, you can pass a list of forbidden words to Printess via the attach call offensiveWords. You can set this list pin in the account portal. Also, the “offensive” option must be activated on the frame level.

Offensive words are just a comma-separated list of strings. Additionally, each offensive word can have a replacement string separated by a colon.

offensiveWords: "shit,fuck:f**k,piss"

In the above example, the words “shit” and “piss” would throw a validation exception and “fuck” would be replaced by “f**k”

Avoid Space Around

When you would like to disable zooming and panning in Printess, you can command Printess to exactly frame the Template you have loaded. This is what the autoScale parameter is for.

autoScale {
  maxWidth: 500,
  maxHeight: 700
};

autoScale accepts 2 parameters, maxWidth and maxHeight. Both set the maximum expansion the Printess container can occupy. The final size will be calculated depending on the aspect ratio of the loaded template.

Restrict Pan and Zoom

When using autoScale you can disable the possibility of the user panning and zooming around a loaded Template by simply setting the allowZoomAndPan parameter to false.

allowZoomAndPan: false;

Save and Load Work

To store the result of what your customer has configured in Printess, just call printess.saveJson(). In return, you will get a token that can be used to easily load the configuration when the customer returns or wants to make changes. You also can use this token to load the customer’s design in your support portal to apply any client fixes you might have received.

Save Customers Work

const myToken = await printess.saveJson();

Load Customers Work

await printess.loadJson(myToken);

To test this function, we added the Save State button in the toolbar. This saves the current state and returns a token to load it later. To try it out, just make some changes or load a different Template, and then press Load State. Paste the token to the prompt and you will see the state you previously stored!

Form Fields via API

Printess utilizes Form Fields which are created by the designer and can be changed by the buyer.

Passing Form Field Values

Let’s say you want to pre-populate form fields with customer data when showing off the Printess Editor? You can easily do this by just passing an array of objects when loading the Printess editor:

api = printess.attachPrintess({
    ...
    formFields: [
      {
        name: "Name",
        value: "Peter Meyer"
      },
      {
        name: "Email",
        value: "peter.meyer@printess.com"
      }
    ]
  });

Each object needs to address a form field by its name and set its value as a string. Even if the form field type is a number or object, you still need to pass the value as a string. The above example could be used to pre-populate a business card template.

Receiving Form Field Updates

Another useful callback is formFieldChanged which is helpful when a user changes a certain configuration that has been marked as price relevant. Depending on the resulting value, you can adjust the price of the product accordingly.

These form fields can contain price relevant information such as material or color. The Retro Sign example Template demonstrates a couple of these form fields. Material, Size (and if a solid material is selected - Drill Holes and Varnish). All 4 form fields are possibly price relevant, so the eCommerce app must know if any of these values are changed.

const formFieldChanged = (name, value) => {
  alert('Form Field: [' + name + "] changed to '" + value + "'");
};
printess.attachPrintess({
  /* ... all other properties ... */
  formFieldChangedCallback: formFieldChanged,
});

Creating a Thumbnail Image

After the buyer has sent the design to the shopping basket you might want to retrieve a small image of the current layout. Do this by getting a URL of such a “Thumbnail” you can call:

const fileName = 'thumb_' + new Date().getTime() + '.png';
const documentName = '';
const width = 400; // max is 400
const height = 400; // max is 400

printess
  .renderFirstPageImage(fileName, documentName, width, height)
  .then((thumbnailUrl) => {
    window.open(thumbnailUrl);
  });

fileName: Has to be set so you can put the basket ID here to override an existing image.

documentName: Can be set if you want a thumbnail of a specific preview document. Otherwise, the primary document will be taken. If you want to get a thumbnail of any existing preview document just pass “!PREVIEW!”. If no “preview” document is found it falls back to the “primary” document and then to the first document of the Template. You can see this in action in our “T-Shirt” example.

width: The maximum width of the thumbnail based on the aspect ratio of the document. The resulting thumbnail width can be made smaller.

height: The maximum height of the thumbnail based on the aspect ratio of the document. The resulting thumbnail-height can be made smaller.

TIP: Press the Create Thumbnail button to show a thumbnail of the current Template.

WARNING The thumbnail generation can take a couple seconds. If you request the thumbnail when the buyer clicks Continue, you need to show an overlay screen and wait for the call to finish before you unload the Printess Editor!