expo-print 12.2.1 → 12.4.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 (43) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +10 -3
  3. package/android/build.gradle +8 -10
  4. package/android/src/main/AndroidManifest.xml +1 -3
  5. package/android/src/main/java/expo/modules/print/PrintDocumentAdapter.kt +12 -10
  6. package/android/src/main/java/expo/modules/print/PrintExceptions.kt +27 -0
  7. package/android/src/main/java/expo/modules/print/PrintModule.kt +125 -103
  8. package/android/src/main/java/expo/modules/print/PrintOptions.kt +14 -0
  9. package/android/src/main/java/expo/modules/print/PrintPDFRenderTask.kt +29 -35
  10. package/android/src/main/java/expo/modules/print/PrintResponse.kt +11 -0
  11. package/build/ExponentPrint.d.ts +2 -2
  12. package/build/ExponentPrint.d.ts.map +1 -1
  13. package/build/ExponentPrint.js +3 -2
  14. package/build/ExponentPrint.js.map +1 -1
  15. package/build/Print.d.ts +5 -0
  16. package/build/Print.d.ts.map +1 -1
  17. package/build/Print.js +5 -0
  18. package/build/Print.js.map +1 -1
  19. package/build/Print.types.d.ts +0 -1
  20. package/build/Print.types.d.ts.map +1 -1
  21. package/build/Print.types.js.map +1 -1
  22. package/expo-module.config.json +9 -0
  23. package/ios/{EXPrint.podspec → ExpoPrint.podspec} +4 -3
  24. package/ios/ExpoPrintExceptions.swift +67 -0
  25. package/ios/ExpoPrintModule.swift +58 -0
  26. package/ios/ExpoPrintToFile.swift +113 -0
  27. package/ios/ExpoPrintWithPrinter.swift +147 -0
  28. package/ios/ExpoPrinterSelector.swift +43 -0
  29. package/ios/ExpoWKPDFRenderer.swift +64 -0
  30. package/ios/ExpoWKViewPrintPDFRenderer.swift +39 -0
  31. package/ios/PrintOptions.swift +86 -0
  32. package/ios/PrintResponse.swift +22 -0
  33. package/package.json +2 -2
  34. package/src/ExponentPrint.ts +5 -2
  35. package/src/Print.ts +5 -0
  36. package/src/Print.types.ts +0 -1
  37. package/android/src/main/java/expo/modules/print/PrintPackage.kt +0 -11
  38. package/ios/EXPrint/EXPrint.h +0 -10
  39. package/ios/EXPrint/EXPrint.m +0 -408
  40. package/ios/EXPrint/EXWKPDFRenderer.h +0 -20
  41. package/ios/EXPrint/EXWKPDFRenderer.m +0 -67
  42. package/ios/EXPrint/EXWKViewPrintPDFRenderer.h +0 -17
  43. package/ios/EXPrint/EXWKViewPrintPDFRenderer.m +0 -54
