David PHAM-VAN

Implement Image orientation

@@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
14 - Add Ascii85 test 14 - Add Ascii85 test
15 - Add more warnings on type1 fonts 15 - Add more warnings on type1 fonts
16 - Simplify PdfImage constructor 16 - Simplify PdfImage constructor
  17 +- Implement Image orientation
17 18
18 ## 1.3.23 19 ## 1.3.23
19 20
@@ -200,7 +200,13 @@ class PDFImage extends PdfImage { @@ -200,7 +200,13 @@ class PDFImage extends PdfImage {
200 @required int height, 200 @required int height,
201 bool alpha = true, 201 bool alpha = true,
202 }) : super._(pdfDocument, 202 }) : super._(pdfDocument,
203 - image: image, width: width, height: height, alpha: alpha); 203 + image: image,
  204 + width: width,
  205 + height: height,
  206 + alpha: alpha,
  207 + alphaChannel: false,
  208 + jpeg: false,
  209 + orientation: PdfImageOrientation.topLeft);
204 } 210 }
205 211
206 @deprecated 212 @deprecated
@@ -108,7 +108,33 @@ class PdfGraphics { @@ -108,7 +108,33 @@ class PdfGraphics {
108 108
109 // q w 0 0 h x y cm % the coordinate matrix 109 // q w 0 0 h x y cm % the coordinate matrix
110 buf.putString('q '); 110 buf.putString('q ');
111 - buf.putNumList(<double>[w, 0, 0, h, x, y]); 111 + switch (img.orientation) {
  112 + case PdfImageOrientation.topLeft:
  113 + buf.putNumList(<double>[w, 0, 0, h, x, y]);
  114 + break;
  115 + case PdfImageOrientation.topRight:
  116 + buf.putNumList(<double>[-w, 0, 0, h, w - x, y]);
  117 + break;
  118 + case PdfImageOrientation.bottomRight:
  119 + buf.putNumList(<double>[-w, 0, 0, -h, w - x, h - y]);
  120 + break;
  121 + case PdfImageOrientation.bottomLeft:
  122 + buf.putNumList(<double>[w, 0, 0, -h, x, h - y]);
  123 + break;
  124 + case PdfImageOrientation.leftTop:
  125 + buf.putNumList(<double>[0, -h, -w, 0, w - x, h - y]);
  126 + break;
  127 + case PdfImageOrientation.rightTop:
  128 + buf.putNumList(<double>[0, h, -w, 0, w - x, y]);
  129 + break;
  130 + case PdfImageOrientation.rightBottom:
  131 + buf.putNumList(<double>[0, h, w, 0, x, y]);
  132 + break;
  133 + case PdfImageOrientation.leftBottom:
  134 + buf.putNumList(<double>[0, -h, w, 0, x, h - y]);
  135 + break;
  136 + }
  137 +
112 buf.putString(' cm ${img.name} Do Q\n'); 138 buf.putString(' cm ${img.name} Do Q\n');
113 } 139 }
114 140
@@ -16,6 +16,19 @@ @@ -16,6 +16,19 @@
16 16
17 part of pdf; 17 part of pdf;
18 18
  19 +/// Represents the position of the first pixel in the data stream
  20 +/// This corresponds to the exif orientations
  21 +enum PdfImageOrientation {
  22 + topLeft,
  23 + topRight,
  24 + bottomRight,
  25 + bottomLeft,
  26 + leftTop,
  27 + rightTop,
  28 + rightBottom,
  29 + leftBottom,
  30 +}
  31 +
