Rendering Multipage Products

This topic dwells on two aspects of multipage products: rendering hi-res output files and displaying proof images.

Rendering Hi-res Output Files

The only distinctive feature of rendering multipage products is the number of hi-res output files you get. You can get a single hi-res output file containing all pages or multiple files, one per each page in the product. This behavior is configured via the two parameters, hiResOutputToSeparateFiles and hiResOutputFileFormat. The hiResOutputToSeparateFiles parameter specifies if an output file should be created for each page of a product. The hiResOutputFileFormat parameter is involved because the only hi-res output format in Customer's Canvas which can contain multiple pages is PDF. Let us consider both configurations.

Rendering a Single File

To create a single hi-res output file containing all pages of a product, set the output type to PDF and disable rendering to separate files. In this case, the output file will be named result.pdf.

To make this configuration default for all products, edit the ~\Configuration\clientConfig.json file.

JavaScript
"rendering": {
    "hiResOutputToSeparateFiles": false,
    "hiResOutputFileFormat": "PDF",
    ...
}

To change these parameters on a product level without changing global settings in the clientConfig.json file, use the IConfiguration interface in Iframe API.

JavaScript
configuration = {
    rendering: {
        hiResOutputToSeparateFiles: false,
        hiResOutputFileFormat: "PDF"
    }
};

Rendering Multiple Files

To create multiple hi-res output files, one for each page, enable rendering to separate files; the output file type can be any of the supported ones.

To make this configuration default for all products, edit the ~\Configuration\clientConfig.json file.

JavaScript
"rendering": {
    "hiResOutputToSeparateFiles": true,
    "hiResOutputFileFormat": "JPEG",
    ...
}

To change these parameters on a product level without changing global settings in the clientConfig.json file, use the IConfiguration interface in Iframe API.

JavaScript
configuration = {
    rendering: {
        hiResOutputToSeparateFiles: true,
        hiResOutputFileFormat: "JPEG"
    }
};

Displaying the Proof Images

In multipage products, proof images for all pages should be available to the user so that they can approve them and give the product the "go-ahead" for printing. The code sample in this section represents a three-step wizard that contains the design page, the approval page that allows a user to view proof images for all pages, and the finish page. Its workflow includes the following steps:

  1. The user creates a design.
  2. The user initializes the transfer to the approval page. The system saves the product and displays the proof images.
  3. The user can return to editing the product. In this case, the system restores the product using the state ID received in the previous step.
  4. The user approves the design. The system displays the finish page containing links to the hi-res output and the return-to-edit URL.

To make this sample work, do the following:

  1. Copy and paste the code to the index.html file.
  2. Replace example.com with the name of the site where your Customer's Canvas instance is hosted and save the file.
  3. Open index.html in a browser.
