The WebClientPrint Server Component
Overview
WebClientPrint is a client-server solution. The server-side component is a .NET Core Standard Library (Neodynamic.SDK.WebClientPrintCore.dll) which is referenced by your ASP.NET Core website to generate Client Print Jobs.
A Client Print Job allows you to specify:
- What client printer to use, for example:
- Use "the Default printer" of the client machine. Printing will be performed without displaying any dialog!
- Use "a specific installed printer name" on the client machine. Printing will be performed without displaying any dialog! This also applies for Shared Printers on the client machine network with a UNC name/path!
- Display a "printer dialog" to let the user to select the printer
- Explicitly specify the client printer settings for parallel (LPT) Centronics or serial RS-232 ports or IP/Ethernet Network printers.
- And the commands or file you want to print or send to the client printer.
The WebClientPrint server component requires:
- ASP.NET Core 1.0+
- jQuery 1.4.1+
Client Print Jobs
A Client Print Job (CPJ) contains info about the print job to be processed at the client side by the WCPP utility. The info includes the type of printer to be used and that is available at the client side as well as the data, text or raw commands you want to send or print to such printer.
Creating Client Print Jobs
A CPJ is specified by creating an instance of the ClientPrintJob class. The target client printer is specified through the ClientPrinter property and the data, text or raw commands are specified through the PrinterCommands property. That's it! The Client Printer Types and Printer Commands topics below provide details and sample code of those main properties.
Client Printer Types
The Client Printer is specified through the ClientPrinter property and it accepts an object that should be an instance of any of the following ClientPrint-derived classes:
- DefaultPrinter
- UserSelectedPrinter
- InstalledPrinter
- ParallelPortPrinter
- SerialPortPrinter
- NetworkPrinter
Read on to see how to create CPJ for each of the supported client printer types.
DefaultPrinter
The DefaultPrinter allows you to print to the client's printer which is set up as the "default printer". The printing is performed without displaying any dialog to the user. Here is a snippet code of how to use this option:
using Neodynamic.SDK.Web;
//...
//Create a ClientPrintJob obj that will be processed at the client side by the WCPP
ClientPrintJob cpj = new ClientPrintJob();
//use default printer on the client machine
cpj.ClientPrinter = new DefaultPrinter();
//set the commands to send to the printer
cpj.PrinterCommands = "PRINTER_COMMANDS_GO_HERE";
UserSelectedPrinter
The UserSelectedPrinter allows you to let the user to dynamically select which printer he/she wants to use by displaying a Print Dialog box. That print dialog will list all the installed printers in the user's machine. Here is a snippet code of how to use this option:
using Neodynamic.SDK.Web;
//...
//Create a ClientPrintJob obj that will be processed at the client side by the WCPP
ClientPrintJob cpj = new ClientPrintJob();
//let the user to select the printer by a print dialog box
cpj.ClientPrinter = new UserSelectedPrinter();
//set the commands to send to the printer
cpj.PrinterCommands = "PRINTER_COMMANDS_GO_HERE";
InstalledPrinter
The InstalledPrinter allows you to specify the "Printer Name" of an installed printer at the client machine. By "installed" it means, a printer which is listed in the Printers section of Windows, Linux or Mac OS. Those printers are generally installed by using a driver provided by the manufacturer.
For Raw printing, installing the "Generic / Text Only" driver that comes with Windows OS (and similar driver on Linux & Mac OS) is sufficient in many cases. The "Generic / Text Only" driver is also useful when you cannot get the original driver from the manufacturer because it does not support a given version of Windows or the printer is too old, etc.
So, suppose the user has a printer installed which name is "MyLocalPrinter". Here is a snippet code of how to use this option:
using Neodynamic.SDK.Web;
//...
//Create a ClientPrintJob obj that will be processed at the client side by the WCPP
ClientPrintJob cpj = new ClientPrintJob();
//set the installed printer's name on the client machine
cpj.ClientPrinter = new InstalledPrinter("MyLocalPrinter");
//set the commands to send to the printer
cpj.PrinterCommands = "PRINTER_COMMANDS_GO_HERE";
Special case for Shared Network printers: When the user has an installed printer which is a Shared Network Printer (i.e. a printer attached to another computer on the network), then you can use the InstalledPrinter option to specify such kind of printer. The correct way to specify the printer's name in these cases is by using the UNC (Universal Naming Convention) name/path to reach the printer.
So, suppose the user has a network printer installed which UNC path is "\anotherPC\CoolBrandPrinter". Here is a snippet code of how to use this option:
using Neodynamic.SDK.Web;
//...
//Create a ClientPrintJob obj that will be processed at the client side by the WCPP
ClientPrintJob cpj = new ClientPrintJob();
//set the installed network printer's UNC path/name
cpj.ClientPrinter = new InstalledPrinter("\\anotherPC\CoolBrandPrinter");
//set the commands to send to the printer
cpj.PrinterCommands = "PRINTER_COMMANDS_GO_HERE";
ParallelPortPrinter
The ParallelPortPrinter allows you to specify the parallel port (LPT) of the client machine that the printer is connected to. The client does not need to install the printer through Windows OS. However, a valid LPT port needs to be available and the printer connected to such port through a Centronics interface.
For Linux & Mac OS, Parallel Port printers must be installed through CUPS and then set the assigned printer name through the InstalledPrinter option.
Here is a snippet code of how to use this option:
using Neodynamic.SDK.Web;
//...
//Create a ClientPrintJob obj that will be processed at the client side by the WCPP
ClientPrintJob cpj = new ClientPrintJob();
//use the printer available at LPT1 port on the client machine
cpj.ClientPrinter = new ParallelPortPrinter("LPT1");
//set the commands to send to the printer
cpj.PrinterCommands = "PRINTER_COMMANDS_GO_HERE";
SerialPortPrinter
The SerialPortPrinter allows you to specify the serial port (RS-232) settings of the client printer. The client does not need to install the printer through Windows OS. However, a valid serial port needs to be available and the printer connected to such port. Please refer to the client's printer manual for details about the serial port settings.
For Linux & Mac OS, Serial RS232 Port printers must be installed through CUPS and then set the assigned printer name through the InstalledPrinter option.
Here is a snippet code of how to use this option:
using Neodynamic.SDK.Web;
//...
//Create a ClientPrintJob obj that will be processed at the client side by the WCPP
ClientPrintJob cpj = new ClientPrintJob();
//use the printer available at COM1 port and these settings
//NOTE: Refer to the printer manual for details
cpj.ClientPrinter = new SerialPortPrinter("COM1", 9600, SerialPortParity.None, SerialPortStopBits.One, 8, SerialPortHandshake.XOnXOff);
//set the commands to send to the printer
cpj.PrinterCommands = "PRINTER_COMMANDS_GO_HERE";
NetworkPrinter
The NetworkPrinter allows you to specify a TCP/IP Ethernet-enabled printer that can be reached from the client machine. The client printer in this case can be specified by using the IP address or DNS name as well as the port number.
For Linux & Mac OS, IP/Ethernet printers should be installed through CUPS and then set the assigned printer name through the InstalledPrinter option.
Here is a snippet code of how to use this option:
using Neodynamic.SDK.Web;
//...
//Create a ClientPrintJob obj that will be processed at the client side by the WCPP
ClientPrintJob cpj = new ClientPrintJob();
//set the printer's IP address or DNS name and port number
cpj.ClientPrinter = new NetworkPrinter("10.0.0.8", 9100);
//set the commands to send to the printer
cpj.PrinterCommands = "PRINTER_COMMANDS_GO_HERE";
Printer Commands (RAW Data Printing)
The printer commands you want to send to the client printer is specified through the PrinterCommands property of the ClientPrintJob class. The PrinterCommands property accepts a String type. However, if your printer commands are stored in a physical file on disk or in a database, then you can use the BinaryPrinterCommands properties instead as it accepts a byte array which is suitable for these scenarios.
String Format for Printer Commands
As stated before, the PrinterCommands property accepts a String type for the commands you want to send to the client printer through a ClientPrintJob object.
Some Printer Command/Programming Languages feature non-printable or non-human-readable characters in their syntax. Common samples are the ESC/P language which uses ASCII Dec 27 (ESC), ASCII Dec 10 (LF), ASCII Dec 13 (CR), etc; thermal printer languages like Zebra ZPL which sometimes needs to specify ASCII Dec 29 (GS), ASCII Dec 30 (RS), ASCII Dec 4 (EOT), etc.
These non-printable chars could require many string concatenations with VB or C#. To avoid such situation and to provide a better readability of your printer commands, WebClientPrint allows you to format any character by expressing it in VB or C# Hexadecimal notation within the string commands.
For example, suppose you need to send ESC/P codes for "Set page length in inches" which syntax is as follows: [ESC] C [NUL] n
Where:
- [ESC]: is ASCII Hex 1B
- C: is ASCII Hex 43
- [NUL]: is ASCII 00
- n: is a number in the range 1 < n < 22
Then to specify this command as a string and using the VB or C# Hexadecimal notation:
//set the commands to send to the printer
cpj.PrinterCommands = "0x1B C 0x00 5";
//enable Hex notation for commands
cpj.FormatHexValues = true;
Binary Format for Printer Commands
As stated before, the BinaryPrinterCommands property accepts a byte array for the commands you want to send to the client printer through a ClientPrintJob object. You can get such bytes from anywhere but common scenarios are files on disk or databases. Here is a snippet of how you could get and set the printer commands stored in a file on disk. In this sample code we get the printer commands from a PRN file.
//get and set the commands to send to the printer from a file
cpj.BinaryPrinterCommands = System.IO.File.ReadAllBytes("c:/temp/invoice.prn");
Printing Common File Formats
In addition to print native commands, you can also use WebClientPrint solution to send and print common file formats like PDF, TXT, DOC, XLS, JPG, PNG, TIFF (Multipage), etc.
The Print File feature has the following requirements:
File Format | Windows Clients | Linux & Mac Clients |
---|---|---|
DOC, DOCX | Microsoft Word is required | LibreOffice is required |
XLS, XLSX | Microsoft Excel is required | LibreOffice is required |
Natively supported! | Natively supported! | |
TXT | Natively supported! | Natively supported! |
JPEG | Natively supported! | Natively supported! |
PNG | Natively supported! | Natively supported! |
BMP | Natively supported! | Natively supported! |
Printer Support | You can print files to local installed printers ONLY! Parallel, Serial and IP/Ethernet printers are NOT supported. | You can print files to any installed printers through CUPS system. |
The file you want to send to the client printer is specified through the PrintFile property of the ClientPrintJob class. The PrintFile property accepts an instance of the PrintFile type.
The following snipped shows how to set up and print a PDF file that is located in an ASP.NET website folder:
The source code of the WebClientPrint Sample Website at github has a complete code for testing this feature.
//temp file name that will be created at the client machine
string tempFileName = Guid.NewGuid().ToString("N") + ".pdf";
//Create a PrintFile object
//NOTE: _hostEnvironment is a var of type Microsoft.AspNetCore.Hosting.IHostingEnvironment
//declared in your Controller class whichs generates the ClientPrintJob obj
PrintFile fileToPrint = new PrintFile(_hostEnvironment.ContentRootPath + "/files/LoremIpsum.pdf", tempFileName);
//Create a ClientPrintJob and set the PrintFile
ClientPrintJob cpj = new ClientPrintJob();
cpj.PrintFile = fileToPrint;
Printing multiple files is possible too!
In addition to printing a single file, you can send to the client printer multiple files! For example, you could want to print a PDF file, plus an image file. Or multiple Word docs plus other Excel sheets!
The files you want to send to the client printer are specified through the PrintFileGroup property of the ClientPrintJob class. The PrintFileGroup property is a collection of objects that are instances of the PrintFile type.
The following snipped code shows how to set up and print a PDF file plus a Word (DOC) file that are located in an ASP.NET website folder:
//Create a ClientPrintJob and set the PrintFile objects
ClientPrintJob cpj = new ClientPrintJob();
//Add as many PrintFile objects as you want to print
//NOTE: _hostEnvironment is a var of type Microsoft.AspNetCore.Hosting.IHostingEnvironment
//declared in your Controller class whichs generates the ClientPrintJob obj
cpj.PrintFileGroup.Add(new PrintFile(_hostEnvironment.ContentRootPath + "/files/PaymentInstructions.pdf", "PaymentInstructions.pdf"));
cpj.PrintFileGroup.Add(new PrintFile(_hostEnvironment.ContentRootPath + "/files/Invoice.doc", "Invoice.doc"));
Printing multiple ClientPrintJob objects with one shot!
Suppose you need to send raw commands to a printer and at the same time print a document to another one, all in just one click... In these cases, the brand new ClientPrintJobGroup class comes to the rescue. You can create as many individual ClientPrintJob objects as you need and then specify them to the ClientPrintJobGroup class to get them processed at the client machine by the WCPP utility.
Sending the ClientPrintJob to the client
After you create a ClientPrintJob object, set its main properties like the target ClientPrinter and the PrinterCommands or PrintFile you want to print; then the final step is to send the ClientPrintJob to the client machine from your ASP.NET Core website. This must be done from a Controller class where you have to return a FileContentResult object (part of ASP.NET Core) which will be created by specifying the ClientPrintJob Content and specifying the MIME Type as "application/octet-stream". Here is a snippet of how you should do this:
The source code of the WebClientPrint Sample Website at github has a complete code for this topic.
//Creating a ClientPrintJob obj...
ClientPrintJob cpj = new ClientPrintJob();
//Setting properties like ClientPrinter and Commands or Files
//....
//Create and return a FileContentResult
//to be processed at client side by WCPP Utility
return File(cpj.GetContent(), "application/octet-stream");
WebClientPrint Scripting
The WebClientPrint Server Component allows you create ClientPrintJob objects that are sent to the client machine where the WCPP utility will process them. To launch the WCPP installed at the client machine, you need to add some script code to your View page. The WebClientPrint class provides you with all the needed to perform this task without efforts at all.
Creating the WebClientPrintAPIController Class
Before you can use the WebClientPrint solution in your ASP.NET Core website, the first thing to do is to create a new Controller and naming it WebClientPrintAPIController
After WebClientPrintController class is created, copy/paste the following code:
WebClientPrintAPIController requires Session (add a reference to "Microsoft.AspNetCore.Session" pachage in your project.json) so be sure you have Session enabled in the Startup class inside the ConfigureServices method. You should have or add a code like this: services.AddSession();
In addition to that, you should have or add this code to the Configure method in the same class: app.UseSession();
WebClientPrintAPIController also requires a Cache object to store user related stuff like the list of installed printers. In the code below it uses MemoryCache so be sure you have Cache enabled in the Startup class inside the ConfigureServices method. You should have or add a code like this: services.AddMemoryCache(); (NOTE: You can use another Caching technique if needed, in which case, you'll have to modify the caching related code in the WebClientPrint Controller class)
using System;
using Microsoft.AspNetCore.Mvc;
using Neodynamic.SDK.Web;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.AspNetCore.Authorization;
namespace YOUR_NAMESPACE_GOES_HERE.Controllers
{
[Authorize]
public class WebClientPrintAPIController : Controller
{
//IMPORTANT NOTE >>>>>>>>>>
// We're going to use MemoryCache to store users related staff like
// the list of printers and they have the WCPP client utility installed
// BUT you can change it based on your dev needs!!!
// For instance, you could use a Distributed Cache instead!
//>>>>>>>>>>>>>>>>>>>>>>>>>
private readonly IMemoryCache _MemoryCache;
public WebClientPrintAPIController(IMemoryCache memCache)
{
_MemoryCache = memCache;
}
[AllowAnonymous]
public IActionResult ProcessRequest()
{
//get session ID
string sessionID = HttpContext.Request.Query["sid"].ToString();
//get Query String
string queryString = HttpContext.Request.QueryString.Value;
try
{
//Determine and get the Type of Request
RequestType prType = WebClientPrint.GetProcessRequestType(queryString);
if (prType == RequestType.GenPrintScript ||
prType == RequestType.GenWcppDetectScript)
{
//Let WebClientPrint to generate the requested script
byte[] script = WebClientPrint.GenerateScript(Url.Action("ProcessRequest", "WebClientPrintAPI", null, HttpContext.Request.Scheme), queryString);
return File(script, "application/x-javascript", "WebClientPrintScript");
}
else if (prType == RequestType.ClientSetWcppVersion)
{
//This request is a ping from the WCPP utility
//so store the session ID indicating it has the WCPP installed
//also store the WCPP Version if available
string wcppVersion = HttpContext.Request.Query["wcppVer"];
if (string.IsNullOrEmpty(wcppVersion))
wcppVersion = "1.0.0.0";
_MemoryCache.Set(sessionID + "wcppInstalled", wcppVersion);
}
else if (prType == RequestType.ClientSetInstalledPrinters)
{
//WCPP Utility is sending the installed printers at client side
//so store this info with the specified session ID
string printers = HttpContext.Request.Query["printers"].ToString();
if (!string.IsNullOrEmpty(printers) && printers.Length > 0)
printers = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printers));
_MemoryCache.Set(sessionID + "printers", printers);
}
else if (prType == RequestType.ClientSetInstalledPrintersInfo)
{
//WCPP Utility is sending the client installed printers with detailed info
//so store this info with the specified session ID
//Printers Info is in JSON format
string printersInfo = HttpContext.Request.Form["printersInfoContent"];
if (string.IsNullOrEmpty(printersInfo) == false)
printersInfo = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printersInfo));
_MemoryCache.Set(sessionID + "printersInfo", printersInfo);
}
else if (prType == RequestType.ClientGetWcppVersion)
{
//return the WCPP version for the specified sid if any
bool sidWcppVersion = (_MemoryCache.Get<string>(sessionID + "wcppInstalled") != null);
return Ok(sidWcppVersion ? _MemoryCache.Get<string>(sessionID + "wcppInstalled") : "");
}
else if (prType == RequestType.ClientGetInstalledPrinters)
{
//return the installed printers for the specified sid if any
bool sidHasPrinters = (_MemoryCache.Get<string>(sessionID + "printers") != null);
return Ok(sidHasPrinters ? _MemoryCache.Get<string>(sessionID + "printers") : "");
}
else if (prType == RequestType.ClientGetInstalledPrintersInfo)
{
//return the installed printers with detailed info for the specified Session ID (sid) if any
bool sidHasPrinters = (_MemoryCache.Get<string>(sessionID + "printersInfo") != null);
return Ok(sidHasPrinters ? _MemoryCache.Get<string>(sessionID + "printersInfo") : "");
}
}
catch
{
return BadRequest();
}
return Ok();
}
}
}
Registering the WebClientPrint Script
In the View page where the user will perform the printing action e.g. through a button, hyperlink/anchor, script function, etc.; you must register WebClientPrint script.
Suppose you have a Controller class named DemoPrintCommandsController with a method called PrintCommands which generates and returns a ClientPrintJob object, then in the View where you allow users to print, the following code needs to be added inside the <body> tag:
IMPORTANT: Remember to add a link to jQuery 1.4.1+ in the View where you are going to use WebClientPrint script code.
@* Register the WebClientPrint script code. *@
@Html.Raw(Neodynamic.SDK.Web.WebClientPrint.CreateScript(Url.Action("ProcessRequest", "WebClientPrintAPI", null, Url.ActionContext.HttpContext.Request.Scheme), Url.Action("PrintCommands", "DemoPrintCommands", null, Url.ActionContext.HttpContext.Request.Scheme), Url.ActionContext.HttpContext.Session.Id))
This must be done once in your View and it will allow you to use the jsWebClientPrint (a javascript object) later on for starting the printing process or getting the list of installed client printers from the View.
Printing by using jsWebClientPrint
jsWebClientPrint is the javascript object that you will use to launch the WCPP utility at the client machine.
Before trying to use it, you must ensure you have done what is stated in the Creating the WebClientPrintAPIController Class and Registering the WebClientPrint Script topics.
The jsWebClientPrint object features a print() function that you can invoke from HTML or javascript code. The print() function is the one which will launch the WCPP utility at the client machine so it can process the ClientPrintJob generated from your ASP.NET Core website.
You can pass to the print() function any number of useful data/parameters that you might need to analyze in the method of your Controller class that generates the ClientPrintJob objects. For instance, in our sample code, you will find a javascript code that looks like the following:
<a onclick="javascript:jsWebClientPrint.print('useDefaultPrinter=' + $('#useDefaultPrinter').attr('checked') + '&printerName=' + $('#installedPrinterName').val() + '&filetype=' + $('#ddlFileType').val());">Print File...</a>
The data/parameters are expressed in the same way as it's done with the Query String and you can gather them from your Controller class.
Get Installed Printers
jsWebClientPrint allows you to get the list of installed printers available at the client machine by using the getPrinters() function. By "installed printers" it means the printers that are listed and installed under Windows, Linux or Mac OS.
Here's a simple code about how to use this feature. The code has an HTML button that when clicked, it invokes the getPrinters() function. There's also an HTML select element that will be filled with the installed printers if the code on getting the printers successes.
<input type="button" value="Load Installed Printers..." onclick="javascript:jsWebClientPrint.getPrinters();" />
<br />
<select id="installedPrintersList"></select>
And here is the javascript code to handle the installed printers:
The variable named as wcppGetPrintersTimeout_ms holds how much time (in milliseconds) the script code needs to wait for the installed printers list sent by WCPP from the client side to your website. The variable named as wcppGetPrintersTimeoutStep_ms specifies the period of time after which the script code will request the list of installed printers to the server. In the sample code below, we set a timeout to 10000 ms (i.e. 10 seconds) with a timeout step of 500 ms but you can change it to whatever you want.
<script type="text/javascript">
var wcppGetPrintersTimeout_ms = 10000; //10 sec
var wcppGetPrintersTimeoutStep_ms = 500; //0.5 sec
function wcpGetPrintersOnSuccess(){
// Display client installed printers
if(arguments[0].length > 0){
var p=arguments[0].split("|");
var options = '';
for (var i = 0; i < p.length; i++) {
options += '<option>' + p[i] + '</option>';
}
$('#installedPrintersList').html(options);
$('#installedPrintersList').focus();
}else{
alert("No printers are installed in your system.");
}
}
function wcpGetPrintersOnFailure() {
// Do something if printers cannot be got from the client
alert("No printers are installed in your system.");
}
</script>
Get Installed Printers with detailed info
jsWebClientPrint also allows you to get the list of installed printers available at the client machine with further details about them by using the getPrintersInfo() function. In addition to the printers name, you can also get info like Supported Trays and Papers, Port name, Vertical and Horizontal Resolution, whether the printer is connected, shared, default, local, network and more!
In the source code of the WebClientPrint Sample Website at github, you will find a complete sample source code using this feature.
Detecting whether WCPP is installed at the client machine
The WCPP utility (that the user must have it installed) is a key component of the WebClientPrint solution. So, it is very important you can detect somehow whether the WCPP is already installed at the client machine from your ASP.NET Core website. In the source code of the WebClientPrint Sample Website at github, you will find a complete sample source code using the detecting technique. However, here are the basic steps involved in this task.
Registering the WCPP detection Script
First of all, be sure you have already done the stated in the Creating the WebClientPrintAPIController Class topic. After that, you have to add a simple line of code in the ASP.NET HTML markup code of your View page. This View page is the one you will be using for detecting whether the user has the WCPP installed or not. It should be the initial page the user should visit before trying to go to the page where you provide the WebClientPrint functionality.
So, open the View page and add the following line of code inside the HTML
:
IMPORTANT: Remember to add a link to jQuery 1.4.1+ in the View where you are going to use WCPP Detection script code.
@* WCPP detection script *@
@Html.Raw(Neodynamic.SDK.Web.WebClientPrint.CreateWcppDetectionScript(Url.Action("ProcessRequest", "WebClientPrintAPI", null, Url.ActionContext.HttpContext.Request.Scheme), Url.ActionContext.HttpContext.Session.Id))
Handling the WCPP detection with JavaScript
Once you've registered the WCPP detection script in your View page, then you can handle the detection result i.e. success or failure and act according to it. You have to write javascript code inside two special functions for handling the detection result. Those functions are named as wcppDetectOnSuccess() and wcppDetectOnFailure()
So, in the View page you are using for "WCPP detection", you should add a javascript code like the following:
The variable named as wcppPingTimeout_ms holds how much time (in milliseconds) the detection script needs to wait for a ping from the WCPP at the client side to your website. In the sample code below, we set it to 10000 ms (i.e. 10 seconds) but you can change it to whatever you want.
<script type="text/javascript">
var wcppPingTimeout_ms = 10000; //10 sec
var wcppPingTimeoutStep_ms = 500; //0.5 sec
function wcppDetectOnSuccess(){
//WCPP utility is installed at the client side
//redirect to WebClientPrint sample page
//TIP! You can get the WCPP Version installed through arguments[0]
window.location.href = "URL_TO_SAMPLE_PAGE";
}
function wcppDetectOnFailure() {
//It seems WCPP is not installed at the client side
//kindly ask the user to install it
PROVIDE_DOWNLOAD_LINK_TO_WCPP_INSTALLER
}
</script>