@dimer47/capacitor-plugin-printer 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CapacitorPluginPrinter.podspec +17 -0
  2. package/LICENSE +202 -0
  3. package/Package.swift +31 -0
  4. package/README.md +505 -0
  5. package/android/build.gradle +68 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintAdapter.kt +73 -0
  8. package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintContent.kt +78 -0
  9. package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintIO.kt +136 -0
  10. package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintManager.kt +219 -0
  11. package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintOptions.kt +260 -0
  12. package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintProxy.kt +44 -0
  13. package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrinterPlugin.kt +267 -0
  14. package/dist/esm/definitions.d.ts +307 -0
  15. package/dist/esm/definitions.js +2 -0
  16. package/dist/esm/definitions.js.map +1 -0
  17. package/dist/esm/index.d.ts +4 -0
  18. package/dist/esm/index.js +7 -0
  19. package/dist/esm/index.js.map +1 -0
  20. package/dist/esm/web.d.ts +13 -0
  21. package/dist/esm/web.js +47 -0
  22. package/dist/esm/web.js.map +1 -0
  23. package/dist/plugin.cjs.js +61 -0
  24. package/dist/plugin.cjs.js.map +1 -0
  25. package/dist/plugin.js +64 -0
  26. package/dist/plugin.js.map +1 -0
  27. package/ios/Sources/PrinterPlugin/PrinterControllerHelper.swift +33 -0
  28. package/ios/Sources/PrinterPlugin/PrinterFont.swift +99 -0
  29. package/ios/Sources/PrinterPlugin/PrinterInfo.swift +64 -0
  30. package/ios/Sources/PrinterPlugin/PrinterItem.swift +104 -0
  31. package/ios/Sources/PrinterPlugin/PrinterLayout.swift +61 -0
  32. package/ios/Sources/PrinterPlugin/PrinterPaper.swift +71 -0
  33. package/ios/Sources/PrinterPlugin/PrinterPlugin.swift +327 -0
  34. package/ios/Sources/PrinterPlugin/PrinterRenderer.swift +135 -0
  35. package/ios/Sources/PrinterPlugin/PrinterUnit.swift +45 -0
  36. package/package.json +75 -0
