From ce27a883aba603dbf754685f98a4c604ffd5fa08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Katzer?= Date: Fri, 25 Jan 2019 12:42:22 +0100 Subject: [PATCH] Update plugin interface with some breaking changes --- src/ios/APPPrinter.h | 7 +- src/ios/APPPrinter.m | 28 ++- src/ios/APPPrinterItem.h | 2 +- src/ios/APPPrinterItem.m | 7 +- src/ios/APPPrinterUnit.h | 2 +- src/ios/APPPrinterUnit.m | 5 +- www/printer.js | 439 +++++++++++++++++++++++++-------------- 7 files changed, 321 insertions(+), 169 deletions(-) diff --git a/src/ios/APPPrinter.h b/src/ios/APPPrinter.h index efcee0d..48f936e 100755 --- a/src/ios/APPPrinter.h +++ b/src/ios/APPPrinter.h @@ -23,11 +23,12 @@ @interface APPPrinter : CDVPlugin -// Find out whether printing is supported on this platform - (void) check:(CDVInvokedUrlCommand *)command; -// Displays system interface for selecting a printer + +- (void) utis:(CDVInvokedUrlCommand *)command; + - (void) pick:(CDVInvokedUrlCommand *)command; -// Prints the content + - (void) print:(CDVInvokedUrlCommand *)command; @end diff --git a/src/ios/APPPrinter.m b/src/ios/APPPrinter.m index 13fc84c..b4b40d0 100755 --- a/src/ios/APPPrinter.m +++ b/src/ios/APPPrinter.m @@ -42,22 +42,30 @@ - (void) check:(CDVInvokedUrlCommand *)command { [self.commandDelegate runInBackground:^{ - BOOL res = NO; - - if (command.arguments.count == 0) - { - res = UIPrintInteractionController.isPrintingAvailable; - } - else - { - res = [APPPrinterItem canPrintURL:command.arguments[0]]; - } + BOOL res = [APPPrinterItem canPrintURL:command.arguments[0]]; [self sendResultWithMessageAsBool:res callbackId:command.callbackId]; }]; } +/* + * List all printable document types (utis). + */ +- (void) utis:(CDVInvokedUrlCommand *)command +{ + [self.commandDelegate runInBackground:^{ + NSSet *utis = UIPrintInteractionController.printableUTIs; + + CDVPluginResult* result = + [CDVPluginResult resultWithStatus:CDVCommandStatus_OK + messageAsArray:utis.allObjects]; + + [self.commandDelegate sendPluginResult:result + callbackId:command.callbackId]; + }]; +} + /** * Displays system interface for selecting a printer. */ diff --git a/src/ios/APPPrinterItem.h b/src/ios/APPPrinterItem.h index 115ece8..22bd1fb 100644 --- a/src/ios/APPPrinterItem.h +++ b/src/ios/APPPrinterItem.h @@ -23,6 +23,6 @@ + (id) ItemFromURL:(NSString *)url; -+ (BOOL) canPrintURL:(NSString *)url; ++ (BOOL) canPrintURL:(nullable NSString *)url; @end diff --git a/src/ios/APPPrinterItem.m b/src/ios/APPPrinterItem.m index 96646af..2db8ab6 100644 --- a/src/ios/APPPrinterItem.m +++ b/src/ios/APPPrinterItem.m @@ -45,9 +45,12 @@ * * @return true if its able to render the content of the file. */ -+ (BOOL) canPrintURL:(NSString *)url ++ (BOOL) canPrintURL:(nullable NSString *)url { - if (![NSURL URLWithString:url]) return YES; + if (![NSURL URLWithString:url]) + { + return UIPrintInteractionController.isPrintingAvailable; + } id item = [self ItemFromURL:url]; diff --git a/src/ios/APPPrinterUnit.h b/src/ios/APPPrinterUnit.h index 12b5f07..f6e160f 100644 --- a/src/ios/APPPrinterUnit.h +++ b/src/ios/APPPrinterUnit.h @@ -21,6 +21,6 @@ @interface APPPrinterUnit : NSObject -+ (double) convert:(NSString *)unit; ++ (double) convert:(nullable NSString *)unit; @end diff --git a/src/ios/APPPrinterUnit.m b/src/ios/APPPrinterUnit.m index 88f3dee..a48ae73 100644 --- a/src/ios/APPPrinterUnit.m +++ b/src/ios/APPPrinterUnit.m @@ -23,8 +23,11 @@ @implementation APPPrinterUnit -+ (double) convert:(NSString *)unit ++ (double) convert:(nullable NSString *)unit { + if ([unit isEqual:[NSNull null]]) + return 1.0; + if ([unit isEqualToString:@"in"]) return 72.0; diff --git a/www/printer.js b/www/printer.js index 99b2271..e002935 100755 --- a/www/printer.js +++ b/www/printer.js @@ -1,190 +1,221 @@ /* - Copyright 2013-2016 appPlant GmbH + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. -*/ + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ var exec = require('cordova/exec'); -/** - * List of all available options with their default value. - * - * @return {Object} - */ -exports.getDefaults = function () { - return { - // name: 'unknown', - // duplex: 'none', - // landscape: false, - // graystyle: false, - // border: true, - copies: 1, +// Defaults +exports._defaults = { + // name: 'unknown', + // duplex: 'none', + // landscape: false, + // graystyle: false, + // border: true, + // copies: 1, - ui: { - hideNumberOfCopies: false, - hidePaperFormat: false, - bounds: [40, 30, 0, 0] - }, + ui: { + hideNumberOfCopies: false, + hidePaperFormat: false, + bounds: [40, 30, 0, 0] + }, - paper: { - unit: 'cm', - height: 0, - width: 0, - length: 0 - }, + // paper: { + // unit: 'cm', + // height: 0, + // width: 0, + // length: 0 + // }, - layout: { - unit: 'cm', - maxHeight: 0, - maxWidth: 0, - padding: { top: 0, left: 0, right: 0, bottom: 0 } - }, + // layout: { + // unit: 'cm', + // maxHeight: 0, + // maxWidth: 0, + // padding: { top: 0, left: 0, right: 0, bottom: 0 } + // }, - header: { - unit: 'cm', - height: 1, + // header: { + // unit: 'cm', + // height: 1, - labels: [{ - text: 'Awesome Printer Plug-in', - style: { - align: 'center', - italic: true, - color: '#FF0000' - } - },{ - showPageIndex: true, - style: { - align: 'right', - bold: true - } - }] - }, + // labels: [{ + // text: 'Awesome Printer Plug-in', + // style: { + // align: 'center', + // italic: true, + // color: '#FF0000' + // } + // },{ + // showPageIndex: true, + // style: { + // align: 'right', + // bold: true + // } + // }] + // }, - footer: { - unit: 'mm', - height: 3, + // footer: { + // unit: 'mm', + // height: 3, - label: { - text: 'Copyright (c) 2013-2019 Sebastián Katzer', - style: { size: 9 }, - position: { - unit: 'mm', - top: 1.5, - right: 5 - } - } - } - }; + // label: { + // text: 'Copyright (c) 2013-2019 Sebastián Katzer', + // style: { size: 9 }, + // position: { + // unit: 'mm', + // top: 1.5, + // right: 5 + // } + // } + // } }; /** - * Checks if the printer service is avaible (iOS) - * or if services are available (Android). + * Test if the printer service is able to print + * the ressource specified by URL. * - * @param {Function} callback - * A callback function - * @param {Object?} scope - * The scope of the callback (default: window) + * @param [ String ] uri A file URI. + * @param [ Function ] callback The callback function. + * @param [ Object ] scope The scope for the function. * - * @return {Boolean} + * @return [ Void ] */ -exports.check = function (callback, scope) { - var fn = this._createCallbackFn(callback); +exports.canPrint = function (uri, callback, scope) { - exec(fn, null, 'Printer', 'check', []); + if (typeof uri == 'function') { + scope = callback; + callback = uri; + uri = null; + } + + var fn = this._createCallbackFn(callback, scope); + + exec(fn, null, 'Printer', 'check', [uri]); }; /** - * @deprecated API call. Use `check` instead! + * Returns a list of all printable document types. + * + * @param [ Function ] callback The callback function. + * @param [ Object ] scope The scope for the function. + * + * @return [ Void ] */ -exports.isAvailable = function () { - exports.check.apply(exports, arguments); +exports.getPrintableUTIs = function (callback, scope) { + var fn = this._createCallbackFn(callback, scope); + + exec(fn, null, 'Printer', 'utis', []); }; /** * Displays system interface for selecting a printer. * - * @param {Function} callback - * A callback function - * @param {Object} options - * Options for the printer picker + * @param [ Object ] options Optional options. + * @param [ Function ] callback The callback function. + * @param [ Object ] scope The scope for the function. + * + * @return [ Void ] */ -exports.pick = function (callback, options) { - var fn = this._createCallbackFn(callback); - var params = options || {}; +exports.pick = function (options, callback, scope) { - params = this.mergeWithDefaults(params); + if (typeof options == 'function') { + scope = callback; + callback = options; + options = {}; + } + + var fn = this._createCallbackFn(callback, scope), + params = this._mergeWithDefaults(options || {}); exec(fn, null, 'Printer', 'pick', [params]); }; /** - * Sends the content to the Printing Framework. + * Sends the content to the printer. * - * @param {String} content - * HTML string or DOM node - * if latter, innerHTML is used to get the content - * @param {Object} options - * Options for the print job - * @param {Function?} callback - * A callback function - * @param {Object?} scope - * The scope of the callback (default: window) + * @param [ String ] content The plain/html text or a file URI. + * @param [ Object ] options Options for the print job. + * @param [ Function ] callback The callback function. + * @param [ Object ] scope The scope for the function. */ exports.print = function (content, options, callback, scope) { - var page = content.innerHTML || content, - params = options || {}, - fn = this._createCallbackFn(callback); - if (typeof page != 'string') { - console.log('Print function requires an HTML string. Not an object'); - return; + if (typeof content == 'function') { + scope = options; + callback = content; + options = {}; + content = null; } - if (typeof params == 'string') { - params = { name: params }; + if (typeof options == 'function') { + scope = callback; + callback = options; + options = typeof content != 'string' ? content : {}; + content = typeof content == 'string' ? content : null; } - params = this.mergeWithDefaults(params); + var fn = this._createCallbackFn(callback, scope), + params = this._mergeWithDefaults(options || {}); - if ([null, undefined, ''].indexOf(params.name) > -1) { - params.name = this.getDefaults().name; - } - - exec(fn, null, 'Printer', 'print', [page, params]); + exec(fn, null, 'Printer', 'print', [content, params]); }; /** - * @private + * The (platform specific) default settings. * - * Merge settings with default values. - * - * @param {Object} options - * The custom options - * - * @retrun {Object} - * Default values merged - * with custom values + * @return [ Object ] */ -exports.mergeWithDefaults = function (options) { +exports.getDefaults = function () { + var map = Object.assign({}, this._defaults); + + for (var key in map) { + if (Array.isArray(map[key])) { + map[key] = Array.from(map[key]); + } else + if (Object.prototype.isPrototypeOf(map[key])) { + map[key] = Object.assign({}, map[key]); + } + } + + return map; +}; + +/** + * Overwrite default settings. + * + * @param [ Object ] newDefaults New default values. + * + * @return [ Void ] + */ +exports.setDefaults = function (newDefaults) { + Object.assign(this._defaults, newDefaults); +}; + +/** + * Merge custom properties with the default values. + * + * @param [ Object ] options Set of custom values. + * + * @retrun [ Object ] + */ +exports._mergeWithDefaults = function (options) { var defaults = this.getDefaults(); - if (options.bounds && !options.bounds.length) { + if (options.bounds && !Array.isArray(options.bounds)) { options.bounds = [ options.bounds.left || defaults.bounds[0], options.bounds.top || defaults.bounds[1], @@ -197,38 +228,144 @@ exports.mergeWithDefaults = function (options) { options.duplex = options.duplex ? 'long' : 'none'; } - for (var key in defaults) { - if (!options.hasOwnProperty(key)) { - options[key] = defaults[key]; - continue; - } + Object.assign(defaults, options); - if (typeof options[key] != typeof defaults[key]) { + for (var key in defaults) { + if (defaults[key] !== null) { + options[key] = defaults[key]; + } else { delete options[key]; } } + options.meta = { + plugin: 'cordova-plugin-printer', + version: '0.8.0-alpha' + }; + return options; }; /** * @private * - * Creates a callback, which will be executed within a specific scope. + * Creates a callback, which will be executed + * within a specific scope. * - * @param {Function} callbackFn - * The callback function - * @param {Object} scope - * The scope for the function + * @param [ Function ] callback The callback function. + * @param [ Object ] scope The scope for the function. * - * @return {Function} - * The new callback function + * @return [ Function ] The new callback function */ -exports._createCallbackFn = function (callbackFn, scope) { - if (typeof callbackFn != 'function') +exports._createCallbackFn = function (callback, scope) { + + if (typeof callback !== 'function') return; return function () { - callbackFn.apply(scope || this, arguments); + callback.apply(scope || this, arguments); }; }; + +// Polyfill for Object.assign +if (typeof Object.assign != 'function') { + Object.assign = function(target) { + 'use strict'; + if (target == null) { + throw new TypeError('Cannot convert undefined or null to object'); + } + + target = Object(target); + for (var index = 1; index < arguments.length; index++) { + var source = arguments[index]; + if (source != null) { + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + } + return target; + }; +} + +// Polyfill for Array.from +// Production steps of ECMA-262, Edition 6, 22.1.2.1 +// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from +if (!Array.from) { + Array.from = (function () { + var toStr = Object.prototype.toString; + var isCallable = function (fn) { + return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; + }; + var toInteger = function (value) { + var number = Number(value); + if (isNaN(number)) { return 0; } + if (number === 0 || !isFinite(number)) { return number; } + return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); + }; + var maxSafeInteger = Math.pow(2, 53) - 1; + var toLength = function (value) { + var len = toInteger(value); + return Math.min(Math.max(len, 0), maxSafeInteger); + }; + + // The length property of the from method is 1. + return function from(arrayLike/*, mapFn, thisArg */) { + // 1. Let C be the this value. + var C = this; + + // 2. Let items be ToObject(arrayLike). + var items = Object(arrayLike); + + // 3. ReturnIfAbrupt(items). + if (arrayLike == null) { + throw new TypeError("Array.from requires an array-like object - not null or undefined"); + } + + // 4. If mapfn is undefined, then let mapping be false. + var mapFn = arguments.length > 1 ? arguments[1] : void undefined; + var T; + if (typeof mapFn !== 'undefined') { + // 5. else + // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. + if (!isCallable(mapFn)) { + throw new TypeError('Array.from: when provided, the second argument must be a function'); + } + + // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 2) { + T = arguments[2]; + } + } + + // 10. Let lenValue be Get(items, "length"). + // 11. Let len be ToLength(lenValue). + var len = toLength(items.length); + + // 13. If IsConstructor(C) is true, then + // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len. + // 14. a. Else, Let A be ArrayCreate(len). + var A = isCallable(C) ? Object(new C(len)) : new Array(len); + + // 16. Let k be 0. + var k = 0; + // 17. Repeat, while k < len… (also steps a - h) + var kValue; + while (k < len) { + kValue = items[k]; + if (mapFn) { + A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); + } else { + A[k] = kValue; + } + k += 1; + } + // 18. Let putStatus be Put(A, "length", len, true). + A.length = len; + // 20. Return A. + return A; + }; + }()); +}