HTML
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <meta charset="utf-8" />
    <title>Sample business card - Customer's Canvas</title>

    <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
    <link href="index.css" rel="stylesheet" />
    <!-- The IFrame API script. IMPORTANT! Do not remove or change the ID. -->
    <script id="CcIframeApiScript" type="text/javascript" src="http://example.com/Resources/SPEditor/Scripts/IFrame/IframeApi.js">
    </script>

    <script type="text/ecmascript">
        $(function () {
            // Defining the product.
            productDefinition = {surfaces: { "designFolder": "photoBook" } };

            // Defining the editor configuration.
            configuration = { widgets: {FinishButton: {mode: "Disabled"}}};

            var editorFrame = $("#editorFrame");

            var editor = null;

            // Loading the product into the editor.
            function loadProduct(product) {

                // Loading the editor.
                CustomersCanvas.IframeApi.loadEditor(editorFrame[0], product, configuration)
                    // 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("Load failed with exception: ", error);
                    });
            }

            // Loading the product defined above into the editor.
            loadProduct(productDefinition);

            var currentPage = "design";

            // JSON Object containing data generated by finishProductDesign.
            var renderData = null;

            // JSON Object containing data generated by saveProduct.
            var saveData = null;

            // Saving the product and getting links to the proof images.
            $("#editorPage #nextButton").click(function () {

                // Saving the product.
                editor.saveProduct()
                    // If the product has been successfully saved.
                    .then(function (result) {
                        // Saving the product state.
                        saveData = result;
                    })
                    // If there was an error thrown when saving the product.
                    .catch(function (error) {
                        saveData = error;
                        console.error("Product saving failed with exception: ", error);
                    });
                // Getting the proof images.
                editor.getProofImages()
                    // If proof images have been successfully received.
                    .then(function (result) {
                        // Saving proof images info.
                        ApprovePage.approveData = result;

                        // Go to the approval page.
                        goToPage("approve");
                    })
                    // If there was an error thrown while getting the proof images.
                    .catch(function (error) {
                        ApprovePage.approveData = error;
                    });
            });

            // Completing the product customization.
            $("#approvePage #approveButton").click(function () {

                editor.finishProductDesign()
                    // If the product customization has been successfully completed.
                    .then(function (result) {
                        // Saving hi-res output info.
                        renderData = result;

                        // Go to the finish page.
                        goToPage("finish");
                    })
                    // If there was an error thrown while completing the product customization.
                    .catch(function (error) {
                        renderData = error;
                    });
            });

            // Loading the product in the original state into the editor.
            $("#finishOrderPage #newDesign").click(function () {
                goToPage("design");
            });

            // Reopening the product in the editor.
            $("#approvePage #lnkEditAgain").click(function () {
                loadProduct(saveData.stateId);
                goToPage("design");
            });

            // Initializing the design and finish pages with the product info.

            // Initializing the finish page with the print-ready output URL and a link for reopening the product in the editor for further editing.
            function setFinishPageData() {
                $("#finishOrderPage #hiResLink").attr("href", renderData.hiResOutputUrls[0]);
                $("#finishOrderPage #linkForFeatureEdit").attr("href", renderData.returnToEditUrl);
            };

            // Loading the product into the editor.
            function setEditorPageData() {
                loadProduct(productDefinition);
            };

            // Wizard navigation.

            function goToPage(to) {
                setupPage(to);
            }
            window.onpopstate = function (e) { setupPage(e.state); }

            var workflowPages = {
                "design": { elements: $("#editorPage"), setData: setEditorPageData },
                "approve": { elements: $("#approvePage"), setData: ApprovePage.setData },
                "finish": { elements: $("#finishOrderPage"), setData: setFinishPageData }
            }

            function setupPage(page) {
                var destPageData = workflowPages[page]

                if (typeof destPageData.setData === "function")
                    destPageData.setData();

                workflowPages[currentPage].elements.fadeOut(function () {
                    destPageData.elements.show();

                    currentPage = page;
                });
            }
        })
    </script>

    <script id="approvePageModule" type="text/ecmascript">
        (function () {
            var currentProofIndex = 0;

            window.ApprovePage = {
                // Displaying the proof image.
                setPreviewImage: function (index) {
                    var proofImageUrls = ApprovePage.approveData.proofImageUrls;

                    if (index < proofImageUrls.length && index >= 0) {
                        if (ApprovePage.proofImg.attr("src") != proofImageUrls[index]) {
                            ApprovePage.proofImg.hide();
                            ApprovePage.proofImg.attr("src", proofImageUrls[index]);
                        }

                        currentProofIndex = index;
                        $('#approvePage #pageNumber').text(currentProofIndex + 1 + " of " + proofImageUrls.length);
                        $("#approvePage #leftBtn").prop('disabled', index == 0);
                        $("#approvePage #rightBtn").prop('disabled', index == proofImageUrls.length - 1);
                    }
                },

                // Initializing the approval page.
                setData: function () {
                    // Displaying and hiding navigation buttons.
                    if (ApprovePage.approveData.proofImageUrls.length === 1) {
                        $('#approvePage .arrowBtn').css("display", "none");
                        $('#approvePage #pageNumber').css("display", "none");
                    } else {
                        $('#approvePage .arrowBtn').css("display", "inline-block");
                        $('#approvePage #pageNumber').css("display", "block");
                    }
                    // Displaying the proof image of the first page.
                    ApprovePage.setPreviewImage(0);
                }
            };

            $(function () {
                var proofImg = $("#approvePage .proofImg");
                ApprovePage.proofImg = proofImg;

                proofImg.on("load", function () {
                    proofImg.show(null);

                    var parentHeight = proofImg.parent().height();
                    var imgHeight = proofImg[0].height;

                    proofImg.css("margin-top", imgHeight <= parentHeight ? (parentHeight - imgHeight) / 2 + "px" : 0);

                }).on("error", function (e) {
                    if (window.console)
                        console.log(e);
                });

                // Display the previous proof image.
                $("#approvePage #leftBtn").click(function () {
                    ApprovePage.setPreviewImage(currentProofIndex - 1);
                });

                // Display the next proof image.
                $("#approvePage #rightBtn").click(function () {
                    ApprovePage.setPreviewImage(currentProofIndex + 1);
                });
            });

        })();
    </script>
</head>