@@ -0,0 +1,47 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class PrinterWeb extends WebPlugin {
3
+ async print(options) {
4
+ if (options.content) {
5
+ const printWindow = window.open('', '_blank');
6
+ if (printWindow) {
7
+ printWindow.document.write(options.content);
8
+ printWindow.document.close();
9
+ printWindow.focus();
10
+ printWindow.print();
11
+ printWindow.close();
12
+ }
13
+ else {
14
+ window.print();
15
+ }
16
+ }
17
+ else {
18
+ window.print();
19
+ }
20
+ return { success: true };
21
+ }
22
+ async printHtml({ html, ...opts }) {
23
+ return this.print({ content: html, ...opts });
24
+ }
25
+ async printPdf(_options) {
26
+ throw this.unimplemented('printPdf() is not available on web.');
27
+ }
28
+ async printBase64(_options) {
29
+ throw this.unimplemented('printBase64() is not available on web.');
30
+ }
31
+ async printFile(_options) {
32
+ throw this.unimplemented('printFile() is not available on web.');
33
+ }
34
+ async printWebView(options) {
35
+ return this.print({ ...options });
36
+ }
37
+ async canPrintItem(_options) {
38
+ return { available: typeof window.print === 'function' };
39
+ }
40
+ async getPrintableTypes() {
41
+ return { types: [] };
42
+ }
43
+ async pick(_options) {
44
+ throw this.unimplemented('pick() is not available on web.');
45
+ }
46
+ }
47
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAkB5C,MAAM,OAAO,UAAW,SAAQ,SAAS;IACvC,KAAK,CAAC,KAAK,CAAC,OAAqB;QAC/B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC9C,IAAI,WAAW,EAAE,CAAC;gBAChB,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC5C,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAC7B,WAAW,CAAC,KAAK,EAAE,CAAC;gBACpB,WAAW,CAAC,KAAK,EAAE,CAAC;gBACpB,WAAW,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,EAAoB;QACjD,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAyB;QACtC,MAAM,IAAI,CAAC,aAAa,CAAC,qCAAqC,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,QAA4B;QAC5C,MAAM,IAAI,CAAC,aAAa,CAAC,wCAAwC,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAA0B;QACxC,MAAM,IAAI,CAAC,aAAa,CAAC,sCAAsC,CAAC,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAA6B;QAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,QAA0B;QAC3C,OAAO,EAAE,SAAS,EAAE,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAsB;QAC/B,MAAM,IAAI,CAAC,aAAa,CAAC,iCAAiC,CAAC,CAAC;IAC9D,CAAC;CACF"}
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ const Printer = core.registerPlugin('Printer', {
6
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.PrinterWeb()),
7
+ });
8
+
9
+ class PrinterWeb extends core.WebPlugin {
10
+ async print(options) {
11
+ if (options.content) {
12
+ const printWindow = window.open('', '_blank');
13
+ if (printWindow) {
14
+ printWindow.document.write(options.content);
15
+ printWindow.document.close();
16
+ printWindow.focus();
17
+ printWindow.print();
18
+ printWindow.close();
19
+ }
20
+ else {
21
+ window.print();
22
+ }
23
+ }
24
+ else {
25
+ window.print();
26
+ }
27
+ return { success: true };
28
+ }
29
+ async printHtml({ html, ...opts }) {
30
+ return this.print({ content: html, ...opts });
31
+ }
32
+ async printPdf(_options) {
33
+ throw this.unimplemented('printPdf() is not available on web.');
34
+ }
35
+ async printBase64(_options) {
36
+ throw this.unimplemented('printBase64() is not available on web.');
37
+ }
38
+ async printFile(_options) {
39
+ throw this.unimplemented('printFile() is not available on web.');
40
+ }
41
+ async printWebView(options) {
42
+ return this.print({ ...options });
43
+ }
44
+ async canPrintItem(_options) {
45
+ return { available: typeof window.print === 'function' };
46
+ }
47
+ async getPrintableTypes() {
48
+ return { types: [] };
49
+ }
50
+ async pick(_options) {
51
+ throw this.unimplemented('pick() is not available on web.');
52
+ }
53
+ }
54
+
55
+ var web = /*#__PURE__*/Object.freeze({
56
+ __proto__: null,
57
+ PrinterWeb: PrinterWeb
58
+ });
59
+
60
+ exports.Printer = Printer;
61
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst Printer = registerPlugin('Printer', {\n web: () => import('./web').then((m) => new m.PrinterWeb()),\n});\nexport * from './definitions';\nexport { Printer };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class PrinterWeb extends WebPlugin {\n async print(options) {\n if (options.content) {\n const printWindow = window.open('', '_blank');\n if (printWindow) {\n printWindow.document.write(options.content);\n printWindow.document.close();\n printWindow.focus();\n printWindow.print();\n printWindow.close();\n }\n else {\n window.print();\n }\n }\n else {\n window.print();\n }\n return { success: true };\n }\n async printHtml({ html, ...opts }) {\n return this.print({ content: html, ...opts });\n }\n async printPdf(_options) {\n throw this.unimplemented('printPdf() is not available on web.');\n }\n async printBase64(_options) {\n throw this.unimplemented('printBase64() is not available on web.');\n }\n async printFile(_options) {\n throw this.unimplemented('printFile() is not available on web.');\n }\n async printWebView(options) {\n return this.print({ ...options });\n }\n async canPrintItem(_options) {\n return { available: typeof window.print === 'function' };\n }\n async getPrintableTypes() {\n return { types: [] };\n }\n async pick(_options) {\n throw this.unimplemented('pick() is not available on web.');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,OAAO,GAAGA,mBAAc,CAAC,SAAS,EAAE;AAC1C,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;AAC9D,CAAC;;ACFM,MAAM,UAAU,SAASC,cAAS,CAAC;AAC1C,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE;AACzB,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE;AAC7B,YAAY,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC;AACzD,YAAY,IAAI,WAAW,EAAE;AAC7B,gBAAgB,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;AAC3D,gBAAgB,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE;AAC5C,gBAAgB,WAAW,CAAC,KAAK,EAAE;AACnC,gBAAgB,WAAW,CAAC,KAAK,EAAE;AACnC,gBAAgB,WAAW,CAAC,KAAK,EAAE;AACnC,YAAY;AACZ,iBAAiB;AACjB,gBAAgB,MAAM,CAAC,KAAK,EAAE;AAC9B,YAAY;AACZ,QAAQ;AACR,aAAa;AACb,YAAY,MAAM,CAAC,KAAK,EAAE;AAC1B,QAAQ;AACR,QAAQ,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;AAChC,IAAI;AACJ,IAAI,MAAM,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE;AACvC,QAAQ,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;AACrD,IAAI;AACJ,IAAI,MAAM,QAAQ,CAAC,QAAQ,EAAE;AAC7B,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,qCAAqC,CAAC;AACvE,IAAI;AACJ,IAAI,MAAM,WAAW,CAAC,QAAQ,EAAE;AAChC,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,wCAAwC,CAAC;AAC1E,IAAI;AACJ,IAAI,MAAM,SAAS,CAAC,QAAQ,EAAE;AAC9B,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,sCAAsC,CAAC;AACxE,IAAI;AACJ,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE;AAChC,QAAQ,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;AACzC,IAAI;AACJ,IAAI,MAAM,YAAY,CAAC,QAAQ,EAAE;AACjC,QAAQ,OAAO,EAAE,SAAS,EAAE,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE;AAChE,IAAI;AACJ,IAAI,MAAM,iBAAiB,GAAG;AAC9B,QAAQ,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;AAC5B,IAAI;AACJ,IAAI,MAAM,IAAI,CAAC,QAAQ,EAAE;AACzB,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,iCAAiC,CAAC;AACnE,IAAI;AACJ;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,64 @@
1
+ var capacitorPrinter = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const Printer = core.registerPlugin('Printer', {
5
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.PrinterWeb()),
6
+ });
7
+
8
+ class PrinterWeb extends core.WebPlugin {
9
+ async print(options) {
10
+ if (options.content) {
11
+ const printWindow = window.open('', '_blank');
12
+ if (printWindow) {
13
+ printWindow.document.write(options.content);
14
+ printWindow.document.close();
15
+ printWindow.focus();
16
+ printWindow.print();
17
+ printWindow.close();
18
+ }
19
+ else {
20
+ window.print();
21
+ }
22
+ }
23
+ else {
24
+ window.print();
25
+ }
26
+ return { success: true };
27
+ }
28
+ async printHtml({ html, ...opts }) {
29
+ return this.print({ content: html, ...opts });
30
+ }
31
+ async printPdf(_options) {
32
+ throw this.unimplemented('printPdf() is not available on web.');
33
+ }
34
+ async printBase64(_options) {
35
+ throw this.unimplemented('printBase64() is not available on web.');
36
+ }
37
+ async printFile(_options) {
38
+ throw this.unimplemented('printFile() is not available on web.');
39
+ }
40
+ async printWebView(options) {
41
+ return this.print({ ...options });
42
+ }
43
+ async canPrintItem(_options) {
44
+ return { available: typeof window.print === 'function' };
45
+ }
46
+ async getPrintableTypes() {
47
+ return { types: [] };
48
+ }
49
+ async pick(_options) {
50
+ throw this.unimplemented('pick() is not available on web.');
51
+ }
52
+ }
53
+
54
+ var web = /*#__PURE__*/Object.freeze({
55
+ __proto__: null,
56
+ PrinterWeb: PrinterWeb
57
+ });
58
+
59
+ exports.Printer = Printer;
60
+
61
+ return exports;
62
+
63
+ })({}, capacitorExports);
64
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst Printer = registerPlugin('Printer', {\n web: () => import('./web').then((m) => new m.PrinterWeb()),\n});\nexport * from './definitions';\nexport { Printer };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class PrinterWeb extends WebPlugin {\n async print(options) {\n if (options.content) {\n const printWindow = window.open('', '_blank');\n if (printWindow) {\n printWindow.document.write(options.content);\n printWindow.document.close();\n printWindow.focus();\n printWindow.print();\n printWindow.close();\n }\n else {\n window.print();\n }\n }\n else {\n window.print();\n }\n return { success: true };\n }\n async printHtml({ html, ...opts }) {\n return this.print({ content: html, ...opts });\n }\n async printPdf(_options) {\n throw this.unimplemented('printPdf() is not available on web.');\n }\n async printBase64(_options) {\n throw this.unimplemented('printBase64() is not available on web.');\n }\n async printFile(_options) {\n throw this.unimplemented('printFile() is not available on web.');\n }\n async printWebView(options) {\n return this.print({ ...options });\n }\n async canPrintItem(_options) {\n return { available: typeof window.print === 'function' };\n }\n async getPrintableTypes() {\n return { types: [] };\n }\n async pick(_options) {\n throw this.unimplemented('pick() is not available on web.');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,OAAO,GAAGA,mBAAc,CAAC,SAAS,EAAE;IAC1C,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;IAC9D,CAAC;;ICFM,MAAM,UAAU,SAASC,cAAS,CAAC;IAC1C,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE;IACzB,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE;IAC7B,YAAY,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC;IACzD,YAAY,IAAI,WAAW,EAAE;IAC7B,gBAAgB,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;IAC3D,gBAAgB,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE;IAC5C,gBAAgB,WAAW,CAAC,KAAK,EAAE;IACnC,gBAAgB,WAAW,CAAC,KAAK,EAAE;IACnC,gBAAgB,WAAW,CAAC,KAAK,EAAE;IACnC,YAAY;IACZ,iBAAiB;IACjB,gBAAgB,MAAM,CAAC,KAAK,EAAE;IAC9B,YAAY;IACZ,QAAQ;IACR,aAAa;IACb,YAAY,MAAM,CAAC,KAAK,EAAE;IAC1B,QAAQ;IACR,QAAQ,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;IAChC,IAAI;IACJ,IAAI,MAAM,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE;IACvC,QAAQ,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;IACrD,IAAI;IACJ,IAAI,MAAM,QAAQ,CAAC,QAAQ,EAAE;IAC7B,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,qCAAqC,CAAC;IACvE,IAAI;IACJ,IAAI,MAAM,WAAW,CAAC,QAAQ,EAAE;IAChC,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,wCAAwC,CAAC;IAC1E,IAAI;IACJ,IAAI,MAAM,SAAS,CAAC,QAAQ,EAAE;IAC9B,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,sCAAsC,CAAC;IACxE,IAAI;IACJ,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE;IAChC,QAAQ,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;IACzC,IAAI;IACJ,IAAI,MAAM,YAAY,CAAC,QAAQ,EAAE;IACjC,QAAQ,OAAO,EAAE,SAAS,EAAE,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE;IAChE,IAAI;IACJ,IAAI,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;IAC5B,IAAI;IACJ,IAAI,MAAM,IAAI,CAAC,QAAQ,EAAE;IACzB,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,iCAAiC,CAAC;IACnE,IAAI;IACJ;;;;;;;;;;;;;;;"}
@@ -0,0 +1,33 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Configures the shared UIPrintInteractionController with given settings.
5
+ class PrinterControllerHelper {
6
+
7
+ static func sharedController(with settings: [String: Any]) -> UIPrintInteractionController {
8
+ let ctrl = UIPrintInteractionController.shared
9
+
10
+ // Reset previous state to avoid stale data from prior print jobs
11
+ ctrl.printPageRenderer = nil
12
+ ctrl.printFormatter = nil
13
+ ctrl.printingItem = nil
14
+ ctrl.printingItems = nil
15
+
16
+ ctrl.printInfo = PrinterInfo.printInfo(with: settings)
17
+
18
+ // Default to showing paper selection so users can pick A4, A5, Letter, etc.
19
+ ctrl.showsPaperSelectionForLoadedPapers = true
20
+ ctrl.showsNumberOfCopies = true
21
+
22
+ if let ui = settings["ui"] as? [String: Any] {
23
+ if let hide = ui["hideNumberOfCopies"] as? Bool, hide {
24
+ ctrl.showsNumberOfCopies = false
25
+ }
26
+ if let hide = ui["hidePaperFormat"] as? Bool, hide {
27
+ ctrl.showsPaperSelectionForLoadedPapers = false
28
+ }
29
+ }
30
+
31
+ return ctrl
32
+ }
33
+ }
@@ -0,0 +1,99 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Handles font styling and attributes from settings dictionary.
5
+ class PrinterFont {
6
+
7
+ private let settings: [String: Any]
8
+
9
+ init(dictionary: [String: Any]?) {
10
+ self.settings = dictionary ?? [:]
11
+ }
12
+
13
+ var attributes: [NSAttributedString.Key: Any] {
14
+ let style = NSMutableParagraphStyle()
15
+ style.alignment = self.alignment
16
+
17
+ return [
18
+ .font: self.font,
19
+ .paragraphStyle: style,
20
+ .foregroundColor: self.color,
21
+ ]
22
+ }
23
+
24
+ var font: UIFont {
25
+ var size = (settings["size"] as? NSNumber)?.doubleValue ?? 0
26
+
27
+ if size <= 0 {
28
+ size = Double(UIFont.smallSystemFontSize)
29
+ }
30
+
31
+ var uiFont: UIFont
32
+ if let fontName = settings["name"] as? String,
33
+ let namedFont = UIFont(name: fontName, size: size)
34
+ {
35
+ uiFont = namedFont
36
+ } else {
37
+ uiFont = UIFont.systemFont(ofSize: size)
38
+ }
39
+
40
+ var traits: UIFontDescriptor.SymbolicTraits = []
41
+
42
+ if settings["bold"] as? Bool == true {
43
+ traits.insert(.traitBold)
44
+ }
45
+ if settings["italic"] as? Bool == true {
46
+ traits.insert(.traitItalic)
47
+ }
48
+
49
+ if !traits.isEmpty,
50
+ let descriptor = uiFont.fontDescriptor.withSymbolicTraits(traits)
51
+ {
52
+ uiFont = UIFont(descriptor: descriptor, size: size)
53
+ }
54
+
55
+ return uiFont
56
+ }
57
+
58
+ var color: UIColor {
59
+ guard let hex = settings["color"] as? String, !hex.isEmpty else {
60
+ return .darkText
61
+ }
62
+
63
+ return PrinterFont.color(fromHex: hex) ?? .darkText
64
+ }
65
+
66
+ var alignment: NSTextAlignment {
67
+ guard let align = settings["align"] as? String else {
68
+ return .natural
69
+ }
70
+
71
+ switch align {
72
+ case "left": return .left
73
+ case "right": return .right
74
+ case "center": return .center
75
+ case "justified": return .justified
76
+ default: return .natural
77
+ }
78
+ }
79
+
80
+ // MARK: - Private
81
+
82
+ private static func color(fromHex hex: String) -> UIColor? {
83
+ var cleanHex = hex
84
+ if cleanHex.hasPrefix("#") {
85
+ cleanHex = String(cleanHex.dropFirst())
86
+ }
87
+
88
+ var rgb: UInt64 = 0
89
+ let scanner = Scanner(string: cleanHex)
90
+ scanner.scanHexInt64(&rgb)
91
+
92
+ return UIColor(
93
+ red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0,
94
+ green: CGFloat((rgb & 0xFF00) >> 8) / 255.0,
95
+ blue: CGFloat(rgb & 0xFF) / 255.0,
96
+ alpha: 1.0
97
+ )
98
+ }
99
+ }
@@ -0,0 +1,64 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Converts settings dictionary to UIPrintInfo.
5
+ class PrinterInfo {
6
+
7
+ static func printInfo(with spec: [String: Any]) -> UIPrintInfo {
8
+ let info = UIPrintInfo(dictionary: nil)
9
+ let duplex = spec["duplex"] as? String
10
+ let jobName = spec["name"] as? String
11
+ let copies = max((spec["copies"] as? Int) ?? 1, 1)
12
+
13
+ // Orientation
14
+ if let orientation = spec["orientation"] as? String {
15
+ switch orientation {
16
+ case "landscape":
17
+ info.orientation = .landscape
18
+ case "portrait":
19
+ info.orientation = .portrait
20
+ default:
21
+ break
22
+ }
23
+ }
24
+
25
+ // Output type
26
+ let monochrome = spec["monochrome"] as? Bool ?? false
27
+ let photo = spec["photo"] as? Bool ?? false
28
+
29
+ if monochrome {
30
+ info.outputType = photo ? .photoGrayscale : .grayscale
31
+ } else if photo {
32
+ info.outputType = .photo
33
+ }
34
+
35
+ // Duplex
36
+ if let duplex = duplex {
37
+ switch duplex {
38
+ case "long":
39
+ info.duplex = .longEdge
40
+ case "short":
41
+ info.duplex = .shortEdge
42
+ case "none":
43
+ info.duplex = .none
44
+ default:
45
+ break
46
+ }
47
+ }
48
+
49
+ // Copies (using private API via KVC, same as original plugin)
50
+ if copies > 1 {
51
+ let selector = NSSelectorFromString("_copies")
52
+ if info.responds(to: selector) {
53
+ info.setValue(NSNumber(value: copies), forKey: "_copies")
54
+ }
55
+ }
56
+
57
+ // Job name
58
+ if let jobName = jobName, !jobName.isEmpty {
59
+ info.jobName = jobName
60
+ }
61
+
62
+ return info
63
+ }
64
+ }
@@ -0,0 +1,104 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Handles converting URLs/paths into printable items (NSURL or NSData).
5
+ class PrinterItem {
6
+
7
+ /// Returns the printing item referred by URL, either as URL or Data.
8
+ static func item(from urlString: String) -> Any? {
9
+ return PrinterItem().itemFromURL(urlString)
10
+ }
11
+
12
+ /// Returns whether the print framework can render the referenced file.
13
+ static func canPrintURL(_ url: String?) -> Bool {
14
+ guard let url = url, !(url as NSString).isEqual(to: NSNull().description), !url.isEmpty else {
15
+ return UIPrintInteractionController.isPrintingAvailable
16
+ }
17
+
18
+ guard URL(string: url)?.scheme != nil else {
19
+ return UIPrintInteractionController.isPrintingAvailable
20
+ }
21
+
22
+ let item = PrinterItem().itemFromURL(url)
23
+
24
+ if let data = item as? Data {
25
+ return UIPrintInteractionController.canPrint(data)
26
+ } else if let fileURL = item as? URL {
27
+ return UIPrintInteractionController.canPrint(fileURL)
28
+ }
29
+
30
+ return false
31
+ }
32
+
33
+ // MARK: - Private
34
+
35
+ private func itemFromURL(_ path: String) -> Any? {
36
+ if path.hasPrefix("file:///") {
37
+ return urlForFile(path)
38
+ } else if path.hasPrefix("res:") {
39
+ return urlForResource(path)
40
+ } else if path.hasPrefix("file://") {
41
+ return urlForAsset(path)
42
+ } else if path.hasPrefix("base64:") {
43
+ return dataFromBase64(path)
44
+ }
45
+
46
+ let fm = FileManager.default
47
+ if !fm.fileExists(atPath: path) {
48
+ NSLog("[capacitor-plugin-printer] File not found: %@", path)
49
+ }
50
+
51
+ return URL(fileURLWithPath: path)
52
+ }
53
+
54
+ private func urlForFile(_ path: String) -> URL {
55
+ let absPath = String(path.dropFirst(7))
56
+ let fm = FileManager.default
57
+
58
+ if !fm.fileExists(atPath: absPath) {
59
+ NSLog("[capacitor-plugin-printer] File not found: %@", absPath)
60
+ }
61
+
62
+ return URL(fileURLWithPath: absPath)
63
+ }
64
+
65
+ private func urlForResource(_ path: String) -> URL {
66
+ let fm = FileManager.default
67
+ let bundlePath = Bundle.main.resourcePath ?? ""
68
+ var adjustedPath = path
69
+
70
+ if path == "res://icon" {
71
+ if UIDevice.current.userInterfaceIdiom == .pad {
72
+ adjustedPath = "res://AppIcon76x76@2x~ipad.png"
73
+ } else {
74
+ adjustedPath = "res://AppIcon60x60@2x.png"
75
+ }
76
+ }
77
+
78
+ let absPath = bundlePath + adjustedPath.replacingOccurrences(of: "res:/", with: "")
79
+
80
+ if !fm.fileExists(atPath: absPath) {
81
+ NSLog("[capacitor-plugin-printer] File not found: %@", absPath)
82
+ }
83
+
84
+ return URL(fileURLWithPath: absPath)
85
+ }
86
+
87
+ private func urlForAsset(_ path: String) -> URL {
88
+ let fm = FileManager.default
89
+ let bundlePath = Bundle.main.bundlePath
90
+ let absPath = bundlePath + "/public" + String(path.dropFirst(6))
91
+
92
+ if !fm.fileExists(atPath: absPath) {
93
+ NSLog("[capacitor-plugin-printer] File not found: %@", absPath)
94
+ }
95
+
96
+ return URL(fileURLWithPath: absPath)
97
+ }
98
+
99
+ private func dataFromBase64(_ url: String) -> Data? {
100
+ guard url.count > 9 else { return nil }
101
+ let base64String = String(url.dropFirst(9))
102
+ return Data(base64Encoded: base64String, options: .ignoreUnknownCharacters)
103
+ }
104
+ }
@@ -0,0 +1,61 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Configures print formatter layout (margins, max dimensions, font).
5
+ class PrinterLayout {
6
+
7
+ private var contentInsets: UIEdgeInsets = .zero
8
+ private var maximumContentWidth: CGFloat = 0
9
+ private var maximumContentHeight: CGFloat = 0
10
+
11
+ init(dictionary spec: [String: Any]?) {
12
+ guard let spec = spec else { return }
13
+
14
+ if let margin = spec["margin"] as? [String: Any] {
15
+ contentInsets = UIEdgeInsets(
16
+ top: CGFloat(PrinterUnit.convert(margin["top"])),
17
+ left: CGFloat(PrinterUnit.convert(margin["left"])),
18
+ bottom: CGFloat(PrinterUnit.convert(margin["bottom"])),
19
+ right: CGFloat(PrinterUnit.convert(margin["right"]))
20
+ )
21
+ }
22
+
23
+ maximumContentWidth = CGFloat(PrinterUnit.convert(spec["maxWidth"]))
24
+ maximumContentHeight = CGFloat(PrinterUnit.convert(spec["maxHeight"]))
25
+ }
26
+
27
+ /// Configures formatter with layout and optional font settings.
28
+ static func configureFormatter(
29
+ _ formatter: UIPrintFormatter, withSettings settings: [String: Any]
30
+ ) -> UIPrintFormatter {
31
+ let layout = PrinterLayout(dictionary: settings)
32
+ layout.configureFormatter(formatter)
33
+
34
+ if let textFormatter = formatter as? UISimpleTextPrintFormatter {
35
+ layout.configureTextFormatter(textFormatter, withSettings: settings)
36
+ }
37
+
38
+ return formatter
39
+ }
40
+
41
+ func configureFormatter(_ formatter: UIPrintFormatter) {
42
+ if maximumContentHeight > 0 {
43
+ formatter.maximumContentHeight = maximumContentHeight
44
+ }
45
+
46
+ if maximumContentWidth > 0 {
47
+ formatter.maximumContentWidth = maximumContentWidth
48
+ }
49
+
50
+ formatter.perPageContentInsets = contentInsets
51
+ }
52
+
53
+ func configureTextFormatter(
54
+ _ formatter: UISimpleTextPrintFormatter, withSettings settings: [String: Any]
55
+ ) {
56
+ let font = PrinterFont(dictionary: settings["font"] as? [String: Any])
57
+ formatter.font = font.font
58
+ formatter.color = font.color
59
+ formatter.textAlignment = font.alignment
60
+ }
61
+ }
@@ -0,0 +1,71 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Handles paper size selection for print jobs.
5
+ /// Supports both named sizes (A4, A5, Letter, etc.) and custom width/height.
6
+ class PrinterPaper {
7
+
8
+ let size: CGSize
9
+ let length: CGFloat
10
+
11
+ /// Common paper sizes in points (72 points = 1 inch).
12
+ private static let namedSizes: [String: CGSize] = [
13
+ // ISO A series
14
+ "A0": CGSize(width: 2383.94, height: 3370.39),
15
+ "A1": CGSize(width: 1683.78, height: 2383.94),
16
+ "A2": CGSize(width: 1190.55, height: 1683.78),
17
+ "A3": CGSize(width: 841.89, height: 1190.55),
18
+ "A4": CGSize(width: 595.28, height: 841.89),
19
+ "A5": CGSize(width: 419.53, height: 595.28),
20
+ "A6": CGSize(width: 297.64, height: 419.53),
21
+ "A7": CGSize(width: 209.76, height: 297.64),
22
+ "A8": CGSize(width: 147.40, height: 209.76),
23
+ "A9": CGSize(width: 104.88, height: 147.40),
24
+ "A10": CGSize(width: 73.70, height: 104.88),
25
+ // ISO B series
26
+ "B0": CGSize(width: 2834.65, height: 4008.19),
27
+ "B1": CGSize(width: 2004.09, height: 2834.65),
28
+ "B2": CGSize(width: 1417.32, height: 2004.09),
29
+ "B3": CGSize(width: 1000.63, height: 1417.32),
30
+ "B4": CGSize(width: 708.66, height: 1000.63),
31
+ "B5": CGSize(width: 498.90, height: 708.66),
32
+ "B6": CGSize(width: 354.33, height: 498.90),
33
+ // North America
34
+ "LETTER": CGSize(width: 612, height: 792),
35
+ "LEGAL": CGSize(width: 612, height: 1008),
36
+ "TABLOID": CGSize(width: 792, height: 1224),
37
+ "LEDGER": CGSize(width: 1224, height: 792),
38
+ "JUNIOR_LEGAL": CGSize(width: 576, height: 360),
39
+ "GOVT_LETTER": CGSize(width: 576, height: 756),
40
+ // Photo sizes
41
+ "4X6": CGSize(width: 288, height: 432),
42
+ "5X7": CGSize(width: 360, height: 504),
43
+ "8X10": CGSize(width: 576, height: 720),
44
+ // JIS
45
+ "JIS_B4": CGSize(width: 728.50, height: 1031.81),
46
+ "JIS_B5": CGSize(width: 515.91, height: 728.50),
47
+ ]
48
+
49
+ init(dictionary spec: [String: Any]?) {
50
+ let spec = spec ?? [:]
51
+ let name = (spec["name"] as? String)?.uppercased() ?? ""
52
+
53
+ if !name.isEmpty, let namedSize = PrinterPaper.namedSizes[name] {
54
+ self.size = namedSize
55
+ } else {
56
+ self.size = CGSize(
57
+ width: CGFloat(PrinterUnit.convert(spec["width"])),
58
+ height: CGFloat(PrinterUnit.convert(spec["height"]))
59
+ )
60
+ }
61
+ self.length = CGFloat(PrinterUnit.convert(spec["length"]))
62
+ }
63
+
64
+ /// Finds the best matching paper from available paper list.
65
+ func bestPaper(from list: [UIPrintPaper]) -> UIPrintPaper? {
66
+ if size.height > 0 || size.width > 0 {
67
+ return UIPrintPaper.bestPaper(forPageSize: size, withPapersFrom: list)
68
+ }
69
+ return nil
70
+ }
71
+ }