The WebClientPrint Server Component
Overview
WebClientPrint is a client-server solution. The server-side component is a .NET Class Library (Neodynamic.SDK.WebClientPrint.dll) which is referenced by your ASP.NET website to generate Client Print Jobs.
A Client Print Job allows you to specify:
- Which 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 4.6.1+ or ASP.NET Core 2.0+
- System.Text.Encoding.CodePages 4.6.0+
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:
C#
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";
VB
Imports Neodynamic.SDK.Web
'...
'Create a ClientPrintJob obj that will be processed at the client side by the WCPP
Dim cpj As 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:
C#
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";
VB
Imports Neodynamic.SDK.Web
'...
'Create a ClientPrintJob obj that will be processed at the client side by the WCPP
Dim cpj As 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.
Note
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:
C#
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";
VB
Imports Neodynamic.SDK.Web
'...
'Create a ClientPrintJob obj that will be processed at the client side by the WCPP
Dim 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"
Note
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:
C#
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";
VB
Imports Neodynamic.SDK.Web
'...
'Create a ClientPrintJob obj that will be processed at the client side by the WCPP
Dim cpj As 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"
You can also specify the Tray Name as well as the Paper Name to be used by the specified printer when printing the following file formats: PNG, JPG, BMP, TIF, TXT & PDF
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.
Note
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:
C#
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";
VB
Imports Neodynamic.SDK.Web
'...
'Create a ClientPrintJob obj that will be processed at the client side by the WCPP
Dim cpj As 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.
Note
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:
C#
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";
VB
Imports Neodynamic.SDK.Web
'...
'Create a ClientPrintJob obj that will be processed at the client side by the WCPP
Dim cpj As 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.
Note
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:
C#
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";
VB
Imports Neodynamic.SDK.Web
'...
'Create a ClientPrintJob obj that will be processed at the client side by the WCPP
Dim cpj As 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:
C#
//set the commands to send to the printer
cpj.PrinterCommands = "0x1B C 0x00 5";
//enable Hex notation for commands
cpj.FormatHexValues = true;
VB
'set the commands to send to the printer
cpj.PrinterCommands = "&H1B C &H00 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.
C#
//get and set the commands to send to the printer from a file
cpj.BinaryPrinterCommands = System.IO.File.ReadAllBytes("c:/temp/invoice.prn");
VB
'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, TIF (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! |
TIF | Natively supported! | Natively supported! |
JPG | 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 for printing files but for printing raw commands. | 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.
Note
The source code of the WebClientPrint Sample Website at github has a complete code for testing this feature.
Encrypting File Content
WebClientPrint allows you to protect the content of the file to be printed (supported only for PDF, TIF, TXT, JPG, and PNG formats) by encrypting them at the server side. WebClientPrint uses RSA asymetric encryption with a dynamic public key generated by WCPP utility for each printing request to encrypt the AES-256 params from your website. The file to print is encrypted by using AES-256 symetric encryption in the server side and stored at the client machine disk. At printing time, WCPP will decrypt the file in system memory to print it. The involved file, is kept in client system temp folder but with the content being encrypted and unusable, and after printing, it's deleted.
The Print File Encryption feature has the following clients support:
File Format | Windows Clients | Linux & Mac Clients |
---|---|---|
✔ | ✘ | |
TIF | ✔ | ✔ |
TXT | ✔ | ✔ |
JPEG | ✔ | ✔ |
PNG | ✔ | ✔ |
BMP | ✔ | ✔ |
Others | ✘ | ✘ |
To enable file encryption, the dynamic publick key sent by the WCPP utility must be set to the PrintFile's EncryptMetadata property. In addition, the encrypt=true
param must be spcified to the jsWebClientPrint.print()
javascript function. For example:
<a onclick="javascript:jsWebClientPrint.print('encrypt=true' '&useDefaultPrinter=' + $('#useDefaultPrinter').attr('checked') + '&printerName=' + $('#installedPrinterName').val() + '&filetype=' + $('#ddlFileType').val());">Print Encrypted File...</a>
Note
The source code of the WebClientPrint Sample Website at github has a complete code for testing this feature.
Password Protected Files
WebClientPrint allows you to print password protected PDF, DOC & XLS files. The password protected file is set at server side and the password to unlock it is embedded into the file encrypted with RSA as part of the metadata file. A dynamic RSA public key sent by WCPP utility is signed and then verified at server side. At printing time, WCPP will decrypt the password to unlock the file in system memory to finally print it. The involved file, is kept in client system temp folder with the content being protected by a password and after printing, it's deleted.
The Print Password Protected File feature has the following clients support:
File Format | Windows Clients | Linux & Mac Clients |
---|---|---|
✔ | ✘ | |
DOC | ✔ | ✘ |
XLS | ✔ | ✘ |
Others | ✘ | ✘ |
To enable password file encryption, the dynamic publick key sent by the WCPP utility must be set to the PrintFile's EncryptMetadata property. In addition, the encryptPassword=true
param must be spcified to the jsWebClientPrint.print()
javascript function. For example:
<a onclick="javascript:jsWebClientPrint.print('encryptPassword=true' '&useDefaultPrinter=' + $('#useDefaultPrinter').attr('checked') + '&printerName=' + $('#installedPrinterName').val() + '&filetype=' + $('#ddlFileType').val());">Print Encrypted File...</a>
Note
The source code of the WebClientPrint Sample Website at github has a complete code for testing this feature.
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.
Checkout the WebClientPrint Articles section for sample code about this topic.
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.
Checkout the WebClientPrint Articles section for sample code about this topic.
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 website. This must be done from a Controller or HTTP Handler class where you have to return the binary content which will be created by invoking the ClientPrintJob Content and specifying the MIME Type as "application/octet-stream".
Note
The source code of the WebClientPrint Sample Website at github has a complete code for this topic so please take a look at it!
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 or WebForm page. The WebClientPrint class provides you with all the needed to perform this task without efforts at all.
Creating the WebClientPrintAPI Class
Note
The source code of the WebClientPrint Sample Website at github has a complete code for this topic so please take a look at it!
For ASP.NET CORE MVC Projects
Before you can use the WebClientPrint solution in your ASP.NET CORE MVC website, the first thing to do is to create a new Controller and name it WebClientPrintAPIController
After WebClientPrintAPIController class is created, copy/paste the following code:
C#
using System;
using Microsoft.AspNetCore.Mvc;
using Neodynamic.SDK.Web;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.AspNetCore.Authorization;
namespace WCPAspNetCoreCS.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();
}
}
}
For ASP.NET MVC Projects
Before you can use the WebClientPrint solution in your ASP.NET MVC website, the first thing to do is to create a new Controller and name it WebClientPrintAPIController
After WebClientPrintAPIController class is created, copy/paste the following code:
C#
using System;
using System.Web;
using System.Web.Mvc;
using Neodynamic.SDK.Web;
namespace WCPMVCCS.Controllers
{
//*********************************
// IMPORTANT NOTE
// In this sample we store users related stuff (like
// the list of printers and whether they have the WCPP
// client utility installed) in the Application cache
// object part of ASP.NET BUT you can change it to
// another different storage (like a DB or file server)!
// which will be required in Load Balacing scenarios
//*********************************
public class WebClientPrintAPIController : Controller
{
[AllowAnonymous]
public void ProcessRequest()
{
//get session ID
string sessionID = (HttpContext.Request["sid"] != null ? HttpContext.Request["sid"] : null);
//get Query String
string queryString = HttpContext.Request.Url.Query;
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.Url.Scheme), queryString);
HttpContext.Response.ContentType = "text/javascript";
HttpContext.Response.BinaryWrite(script);
HttpContext.Response.End();
}
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["wcppVer"];
if (string.IsNullOrEmpty(wcppVersion))
wcppVersion = "1.0.0.0";
HttpContext.Application.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["printers"];
if (string.IsNullOrEmpty(printers) == false)
printers = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printers));
HttpContext.Application.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.Params["printersInfoContent"];
if (string.IsNullOrEmpty(printersInfo) == false)
printersInfo = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printersInfo));
HttpContext.Application.Set(sessionID + "printersInfo", printersInfo);
}
else if (prType == RequestType.ClientGetWcppVersion)
{
//return the WCPP version for the specified sid if any
bool sidWcppVersion = (HttpContext.Application.Get(sessionID + "wcppInstalled") != null);
HttpContext.Response.ContentType = "text/plain";
HttpContext.Response.Write((sidWcppVersion ? HttpContext.Application.Get(sessionID + "wcppInstalled") : ""));
HttpContext.Response.End();
}
else if (prType == RequestType.ClientGetInstalledPrinters)
{
//return the installed printers for the specified sid if any
bool sidHasPrinters = (HttpContext.Application.Get(sessionID + "printers") != null);
HttpContext.Response.ContentType = "text/plain";
HttpContext.Response.Write((sidHasPrinters ? HttpContext.Application.Get(sessionID + "printers") : ""));
HttpContext.Response.End();
}
else if (prType == RequestType.ClientGetInstalledPrintersInfo)
{
//return the installed printers with detailed info for the specified Session ID (sid) if any
bool sidHasPrinters = (HttpContext.Application[sessionID + "printersInfo"] != null);
HttpContext.Response.ContentType = "text/plain";
HttpContext.Response.Write(sidHasPrinters ? HttpContext.Application[sessionID + "printersInfo"] : "");
}
}
catch (Exception ex)
{
HttpContext.Response.StatusCode = 500;
HttpContext.Response.ContentType = "text/plain";
HttpContext.Response.Write(ex.Message + " - StackTrace: " + ex.StackTrace);
HttpContext.Response.End();
}
}
}
}
VB
Imports System.Web.Mvc
Imports Neodynamic.SDK.Web
Namespace Controllers
Public Class WebClientPrintAPIController
Inherits Controller
' GET: WebClientPrintAPI
Function Index() As ActionResult
Return View()
End Function
'*********************************
' IMPORTANT NOTE
' In this sample we store users related stuff (like
' the list of printers and whether they have the WCPP
' client utility installed) in the Application cache
' object part of ASP.NET BUT you can change it to
' another different storage (like a DB or file server)!
' which will be required in Load Balacing scenarios
'*********************************
<AllowAnonymous>
Public Sub ProcessRequest()
'get session ID
Dim sessionID As String = (If(HttpContext.Request("sid") IsNot Nothing, HttpContext.Request("sid"), Nothing))
'get Query String
Dim queryString As String = HttpContext.Request.Url.Query
Try
'Determine and get the Type of Request
Dim prType As RequestType = WebClientPrint.GetProcessRequestType(queryString)
If prType = RequestType.GenPrintScript OrElse prType = RequestType.GenWcppDetectScript Then
'Let WebClientPrint to generate the requested script
Dim script As Byte() = WebClientPrint.GenerateScript(Url.Action("ProcessRequest", "WebClientPrintAPI", Nothing, HttpContext.Request.Url.Scheme), queryString)
HttpContext.Response.ContentType = "text/javascript"
HttpContext.Response.BinaryWrite(script)
HttpContext.Response.End()
ElseIf prType = RequestType.ClientSetWcppVersion Then
'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
Dim wcppVersion As String = HttpContext.Request("wcppVer")
If String.IsNullOrEmpty(wcppVersion) Then
wcppVersion = "1.0.0.0"
End If
HttpContext.Application.Set(sessionID & "wcppInstalled", wcppVersion)
ElseIf prType = RequestType.ClientSetInstalledPrinters Then
'WCPP Utility is sending the installed printers at client side
'so store this info with the specified session ID
Dim printers As String = HttpContext.Request("printers")
If String.IsNullOrEmpty(printers) = False Then
printers = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printers))
End If
HttpContext.Application.Set(sessionID & "printers", printers)
ElseIf prType = RequestType.ClientSetInstalledPrintersInfo Then
'WCPP Utility is sending the installed printers at client side
'so store this info with the specified session ID
'Printers Info is in JSON format
Dim printersInfo As String = Httpcontext.Request.Params("printersInfoContent")
If Not String.IsNullOrEmpty(printersInfo) Then
printersInfo = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printersInfo))
End If
HttpContext.Application.Set(sessionID & "printersInfo", printersInfo)
ElseIf prType = RequestType.ClientGetWcppVersion Then
'return the WCPP version for the specified sid if any
Dim sidWcppVersion As Boolean = (HttpContext.Application(sessionID & "wcppInstalled") IsNot Nothing)
HttpContext.Response.ContentType = "text/plain"
If (sidWcppVersion) Then
HttpContext.Response.Write(HttpContext.Application(sessionID & "wcppInstalled").ToString())
End If
HttpContext.Response.End()
ElseIf prType = RequestType.ClientGetInstalledPrinters Then
'return the installed printers for the specified sid if any
Dim sidHasPrinters As Boolean = (HttpContext.Application(sessionID & "printers") IsNot Nothing)
HttpContext.Response.ContentType = "text/plain"
If (sidHasPrinters) Then
HttpContext.Response.Write(HttpContext.Application(sessionID & "printers").ToString())
End If
HttpContext.Response.End()
ElseIf prType = RequestType.ClientGetInstalledPrintersInfo Then
'return the installed printers with detailed info for the specified Session ID (sid) if any
Dim sidHasPrinters As Boolean = (HttpContext.Application(sessionID & "printersInfo") IsNot Nothing)
HttpContext.Response.ContentType = "text/plain"
If (sidHasPrinters) Then
HttpContext.Response.Write(HttpContext.Application(sessionID & "printersInfo").ToString())
End If
HttpContext.Response.End()
End If
Catch ex As Exception
HttpContext.Response.StatusCode = 500
HttpContext.Response.ContentType = "text/plain"
HttpContext.Response.Write(ex.Message + " - StackTrace: " + ex.StackTrace)
HttpContext.Response.End()
End Try
End Sub
End Class
End Namespace
For ASP.NET WebForms Projects
Before you can use the WebClientPrint solution in your ASP.NET WebForms website, the first thing to do is to create a new Generic Handler and name it WebClientPrintAPI
After WebClientPrintAPI class is created, copy/paste the following code:
C#
using System;
using System.Web;
using System.IO;
using Neodynamic.SDK.Web;
public class WebClientPrintAPI : IHttpHandler {
//*********************************
// IMPORTANT NOTE
// In this sample we store users related stuff (like
// the list of printers and whether they have the WCPP
// client utility installed) in the Application cache
// object part of ASP.NET BUT you can change it to
// another different storage (like a DB or file server)!
// which will be required in Load Balacing scenarios
//*********************************
public void ProcessRequest (HttpContext context) {
//get session ID
string sessionID = (context.Request["sid"] != null) ? context.Request["sid"].ToString() : null;
//get Query String
string queryString = context.Request.Url.Query;
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(context.Request.Url.AbsoluteUri.Replace(queryString, ""), queryString);
context.Response.ContentType = "text/javascript";
context.Response.BinaryWrite(script);
}
else if (prType == RequestType.ClientSetWcppVersion)
{
//This request is a ping from the WCPP utility
//so store the session ID indicating this user has the WCPP installed
//also store the WCPP Version if available
string wcppVersion = context.Request["wcppVer"];
if (string.IsNullOrEmpty(wcppVersion))
wcppVersion = "1.0.0.0";
context.Application.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 = context.Request["printers"];
if (string.IsNullOrEmpty(printers) == false)
printers = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printers));
context.Application.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 = context.Request.Params["printersInfoContent"];
if (string.IsNullOrEmpty(printersInfo) == false)
printersInfo = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printersInfo));
context.Application.Set(sessionID + "printersInfo", printersInfo);
}
else if (prType == RequestType.ClientGetWcppVersion)
{
//return the WCPP version for the specified Session ID (sid) if any
bool sidWcppVersion = (context.Application[sessionID + "wcppInstalled"] != null);
context.Response.ContentType = "text/plain";
context.Response.Write(sidWcppVersion ? context.Application[sessionID + "wcppInstalled"] : "");
}
else if (prType == RequestType.ClientGetInstalledPrinters)
{
//return the installed printers for the specified Session ID (sid) if any
bool sidHasPrinters = (context.Application[sessionID + "printers"] != null);
context.Response.ContentType = "text/plain";
context.Response.Write(sidHasPrinters ? context.Application[sessionID + "printers"] : "");
}
else if (prType == RequestType.ClientGetInstalledPrintersInfo)
{
//return the installed printers with detailed info for the specified Session ID (sid) if any
bool sidHasPrinters = (context.Application[sessionID + "printersInfo"] != null);
context.Response.ContentType = "text/plain";
context.Response.Write(sidHasPrinters ? context.Application[sessionID + "printersInfo"] : "");
}
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
context.Response.ContentType = "text/plain";
context.Response.Write(ex.Message + " - " + ex.StackTrace);
}
}
public bool IsReusable {
get {
return false;
}
}
}
VB
Imports System
Imports System.Web
Imports Neodynamic.SDK.Web
Public Class WebClientPrintAPI : Implements IHttpHandler
'*********************************
' IMPORTANT NOTE
' In this sample we store users related stuff (like
' the list of printers and whether they have the WCPP
' client utility installed) in the Application cache
' object part of ASP.NET BUT you can change it to
' another different storage (like a DB or file server)!
' which will be required in Load Balacing scenarios
'*********************************
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
'get session ID
Dim sessionID As String = ""
If (context.Request("sid") IsNot Nothing) Then
sessionID = context.Request("sid")
End If
'get Query String
Dim queryString As String = context.Request.Url.Query
Try
'Determine and get the Type of Request
Dim prType As RequestType = WebClientPrint.GetProcessRequestType(queryString)
If prType = RequestType.GenPrintScript OrElse prType = RequestType.GenWcppDetectScript Then
'Let WebClientPrint to generate the requested script
Dim script As Byte() = WebClientPrint.GenerateScript(context.Request.Url.AbsoluteUri.Replace(queryString, ""), queryString)
context.Response.ContentType = "text/javascript"
context.Response.BinaryWrite(script)
ElseIf prType = RequestType.ClientSetWcppVersion Then
'This request is a ping from the WCPP utility
'so store the session ID indicating this user has the WCPP installed
'also store the WCPP Version if available
Dim wcppVersion As String = context.Request("wcppVer")
If String.IsNullOrEmpty(wcppVersion) Then
wcppVersion = "1.0.0.0"
End If
context.Application.Set(sessionID & "wcppInstalled", wcppVersion)
ElseIf prType = RequestType.ClientSetInstalledPrinters Then
'WCPP Utility is sending the installed printers at client side
'so store this info with the specified session ID
Dim printers As String = context.Request("printers")
If Not String.IsNullOrEmpty(printers) Then
printers = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printers))
End If
context.Application.Set(sessionID & "printers", printers)
ElseIf prType = RequestType.ClientSetInstalledPrintersInfo Then
'WCPP Utility is sending the installed printers at client side
'so store this info with the specified session ID
'Printers Info is in JSON format
Dim printersInfo As String = context.Request.Params("printersInfoContent")
If Not String.IsNullOrEmpty(printersInfo) Then
printersInfo = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(printersInfo))
End If
context.Application.Set(sessionID & "printersInfo", printersInfo)
ElseIf prType = RequestType.ClientGetWcppVersion Then
'return the WCPP version for the specified Session ID (sid) if any
Dim sidWcppVersion As Boolean = (context.Application(sessionID & "wcppInstalled") IsNot Nothing)
context.Response.ContentType = "text/plain"
If (sidWcppVersion) Then
context.Response.Write(context.Application(sessionID & "wcppInstalled").ToString())
End If
ElseIf prType = RequestType.ClientGetInstalledPrinters Then
'return the installed printers for the specified Session ID (sid) if any
Dim sidHasPrinters As Boolean = (context.Application(sessionID & "printers") IsNot Nothing)
context.Response.ContentType = "text/plain"
If (sidHasPrinters) Then
context.Response.Write(context.Application(sessionID & "printers").ToString())
End If
ElseIf prType = RequestType.ClientGetInstalledPrintersInfo Then
'return the installed printers with detailed info for the specified Session ID (sid) if any
Dim sidHasPrinters As Boolean = (context.Application(sessionID & "printersInfo") IsNot Nothing)
context.Response.ContentType = "text/plain"
If (sidHasPrinters) Then
context.Response.Write(context.Application(sessionID & "printersInfo").ToString())
End If
End If
Catch ex As Exception
context.Response.StatusCode = 500
context.Response.ContentType = "text/plain"
context.Response.Write(ex.Message + " - " + ex.StackTrace)
End Try
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Registering the WebClientPrint Script
For ASP.NET CORE MVC Projects
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:
@* 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.
For ASP.NET MVC Projects
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:
@* Register the WebClientPrint script code. The param is the URL for the PrintCommands method in the DemoPrintCommandsController. *@
@Html.Raw(Neodynamic.SDK.Web.WebClientPrint.CreateScript(Url.Action("ProcessRequest", "WebClientPrintAPI", null, HttpContext.Current.Request.Url.Scheme), Url.Action("PrintCommands", "DemoPrintCommands", null, HttpContext.Current.Request.Url.Scheme), HttpContext.Current.Session.SessionID)))
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.
For ASP.NET WebForms Projects
In the WebForm 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 Generic Handler class named DemoPrintCommands which generates and returns a ClientPrintJob object, then in the WebForm page where you allow users to print, the following code needs to be added inside the <body> tag:
<%-- Register the WebClientPrint script code --%>
<%=Neodynamic.SDK.Web.WebClientPrint.CreateScript(HttpContext.Current.Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Host + ":" + HttpContext.Current.Request.Url.Port + "/WebClientPrintAPI.ashx", HttpContext.Current.Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Host + ":" + HttpContext.Current.Request.Url.Port + "/DemoPrintCommandsHandler.ashx", HttpContext.Current.Session.SessionID)%>
This must be done once in your WebForm 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.
Important
Before trying to use it, you must ensure you have done what is stated in the Creating the WebClientPrintAPI 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 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:
HTML/JS
<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 or Generic Handler 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.
HTML/JS
<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:
Note
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.
HTML/JS
<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 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 WebClientPrintAPI Class topic. After that, you have to add a simple line of code in the ASP.NET HTML markup code of your View page or WebForm. This View page or WebForm 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
:
For ASP.NET CORE MVC Projects
@* 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))
For ASP.NET MVC Projects
@* WCPP detection script *@
@Html.Raw(Neodynamic.SDK.Web.WebClientPrint.CreateWcppDetectionScript(Url.Action("ProcessRequest", "WebClientPrintAPI", null, HttpContext.Current.Request.Url.Scheme), HttpContext.Current.Session.SessionID))
For ASP.NET WebForms Projects
<%-- WCPP detection script code --%>
<%=Neodynamic.SDK.Web.WebClientPrint.CreateWcppDetectionScript(HttpContext.Current.Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Host + ":" + HttpContext.Current.Request.Url.Port + "/WebClientPrintAPI.ashx", HttpContext.Current.Session.SessionID)%>
Handling the WCPP detection with JavaScript
Once you've registered the WCPP detection script in your View page or WebForm, 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 or WebForm you are using for "WCPP detection", you should add a javascript code like the following:
Note
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.
HTML/JS
<script type="text/javascript">
var wcppPingTimeout_ms = 60000; //60 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>