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.
- package/CHANGELOG.md +17 -0
- package/README.md +10 -3
- package/android/build.gradle +8 -10
- package/android/src/main/AndroidManifest.xml +1 -3
- package/android/src/main/java/expo/modules/print/PrintDocumentAdapter.kt +12 -10
- package/android/src/main/java/expo/modules/print/PrintExceptions.kt +27 -0
- package/android/src/main/java/expo/modules/print/PrintModule.kt +125 -103
- package/android/src/main/java/expo/modules/print/PrintOptions.kt +14 -0
- package/android/src/main/java/expo/modules/print/PrintPDFRenderTask.kt +29 -35
- package/android/src/main/java/expo/modules/print/PrintResponse.kt +11 -0
- package/build/ExponentPrint.d.ts +2 -2
- package/build/ExponentPrint.d.ts.map +1 -1
- package/build/ExponentPrint.js +3 -2
- package/build/ExponentPrint.js.map +1 -1
- package/build/Print.d.ts +5 -0
- package/build/Print.d.ts.map +1 -1
- package/build/Print.js +5 -0
- package/build/Print.js.map +1 -1
- package/build/Print.types.d.ts +0 -1
- package/build/Print.types.d.ts.map +1 -1
- package/build/Print.types.js.map +1 -1
- package/expo-module.config.json +9 -0
- package/ios/{EXPrint.podspec → ExpoPrint.podspec} +4 -3
- package/ios/ExpoPrintExceptions.swift +67 -0
- package/ios/ExpoPrintModule.swift +58 -0
- package/ios/ExpoPrintToFile.swift +113 -0
- package/ios/ExpoPrintWithPrinter.swift +147 -0
- package/ios/ExpoPrinterSelector.swift +43 -0
- package/ios/ExpoWKPDFRenderer.swift +64 -0
- package/ios/ExpoWKViewPrintPDFRenderer.swift +39 -0
- package/ios/PrintOptions.swift +86 -0
- package/ios/PrintResponse.swift +22 -0
- package/package.json +2 -2
- package/src/ExponentPrint.ts +5 -2
- package/src/Print.ts +5 -0
- package/src/Print.types.ts +0 -1
- package/android/src/main/java/expo/modules/print/PrintPackage.kt +0 -11
- package/ios/EXPrint/EXPrint.h +0 -10
- package/ios/EXPrint/EXPrint.m +0 -408
- package/ios/EXPrint/EXWKPDFRenderer.h +0 -20
- package/ios/EXPrint/EXWKPDFRenderer.m +0 -67
- package/ios/EXPrint/EXWKViewPrintPDFRenderer.h +0 -17
- 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
|
-
|
|
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/
|
|
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
|
package/android/build.gradle
CHANGED
|
@@ -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.
|
|
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.
|
|
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 {
|
|
@@ -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
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
52
|
+
printFailed(callback, CannotLoadUriException(uri, e), continuation)
|
|
51
53
|
}
|
|
52
54
|
} else {
|
|
53
|
-
printFailed(callback,
|
|
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,
|
|
67
|
-
|
|
68
|
-
|
|
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.
|
|
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.
|
|
9
|
-
import expo.modules.
|
|
10
|
-
import
|
|
11
|
-
import expo.modules.
|
|
12
|
-
import expo.modules.
|
|
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
|
|
17
|
-
private val ORIENTATION_PORTRAIT = "portrait"
|
|
18
|
-
private val ORIENTATION_LANDSCAPE = "landscape"
|
|
26
|
+
class PrintModule : Module() {
|
|
19
27
|
private val jobName = "Printing"
|
|
20
|
-
|
|
28
|
+
override fun definition() = ModuleDefinition {
|
|
29
|
+
Name("ExpoPrint")
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
AsyncFunction("print") Coroutine { options: PrintOptions ->
|
|
32
|
+
return@Coroutine print(options)
|
|
33
|
+
}
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
AsyncFunction("printToFileAsync") Coroutine { options: PrintOptions ->
|
|
36
|
+
return@Coroutine printToFile(options)
|
|
37
|
+
}
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
} catch (e:
|
|
78
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
132
|
+
override fun onRenderError(exception: CodedException) {
|
|
133
|
+
continuation.resumeWithException(exception)
|
|
119
134
|
}
|
|
120
|
-
|
|
135
|
+
}
|
|
121
136
|
}
|
|
122
137
|
|
|
123
|
-
private fun
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
14
|
-
import expo.modules.
|
|
13
|
+
import androidx.annotation.UiThread
|
|
14
|
+
import expo.modules.kotlin.exception.CodedException
|
|
15
15
|
import java.io.File
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
import kotlin.math.roundToInt
|
|
18
18
|
|
|
19
|
-
class PrintPDFRenderTask(private val context: Context, private val options:
|
|
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
|
-
|
|
32
|
+
@UiThread
|
|
33
|
+
fun render(outputFile: File?, fileDescriptor: ParcelFileDescriptor?, callbacks: Callbacks) {
|
|
33
34
|
this.callbacks = callbacks
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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.
|
|
51
|
+
if (options.html != null) {
|
|
62
52
|
var width = DEFAULT_MEDIA_WIDTH
|
|
63
53
|
var height = DEFAULT_MEDIA_HEIGHT
|
|
64
|
-
|
|
65
|
-
width =
|
|
54
|
+
options.width?.let {
|
|
55
|
+
width = it
|
|
66
56
|
}
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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(
|
|
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(
|
|
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
|
package/build/ExponentPrint.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
export 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":";
|
|
1
|
+
{"version":3,"file":"ExponentPrint.d.ts","sourceRoot":"","sources":["../src/ExponentPrint.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,WAAW,KAAmC,CAAC;AAErD,eAAe,WAAW,CAAC"}
|
package/build/ExponentPrint.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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,
|
|
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
|
*/
|