print_job.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /*
  2. * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "print_job.h"
  17. #include "printing.h"
  18. #include <fpdfview.h>
  19. #include <objbase.h>
  20. #include <shlobj.h>
  21. #include <shlwapi.h>
  22. #include <tchar.h>
  23. #include <codecvt>
  24. #include <fstream>
  25. #include <iterator>
  26. #include <numeric>
  27. namespace nfet {
  28. const auto pdfDpi = 72;
  29. std::string toUtf8(std::wstring wstr) {
  30. int cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr,
  31. 0, nullptr, nullptr);
  32. LPSTR lpMultiByteStr = (LPSTR)malloc(cbMultiByte);
  33. cbMultiByte =
  34. WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, lpMultiByteStr,
  35. cbMultiByte, nullptr, nullptr);
  36. std::string ret = lpMultiByteStr;
  37. free(lpMultiByteStr);
  38. return ret;
  39. }
  40. std::string toUtf8(TCHAR* tstr) {
  41. #ifndef UNICODE
  42. #error "Non unicode build not supported"
  43. #endif
  44. if (!tstr) {
  45. return std::string{};
  46. }
  47. return toUtf8(std::wstring{tstr});
  48. }
  49. std::wstring fromUtf8(std::string str) {
  50. auto len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(),
  51. static_cast<int>(str.length()), nullptr, 0);
  52. if (len <= 0) {
  53. return L"";
  54. }
  55. auto wstr = std::wstring{};
  56. wstr.resize(len);
  57. MultiByteToWideChar(CP_UTF8, 0, str.c_str(), static_cast<int>(str.length()),
  58. &wstr[0], len);
  59. return wstr;
  60. }
  61. PrintJob::PrintJob(Printing* printing, int index)
  62. : printing{printing}, index{index} {}
  63. bool PrintJob::printPdf(const std::string& name,
  64. std::string printer,
  65. double width,
  66. double height,
  67. bool usePrinterSettings) {
  68. documentName = name;
  69. auto dm = static_cast<DEVMODE*>(GlobalAlloc(0, sizeof(DEVMODE)));
  70. if (usePrinterSettings) {
  71. dm = nullptr; // to use default driver config
  72. } else {
  73. ZeroMemory(dm, sizeof(DEVMODE));
  74. dm->dmSize = sizeof(DEVMODE);
  75. dm->dmFields =
  76. DM_ORIENTATION | DM_PAPERSIZE | DM_PAPERLENGTH | DM_PAPERWIDTH;
  77. dm->dmPaperSize = 0;
  78. if (width > height) {
  79. dm->dmOrientation = DMORIENT_LANDSCAPE;
  80. dm->dmPaperWidth = static_cast<short>(round(height * 254 / 72));
  81. dm->dmPaperLength = static_cast<short>(round(width * 254 / 72));
  82. } else {
  83. dm->dmOrientation = DMORIENT_PORTRAIT;
  84. dm->dmPaperWidth = static_cast<short>(round(width * 254 / 72));
  85. dm->dmPaperLength = static_cast<short>(round(height * 254 / 72));
  86. }
  87. }
  88. if (printer.empty()) {
  89. PRINTDLG pd;
  90. // Initialize PRINTDLG
  91. ZeroMemory(&pd, sizeof(pd));
  92. pd.lStructSize = sizeof(pd);
  93. // Initialize PRINTDLG
  94. pd.hwndOwner = nullptr;
  95. pd.hDevMode = dm;
  96. pd.hDevNames = nullptr; // Don't forget to free or store hDevNames.
  97. pd.hDC = nullptr;
  98. pd.Flags = PD_USEDEVMODECOPIES | PD_RETURNDC | PD_PRINTSETUP |
  99. PD_NOSELECTION | PD_NOPAGENUMS;
  100. pd.nCopies = 1;
  101. pd.nFromPage = 0xFFFF;
  102. pd.nToPage = 0xFFFF;
  103. pd.nMinPage = 1;
  104. pd.nMaxPage = 0xFFFF;
  105. auto r = PrintDlg(&pd);
  106. if (r != 1) {
  107. printing->onCompleted(this, false, "");
  108. DeleteDC(hDC);
  109. GlobalFree(hDevNames);
  110. ClosePrinter(hDevMode);
  111. return true;
  112. }
  113. hDC = pd.hDC;
  114. hDevMode = pd.hDevMode;
  115. hDevNames = pd.hDevNames;
  116. } else {
  117. hDC = CreateDC(TEXT("WINSPOOL"), fromUtf8(printer).c_str(), nullptr, dm);
  118. if (!hDC) {
  119. return false;
  120. }
  121. hDevMode = dm;
  122. hDevNames = nullptr;
  123. }
  124. auto dpiX = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSX)) / pdfDpi;
  125. auto dpiY = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSY)) / pdfDpi;
  126. auto pageWidth =
  127. static_cast<double>(GetDeviceCaps(hDC, PHYSICALWIDTH)) / dpiX;
  128. auto pageHeight =
  129. static_cast<double>(GetDeviceCaps(hDC, PHYSICALHEIGHT)) / dpiY;
  130. auto printableWidth = static_cast<double>(GetDeviceCaps(hDC, HORZRES)) / dpiX;
  131. auto printableHeight =
  132. static_cast<double>(GetDeviceCaps(hDC, VERTRES)) / dpiY;
  133. auto marginLeft =
  134. static_cast<double>(GetDeviceCaps(hDC, PHYSICALOFFSETX)) / dpiX;
  135. auto marginTop =
  136. static_cast<double>(GetDeviceCaps(hDC, PHYSICALOFFSETY)) / dpiY;
  137. auto marginRight = pageWidth - printableWidth - marginLeft;
  138. auto marginBottom = pageHeight - printableHeight - marginTop;
  139. printing->onLayout(this, pageWidth, pageHeight, marginLeft, marginTop,
  140. marginRight, marginBottom);
  141. return true;
  142. }
  143. std::vector<Printer> PrintJob::listPrinters() {
  144. LPTSTR defaultPrinter;
  145. DWORD size = 0;
  146. GetDefaultPrinter(nullptr, &size);
  147. defaultPrinter = static_cast<LPTSTR>(malloc(size * sizeof(TCHAR)));
  148. if (!GetDefaultPrinter(defaultPrinter, &size)) {
  149. size = 0;
  150. }
  151. auto printers = std::vector<Printer>{};
  152. DWORD needed = 0;
  153. DWORD returned = 0;
  154. const auto flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
  155. EnumPrinters(flags, nullptr, 2, nullptr, 0, &needed, &returned);
  156. auto buffer = (PRINTER_INFO_2*)malloc(needed);
  157. if (!buffer) {
  158. return printers;
  159. }
  160. auto result = EnumPrinters(flags, nullptr, 2, (LPBYTE)buffer, needed, &needed,
  161. &returned);
  162. if (result == 0) {
  163. free(buffer);
  164. return printers;
  165. }
  166. for (DWORD i = 0; i < returned; i++) {
  167. printers.push_back(Printer{
  168. toUtf8(buffer[i].pPrinterName), toUtf8(buffer[i].pPrinterName),
  169. toUtf8(buffer[i].pDriverName), toUtf8(buffer[i].pLocation),
  170. toUtf8(buffer[i].pComment),
  171. size > 0 && _tcsncmp(buffer[i].pPrinterName, defaultPrinter, size) == 0,
  172. (buffer[i].Status &
  173. (PRINTER_STATUS_NOT_AVAILABLE | PRINTER_STATUS_ERROR |
  174. PRINTER_STATUS_OFFLINE | PRINTER_STATUS_PAUSED)) == 0});
  175. }
  176. free(buffer);
  177. free(defaultPrinter);
  178. return printers;
  179. }
  180. void PrintJob::writeJob(std::vector<uint8_t> data) {
  181. auto dpiX = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSX)) / pdfDpi;
  182. auto dpiY = static_cast<double>(GetDeviceCaps(hDC, LOGPIXELSY)) / pdfDpi;
  183. DOCINFO docInfo;
  184. ZeroMemory(&docInfo, sizeof(docInfo));
  185. docInfo.cbSize = sizeof(docInfo);
  186. auto docName = fromUtf8(documentName);
  187. docInfo.lpszDocName = docName.c_str();
  188. auto r = StartDoc(hDC, &docInfo);
  189. FPDF_LIBRARY_CONFIG config;
  190. config.version = 2;
  191. config.m_pUserFontPaths = nullptr;
  192. config.m_pIsolate = nullptr;
  193. config.m_v8EmbedderSlot = 0;
  194. FPDF_InitLibraryWithConfig(&config);
  195. auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr);
  196. if (!doc) {
  197. FPDF_DestroyLibrary();
  198. return;
  199. }
  200. auto pages = FPDF_GetPageCount(doc);
  201. auto marginLeft = GetDeviceCaps(hDC, PHYSICALOFFSETX);
  202. auto marginTop = GetDeviceCaps(hDC, PHYSICALOFFSETY);
  203. for (auto pageNum = 0; pageNum < pages; pageNum++) {
  204. StartPage(hDC);
  205. auto page = FPDF_LoadPage(doc, pageNum);
  206. if (!page) {
  207. EndPage(hDC);
  208. continue;
  209. }
  210. auto pdfWidth = FPDF_GetPageWidth(page);
  211. auto pdfHeight = FPDF_GetPageHeight(page);
  212. int bWidth = static_cast<int>(pdfWidth * dpiX);
  213. int bHeight = static_cast<int>(pdfHeight * dpiY);
  214. FPDF_RenderPage(hDC, page, -marginLeft, -marginTop, bWidth, bHeight, 0,
  215. FPDF_ANNOT | FPDF_PRINTING);
  216. FPDF_ClosePage(page);
  217. r = EndPage(hDC);
  218. }
  219. FPDF_CloseDocument(doc);
  220. FPDF_DestroyLibrary();
  221. EndDoc(hDC);
  222. DeleteDC(hDC);
  223. GlobalFree(hDevNames);
  224. ClosePrinter(hDevMode);
  225. printing->onCompleted(this, true, "");
  226. }
  227. void PrintJob::cancelJob(const std::string& error) {}
  228. bool PrintJob::sharePdf(std::vector<uint8_t> data, const std::string& name) {
  229. TCHAR lpTempPathBuffer[MAX_PATH];
  230. auto ret = GetTempPath(MAX_PATH, lpTempPathBuffer);
  231. if (ret > MAX_PATH || (ret == 0)) {
  232. return false;
  233. }
  234. auto filename = fromUtf8(toUtf8(lpTempPathBuffer) + "\\" + name);
  235. auto output_file =
  236. std::basic_ofstream<uint8_t>{filename, std::ios::out | std::ios::binary};
  237. output_file.write(data.data(), data.size());
  238. output_file.close();
  239. SHELLEXECUTEINFO ShExecInfo;
  240. ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
  241. ShExecInfo.fMask = 0;
  242. ShExecInfo.hwnd = nullptr;
  243. ShExecInfo.lpVerb = TEXT("open");
  244. ShExecInfo.lpFile = filename.c_str();
  245. ShExecInfo.lpParameters = nullptr;
  246. ShExecInfo.lpDirectory = nullptr;
  247. ShExecInfo.nShow = SW_SHOWDEFAULT;
  248. ShExecInfo.hInstApp = nullptr;
  249. ret = ShellExecuteEx(&ShExecInfo);
  250. return ret == TRUE;
  251. }
  252. void PrintJob::pickPrinter(void* result) {}
  253. void PrintJob::rasterPdf(std::vector<uint8_t> data,
  254. std::vector<int> pages,
  255. double scale) {
  256. FPDF_LIBRARY_CONFIG config;
  257. config.version = 2;
  258. config.m_pUserFontPaths = nullptr;
  259. config.m_pIsolate = nullptr;
  260. config.m_v8EmbedderSlot = 0;
  261. FPDF_InitLibraryWithConfig(&config);
  262. auto doc = FPDF_LoadMemDocument64(data.data(), data.size(), nullptr);
  263. if (!doc) {
  264. FPDF_DestroyLibrary();
  265. printing->onPageRasterEnd(this, "Cannot raster a malformed PDF file");
  266. return;
  267. }
  268. auto pageCount = FPDF_GetPageCount(doc);
  269. if (pages.size() == 0) {
  270. // Use all pages
  271. pages.resize(pageCount);
  272. std::iota(std::begin(pages), std::end(pages), 0);
  273. }
  274. for (auto n : pages) {
  275. if (n >= pageCount) {
  276. continue;
  277. }
  278. auto page = FPDF_LoadPage(doc, n);
  279. if (!page) {
  280. continue;
  281. }
  282. auto width = FPDF_GetPageWidth(page);
  283. auto height = FPDF_GetPageHeight(page);
  284. auto bWidth = static_cast<int>(width * scale);
  285. auto bHeight = static_cast<int>(height * scale);
  286. auto bitmap = FPDFBitmap_Create(bWidth, bHeight, 0);
  287. FPDFBitmap_FillRect(bitmap, 0, 0, bWidth, bHeight, 0xffffffff);
  288. FPDF_RenderPageBitmap(bitmap, page, 0, 0, bWidth, bHeight, 0,
  289. FPDF_ANNOT | FPDF_LCD_TEXT);
  290. uint8_t* p = static_cast<uint8_t*>(FPDFBitmap_GetBuffer(bitmap));
  291. auto stride = FPDFBitmap_GetStride(bitmap);
  292. size_t l = static_cast<size_t>(bHeight * stride);
  293. // BGRA to RGBA conversion
  294. for (auto y = 0; y < bHeight; y++) {
  295. auto offset = y * stride;
  296. for (auto x = 0; x < bWidth; x++) {
  297. auto t = p[offset];
  298. p[offset] = p[offset + 2];
  299. p[offset + 2] = t;
  300. offset += 4;
  301. }
  302. }
  303. printing->onPageRasterized(std::vector<uint8_t>{p, p + l}, bWidth, bHeight,
  304. this);
  305. FPDFBitmap_Destroy(bitmap);
  306. FPDF_ClosePage(page);
  307. }
  308. FPDF_CloseDocument(doc);
  309. FPDF_DestroyLibrary();
  310. printing->onPageRasterEnd(this, "");
  311. }
  312. std::map<std::string, bool> PrintJob::printingInfo() {
  313. return std::map<std::string, bool>{
  314. {"directPrint", true}, {"dynamicLayout", true}, {"canPrint", true},
  315. {"canListPrinters", true}, {"canConvertHtml", false}, {"canShare", true},
  316. {"canRaster", true},
  317. };
  318. }
  319. } // namespace nfet