@iftek/react-native-print 0.12.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 (42) hide show
  1. package/.github/stale.yml +16 -0
  2. package/.github/workflows/windows-ci.yml +40 -0
  3. package/@iftek-react-native-print.podspec +19 -0
  4. package/LICENSE +21 -0
  5. package/README.md +230 -0
  6. package/android/.gradle/7.4/checksums/checksums.lock +0 -0
  7. package/android/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock +0 -0
  8. package/android/.gradle/7.4/dependencies-accessors/gc.properties +0 -0
  9. package/android/.gradle/7.4/fileChanges/last-build.bin +0 -0
  10. package/android/.gradle/7.4/fileHashes/fileHashes.lock +0 -0
  11. package/android/.gradle/7.4/gc.properties +0 -0
  12. package/android/.gradle/vcs-1/gc.properties +0 -0
  13. package/android/build.gradle +24 -0
  14. package/android/local.properties +8 -0
  15. package/android/src/main/AndroidManifest.xml +4 -0
  16. package/android/src/main/java/com/christopherdro/RNPrint/RNPrintModule.java +237 -0
  17. package/android/src/main/java/com/christopherdro/RNPrint/RNPrintPackage.java +32 -0
  18. package/index.js +3 -0
  19. package/ios/RNPrint/RNPrint.h +15 -0
  20. package/ios/RNPrint/RNPrint.m +197 -0
  21. package/ios/RNPrint.xcodeproj/project.pbxproj +389 -0
  22. package/ios/RNPrint.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  23. package/ios/RNPrint.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  24. package/ios/RNPrintTests/Info.plist +24 -0
  25. package/package.json +23 -0
  26. package/scripts/examples_postinstall.js +117 -0
  27. package/types/index.d.ts +19 -0
  28. package/windows/README.md +57 -0
  29. package/windows/RNPrint/PropertySheet.props +16 -0
  30. package/windows/RNPrint/RNPrint.cpp +375 -0
  31. package/windows/RNPrint/RNPrint.def +3 -0
  32. package/windows/RNPrint/RNPrint.h +89 -0
  33. package/windows/RNPrint/RNPrint.vcxproj +163 -0
  34. package/windows/RNPrint/RNPrint.vcxproj.filters +33 -0
  35. package/windows/RNPrint/ReactPackageProvider.cpp +15 -0
  36. package/windows/RNPrint/ReactPackageProvider.h +16 -0
  37. package/windows/RNPrint/ReactPackageProvider.idl +9 -0
  38. package/windows/RNPrint/packages.config +4 -0
  39. package/windows/RNPrint/pch.cpp +1 -0
  40. package/windows/RNPrint/pch.h +16 -0
  41. package/windows/RNPrint62.sln +254 -0
  42. package/windows/RNPrint63.sln +226 -0
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+
3
+ /*
4
+ * Using libraries within examples and linking them within packages.json like:
5
+ * "react-native-library-name": "file:../"
6
+ * will cause problems with the metro bundler if the example will run via
7
+ * `react-native run-[ios|android]`. This will result in an error as the metro
8
+ * bundler will find multiple versions for the same module while resolving it.
9
+ * The reason for that is that if the library is installed it also copies in the
10
+ * example folder itself as well as the node_modules folder of the library
11
+ * although their are defined in .npmignore and should be ignored in theory.
12
+ *
13
+ * This postinstall script removes the node_modules folder as well as all
14
+ * entries from the libraries .npmignore file within the examples node_modules
15
+ * folder after the library was installed. This should resolve the metro
16
+ * bundler issue mentioned above.
17
+ *
18
+ * It is expected this scripts lives in the libraries root folder within a
19
+ * scripts folder. As first parameter the relative path to the libraries
20
+ * folder within the example's node_modules folder may be provided.
21
+ * This script will determine the path from this project's package.json file
22
+ * if no such relative path is provided.
23
+ * An example's package.json entry could look like:
24
+ * "postinstall": "node ../scripts/examples_postinstall.js node_modules/react-native-library-name/"
25
+ */
26
+
27
+ 'use strict';
28
+
29
+ const fs = require('fs');
30
+ const path = require('path');
31
+
32
+ /// Delete all files and directories for the given path
33
+ const removeFileDirectoryRecursively = fileDirPath => {
34
+ // Remove file
35
+ if (!fs.lstatSync(fileDirPath).isDirectory()) {
36
+ fs.unlinkSync(fileDirPath);
37
+ return;
38
+ }
39
+
40
+ // Go down the directory an remove each file / directory recursively
41
+ fs.readdirSync(fileDirPath).forEach(entry => {
42
+ const entryPath = path.join(fileDirPath, entry);
43
+ removeFileDirectoryRecursively(entryPath);
44
+ });
45
+ fs.rmdirSync(fileDirPath);
46
+ };
47
+
48
+ const removeFileDir = fileDirPath => {
49
+ try {
50
+ removeFileDirectoryRecursively(fileDirPath);
51
+ console.log(`Deleted: ${fileDirPath}`);
52
+ } catch (err) {
53
+ console.log(`Error deleting ${fileDirPath}: ${err.message}`);
54
+ }
55
+ };
56
+
57
+ /// Remove example/node_modules/react-native-library-name/node_modules directory
58
+ const removeLibraryNodeModulesPath = nodeModulesPath => {
59
+ if (!fs.existsSync(nodeModulesPath)) {
60
+ console.log(
61
+ `No node_modules found at ${nodeModulesPath}. Skipping delete.`,
62
+ );
63
+ return;
64
+ }
65
+
66
+ removeFileDir(nodeModulesPath);
67
+ };
68
+
69
+ /// Remove all entries from the .npmignore within example/node_modules/react-native-library-name/
70
+ const removeLibraryNpmIgnorePaths = (npmIgnorePath, libraryNodeModulesPath) => {
71
+ if (!fs.existsSync(npmIgnorePath)) {
72
+ console.log(
73
+ `No .npmignore found at ${npmIgnorePath}. Skipping deleting content.`,
74
+ );
75
+ return;
76
+ }
77
+
78
+ fs.readFileSync(npmIgnorePath, 'utf8')
79
+ .split(/\r?\n/)
80
+ .forEach(entry => {
81
+ if (entry.length === 0) {
82
+ return;
83
+ }
84
+
85
+ const npmIgnoreLibraryNodeModulesEntryPath = path.resolve(
86
+ libraryNodeModulesPath,
87
+ entry,
88
+ );
89
+ if (!fs.existsSync(npmIgnoreLibraryNodeModulesEntryPath)) {
90
+ return;
91
+ }
92
+
93
+ removeFileDir(npmIgnoreLibraryNodeModulesEntryPath);
94
+ });
95
+ };
96
+
97
+ // Main start sweeping process
98
+ (() => {
99
+ // Read out dir of example project
100
+ const cwd = process.cwd();
101
+
102
+ console.log(`Starting postinstall cleanup for ${cwd}`);
103
+
104
+ // Resolve the React Native library's path within the example's node_modules directory
105
+ const libraryNodeModulesPath =
106
+ process.argv.length > 2
107
+ ? path.resolve(cwd, process.argv[2])
108
+ : path.resolve(cwd, 'node_modules', require('../package.json').name);
109
+
110
+ removeLibraryNodeModulesPath(
111
+ path.resolve(libraryNodeModulesPath, 'node_modules'),
112
+ );
113
+ removeLibraryNpmIgnorePaths(
114
+ path.resolve(__dirname, '../.npmignore'),
115
+ libraryNodeModulesPath,
116
+ );
117
+ })();
@@ -0,0 +1,19 @@
1
+ type PrintOptionsType = {
2
+ printerURL?: string;
3
+ isLandscape?: boolean;
4
+ jobName?: string;
5
+ } & ({ html: string } | { filePath: string });
6
+
7
+ type SelectPrinterOptionsType = {
8
+ x: number;
9
+ y: number;
10
+ };
11
+
12
+ type SelectPrinterReturnType = {
13
+ name: string;
14
+ url: string;
15
+ }
16
+
17
+ export function print(options: PrintOptionsType): Promise<any>;
18
+
19
+ export function selectPrinter(options: SelectPrinterOptionsType): Promise<SelectPrinterReturnType>;
@@ -0,0 +1,57 @@
1
+ # react-native-print Windows Implementation
2
+
3
+ ## Module Installation
4
+ You can either use autolinking on react-native-windows 0.63 and later or manually link the module on earlier realeases.
5
+
6
+ ## Automatic install with autolinking on RNW >= 0.63
7
+ react-native-print supports autolinking. Just call: `npm i react-native-print --save`
8
+
9
+ ## Manual installation on RNW >= 0.62
10
+ 1. `npm install react-native-print --save`
11
+ 2. Open your solution in Visual Studio 2019 (eg. `windows\yourapp.sln`)
12
+ 3. Right-click Solution icon in Solution Explorer > Add > Existing Project...
13
+ 4. Add `node_modules\react-native-print\windows\RNPrint\RNPrint.vcxproj`
14
+ 5. Right-click main application project > Add > Reference...
15
+ 6. Select `RNPrint` in Solution Projects
16
+ 7. In app `pch.h` add `#include "winrt/RNPrint.h"`
17
+ 8. In `App.cpp` add `PackageProviders().Append(winrt::RNPrint::ReactPackageProvider());` before `InitializeComponent();`
18
+
19
+ ## Windows print canvas
20
+
21
+ On Windows, `react-native-print` needs an element in the visual tree to add the printable pages to.
22
+ It will look for a XAML `Canvas` named `RNPrintCanvas` and use it.
23
+ This needs to be added to the XAML tree of the screens where `react-native-print` is used.
24
+
25
+ As an example, in `windows/myapp/MainPage.xaml` from the `react-native-windows` app template this can be done by adding a XAML `Grid` with an invisible `Canvas` alongside the `ReactRootView`. Change `windows/myapp/MainPage.xaml` from:
26
+ ```xaml
27
+ <Page
28
+ ...
29
+ >
30
+ <react:ReactRootView
31
+ x:Name="ReactRootView"
32
+ ...
33
+ />
34
+ </Page>
35
+ ```
36
+ to
37
+ ```xaml
38
+ <Page
39
+ ...
40
+ >
41
+ <Grid>
42
+ <Canvas x:Name="RNPrintCanvas" Opacity="0" />
43
+ <react:ReactRootView
44
+ x:Name="ReactRootView"
45
+ ...
46
+ />
47
+ </Grid>
48
+ </Page>
49
+ ```
50
+
51
+ ## Module development
52
+
53
+ If you want to contribute to this module Windows implementation, first you must install the [Windows Development Dependencies](https://aka.ms/rnw-deps).
54
+
55
+ You must temporarily install the `react-native-windows` package. Versions of `react-native-windows` and `react-native` must match, e.g. if the module uses `react-native@0.62`, install `npm i react-native-windows@^0.62 --dev`.
56
+
57
+ Now, you will be able to open corresponding `RNPrint...sln` file, e.g. `RNPrint62.sln` for `react-native-windows@0.62`.
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <ImportGroup Label="PropertySheets" />
4
+ <PropertyGroup Label="UserMacros" />
5
+ <!--
6
+ To customize common C++/WinRT project properties:
7
+ * right-click the project node
8
+ * expand the Common Properties item
9
+ * select the C++/WinRT property page
10
+
11
+ For more advanced scenarios, and complete documentation, please see:
12
+ https://github.com/Microsoft/xlang/tree/master/src/package/cppwinrt/nuget
13
+ -->
14
+ <PropertyGroup />
15
+ <ItemDefinitionGroup />
16
+ </Project>
@@ -0,0 +1,375 @@
1
+ #include "pch.h"
2
+ #include "RNPrint.h"
3
+
4
+ namespace winrt::RNPrint
5
+ {
6
+ // Searches for the RNPrintCanvas element in the visual tree.
7
+ Canvas RNPrint::searchForPrintCanvas(xaml::DependencyObject startNode)
8
+ {
9
+ const int count = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(startNode);
10
+ Canvas result = nullptr;
11
+ for (int i = 0; i < count; i++)
12
+ {
13
+ xaml::DependencyObject current = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(startNode, i);
14
+ Canvas temp = current.try_as<Canvas>();
15
+ if (temp)
16
+ {
17
+ std::string tempName = winrt::to_string(temp.Name());
18
+ if (tempName == RNPrintCanvasName)
19
+ {
20
+ return temp;
21
+ }
22
+ } else
23
+ {
24
+ result = searchForPrintCanvas(current);
25
+ if (result)
26
+ {
27
+ break;
28
+ }
29
+ }
30
+ }
31
+ return result;
32
+ }
33
+
34
+ // cleans up local variables and XAML elements.
35
+ void RNPrint::cleanUp()
36
+ {
37
+ reactContext.UIDispatcher().Post([=]()
38
+ {
39
+ auto printMan = PrintManager::GetForCurrentView();
40
+ {
41
+ std::lock_guard<std::mutex> guard(printSync);
42
+ servingPrintRequest = false;
43
+ if (printToken)
44
+ {
45
+ printMan.PrintTaskRequested(printToken);
46
+ }
47
+ printToken = {};
48
+ pageCollection.clear();
49
+ if (printCanvas)
50
+ {
51
+ printCanvas.Children().Clear();
52
+ }
53
+ }
54
+ }
55
+ );
56
+ }
57
+
58
+ // Generates a XAML page asynchronously.
59
+ concurrency::task<std::optional<xaml::UIElement>> RNPrint::GeneratePageAsync(
60
+ PdfDocument pdfDocument,
61
+ int pageNumber,
62
+ PrintPageDescription pageDescription)
63
+ {
64
+ Image pageImage;
65
+ PdfPage pdfPage = pdfDocument.GetPage(pageNumber - 1);
66
+ BitmapImage pageSource;
67
+ pageImage.Source(pageSource);
68
+ InMemoryRandomAccessStream pageStream;
69
+ Canvas page;
70
+ page.Width(pageDescription.PageSize.Width);
71
+ page.Height(pageDescription.PageSize.Height);
72
+
73
+ const float leftMargin = pageDescription.ImageableRect.X;
74
+ const float topMargin = pageDescription.ImageableRect.Y;
75
+ const float printableWidth = pageDescription.ImageableRect.Width;
76
+ const float printableHeight = pageDescription.ImageableRect.Height;
77
+
78
+ Canvas printableAreaCanvas;
79
+ printableAreaCanvas.Width(printableWidth);
80
+ printableAreaCanvas.Height(printableWidth);
81
+
82
+ auto pdfContentDimensions = pdfPage.Dimensions().MediaBox();
83
+ float pdfContentWidth = pdfContentDimensions.Width;
84
+ float pdfContentHeight = pdfContentDimensions.Height;
85
+
86
+ //Scale to Fit logic
87
+ const float scale = min(printableHeight / pdfContentHeight, printableWidth / pdfContentWidth);
88
+ pdfContentWidth *= scale;
89
+ pdfContentHeight *= scale;
90
+ Canvas::SetLeft(printableAreaCanvas, (double)leftMargin + (printableWidth - pdfContentWidth) / 2);
91
+ Canvas::SetTop(printableAreaCanvas, (double)topMargin + (printableHeight - pdfContentHeight) / 2);
92
+
93
+ auto renderOptions = PdfPageRenderOptions();
94
+ renderOptions.SourceRect(pdfContentDimensions);
95
+ renderOptions.DestinationWidth(pdfContentWidth);
96
+ renderOptions.DestinationHeight(pdfContentHeight);
97
+
98
+ printableAreaCanvas.Children().Append(pageImage);
99
+ page.Children().Append(printableAreaCanvas);
100
+
101
+ // Can't block or co_await on this Delegate.
102
+ auto renderToStreamTask = pdfPage.RenderToStreamAsync(pageStream, renderOptions);
103
+ return concurrency::create_task([=]
104
+ {
105
+ renderToStreamTask.get();
106
+ reactContext.UIDispatcher().Post([=]()
107
+ {
108
+ // Needs to be run on UI thread.
109
+ pageSource.SetSource(pageStream);
110
+ });
111
+ }).then([=]() -> std::optional<xaml::UIElement>
112
+ {
113
+ return page;
114
+ });
115
+ }
116
+
117
+ // Asynchronously loads the PDF document, registers print callbacks and fires the print UI.
118
+ IAsyncAction RNPrint::PrintAsyncHelper(
119
+ RNPrintOptions options,
120
+ ReactPromise<JSValue> promise) noexcept
121
+ {
122
+ auto capturedPromise = promise;
123
+ auto capturedOptions = options;
124
+
125
+
126
+ if ((!capturedOptions.html && !capturedOptions.filePath) || (capturedOptions.html && capturedOptions.filePath))
127
+ {
128
+ cleanUp();
129
+ capturedPromise.Reject("Must provide either 'html' or 'filePath'. Both are either missing or passed together.");
130
+ co_return;
131
+ }
132
+
133
+ if (capturedOptions.html)
134
+ {
135
+ cleanUp();
136
+ capturedPromise.Reject("Printing HTML not supported");
137
+ co_return;
138
+ } else
139
+ {
140
+ // Pdf print instructions.
141
+ winrt::Windows::Foundation::Uri uri(winrt::to_hstring(*capturedOptions.filePath));
142
+
143
+ bool isValidURL = !uri.Host().empty() && (winrt::to_string(uri.SchemeName()) == "http" || winrt::to_string(uri.SchemeName()) == "https");
144
+ PdfDocument pdfDocument = nullptr;
145
+
146
+ if (isValidURL)
147
+ {
148
+ // Should be a valid URL.
149
+ auto httpClient = HttpClient();
150
+ auto httpResponseMessage = co_await httpClient.GetAsync(uri);
151
+ auto memoryStream = InMemoryRandomAccessStream();
152
+ co_await httpResponseMessage.Content().WriteToStreamAsync(memoryStream.GetOutputStreamAt(0));
153
+ pdfDocument = co_await PdfDocument::LoadFromStreamAsync(memoryStream);
154
+ } else
155
+ {
156
+ // Not a valid URL, try to open as a file.
157
+ StorageFile pdfFile = nullptr;
158
+ pdfFile = co_await StorageFile::GetFileFromPathAsync(winrt::to_hstring(*capturedOptions.filePath));
159
+ pdfDocument = co_await PdfDocument::LoadFromFileAsync(pdfFile);
160
+ }
161
+ if (pdfDocument == nullptr)
162
+ {
163
+ cleanUp();
164
+ capturedPromise.Reject("Couldn't open the PDF file.");
165
+ co_return;
166
+ } else
167
+ {
168
+ auto printMan = PrintManager::GetForCurrentView();
169
+ printDoc = PrintDocument();
170
+ printDocSource = printDoc.DocumentSource();
171
+
172
+ printDoc.Paginate(
173
+ [=](auto const& sender, PaginateEventArgs const& args)
174
+ {
175
+ {
176
+ // Clears the XAML pages.
177
+ std::lock_guard<std::mutex> guard(printSync);
178
+ pageCollection.clear();
179
+ printCanvas.Children().Clear();
180
+ }
181
+ auto printTaskOptions = args.PrintTaskOptions();
182
+ printPageDescr = printTaskOptions.GetPageDescription(0);
183
+ numberOfPages = pdfDocument.PageCount();
184
+ sender.as<PrintDocument>().SetPreviewPageCount(numberOfPages, PreviewPageCountType::Final);
185
+ }
186
+ );
187
+
188
+ printDoc.GetPreviewPage(
189
+ [=](auto const& sender, GetPreviewPageEventArgs const& args) -> void
190
+ {
191
+ auto printDocArg = sender.as<PrintDocument>();
192
+ const int pageNumber = args.PageNumber();
193
+ GeneratePageAsync(pdfDocument, pageNumber, printPageDescr).then([=](std::optional<xaml::UIElement> generatedPage)
194
+ {
195
+ reactContext.UIDispatcher().Post([=]()
196
+ {
197
+ {
198
+ std::lock_guard<std::mutex> guard(printSync);
199
+ printCanvas.Children().Append(*generatedPage);
200
+ printCanvas.InvalidateMeasure();
201
+ printCanvas.UpdateLayout();
202
+ }
203
+ printDocArg.SetPreviewPage(pageNumber, *generatedPage);
204
+ });
205
+ }
206
+ );
207
+ }
208
+ );
209
+
210
+ printDoc.AddPages(
211
+ [=](auto const& sender, AddPagesEventArgs const& args)
212
+ {
213
+ PrintDocument printDoc = sender.as<PrintDocument>();
214
+ std::vector<concurrency::task<void>> createPageTasks;
215
+
216
+ // Generate tasks for each page that needs to be sent to the printer.
217
+ for (int i = 0; i < numberOfPages; i++)
218
+ {
219
+ createPageTasks.push_back(GeneratePageAsync(pdfDocument, i + 1, printPageDescr).then([=](std::optional<xaml::UIElement> generatedPage)
220
+ {
221
+ pageCollection[i] = *generatedPage;
222
+ }
223
+ ));
224
+ }
225
+
226
+ // After all pages have been generated, submit them for printing.
227
+ concurrency::when_all(createPageTasks.begin(), createPageTasks.end()).then(
228
+ [=]()
229
+ {
230
+ reactContext.UIDispatcher().Post([=]()
231
+ {
232
+ std::for_each(pageCollection.begin(), pageCollection.end(),
233
+ [=](auto keyValue)
234
+ {
235
+
236
+ printDoc.AddPage(*(keyValue.second));
237
+ }
238
+ );
239
+ printDoc.AddPagesComplete();
240
+ }
241
+ );
242
+ }
243
+ );
244
+
245
+ });
246
+
247
+ printToken = printMan.PrintTaskRequested(
248
+ [=](PrintManager const& sender, PrintTaskRequestedEventArgs const& args)
249
+ {
250
+ auto printTask = args.Request().CreatePrintTask(winrt::to_hstring(capturedOptions.jobName),
251
+ [=](PrintTaskSourceRequestedArgs const& args)
252
+ {
253
+ args.SetSource(printDocSource);
254
+ }
255
+ );
256
+ if (options.isLandscape)
257
+ {
258
+ printTask.Options().Orientation(PrintOrientation::Landscape);
259
+ } else
260
+ {
261
+ printTask.Options().Orientation(PrintOrientation::Portrait);
262
+ }
263
+
264
+ printTask.Completed([=](PrintTask const& sender, PrintTaskCompletedEventArgs const& args)
265
+ {
266
+ cleanUp();
267
+ switch (args.Completion())
268
+ {
269
+ case PrintTaskCompletion::Failed:
270
+ capturedPromise.Reject("Failed to print.");
271
+ break;
272
+ case PrintTaskCompletion::Abandoned:
273
+ capturedPromise.Reject("Printing Abandoned");
274
+ break;
275
+ default:
276
+ capturedPromise.Resolve(options.jobName);
277
+ break;
278
+ }
279
+ }
280
+ );
281
+ }
282
+ );
283
+
284
+ co_await PrintManager::ShowPrintUIAsync();
285
+ }
286
+ }
287
+ }
288
+
289
+ void RNPrint::Print(JSValueObject&& options, ReactPromise<JSValue> promise) noexcept
290
+ {
291
+ bool ok_to_go_ahead = true;
292
+ {
293
+ // Only support one print request at a time.
294
+ std::lock_guard<std::mutex> guard(printSync);
295
+ if (servingPrintRequest)
296
+ {
297
+ ok_to_go_ahead = false;
298
+ } else
299
+ {
300
+ servingPrintRequest = true;
301
+ }
302
+ }
303
+ if (!ok_to_go_ahead)
304
+ {
305
+ promise.Reject("Another print request is already being served.");
306
+ return;
307
+ }
308
+
309
+ RNPrintOptions printOptions;
310
+
311
+ if (options.find("html") != options.end())
312
+ {
313
+ printOptions.html = options["html"].AsString();
314
+ }
315
+
316
+ std::optional<std::string> filePath = std::nullopt;
317
+ if (options.find("filePath") != options.end())
318
+ {
319
+ printOptions.filePath = options["filePath"].AsString();
320
+ }
321
+
322
+ printOptions.isLandscape = (options.find("isLandscape") != options.end() ? options["isLandscape"].AsBoolean() : false);
323
+ printOptions.jobName = (options.find("jobName") != options.end() ? options["jobName"].AsString() : defaultJobName);
324
+ reactContext.UIDispatcher().Post([=]()
325
+ {
326
+ xaml::FrameworkElement root{ nullptr };
327
+
328
+ auto window = xaml::Window::Current();
329
+
330
+ if (window != nullptr)
331
+ {
332
+ root = window.Content().try_as<xaml::FrameworkElement>();
333
+ } else
334
+ {
335
+ if (auto xamlRoot = React::XamlUIService::GetXamlRoot(reactContext.Properties().Handle()))
336
+ {
337
+ root = xamlRoot.Content().try_as<xaml::FrameworkElement>();
338
+ }
339
+ }
340
+
341
+ if (!root)
342
+ {
343
+ cleanUp();
344
+ promise.Reject("A valid XAML root was not found.");
345
+ return;
346
+ }
347
+
348
+ printCanvas = searchForPrintCanvas(root);
349
+
350
+ if (!printCanvas)
351
+ {
352
+ cleanUp();
353
+ promise.Reject("The XAML Canvas named \"RNPrintCanvas\" was not found.");
354
+ return;
355
+ }
356
+
357
+ auto asyncOp = PrintAsyncHelper(printOptions, promise);
358
+ asyncOp.Completed([=](auto action, auto status)
359
+ {
360
+ // Here we handle any unhandled exceptions thrown during the
361
+ // asynchronous call by rejecting the promise with the error code
362
+ if (status == winrt::Windows::Foundation::AsyncStatus::Error)
363
+ {
364
+ cleanUp();
365
+ std::stringstream errorCode;
366
+ errorCode << "0x" << std::hex << action.ErrorCode() << std::dec << std::endl;
367
+
368
+ auto error = winrt::Microsoft::ReactNative::ReactError();
369
+ error.Message = "HRESULT " + errorCode.str() + ": " + std::system_category().message(action.ErrorCode());
370
+ promise.Reject(error);
371
+ }
372
+ });
373
+ });
374
+ }
375
+ }
@@ -0,0 +1,3 @@
1
+ EXPORTS
2
+ DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
3
+ DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
@@ -0,0 +1,89 @@
1
+ #pragma once
2
+
3
+ #include "pch.h"
4
+ #include "NativeModules.h"
5
+
6
+ #include <functional>
7
+ #include <sstream>
8
+ #include <mutex>
9
+ #include <string_view>
10
+
11
+ #include "JSValue.h"
12
+
13
+ using namespace winrt::Microsoft::ReactNative;
14
+ using namespace winrt::Windows::Data::Pdf;
15
+ using namespace winrt::Windows::Foundation;
16
+ using namespace winrt::Windows::Storage;
17
+ using namespace winrt::Windows::Storage::Streams;
18
+ using namespace winrt::Windows::Web::Http;
19
+ using namespace winrt::Windows::Graphics::Printing;
20
+ using namespace winrt::Windows::Graphics::Printing::OptionDetails;
21
+ using namespace winrt::Windows::UI::Xaml::Printing;
22
+ using namespace winrt::Windows::UI::Xaml::Controls;
23
+ using namespace winrt::Windows::UI::Xaml::Media::Imaging;
24
+ using namespace winrt::Windows::UI::Xaml::Media;
25
+
26
+ namespace winrt::RNPrint
27
+ {
28
+ // Represent the options object passed to print()
29
+ struct RNPrintOptions
30
+ {
31
+ std::optional<std::string> html = std::nullopt;
32
+ std::optional<std::string> filePath = std::nullopt;
33
+ bool isLandscape = false;
34
+ std::string jobName;
35
+ };
36
+
37
+ REACT_MODULE(RNPrint);
38
+ struct RNPrint
39
+ {
40
+ inline static constexpr std::string_view Name = "RNPrint";
41
+
42
+ // Default name for the print job.
43
+ inline static constexpr std::string_view defaultJobName = "Document";
44
+
45
+ // Name for the XAML Canvas to add preview folders to.
46
+ inline static constexpr std::string_view RNPrintCanvasName = "RNPrintCanvas";
47
+
48
+ // These are needed between print PDF callbacks, so we place them in the struct.
49
+ ReactContext reactContext = nullptr;
50
+ Canvas printCanvas = nullptr; // holds reference to RNPrintCanvas in visual tree
51
+ PrintPageDescription printPageDescr;
52
+ PrintDocument printDoc = nullptr;
53
+ IPrintDocumentSource printDocSource;
54
+ int numberOfPages = 0;
55
+ std::map<int, std::optional<xaml::UIElement>> pageCollection; // references to the xaml pages
56
+ std::mutex printSync; // prevent race conditions when manipulating pageCollection or checking a print request is being processed
57
+ bool servingPrintRequest = false;
58
+
59
+ // Event token for print registratio.
60
+ winrt::event_token printToken;
61
+
62
+ REACT_INIT(RNPrint_Init);
63
+ void RNPrint_Init(ReactContext const& context) noexcept
64
+ {
65
+ reactContext = context;
66
+ }
67
+
68
+ // Searches for the RNPrintCanvas element in the visual tree.
69
+ Canvas searchForPrintCanvas(xaml::DependencyObject startNode);
70
+
71
+ // cleans up local variables and XAML elements.
72
+ void cleanUp();
73
+
74
+ // Generates a XAML page asynchronously.
75
+ concurrency::task<std::optional<xaml::UIElement>> GeneratePageAsync(
76
+ PdfDocument pdfDocument,
77
+ int pageNumber,
78
+ PrintPageDescription pageDescription);
79
+
80
+ // Asynchronously loads the PDF document, registers print callbacks and fires the print UI.
81
+ IAsyncAction PrintAsyncHelper(
82
+ RNPrintOptions options,
83
+ ReactPromise<JSValue> promise) noexcept;
84
+
85
+ REACT_METHOD(Print, L"print");
86
+ void Print(JSValueObject&& options, ReactPromise<JSValue> promise) noexcept;
87
+
88
+ };
89
+ }