print_job.cc 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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 <linux/memfd.h>
  18. #include <stdlib.h>
  19. #include <sys/syscall.h>
  20. #include <sys/types.h>
  21. #include <sys/wait.h>
  22. #include <unistd.h>
  23. #include <cstring>
  24. #include <string>
  25. #include <fpdfview.h>
  26. print_job::print_job(int index) : index(index) {}
  27. print_job::~print_job() {}
  28. static gboolean add_printer(GtkPrinter* printer, gpointer data) {
  29. auto printers = static_cast<FlValue*>(data);
  30. auto map = fl_value_new_map();
  31. auto name = gtk_printer_get_name(printer);
  32. auto loc = gtk_printer_get_location(printer);
  33. auto cmt = gtk_printer_get_description(printer);
  34. fl_value_set_string(map, "url", fl_value_new_string(name));
  35. fl_value_set_string(map, "name", fl_value_new_string(name));
  36. if (loc) {
  37. fl_value_set_string(map, "location", fl_value_new_string(loc));
  38. }
  39. if (cmt) {
  40. fl_value_set_string(map, "comment", fl_value_new_string(cmt));
  41. }
  42. fl_value_set_string(map, "default",
  43. fl_value_new_bool(gtk_printer_is_default(printer)));
  44. fl_value_set_string(map, "available",
  45. fl_value_new_bool(gtk_printer_is_active(printer) &&
  46. gtk_printer_accepts_pdf(printer)));
  47. fl_value_append(printers, map);
  48. return false;
  49. }
  50. FlValue* print_job::list_printers() {
  51. auto printers = fl_value_new_list();
  52. gtk_enumerate_printers(add_printer, printers, nullptr, true);
  53. return printers;
  54. }
  55. static GtkPrinter* _printer;
  56. static gboolean search_printer(GtkPrinter* printer, gpointer data) {
  57. auto search = static_cast<gchar*>(data);
  58. auto name = gtk_printer_get_name(printer);
  59. if (strcmp(name, search) == 0) {
  60. _printer = static_cast<GtkPrinter*>(g_object_ref(printer));
  61. return true;
  62. }
  63. return false;
  64. }
  65. bool print_job::direct_print_pdf(const gchar* name,
  66. const uint8_t data[],
  67. size_t size,
  68. const gchar* printer) {
  69. _printer = nullptr;
  70. auto pname = strdup(printer);
  71. gtk_enumerate_printers(search_printer, pname, nullptr, true);
  72. free(pname);
  73. if (!_printer) {
  74. return false;
  75. }
  76. auto settings = gtk_print_settings_new();
  77. auto setup = gtk_page_setup_new();
  78. printJob = gtk_print_job_new(name, _printer, settings, setup);
  79. this->write_job(data, size);
  80. g_object_unref(_printer);
  81. g_object_unref(settings);
  82. g_object_unref(setup);
  83. g_object_unref(printJob);
  84. return true;
  85. }
  86. static void job_completed(GtkPrintJob* gtk_print_job,
  87. gpointer user_data,
  88. const GError* error) {
  89. auto job = static_cast<print_job*>(user_data);
  90. if (job->dialog) {
  91. gtk_widget_destroy(GTK_WIDGET(job->dialog));
  92. }
  93. on_completed(job, error == nullptr,
  94. error != nullptr ? error->message : nullptr);
  95. }
  96. bool print_job::print_pdf(const gchar* name,
  97. const gchar* printer,
  98. double pageWidth,
  99. double pageHeight,
  100. double marginLeft,
  101. double marginTop,
  102. double marginRight,
  103. double marginBottom) {
  104. GtkPrintSettings* settings;
  105. GtkPageSetup* setup;
  106. if (printer != nullptr) {
  107. _printer = nullptr;
  108. auto pname = strdup(printer);
  109. gtk_enumerate_printers(search_printer, pname, nullptr, true);
  110. free(pname);
  111. if (!_printer) {
  112. on_completed(this, false, "Printer not found");
  113. return false;
  114. }
  115. settings = gtk_print_settings_new();
  116. setup = gtk_page_setup_new();
  117. } else {
  118. dialog = GTK_PRINT_UNIX_DIALOG(gtk_print_unix_dialog_new(name, nullptr));
  119. gtk_print_unix_dialog_set_manual_capabilities(
  120. dialog, (GtkPrintCapabilities)(GTK_PRINT_CAPABILITY_GENERATE_PDF));
  121. gtk_print_unix_dialog_set_embed_page_setup(dialog, true);
  122. gtk_print_unix_dialog_set_support_selection(dialog, false);
  123. gtk_widget_realize(GTK_WIDGET(dialog));
  124. auto loop = true;
  125. while (loop) {
  126. auto response = gtk_dialog_run(GTK_DIALOG(dialog));
  127. switch (response) {
  128. case GTK_RESPONSE_OK: {
  129. _printer = gtk_print_unix_dialog_get_selected_printer(
  130. GTK_PRINT_UNIX_DIALOG(dialog));
  131. settings =
  132. gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog));
  133. setup = gtk_print_unix_dialog_get_page_setup(
  134. GTK_PRINT_UNIX_DIALOG(dialog));
  135. gtk_widget_hide(GTK_WIDGET(dialog));
  136. loop = false;
  137. } break;
  138. case GTK_RESPONSE_APPLY: // Preview
  139. break;
  140. default: // Cancel
  141. gtk_widget_destroy(GTK_WIDGET(dialog));
  142. on_completed(this, false, nullptr);
  143. return true;
  144. }
  145. }
  146. }
  147. if (!gtk_printer_accepts_pdf(_printer)) {
  148. on_completed(this, false, "This printer does not accept PDF jobs");
  149. g_object_unref(_printer);
  150. g_object_unref(settings);
  151. g_object_unref(setup);
  152. return false;
  153. }
  154. auto _width = gtk_page_setup_get_paper_width(setup, GTK_UNIT_POINTS);
  155. auto _height = gtk_page_setup_get_paper_height(setup, GTK_UNIT_POINTS);
  156. auto _marginLeft = gtk_page_setup_get_left_margin(setup, GTK_UNIT_POINTS);
  157. auto _marginTop = gtk_page_setup_get_top_margin(setup, GTK_UNIT_POINTS);
  158. auto _marginRight = gtk_page_setup_get_right_margin(setup, GTK_UNIT_POINTS);
  159. auto _marginBottom = gtk_page_setup_get_bottom_margin(setup, GTK_UNIT_POINTS);
  160. printJob = gtk_print_job_new(name, _printer, settings, setup);
  161. on_layout(this, _width, _height, _marginLeft, _marginTop, _marginRight,
  162. _marginBottom);
  163. g_object_unref(_printer);
  164. g_object_unref(settings);
  165. g_object_unref(setup);
  166. return true;
  167. }
  168. void print_job::write_job(const uint8_t data[], size_t size) {
  169. auto fd = syscall(SYS_memfd_create, "printing", 0);
  170. size_t offset = 0;
  171. ssize_t n;
  172. while ((n = write(fd, data + offset, size - offset)) >= 0 &&
  173. size - offset > 0) {
  174. offset += n;
  175. }
  176. if (n < 0) {
  177. on_completed(this, false, "Unable to copy the PDF data");
  178. }
  179. lseek(fd, 0, SEEK_SET);
  180. gtk_print_job_set_source_fd(printJob, fd, nullptr);
  181. gtk_print_job_send(printJob, job_completed, this, nullptr);
  182. }
  183. void print_job::cancel_job(const gchar* error) {}
  184. bool print_job::share_pdf(const uint8_t data[],
  185. size_t size,
  186. const gchar* name) {
  187. auto filename = "/tmp/" + std::string(name);
  188. auto fd = fopen(filename.c_str(), "wb");
  189. fwrite(data, size, 1, fd);
  190. fclose(fd);
  191. auto pid = fork();
  192. if (pid < 0) { // error occurred
  193. return false;
  194. } else if (pid == 0) { // child process
  195. execlp("xdg-open", "xdg-open", filename.c_str(), nullptr);
  196. }
  197. int status = 0;
  198. waitpid(pid, &status, 0);
  199. return status == 0;
  200. }
  201. void print_job::raster_pdf(const uint8_t data[],
  202. size_t size,
  203. const int32_t pages[],
  204. size_t pages_count,
  205. double scale) {
  206. FPDF_LIBRARY_CONFIG config;
  207. config.version = 2;
  208. config.m_pUserFontPaths = nullptr;
  209. config.m_pIsolate = nullptr;
  210. config.m_v8EmbedderSlot = 0;
  211. FPDF_InitLibraryWithConfig(&config);
  212. auto doc = FPDF_LoadMemDocument64(data, size, nullptr);
  213. if (!doc) {
  214. FPDF_DestroyLibrary();
  215. on_page_raster_end(this, "Cannot raster a malformed PDF file");
  216. return;
  217. }
  218. auto pageCount = FPDF_GetPageCount(doc);
  219. auto allPages = false;
  220. if (pages_count == 0) {
  221. allPages = true;
  222. pages_count = pageCount;
  223. }
  224. for (auto pn = 0; pn < pages_count; pn++) {
  225. auto n = allPages ? pn : pages[pn];
  226. if (n >= pageCount) {
  227. continue;
  228. }
  229. auto page = FPDF_LoadPage(doc, n);
  230. if (!page) {
  231. continue;
  232. }
  233. auto width = FPDF_GetPageWidth(page);
  234. auto height = FPDF_GetPageHeight(page);
  235. auto bWidth = static_cast<int>(width * scale);
  236. auto bHeight = static_cast<int>(height * scale);
  237. auto bitmap = FPDFBitmap_Create(bWidth, bHeight, 0);
  238. FPDFBitmap_FillRect(bitmap, 0, 0, bWidth, bHeight, 0xffffffff);
  239. FPDF_RenderPageBitmap(bitmap, page, 0, 0, bWidth, bHeight, 0,
  240. FPDF_ANNOT | FPDF_LCD_TEXT | FPDF_NO_NATIVETEXT);
  241. uint8_t* p = static_cast<uint8_t*>(FPDFBitmap_GetBuffer(bitmap));
  242. auto stride = FPDFBitmap_GetStride(bitmap);
  243. size_t l = static_cast<size_t>(bHeight * stride);
  244. // BGRA to RGBA conversion
  245. for (auto y = 0; y < bHeight; y++) {
  246. auto offset = y * stride;
  247. for (auto x = 0; x < bWidth; x++) {
  248. auto t = p[offset];
  249. p[offset] = p[offset + 2];
  250. p[offset + 2] = t;
  251. offset += 4;
  252. }
  253. }
  254. on_page_rasterized(this, p, l, bWidth, bHeight);
  255. FPDFBitmap_Destroy(bitmap);
  256. FPDF_ClosePage(page);
  257. }
  258. FPDF_CloseDocument(doc);
  259. FPDF_DestroyLibrary();
  260. on_page_raster_end(this, nullptr);
  261. }
  262. FlValue* print_job::printing_info() {
  263. FlValue* result = fl_value_new_map();
  264. fl_value_set_string(result, "canPrint", fl_value_new_bool(true));
  265. fl_value_set_string(result, "canShare", fl_value_new_bool(true));
  266. fl_value_set_string(result, "canRaster", fl_value_new_bool(true));
  267. fl_value_set_string(result, "canListPrinters", fl_value_new_bool(true));
  268. fl_value_set_string(result, "directPrint", fl_value_new_bool(true));
  269. fl_value_set_string(result, "dynamicLayout", fl_value_new_bool(true));
  270. return result;
  271. }