David PHAM-VAN

Add Flutter Printing plugin

... ... @@ -12,3 +12,14 @@ doc/api/
*.pdf
*.ttf
.DS_Store
.dart_tool/
.packages
.pub/
pubspec.lock
build/
printing/android/local.properties
.idea
*.iml
... ...
# 1.0.0
* Initial release.
... ...
TODO: Add your license here.
... ...
# Printing
Plugin that allows Flutter apps to generate and print documents to android or ios compatible printers
## Getting Started
For help getting started with Flutter, view our online
[documentation](https://flutter.io/).
For help on editing plugin code, view the [documentation](https://flutter.io/developing-packages/#edit-plugin-package).
... ...
group 'net.nfet.flutter.printing'
version '1.0-SNAPSHOT'
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 27
defaultConfig {
minSdkVersion 19
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
}
dependencies {
api 'com.android.support:appcompat-v7:27.1.1'
}
... ...
org.gradle.jvmargs=-Xmx1536M
... ...
rootProject.name = 'printing'
... ...
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.nfet.flutter.printing">
<application>
<provider
android:name=".PrintFileProvider"
android:authorities="${applicationId}.flutter.printing"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/flutter_printing_file_paths"/>
</provider>
</application>
</manifest>
... ...
/*
* Copyright (C) 2018, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package net.nfet.flutter.printing;
import android.support.v4.content.FileProvider;
public class PrintFileProvider extends FileProvider {
}
... ...
/*
* Copyright (C) 2018, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package net.nfet.flutter.printing;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentInfo;
import android.print.PrintManager;
import android.print.pdf.PrintedPdfDocument;
import android.support.v4.content.FileProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/**
* PrintingPlugin
*/
public class PrintingPlugin implements MethodCallHandler {
private static PrintManager printManager;
private final Activity activity;
private PrintingPlugin(Activity activity) {
this.activity = activity;
}
/**
* Plugin registration.
*/
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "printing");
channel.setMethodCallHandler(new PrintingPlugin(registrar.activity()));
printManager = (PrintManager) registrar.activity().getSystemService(Context.PRINT_SERVICE);
}
@Override
public void onMethodCall(MethodCall call, Result result) {
switch (call.method) {
case "printPdf":
printPdf((byte[]) call.argument("doc"));
result.success(0);
break;
case "sharePdf":
sharePdf((byte[]) call.argument("doc"));
result.success(0);
break;
default:
result.notImplemented();
break;
}
}
private void printPdf(final byte[] badgeData) {
PrintDocumentAdapter pda = new PrintDocumentAdapter() {
PrintedPdfDocument mPdfDocument;
@Override
public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor parcelFileDescriptor, CancellationSignal cancellationSignal, WriteResultCallback writeResultCallback) {
OutputStream output = null;
try {
output = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
output.write(badgeData, 0, badgeData.length);
writeResultCallback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (output != null) {
output.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) {
// Create a new PdfDocument with the requested page attributes
mPdfDocument = new PrintedPdfDocument(activity, newAttributes);
// Respond to cancellation request
if (cancellationSignal.isCanceled()) {
callback.onLayoutCancelled();
return;
}
// Return print information to print framework
PrintDocumentInfo info = new PrintDocumentInfo
.Builder("badge.pdf")
.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
.setPageCount(1).build();
// Content layout reflow is complete
callback.onLayoutFinished(info, true);
}
@Override
public void onFinish() {
//noinspection ResultOfMethodCallIgnored
}
};
String jobName = "Badge";
printManager.print(jobName, pda, null);
}
private void sharePdf(byte[] data) {
try {
final File externalFilesDirectory = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File shareFile = File.createTempFile("badge", ".pdf", externalFilesDirectory);
FileOutputStream stream = new FileOutputStream(shareFile);
stream.write(data);
stream.close();
Uri apkURI = FileProvider.getUriForFile(activity,
activity.getApplicationContext().getPackageName() + ".flutter.printing", shareFile);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("application/pdf");
shareIntent.putExtra(Intent.EXTRA_STREAM, apkURI);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Intent chooserIntent = Intent.createChooser(shareIntent, null);
activity.startActivity(chooserIntent);
shareFile.deleteOnExit();
} catch (IOException e) {
e.printStackTrace();
}
}
}
... ...
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
\ No newline at end of file
... ...
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:printing/printing.dart';
void main() => runApp(new MaterialApp(home: new MyApp()));
class MyApp extends StatelessWidget {
final shareWidget = new GlobalKey();
PDFDocument _generateDocument() {
final pdf = new PDFDocument();
final page = new PDFPage(pdf, pageFormat: PDFPageFormat.A4);
final g = page.getGraphics();
final font = new PDFFont(pdf);
final top = page.pageFormat.height;
g.setColor(new PDFColor(0.0, 1.0, 1.0));
g.drawRect(50.0 * PDFPageFormat.MM, top - 80.0 * PDFPageFormat.MM,
100.0 * PDFPageFormat.MM, 50.0 * PDFPageFormat.MM);
g.fillPath();
g.setColor(new PDFColor(0.3, 0.3, 0.3));
g.drawString(
font, 12.0, "Hello World!", 10.0 * PDFPageFormat.MM, top - 10.0 * PDFPageFormat.MM);
return pdf;
}
void _printPdf() {
print("Print ...");
final pdf = _generateDocument();
Printing.printPdf(document: pdf);
}
void _sharePdf() {
print("Share ...");
final pdf = _generateDocument();
// Calculate the widget center for iPad sharing popup position
final RenderBox referenceBox = shareWidget.currentContext.findRenderObject();
final topLeft = referenceBox.localToGlobal(referenceBox.paintBounds.topLeft);
final bottomRight = referenceBox.localToGlobal(referenceBox.paintBounds.bottomRight);
final bounds = new Rect.fromPoints(topLeft, bottomRight);
Printing.sharePdf(document: pdf, bounds: bounds);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text('Printing example'),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new RaisedButton(child: new Text('Print Document'), onPressed: _printPdf),
new RaisedButton(
key: shareWidget, child: new Text('Share Document'), onPressed: _sharePdf),
],
),
),
);
}
}
... ...
/*
* Copyright (C) 2018, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#import <Flutter/Flutter.h>
@interface PrintingPlugin : NSObject<FlutterPlugin, UIPrintInteractionControllerDelegate>
@end
... ...
/*
* Copyright (C) 2018, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#import "PrintingPlugin.h"
@implementation PrintingPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"printing"
binaryMessenger:[registrar messenger]];
PrintingPlugin* instance = [[PrintingPlugin alloc] init];
[registrar addMethodCallDelegate:instance channel:channel];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"printPdf" isEqualToString:call.method]) {
[self printPdf:[call.arguments objectForKey:@"doc"]];
result(@1);
} else if ([@"sharePdf" isEqualToString:call.method]) {
[self
sharePdf:[call.arguments objectForKey:@"doc"]
withSourceRect:CGRectMake(
[[call.arguments objectForKey:@"x"] floatValue],
[[call.arguments objectForKey:@"y"] floatValue],
[[call.arguments objectForKey:@"w"] floatValue],
[[call.arguments objectForKey:@"h"] floatValue])];
result(@1);
} else {
result(FlutterMethodNotImplemented);
}
}
- (void)printPdf:(nonnull FlutterStandardTypedData *)data {
BOOL printing = [UIPrintInteractionController isPrintingAvailable];
if (!printing) {
NSLog(@"printing not available");
return;
}
BOOL dataOK = [UIPrintInteractionController canPrintData:[data data]];
if (!dataOK) NSLog(@"data not ok to be printed");
UIPrintInteractionController *controller = [UIPrintInteractionController sharedPrintController];
[controller setDelegate:self];
[controller setPrintingItem:[data data]];
UIPrintInteractionCompletionHandler completionHandler = ^(UIPrintInteractionController *printController, BOOL completed, NSError *error) {
if(!completed && error){
NSLog(@"FAILED! due to error in domain %@ with error code %u", error.domain, (unsigned int)error.code);
}
};
[controller presentAnimated:YES completionHandler:completionHandler];
}
- (void)sharePdf:(nonnull FlutterStandardTypedData *)data withSourceRect:(CGRect)rect {
NSURL *tmpDirURL = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
CFUUIDRef uuid = CFUUIDCreate(NULL);
assert(uuid != NULL);
CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid);
assert(uuidStr != NULL);
NSURL *fileURL = [[tmpDirURL URLByAppendingPathComponent:[NSString stringWithFormat:@"pdf-%@", uuidStr]] URLByAppendingPathExtension:@"pdf"];
assert(fileURL != NULL);
CFRelease(uuidStr);
CFRelease(uuid);
NSString *path = [fileURL path];
NSError *error;
if (![[data data] writeToFile:path options:NSDataWritingAtomic error:&error]) {
NSLog(@"sharePdf error: %@", [error localizedDescription]);
return;
}
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[fileURL] applicationActivities:nil];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIViewController *controller = [UIApplication sharedApplication].keyWindow.rootViewController;
activityViewController.popoverPresentationController.sourceView = controller.view;
activityViewController.popoverPresentationController.sourceRect = rect;
}
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:activityViewController animated:YES completion:nil];
}
@end
... ...
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'printing'
s.version = '0.0.1'
s.summary = 'Plugin that allows Flutter apps to generate and print documents to android or ios compatible printers'
s.description = <<-DESC
Plugin that allows Flutter apps to generate and print documents to android or ios compatible printers
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
s.ios.deployment_target = '8.0'
end
... ...
/*
* Copyright (C) 2018, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import 'dart:async';
import 'dart:ui';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:pdf/pdf.dart';
class Printing {
static const MethodChannel _channel = const MethodChannel('printing');
static Future<Null> printPdf({PDFDocument document, List<int> bytes}) async {
assert(document != null || bytes != null);
assert(!(document == null && bytes == null));
if (document != null) bytes = document.save();
final Map<String, dynamic> params = <String, dynamic>{
'doc': bytes,
};
await _channel.invokeMethod('printPdf', params);
}
static Future<Null> sharePdf({PDFDocument document, List<int> bytes, Rect bounds}) async {
assert(document != null || bytes != null);
assert(!(document == null && bytes == null));
if (document != null) bytes = document.save();
if (bounds == null) {
bounds = new Rect.fromCircle(center: Offset.zero, radius: 10.0);
}
final Map<String, dynamic> params = <String, dynamic>{
'doc': bytes,
'x': bounds.left,
'y': bounds.top,
'w': bounds.width,
'h': bounds.height,
};
await _channel.invokeMethod('sharePdf', params);
}
}
... ...
name: printing
author: David PHAM-VAN <dev.nfet.net@gmail.com>
description: Plugin that allows Flutter apps to generate and print documents to android or ios compatible printers
homepage: https://github.com/davbfr/dart_pdf/printing
version: 1.0.0
environment:
sdk: ">=1.19.0 <2.0.0"
dependencies:
flutter:
sdk: flutter
pdf: "^1.0.3"
flutter:
plugin:
androidPackage: net.nfet.flutter.printing
pluginClass: PrintingPlugin
... ...