package/CHANGELOG.md CHANGED
@@ -10,6 +10,23 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 12.4.0 — 2023-06-21
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fixed Android build warnings for Gradle version 8. ([#22537](https://github.com/expo/expo/pull/22537), [#22609](https://github.com/expo/expo/pull/22609) by [@kudo](https://github.com/kudo))
18
+
19
+ ## 12.3.0 — 2023-05-08
20
+
21
+ ### 🎉 New features
22
+
23
+ - Migrated iOS codebase to use Expo modules API. ([#21561](https://github.com/expo/expo/pull/21561) by [@behenate](https://github.com/behenate))
24
+ - Migrated Android codebase to use the Expo modules API and Kotlin coroutines. ([#21714](https://github.com/expo/expo/pull/21714) by [@behenate](https://github.com/behenate))
25
+
26
+ ### 🐛 Bug fixes
27
+
28
+ - Fixed [Multiple Print Bug on iOS 16](https://github.com/expo/expo/issues/19399). ([#21561](https://github.com/expo/expo/pull/21561) by [@behenate](https://github.com/behenate))
29
+
13
30
  ## 12.2.1 — 2023-02-09
14
31
 
15
32
  _This version does not introduce any user-facing changes._
package/README.md CHANGED
@@ -1,4 +1,11 @@
1
- # expo-print
1
+ <p>
2
+ <a href="https://docs.expo.dev/versions/latest/sdk/print/">
3
+ <img
4
+ src="../../.github/resources/expo-print.svg"
5
+ alt="expo-print"
6
+ height="64" />
7
+ </a>
8
+ </p>
2
9
 
3
10
  Provides an API for iOS (AirPrint) and Android printing functionality.
4
11
 
@@ -9,7 +16,7 @@ Provides an API for iOS (AirPrint) and Android printing functionality.
9
16
 
10
17
  # Installation in managed Expo projects
11
18
 
12
- For [managed](https://docs.expo.dev/versions/latest/introduction/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/print/).
19
+ For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/print/).
13
20
 
14
21
  # Installation in bare React Native projects
15
22
 
@@ -18,7 +25,7 @@ For bare React Native projects, you must ensure that you have [installed and con
18
25
  ### Add the package to your npm dependencies
19
26
 
20
27
  ```
21
- expo install expo-print
28
+ npx expo install expo-print
22
29
  ```
23
30
 
24
31
  ### Configure for iOS
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '12.2.1'
6
+ version = '12.4.0'
7
7
 
8
8
  buildscript {
9
9
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -35,19 +35,11 @@ buildscript {
35
35
  }
36
36
  }
37
37
 
38
- // Creating sources with comments
39
- task androidSourcesJar(type: Jar) {
40
- classifier = 'sources'
41
- from android.sourceSets.main.java.srcDirs
42
- }
43
-
44
38
  afterEvaluate {
45
39
  publishing {
46
40
  publications {
47
41
  release(MavenPublication) {
48
42
  from components.release
49
- // Add additional sourcesJar to artifacts
50
- artifact(androidSourcesJar)
51
43
  }
52
44
  }
53
45
  repositories {
@@ -70,15 +62,21 @@ android {
70
62
  jvmTarget = JavaVersion.VERSION_11.majorVersion
71
63
  }
72
64
 
65
+ namespace "expo.modules.print"
73
66
  defaultConfig {
74
67
  minSdkVersion safeExtGet("minSdkVersion", 21)
75
68
  targetSdkVersion safeExtGet("targetSdkVersion", 33)
76
69
  versionCode 27
77
- versionName "12.2.1"
70
+ versionName "12.4.0"
78
71
  }
79
72
  lintOptions {
80
73
  abortOnError false
81
74
  }
75
+ publishing {
76
+ singleVariant("release") {
77
+ withSourcesJar()
78
+ }
79
+ }
82
80
  }
83
81
 
84
82
  dependencies {
@@ -1,5 +1,3 @@
1
-
2
- <manifest package="expo.modules.print">
1
+ <manifest>
3
2
 
4
3
  </manifest>
5
-
@@ -10,16 +10,18 @@ import android.print.PrintAttributes
10
10
  import android.print.PrintDocumentAdapter
11
11
  import android.print.PrintDocumentInfo
12
12
  import android.webkit.URLUtil
13
- import expo.modules.core.Promise
14
13
  import java.io.IOException
14
+ import java.lang.ref.WeakReference
15
15
  import java.net.URL
16
+ import kotlin.coroutines.Continuation
17
+ import kotlin.coroutines.resumeWithException
16
18
 
17
- class PrintDocumentAdapter(val context: Context, val promise: Promise, private val uri: String?) : PrintDocumentAdapter() {
19
+ class PrintDocumentAdapter(private val context: WeakReference<Context>, private val continuation: Continuation<Unit>, private val uri: String?) : PrintDocumentAdapter() {
18
20
  private val jobName = "Printing"
19
21
 
20
22
  override fun onWrite(pages: Array<PageRange>, destination: ParcelFileDescriptor, cancellationSignal: CancellationSignal, callback: WriteResultCallback) {
21
23
  if (uri == null) {
22
- printFailed(callback, "E_INVALID_URI", "Given URI is null.", promise)
24
+ printFailed(callback, NullUriException(), continuation)
23
25
  return
24
26
  }
25
27
  val isUrl = URLUtil.isValidUrl(uri)
@@ -28,7 +30,7 @@ class PrintDocumentAdapter(val context: Context, val promise: Promise, private v
28
30
  try {
29
31
  val inputStream = if (URLUtil.isContentUrl(uri)) {
30
32
  // URI starting with content://
31
- context.contentResolver.openInputStream(Uri.parse(uri))
33
+ context.get()?.contentResolver?.openInputStream(Uri.parse(uri))
32
34
  } else {
33
35
  // other URIs, like file://
34
36
  URL(uri).openStream()
@@ -38,7 +40,7 @@ class PrintDocumentAdapter(val context: Context, val promise: Promise, private v
38
40
  }
39
41
  } catch (e: Exception) {
40
42
  e.printStackTrace()
41
- printFailed(callback, "E_CANNOT_LOAD", e.message, promise)
43
+ printFailed(callback, CannotLoadUriException(uri, e), continuation)
42
44
  }
43
45
  }.start()
44
46
  } else if (uri.startsWith("data:") && uri.contains(";base64,")) {
@@ -47,10 +49,10 @@ class PrintDocumentAdapter(val context: Context, val promise: Promise, private v
47
49
  FileUtils.copyToOutputStream(destination, callback, it)
48
50
  }
49
51
  } catch (e: IOException) {
50
- printFailed(callback, "E_CANNOT_LOAD", "An error occurred while trying to load given data URI.", promise)
52
+ printFailed(callback, CannotLoadUriException(uri, e), continuation)
51
53
  }
52
54
  } else {
53
- printFailed(callback, "E_INVALID_URI", "Given URI is not valid.", promise)
55
+ printFailed(callback, InvalidUriException(), continuation)
54
56
  }
55
57
  }
56
58
 
@@ -63,8 +65,8 @@ class PrintDocumentAdapter(val context: Context, val promise: Promise, private v
63
65
  callback.onLayoutFinished(pdi, true)
64
66
  }
65
67
 
66
- private fun printFailed(callback: WriteResultCallback, code: String, error: CharSequence?, promise: Promise) {
67
- callback.onWriteFailed(error)
68
- promise.reject(code, error as String)
68
+ private fun printFailed(callback: WriteResultCallback, exception: Throwable, continuation: Continuation<Unit>) {
69
+ continuation.resumeWithException(exception)
70
+ callback.onWriteFailed(exception.localizedMessage)
69
71
  }
70
72
  }
@@ -0,0 +1,27 @@
1
+ package expo.modules.print
2
+
3
+ import expo.modules.kotlin.exception.CodedException
4
+
5
+ internal class Base64EncodingFailedException(cause: Throwable? = null) :
6
+ CodedException("An error occurred while encoding PDF file to base64 string: ", cause)
7
+
8
+ internal class UnexpectedPrintException(message: String, cause: Throwable? = null) :
9
+ CodedException(message, cause)
10
+
11
+ internal class CannotLoadUriException(uri: String? = "null", cause: Throwable? = null) :
12
+ CodedException("An error occurred while trying to load the following data URI: $uri", cause)
13
+
14
+ internal class InvalidUriException(uri: String? = "null") :
15
+ CodedException("Given URI: $uri is not valid")
16
+
17
+ internal class NullUriException :
18
+ CodedException("Given URI is null")
19
+
20
+ internal class PdfWriteException(cause: Throwable? = null) :
21
+ CodedException("An error occured while writing the PDF data", cause)
22
+
23
+ internal class FileNotFoundException(cause: Throwable? = null) :
24
+ CodedException("Cannot create or open a file", cause)
25
+
26
+ internal class PrintManagerNotAvailableException :
27
+ CodedException("Cannot find the PrintManager")
@@ -1,140 +1,162 @@
1
1
  package expo.modules.print
2
2
 
3
3
  import android.content.Context
4
- import android.os.Bundle
4
+ import android.os.ParcelFileDescriptor
5
5
  import android.print.PrintAttributes
6
- import android.print.PrintDocumentAdapter
7
6
  import android.print.PrintManager
8
- import expo.modules.core.ExportedModule
9
- import expo.modules.core.ModuleRegistry
10
- import expo.modules.core.Promise
11
- import expo.modules.core.interfaces.ActivityProvider
12
- import expo.modules.core.interfaces.ExpoMethod
7
+ import expo.modules.kotlin.modules.Module
8
+ import expo.modules.kotlin.modules.ModuleDefinition
9
+ import android.print.PrintDocumentAdapter
10
+ import expo.modules.kotlin.exception.CodedException
11
+ import expo.modules.kotlin.exception.Exceptions
12
+ import expo.modules.kotlin.functions.Coroutine
13
+ import kotlinx.coroutines.Dispatchers
14
+ import kotlinx.coroutines.suspendCancellableCoroutine
15
+ import kotlinx.coroutines.withContext
13
16
  import java.io.File
14
17
  import java.io.IOException
18
+ import java.lang.ref.WeakReference
19
+ import kotlin.coroutines.Continuation
20
+ import kotlin.coroutines.resume
21
+ import kotlin.coroutines.resumeWithException
22
+
23
+ private const val ORIENTATION_PORTRAIT = "portrait"
24
+ private const val ORIENTATION_LANDSCAPE = "landscape"
15
25
 
16
- class PrintModule(context: Context) : ExportedModule(context) {
17
- private val ORIENTATION_PORTRAIT = "portrait"
18
- private val ORIENTATION_LANDSCAPE = "landscape"
26
+ class PrintModule : Module() {
19
27
  private val jobName = "Printing"
20
- private lateinit var moduleRegistry: ModuleRegistry
28
+ override fun definition() = ModuleDefinition {
29
+ Name("ExpoPrint")
21
30
 
22
- override fun onCreate(moduleRegistry: ModuleRegistry) {
23
- this.moduleRegistry = moduleRegistry
24
- }
31
+ AsyncFunction("print") Coroutine { options: PrintOptions ->
32
+ return@Coroutine print(options)
33
+ }
25
34
 
26
- override fun getName(): String {
27
- return "ExponentPrint"
28
- }
35
+ AsyncFunction("printToFileAsync") Coroutine { options: PrintOptions ->
36
+ return@Coroutine printToFile(options)
37
+ }
29
38
 
30
- override fun getConstants(): MutableMap<String, Any?> {
31
- return hashMapOf(
32
- "Orientation" to hashMapOf(
39
+ Constants(
40
+ "Orientation" to mapOf(
33
41
  "portrait" to ORIENTATION_PORTRAIT,
34
42
  "landscape" to ORIENTATION_LANDSCAPE
35
43
  )
36
44
  )
37
45
  }
38
46
 
39
- @ExpoMethod
40
- fun print(options: Map<String?, Any?>, promise: Promise) {
41
- val html = if (options.containsKey("html")) {
42
- options["html"] as String?
43
- } else {
44
- null
45
- }
46
- val uri = if (options.containsKey("uri")) {
47
- options["uri"] as String?
48
- } else {
49
- null
47
+ val context: Context
48
+ get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
49
+
50
+ private val currentActivity
51
+ get() = appContext.activityProvider?.currentActivity ?: throw Exceptions.MissingActivity()
52
+
53
+ private suspend fun print(options: PrintOptions) {
54
+ withContext(Dispatchers.Main) {
55
+ suspendCancellableCoroutine { continuation ->
56
+ if (options.html != null) {
57
+ // Renders HTML to PDF and then prints
58
+ try {
59
+ val renderTask = PrintPDFRenderTask(context, options)
60
+ renderTask.render(
61
+ null,
62
+ null,
63
+ createPrintCallbacks(options, continuation)
64
+ )
65
+ } catch (e: Exception) {
66
+ continuation.resumeWithException(UnexpectedPrintException("There was an error while trying to print HTML ", e))
67
+ }
68
+ } else {
69
+ // Prints from given URI (file path or base64 data URI starting with `data:*;base64,`)
70
+ try {
71
+ val pda = PrintDocumentAdapter(WeakReference(context), continuation, options.uri)
72
+ printDocumentToPrinter(pda, options)
73
+ continuation.resume(null)
74
+ } catch (e: Exception) {
75
+ continuation.resumeWithException(UnexpectedPrintException("There was an error while trying to print file ", e))
76
+ }
77
+ }
78
+ }
50
79
  }
51
- if (html != null) {
52
- // Renders HTML to PDF and then prints
80
+ }
81
+
82
+ private suspend fun printToFile(options: PrintOptions): FilePrintResult? {
83
+ var filePath: String
84
+ var fileDescriptor: ParcelFileDescriptor?
85
+ var outputFile: File
86
+
87
+ // Create the files on IO thread
88
+ withContext(Dispatchers.IO) {
53
89
  try {
54
- val renderTask = PrintPDFRenderTask(context, options, moduleRegistry)
55
- renderTask.render(
56
- null,
57
- object : PrintPDFRenderTask.Callbacks() {
58
- override fun onRenderFinished(document: PrintDocumentAdapter, outputFile: File?, numberOfPages: Int) {
59
- printDocumentToPrinter(document, options)
60
- promise.resolve(null)
61
- }
62
-
63
- override fun onRenderError(errorCode: String?, errorMessage: String?, exception: Exception?) {
64
- promise.reject(errorCode, errorMessage, exception)
65
- }
66
- }
67
- )
68
- } catch (e: Exception) {
69
- promise.reject("E_CANNOT_PRINT", "There was an error while trying to print HTML.", e)
90
+ filePath = FileUtils.generateFilePath(context)
91
+ } catch (e: IOException) {
92
+ throw UnexpectedPrintException("An unknown I/O exception occurred ", e)
70
93
  }
71
- } else {
72
- // Prints from given URI (file path or base64 data URI starting with `data:*;base64,`)
73
94
  try {
74
- val pda = PrintDocumentAdapter(context, promise, uri)
75
- printDocumentToPrinter(pda, options)
76
- promise.resolve(null)
77
- } catch (e: Exception) {
78
- promise.reject("E_CANNOT_PRINT", "There was an error while trying to print a file.", e)
95
+ outputFile = File(filePath)
96
+ outputFile.createNewFile()
97
+ fileDescriptor = ParcelFileDescriptor.open(outputFile, ParcelFileDescriptor.MODE_TRUNCATE or ParcelFileDescriptor.MODE_WRITE_ONLY)
98
+ } catch (e: IOException) {
99
+ throw FileNotFoundException(e)
79
100
  }
80
101
  }
81
- }
82
102
 
83
- @ExpoMethod
84
- fun printToFileAsync(options: Map<String?, Any?>, promise: Promise) {
85
- val filePath: String
86
- try {
87
- filePath = FileUtils.generateFilePath(context)
88
- } catch (e: IOException) {
89
- promise.reject("E_PRINT_FAILED", "An unknown I/O exception occurred.", e)
90
- return
103
+ return withContext(Dispatchers.Main) {
104
+ return@withContext suspendCancellableCoroutine { continuation ->
105
+ val renderTask = PrintPDFRenderTask(context, options)
106
+ renderTask.render(
107
+ outputFile,
108
+ fileDescriptor,
109
+ createPrintToFileCallbacks(options, continuation)
110
+ )
111
+ }
91
112
  }
92
- val renderTask = PrintPDFRenderTask(context, options, moduleRegistry)
93
- renderTask.render(
94
- filePath,
95
- object : PrintPDFRenderTask.Callbacks() {
96
- override fun onRenderFinished(document: PrintDocumentAdapter, outputFile: File?, numberOfPages: Int) {
97
- val uri = FileUtils.uriFromFile(outputFile).toString()
98
- var base64: String? = null
99
- if (options.containsKey("base64") && (options["base64"] as Boolean? == true)) {
100
- try {
101
- base64 = outputFile?.let { FileUtils.encodeFromFile(it) }
102
- } catch (e: IOException) {
103
- promise.reject("E_PRINT_BASE64_FAILED", "An error occurred while encoding PDF file to base64 string.", e)
104
- return
105
- }
113
+ }
114
+
115
+ private fun createPrintToFileCallbacks(options: PrintOptions, continuation: Continuation<FilePrintResult>): PrintPDFRenderTask.Callbacks {
116
+ return object : PrintPDFRenderTask.Callbacks() {
117
+ override fun onRenderFinished(document: PrintDocumentAdapter, outputFile: File?, numberOfPages: Int) {
118
+ val uri = FileUtils.uriFromFile(outputFile).toString()
119
+ var base64: String? = null
120
+ if (options.base64) {
121
+ try {
122
+ base64 = outputFile?.let { FileUtils.encodeFromFile(it) }
123
+ } catch (e: IOException) {
124
+ continuation.resumeWithException(Base64EncodingFailedException(e))
125
+ return
106
126
  }
107
- promise.resolve(
108
- Bundle().apply {
109
- putString("uri", uri)
110
- putInt("numberOfPages", numberOfPages)
111
- if (base64 != null) putString("base64", base64)
112
- }
113
- )
114
127
  }
128
+ val result = FilePrintResult(uri, numberOfPages, base64)
129
+ continuation.resume(result)
130
+ }
115
131
 
116
- override fun onRenderError(errorCode: String?, errorMessage: String?, exception: Exception?) {
117
- promise.reject(errorCode, errorMessage, exception)
118
- }
132
+ override fun onRenderError(exception: CodedException) {
133
+ continuation.resumeWithException(exception)
119
134
  }
120
- )
135
+ }
121
136
  }
122
137
 
123
- private fun printDocumentToPrinter(document: PrintDocumentAdapter, options: Map<String?, Any?>) {
124
- val printManager = moduleRegistry
125
- .getModule(ActivityProvider::class.java)
126
- .currentActivity
127
- ?.getSystemService(Context.PRINT_SERVICE) as? PrintManager
128
- val attributes = getAttributesFromOptions(options)
129
- printManager?.print(jobName, document, attributes.build())
130
- }
138
+ private fun createPrintCallbacks(options: PrintOptions, continuation: Continuation<Unit>): PrintPDFRenderTask.Callbacks {
139
+ return object : PrintPDFRenderTask.Callbacks() {
140
+ override fun onRenderFinished(document: PrintDocumentAdapter, outputFile: File?, numberOfPages: Int) {
141
+ printDocumentToPrinter(document, options)
142
+ continuation.resume(Unit)
143
+ }
131
144
 
132
- private fun getAttributesFromOptions(options: Map<String?, Any?>): PrintAttributes.Builder {
133
- val orientation = if (options.containsKey("orientation")) {
134
- options["orientation"] as String?
135
- } else {
136
- null
145
+ override fun onRenderError(exception: CodedException) {
146
+ continuation.resumeWithException(exception)
147
+ }
137
148
  }
149
+ }
150
+
151
+ private fun printDocumentToPrinter(document: PrintDocumentAdapter, options: PrintOptions) {
152
+ (currentActivity.getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let {
153
+ val attributes = getAttributesFromOptions(options)
154
+ it.print(jobName, document, attributes.build())
155
+ } ?: throw PrintManagerNotAvailableException()
156
+ }
157
+
158
+ private fun getAttributesFromOptions(options: PrintOptions): PrintAttributes.Builder {
159
+ val orientation = options.orientation
138
160
  val builder = PrintAttributes.Builder()
139
161
 
140
162
  // @tsapeta: Unfortunately these attributes might be ignored on some devices or Android versions,
@@ -0,0 +1,14 @@
1
+ package expo.modules.print
2
+
3
+ import expo.modules.kotlin.records.Field
4
+ import expo.modules.kotlin.records.Record
5
+ import java.io.Serializable
6
+
7
+ internal class PrintOptions(
8
+ @Field var html: String? = null,
9
+ @Field var uri: String? = null,
10
+ @Field var width: Int? = null,
11
+ @Field var height: Int? = null,
12
+ @Field var orientation: String? = null,
13
+ @Field var base64: Boolean = false,
14
+ ) : Record, Serializable
@@ -10,13 +10,13 @@ import android.print.PrintDocumentAdapterLayoutCallback
10
10
  import android.print.PrintDocumentAdapterWriteCallback
11
11
  import android.webkit.WebView
12
12
  import android.webkit.WebViewClient
13
- import expo.modules.core.ModuleRegistry
14
- import expo.modules.core.interfaces.services.UIManager
13
+ import androidx.annotation.UiThread
14
+ import expo.modules.kotlin.exception.CodedException
15
15
  import java.io.File
16
- import java.io.IOException
16
+
17
17
  import kotlin.math.roundToInt
18
18
 
19
- class PrintPDFRenderTask(private val context: Context, private val options: Map<String?, Any?>, private val moduleRegistry: ModuleRegistry) {
19
+ internal class PrintPDFRenderTask(private val context: Context, private val options: PrintOptions) {
20
20
  private val PIXELS_PER_INCH = 72
21
21
  private val MILS_PER_INCH = 1000.0
22
22
  private val PIXELS_PER_MIL = PIXELS_PER_INCH / MILS_PER_INCH
@@ -29,52 +29,46 @@ class PrintPDFRenderTask(private val context: Context, private val options: Map<
29
29
  private lateinit var document: PrintDocumentAdapter
30
30
  private var numberOfPages = 0
31
31
 
32
- fun render(filePath: String?, callbacks: Callbacks) {
32
+ @UiThread
33
+ fun render(outputFile: File?, fileDescriptor: ParcelFileDescriptor?, callbacks: Callbacks) {
33
34
  this.callbacks = callbacks
34
- filePath?.let {
35
- try {
36
- outputFile = File(it)
37
- outputFile.createNewFile()
38
- fileDescriptor = ParcelFileDescriptor.open(outputFile, ParcelFileDescriptor.MODE_TRUNCATE or ParcelFileDescriptor.MODE_WRITE_ONLY)
39
- } catch (e: IOException) {
40
- this.callbacks.onRenderError("E_FILE_NOT_FOUND", "Cannot create or open a file.", e)
41
- return
42
- }
43
- }
44
- moduleRegistry.getModule(UIManager::class.java).runOnUiQueueThread {
45
- val html = if (options.containsKey("html")) {
46
- options["html"] as String
47
- } else {
48
- ""
49
- }
50
- webView = WebView(context)
51
- val settings = webView.settings
52
- settings.defaultTextEncodingName = "UTF-8"
53
- webView.webViewClient = webViewClient
54
- webView.loadDataWithBaseURL(null, html, "text/html; charset=utf-8", "UTF-8", null)
35
+ this.fileDescriptor = fileDescriptor
36
+ outputFile?.let {
37
+ this.outputFile = it
55
38
  }
39
+
40
+ val html = options.html ?: ""
41
+ webView = WebView(context)
42
+ val settings = webView.settings
43
+ settings.defaultTextEncodingName = "UTF-8"
44
+ webView.webViewClient = webViewClient
45
+ webView.loadDataWithBaseURL(null, html, "text/html; charset=utf-8", "UTF-8", null)
56
46
  }
57
47
 
58
48
  private val printAttributes: PrintAttributes
59
49
  get() {
60
50
  val builder = PrintAttributes.Builder()
61
- if (options.containsKey("html")) {
51
+ if (options.html != null) {
62
52
  var width = DEFAULT_MEDIA_WIDTH
63
53
  var height = DEFAULT_MEDIA_HEIGHT
64
- if (options.containsKey("width") && options["width"] != null) {
65
- width = (options["width"] as Number).toInt()
54
+ options.width?.let {
55
+ width = it
66
56
  }
67
- if (options.containsKey("height") && options["height"] != null) {
68
- height = (options["height"] as Number).toInt()
57
+
58
+ options.height?.let {
59
+ height = it
69
60
  }
61
+
70
62
  var mediaSize = PrintAttributes.MediaSize(
71
63
  "id",
72
64
  "label",
73
65
  (width / PIXELS_PER_MIL).roundToInt(),
74
66
  (height / PIXELS_PER_MIL).roundToInt()
75
67
  )
76
- if (options.containsKey("orientation") && "landscape" == options["orientation"]) {
77
- mediaSize = mediaSize.asLandscape()
68
+ options.orientation?.let {
69
+ if (it === "landscape") {
70
+ mediaSize = mediaSize.asLandscape()
71
+ }
78
72
  }
79
73
  builder
80
74
  .setMediaSize(mediaSize)
@@ -111,13 +105,13 @@ class PrintPDFRenderTask(private val context: Context, private val options: Map<
111
105
  }
112
106
 
113
107
  override fun onWriteFailed(error: CharSequence?) {
114
- callbacks.onRenderError("E_PRINT_FAILED", "An error occurred while writing PDF data.", null)
108
+ callbacks.onRenderError(PdfWriteException())
115
109
  }
116
110
  }
117
111
 
118
112
  abstract class Callbacks {
119
113
  open fun onRenderFinished(document: PrintDocumentAdapter, outputFile: File?, numberOfPages: Int) = Unit
120
114
 
121
- open fun onRenderError(errorCode: String?, errorMessage: String?, exception: Exception?) = Unit
115
+ open fun onRenderError(exception: CodedException) = Unit
122
116
  }
123
117
  }
@@ -0,0 +1,11 @@
1
+ package expo.modules.print
2
+
3
+ import expo.modules.kotlin.records.Field
4
+ import expo.modules.kotlin.records.Record
5
+ import java.io.Serializable
6
+
7
+ internal class FilePrintResult(
8
+ @Field var uri: String = "",
9
+ @Field var numberOfPages: Int = 0,
10
+ @Field var base64: String? = null
11
+ ) : Record, Serializable
@@ -1,3 +1,3 @@
1
- declare const _default: import("expo-modules-core").ProxyNativeModule;
2
- export default _default;
1
+ declare const printModule: any;
2
+ export default printModule;
3
3
  //# sourceMappingURL=ExponentPrint.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExponentPrint.d.ts","sourceRoot":"","sources":["../src/ExponentPrint.ts"],"names":[],"mappings":";AACA,wBAAgD"}
1
+ {"version":3,"file":"ExponentPrint.d.ts","sourceRoot":"","sources":["../src/ExponentPrint.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,WAAW,KAAmC,CAAC;AAErD,eAAe,WAAW,CAAC"}
@@ -1,3 +1,4 @@
1
- import { NativeModulesProxy } from 'expo-modules-core';
2
- export default NativeModulesProxy.ExponentPrint;
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ const printModule = requireNativeModule('ExpoPrint');
3
+ export default printModule;
3
4
  //# sourceMappingURL=ExponentPrint.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExponentPrint.js","sourceRoot":"","sources":["../src/ExponentPrint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,eAAe,kBAAkB,CAAC,aAAa,CAAC","sourcesContent":["import { NativeModulesProxy } from 'expo-modules-core';\nexport default NativeModulesProxy.ExponentPrint;\n"]}
1
+ {"version":3,"file":"ExponentPrint.js","sourceRoot":"","sources":["../src/ExponentPrint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,WAAW,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;AAErD,eAAe,WAAW,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\n\nconst printModule = requireNativeModule('ExpoPrint');\n\nexport default printModule;\n"]}
package/build/Print.d.ts CHANGED
@@ -9,6 +9,11 @@ export declare const Orientation: OrientationType;
9
9
  * > Note: On iOS, printing from HTML source doesn't support local asset URLs (due to `WKWebView`
10
10
  * > limitations). As a workaround you can use inlined base64-encoded strings.
11
11
  * > See [this comment](https://github.com/expo/expo/issues/7940#issuecomment-657111033) for more details.
12
+ *
13
+ * > Note: on iOS, when printing without providing a `PrintOptions.printerUrl` the `Promise` will be
14
+ * > resolved once printing is started in the native print window and rejected if the window is closed without
15
+ * > starting the print. On Android the `Promise` will be resolved immediately after displaying the native print window
16
+ * > and won't be rejected if the window is closed without starting the print.
12
17
  * @param options A map defining what should be printed.
13
18
  * @return Resolves to an empty `Promise` if printing started.
14
19
  */