Skip to content

Commit

Permalink
[native_pdf_renderer] Small improovements, added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeShkurko committed Oct 31, 2019
1 parent 2796907 commit 7ff9329
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 36 deletions.
7 changes: 7 additions & 0 deletions packages/native_pdf_renderer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 1.6.0

* Added more documentation for public properties and methods
* `crop` property in `render` method marked as deprecated, usage `cropRect` instead
* Added `isClosed` property for `PDFDocument` and `PDFPage`
* Added tests

## 1.5.0

* Added crop option for rendering (#11)
Expand Down
2 changes: 2 additions & 0 deletions packages/native_pdf_renderer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

`Flutter` Plugin to render PDF pages as images on both **Android 5.0+** and **iOS 11.0+** devices.

For IOS need set swift version to 5 ([instruction](https://stackoverflow.com/questions/46338588/xcode-9-swift-language-version-swift-version/46339401#46339401), [issue](https://github.com/rbcprolabs/flutter_plugins/issues/3))

## Getting Started
In your flutter project add the dependency:

Expand Down
1 change: 1 addition & 0 deletions packages/native_pdf_renderer/example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
name: native_pdf_renderer_example
description: Demonstrates how to use the native_pdf_renderer plugin.
version: 1.0.0
publish_to: 'none'

environment:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Flutter Plugin to render a PDF file.
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
s.swift_version = '5.0'

s.ios.deployment_target = '8.0'
end
Expand Down
51 changes: 39 additions & 12 deletions packages/native_pdf_renderer/lib/src/document.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'page.dart';

/// PDF page image renderer
class PDFDocument {
const PDFDocument._({
PDFDocument._({
@required this.sourceName,
@required this.id,
@required this.pagesCount,
Expand All @@ -26,7 +27,19 @@ class PDFDocument {
/// Starts from 1.
final int pagesCount;

Future<void> close() => _channel.invokeMethod('close.document', id);
/// Is the document closed
bool isClosed = false;

/// After you finish working with the document,
/// you should close it to avoid memory leak.
Future<void> close() {
if (isClosed) {
throw PdfDocumentAlreadyClosedException();
} else {
isClosed = true;
}
return _channel.invokeMethod('close.document', id);
}

static PDFDocument _open(Map<dynamic, dynamic> obj, String sourceName) =>
PDFDocument._(
Expand All @@ -35,13 +48,16 @@ class PDFDocument {
pagesCount: obj['pagesCount'] as int,
);

/// Open PDF document from filesystem path
static Future<PDFDocument> openFile(String filePath) async => _open(
await _channel.invokeMethod<Map<dynamic, dynamic>>(
'open.document.file',
filePath,
),
'file:$filePath');
await _channel.invokeMethod<Map<dynamic, dynamic>>(
'open.document.file',
filePath,
),
'file:$filePath',
);

/// Open PDF document from application assets
static Future<PDFDocument> openAsset(String name) async => _open(
await _channel.invokeMethod<Map<dynamic, dynamic>>(
'open.document.asset',
Expand All @@ -50,6 +66,7 @@ class PDFDocument {
'asset:$name',
);

/// Open PDF file from memory (Uint8List)
static Future<PDFDocument> openData(Uint8List data) async => _open(
await _channel.invokeMethod<Map<dynamic, dynamic>>(
'open.document.data',
Expand All @@ -60,14 +77,19 @@ class PDFDocument {

/// Get page object. The first page is 1.
Future<PDFPage> getPage(int pageNumber) async {
if (isClosed) {
throw PdfDocumentAlreadyClosedException();
}
if (pageNumber < 1 || pageNumber > pagesCount) {
return null;
}
final obj =
await _channel.invokeMethod<Map<dynamic, dynamic>>('open.page', {
'documentId': id,
'page': pageNumber,
});
final obj = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'open.page',
{
'documentId': id,
'page': pageNumber,
},
);
return PDFPage(
document: this,
id: obj['id'] as String,
Expand All @@ -87,3 +109,8 @@ class PDFDocument {
String toString() =>
'$runtimeType{document: $sourceName, id: $id, pagesCount: $pagesCount}';
}

class PdfDocumentAlreadyClosedException implements Exception {
@override
String toString() => '$runtimeType: Document already closed';
}
84 changes: 69 additions & 15 deletions packages/native_pdf_renderer/lib/src/page.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'dart:async';
import 'package:extension/enum.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'document.dart';
import 'page_image.dart';

/// Image compression format
class PDFPageFormat extends Enum<int> {
const PDFPageFormat(int val) : super(val);

Expand All @@ -15,20 +17,44 @@ class PDFPageFormat extends Enum<int> {
static const PDFPageFormat WEBP = PDFPageFormat(2);
}

/// Render options for a specific part of the page
@Deprecated('Usage [cropRect] property instead prop')
class PDFCropDef {
const PDFCropDef({
PDFCropDef({
@required this.x,
@required this.y,
@required this.width,
@required this.height,
});

final int x, y;
final int width, height;
/// Indent left to render part of the page
final int x;

/// Indent top to render part of the page
final int y;

/// Width required for image rendering
final int width;

/// Height required for image rendering
final int height;

/// Is the page closed
bool isClosed = false;

/// Fallback for generating rect
Rect get rect => Rect.fromLTWH(
x.toDouble(),
y.toDouble(),
width.toDouble(),
height.toDouble(),
);
}

/// An integral part of a document is its page,
/// which contains a method [render] for rendering into an image
class PDFPage {
const PDFPage({
PDFPage({
@required this.document,
@required this.id,
@required this.pageNumber,
Expand All @@ -53,31 +79,54 @@ class PDFPage {
/// Page source height in pixels
final int height;

/// Is the page closed
bool isClosed = false;

/// Render a full image of specified PDF file.
///
/// [width], [height] specify resolution to render in pixels.
/// As default PNG uses transparent background. For change it you can set
/// [backgroundColor] property like a hex string ('#000000')
/// [format] - image type, all types can be seen here [PDFPageFormat]
/// [cropRect] - render only the necessary part of the image
Future<PDFPageImage> render({
@required int width,
@required int height,
PDFPageFormat format = PDFPageFormat.PNG,
String backgroundColor,
PDFCropDef crop,
}) =>
PDFPageImage.render(
pageId: id,
width: width,
height: height,
format: format,
backgroundColor: backgroundColor,
crop: crop,
);
// ignore: deprecated_member_use_from_same_package
@Deprecated('Use cropRect instead') PDFCropDef crop,
Rect cropRect,
}) {
if (document.isClosed) {
throw PdfDocumentAlreadyClosedException();
} else if (isClosed) {
throw PdfPageAlreadyClosedException();
}

final rect = cropRect ?? crop?.rect;

return PDFPageImage.render(
pageId: id,
width: width,
height: height,
format: format,
backgroundColor: backgroundColor,
crop: rect,
);
}

/// Before open another page it is necessary to close the previous.
///
/// The android platform does not allow parallel rendering.
Future<void> close() => _channel.invokeMethod('close.page', id);
Future<void> close() {
if (isClosed) {
throw PdfPageAlreadyClosedException();
} else {
isClosed = true;
}
return _channel.invokeMethod('close.page', id);
}

@override
bool operator ==(Object other) =>
Expand All @@ -95,3 +144,8 @@ class PDFPage {
'width: $width, '
'height: $height}';
}

class PdfPageAlreadyClosedException implements Exception {
@override
String toString() => '$runtimeType: Page already closed';
}
35 changes: 28 additions & 7 deletions packages/native_pdf_renderer/lib/src/page_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import 'dart:async';
import 'dart:io';
import 'dart:typed_data' show Uint8List;

import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'page.dart';

/// Object containing a rendered image
/// in a pre-selected format in [render] method
/// of [PDFPage]
class PDFPageImage {
const PDFPageImage._({
@required this.id,
Expand Down Expand Up @@ -37,17 +41,25 @@ class PDFPageImage {
/// Target compression format
final PDFPageFormat format;

/// Render a full image of specified PDF file.
///
/// [width], [height] specify resolution to render in pixels.
/// As default PNG uses transparent background. For change it you can set
/// [backgroundColor] property like a hex string ('#000000')
/// [format] - image type, all types can be seen here [PDFPageFormat]
/// [crop] - render only the necessary part of the image
static Future<PDFPageImage> render({
@required String pageId,
@required int width,
@required int height,
@required PDFPageFormat format,
@required String backgroundColor,
PDFCropDef crop,
@required Rect crop,
}) async {
if (format == PDFPageFormat.WEBP && Platform.isIOS) {
throw Exception(
'PDF Renderer on IOS platform does not support WEBP format');
throw PdfNotSupportException(
'PDF Renderer on IOS platform does not support WEBP format',
);
}

final obj = await _channel.invokeMethod('render', {
Expand All @@ -57,10 +69,10 @@ class PDFPageImage {
'format': format.value,
'backgroundColor': backgroundColor,
'crop': crop != null,
'crop_x': crop?.x,
'crop_y': crop?.y,
'crop_height': crop?.height,
'crop_width': crop?.width,
'crop_x': crop?.left?.toInt(),
'crop_y': crop?.top?.toInt(),
'crop_height': crop?.height?.toInt(),
'crop_width': crop?.width?.toInt(),
});

if (!(obj is Map<dynamic, dynamic>)) {
Expand Down Expand Up @@ -95,3 +107,12 @@ class PDFPageImage {
'height: $height, '
'bytesLength: ${bytes.lengthInBytes}}';
}

class PdfNotSupportException implements Exception {
PdfNotSupportException(this.message);

final String message;

@override
String toString() => '$runtimeType: $message';
}
Loading

0 comments on commit 7ff9329

Please sign in to comment.