Multipage Products

A multipage product support is required by a wide range of printing products, from two-sided business cards and booklets to full-fledged magazines and photobooks. The multipage products are not just about adding an ability to switch between pages in the editor. For example, books usually contain complex formatted text, including text wrapped around images, which has to be supported by the editor. Another aspect of the multipage product support is the editor speed. Since photobooks sometimes contain tens or even hundreds of pages, the editor's back end has to be optimized to process such an amount of graphics. The size of a book can be up to hundreds of megabytes or even a gigabyte, so it requires special techniques to work with such volumes of data. Fortunately, Customer's Canvas implements all these functionalities behind the scenes. All you need to do is set up a template for each page of your product.

In this topic, we elaborate on several ways to open and configure multipage products in Customer's Canvas and explain how to work with pages (surfaces) when the product is loaded. The rendering of multipage products is discussed in the Rendering Multipage Products topic.

Loading Multipage Products

You can create multipage products in a similar way as you do for the single page ones. Likewise, you use the loadEditor method, but the arguments you pass there differ.

Foremost, let us recall how you create single page products from PSD templates. You pass a string with a template name (without the extension) to the loadEditor method, the templates should be placed in the \ProductTemplates\designs\ folder. The example is:

HTML
<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Example of loading the editor</title>
    <!-- The IFrame API script. IMPORTANT! Do not remove or change the id. -->
    <script id="CcIframeApiScript" type="text/javascript" src="http://example.com/CustomersCanvas/Resources/SPEditor/Scripts/IFrame/IframeApi.js">
    </script>
</head>

<body>
    <!-- The iframe to display the editor into. -->
    <iframe id="editorFrame" width="100%" height="800px"></iframe>
</body>

<script>
    // Initializing product with only one template "mytemplate.psd".
    productDefinition = { surfaces: ["mytemplate"] };
    // Getting the iframe element to display the editor into.
    var iframe = document.getElementById("editorFrame");
    var editor = null;
    // Loading the editor.
    CustomersCanvas.IframeApi.loadEditor(iframe, productDefinition)
    // If the editor has been successfully loaded.
    .then(function (e) {
        editor = e;
    })
    // If there was an error thrown when loading the editor.
    .catch(function (error) {
        console.error("The editor failed to load with an exception: ", error);
    });
</script>
</html>

To load a multipage product, the only thing you should change in this code is how you initialize the productDefinition variable. Let us look at several ways to set it up.

Loading Pages from Folder

If your product consists of a significant amount of pages, it would be convenient to organize the templates into folders and load the entire folder as a product. To do this, create a subfolder in the \ProductTemplates\designs\ folder and put your templates there. After that, create an object implementing the ISurfacesFromFolder interface with the designFolder property set to this folder, and assign it to the surfaces property:

JavaScript
productDefinition = {
    surfaces: {
        designFolder: "myphotoBook" 
    }
};

The product pages are arranged alphabetically, by the template file names. For example, if template files are named a.psd, b.psd, and c.psd, then a.psd is the first page, b.psd is the second one, and c.psd is the third page.

Also, you can set a page mockups and preview mockups via the ISurfacesFromFolder.mockupFolder and ISurfacesFromFolder.previewMockupFolder properties:

JavaScript
productDefinition = {
    surfaces: {
        designFolder: "myphotoBook",
        mockupFolder: "pageMockups",
        previewMockupFolder: "pagePreviewMockups"
    }
};

Configuring Each Page Individually

Creating products from folders is easy, but it has certain disadvantages. For example, if there are many products and a few templates, it is unreasonable to copy the same files between many folders. To avoid this, it is possible to pass an array of PSD filenames to surfaces. For example, the easiest way to define a two-sided business card is:

JavaScript
productDefinition = {
    surfaces: ["businesscard2_side1", "businesscard2_side1"]
};

The pages appear in the same order as they are passed here.

Creating Product from State Files

You can pass names of state files instead of PSD filenames to the loadEditor method:

JavaScript
productDefinition = ["7a6ecf23-1286-4e90-8f18-c7c1c77e3cb0", "565d5aa3-74ba-4d6a-860f-620410049847", "ed0678cc-ed84-4471-99ac-355360a7b51c"];

A state file may contain a number of surfaces. As a result, the editor combines all surfaces from these states into a single product. The page order in the resulting product will be the following: all pages from the first state loaded in the order as they went into it, all pages from the second state, and so on.

Specifying Additional Properties

You can configure safety lines, folding lines, and spines in the same manner as you do for single page products. For example, the following snippet sets up a folding line, which is displayed on each page:

JavaScript
productDefinition = {
    surfaces: {
        designFolder: "myphotoBook",
        foldingLines: [{
            isVertical: false,
            pen: {color: "green"}
        }]
    }
};

Such settings can be product-wide or specific to a certain page. If safety lines are configured both for a product and for a page, then the page settings override the product-wide ones. Let us illustrate this. The following product definition specifies three pages, where the first and the second pages have a safety line with the same parameters, but the third page has its own safety line:

JavaScript
productDefinition = {
    surfaces: [
    // These pages have default safety lines
    "page1", "page2",
    // The page3 has custom safety lines
    {   designFile: "page3",
        safetyLines: [
            {margin: 15, color: "green"},
            {margin: 7.5, color:"red"}
        ]
    }
]};

