diff --git a/package.json b/package.json index 653d10d..4827593 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/gordol/cordova-brother-label-printer" + "url": "https://github.com/eugenedw/cordova-brother-label-printer" }, "keywords": [ "brother", @@ -31,10 +31,10 @@ "author": "Thomas Gordon Lowrey IV", "licenses": [{ "type": "MIT", - "url": "https://github.com/gordol/cordova-brother-label-printer/blob/master/LICENSE" + "url": "https://github.com/eugenedw/cordova-brother-label-printer/blob/master/LICENSE" }], "bugs": { - "url": "https://github.com/gordol/cordova-brother-label-printer/issues" + "url": "https://github.com/eugenedw/cordova-brother-label-printer/cordova-brother-label-printer/issues" }, - "homepage": "https://github.com/gordol/cordova-brother-label-printer#readme" + "homepage": "https://github.com/eugenedw/cordova-brother-label-printer/cordova-brother-label-printer#readme" } diff --git a/plugin.xml b/plugin.xml index 80df7d5..2fcd5c7 100644 --- a/plugin.xml +++ b/plugin.xml @@ -3,12 +3,12 @@ + version="0.0.3.e"> BrotherPrinter Cordova hooks for Brother Print SDK - https://github.com/gordol/cordova-brother-label-printer.git + https://github.com/eugenedw/cordova-brother-label-printer.git brother, sdk, print diff --git a/src/android/BrotherPrinter.java b/src/android/BrotherPrinter.java index 372fc82..ab61d79 100644 --- a/src/android/BrotherPrinter.java +++ b/src/android/BrotherPrinter.java @@ -1,75 +1,104 @@ package com.threescreens.cordova.plugin.brotherPrinter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import java.io.File; -import java.io.FileWriter; -import java.io.InputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; - -import org.apache.cordova.CallbackContext; -import org.apache.cordova.CordovaPlugin; -import org.apache.cordova.PluginResult; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.app.Activity; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Picture; -import android.os.Handler; -import android.util.Base64; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.WebView; -import android.webkit.WebViewClient; - import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; -import android.app.PendingIntent; +import android.os.Build; +import android.util.Base64; +import android.util.Log; import com.brother.ptouch.sdk.LabelInfo; import com.brother.ptouch.sdk.NetPrinter; import com.brother.ptouch.sdk.Printer; import com.brother.ptouch.sdk.PrinterInfo; +import com.brother.ptouch.sdk.PrinterInfo.ErrorCode; import com.brother.ptouch.sdk.PrinterStatus; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONException; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * BrotherPrinter - cordova plugin providing access to networked printers + * + * - updated March 24, 2018 + * > added method for searching printers + * > implemented PDF printing + * > supplied updated printer list based upon latest Brother SDK (3.0.9) + * > introduced Javadoc + */ public class BrotherPrinter extends CordovaPlugin { - String modelName = "QL-720NW"; - private NetPrinter[] netPrinters; + //Holds the name of the valid network printers to discover during search + private static final String[] modelNames = {"710W","720NW","800","810W","820NWB","QL-710W","QL-720NW","QL-800","QL-800W","QL-820NWB"}; + + //Holds valid paper sizes for the QL series printers + private static final String[] PS_QL = {"W17H54", "W17H87", "W23H23", "W29H42", "W29H90", "W38H90", "W39H48", "W52H29", "W54H29", "W62H29", "W62H100", "W60H86", "W12", "W29", "W38", "W50", "W54", "W62", "W62RB"}; - private String ipAddress = null; - private String macAddress = null; - private Boolean searched = false; - private Boolean found = false; + //Holds the map of NetPrinter objects available based upon the search operation, keyed by their serial number + private Map> discoveredNetworkPrinters; - //token to make it easy to grep logcat - private static final String TAG = "print"; + //Holds the NetPrinter (converted to a Map object) selected during search + private Map selectedPrinter; + //Holds the token to make it easy to grep logcat + private static final String TAG = "[BrotherPrinter Plugin]"; + + //the callback context through which responses to plugin requests should be sent private CallbackContext callbackctx; + /** + * entry point to the plugin from the cordova context + * + * @param action the method to trigger + * @param args the JSON array of arguments to pass to the requested method + * @param callbackContext the context through which results from the requested method should be passed + * + * @throws JSONException if the arguments are not compatible (TODO: clarify this doc entry) + * + */ @Override public boolean execute (String action, JSONArray args, CallbackContext callbackContext) throws JSONException { if ("findNetworkPrinters".equals(action)) { - findNetworkPrinters(callbackContext); + findNetworkPrinters(args, callbackContext); + return true; + } + + if ("setSessionPrinter".equals(action)) { + setSessionPrinter(args, callbackContext); return true; } if ("printViaSDK".equals(action)) { - printViaSDK(args, callbackContext); + printBitmapImage(args, callbackContext); + return true; + } + + if ("printBitmapImage".equals(action)) { + printBitmapImage(args, callbackContext); + return true; + } + + if ("printPdf".equals(action)) { + printPdf(args, callbackContext); return true; } @@ -81,75 +110,111 @@ public boolean execute (String action, JSONArray args, CallbackContext callbackC return false; } + /** + * utility method used to process printer search using @link #modelNames provided + * + * @return an array of printers that were found + */ private NetPrinter[] enumerateNetPrinters() { Printer myPrinter = new Printer(); PrinterInfo myPrinterInfo = new PrinterInfo(); - netPrinters = myPrinter.getNetPrinters(modelName); - return netPrinters; + NetPrinter[] _netPrinters = myPrinter.getNetPrinters(modelNames); + return _netPrinters; } - private void findNetworkPrinters(final CallbackContext callbackctx) { + /** + * uses the provided serial number to set the session printer from list of NetPrinter objects discovered + * + * @param args JSONArray containing the serial number of the printer to select + * @param callbackctx the context provided by the method invoking this request + * + */ + private void setSessionPrinter(final JSONArray args, final CallbackContext callbackctx){ + + final String serialnumber = args.optString(0,null); + + if( serialnumber != null && discoveredNetworkPrinters != null ){ + //check for printer serial number in the list of found printers + this.selectedPrinter = discoveredNetworkPrinters.get(serialnumber); + } cordova.getThreadPool().execute(new Runnable() { public void run() { try{ + PluginResult result; + if( selectedPrinter == null ){ + String message = "No printers found with the serial number provided."; + if( discoveredNetworkPrinters == null || discoveredNetworkPrinters.size() == 0 ){ + message = "No printers were discovered with findNetworkPrinters() method."; + } + result = new PluginResult(PluginResult.Status.ERROR, message); + } + else{ + result = new PluginResult(PluginResult.Status.OK, "printer set"); + } + callbackctx.sendPluginResult(result); + }catch(Exception e){ + e.printStackTrace(); + } + } + }); - searched = true; - - NetPrinter[] netPrinters = enumerateNetPrinters(); - int netPrinterCount = netPrinters.length; - - ArrayList netPrintersList = null; - if(netPrintersList != null) netPrintersList.clear(); - netPrintersList = new ArrayList(); + } - if (netPrinterCount > 0) { - found = true; - Log.d(TAG, "---- network printers found! ----"); + /** + * searches the network for a printer matching those deemed valid for the plugin (contained in {@link #modelNames}) + * + * @param callbackctx the context provided by the method invoking this request + */ + private void findNetworkPrinters(final JSONArray args, final CallbackContext callbackctx) { - for (int i = 0; i < netPrinterCount; i++) { - Map netPrinter = new HashMap(); + cordova.getThreadPool().execute(new Runnable() { + public void run() { + try{ - ipAddress = netPrinters[i].ipAddress; - macAddress = netPrinters[i].macAddress; + NetPrinter[] _netPrinters = enumerateNetPrinters(); + int totalPrinters = 0; - netPrinter.put("ipAddress", netPrinters[i].ipAddress); - netPrinter.put("macAddress", netPrinters[i].macAddress); - netPrinter.put("serNo", netPrinters[i].serNo); - netPrinter.put("nodeName", netPrinters[i].nodeName); + if( _netPrinters != null && _netPrinters.length > 0 ){ - netPrintersList.add(netPrinter); + Log.d(TAG, "---- network printers found! ----"); - Log.d(TAG, - " idx: " + Integer.toString(i) - + "\n model: " + netPrinters[i].modelName - + "\n ip: " + netPrinters[i].ipAddress - + "\n mac: " + netPrinters[i].macAddress - + "\n serial: " + netPrinters[i].serNo - + "\n name: " + netPrinters[i].nodeName - ); - } + totalPrinters = _netPrinters.length; + discoveredNetworkPrinters = new HashMap>(); + ArrayList netPrintersList = new ArrayList(Arrays.asList(_netPrinters)); + for( NetPrinter np : netPrintersList ){ + + Map _printer = new HashMap(); + _printer.put("ipAddress", np.ipAddress); + _printer.put("macAddress", np.macAddress); + _printer.put("serNo", np.serNo); + _printer.put("nodeName", np.nodeName); + _printer.put("paperName","W62H100"); + _printer.put("paperNameArray",Arrays.toString(PS_QL)); + discoveredNetworkPrinters.put(np.serNo,_printer); + + Log.d(TAG, "model: " + np.modelName + + "\n ip: " + np.ipAddress + + "\n mac: " + np.macAddress + + "\n serial: " + np.serNo + + "\n name: " + np.nodeName + ); + + }; Log.d(TAG, "---- /network printers found! ----"); - - }else if (netPrinterCount == 0 ) { - found = false; - Log.d(TAG, "!!!! No network printers found !!!!"); + } + else{ + //no network printers could be found based upon the modelNames search parameter supplied + Log.d(TAG, "!!!! No compatible network printers found !!!!"); } - JSONArray args = new JSONArray(); - PluginResult result; - - Boolean available = netPrinterCount > 0; - - args.put(available); - args.put(netPrintersList); - - result = new PluginResult(PluginResult.Status.OK, args); - + args.put(totalPrinters); + args.put((new JSONObject(discoveredNetworkPrinters)).toString()); + PluginResult result = new PluginResult(PluginResult.Status.OK, args); callbackctx.sendPluginResult(result); - }catch(Exception e){ + }catch(Exception e){ e.printStackTrace(); } @@ -159,29 +224,146 @@ public void run() { } + /** + * utility method to create a bitmap from a base64 string + * + * @param base64 the string to convert to an image + * @param callbackctx the context provided by the method invoking this request + * @return a bitmap {@see android.graphics.Bitmap} image + */ public static Bitmap bmpFromBase64(String base64, final CallbackContext callbackctx){ try{ byte[] bytes = Base64.decode(base64, Base64.DEFAULT); return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - }catch(Exception e){ + }catch(Exception e){ e.printStackTrace(); return null; } } - private void printViaSDK(final JSONArray args, final CallbackContext callbackctx) { + /** + * prints a PDF using a file path provided and defined printer + * + * @param args JSONArray containing the filepath string and printer object to use + * @param callbackctx the context provided by the method invoking this request + */ + private void printPdf(final JSONArray args, final CallbackContext callbackctx) { + + final String filepath = args.optString(0,null); + final String param1 = args.optString(1,null); + final String param2 = args.optString(2,null); + + if( param1 != null && param1.startsWith("printer") + && discoveredNetworkPrinters != null ){ + //check for printer serial number in the list of found printers + //this assumes the user is overriding any prior session printer that may have been selected + this.selectedPrinter = discoveredNetworkPrinters.get(param1.split(":")[1].trim()); + } - final Bitmap bitmap = bmpFromBase64(args.optString(0, null), callbackctx); + //user may have supplied a paper name manually + if( param1 != null && param1.startsWith("paper") ){ + this.selectedPrinter.put("paperName",param1.split(":")[1].trim()); + } + else if( param2 != null ){ + this.selectedPrinter.put("paperName",param2.split(":")[1].trim()); + } - if(!searched){ + if(this.selectedPrinter == null){ PluginResult result; - result = new PluginResult(PluginResult.Status.ERROR, "You must first run findNetworkPrinters() to search the network."); + result = new PluginResult(PluginResult.Status.ERROR, "No printers have been selected. You must first run findNetworkPrinters() to search the network."); callbackctx.sendPluginResult(result); } - if(!found){ + cordova.getThreadPool().execute(new Runnable() { + public void run() { + try{ + + Printer myPrinter = new Printer(); + + PrinterInfo myPrinterInfo = myPrinter.getPrinterInfo(); + + myPrinterInfo.printerModel = PrinterInfo.Model.QL_720NW; + myPrinterInfo.port = PrinterInfo.Port.NET; + myPrinterInfo.printMode = PrinterInfo.PrintMode.ORIGINAL; + myPrinterInfo.orientation = PrinterInfo.Orientation.PORTRAIT; + myPrinterInfo.paperSize = PrinterInfo.PaperSize.CUSTOM; + myPrinterInfo.ipAddress = selectedPrinter.get("ipAddress"); + myPrinterInfo.macAddress = selectedPrinter.get("macAddress"); + myPrinterInfo.isAutoCut = true; + myPrinterInfo.isCutAtEnd = true; + + myPrinterInfo.labelNameIndex = LabelInfo.QL700.valueOf(selectedPrinter.get("paperName")).ordinal(); + + myPrinter.setPrinterInfo(myPrinterInfo); + + PrinterStatus status = new PrinterStatus(); + + //get the total number of pages in the PDF + int totalpages = 0; + if (Build.VERSION.SDK_INT < 21) { + totalpages = myPrinter.getPDFPages(filepath); + } else { + totalpages = myPrinter.getPDFFilePages(filepath); + } + + for (int i = 0; i < totalpages; i++) { + if (Build.VERSION.SDK_INT < 21) { + status = myPrinter.printPDF(filepath, i+1); + } else { + status = myPrinter.printPdfFile(filepath, i+1); + } + if (status.errorCode != ErrorCode.ERROR_NONE) { + break; + } + } + + //converting the enum ErrorCode object to a string for debugging + String status_code = ""+status.errorCode; + + Log.d(TAG, "PrinterStatus: "+status_code); + + PluginResult result; + result = new PluginResult(PluginResult.Status.OK, status_code); + callbackctx.sendPluginResult(result); + + }catch(Exception e){ + e.printStackTrace(); + } + } + }); + } + + /** + * prints a BMP image using the base64 string representing the file contents + * + * @param args JSONArray containing the filepath string and printer object to use + * @param callbackctx the context provided by the method invoking this request + * + */ + private void printBitmapImage(final JSONArray args, final CallbackContext callbackctx) { + + final Bitmap bitmap = bmpFromBase64(args.optString(0, null), callbackctx); + final String param1 = args.optString(1,null); + final String param2 = args.optString(2,null); + + if( param1 != null && param1.startsWith("printer") + && discoveredNetworkPrinters != null ){ + //check for printer serial number in the list of found printers + //this assumes the user is overriding any prior session printer that may have been selected + this.selectedPrinter = discoveredNetworkPrinters.get(param1.split(":")[1].trim()); + } + + //user may have supplied a paper name manually + if( param1 != null && param1.startsWith("paper") ){ + this.selectedPrinter.put("paperName",param1.split(":")[1].trim()); + } + else if( param2 != null ){ + this.selectedPrinter.put("paperName",param2.split(":")[1].trim()); + } + + if(this.selectedPrinter == null){ PluginResult result; - result = new PluginResult(PluginResult.Status.ERROR, "No printer was found. Aborting."); + result = new PluginResult(PluginResult.Status.ERROR, "No printers have been selected. You must first run findNetworkPrinters() to search the network."); callbackctx.sendPluginResult(result); } @@ -199,30 +381,19 @@ public void run() { myPrinterInfo.printMode = PrinterInfo.PrintMode.ORIGINAL; myPrinterInfo.orientation = PrinterInfo.Orientation.PORTRAIT; myPrinterInfo.paperSize = PrinterInfo.PaperSize.CUSTOM; - myPrinterInfo.ipAddress = ipAddress; - myPrinterInfo.macAddress = macAddress; - - myPrinter.setPrinterInfo(myPrinterInfo); + myPrinterInfo.ipAddress = selectedPrinter.get("ipAddress"); + myPrinterInfo.macAddress = selectedPrinter.get("macAddress"); + myPrinterInfo.isAutoCut = true; + myPrinterInfo.isCutAtEnd = true; - LabelInfo myLabelInfo = new LabelInfo(); + //this may need to be parameterized via options arguments + myPrinterInfo.labelNameIndex = LabelInfo.QL700.valueOf(selectedPrinter.get("paperName")).ordinal(); - myLabelInfo.labelNameIndex = myPrinter.checkLabelInPrinter(); - myLabelInfo.isAutoCut = true; - myLabelInfo.isEndCut = true; - myLabelInfo.isHalfCut = false; - myLabelInfo.isSpecialTape = false; - - //label info must be set after setPrinterInfo, it's not in the docs - myPrinter.setLabelInfo(myLabelInfo); + myPrinter.setPrinterInfo(myPrinterInfo); - String labelWidth = ""+myPrinter.getLabelParam().labelWidth; - String paperWidth = ""+myPrinter.getLabelParam().paperWidth; - Log.d(TAG, "paperWidth = " + paperWidth); - Log.d(TAG, "labelWidth = " + labelWidth); - PrinterStatus status = myPrinter.printImage(bitmap); - //casting to string doesn't work, but this does... wtf Brother + //converting the enum ErrorCode object to a string for debugging String status_code = ""+status.errorCode; Log.d(TAG, "PrinterStatus: "+status_code); @@ -231,7 +402,7 @@ public void run() { result = new PluginResult(PluginResult.Status.OK, status_code); callbackctx.sendPluginResult(result); - }catch(Exception e){ + }catch(Exception e){ e.printStackTrace(); } } @@ -239,6 +410,13 @@ public void run() { } + /** + * captures the USB configuration settings for the attached printers + * + * TODO provide clarification as to what this method does + * @param args JSONArray containing parameters + * @param callbackctx the context provided by the method invoking this request + */ private void sendUSBConfig(final JSONArray args, final CallbackContext callbackctx){ cordova.getThreadPool().execute(new Runnable() { @@ -281,12 +459,12 @@ public void onReceive(Context context, Intent intent) { if (!usbManager.hasPermission(usbDevice)) { usbManager.requestPermission(usbDevice, permissionIntent); } else { - break; + break; } - try { + try { Thread.sleep(1000); - } catch (InterruptedException e) { + } catch (InterruptedException e) { e.printStackTrace(); } } @@ -301,13 +479,7 @@ public void onReceive(Context context, Intent intent) { myPrinter.setPrinterInfo(myPrinterInfo); - LabelInfo myLabelInfo = new LabelInfo(); - - myLabelInfo.labelNameIndex = myPrinter.checkLabelInPrinter(); - myLabelInfo.isAutoCut = true; - myLabelInfo.isEndCut = true; - myLabelInfo.isHalfCut = false; - myLabelInfo.isSpecialTape = false; + LabelInfo myLabelInfo = myPrinter.getLabelInfo(); //label info must be set after setPrinterInfo, it's not in the docs myPrinter.setLabelInfo(myLabelInfo); @@ -334,7 +506,7 @@ public void onReceive(Context context, Intent intent) { } catch (IOException e) { Log.d(TAG, "Temp file action failed: " + e.toString()); - } + } } }); diff --git a/src/android/libs/BrotherPrintLibrary.jar b/src/android/libs/BrotherPrintLibrary.jar index 74f57b2..4947309 100755 Binary files a/src/android/libs/BrotherPrintLibrary.jar and b/src/android/libs/BrotherPrintLibrary.jar differ diff --git a/src/android/libs/MobilePrintLib.jar b/src/android/libs/MobilePrintLib.jar new file mode 100755 index 0000000..81ddf89 Binary files /dev/null and b/src/android/libs/MobilePrintLib.jar differ diff --git a/src/android/libs/armeabi/libAndrJFPDFEMB.so b/src/android/libs/armeabi/libAndrJFPDFEMB.so new file mode 100755 index 0000000..12fce95 Binary files /dev/null and b/src/android/libs/armeabi/libAndrJFPDFEMB.so differ diff --git a/src/android/libs/armeabi/libcreatedata.so b/src/android/libs/armeabi/libcreatedata.so index b4f1d46..afc914f 100755 Binary files a/src/android/libs/armeabi/libcreatedata.so and b/src/android/libs/armeabi/libcreatedata.so differ diff --git a/www/printer.js b/www/printer.js index 5939292..61dbc6d 100755 --- a/www/printer.js +++ b/www/printer.js @@ -7,6 +7,48 @@ BrotherPrinter.prototype = { } cordova.exec(callbackFn, null, 'BrotherPrinter', 'findNetworkPrinters', []) }, + setSessionPrinter: function (data, callback) { + if (!data || !data.length) { + console.log('No data passed in. Expects a string representing the serial number.') + return + } + cordova.exec(callback, function(err){console.log('error: '+err)}, 'BrotherPrinter', 'setSessionPrinter', [data]) + }, + printPdf: function( options, callback ){ + + if( !options || options.file === undefined ){ + console.log("No path for the pdf was specified"); + return; + } + + var args = [options.file]; + if( options.printer !== undefined ){ + args.push("printer:" + options.printer); + } + if( options.paper !== undefined ){ + args.push("paper:" + options.paper); + } + + cordova.exec(callback, function(err){ console.log('error: '+err)}, 'BrotherPrinter', 'printPdf', args); + }, + printBitmapImage: function (options, callback) { + + if (!options || options.image === undefined) { + console.log('No data passed in. Expects a bitmap (base64).') + return + } + + var args = [options.image]; + if( options.printer !== undefined ){ + args.push("printer:" + options.printer); + } + if( options.paper !== undefined ){ + args.push("paper:" + options.paper); + } + + cordova.exec(callback, function(err){console.log('error: '+err)}, 'BrotherPrinter', 'printViaSDK', args) + + }, printViaSDK: function (data, callback) { if (!data || !data.length) { console.log('No data passed in. Expects a bitmap.')