// 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; }