Showing
10 changed files
with
723 additions
and
1 deletions
| @@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
| 13 | # limitations under the License. | 13 | # limitations under the License. |
| 14 | 14 | ||
| 15 | DART_SRC=$(shell find . -name '*.dart') | 15 | DART_SRC=$(shell find . -name '*.dart') |
| 16 | -CLNG_SRC=$(shell find printing/ios printing/macos printing/windows printing/android -name '*.cpp' -o -name '*.m' -o -name '*.h' -o -name '*.java') | 16 | +CLNG_SRC=$(shell find printing/ios printing/macos printing/windows printing/linux printing/android -name '*.cpp' -o -name '*.cc' -o -name '*.m' -o -name '*.h' -o -name '*.java') |
| 17 | SWFT_SRC=$(shell find printing/ios printing/macos -name '*.swift') | 17 | SWFT_SRC=$(shell find printing/ios printing/macos -name '*.swift') |
| 18 | FONTS=pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf demo/assets/roboto1.ttf demo/assets/roboto2.ttf demo/assets/roboto3.ttf demo/assets/open-sans.ttf demo/assets/open-sans-bold.ttf pdf/hacen-tunisia.ttf pdf/material.ttf demo/assets/material.ttf | 18 | FONTS=pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf demo/assets/roboto1.ttf demo/assets/roboto2.ttf demo/assets/roboto3.ttf demo/assets/open-sans.ttf demo/assets/open-sans-bold.ttf pdf/hacen-tunisia.ttf pdf/material.ttf demo/assets/material.ttf |
| 19 | COV_PORT=9292 | 19 | COV_PORT=9292 |
printing/linux/CMakeLists.txt
0 → 100644
| 1 | +# Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com> | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
| 4 | +# use this file except in compliance with the License. You may obtain a copy of | ||
| 5 | +# the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
| 11 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
| 12 | +# License for the specific language governing permissions and limitations under | ||
| 13 | +# the License. | ||
| 14 | + | ||
| 15 | +cmake_minimum_required(VERSION 3.10) | ||
| 16 | +set(PROJECT_NAME "printing") | ||
| 17 | +project(${PROJECT_NAME} LANGUAGES CXX) | ||
| 18 | + | ||
| 19 | +# Download pdfium | ||
| 20 | +include(../windows/DownloadProject.cmake) | ||
| 21 | +download_project( | ||
| 22 | + PROJ | ||
| 23 | + pdfium | ||
| 24 | + URL | ||
| 25 | + https://github.com/bblanchon/pdfium-binaries/releases/latest/download/pdfium-linux.tgz | ||
| 26 | + ) | ||
| 27 | + | ||
| 28 | +# This value is used when generating builds using this plugin, so it must not be | ||
| 29 | +# changed | ||
| 30 | +set(PLUGIN_NAME "printing_plugin") | ||
| 31 | + | ||
| 32 | +include(${pdfium_SOURCE_DIR}/PDFiumConfig.cmake) | ||
| 33 | + | ||
| 34 | +# System-level dependencies. | ||
| 35 | +find_package(PkgConfig REQUIRED) | ||
| 36 | +pkg_check_modules(GTKUnixPrint REQUIRED IMPORTED_TARGET gtk+-unix-print-3.0) | ||
| 37 | + | ||
| 38 | +add_library(${PLUGIN_NAME} SHARED | ||
| 39 | + "printing_plugin.cc" | ||
| 40 | + "include/printing/printing_plugin.h" | ||
| 41 | + "print_job.cc" | ||
| 42 | + "print_job.h" | ||
| 43 | + ) | ||
| 44 | + | ||
| 45 | +apply_standard_settings(${PLUGIN_NAME}) | ||
| 46 | +set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) | ||
| 47 | +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) | ||
| 48 | +target_include_directories(${PLUGIN_NAME} | ||
| 49 | + INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") | ||
| 50 | +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) | ||
| 51 | +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK PkgConfig::GTKUnixPrint) | ||
| 52 | +target_link_libraries(${PLUGIN_NAME} PRIVATE pdfium) | ||
| 53 | + | ||
| 54 | +# List of absolute paths to libraries that should be bundled with the plugin | ||
| 55 | +set(printing_bundled_libraries | ||
| 56 | + "${PDFium_LIBRARY}" | ||
| 57 | + PARENT_SCOPE) |
| 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 | + | ||
| 17 | +#ifndef FLUTTER_PLUGIN_PRINTING_PLUGIN_H_ | ||
| 18 | +#define FLUTTER_PLUGIN_PRINTING_PLUGIN_H_ | ||
| 19 | + | ||
| 20 | +#include <flutter_linux/flutter_linux.h> | ||
| 21 | + | ||
| 22 | +G_BEGIN_DECLS | ||
| 23 | + | ||
| 24 | +#ifdef FLUTTER_PLUGIN_IMPL | ||
| 25 | +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) | ||
| 26 | +#else | ||
| 27 | +#define FLUTTER_PLUGIN_EXPORT | ||
| 28 | +#endif | ||
| 29 | + | ||
| 30 | +typedef struct _PrintingPlugin PrintingPlugin; | ||
| 31 | +typedef struct { | ||
| 32 | + GObjectClass parent_class; | ||
| 33 | +} PrintingPluginClass; | ||
| 34 | + | ||
| 35 | +FLUTTER_PLUGIN_EXPORT GType printing_plugin_get_type(); | ||
| 36 | + | ||
| 37 | +FLUTTER_PLUGIN_EXPORT void printing_plugin_register_with_registrar( | ||
| 38 | + FlPluginRegistrar* registrar); | ||
| 39 | + | ||
| 40 | +G_END_DECLS | ||
| 41 | + | ||
| 42 | +#endif // FLUTTER_PLUGIN_PRINTING_PLUGIN_H_ |
printing/linux/print_job.cc
0 → 100644
| 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 | + | ||
| 17 | +#include "print_job.h" | ||
| 18 | + | ||
| 19 | +#include <stdlib.h> | ||
| 20 | +#include <sys/mman.h> | ||
| 21 | +#include <sys/types.h> | ||
| 22 | +#include <sys/wait.h> | ||
| 23 | +#include <string> | ||
| 24 | + | ||
| 25 | +#include <fpdfview.h> | ||
| 26 | + | ||
| 27 | +print_job::print_job(int index) : index(index) {} | ||
| 28 | + | ||
| 29 | +print_job::~print_job() {} | ||
| 30 | + | ||
| 31 | +static gboolean add_printer(GtkPrinter* printer, gpointer data) { | ||
| 32 | + auto printers = static_cast<FlValue*>(data); | ||
| 33 | + | ||
| 34 | + auto map = fl_value_new_map(); | ||
| 35 | + auto name = gtk_printer_get_name(printer); | ||
| 36 | + auto loc = gtk_printer_get_location(printer); | ||
| 37 | + auto cmt = gtk_printer_get_description(printer); | ||
| 38 | + | ||
| 39 | + fl_value_set_string(map, "url", fl_value_new_string(name)); | ||
| 40 | + fl_value_set_string(map, "name", fl_value_new_string(name)); | ||
| 41 | + if (loc) { | ||
| 42 | + fl_value_set_string(map, "location", fl_value_new_string(loc)); | ||
| 43 | + } | ||
| 44 | + if (cmt) { | ||
| 45 | + fl_value_set_string(map, "comment", fl_value_new_string(cmt)); | ||
| 46 | + } | ||
| 47 | + fl_value_set_string(map, "default", | ||
| 48 | + fl_value_new_bool(gtk_printer_is_default(printer))); | ||
| 49 | + fl_value_set_string(map, "available", | ||
| 50 | + fl_value_new_bool(gtk_printer_is_active(printer) && | ||
| 51 | + gtk_printer_accepts_pdf(printer))); | ||
| 52 | + | ||
| 53 | + fl_value_append(printers, map); | ||
| 54 | + return false; | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +FlValue* print_job::list_printers() { | ||
| 58 | + auto printers = fl_value_new_list(); | ||
| 59 | + gtk_enumerate_printers(add_printer, printers, nullptr, true); | ||
| 60 | + return printers; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +static GtkPrinter* _printer; | ||
| 64 | + | ||
| 65 | +static gboolean search_printer(GtkPrinter* printer, gpointer data) { | ||
| 66 | + auto search = static_cast<gchar*>(data); | ||
| 67 | + auto name = gtk_printer_get_name(printer); | ||
| 68 | + | ||
| 69 | + if (strcmp(name, search) == 0) { | ||
| 70 | + _printer = static_cast<GtkPrinter*>(g_object_ref(printer)); | ||
| 71 | + return true; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + return false; | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +bool print_job::direct_print_pdf(const gchar* name, | ||
| 78 | + const uint8_t data[], | ||
| 79 | + size_t size, | ||
| 80 | + const gchar* printer) { | ||
| 81 | + _printer = nullptr; | ||
| 82 | + auto pname = strdup(printer); | ||
| 83 | + gtk_enumerate_printers(search_printer, pname, nullptr, true); | ||
| 84 | + free(pname); | ||
| 85 | + | ||
| 86 | + if (!_printer) { | ||
| 87 | + return false; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + auto settings = gtk_print_settings_new(); | ||
| 91 | + auto setup = gtk_page_setup_new(); | ||
| 92 | + printJob = gtk_print_job_new(name, _printer, settings, setup); | ||
| 93 | + this->write_job(data, size); | ||
| 94 | + | ||
| 95 | + g_object_unref(_printer); | ||
| 96 | + g_object_unref(settings); | ||
| 97 | + g_object_unref(setup); | ||
| 98 | + g_object_unref(printJob); | ||
| 99 | + | ||
| 100 | + return true; | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +static void job_completed(GtkPrintJob* gtk_print_job, | ||
| 104 | + gpointer user_data, | ||
| 105 | + const GError* error) { | ||
| 106 | + auto job = static_cast<print_job*>(user_data); | ||
| 107 | + on_completed(job, error == nullptr, | ||
| 108 | + error != nullptr ? error->message : nullptr); | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | +bool print_job::print_pdf(const gchar* name, | ||
| 112 | + double pageWidth, | ||
| 113 | + double pageHeight, | ||
| 114 | + double marginLeft, | ||
| 115 | + double marginTop, | ||
| 116 | + double marginRight, | ||
| 117 | + double marginBottom) { | ||
| 118 | + documentName = strdup(name); | ||
| 119 | + auto dialog = | ||
| 120 | + GTK_PRINT_UNIX_DIALOG(gtk_print_unix_dialog_new(documentName, nullptr)); | ||
| 121 | + gtk_print_unix_dialog_set_manual_capabilities( | ||
| 122 | + dialog, (GtkPrintCapabilities)(GTK_PRINT_CAPABILITY_GENERATE_PDF)); | ||
| 123 | + | ||
| 124 | + gtk_widget_realize(GTK_WIDGET(dialog)); | ||
| 125 | + | ||
| 126 | + auto response = gtk_dialog_run(GTK_DIALOG(dialog)); | ||
| 127 | + gtk_widget_hide(GTK_WIDGET(dialog)); | ||
| 128 | + | ||
| 129 | + switch (response) { | ||
| 130 | + case GTK_RESPONSE_OK: { | ||
| 131 | + GtkPrinter* printer = gtk_print_unix_dialog_get_selected_printer( | ||
| 132 | + GTK_PRINT_UNIX_DIALOG(dialog)); | ||
| 133 | + if (!gtk_printer_accepts_pdf(printer)) { | ||
| 134 | + on_completed(this, false, "This printer does not accept PDF jobs"); | ||
| 135 | + break; | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + GtkPrintSettings* settings = | ||
| 139 | + gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog)); | ||
| 140 | + GtkPageSetup* setup = | ||
| 141 | + gtk_print_unix_dialog_get_page_setup(GTK_PRINT_UNIX_DIALOG(dialog)); | ||
| 142 | + | ||
| 143 | + auto width = gtk_page_setup_get_page_width(setup, GTK_UNIT_POINTS); | ||
| 144 | + auto height = gtk_page_setup_get_page_height(setup, GTK_UNIT_POINTS); | ||
| 145 | + auto marginLeft = gtk_page_setup_get_left_margin(setup, GTK_UNIT_POINTS); | ||
| 146 | + auto marginTop = gtk_page_setup_get_top_margin(setup, GTK_UNIT_POINTS); | ||
| 147 | + auto marginRight = | ||
| 148 | + gtk_page_setup_get_right_margin(setup, GTK_UNIT_POINTS); | ||
| 149 | + auto marginBottom = | ||
| 150 | + gtk_page_setup_get_bottom_margin(setup, GTK_UNIT_POINTS); | ||
| 151 | + | ||
| 152 | + printJob = gtk_print_job_new(name, printer, settings, setup); | ||
| 153 | + | ||
| 154 | + on_layout(this, width, height, marginLeft, marginTop, marginRight, | ||
| 155 | + marginBottom); | ||
| 156 | + | ||
| 157 | + g_object_unref(settings); | ||
| 158 | + gtk_widget_destroy(GTK_WIDGET(dialog)); | ||
| 159 | + | ||
| 160 | + return true; | ||
| 161 | + } | ||
| 162 | + case GTK_RESPONSE_DELETE_EVENT: // Fall through. | ||
| 163 | + case GTK_RESPONSE_CANCEL: // Cancel | ||
| 164 | + case GTK_RESPONSE_APPLY: // Preview | ||
| 165 | + break; | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + gtk_widget_destroy(GTK_WIDGET(dialog)); | ||
| 169 | + on_completed(this, false, nullptr); | ||
| 170 | + return true; | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | +void print_job::write_job(const uint8_t data[], size_t size) { | ||
| 174 | + auto fd = memfd_create("printing", 0); | ||
| 175 | + size_t offset = 0; | ||
| 176 | + size_t n; | ||
| 177 | + while ((n = write(fd, data + offset, size - offset)) >= 0 && | ||
| 178 | + size - offset > 0) { | ||
| 179 | + offset += n; | ||
| 180 | + } | ||
| 181 | + if (n < 0) { | ||
| 182 | + on_completed(this, false, "Unable to copy the PDF data"); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + lseek(fd, 0, SEEK_SET); | ||
| 186 | + | ||
| 187 | + gtk_print_job_set_source_fd(printJob, fd, nullptr); | ||
| 188 | + gtk_print_job_send(printJob, job_completed, this, nullptr); | ||
| 189 | +} | ||
| 190 | + | ||
| 191 | +void print_job::cancel_job(const gchar* error) {} | ||
| 192 | + | ||
| 193 | +bool print_job::share_pdf(const uint8_t data[], | ||
| 194 | + size_t size, | ||
| 195 | + const gchar* name) { | ||
| 196 | + auto filename = "/tmp/" + std::string(name); | ||
| 197 | + | ||
| 198 | + auto fd = fopen(filename.c_str(), "wb"); | ||
| 199 | + fwrite(data, size, 1, fd); | ||
| 200 | + fclose(fd); | ||
| 201 | + | ||
| 202 | + auto pid = fork(); | ||
| 203 | + | ||
| 204 | + if (pid < 0) { // error occurred | ||
| 205 | + return false; | ||
| 206 | + } else if (pid == 0) { // child process | ||
| 207 | + execlp("xdg-open", "xdg-open", filename.c_str(), nullptr); | ||
| 208 | + } | ||
| 209 | + | ||
| 210 | + int status = 0; | ||
| 211 | + waitpid(pid, &status, 0); | ||
| 212 | + | ||
| 213 | + return status == 0; | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +void print_job::raster_pdf(const uint8_t data[], | ||
| 217 | + size_t size, | ||
| 218 | + const int32_t pages[], | ||
| 219 | + size_t pages_count, | ||
| 220 | + double scale) { | ||
| 221 | + FPDF_InitLibraryWithConfig(nullptr); | ||
| 222 | + | ||
| 223 | + auto doc = FPDF_LoadMemDocument64(data, size, nullptr); | ||
| 224 | + if (!doc) { | ||
| 225 | + FPDF_DestroyLibrary(); | ||
| 226 | + on_page_raster_end(this); | ||
| 227 | + return; | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + auto pageCount = FPDF_GetPageCount(doc); | ||
| 231 | + auto allPages = false; | ||
| 232 | + | ||
| 233 | + if (pages_count == 0) { | ||
| 234 | + allPages = true; | ||
| 235 | + pages_count = pageCount; | ||
| 236 | + } | ||
| 237 | + | ||
| 238 | + for (auto pn = 0; pn < pages_count; pn++) { | ||
| 239 | + auto n = allPages ? pn : pages[pn]; | ||
| 240 | + if (n >= pageCount) { | ||
| 241 | + continue; | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + auto page = FPDF_LoadPage(doc, n); | ||
| 245 | + if (!page) { | ||
| 246 | + continue; | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + auto width = FPDF_GetPageWidth(page); | ||
| 250 | + auto height = FPDF_GetPageHeight(page); | ||
| 251 | + | ||
| 252 | + auto bWidth = static_cast<int>(width * scale); | ||
| 253 | + auto bHeight = static_cast<int>(height * scale); | ||
| 254 | + | ||
| 255 | + auto bitmap = FPDFBitmap_Create(bWidth, bHeight, 0); | ||
| 256 | + FPDFBitmap_FillRect(bitmap, 0, 0, bWidth, bHeight, 0xffffffff); | ||
| 257 | + | ||
| 258 | + FPDF_RenderPageBitmap(bitmap, page, 0, 0, bWidth, bHeight, 0, FPDF_ANNOT); | ||
| 259 | + | ||
| 260 | + uint8_t* p = static_cast<uint8_t*>(FPDFBitmap_GetBuffer(bitmap)); | ||
| 261 | + auto stride = FPDFBitmap_GetStride(bitmap); | ||
| 262 | + size_t l = static_cast<size_t>(bHeight * stride); | ||
| 263 | + | ||
| 264 | + // BGRA to RGBA conversion | ||
| 265 | + for (auto y = 0; y < bHeight; y++) { | ||
| 266 | + auto offset = y * stride; | ||
| 267 | + for (auto x = 0; x < bWidth; x++) { | ||
| 268 | + auto t = p[offset]; | ||
| 269 | + p[offset] = p[offset + 2]; | ||
| 270 | + p[offset + 2] = t; | ||
| 271 | + offset += 4; | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + on_page_rasterized(this, p, l, bWidth, bHeight); | ||
| 276 | + | ||
| 277 | + FPDFBitmap_Destroy(bitmap); | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + FPDF_CloseDocument(doc); | ||
| 281 | + | ||
| 282 | + FPDF_DestroyLibrary(); | ||
| 283 | + | ||
| 284 | + on_page_raster_end(this); | ||
| 285 | +} | ||
| 286 | + | ||
| 287 | +FlValue* print_job::printing_info() { | ||
| 288 | + FlValue* result = fl_value_new_map(); | ||
| 289 | + fl_value_set_string(result, "canPrint", fl_value_new_bool(true)); | ||
| 290 | + fl_value_set_string(result, "canShare", fl_value_new_bool(true)); | ||
| 291 | + fl_value_set_string(result, "canRaster", fl_value_new_bool(true)); | ||
| 292 | + fl_value_set_string(result, "canListPrinters", fl_value_new_bool(true)); | ||
| 293 | + fl_value_set_string(result, "directPrint", fl_value_new_bool(true)); | ||
| 294 | + fl_value_set_string(result, "dynamicLayout", fl_value_new_bool(true)); | ||
| 295 | + return result; | ||
| 296 | +} |
printing/linux/print_job.h
0 → 100644
| 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 | + | ||
| 17 | +#ifndef PRINTING_PLUGIN_PRINT_JOB_H_ | ||
| 18 | +#define PRINTING_PLUGIN_PRINT_JOB_H_ | ||
| 19 | + | ||
| 20 | +#include <flutter_linux/flutter_linux.h> | ||
| 21 | +#include <gtk/gtk.h> | ||
| 22 | +#include <gtk/gtkunixprint.h> | ||
| 23 | + | ||
| 24 | +class print_job { | ||
| 25 | + private: | ||
| 26 | + const int index; | ||
| 27 | + char* documentName = nullptr; | ||
| 28 | + GtkPrintJob* printJob; | ||
| 29 | + | ||
| 30 | + public: | ||
| 31 | + print_job(int index); | ||
| 32 | + | ||
| 33 | + ~print_job(); | ||
| 34 | + | ||
| 35 | + int get_id() { return index; }; | ||
| 36 | + | ||
| 37 | + static FlValue* list_printers(); | ||
| 38 | + | ||
| 39 | + bool direct_print_pdf(const gchar* name, | ||
| 40 | + const uint8_t data[], | ||
| 41 | + size_t size, | ||
| 42 | + const gchar* printer); | ||
| 43 | + | ||
| 44 | + bool print_pdf(const gchar* name, | ||
| 45 | + double pageWidth, | ||
| 46 | + double pageHeight, | ||
| 47 | + double marginLeft, | ||
| 48 | + double marginTop, | ||
| 49 | + double marginRight, | ||
| 50 | + double marginBottom); | ||
| 51 | + | ||
| 52 | + void write_job(const uint8_t data[], size_t size); | ||
| 53 | + | ||
| 54 | + void cancel_job(const gchar* error); | ||
| 55 | + | ||
| 56 | + static bool share_pdf(const uint8_t data[], size_t size, const gchar* name); | ||
| 57 | + | ||
| 58 | + void raster_pdf(const uint8_t data[], | ||
| 59 | + size_t size, | ||
| 60 | + const int32_t pages[], | ||
| 61 | + size_t pages_count, | ||
| 62 | + double scale); | ||
| 63 | + | ||
| 64 | + static FlValue* printing_info(); | ||
| 65 | +}; | ||
| 66 | + | ||
| 67 | +void on_page_rasterized(print_job* job, | ||
| 68 | + const uint8_t* data, | ||
| 69 | + size_t size, | ||
| 70 | + int width, | ||
| 71 | + int height); | ||
| 72 | + | ||
| 73 | +void on_page_raster_end(print_job* job); | ||
| 74 | + | ||
| 75 | +void on_layout(print_job* job, | ||
| 76 | + double pageWidth, | ||
| 77 | + double pageHeight, | ||
| 78 | + double marginLeft, | ||
| 79 | + double marginTop, | ||
| 80 | + double marginRight, | ||
| 81 | + double marginBottom); | ||
| 82 | + | ||
| 83 | +void on_completed(print_job* job, bool completed, const char* error); | ||
| 84 | + | ||
| 85 | +#endif // PRINTING_PLUGIN_PRINT_JOB_H_ |
printing/linux/printing_plugin.cc
0 → 100644
| 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 | + | ||
| 17 | +#include "include/printing/printing_plugin.h" | ||
| 18 | + | ||
| 19 | +#include <memory> | ||
| 20 | + | ||
| 21 | +#include <flutter_linux/flutter_linux.h> | ||
| 22 | +#include <gtk/gtk.h> | ||
| 23 | + | ||
| 24 | +#include "print_job.h" | ||
| 25 | + | ||
| 26 | +#define PRINTING_PLUGIN(obj) \ | ||
| 27 | + (G_TYPE_CHECK_INSTANCE_CAST((obj), printing_plugin_get_type(), \ | ||
| 28 | + PrintingPlugin)) | ||
| 29 | + | ||
| 30 | +struct _PrintingPlugin { | ||
| 31 | + GObject parent_instance; | ||
| 32 | +}; | ||
| 33 | + | ||
| 34 | +G_DEFINE_TYPE(PrintingPlugin, printing_plugin, g_object_get_type()) | ||
| 35 | + | ||
| 36 | +static FlMethodChannel* channel; | ||
| 37 | + | ||
| 38 | +// Called when a method call is received from Flutter. | ||
| 39 | +static void printing_plugin_handle_method_call(PrintingPlugin* self, | ||
| 40 | + FlMethodCall* method_call) { | ||
| 41 | + g_autoptr(FlMethodResponse) response = nullptr; | ||
| 42 | + | ||
| 43 | + const gchar* method = fl_method_call_get_name(method_call); | ||
| 44 | + FlValue* args = fl_method_call_get_args(method_call); | ||
| 45 | + | ||
| 46 | + if (strcmp(method, "printingInfo") == 0) { | ||
| 47 | + g_autoptr(FlValue) result = print_job::printing_info(); | ||
| 48 | + response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); | ||
| 49 | + | ||
| 50 | + } else if (strcmp(method, "listPrinters") == 0) { | ||
| 51 | + g_autoptr(FlValue) result = print_job::list_printers(); | ||
| 52 | + response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); | ||
| 53 | + | ||
| 54 | + } else if (strcmp(method, "printPdf") == 0) { | ||
| 55 | + auto name = fl_value_get_string(fl_value_lookup_string(args, "name")); | ||
| 56 | + auto jobNum = fl_value_get_int(fl_value_lookup_string(args, "job")); | ||
| 57 | + auto pageWidth = fl_value_get_float(fl_value_lookup_string(args, "width")); | ||
| 58 | + auto pageHeight = | ||
| 59 | + fl_value_get_float(fl_value_lookup_string(args, "height")); | ||
| 60 | + auto marginLeft = | ||
| 61 | + fl_value_get_float(fl_value_lookup_string(args, "marginLeft")); | ||
| 62 | + auto marginTop = | ||
| 63 | + fl_value_get_float(fl_value_lookup_string(args, "marginTop")); | ||
| 64 | + auto marginRight = | ||
| 65 | + fl_value_get_float(fl_value_lookup_string(args, "marginRight")); | ||
| 66 | + auto marginBottom = | ||
| 67 | + fl_value_get_float(fl_value_lookup_string(args, "marginBottom")); | ||
| 68 | + | ||
| 69 | + auto job = new print_job(jobNum); | ||
| 70 | + auto res = job->print_pdf(name, pageWidth, pageHeight, marginLeft, | ||
| 71 | + marginTop, marginRight, marginBottom); | ||
| 72 | + if (!res) { | ||
| 73 | + delete job; | ||
| 74 | + } | ||
| 75 | + g_autoptr(FlValue) result = fl_value_new_int(res); | ||
| 76 | + response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); | ||
| 77 | + | ||
| 78 | + } else if (strcmp(method, "directPrintPdf") == 0) { | ||
| 79 | + auto jobNum = fl_value_get_int(fl_value_lookup_string(args, "job")); | ||
| 80 | + auto name = fl_value_get_string(fl_value_lookup_string(args, "name")); | ||
| 81 | + auto printer = fl_value_get_string(fl_value_lookup_string(args, "printer")); | ||
| 82 | + auto doc = fl_value_get_uint8_list(fl_value_lookup_string(args, "doc")); | ||
| 83 | + auto size = fl_value_get_length(fl_value_lookup_string(args, "doc")); | ||
| 84 | + | ||
| 85 | + auto job = std::make_unique<print_job>(jobNum); | ||
| 86 | + auto res = job->direct_print_pdf(name, doc, size, printer); | ||
| 87 | + g_autoptr(FlValue) result = fl_value_new_int(res); | ||
| 88 | + response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); | ||
| 89 | + | ||
| 90 | + } else if (strcmp(method, "sharePdf") == 0) { | ||
| 91 | + auto name = fl_value_get_string(fl_value_lookup_string(args, "name")); | ||
| 92 | + auto doc = fl_value_get_uint8_list(fl_value_lookup_string(args, "doc")); | ||
| 93 | + auto size = fl_value_get_length(fl_value_lookup_string(args, "doc")); | ||
| 94 | + | ||
| 95 | + auto res = print_job::share_pdf(doc, size, name); | ||
| 96 | + g_autoptr(FlValue) result = fl_value_new_int(res); | ||
| 97 | + response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); | ||
| 98 | + | ||
| 99 | + } else if (strcmp(method, "rasterPdf") == 0) { | ||
| 100 | + auto doc = fl_value_get_uint8_list(fl_value_lookup_string(args, "doc")); | ||
| 101 | + auto size = fl_value_get_length(fl_value_lookup_string(args, "doc")); | ||
| 102 | + auto v_pages = fl_value_lookup_string(args, "pages"); | ||
| 103 | + int32_t* pages = nullptr; | ||
| 104 | + size_t pages_count = 0; | ||
| 105 | + if (fl_value_get_type(v_pages) == FL_VALUE_TYPE_LIST) { | ||
| 106 | + pages_count = fl_value_get_length(v_pages); | ||
| 107 | + pages = (int32_t*)malloc(sizeof(int32_t) * pages_count); | ||
| 108 | + for (auto n = 0; n < pages_count; n++) { | ||
| 109 | + pages[n] = fl_value_get_int(fl_value_get_list_value(v_pages, n)); | ||
| 110 | + } | ||
| 111 | + } | ||
| 112 | + auto scale = fl_value_get_float(fl_value_lookup_string(args, "scale")); | ||
| 113 | + auto jobNum = fl_value_get_int(fl_value_lookup_string(args, "job")); | ||
| 114 | + auto job = std::make_unique<print_job>(jobNum); | ||
| 115 | + job->raster_pdf(doc, size, pages, pages_count, scale); | ||
| 116 | + free(pages); | ||
| 117 | + | ||
| 118 | + g_autoptr(FlValue) result = fl_value_new_bool(true); | ||
| 119 | + response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); | ||
| 120 | + | ||
| 121 | + } else { | ||
| 122 | + response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + fl_method_call_respond(method_call, response, nullptr); | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +static void printing_plugin_dispose(GObject* object) { | ||
| 129 | + G_OBJECT_CLASS(printing_plugin_parent_class)->dispose(object); | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +static void printing_plugin_class_init(PrintingPluginClass* klass) { | ||
| 133 | + G_OBJECT_CLASS(klass)->dispose = printing_plugin_dispose; | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +static void printing_plugin_init(PrintingPlugin* self) {} | ||
| 137 | + | ||
| 138 | +static void method_call_cb(FlMethodChannel* channel, | ||
| 139 | + FlMethodCall* method_call, | ||
| 140 | + gpointer user_data) { | ||
| 141 | + PrintingPlugin* plugin = PRINTING_PLUGIN(user_data); | ||
| 142 | + printing_plugin_handle_method_call(plugin, method_call); | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +void printing_plugin_register_with_registrar(FlPluginRegistrar* registrar) { | ||
| 146 | + PrintingPlugin* plugin = | ||
| 147 | + PRINTING_PLUGIN(g_object_new(printing_plugin_get_type(), nullptr)); | ||
| 148 | + | ||
| 149 | + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); | ||
| 150 | + channel = fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), | ||
| 151 | + "net.nfet.printing", FL_METHOD_CODEC(codec)); | ||
| 152 | + fl_method_channel_set_method_call_handler( | ||
| 153 | + channel, method_call_cb, g_object_ref(plugin), g_object_unref); | ||
| 154 | + | ||
| 155 | + g_object_unref(plugin); | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +void on_page_rasterized(print_job* job, | ||
| 159 | + const uint8_t* data, | ||
| 160 | + size_t size, | ||
| 161 | + int width, | ||
| 162 | + int height) { | ||
| 163 | + g_autoptr(FlValue) map = fl_value_new_map(); | ||
| 164 | + fl_value_set_string(map, "image", fl_value_new_uint8_list(data, size)); | ||
| 165 | + fl_value_set_string(map, "width", fl_value_new_int(width)); | ||
| 166 | + fl_value_set_string(map, "height", fl_value_new_int(height)); | ||
| 167 | + fl_value_set_string(map, "job", fl_value_new_int(job->get_id())); | ||
| 168 | + | ||
| 169 | + fl_method_channel_invoke_method(channel, "onPageRasterized", map, nullptr, | ||
| 170 | + nullptr, nullptr); | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | +void on_page_raster_end(print_job* job) { | ||
| 174 | + g_autoptr(FlValue) map = fl_value_new_map(); | ||
| 175 | + fl_value_set_string(map, "job", fl_value_new_int(job->get_id())); | ||
| 176 | + | ||
| 177 | + fl_method_channel_invoke_method(channel, "onPageRasterEnd", map, nullptr, | ||
| 178 | + nullptr, nullptr); | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +static void on_layout_response_cb(GObject* object, | ||
| 182 | + GAsyncResult* result, | ||
| 183 | + gpointer user_data) { | ||
| 184 | + print_job* job = static_cast<print_job*>(user_data); | ||
| 185 | + g_autoptr(GError) error = nullptr; | ||
| 186 | + g_autoptr(FlMethodResponse) response = | ||
| 187 | + fl_method_channel_invoke_method_finish(channel, result, &error); | ||
| 188 | + if (!response) { | ||
| 189 | + job->cancel_job(error->message); | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + if (FL_IS_METHOD_SUCCESS_RESPONSE(response)) { | ||
| 193 | + FlValue* result = fl_method_success_response_get_result( | ||
| 194 | + FL_METHOD_SUCCESS_RESPONSE(response)); | ||
| 195 | + auto data = fl_value_get_uint8_list(result); | ||
| 196 | + auto size = fl_value_get_length(result); | ||
| 197 | + job->write_job(data, size); | ||
| 198 | + } else if (FL_IS_METHOD_ERROR_RESPONSE(response)) { | ||
| 199 | + FlMethodErrorResponse* error_response = FL_METHOD_ERROR_RESPONSE(response); | ||
| 200 | + // fl_method_error_response_get_code(error_response); | ||
| 201 | + auto message = fl_method_error_response_get_message(error_response); | ||
| 202 | + // fl_method_error_response_get_details(error_response); | ||
| 203 | + job->cancel_job(message); | ||
| 204 | + } | ||
| 205 | +} | ||
| 206 | + | ||
| 207 | +void on_layout(print_job* job, | ||
| 208 | + double pageWidth, | ||
| 209 | + double pageHeight, | ||
| 210 | + double marginLeft, | ||
| 211 | + double marginTop, | ||
| 212 | + double marginRight, | ||
| 213 | + double marginBottom) { | ||
| 214 | + g_autoptr(FlValue) map = fl_value_new_map(); | ||
| 215 | + fl_value_set_string(map, "job", fl_value_new_int(job->get_id())); | ||
| 216 | + fl_value_set_string(map, "width", fl_value_new_float(pageWidth)); | ||
| 217 | + fl_value_set_string(map, "height", fl_value_new_float(pageHeight)); | ||
| 218 | + fl_value_set_string(map, "marginLeft", fl_value_new_float(marginLeft)); | ||
| 219 | + fl_value_set_string(map, "marginTop", fl_value_new_float(marginTop)); | ||
| 220 | + fl_value_set_string(map, "marginRight", fl_value_new_float(marginRight)); | ||
| 221 | + fl_value_set_string(map, "marginBottom", fl_value_new_float(marginBottom)); | ||
| 222 | + | ||
| 223 | + fl_method_channel_invoke_method(channel, "onLayout", map, nullptr, | ||
| 224 | + on_layout_response_cb, job); | ||
| 225 | +} | ||
| 226 | + | ||
| 227 | +void on_completed(print_job* job, bool completed, const char* error) { | ||
| 228 | + g_autoptr(FlValue) map = fl_value_new_map(); | ||
| 229 | + fl_value_set_string(map, "job", fl_value_new_int(job->get_id())); | ||
| 230 | + fl_value_set_string(map, "completed", fl_value_new_bool(completed)); | ||
| 231 | + if (error != nullptr) { | ||
| 232 | + fl_value_set_string(map, "error", fl_value_new_string(error)); | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + fl_method_channel_invoke_method(channel, "onCompleted", map, nullptr, nullptr, | ||
| 236 | + nullptr); | ||
| 237 | +} |
| @@ -38,6 +38,8 @@ flutter: | @@ -38,6 +38,8 @@ flutter: | ||
| 38 | pluginClass: PrintingPlugin | 38 | pluginClass: PrintingPlugin |
| 39 | ios: | 39 | ios: |
| 40 | pluginClass: PrintingPlugin | 40 | pluginClass: PrintingPlugin |
| 41 | + linux: | ||
| 42 | + pluginClass: PrintingPlugin | ||
| 41 | macos: | 43 | macos: |
| 42 | pluginClass: PrintingPlugin | 44 | pluginClass: PrintingPlugin |
| 43 | web: | 45 | web: |
-
Please register or login to post a comment