<body>
    <div id="wrapper">
        <div id="content">
            <!-- The design page -->
            <div id="editorPage" class="area">
                <div id="iframeWrapper">
                    <iframe id="editorFrame" width="100%" height="800px"></iframe>
                </div>
                <div id="saveAndNextButtonsWrapper">
                    <!-- The Finish design button -->
                    <input id="nextButton" type="button" class="btn btn-success btn-lg" value="Finish design >" />
                </div>
            </div>
            <!-- The approval page -->
            <div id="approvePage" class="area" style="display: none">
                <div class="container-fluid">
                    <h1>Approve Your Product</h1>
                    <div class="row">
                        <div class="col-md-12">
                            <div id="pageNumber"></div>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-md-1 btnContainer left">
                            <input id="leftBtn" type="button" class="btn arrowBtn" />
                        </div>
                        <div class="col-md-10 proofImgContainer">
                            <img class="proofImg" />
                        </div>
                        <div class="col-md-1 btnContainer right">
                            <input id="rightBtn" type="button" class="btn arrowBtn" />
                        </div>
                    </div>
                </div>

                <div class="agree">
                    <h1>Approval</h1>

                    <h3>
                        The product will be printed exactly as it appears above. By clicking the "Approve" button below, I agree that spelling, content, and layout are correct.
                    </h3>

                    <div id="approveButtonWrapper">
                        <input id="approveButton" type="button" class="btn btn-success btn-lg approveButton" value="Approve >" />
                    </div>
                </div>
                <div class="return">
                    <a id="lnkEditAgain">&lt; I want to make some changes</a>
                </div>
            </div>

            <!-- The finish page -->
            <div id="finishOrderPage" class="area" style="display: none">
                <h1 class="">Your Product is Ready</h1>
                <ul>
                    <!-- a link for downloading the hi-res output -->
                    <li>The print-ready file can be downloaded from <a id="hiResLink">this link</a></li>
                    <!-- a link to reopen the product in the web-to-print editor -->
                    <li>You can return to designing the product by initializing the editor using <a id="linkForFeatureEdit">this link</a></li>
                </ul>
                <div class="right">
                    <input id="newDesign" type="button" class="btn btn-info btn-lg" value="< New design" />
                </div>
            </div>
        </div>
    </div>
</body>
</html>

Additionally, you should copy the following CSS snippet and save it to the index.css file in the same folder where the index.html is placed.

CSS
body {
    font: 12px/18px Arial, "Helvetica CY", "Nimbus Sans L", sans-serif;
    height: 100%;
    min-width: 960px;
}

.area,
.fluid {
    position: relative;
}

.area {
    width: 940px;
    margin: 0 auto;
}

#editorFrame {
    display: block;
    border: 0;
}

#content {
    padding: 0px 0px 100px;
}

#buttonsWrapper {
    text-align: right;
    margin-top: 10px;
}

/* The Approve page
-----------------------------------------------------------------------------*/
#approvePage{
    font: 14px Tahoma,Arial,Helvetica,sans-serif;
}

#approvePage .arrowBtn {
    width: 40px;
    height: 88px;
    background-size: 40px 88px;
    background-position: center;
    background-repeat: no-repeat;
    background-color: rgba(67, 40, 229, 0.16);
}

#approvePage .btnContainer {
    height: 88px;
    position: absolute;
    top: 50%;
    margin-top: -44px;
}

#approvePage .left {
    left: 0;
}

#approvePage .right {
    right: 0;
}

#approvePage #rightBtn {
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23ff0000'%2F%3E%3C%2Fsvg%3E");
}

#approvePage  #leftBtn {
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23ff0000'%2F%3E%3C%2Fsvg%3E");
}

#approvePage .proofImg{
    max-width: 600px;
    max-height: 600px;
    min-height: 150px;
    box-shadow: rgba(100, 100, 100, 0.8) 0 0 3px;
    margin-top: 1em;
}

#approvePage .proofImgContainer {
    height: 600px;
    margin-top: 10px;
    margin-left: 8.33333333%;
    margin-right: 8.33333333%;
}

#approvePage .row {
    position: relative;
}

#approvePage .container-fluid{
    text-align: center;
}

#approvePage .agree {
    border: dashed 2px #f00;
    text-align: left;
    padding: 4px 16px 16px;
    margin-top: 15px;
}

#approvePage .agree #approveButtonWrapper {
    text-align: right;
}

#approvePage .agree .return {
    margin-top: 20px;
    font-size: 14pt;
}

#approvePage .agree .return a, a:active, a:focus {
    border-bottom: 1px solid rgb(169, 227, 149);
    color: rgb(100, 188, 70);
}

#approvePage .agree .return a:hover {
    color: rgb(43, 121, 16);
    text-decoration: none;
    cursor: pointer;
}

#finishOrderPage h3 {
    text-align: justify;
}

#finishOrderPage h1 {
    text-align: center;
}

#finishOrderPage .right {
    text-align: right;
}

#finishOrderPage ul {
    list-style-type: disc;
}

See Also

Manual

IFrame API Reference