// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
// File Name:
//
// USBMON_Bidi_JavaScript_File.js
//
// Abstract:
//
// Sample USBMON Javascript extension file for v4 printer drivers.
//
/// Base64 Encode from
///
/// @param input - binary buffer
///
/// @return String - Encoding Result
///
function Encode64( input )
{
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
do {
chr1 = input[i++];
chr2 = input[i++];
chr3 = input[i++];
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
} while (i < input.length);
return output;
}
/// Make PJL Command
///
/// @param Query - PJL String
///
/// @return String - added prefix and postfix data
///
function GetPJLCommand(Query) {
var Prefix = [0x1B, 0x25, 0x2D, 0x31, 0x32, 0x33, 0x34, 0x35, 0x58, 0x40, 0x50, 0x4A, 0x4C, 0x0D, 0x0A];
var Postfix = [0x1B, 0x25, 0x2D, 0x31, 0x32, 0x33, 0x34, 0x35, 0x58];
var Command = Prefix.concat(Query, Postfix);
return Command;
}
/// Read Command from device
///
/// @param printerStream - Printer stream for communication
/// @Param Header - Command Header
/// @Param bufferSize - Minimum Buffer Size
///
/// @return String - read data that exclude Header data.
///
function ReadString(printerStream, Header, bufferSize) {
var nBufferSize = 50;
if (bufferSize > nBufferSize)
nBufferSize = bufferSize;
var ReadData = "";
for (var index = 0; index < 3; index++) {
var readBuffer = printerStream.read(nBufferSize);
if ((null === readBuffer) || (0 >= readBuffer.length))
break;
if (readBuffer.length <= Header.length)
continue;
var Data = "";
for (var i = 0; i < readBuffer.length; i++) {
Data += String.fromCharCode(readBuffer[i]);
}
var nPos = -1;
if ("" !== Header)
nPos = Data.search(Header);
if (-1 < nPos) {
ReadData = Data.substring(Header.length);
break;
}
}
return ReadData;
}
///
/// Get the requested Schema(s).
///
/// The script can use the 'schemaRequests' object to iterate through the Query Keys requested by the user. Based on the query keys,
/// the script should use the 'printerStream' object to communicate with the USB print device and determine the values of one or more Bidi
/// Schema elements. For each Bidi Schema element the new value can be returned to the caller by using functions of the 'printerBidiSchemaResponses'
/// object. Once all query keys have been processed and all values added to the 'printerBidiSchemaResponses' object the script can return.
///
/// It is possible the attached device is not ready to return some of the requested data. In this case the function can return a value of 1 to indicate the call
/// should be retried after a wait.
///
///
/// Script context object.
///
///
/// Allows the script to Write/Read data from the attached USB device.
///
///
/// Array of strings that contains all the requested Query Keys.
///
///
/// Object the script will use to store all responses to query keys.
///
///
/// Integer value indicating function completion status.
/// 1 - The attached device was not ready to provide some requested information. Call the function again using any Requery Keys added during processing.
/// 0 - The script completed successfuly.
///
function getSchemas( scriptContext, printerStream, schemaRequests, printerBidiSchemaResponses )
{
var retVal = 0;
var i = 0;
debugger;
var bag = scriptContext.QueueProperties;
var queueProperty = bag.GetString("Config:Win8Support");
//Loop through all the QueryKeys provided in the schemaRequests object
for ( var index = 0; index < schemaRequests.length; index++ ) {
var key = schemaRequests[index];
if (("On" !== queueProperty) && ("DEVICEID" !== key)) {
if ("Off" !== queueProperty) {
retVal = 1;
printerBidiSchemaResponses.addRequeryKey(key);
}
continue;
}
if (key === "DEVICEID") {
//Request: @PJL INFO ID
//Response: @PJL INFO ID\n"Samsung ML-375x Series"
var bId = false;
var Id = "";
var Data = [0x40, 0x50, 0x4A, 0x4C, 0x20, 0x49, 0x4E, 0x46, 0x4F, 0x20, 0x49, 0x44, 0x0D, 0x0A];
var writeData = GetPJLCommand(Data);
var bytesWritten = printerStream.write(writeData);
if (bytesWritten === writeData.length) {
var result = ReadString(printerStream, "@PJL INFO ID", 128);
if ("" !== result) {
var nStart = result.indexOf('\"');
var nEnd = result.lastIndexOf('\"');
Id = result.substring(nStart+1, nEnd);
if ("" !== Id)
bId = true;
}
}
if (false === bId) {
continue;
}
printerBidiSchemaResponses.addString("\\Printer.DeviceInfo:Id", Id);
//Request: @PJL GETDEVICEID
//Response: @PJL GETDEVICEID "deviceid.."
var bDeviceId = false;
var DeviceId = "";
var Data = [0x40, 0x50, 0x4A, 0x4C, 0x20, 0x47, 0x45, 0x54, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x49, 0x44, 0x0D, 0x0A];
var writeData = GetPJLCommand(Data);
var bytesWritten = printerStream.write( writeData );
if (bytesWritten === writeData.length) {
var result = ReadString(printerStream, "@PJL GETDEVICEID=", 512);
DeviceId = result.replace(/\"/g, '');
if ("" !== DeviceId)
{
bDeviceId = true;
if (-1 < DeviceId.search(/win8/i))
printerBidiSchemaResponses.addBool("\\Printer.Samsung.AutoConfiguration:Supported", true);
}
}
if (false === bDeviceId) {
if (bId)
printerBidiSchemaResponses.addBool("\\Printer.Samsung.AutoConfiguration:Supported", false);
}
printerBidiSchemaResponses.addString("\\Printer.DeviceInfo:DeviceId", DeviceId);
}
else if (key === "DEVICEIMAGE") {
//Request: 1B 91 00 00 00 00 00 03 00 00 00 01 00 00 00 00
//Response: 1B 91 00 00 00 00 00 03 00 00 00 01 01 12 24 33
// Image data
var ImageData = "";
var writeData = [0x1B, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00];
var bytesWritten = printerStream.write(writeData);
if (bytesWritten === writeData.length) {
for (i = 0; i < 3; i++) {
var readBuffer = printerStream.read(50);
if ((null === readBuffer) || (0 === readBuffer.length))
break;
if (readBuffer.length == 16) {
if ((readBuffer[0] !== 0x1B) || (readBuffer[1] !== 0x91))
continue;
var size = 0;
for (i = 0; i < 4; i++) {
size *= 256;
size += readBuffer[12 + i];
}
if (size > 0) {
var readBuffer2 = printerStream.read(size);
if ((null != readBuffer2) && (readBuffer2.length === size)) {
ImageData = Encode64(readBuffer2);
}
}
break;
}
}
}
printerBidiSchemaResponses.addString("\\Printer.DeviceInfo:Image", ImageData);
}
else if (key === "LITESM_SPEC") {
//Request: @PJL LITESMSPEC
//Response: @PJL LITESMSPEC .....
var SpecXml = "";
var Data = [0x40, 0x50, 0x4A, 0x4C, 0x20, 0x4C, 0x49, 0x54, 0x45, 0x53, 0x4D, 0x53, 0x50, 0x45, 0x43, 0x0D, 0x0A];
var writeData = GetPJLCommand(Data);
var bytesWritten = printerStream.write( writeData );
if (bytesWritten === writeData.length) {
SpecXml = ReadString(printerStream, "@PJL LITESMSPEC ", 102400);
}
printerBidiSchemaResponses.addString("\\Printer.Samsung.LiteSM:Specification", SpecXml);
}
else if (key === "LITESM_CAP") {
var CapData = "";
//Request: 1B 24 F4 00 00 00 00 00 00 00
//Response: 1B 24 F4 00 00 00 00 01 24 13
var writeData = [ 0x1B, 0x24, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ];
var bytesWritten = printerStream.write( writeData );
if (bytesWritten === writeData.length) {
for (i = 0; i < 3; i++) {
var readBuffer = printerStream.read(50);
if ((null === readBuffer) || (0 === readBuffer.length))
break;
if (readBuffer.length === 10) {
if ((readBuffer[0] !== 0x1B) || (readBuffer[1] !== 0x24) || (readBuffer[2] !== 0xF4))
continue;
var Size = 0;
for (i = 0; i < 4; i++) {
Size *= 256;
Size += readBuffer[6 + i];
}
//Request: 1B 24 F4 01 00 00 00 00 00 00
//Response: .....
var writeData2 = [0x1B, 0x24, 0xF4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
var byteWritten2 = printerStream.write(writeData2);
if (byteWritten2 === writeData2.length) {
var readBuffer2 = printerStream.read(Size);
if ((null !== readBuffer2) && (readBuffer2.length === Size)) {
for (i = 0; i < readBuffer2.length; i++) {
CapData += String.fromCharCode(readBuffer2[i]);
}
}
}
break;
}
}
}
printerBidiSchemaResponses.addString("\\Printer.Samsung.LiteSM:Capability", CapData);
}
else if (key === "LITESM_STATUS") {
//Request: @PJL LITESMSTATUS
//Response: @PJL LITESMSTATUS STATUSCODE=0401020354FFFFFF
var SMStat = "";
var Data = [0x40, 0x50, 0x4A, 0x4C, 0x20, 0x4C, 0x49, 0x54, 0x45, 0x53, 0x4D, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x0D, 0x0A];
var writeData = GetPJLCommand(Data);
var bytesWritten = printerStream.write(writeData);
if (bytesWritten === writeData.length) {
SMStat = ReadString(printerStream, "@PJL LITESMSTATUS STATUSCODE=", 45);
}
printerBidiSchemaResponses.addString("\\Printer.Samsung.LiteSM:Status", SMStat);
}
}
return retVal;
}
///
/// Set a requested Bidi Schema Value in the device
///
/// @param scriptContext - Script context object which allows access to IPrinterScriptablePropertyBag for printer driver and queue properties
/// @param printerStream - Allows the script to Write/Read data from the attached USB device
/// @param printerBidiSchemaElement - Contains all the data associated with the Bidi Schema Value to set
///
/// @return Integer value - currently only JavaScript exceptions are expected for failures
///
/// The script can interpret the incoming Bidi Schema value to either set data in the device or perform an action on the device.
///
/// The scriptContext object provides access to the Driver and Queue prorperty bags associated with the current device. These property bags support read-only access.
///
function setSchema( scriptContext, printerStream, printerBidiSchemaElement )
{
return 0;
}
///
/// Retrieve unsolicited Bidi Schema value updates from the USB device during printing.
///
/// This function is only called when a job is printing. A device can provide data on the read channel which this script can interpret into
/// Bidi Schema values and returned to USBMon.
///
/// This function will be called repeatedly during printing. It is expected the device will only return data if it is available and the script can understand it.
/// If the device does not support querying for unsolicited status or the script can determine that there is no need to call this function again, the script can return
/// a value of 2 which will tell the getStatus execution thread in USBMon to exit successfully.
///
/// If the print device does not support retrieving status during a print job this function should be left out of the driver's JavaScipt file altogether. This will inform
/// USBMon to skip invocation of the function.
///
///
/// Accessor for PropertyBags for printer driver and queue properties.
///
///
/// Allows the script to read data from the attached USB device. Calling the write function will fail. This device is opened read-only for this function.
///
///
/// Object the script will use to store all status responses.
///
///
/// Integer value indicating function completion status.
/// 2 - The device no longer (if ever) supports unsolicited status so no need for USBMon to make more calls to this function.
/// 0 - The script completed successfuly.
///
function getStatus( scriptContext, printerStream, printerBidiSchemaResponses )
{
var retVal = 0;
debugger;
var bag = scriptContext.QueueProperties;
var queueProperty = bag.GetString("Config:Win8Support");
if ("Off" === queueProperty) {
return 2;
}
//sample: @PJL DRIVERSTATUS STATUSCODE=00040002
var bReadStatus = false;
var readBuffer = printerStream.read(50);
if (null !== readBuffer) {
var bytesRead = readBuffer.length;
var Header = "@PJL DRIVERSTATUS STATUSCODE=";
if (bytesRead > Header.length) {
var Read = "";
for (i = 0; i < bytesRead; i++)
Read += String.fromCharCode(readBuffer[i]);
if (-1 < Read.search(Header)) {
var Category = Read.substr(Header.length, 4).toUpperCase();
var Status = Read.substr(Header.length + 4, 4).toUpperCase();
if (Category === "0001") { //None
if (Status === "0001")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "None");
}
else if (Category === "0002") { //Door
if (Status === "0001")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "DoorOpen");
}
else if (Category === "0003") { //Marker
if (Status === "0001")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "MarkerSupplyLow");
else if (Status === "0002")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "MarkerFailure");
else if (Status === "0003")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "MarkerSupplyEmpty");
}
else if (Category === "0004") { //Media
if (Status === "0001")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "MediaLow");
else if (Status === "0002")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "MediaEmpty");
else if (Status === "0003")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "MediaJam");
else if (Status === "0004")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "MediaNeeded");
}
else if (Category === "0005") { //Output
if (Status === "0001")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "OutputAreaAlmostFull");
else if (Status === "0002")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "OutputAreaFull");
}
else if (Category === "F000") { //AttentionRequired
if (Status === "0001")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "AttentionRequired");
}
else if (Category === "F100") { //Paused
if (Status === "0001")
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "Paused");
}
else if (Category === "FFFF") { //Job End
if (Status === "FFFF") {
printerBidiSchemaResponses.addString("\\Printer.Status.Summary:StateReason", "None");
retVal = 2;
}
}
bReadStatus = true;
}
}
}
if ("None" === queueProperty) {
if (bReadStatus) {
printerBidiSchemaResponses.addBool("\\Printer.Samsung.AutoConfiguration:Supported", true);
}
else {
printerBidiSchemaResponses.addBool("\\Printer.Samsung.AutoConfiguration:Supported", false);
retVal = 2;
}
}
return retVal;
}