David PHAM-VAN

Fix raster crash on all OS

1 # Changelog 1 # Changelog
2 2
  3 +## 5.2.2
  4 +
  5 +- Fix raster crash on all OS.
  6 +
3 ## 5.2.1 7 ## 5.2.1
4 8
5 - Fix Linux build 9 - Fix Linux build
@@ -194,9 +194,12 @@ public class PrintingHandler implements MethodChannel.MethodCallHandler { @@ -194,9 +194,12 @@ public class PrintingHandler implements MethodChannel.MethodCallHandler {
194 194
195 /// The page has been converted to an image 195 /// The page has been converted to an image
196 @RequiresApi(api = Build.VERSION_CODES.KITKAT) 196 @RequiresApi(api = Build.VERSION_CODES.KITKAT)
197 - void onPageRasterEnd(PrintingJob printJob) { 197 + void onPageRasterEnd(PrintingJob printJob, String error) {
198 HashMap<String, Object> args = new HashMap<>(); 198 HashMap<String, Object> args = new HashMap<>();
199 args.put("job", printJob.index); 199 args.put("job", printJob.index);
  200 + if (error != null) {
  201 + args.put("error", error);
  202 + }
200 203
201 channel.invokeMethod("onPageRasterEnd", args); 204 channel.invokeMethod("onPageRasterEnd", args);
202 } 205 }
@@ -434,8 +434,8 @@ public class PrintingJob extends PrintDocumentAdapter { @@ -434,8 +434,8 @@ public class PrintingJob extends PrintDocumentAdapter {
434 434
435 void rasterPdf(final byte[] data, final ArrayList<Integer> pages, final Double scale) { 435 void rasterPdf(final byte[] data, final ArrayList<Integer> pages, final Double scale) {
436 if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) { 436 if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
437 - Log.e("PDF", "PDF Raster available since Android 5.0 Lollipop (API 21)");  
438 - printing.onPageRasterEnd(this); 437 + printing.onPageRasterEnd(
  438 + this, "PDF Raster available since Android 5.0 Lollipop (API 21)");
439 return; 439 return;
440 } 440 }
441 441
@@ -443,6 +443,7 @@ public class PrintingJob extends PrintDocumentAdapter { @@ -443,6 +443,7 @@ public class PrintingJob extends PrintDocumentAdapter {
443 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 443 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
444 @Override 444 @Override
445 public void run() { 445 public void run() {
  446 + String error = null;
446 try { 447 try {
447 File file = File.createTempFile("printing", null, null); 448 File file = File.createTempFile("printing", null, null);
448 FileOutputStream oStream = new FileOutputStream(file); 449 FileOutputStream oStream = new FileOutputStream(file);
@@ -493,12 +494,14 @@ public class PrintingJob extends PrintDocumentAdapter { @@ -493,12 +494,14 @@ public class PrintingJob extends PrintDocumentAdapter {
493 494
494 } catch (IOException e) { 495 } catch (IOException e) {
495 e.printStackTrace(); 496 e.printStackTrace();
  497 + error = e.getMessage();
496 } 498 }
497 499
  500 + final String finalError = error;
498 new Handler(Looper.getMainLooper()).post(new Runnable() { 501 new Handler(Looper.getMainLooper()).post(new Runnable() {
499 @Override 502 @Override
500 public void run() { 503 public void run() {
501 - printing.onPageRasterEnd(PrintingJob.this); 504 + printing.onPageRasterEnd(PrintingJob.this, finalError);
502 } 505 }
503 }); 506 });
504 } 507 }
@@ -296,13 +296,17 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate @@ -296,13 +296,17 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
296 296
297 public func rasterPdf(data: Data, pages: [Int]?, scale: CGFloat) { 297 public func rasterPdf(data: Data, pages: [Int]?, scale: CGFloat) {
298 let provider = CGDataProvider(data: data as CFData)! 298 let provider = CGDataProvider(data: data as CFData)!
299 - let document = CGPDFDocument(provider)! 299 + let document = CGPDFDocument(provider)
  300 + if document == nil {
  301 + printing.onPageRasterEnd(printJob: self, error: "Cannot raster a malformed PDF file")
  302 + return
  303 + }
300 304
301 DispatchQueue.global().async { 305 DispatchQueue.global().async {
302 - let pageCount = document.numberOfPages 306 + let pageCount = document!.numberOfPages
303 307
304 for pageNum in pages ?? Array(0 ... pageCount - 1) { 308 for pageNum in pages ?? Array(0 ... pageCount - 1) {
305 - guard let page = document.page(at: pageNum + 1) else { continue } 309 + guard let page = document!.page(at: pageNum + 1) else { continue }
306 let angle = CGFloat(page.rotationAngle) * CGFloat.pi / -180 310 let angle = CGFloat(page.rotationAngle) * CGFloat.pi / -180
307 let rect = page.getBoxRect(.mediaBox) 311 let rect = page.getBoxRect(.mediaBox)
308 let width = Int(abs((cos(angle) * rect.width + sin(angle) * rect.height) * scale)) 312 let width = Int(abs((cos(angle) * rect.width + sin(angle) * rect.height) * scale))
@@ -321,6 +325,7 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate @@ -321,6 +325,7 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
321 space: rgb, 325 space: rgb,
322 bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue 326 bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
323 ) 327 )
  328 +
324 if context != nil { 329 if context != nil {
325 context!.translateBy(x: CGFloat(width) / 2, y: CGFloat(height) / 2) 330 context!.translateBy(x: CGFloat(width) / 2, y: CGFloat(height) / 2)
326 context!.scaleBy(x: scale, y: scale) 331 context!.scaleBy(x: scale, y: scale)
@@ -336,7 +341,7 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate @@ -336,7 +341,7 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
336 } 341 }
337 342
338 DispatchQueue.main.sync { 343 DispatchQueue.main.sync {
339 - self.printing.onPageRasterEnd(printJob: self) 344 + self.printing.onPageRasterEnd(printJob: self, error: nil)
340 } 345 }
341 } 346 }
342 } 347 }
@@ -349,6 +354,7 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate @@ -349,6 +354,7 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
349 "canConvertHtml": true, 354 "canConvertHtml": true,
350 "canShare": true, 355 "canShare": true,
351 "canRaster": true, 356 "canRaster": true,
  357 + "canListPrinters": false,
352 ] 358 ]
353 return data 359 return data
354 } 360 }
@@ -193,9 +193,10 @@ public class PrintingPlugin: NSObject, FlutterPlugin { @@ -193,9 +193,10 @@ public class PrintingPlugin: NSObject, FlutterPlugin {
193 channel.invokeMethod("onPageRasterized", arguments: data) 193 channel.invokeMethod("onPageRasterized", arguments: data)
194 } 194 }
195 195
196 - public func onPageRasterEnd(printJob: PrintJob) { 196 + public func onPageRasterEnd(printJob: PrintJob, error: String?) {
197 let data: NSDictionary = [ 197 let data: NSDictionary = [
198 "job": printJob.index, 198 "job": printJob.index,
  199 + "error": error as Any,
199 ] 200 ]
200 channel.invokeMethod("onPageRasterEnd", arguments: data) 201 channel.invokeMethod("onPageRasterEnd", arguments: data)
201 } 202 }
@@ -130,6 +130,10 @@ class MethodChannelPrinting extends PrintingPlatform { @@ -130,6 +130,10 @@ class MethodChannelPrinting extends PrintingPlatform {
130 case 'onPageRasterEnd': 130 case 'onPageRasterEnd':
131 final job = _printJobs.getJob(call.arguments['job']); 131 final job = _printJobs.getJob(call.arguments['job']);
132 if (job != null) { 132 if (job != null) {
  133 + final dynamic error = call.arguments['error'];
  134 + if (error != null) {
  135 + job.onPageRasterized!.addError(error);
  136 + }
133 await job.onPageRasterized!.close(); 137 await job.onPageRasterized!.close();
134 _printJobs.remove(job.index); 138 _printJobs.remove(job.index);
135 } 139 }
@@ -225,6 +225,7 @@ class _PdfPreviewState extends State<PdfPreview> { @@ -225,6 +225,7 @@ class _PdfPreviewState extends State<PdfPreview> {
225 }); 225 });
226 } 226 }
227 227
  228 + try {
228 var pageNum = 0; 229 var pageNum = 0;
229 await for (final PdfRaster page in Printing.raster( 230 await for (final PdfRaster page in Printing.raster(
230 _doc, 231 _doc,
@@ -252,9 +253,28 @@ class _PdfPreviewState extends State<PdfPreview> { @@ -252,9 +253,28 @@ class _PdfPreviewState extends State<PdfPreview> {
252 }); 253 });
253 254
254 pageNum++; 255 pageNum++;
  256 + pages.removeRange(pageNum, pages.length);
  257 + }
  258 + } catch (exception, stack) {
  259 + InformationCollector? collector;
  260 +
  261 + assert(() {
  262 + collector = () sync* {
  263 + yield StringProperty('PageFormat', computedPageFormat.toString());
  264 + };
  265 + return true;
  266 + }());
  267 +
  268 + FlutterError.reportError(FlutterErrorDetails(
  269 + exception: exception,
  270 + stack: stack,
  271 + library: 'printing',
  272 + context: ErrorDescription('while generating a PDF'),
  273 + informationCollector: collector,
  274 + ));
  275 + error = exception;
255 } 276 }
256 277
257 - pages.removeRange(pageNum, pages.length);  
258 _rastering = false; 278 _rastering = false;
259 } 279 }
260 280
@@ -249,7 +249,7 @@ void print_job::raster_pdf(const uint8_t data[], @@ -249,7 +249,7 @@ void print_job::raster_pdf(const uint8_t data[],
249 auto doc = FPDF_LoadMemDocument64(data, size, nullptr); 249 auto doc = FPDF_LoadMemDocument64(data, size, nullptr);
250 if (!doc) { 250 if (!doc) {
251 FPDF_DestroyLibrary(); 251 FPDF_DestroyLibrary();
252 - on_page_raster_end(this); 252 + on_page_raster_end(this, "Cannot raster a malformed PDF file");
253 return; 253 return;
254 } 254 }
255 255
@@ -307,7 +307,7 @@ void print_job::raster_pdf(const uint8_t data[], @@ -307,7 +307,7 @@ void print_job::raster_pdf(const uint8_t data[],
307 307
308 FPDF_DestroyLibrary(); 308 FPDF_DestroyLibrary();
309 309
310 - on_page_raster_end(this); 310 + on_page_raster_end(this, nullptr);
311 } 311 }
312 312
313 FlValue* print_job::printing_info() { 313 FlValue* print_job::printing_info() {
@@ -70,7 +70,7 @@ void on_page_rasterized(print_job* job, @@ -70,7 +70,7 @@ void on_page_rasterized(print_job* job,
70 int width, 70 int width,
71 int height); 71 int height);
72 72
73 -void on_page_raster_end(print_job* job); 73 +void on_page_raster_end(print_job* job, const char* error);
74 74
75 void on_layout(print_job* job, 75 void on_layout(print_job* job,
76 double pageWidth, 76 double pageWidth,
@@ -161,9 +161,12 @@ void on_page_rasterized(print_job* job, @@ -161,9 +161,12 @@ void on_page_rasterized(print_job* job,
161 nullptr, nullptr); 161 nullptr, nullptr);
162 } 162 }
163 163
164 -void on_page_raster_end(print_job* job) { 164 +void on_page_raster_end(print_job* job, const char* error) {
165 g_autoptr(FlValue) map = fl_value_new_map(); 165 g_autoptr(FlValue) map = fl_value_new_map();
166 fl_value_set_string(map, "job", fl_value_new_int(job->get_id())); 166 fl_value_set_string(map, "job", fl_value_new_int(job->get_id()));
  167 + if (error != nullptr) {
  168 + fl_value_set_string(map, "error", fl_value_new_string(error));
  169 + }
167 170
168 fl_method_channel_invoke_method(channel, "onPageRasterEnd", map, nullptr, 171 fl_method_channel_invoke_method(channel, "onPageRasterEnd", map, nullptr,
169 nullptr, nullptr); 172 nullptr, nullptr);
@@ -242,13 +242,17 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { @@ -242,13 +242,17 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate {
242 242
243 public func rasterPdf(data: Data, pages: [Int]?, scale: CGFloat) { 243 public func rasterPdf(data: Data, pages: [Int]?, scale: CGFloat) {
244 let provider = CGDataProvider(data: data as CFData)! 244 let provider = CGDataProvider(data: data as CFData)!
245 - let document = CGPDFDocument(provider)! 245 + let document = CGPDFDocument(provider)
  246 + if document == nil {
  247 + printing.onPageRasterEnd(printJob: self, error: "Cannot raster a malformed PDF file")
  248 + return
  249 + }
246 250
247 DispatchQueue.global().async { 251 DispatchQueue.global().async {
248 - let pageCount = document.numberOfPages 252 + let pageCount = document!.numberOfPages
249 253
250 for pageNum in pages ?? Array(0 ... pageCount - 1) { 254 for pageNum in pages ?? Array(0 ... pageCount - 1) {
251 - guard let page = document.page(at: pageNum + 1) else { continue } 255 + guard let page = document!.page(at: pageNum + 1) else { continue }
252 let angle = CGFloat(page.rotationAngle) * CGFloat.pi / -180 256 let angle = CGFloat(page.rotationAngle) * CGFloat.pi / -180
253 let rect = page.getBoxRect(.mediaBox) 257 let rect = page.getBoxRect(.mediaBox)
254 let width = Int(abs((cos(angle) * rect.width + sin(angle) * rect.height) * scale)) 258 let width = Int(abs((cos(angle) * rect.width + sin(angle) * rect.height) * scale))
@@ -283,7 +287,7 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { @@ -283,7 +287,7 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate {
283 } 287 }
284 288
285 DispatchQueue.main.sync { 289 DispatchQueue.main.sync {
286 - self.printing.onPageRasterEnd(printJob: self) 290 + self.printing.onPageRasterEnd(printJob: self, error: nil)
287 } 291 }
288 } 292 }
289 } 293 }
@@ -291,7 +295,7 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { @@ -291,7 +295,7 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate {
291 public static func printingInfo() -> NSDictionary { 295 public static func printingInfo() -> NSDictionary {
292 let data: NSDictionary = [ 296 let data: NSDictionary = [
293 "directPrint": true, 297 "directPrint": true,
294 - "dynamicLayout": false, 298 + "dynamicLayout": true,
295 "canPrint": true, 299 "canPrint": true,
296 "canConvertHtml": true, 300 "canConvertHtml": true,
297 "canShare": true, 301 "canShare": true,
@@ -188,9 +188,10 @@ public class PrintingPlugin: NSObject, FlutterPlugin { @@ -188,9 +188,10 @@ public class PrintingPlugin: NSObject, FlutterPlugin {
188 channel.invokeMethod("onPageRasterized", arguments: data) 188 channel.invokeMethod("onPageRasterized", arguments: data)
189 } 189 }
190 190
191 - public func onPageRasterEnd(printJob: PrintJob) { 191 + public func onPageRasterEnd(printJob: PrintJob, error: String?) {
192 let data: NSDictionary = [ 192 let data: NSDictionary = [
193 "job": printJob.index, 193 "job": printJob.index,
  194 + "error": error as Any,
194 ] 195 ]
195 channel.invokeMethod("onPageRasterEnd", arguments: data) 196 channel.invokeMethod("onPageRasterEnd", arguments: data)
196 } 197 }
@@ -7,7 +7,7 @@ description: > @@ -7,7 +7,7 @@ description: >
7 homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing 7 homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing
8 repository: https://github.com/DavBfr/dart_pdf 8 repository: https://github.com/DavBfr/dart_pdf
9 issue_tracker: https://github.com/DavBfr/dart_pdf/issues 9 issue_tracker: https://github.com/DavBfr/dart_pdf/issues
10 -version: 5.2.1 10 +version: 5.2.2
11 11
12 environment: 12 environment:
13 sdk: ">=2.12.0-0 <3.0.0" 13 sdk: ">=2.12.0-0 <3.0.0"
@@ -289,7 +289,7 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data, @@ -289,7 +289,7 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data,
289 auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr); 289 auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr);
290 if (!doc) { 290 if (!doc) {
291 FPDF_DestroyLibrary(); 291 FPDF_DestroyLibrary();
292 - printing->onPageRasterEnd(this); 292 + printing->onPageRasterEnd(this, "Cannot raster a malformed PDF file");
293 return; 293 return;
294 } 294 }
295 295
@@ -347,7 +347,7 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data, @@ -347,7 +347,7 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data,
347 347
348 FPDF_DestroyLibrary(); 348 FPDF_DestroyLibrary();
349 349
350 - printing->onPageRasterEnd(this); 350 + printing->onPageRasterEnd(this, nullptr);
351 } 351 }
352 352
353 std::map<std::string, bool> PrintJob::printingInfo() { 353 std::map<std::string, bool> PrintJob::printingInfo() {
@@ -44,13 +44,18 @@ void Printing::onPageRasterized(std::vector<uint8_t> data, @@ -44,13 +44,18 @@ void Printing::onPageRasterized(std::vector<uint8_t> data,
44 }))); 44 })));
45 } 45 }
46 46
47 -void Printing::onPageRasterEnd(PrintJob* job) {  
48 - channel->InvokeMethod("onPageRasterEnd",  
49 - std::make_unique<flutter::EncodableValue>(  
50 - flutter::EncodableValue(flutter::EncodableMap{  
51 - {flutter::EncodableValue("job"),  
52 - flutter::EncodableValue(job->id())},  
53 - }))); 47 +void Printing::onPageRasterEnd(PrintJob* job, const char* error) {
  48 + auto map = flutter::EncodableMap{
  49 + {flutter::EncodableValue("job"), flutter::EncodableValue(job->id())},
  50 + };
  51 +
  52 + if (!error.empty()) {
  53 + map[flutter::EncodableValue("error")] = flutter::EncodableValue(error);
  54 + }
  55 +
  56 + channel->InvokeMethod(
  57 + "onPageRasterEnd",
  58 + std::make_unique<flutter::EncodableValue>(flutter::EncodableValue(map)));
54 } 59 }
55 60
56 class OnLayoutResult : public flutter::MethodResult<flutter::EncodableValue> { 61 class OnLayoutResult : public flutter::MethodResult<flutter::EncodableValue> {
@@ -40,7 +40,7 @@ class Printing { @@ -40,7 +40,7 @@ class Printing {
40 int height, 40 int height,
41 PrintJob* job); 41 PrintJob* job);
42 42
43 - void onPageRasterEnd(PrintJob* job); 43 + void onPageRasterEnd(PrintJob* job, const char* error);
44 44
45 void onLayout(PrintJob* job, 45 void onLayout(PrintJob* job,
46 double pageWidth, 46 double pageWidth,