David PHAM-VAN

Implement missing Windows features

@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 4
5 - Fix Pdf Raster on WEB 5 - Fix Pdf Raster on WEB
6 - Fix Windows memory leaks 6 - Fix Windows memory leaks
  7 +- Implement missing Windows features
7 8
8 ## 3.7.0 9 ## 3.7.0
9 10
@@ -65,6 +65,9 @@ abstract class PrintingPlatform extends PlatformInterface { @@ -65,6 +65,9 @@ abstract class PrintingPlatform extends PlatformInterface {
65 PdfPageFormat format, 65 PdfPageFormat format,
66 ); 66 );
67 67
  68 + /// Enumerate the available printers on the system.
  69 + Future<List<Printer>> listPrinters();
  70 +
68 /// Opens the native printer picker interface, and returns the URL of the selected printer. 71 /// Opens the native printer picker interface, and returns the URL of the selected printer.
69 Future<Printer> pickPrinter(Rect bounds); 72 Future<Printer> pickPrinter(Rect bounds);
70 73
@@ -152,6 +152,26 @@ class MethodChannelPrinting extends PrintingPlatform { @@ -152,6 +152,26 @@ class MethodChannelPrinting extends PrintingPlatform {
152 } 152 }
153 153
154 @override 154 @override
  155 + Future<List<Printer>> listPrinters() async {
  156 + final params = <String, dynamic>{};
  157 + final list =
  158 + await _channel.invokeMethod<List<dynamic>>('listPrinters', params);
  159 +
  160 + final printers = <Printer>[];
  161 +
  162 + for (final printer in list) {
  163 + printers.add(Printer(
  164 + url: printer['url'],
  165 + name: printer['name'],
  166 + model: printer['model'],
  167 + location: printer['location'],
  168 + ));
  169 + }
  170 +
  171 + return printers;
  172 + }
  173 +
  174 + @override
