/// function getSchemas(scriptContext, printerStream, schemaRequests, printerBidiSchemaResponses) { /// /// 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. /// The syntax of each request is ";;[;]*" /// /// /// 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. /// var retVal = 0; /// /// Add IHV specific code here to produce Bidi Schema values based on the QueryKeys provided in the scheamRequests object. /// /// The code can use the printerStream object to write/read with the device to determine required data. /// /// All updated Bidi Schema values can then be returned by adding them to the printerBidiSchemaResponses object /// var pageCache = new PageCache(scriptContext, printerStream); var requestProcessor = new RequestProcessor(printerBidiSchemaResponses, pageCache); var i = 0; for (i = 0; i < schemaRequests.length; i++) { var args = schemaRequests[i].split(';', 4); if (args.length >= 4) { requestProcessor[args[1]](args[0], args[2], args[3]); } } return retVal; } function setSchema(scriptContext, printerStream, printerBidiSchemaElement) { /// /// Set a requested Bidi Schema Value in the device. /// /// The script can interpret the incoming Bidi Schema value to either set data in the device or perform an action on the device. /// /// It is possible the attached device is not ready to process the specified 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. /// /// /// Contains all the data associated with the Bidi Schema Value to set. /// /// /// Integer value indicating function completion status. /// 1 - The attached device was not ready to process/set the requested schema. After a wait the function should be called again with the supplied printerBidiSchemaElement. /// 0 - The script completed successfuly. /// var retVal = 0; /// /// Add IHV specific code here to set the Bidi Schema value in the provided printerBidiSchemaElement object. /// /// The code can use the printerStream object to write/read with the device to determine required data. /// return retVal; } function getStatus(scriptContext, printerStream, printerBidiSchemaResponses) { /// /// 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. /// var retVal = 2; /// /// Add IHV specific code here to read from the device and interpret data into Bidi Schema values /// /// The code can use the printerStream object to read from the device to determine required data. /// /// All updated Bidi Schema values can then be returned by adding them to the printerBidiSchemaResponses object /// return retVal; } function requestStatus(scriptContext, printerStream, printerBidiSchemaResponses) { /// /// Integer value indicating function completion status. /// 2 - The device no longer (if ever) supports solicited status so no need for USBMon to make more calls to this function. /// 0 - The script completed successfuly. /// var retVal = 2; return retVal; } var PrinterBidiSchemaElementType = { PrinterBidiSchemaElementType_Null: 0, PrinterBidiSchemaElementType_Int: 1, PrinterBidiSchemaElementType_Float: 2, PrinterBidiSchemaElementType_Bool: 3, PrinterBidiSchemaElementType_String: 4, PrinterBidiSchemaElementType_Text: 5, PrinterBidiSchemaElementType_Enum: 6, PrinterBidiSchemaElementType_Blob: 7 }; PageCache = function (scriptContext, printerStream) { /// /// PageCache Constructor /// The PageCache maintains an array of page content that has been fetched from the printerStream. /// This content can be retrieved upon demand. if the requested content is not available in the /// cache then it is fetched from the device and placed in the cache (unless excluded) before /// returning it. The content may also be placed in the UserCache in the ScriptContext if needed. /// /// /// Accessor for PropertyBags for printer driver and queue properties /// /// /// Allows the script to write/read data from the attached USB device. /// /// /// a PageCache object /// ///this.UserBag = scriptContext.UserProperties; this.UserBag = null; this.printerStream = printerStream; this.ContentArray = []; this.PendingData = ""; }; PageCache.prototype.GetContent = function (contentName, saveInCache) { /// TODO Implement aged caching in the UserBag so that we can save the content across invocations var content = this["FetchLedmContent"](contentName); if ((content != undefined) && saveInCache) { this["SetContent"](contentName, content); } return content; }; PageCache.prototype.SetContent = function(contentName, content) { this.ContentArray[contentName] = content; }; PageCache.prototype.FetchLedmContent = function(contentName) { var ledmReq =[]; ledmReq = this["GetLedmHtmlRequest"](contentName); // Write takes in a byte array. var bytesWritten = this.printerStream.write(ledmReq); var ledmResponse = " "; var htmlHeader = this["ReadHtmlHeader"](); if (-1 != htmlHeader.indexOf("Content-Length")) { // Get the content length and read that much data ledmResponse = this["ReadResponse"](htmlHeader); } else if (-1 != htmlHeader.indexOf("Transfer-Encoding")) { // Read all the chunks and concatenate all of the data ledmResponse = this["ReadChunkedResponse"](contentName); } return ledmResponse; }; PageCache.prototype.GetLedmHtmlRequest = function (contentName) { var requestStr = "GET ".concat(contentName, " HTTP/1.1\r\nHOST:\r\n\r\n"); var request = new Array(requestStr.length); for (var i = 0; i < requestStr.length; i++) request[i] = requestStr.charCodeAt(i); return request; }; PageCache.prototype.ReadHtmlHeader = function () { // read one character at a time until we // find a double line-feed (/r/n/r/n) - that will always mark the end of the header var readHeader = ""; var headerEnd = -1; var txt = ""; var readmore = true; var now = new Date().getTime(); var endTime = now + 10000; // if complete header has been send by the device while ((readmore) && (endTime > now)) { now = new Date().getTime(); // Read the response. readBuffer = this.printerStream.read(512); var bytesRead = readBuffer.length; for (i = 0; i < bytesRead; i++) { readHeader += String.fromCharCode(readBuffer.shift()); } if (0 < bytesRead) { // In the response if we dont get HTTP 200, then we are NOT reading the response for our request. headerEnd = readHeader.indexOf("HTTP/1.1 200"); if (-1 != -headerEnd) { // This means we have found our requests response. Now try to get the double line feed to read till end of header. headerEnd = readHeader.indexOf("\r\n\r\n"); if (headerEnd == -1) { now = new Date().getTime(); // We need to read more data to read till end of header continue; } else { PendingData = readHeader.slice(headerEnd + 4, readHeader.length); // We have read till the header end. Dont read anymore. readmore = false; } } else { now = new Date().getTime(); // This means we are reading the response of an earlier request. readHeader = ""; continue; } } else { sleep(1000); now = new Date().getTime(); continue; } } return readHeader; }; PageCache.prototype.ReadResponse = function (htmlHeader) { // Extract the content length from the header var index = htmlHeader.indexOf("\r\n\r\n") + 4; // Save the content that we read after the header (if there is any) var content = htmlHeader.slice(index); var contentLengthIndex = htmlHeader.indexOf("Content-Length"); // Separate the content length value var lengthStr = htmlHeader.substring(contentLengthIndex + 15); lengthStr = lengthStr.substring(0, lengthStr.indexOf("\r\n")); // The remaining length is the content length - the carried over length from the header var readLength = new Number(lengthStr) - content.length; // Read the bytes var bytes = 512; var readResponse = ""; do { readBuffer = this.printerStream.read(bytes); var bytesRead = readBuffer.length; for (i = 0; i < bytesRead; i++) { // fromCharCode converts from unicode into ascii readResponse += String.fromCharCode(readBuffer.shift()); } } while (readResponse.length < readLength); // Convert it to a string and return it return content.concat(readResponse); }; PageCache.prototype.ReadChunkedResponse = function (contentName) { // Read each chunck var readResponse = PendingData; var responseEnd = -1; var txt = ""; var lastChunk = false; var now = new Date().getTime(); var endTime = now + 10000; var sizeToRead = 0; var additionalRead = 0; if (PendingData.length == 0) { sizeToRead = 512; } var skipLocalStore = true; while ((false == lastChunk) && (endTime > now)) { now = new Date().getTime(); var localBuff = ""; var bytesRead = 0; if (sizeToRead != 0) { if (sizeToRead < 512) { additionalRead = 512 - sizeToRead; sizeToRead = 512; skipLocalStore = true; } readBuffer = this.printerStream.read(sizeToRead); bytesRead = readBuffer.length; if (bytesRead == 0) { if (additionalRead != 0) { sizeToRead = 512 - additionalRead; additionalRead = 0; } continue; } for (i = 0; i < bytesRead; i++) { if (true == skipLocalStore) { localBuff += String.fromCharCode(readBuffer.shift()); } else { readResponse += String.fromCharCode(readBuffer.shift()); } } if (additionalRead != 0) { if ((bytesRead == sizeToRead)) { readResponse += localBuff.slice(0, 512 - additionalRead); localBuff = localBuff.slice(512 - additionalRead, 512); } else if ((bytesRead < sizeToRead) && (sizeToRead > (512 - additionalRead))) { readResponse += localBuff.slice(0, 512 - additionalRead); localBuff = localBuff.slice(512 - additionalRead, bytesRead); } //additionalRead = 0; } } else { localBuff = readResponse; readResponse = ""; bytesRead = PendingData.length; } if (true == skipLocalStore) { readResponse += localBuff; responseEnd = localBuff.indexOf("\r\n"); if (responseEnd == -1) { return readResponse; } var chunksize = parseInt(localBuff.slice(0, responseEnd), 16); // Last chunk having 0 size. if (chunksize == 0) { lastChunk = true; } // Calculate the next chunk size pending to be read. if (additionalRead != 0) { sizeToRead = chunksize + 2 - (additionalRead - responseEnd) + 2; additionalRead = 0; } else { sizeToRead = chunksize + 2 - (bytesRead - responseEnd) + 2; } skipLocalStore = false; continue; } // For cases where the complete chunk is not read, then read the remaining bytes in the chunk. if ((skipLocalStore != true) && (bytesRead < sizeToRead)) { sizeToRead = sizeToRead - bytesRead; } // if we have read all the bytes in the current chunk, then read the next chunk size. else if ((skipLocalStore != true) && (bytesRead == sizeToRead)) { // Read the line feed. skipLocalStore = true; sizeToRead = 512; } } return readResponse; }; RequestProcessor = function(printerBidiSchemaResponses, pageCache) { /// /// RequestProcessor Constructor /// The RequestProcessor contains the functions used to process requests /// it provides access to the IPrinterBidiSchemaResponses object for sending back responses /// /// /// the responses object passed into the getSchema function /// /// /// a RequestProcessor object /// this.responses = printerBidiSchemaResponses; this.pageCache = pageCache; }; RequestProcessor.prototype.XPathBool = function (bidiQueryStr, contentName, queryArray) { var content = ""; content = this.pageCache.GetContent(contentName, true); var result = false; /// TODO Implement a common XPath Evaluator function if (-1 != content.indexOf(queryArray)) result = true; /// determine whether the result is true and set its value /// TODO Implement the XPath evaluation this.responses.AddBool(bidiQueryStr, result); }; RequestProcessor.prototype.XPathString = function(bidiQueryStr, contentName, queryArray) { /// TODO Implement XPathString function }; RequestProcessor.prototype.StrContainsBool = function(bidiQueryStr, contentName, queryArray) { var content = this.pageCache.GetContent(contentName, true); var result = false; if (content.indexOf(queryArray[0]) >= 0) { result = true; } this.responses.AddBool(bidiQueryStr, result); }; RequestProcessor.prototype.InnerXml = function (tag, xml) { var openRe = new RegExp("<" + tag + ">", "gm"); var closeRe = new RegExp("", "gm"); if (openRe.test(xml) && closeRe.test(xml)) { var inner = ''; inner = xml.substring(openRe.lastIndex, closeRe.lastIndex - tag.length - 3); // this.content = xml.substring(closeRe.lastIndex); return inner; } return null; }; function sleep(milliSeconds){ var startTime = new Date().getTime(); // get the current time while (new Date().getTime() < startTime + milliSeconds); // hog cpu } function clearBuffer(printerStream) { var retVal = false; if (printerStream == null) { return retVal; } var txt = ""; try { // Read the buffer to empty if any thing is there in device // from previous call // This causes a HANG on OJ6700 and PS7520 INKJET devices. Need to re-look. var initialReadBuffer = printerStream.read(1024); var initialBytesRead = initialReadBuffer.length; while (initialBytesRead != 0) { initialReadBuffer = printerStream.read(1024); initialBytesRead = initialReadBuffer.length; } retVal = true; } catch (err) { txt += err.message; } return retVal; }