The following snippet demonstrates how to show safety lines on all pages except for the second one:

JavaScript
productDefinition = {
    defaultSafetyLines: [{margin: 15, color: "green"}, {margin: 7.5, color:"red"}],
    surfaces: [ "page1", {designFile: "page2", safetyLines: []}, "page3" ]
};

When a multipage product is loaded into the web-to-print editor, Customer's Canvas automatically adds the navigation area to the Bottom Toolbar. If a product contains more than two pages, the navigation area displays the pagination control that allows switching between pages using numbered page thumbnails and scrolling them left/right with the navigation buttons:

Swiper in multipage products.

If a print product has only two pages, then the pagination control in the navigation area is replaced with the Front side/Back side buttons.

Swiper in multipage products.

Manipulating Multipage Products

Customer's Canvas IFrame API allows you to manipulate product pages after the editor has loaded. You can get a list of pages, switch the editor from one page to another, and add or remove pages at runtime.

To control product pages, you should get a product model of an editor instance using the Editor.getProduct method. A product contains an array of surfaces (this is what pages are called in the API) and methods for manipulating them. For example, to swap two pages in a product, you should just swap two corresponding elements in the product.surfaces array.

Getting a Product and Its Surfaces

HTML
<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Example of getting product surfaces</title>
    <!-- The IFrame API script. IMPORTANT! Do not remove or change the id. -->
    <script id="CcIframeApiScript" type="text/javascript" src="http://example.com/CustomersCanvas/Resources/SPEditor/Scripts/IFrame/IframeApi.js">
    </script>
</head>

<body>
    <!-- The iframe to display the editor into. -->
    <iframe id="editorFrame" width="100%" height="800px"></iframe>
</body>

<script>
    // Initializing product with only one template "mytemplate.psd".
    productDefinition = { surfaces: ["mytemplate"] };

    // Getting the iframe element to display the editor into.
    var iframe = document.getElementById("editorFrame");
    var editor = null;
    var product = null;

    // Loading the editor.
    CustomersCanvas.IframeApi.loadEditor(iframe, productDefinition)

        // If the editor has been successfully loaded.
        .then(function (e) {
            // Retrieving a reference to the editor.
            editor = e;

            // Getting a product model.
            editor.getProduct()
                // If we have successfully obtained the product.
                .then(function(p) {
                    product = p;
                    // Logging a number of pages.
                    console.log("The product contains " + product.surfaces.length + " page(s)");
                });
        })

        // If there was an error thrown then show the message.
        .catch(function (error) {
            console.error("The editor failed to load with an exception: ", error);
        });

</script>
</html>

Adding Surfaces

The following example shows how to add a blank page and a page with a specific template to a product at runtime:

JavaScript
// Loading the editor.
CustomersCanvas.IframeApi.loadEditor(iframe, productDefinition)

    // If the editor has been successfully loaded.
    .then(function (e) {
        // Retrieving a reference to the editor.
        editor = e;

        // Getting a product model.
        editor.getProduct()
            // If we have successfully obtained the product.
            .then(function(product) {

                // Adding a blank surface.
                product.addSurface(
                {
                    // Defining the surface size.
                    width: 640, height: 480 
                })
                // If the surface has not been added to the product.
                .catch(function (error) {
                    console.error("Failed to add an empty surface with an exception: ", error);
                });

                // Adding a surface based on a template.
                product.addSurface(
                { 
                    // Defining the template file.
                    printAreas: [{ designFile: "postcard_side2"}]
                })
                .catch(function (error) {
                    console.error("Failed to add a surface from template with an exception: ", error);
                });

            });
    })

    // If there was an error thrown then show the message.
    .catch(function (error) {
        console.error("The editor failed to load with an exception: ", error);
    });

Removing Surfaces

The following example shows how to remove two pages from a product at runtime: the first one and the one which is currently selected in the editor:

JavaScript
// Making sure that at least one surface remains in the product after removing a page
if (product.surfaces.length > 1) {

    // Removing the current surface.
    product.removeSurface(product.currentSurface)
        .then(function(result) {
            console.log("Current surface removed");
        })
        .catch(function (error) {
            console.error("Removing the surface failed with exception: ", error);
        });
}

// Making sure that at least one surface remains in the product after removing a page
if (product.surfaces.length > 1) {

    // Removing the first surface.
    product.removeSurface(product.surfaces[0])
        .then(function(result) {
            console.log("The first surface removed");
        })
        .catch(function (error) {
            console.error("Removing the surface failed with exception: ", error);
        });
}

Programmatically Switching Pages in the Editor

The following example switches the editor to the next page relative to the currently selected one:

JavaScript
editor.getProduct()
    .then(function (product) {
        // Getting the current surface index.
        const indexOfCurrentSurface = product.surfaces.lastIndexOf(product.currentSurface);

        let indexOfNextSurface = indexOfCurrentSurface + 1;
        if (indexOfNextSurface > (product.surfaces.length - 1))
            indexOfNextSurface = 0;

        // Switching to the next surface.
        return product.switchTo(product.surfaces[indexOfNextSurface]);
    })
    .then(function(result) {
        console.log("Surface switched");
    })
    .catch(function (error) {
        console.error("Switching the surfaces failed with exception: ", error);
    });

See Also

Manual

IFrame API Reference