155 Future<Printer> pickPrinter(Rect bounds) async { 175 Future<Printer> pickPrinter(Rect bounds) async {
156 final params = <String, dynamic>{ 176 final params = <String, dynamic>{
157 'x': bounds.left, 177 'x': bounds.left,
@@ -191,7 +211,7 @@ class MethodChannelPrinting extends PrintingPlatform { @@ -191,7 +211,7 @@ class MethodChannelPrinting extends PrintingPlatform {
191 final params = <String, dynamic>{ 211 final params = <String, dynamic>{
192 'name': name, 212 'name': name,
193 'printer': printer.url, 213 'printer': printer.url,
194 - 'doc': Uint8List.fromList(bytes), 214 + 'doc': bytes,
195 'job': job.index, 215 'job': job.index,
196 }; 216 };
197 await _channel.invokeMethod<int>('directPrintPdf', params); 217 await _channel.invokeMethod<int>('directPrintPdf', params);
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 import 'dart:async'; 17 import 'dart:async';
18 import 'dart:typed_data'; 18 import 'dart:typed_data';
19 19
  20 +import 'package:flutter/material.dart';
20 import 'package:flutter/rendering.dart' show Rect, Offset; 21 import 'package:flutter/rendering.dart' show Rect, Offset;
21 import 'package:meta/meta.dart'; 22 import 'package:meta/meta.dart';
22 import 'package:pdf/pdf.dart'; 23 import 'package:pdf/pdf.dart';
@@ -48,15 +49,49 @@ mixin Printing { @@ -48,15 +49,49 @@ mixin Printing {
48 return PrintingPlatform.instance.layoutPdf(onLayout, name, format); 49 return PrintingPlatform.instance.layoutPdf(onLayout, name, format);
49 } 50 }
50 51
  52 + /// Enumerate the available printers on the system.
  53 + ///
  54 + /// This is not supported on all platforms. Check the result of [info] to
  55 + /// find at runtime if this feature is available or not.
  56 + static Future<List<Printer>> listPrinters() {
  57 + return PrintingPlatform.instance.listPrinters();
  58 + }
  59 +
51 /// Opens the native printer picker interface, and returns the URL of the 60 /// Opens the native printer picker interface, and returns the URL of the
52 /// selected printer. 61 /// selected printer.
53 /// 62 ///
54 /// This is not supported on all platforms. Check the result of [info] to 63 /// This is not supported on all platforms. Check the result of [info] to
55 /// find at runtime if this feature is available or not. 64 /// find at runtime if this feature is available or not.
56 - static Future<Printer> pickPrinter({Rect bounds}) { 65 + static Future<Printer> pickPrinter({
  66 + @required BuildContext context,
  67 + Rect bounds,
  68 + }) async {
  69 + final _info = await info();
  70 +
  71 + if (_info != null && _info.canListPrinters) {
  72 + assert(
  73 + context != null,
  74 + 'Pass a BuildCOntext to pickPrinter to display a selection list',
  75 + );
  76 + final printers = await listPrinters();
  77 + return await showDialog<Printer>(
  78 + context: context,
  79 + builder: (context) => SimpleDialog(
  80 + children: printers
  81 + .map<Widget>(
  82 + (e) => SimpleDialogOption(
  83 + child: Text(e.name),
  84 + onPressed: () => Navigator.of(context).pop(e),
  85 + ),
  86 + )
  87 + .toList(),
  88 + ),
  89 + );
  90 + }
  91 +
57 bounds ??= Rect.fromCircle(center: Offset.zero, radius: 10); 92 bounds ??= Rect.fromCircle(center: Offset.zero, radius: 10);
58 93
59 - return PrintingPlatform.instance.pickPrinter(bounds); 94 + return await PrintingPlatform.instance.pickPrinter(bounds);
60 } 95 }
61 96
62 /// Prints a Pdf document to a specific local printer with no UI 97 /// Prints a Pdf document to a specific local printer with no UI
@@ -22,12 +22,14 @@ class PrintingInfo { @@ -22,12 +22,14 @@ class PrintingInfo {
22 this.dynamicLayout = false, 22 this.dynamicLayout = false,
23 this.canPrint = false, 23 this.canPrint = false,
24 this.canConvertHtml = false, 24 this.canConvertHtml = false,
  25 + this.canListPrinters = false,
25 this.canShare = false, 26 this.canShare = false,
26 this.canRaster = false, 27 this.canRaster = false,
27 }) : assert(directPrint != null), 28 }) : assert(directPrint != null),
28 assert(dynamicLayout != null), 29 assert(dynamicLayout != null),
29 assert(canPrint != null), 30 assert(canPrint != null),
30 assert(canConvertHtml != null), 31 assert(canConvertHtml != null),
  32 + assert(canListPrinters != null),
31 assert(canShare != null), 33 assert(canShare != null),
32 assert(canRaster != null); 34 assert(canRaster != null);
33 35
@@ -37,6 +39,7 @@ class PrintingInfo { @@ -37,6 +39,7 @@ class PrintingInfo {
37 dynamicLayout: map['dynamicLayout'] ?? false, 39 dynamicLayout: map['dynamicLayout'] ?? false,
38 canPrint: map['canPrint'] ?? false, 40 canPrint: map['canPrint'] ?? false,
39 canConvertHtml: map['canConvertHtml'] ?? false, 41 canConvertHtml: map['canConvertHtml'] ?? false,
  42 + canListPrinters: map['canListPrinters'] ?? false,
40 canShare: map['canShare'] ?? false, 43 canShare: map['canShare'] ?? false,
41 canRaster: map['canRaster'] ?? false, 44 canRaster: map['canRaster'] ?? false,
42 ); 45 );
@@ -57,6 +60,9 @@ class PrintingInfo { @@ -57,6 +60,9 @@ class PrintingInfo {
57 /// The platform implementation is able to convert an html document to Pdf 60 /// The platform implementation is able to convert an html document to Pdf
58 final bool canConvertHtml; 61 final bool canConvertHtml;
59 62
  63 + /// The platform implementation is able list the available printers on the system
  64 + final bool canListPrinters;
  65 +
60 /// The platform implementation is able to share a Pdf document 66 /// The platform implementation is able to share a Pdf document
61 /// to other applications 67 /// to other applications
62 final bool canShare; 68 final bool canShare;
@@ -71,6 +77,7 @@ class PrintingInfo { @@ -71,6 +77,7 @@ class PrintingInfo {
71 directPrint: $directPrint 77 directPrint: $directPrint
72 dynamicLayout: $dynamicLayout 78 dynamicLayout: $dynamicLayout
73 canConvertHtml: $canConvertHtml 79 canConvertHtml: $canConvertHtml
  80 + canListPrinters: $canListPrinters
74 canShare: $canShare 81 canShare: $canShare
75 canRaster: $canRaster'''; 82 canRaster: $canRaster''';
76 83
@@ -81,6 +88,7 @@ class PrintingInfo { @@ -81,6 +88,7 @@ class PrintingInfo {
81 'directPrint': directPrint, 88 'directPrint': directPrint,
82 'dynamicLayout': dynamicLayout, 89 'dynamicLayout': dynamicLayout,
83 'canConvertHtml': canConvertHtml, 90 'canConvertHtml': canConvertHtml,
  91 + 'canListPrinters': canListPrinters,
84 'canShare': canShare, 92 'canShare': canShare,
85 'canRaster': canRaster, 93 'canRaster': canRaster,
86 }; 94 };
@@ -52,10 +52,7 @@ class PrintingPlugin extends PrintingPlatform { @@ -52,10 +52,7 @@ class PrintingPlugin extends PrintingPlatform {
52 ]); 52 ]);
53 53
54 return PrintingInfo( 54 return PrintingInfo(
55 - directPrint: false,  
56 - dynamicLayout: false,  
57 canPrint: true, 55 canPrint: true,
58 - canConvertHtml: false,  
59 canShare: true, 56 canShare: true,
60 canRaster: workerSrc, 57 canRaster: workerSrc,
61 ); 58 );
@@ -159,6 +156,11 @@ class PrintingPlugin extends PrintingPlatform { @@ -159,6 +156,11 @@ class PrintingPlugin extends PrintingPlatform {
159 } 156 }
160 157
161 @override 158 @override
  159 + Future<List<Printer>> listPrinters() {
  160 + throw UnimplementedError();
  161 + }
  162 +
  163 + @override
162 Future<Printer> pickPrinter( 164 Future<Printer> pickPrinter(
163 Rect bounds, 165 Rect bounds,
164 ) { 166 ) {
@@ -66,7 +66,7 @@ void main() { @@ -66,7 +66,7 @@ void main() {
66 66
67 test('pickPrinter', () async { 67 test('pickPrinter', () async {
68 expect( 68 expect(
69 - await Printing.pickPrinter(), 69 + await Printing.pickPrinter(context: null),
70 null, 70 null,
71 ); 71 );
72 }); 72 });
@@ -30,16 +30,106 @@ @@ -30,16 +30,106 @@
30 30
31 namespace nfet { 31 namespace nfet {
32 32
  33 +const auto pdfDpi = 72;
  34 +
  35 +std::string toUtf8(std::wstring wstr) {
  36 + int cbMultiByte =
  37 + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
  38 + LPSTR lpMultiByteStr = (LPSTR)malloc(cbMultiByte);
  39 + cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1,
  40 + lpMultiByteStr, cbMultiByte, NULL, NULL);
  41 + std::string ret = lpMultiByteStr;
  42 + free(lpMultiByteStr);
  43 + return ret;
  44 +}
  45 +
  46 +std::string toUtf8(TCHAR* tstr) {
  47 +#ifndef UNICODE
  48 +#error "Non unicode build not supported"
  49 +#endif
  50 +
  51 + if (!tstr) {
  52 + return std::string{};
  53 + }
  54 +
  55 + return toUtf8(std::wstring{tstr});
  56 +}
  57 +
  58 +std::wstring fromUtf8(std::string str) {
  59 + auto len = MultiByteToWideChar(CP_ACP, 0, str.c_str(),
  60 + static_cast<int>(str.length()), nullptr, 0);
  61 + if (len <= 0) {
  62 + return false;
  63 + }
  64 +
  65 + auto wstr = std::wstring{};
  66 + wstr.resize(len);
  67 + MultiByteToWideChar(CP_ACP, 0, str.c_str(), static_cast<int>(str.length()),
  68 + &wstr[0], len);
  69 +
  70 + return wstr;
  71 +}
  72 +
33 PrintJob::PrintJob(Printing* printing, int index) 73 PrintJob::PrintJob(Printing* printing, int index)
34 : printing(printing), index(index) {} 74 : printing(printing), index(index) {}
35 75
36 -void PrintJob::directPrintPdf(std::string name, 76 +bool PrintJob::directPrintPdf(std::string name,
37 std::vector<uint8_t> data, 77 std::vector<uint8_t> data,
38 - std::string withPrinter) {} 78 + std::string withPrinter) {
  79 + FPDF_InitLibraryWithConfig(nullptr);
  80 +
  81 + auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr);
  82 + if (!doc) {
  83 + FPDF_DestroyLibrary();
  84 + return false;
  85 + }
  86 +
  87 + hDC = CreateDC(TEXT("WINSPOOL"), fromUtf8(withPrinter).c_str(), NULL, NULL);
  88 + if (!hDC) {
  89 + FPDF_CloseDocument(doc);
  90 + FPDF_DestroyLibrary();
  91 + return false;
  92 + }
  93 +
  94 + DOCINFO docInfo;
  95 + ZeroMemory(&docInfo, sizeof(docInfo));
  96 + docInfo.cbSize = sizeof(DOCINFO);
  97 + auto docName = fromUtf8(name);
  98 + docInfo.lpszDocName = docName.c_str();
  99 + StartDoc(hDC, &docInfo);
  100 +
  101 + int pageCount = FPDF_GetPageCount(doc);
  102 +
  103 + for (auto i = 0; i < pageCount; i++) {
  104 + StartPage(hDC);
  105 + auto page = FPDF_LoadPage(doc, i);
  106 + if (!page) {
  107 + EndDoc(hDC);
  108 + DeleteDC(hDC);
  109 + FPDF_CloseDocument(doc);
  110 + FPDF_DestroyLibrary();
  111 + return false;
  112 + }
  113 + auto pageWidth = FPDF_GetPageWidth(page);
  114 + auto pageHeight = FPDF_GetPageHeight(page);
  115 + auto dpiX = GetDeviceCaps(hDC, LOGPIXELSX);
  116 + auto dpiY = GetDeviceCaps(hDC, LOGPIXELSY);
  117 + auto width = static_cast<int>(pageWidth / pdfDpi * dpiX);
  118 + auto height = static_cast<int>(pageHeight / pdfDpi * dpiY);
  119 + FPDF_RenderPage(hDC, page, 0, 0, width, height, 0, FPDF_ANNOT);
  120 + FPDF_ClosePage(page);
  121 + EndPage(hDC);
  122 + }
  123 +
  124 + EndDoc(hDC);
  125 + DeleteDC(hDC);
  126 + FPDF_CloseDocument(doc);
  127 + FPDF_DestroyLibrary();
  128 + return true;
  129 +}
39 130
40 bool PrintJob::printPdf(std::string name) { 131 bool PrintJob::printPdf(std::string name) {
41 PRINTDLG pd; 132 PRINTDLG pd;
42 - // HWND hwnd;  
43 133
44 // Initialize PRINTDLG 134 // Initialize PRINTDLG
45 ZeroMemory(&pd, sizeof(pd)); 135 ZeroMemory(&pd, sizeof(pd));
@@ -60,20 +150,8 @@ bool PrintJob::printPdf(std::string name) { @@ -60,20 +150,8 @@ bool PrintJob::printPdf(std::string name) {
60 auto r = PrintDlg(&pd); 150 auto r = PrintDlg(&pd);
61 151
62 if (r == 1) { 152 if (r == 1) {
63 - // printf("ncopies: %d\n", pd.nCopies);  
64 - // printf("hDevMode: %d\n", (int)pd.hDevMode);  
65 -  
66 - // DEVMODE* b = static_cast<DEVMODE*>(GlobalLock(pd.hDevMode));  
67 - // auto pageDpi = b->dmPrintQuality;  
68 -  
69 - // GlobalUnlock(pd.hDevMode);  
70 -  
71 - // auto pageHeight = b->dmPaperLength;  
72 - // auto pageWidth = b->dmPaperWidth;  
73 - // auto pageScale = b->dmScale / 100;  
74 -  
75 - auto dpiX = static_cast<double>(GetDeviceCaps(pd.hDC, LOGPIXELSX)) / 72;  
76 - auto dpiY = static_cast<double>(GetDeviceCaps(pd.hDC, LOGPIXELSY)) / 72; 153 + auto dpiX = static_cast<double>(GetDeviceCaps(pd.hDC, LOGPIXELSX)) / pdfDpi;
  154 + auto dpiY = static_cast<double>(GetDeviceCaps(pd.hDC, LOGPIXELSY)) / pdfDpi;
77 auto pageWidth = 155 auto pageWidth =
78 static_cast<double>(GetDeviceCaps(pd.hDC, PHYSICALWIDTH)) / dpiX; 156 static_cast<double>(GetDeviceCaps(pd.hDC, PHYSICALWIDTH)) / dpiX;
79 auto pageHeight = 157 auto pageHeight =
@@ -89,16 +167,10 @@ bool PrintJob::printPdf(std::string name) { @@ -89,16 +167,10 @@ bool PrintJob::printPdf(std::string name) {
89 auto marginRight = pageWidth - printableWidth - marginLeft; 167 auto marginRight = pageWidth - printableWidth - marginLeft;
90 auto marginBottom = pageHeight - printableHeight - marginTop; 168 auto marginBottom = pageHeight - printableHeight - marginTop;
91 169
92 - // printf("dpiX: %f\n", dpiX);  
93 - // printf("HORZRES: %d\n", GetDeviceCaps(pd.hDC, HORZRES));  
94 - // printf("PHYSICALOFFSETX: %d\n", GetDeviceCaps(pd.hDC, PHYSICALOFFSETX));  
95 - // printf("pageWidth: %f\n", pageWidth);  
96 -  
97 hDC = pd.hDC; 170 hDC = pd.hDC;
98 hDevMode = pd.hDevMode; 171 hDevMode = pd.hDevMode;
99 hDevNames = pd.hDevNames; 172 hDevNames = pd.hDevNames;
100 -  
101 - // printf("HDC: %llu job: %d\n", (size_t)pd.hDC, index); 173 + documentName = name;
102 174
103 printing->onLayout(this, pageWidth, pageHeight, marginLeft, marginTop, 175 printing->onLayout(this, pageWidth, pageHeight, marginLeft, marginTop,
104 marginRight, marginBottom); 176 marginRight, marginBottom);
@@ -108,111 +180,106 @@ bool PrintJob::printPdf(std::string name) { @@ -108,111 +180,106 @@ bool PrintJob::printPdf(std::string name) {
108 return false; 180 return false;
109 } 181 }
110 182
  183 +std::vector<Printer> PrintJob::listPrinters() {
  184 + auto printers = std::vector<Printer>{};
  185 + DWORD needed = 0;
  186 + DWORD returned = 0;
  187 + const auto flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
  188 +
  189 + EnumPrinters(flags, nullptr, 1, nullptr, 0, &needed, &returned);
  190 +
  191 + auto buffer = (PRINTER_INFO_1*)malloc(needed);
  192 + if (!buffer) {
  193 + return printers;
  194 + }
  195 +
  196 + auto result = EnumPrinters(flags, nullptr, 1, (LPBYTE)buffer, needed, &needed,
  197 + &returned);
  198 +
  199 + if (result == 0) {
  200 + free(buffer);
  201 + return printers;
  202 + }
  203 +
  204 + for (DWORD i = 0; i < returned; i++) {
  205 + printers.push_back(Printer{
  206 + toUtf8(buffer[i].pName),
  207 + toUtf8(buffer[i].pName),
  208 + toUtf8(buffer[i].pDescription),
  209 + toUtf8(buffer[i].pComment),
  210 + });
  211 + }
  212 +
  213 + free(buffer);
  214 + return printers;
  215 +}
  216 +
111 void PrintJob::writeJob(std::vector<uint8_t> data) { 217 void PrintJob::writeJob(std::vector<uint8_t> data) {
112 - // printf("hDC: %llu job: %d\n", (size_t)hDC, index);  
113 - auto dpiX = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSX)) / 72;  
114 - auto dpiY = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSY)) / 72;  
115 -  
116 - // GlobalFree(pd.hDevMode);  
117 -  
118 - // print(  
119 - // 'Paper size: ${pageWidth} ${pageHeight} scale: $pageScale dpi:  
120 - // $pageDpi');  
121 -  
122 - // final printerDC = CreateDCW(nullptr, szPrinter,nullptr,  
123 - // devmode);  
124 - DOCINFO info;  
125 -  
126 - // memset(&info, 0, sizeof(info));  
127 - ZeroMemory(&info, sizeof(info));  
128 - info.cbSize = sizeof(info);  
129 - // info.fwType = 0;  
130 - // info.lpszDatatype = nullptr;  
131 - // info.lpszDocName = nullptr;  
132 - // info.lpszOutput = nullptr;  
133 - // auto printerDC = pd.hDC;  
134 - // print('hDC: ${pd.hDC}');  
135 - auto r = StartDoc(hDC, &info);  
136 - // print('StartDoc = $r'); 218 + auto dpiX = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSX)) / pdfDpi;
  219 + auto dpiY = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSY)) / pdfDpi;
137 220
138 - FPDF_InitLibraryWithConfig(nullptr);  
139 - // final buffer = allocate<ffi.Uint8>(count : bytes.length);  
140 - // final nativeBuffer = buffer.asTypedList(bytes.length);  
141 - // nativeBuffer.setAll(0, bytes); 221 + DOCINFO docInfo;
  222 +
  223 + ZeroMemory(&docInfo, sizeof(docInfo));
  224 + docInfo.cbSize = sizeof(docInfo);
  225 +
  226 + auto docName = fromUtf8(documentName);
  227 + docInfo.lpszDocName = docName.c_str();
142 228
143 - // auto buffer = std::vector<uint8_t>{}; 229 + auto r = StartDoc(hDC, &docInfo);
  230 +
  231 + FPDF_InitLibraryWithConfig(nullptr);
144 232
145 auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr); 233 auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr);
146 if (!doc) { 234 if (!doc) {
147 - // printf("Error loading the document: %d\n", FPDF_GetLastError()); 235 + FPDF_DestroyLibrary();
148 return; 236 return;
149 } 237 }
150 238
151 auto pages = FPDF_GetPageCount(doc); 239 auto pages = FPDF_GetPageCount(doc);
152 - // printf("Page count: %d\n", pages);  
153 240
154 for (auto pageNum = 0; pageNum < pages; pageNum++) { 241 for (auto pageNum = 0; pageNum < pages; pageNum++) {
155 r = StartPage(hDC); 242 r = StartPage(hDC);
156 - // printf("StartPage = %d\n", r);  
157 243
158 auto page = FPDF_LoadPage(doc, pageNum); 244 auto page = FPDF_LoadPage(doc, pageNum);
159 - // print(FPDF_GetLastError()); 245 + if (!page) {
  246 + EndPage(hDC);
  247 + continue;
  248 + }
160 249
161 auto pdfWidth = FPDF_GetPageWidth(page); 250 auto pdfWidth = FPDF_GetPageWidth(page);
162 auto pdfHeight = FPDF_GetPageHeight(page); 251 auto pdfHeight = FPDF_GetPageHeight(page);
163 252
164 - // print('$width x $height');  
165 -  
166 - // printf("pdfWidth: %f dpiX: %f \n", pdfWidth, dpiX);  
167 int bWidth = static_cast<int>(pdfWidth * dpiX); 253 int bWidth = static_cast<int>(pdfWidth * dpiX);
168 int bHeight = static_cast<int>(pdfHeight * dpiY); 254 int bHeight = static_cast<int>(pdfHeight * dpiY);
169 255
170 - // printf("bwidth/bheight: %d x %d\n", bWidth, bHeight);  
171 -  
172 FPDF_RenderPage(hDC, page, 0, 0, bWidth, bHeight, 0, FPDF_ANNOT); 256 FPDF_RenderPage(hDC, page, 0, 0, bWidth, bHeight, 0, FPDF_ANNOT);
173 257
174 r = EndPage(hDC); 258 r = EndPage(hDC);
175 - // printf("EndPage = %d\n", r);  
176 } 259 }
177 260
178 FPDF_CloseDocument(doc); 261 FPDF_CloseDocument(doc);
179 FPDF_DestroyLibrary(); 262 FPDF_DestroyLibrary();
180 263
181 r = EndDoc(hDC); 264 r = EndDoc(hDC);
182 - // printf("EndDoc = %d\n", r);  
183 - // DeleteDC(printerDC); 265 +
184 DeleteDC(hDC); 266 DeleteDC(hDC);
185 GlobalFree(hDevNames); 267 GlobalFree(hDevNames);
186 ClosePrinter(hDevMode); 268 ClosePrinter(hDevMode);
187 -} // namespace nfet 269 +}
188 270
189 void PrintJob::cancelJob(std::string error) {} 271 void PrintJob::cancelJob(std::string error) {}
190 272
191 bool PrintJob::sharePdf(std::vector<uint8_t> data, std::string name) { 273 bool PrintJob::sharePdf(std::vector<uint8_t> data, std::string name) {
192 TCHAR lpTempPathBuffer[MAX_PATH]; 274 TCHAR lpTempPathBuffer[MAX_PATH];
193 - // TCHAR szTempFileName[MAX_PATH];  
194 275
195 auto ret = GetTempPath(MAX_PATH, lpTempPathBuffer); 276 auto ret = GetTempPath(MAX_PATH, lpTempPathBuffer);
196 if (ret > MAX_PATH || (ret == 0)) { 277 if (ret > MAX_PATH || (ret == 0)) {
197 return false; 278 return false;
198 } 279 }
199 280
200 -#ifndef UNICODE  
201 -#error "Non unicode build not supported"  
202 -#endif  
203 -  
204 - auto len = MultiByteToWideChar(CP_ACP, 0, name.c_str(),  
205 - static_cast<int>(name.length()), nullptr, 0);  
206 - if (len <= 0) {  
207 - return false;  
208 - } 281 + auto filename = fromUtf8(toUtf8(lpTempPathBuffer) + "\\" + name);
209 282
210 - auto w_name = std::wstring{};  
211 - w_name.resize(len);  
212 - MultiByteToWideChar(CP_ACP, 0, name.c_str(), static_cast<int>(name.length()),  
213 - &w_name[0], len);  
214 -  
215 - auto filename = std::wstring{lpTempPathBuffer} + L"\\" + w_name;  
216 auto output_file = 283 auto output_file =
217 std::basic_ofstream<uint8_t>{filename, std::ios::out | std::ios::binary}; 284 std::basic_ofstream<uint8_t>{filename, std::ios::out | std::ios::binary};
218 output_file.write(data.data(), data.size()); 285 output_file.write(data.data(), data.size());
@@ -231,7 +298,6 @@ bool PrintJob::sharePdf(std::vector<uint8_t> data, std::string name) { @@ -231,7 +298,6 @@ bool PrintJob::sharePdf(std::vector<uint8_t> data, std::string name) {
231 298
232 ret = ShellExecuteEx(&ShExecInfo); 299 ret = ShellExecuteEx(&ShExecInfo);
233 300
234 - // CoTaskMemFree(pidlWinFiles);  
235 return ret == TRUE; 301 return ret == TRUE;
236 } 302 }
237 303
@@ -244,13 +310,12 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data, @@ -244,13 +310,12 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data,
244 310
245 auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr); 311 auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr);
246 if (!doc) { 312 if (!doc) {
247 - // printf(stderr, "Error: %d\n", FPDF_GetLastError()); 313 + FPDF_DestroyLibrary();
248 printing->onPageRasterEnd(this); 314 printing->onPageRasterEnd(this);
249 return; 315 return;
250 } 316 }
251 317
252 auto pageCount = FPDF_GetPageCount(doc); 318 auto pageCount = FPDF_GetPageCount(doc);
253 - // printf("pdf: pages:%d\n", pageCount);  
254 319
255 if (pages.size() == 0) { 320 if (pages.size() == 0) {
256 // Use all pages 321 // Use all pages
@@ -265,15 +330,12 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data, @@ -265,15 +330,12 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data,
265 330
266 auto page = FPDF_LoadPage(doc, n); 331 auto page = FPDF_LoadPage(doc, n);
267 if (!page) { 332 if (!page) {
268 - // printf("Page Error: %d\n", FPDF_GetLastError());  
269 continue; 333 continue;
270 } 334 }
271 335
272 auto width = FPDF_GetPageWidth(page); 336 auto width = FPDF_GetPageWidth(page);
273 auto height = FPDF_GetPageHeight(page); 337 auto height = FPDF_GetPageHeight(page);
274 338
275 - // printf("pdf: page:%d w:%f h:%f\n", n, width, height);  
276 -  
277 auto bWidth = static_cast<int>(width * scale); 339 auto bWidth = static_cast<int>(width * scale);
278 auto bHeight = static_cast<int>(height * scale); 340 auto bHeight = static_cast<int>(height * scale);
279 341
@@ -308,12 +370,13 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data, @@ -308,12 +370,13 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data,
308 FPDF_DestroyLibrary(); 370 FPDF_DestroyLibrary();
309 371
310 printing->onPageRasterEnd(this); 372 printing->onPageRasterEnd(this);
311 -} // namespace nfet 373 +}
312 374
313 std::map<std::string, bool> PrintJob::printingInfo() { 375 std::map<std::string, bool> PrintJob::printingInfo() {
314 return std::map<std::string, bool>{ 376 return std::map<std::string, bool>{
315 - {"directPrint", true}, {"dynamicLayout", true}, {"canPrint", true},  
316 - {"canConvertHtml", false}, {"canShare", true}, {"canRaster", true}, 377 + {"directPrint", true}, {"dynamicLayout", true}, {"canPrint", true},
  378 + {"canListPrinters", true}, {"canConvertHtml", false}, {"canShare", true},
  379 + {"canRaster", true},
317 }; 380 };
318 } 381 }
319 382
@@ -30,6 +30,19 @@ namespace nfet { @@ -30,6 +30,19 @@ namespace nfet {
30 30
31 class Printing; 31 class Printing;
32 32
  33 +struct Printer {
  34 + const std::string name;
  35 + const std::string url;
  36 + const std::string model;
  37 + const std::string description;
  38 +
  39 + Printer(std::string name,
  40 + std::string url,
  41 + std::string model,
  42 + std::string description)
  43 + : name(name), url(url), model(model), description(description) {}
  44 +};
  45 +
33 class PrintJob { 46 class PrintJob {
34 private: 47 private:
35 Printing* printing; 48 Printing* printing;
@@ -37,17 +50,18 @@ class PrintJob { @@ -37,17 +50,18 @@ class PrintJob {
37 HGLOBAL hDevMode = nullptr; 50 HGLOBAL hDevMode = nullptr;
38 HGLOBAL hDevNames = nullptr; 51 HGLOBAL hDevNames = nullptr;
39 HDC hDC = nullptr; 52 HDC hDC = nullptr;
  53 + std::string documentName;
40 54
41 public: 55 public:
42 PrintJob(Printing* printing, int index); 56 PrintJob(Printing* printing, int index);
43 57
44 - // ~PrintJob() { printf("Delete PrintJob #%d\n", index); }  
45 -  
46 int id() { return index; } 58 int id() { return index; }
47 59
48 - void directPrintPdf(std::string name, 60 + std::vector<Printer> listPrinters();
  61 +
  62 + bool directPrintPdf(std::string name,
49 std::vector<uint8_t> data, 63 std::vector<uint8_t> data,
50 - std::string withPrinter); 64 + std::string printer);
51 65
52 bool printPdf(std::string name); 66 bool printPdf(std::string name);
53 67
@@ -55,9 +55,7 @@ void Printing::onPageRasterEnd(PrintJob* job) { @@ -55,9 +55,7 @@ void Printing::onPageRasterEnd(PrintJob* job) {
55 55
56 class OnLayoutResult : public flutter::MethodResult<flutter::EncodableValue> { 56 class OnLayoutResult : public flutter::MethodResult<flutter::EncodableValue> {
57 public: 57 public:
58 - OnLayoutResult(PrintJob* job) : job(job) {  
59 - // printf("OnLayoutResult (%d) %p\n", job->id(), this);  
60 - } 58 + OnLayoutResult(PrintJob* job) : job(job) {}
61 59
62 private: 60 private:
63 PrintJob* job; 61 PrintJob* job;
@@ -65,7 +63,7 @@ class OnLayoutResult : public flutter::MethodResult<flutter::EncodableValue> { @@ -65,7 +63,7 @@ class OnLayoutResult : public flutter::MethodResult<flutter::EncodableValue> {
65 protected: 63 protected:
66 void SuccessInternal(const flutter::EncodableValue* result) { 64 void SuccessInternal(const flutter::EncodableValue* result) {
67 auto doc = std::get<std::vector<uint8_t>>(*result); 65 auto doc = std::get<std::vector<uint8_t>>(*result);
68 - // printf("Success! (%d) %llu bytes %p\n", job->id(), doc.size(), this); 66 +
69 job->writeJob(doc); 67 job->writeJob(doc);
70 delete job; 68 delete job;
71 } 69 }
@@ -73,14 +71,10 @@ class OnLayoutResult : public flutter::MethodResult<flutter::EncodableValue> { @@ -73,14 +71,10 @@ class OnLayoutResult : public flutter::MethodResult<flutter::EncodableValue> {
73 void ErrorInternal(const std::string& error_code, 71 void ErrorInternal(const std::string& error_code,
74 const std::string& error_message, 72 const std::string& error_message,
75 const flutter::EncodableValue* error_details) { 73 const flutter::EncodableValue* error_details) {
76 - printf("Error!\n");  
77 delete job; 74 delete job;
78 } 75 }
79 76
80 - void NotImplementedInternal() {  
81 - printf("NotImplemented!\n");  
82 - delete job;  
83 - } 77 + void NotImplementedInternal() { delete job; }
84 }; 78 };
85 79
86 void Printing::onLayout(PrintJob* job, 80 void Printing::onLayout(PrintJob* job,
@@ -90,12 +84,6 @@ void Printing::onLayout(PrintJob* job, @@ -90,12 +84,6 @@ void Printing::onLayout(PrintJob* job,
90 double marginTop, 84 double marginTop,
91 double marginRight, 85 double marginRight,
92 double marginBottom) { 86 double marginBottom) {
93 - // printf("onLayout (%d) %fx%f %f %f %f %f\n", job->id(), pageWidth,  
94 - // pageHeight,  
95 - // marginLeft, marginTop, marginRight, marginBottom);  
96 -  
97 - // auto result = std::make_unique<OnLayoutResult>(job);  
98 -  
99 channel->InvokeMethod("onLayout", 87 channel->InvokeMethod("onLayout",
100 std::make_unique<flutter::EncodableValue>( 88 std::make_unique<flutter::EncodableValue>(
101 flutter::EncodableValue(flutter::EncodableMap{ 89 flutter::EncodableValue(flutter::EncodableMap{
@@ -117,4 +105,21 @@ void Printing::onLayout(PrintJob* job, @@ -117,4 +105,21 @@ void Printing::onLayout(PrintJob* job,
117 std::make_unique<OnLayoutResult>(job)); 105 std::make_unique<OnLayoutResult>(job));
118 } 106 }
119 107
  108 +// send completion status to flutter
  109 +void Printing::onCompleted(PrintJob* job, bool completed, std::string error) {
  110 + auto map = flutter::EncodableMap{
  111 + {flutter::EncodableValue("job"), flutter::EncodableValue(job->id())},
  112 + {flutter::EncodableValue("completed"),
  113 + flutter::EncodableValue(completed)},
  114 + };
  115 +
  116 + if (!error.empty()) {
  117 + map[flutter::EncodableValue("error")] = flutter::EncodableValue(error);
  118 + }
  119 +
  120 + channel->InvokeMethod(
  121 + "onCompleted",
  122 + std::make_unique<flutter::EncodableValue>(flutter::EncodableValue(map)));
  123 +}
  124 +
120 } // namespace nfet 125 } // namespace nfet
@@ -24,8 +24,6 @@ @@ -24,8 +24,6 @@
24 24
25 #include <flutter/method_channel.h> 25 #include <flutter/method_channel.h>
26 26
27 -// #include "print_job.h"  
28 -  
29 namespace nfet { 27 namespace nfet {
30 28
31 class PrintJob; 29 class PrintJob;
@@ -51,6 +49,8 @@ class Printing { @@ -51,6 +49,8 @@ class Printing {
51 double marginTop, 49 double marginTop,
52 double marginRight, 50 double marginRight,
53 double marginBottom); 51 double marginBottom);
  52 +
  53 + void Printing::onCompleted(PrintJob* job, bool completed, std::string error);
54 }; 54 };
55 55
56 } // namespace nfet 56 } // namespace nfet
@@ -58,6 +58,7 @@ class PrintingPlugin : public flutter::Plugin { @@ -58,6 +58,7 @@ class PrintingPlugin : public flutter::Plugin {
58 58
59 private: 59 private:
60 Printing printing{}; 60 Printing printing{};
  61 +
61 // Called when a method is called on this plugin's channel from Dart. 62 // Called when a method is called on this plugin's channel from Dart.
62 void HandleMethodCall( 63 void HandleMethodCall(
63 const flutter::MethodCall<flutter::EncodableValue>& method_call, 64 const flutter::MethodCall<flutter::EncodableValue>& method_call,
@@ -66,9 +67,9 @@ class PrintingPlugin : public flutter::Plugin { @@ -66,9 +67,9 @@ class PrintingPlugin : public flutter::Plugin {
66 const auto* arguments = 67 const auto* arguments =
67 std::get_if<flutter::EncodableMap>(method_call.arguments()); 68 std::get_if<flutter::EncodableMap>(method_call.arguments());
68 auto vName = arguments->find(flutter::EncodableValue("name")); 69 auto vName = arguments->find(flutter::EncodableValue("name"));
69 - auto name = vName != arguments->end() 70 + auto name = vName != arguments->end() && !vName->second.IsNull()
70 ? std::get<std::string>(vName->second) 71 ? std::get<std::string>(vName->second)
71 - : std::string{}; 72 + : std::string{"document"};
72 auto vJob = arguments->find(flutter::EncodableValue("job")); 73 auto vJob = arguments->find(flutter::EncodableValue("job"));
73 auto jobNum = vJob != arguments->end() ? std::get<int>(vJob->second) : -1; 74 auto jobNum = vJob != arguments->end() ? std::get<int>(vJob->second) : -1;
74 auto job = new PrintJob{&printing, jobNum}; 75 auto job = new PrintJob{&printing, jobNum};
@@ -78,14 +79,31 @@ class PrintingPlugin : public flutter::Plugin { @@ -78,14 +79,31 @@ class PrintingPlugin : public flutter::Plugin {
78 } 79 }
79 result->Success(flutter::EncodableValue(res ? 1 : 0)); 80 result->Success(flutter::EncodableValue(res ? 1 : 0));
80 } else if (method_call.method_name().compare("directPrintPdf") == 0) { 81 } else if (method_call.method_name().compare("directPrintPdf") == 0) {
81 - auto job = std::make_unique<PrintJob>(&printing, -1);  
82 - job->directPrintPdf("name", std::vector<uint8_t>{}, "withPrinter");  
83 - result->Success(nullptr); 82 + const auto* arguments =
  83 + std::get_if<flutter::EncodableMap>(method_call.arguments());
  84 + auto vName = arguments->find(flutter::EncodableValue("name"));
  85 + auto name = vName != arguments->end() && !vName->second.IsNull()
  86 + ? std::get<std::string>(vName->second)
  87 + : std::string{"document"};
  88 + auto vDoc = arguments->find(flutter::EncodableValue("doc"));
  89 + auto doc = vDoc != arguments->end()
  90 + ? std::get<std::vector<uint8_t>>(vDoc->second)
  91 + : std::vector<uint8_t>{};
  92 + auto vPrinter = arguments->find(flutter::EncodableValue("printer"));
  93 + auto printer = vPrinter != arguments->end()
  94 + ? std::get<std::string>(vPrinter->second)
  95 + : std::string{};
  96 + auto vJob = arguments->find(flutter::EncodableValue("job"));
  97 + auto jobNum = vJob != arguments->end() ? std::get<int>(vJob->second) : -1;
  98 + auto job = std::make_unique<PrintJob>(&printing, jobNum);
  99 + auto res = job->directPrintPdf(name, doc, printer);
  100 + result->Success(flutter::EncodableValue(1));
  101 + printing.onCompleted(job.get(), res, "");
84 } else if (method_call.method_name().compare("sharePdf") == 0) { 102 } else if (method_call.method_name().compare("sharePdf") == 0) {
85 const auto* arguments = 103 const auto* arguments =
86 std::get_if<flutter::EncodableMap>(method_call.arguments()); 104 std::get_if<flutter::EncodableMap>(method_call.arguments());
87 auto vName = arguments->find(flutter::EncodableValue("name")); 105 auto vName = arguments->find(flutter::EncodableValue("name"));
88 - auto name = vName != arguments->end() 106 + auto name = vName != arguments->end() && !vName->second.IsNull()
89 ? std::get<std::string>(vName->second) 107 ? std::get<std::string>(vName->second)
90 : std::string{"document.pdf"}; 108 : std::string{"document.pdf"};
91 auto vDoc = arguments->find(flutter::EncodableValue("doc")); 109 auto vDoc = arguments->find(flutter::EncodableValue("doc"));
@@ -95,10 +113,23 @@ class PrintingPlugin : public flutter::Plugin { @@ -95,10 +113,23 @@ class PrintingPlugin : public flutter::Plugin {
95 auto job = std::make_unique<PrintJob>(&printing, -1); 113 auto job = std::make_unique<PrintJob>(&printing, -1);
96 auto res = job->sharePdf(doc, name); 114 auto res = job->sharePdf(doc, name);
97 result->Success(flutter::EncodableValue(res ? 1 : 0)); 115 result->Success(flutter::EncodableValue(res ? 1 : 0));
98 - } else if (method_call.method_name().compare("pickPrinter") == 0) { 116 + } else if (method_call.method_name().compare("listPrinters") == 0) {
99 auto job = std::make_unique<PrintJob>(&printing, -1); 117 auto job = std::make_unique<PrintJob>(&printing, -1);
100 - job->pickPrinter(nullptr);  
101 - result->Success(nullptr); 118 + auto printers = job->listPrinters();
  119 + auto pl = flutter::EncodableList{};
  120 + for (auto printer : printers) {
  121 + auto mp = flutter::EncodableMap{};
  122 + mp[flutter::EncodableValue("name")] =
  123 + flutter::EncodableValue(printer.name);
  124 + mp[flutter::EncodableValue("url")] =
  125 + flutter::EncodableValue(printer.url);
  126 + mp[flutter::EncodableValue("model")] =
  127 + flutter::EncodableValue(printer.model);
  128 + mp[flutter::EncodableValue("description")] =
  129 + flutter::EncodableValue(printer.description);
  130 + pl.push_back(mp);
  131 + }
  132 + result->Success(pl);
102 } else if (method_call.method_name().compare("rasterPdf") == 0) { 133 } else if (method_call.method_name().compare("rasterPdf") == 0) {
103 const auto* arguments = 134 const auto* arguments =
104 std::get_if<flutter::EncodableMap>(method_call.arguments()); 135 std::get_if<flutter::EncodableMap>(method_call.arguments());
@@ -106,18 +137,21 @@ class PrintingPlugin : public flutter::Plugin { @@ -106,18 +137,21 @@ class PrintingPlugin : public flutter::Plugin {
106 auto doc = vDoc != arguments->end() 137 auto doc = vDoc != arguments->end()
107 ? std::get<std::vector<uint8_t>>(vDoc->second) 138 ? std::get<std::vector<uint8_t>>(vDoc->second)
108 : std::vector<uint8_t>{}; 139 : std::vector<uint8_t>{};
109 - // auto vPages = arguments->find(flutter::EncodableValue("pages"));  
110 - // auto pages = vPages != arguments->end()  
111 - // ? std::get<flutter  
112 - // ::EncodableList>(vPages->second) : flutter  
113 - // ::EncodableList{}; 140 + auto vPages = arguments->find(flutter::EncodableValue("pages"));
  141 + auto lPages = vPages != arguments->end() && !vPages->second.IsNull()
  142 + ? std::get<flutter::EncodableList>(vPages->second)
  143 + : flutter::EncodableList{};
  144 + auto pages = std::vector<int>{};
  145 + for (auto page : lPages) {
  146 + pages.push_back(std::get<int>(page));
  147 + }
114 auto vScale = arguments->find(flutter::EncodableValue("scale")); 148 auto vScale = arguments->find(flutter::EncodableValue("scale"));
115 auto scale = 149 auto scale =
116 vScale != arguments->end() ? std::get<double>(vScale->second) : 1; 150 vScale != arguments->end() ? std::get<double>(vScale->second) : 1;
117 auto vJob = arguments->find(flutter::EncodableValue("job")); 151 auto vJob = arguments->find(flutter::EncodableValue("job"));
118 auto jobNum = vJob != arguments->end() ? std::get<int>(vJob->second) : -1; 152 auto jobNum = vJob != arguments->end() ? std::get<int>(vJob->second) : -1;
119 auto job = std::make_unique<PrintJob>(&printing, jobNum); 153 auto job = std::make_unique<PrintJob>(&printing, jobNum);
120 - job->rasterPdf(doc, std::vector<int>{}, scale); 154 + job->rasterPdf(doc, pages, scale);
121 result->Success(nullptr); 155 result->Success(nullptr);
122 } else if (method_call.method_name().compare("printingInfo") == 0) { 156 } else if (method_call.method_name().compare("printingInfo") == 0) {
123 auto job = std::make_unique<PrintJob>(&printing, -1); 157 auto job = std::make_unique<PrintJob>(&printing, -1);