19 class PdfImage extends PdfXObject { 32 class PdfImage extends PdfXObject {
20 /// Creates a new [PdfImage] instance. 33 /// Creates a new [PdfImage] instance.
21 /// 34 ///
@@ -29,6 +42,7 @@ class PdfImage extends PdfXObject { @@ -29,6 +42,7 @@ class PdfImage extends PdfXObject {
29 @required int width, 42 @required int width,
30 @required int height, 43 @required int height,
31 bool alpha = true, 44 bool alpha = true,
  45 + PdfImageOrientation orientation = PdfImageOrientation.topLeft,
32 }) => 46 }) =>
33 PdfImage._( 47 PdfImage._(
34 pdfDocument, 48 pdfDocument,
@@ -36,18 +50,27 @@ class PdfImage extends PdfXObject { @@ -36,18 +50,27 @@ class PdfImage extends PdfXObject {
36 width: width, 50 width: width,
37 height: height, 51 height: height,
38 alpha: alpha, 52 alpha: alpha,
  53 + alphaChannel: false,
  54 + jpeg: false,
  55 + orientation: orientation,
39 ); 56 );
40 57
41 - PdfImage._(PdfDocument pdfDocument,  
42 - {@required this.image,  
43 - @required this.width,  
44 - @required this.height,  
45 - this.alpha = true,  
46 - this.alphaChannel = false,  
47 - this.jpeg = false})  
48 - : assert(alphaChannel == false || alpha == true), 58 + PdfImage._(
  59 + PdfDocument pdfDocument, {
  60 + @required this.image,
  61 + @required int width,
  62 + @required int height,
  63 + @required this.alpha,
  64 + @required this.alphaChannel,
  65 + @required this.jpeg,
  66 + @required this.orientation,
  67 + }) : assert(alphaChannel == false || alpha == true),
49 assert(width != null), 68 assert(width != null),
50 assert(height != null), 69 assert(height != null),
  70 + assert(jpeg != null),
  71 + assert(orientation != null),
  72 + _width = width,
  73 + _height = height,
51 super(pdfDocument, '/Image', isBinary: true) { 74 super(pdfDocument, '/Image', isBinary: true) {
52 _name = '/Image$objser'; 75 _name = '/Image$objser';
53 params['/Width'] = PdfStream.string(width.toString()); 76 params['/Width'] = PdfStream.string(width.toString());
@@ -56,12 +79,16 @@ class PdfImage extends PdfXObject { @@ -56,12 +79,16 @@ class PdfImage extends PdfXObject {
56 params['/Name'] = PdfStream.string(_name); 79 params['/Name'] = PdfStream.string(_name);
57 80
58 if (alphaChannel == false && alpha) { 81 if (alphaChannel == false && alpha) {
59 - final PdfImage _sMask = PdfImage._(pdfDocument,  
60 - image: image,  
61 - width: width,  
62 - height: height,  
63 - alpha: alpha,  
64 - alphaChannel: true); 82 + final PdfImage _sMask = PdfImage._(
  83 + pdfDocument,
  84 + image: image,
  85 + width: width,
  86 + height: height,
  87 + alpha: alpha,
  88 + alphaChannel: true,
  89 + jpeg: jpeg,
  90 + orientation: orientation,
  91 + );
65 params['/SMask'] = PdfStream.string('${_sMask.objser} 0 R'); 92 params['/SMask'] = PdfStream.string('${_sMask.objser} 0 R');
66 } 93 }
67 94
@@ -76,7 +103,11 @@ class PdfImage extends PdfXObject { @@ -76,7 +103,11 @@ class PdfImage extends PdfXObject {
76 } 103 }
77 } 104 }
78 105
79 - factory PdfImage.jpeg(PdfDocument pdfDocument, {@required Uint8List image}) { 106 + factory PdfImage.jpeg(
  107 + PdfDocument pdfDocument, {
  108 + @required Uint8List image,
  109 + PdfImageOrientation orientation,
  110 + }) {
80 assert(image != null); 111 assert(image != null);
81 112
82 int width; 113 int width;
@@ -117,18 +148,30 @@ class PdfImage extends PdfXObject { @@ -117,18 +148,30 @@ class PdfImage extends PdfXObject {
117 offset += len - 2; 148 offset += len - 2;
118 } 149 }
119 150
120 - return PdfImage._(pdfDocument,  
121 - image: image, width: width, height: height, jpeg: true, alpha: false); 151 + orientation ??= PdfImageOrientation.leftTop;
  152 +
  153 + return PdfImage._(
  154 + pdfDocument,
  155 + image: image,
  156 + width: width,
  157 + height: height,
  158 + jpeg: true,
  159 + alpha: false,
  160 + alphaChannel: false,
  161 + orientation: orientation,
  162 + );
122 } 163 }
123 164
124 /// RGBA Image Data 165 /// RGBA Image Data
125 final Uint8List image; 166 final Uint8List image;
126 167
127 /// Image width 168 /// Image width
128 - final int width; 169 + final int _width;
  170 + int get width => orientation.index >= 4 ? _height : _width;
129 171
130 /// Image height 172 /// Image height
131 - final int height; 173 + final int _height;
  174 + int get height => orientation.index < 4 ? _height : _width;
132 175
133 /// Image has alpha channel 176 /// Image has alpha channel
134 final bool alpha; 177 final bool alpha;
@@ -141,6 +184,9 @@ class PdfImage extends PdfXObject { @@ -141,6 +184,9 @@ class PdfImage extends PdfXObject {
141 /// The image data is a jpeg image 184 /// The image data is a jpeg image
142 final bool jpeg; 185 final bool jpeg;
143 186
  187 + /// The internal orientation of the image
  188 + final PdfImageOrientation orientation;
  189 +
144 /// write the pixels to the stream 190 /// write the pixels to the stream
145 @override 191 @override
146 void _prepare() { 192 void _prepare() {
@@ -151,8 +197,8 @@ class PdfImage extends PdfXObject { @@ -151,8 +197,8 @@ class PdfImage extends PdfXObject {
151 return; 197 return;
152 } 198 }
153 199
154 - final int w = width;  
155 - final int h = height; 200 + final int w = _width;
  201 + final int h = _height;
156 final int s = w * h; 202 final int s = w * h;
157 203
158 final Uint8List out = Uint8List(alphaChannel ? s : s * 3); 204 final Uint8List out = Uint8List(alphaChannel ? s : s * 3);
@@ -15,36 +15,73 @@ @@ -15,36 +15,73 @@
15 */ 15 */
16 16
17 import 'dart:async'; 17 import 'dart:async';
  18 +import 'dart:convert';
18 import 'dart:io'; 19 import 'dart:io';
19 import 'dart:typed_data'; 20 import 'dart:typed_data';
20 21
21 import 'package:pdf/pdf.dart'; 22 import 'package:pdf/pdf.dart';
  23 +import 'package:pdf/widgets.dart';
22 import 'package:test/test.dart'; 24 import 'package:test/test.dart';
23 25
24 -Future<Uint8List> download(String url) async {  
25 - final HttpClient client = HttpClient();  
26 - final HttpClientRequest request = await client.getUrl(Uri.parse(url));  
27 - final HttpClientResponse response = await request.close();  
28 - final BytesBuilder builder = await response.fold(  
29 - BytesBuilder(), (BytesBuilder b, List<int> d) => b..add(d));  
30 - final List<int> data = builder.takeBytes();  
31 - return Uint8List.fromList(data);  
32 -} 26 +Document pdf;
33 27
34 void main() { 28 void main() {
35 - test('Pdf Jpeg', () async {  
36 - final PdfDocument pdf = PdfDocument();  
37 - final PdfPage page = PdfPage(pdf, pageFormat: PdfPageFormat.a4); 29 + setUpAll(() {
  30 + Document.debug = true;
  31 + pdf = Document();
  32 + });
38 33
  34 + test('Pdf Jpeg Download', () async {
39 final PdfImage image = PdfImage.jpeg( 35 final PdfImage image = PdfImage.jpeg(
40 - pdf, 36 + pdf.document,
41 image: await download('https://www.nfet.net/nfet.jpg'), 37 image: await download('https://www.nfet.net/nfet.jpg'),
42 ); 38 );
43 39
44 - final PdfGraphics g = page.getGraphics();  
45 - g.drawImage(image, 30, page.pageFormat.height - 507.0); 40 + pdf.addPage(Page(
  41 + build: (Context context) => Center(child: Image(image)),
  42 + ));
  43 + });
46 44
  45 + test('Pdf Jpeg Orientation', () {
  46 + pdf.addPage(
  47 + Page(
  48 + build: (Context context) => Wrap(
  49 + spacing: 20,
  50 + runSpacing: 20,
  51 + children: List<Widget>.generate(
  52 + PdfImageOrientation.values.length,
  53 + (int index) => SizedBox(
  54 + width: 230,
  55 + height: 230,
  56 + child: Image(
  57 + PdfImage.jpeg(
  58 + pdf.document,
  59 + image: base64.decode(jpegImage),
  60 + orientation: PdfImageOrientation.values[index],
  61 + ),
  62 + ),
  63 + ),
  64 + ),
  65 + ),
  66 + ),
  67 + );
  68 + });
  69 +
  70 + tearDownAll(() {
47 final File file = File('jpeg.pdf'); 71 final File file = File('jpeg.pdf');
48 file.writeAsBytesSync(pdf.save()); 72 file.writeAsBytesSync(pdf.save());
49 }); 73 });
50 } 74 }
  75 +
  76 +Future<Uint8List> download(String url) async {
  77 + final HttpClient client = HttpClient();
  78 + final HttpClientRequest request = await client.getUrl(Uri.parse(url));
  79 + final HttpClientResponse response = await request.close();
  80 + final BytesBuilder builder = await response.fold(
  81 + BytesBuilder(), (BytesBuilder b, List<int> d) => b..add(d));
  82 + final List<int> data = builder.takeBytes();
  83 + return Uint8List.fromList(data);
  84 +}
  85 +
  86 +const String jpegImage =
  87 + '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAEMuMjoyKkM6NjpLR0NPZKZsZFxcZMySmnmm8dT++u3U6eX//////////+Xp////////////////////////////2wBDAUdLS2RXZMRsbMT//+n/////////////////////////////////////////////////////////////////////wAARCAAUAAgDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAP/xAAbEAACAwEBAQAAAAAAAAAAAAABAgARIQMEQf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCvm5joGZi1hj9iPIgIZ7Nhzl5EC3FAikC9N7ERA//Z';