From 0db82a0c7706c0cab644a0808e09837858b33db1 Mon Sep 17 00:00:00 2001 From: palmoni5 Date: Tue, 1 Sep 2026 20:33:06 +0300 Subject: [PATCH 1/2] feat(linux): implement createPdf via the Otzaria WPE runtime print patch Resolves webkit_otzaria_web_view_print_to_pdf/_finish with dlsym, so the plugin builds against stock wpe-webkit headers and degrades gracefully (returns null) on an unpatched libWPEWebKit. Accepts the same pdfConfiguration.settings shape as the Windows implementation (pageWidth/pageHeight in inches, margins, orientation); defaults to A4 with 1cm margins. --- .../in_app_webview_controller.dart | 16 +++++ .../linux/in_app_webview/in_app_webview.cc | 62 +++++++++++++++++++ .../linux/in_app_webview/in_app_webview.h | 8 +++ .../webview_channel_delegate.cc | 57 +++++++++++++++++ 4 files changed, 143 insertions(+) diff --git a/flutter_inappwebview_linux/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_linux/lib/src/in_app_webview/in_app_webview_controller.dart index b4f5408839..838cfe6e78 100644 --- a/flutter_inappwebview_linux/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_linux/lib/src/in_app_webview/in_app_webview_controller.dart @@ -1861,6 +1861,22 @@ class LinuxInAppWebViewController extends PlatformInAppWebViewController return await channel?.invokeMethod('takeScreenshot', args); } + @override + Future createPdf({ + @Deprecated("Use pdfConfiguration instead") + // ignore: deprecated_member_use_from_same_package + IOSWKPDFConfiguration? iosWKPdfConfiguration, + PDFConfiguration? pdfConfiguration, + }) async { + Map args = {}; + args.putIfAbsent( + 'pdfConfiguration', + () => pdfConfiguration?.toMap() ?? iosWKPdfConfiguration?.toMap(), + ); + // מחזיר null כשה-libWPEWebKit שרץ אינו מה-runtime המטולא של אוצריא. + return await channel?.invokeMethod('createPdf', args); + } + @override Future saveState() async { Map args = {}; diff --git a/flutter_inappwebview_linux/linux/in_app_webview/in_app_webview.cc b/flutter_inappwebview_linux/linux/in_app_webview/in_app_webview.cc index 46c7e265cc..988709f0f2 100644 --- a/flutter_inappwebview_linux/linux/in_app_webview/in_app_webview.cc +++ b/flutter_inappwebview_linux/linux/in_app_webview/in_app_webview.cc @@ -2473,6 +2473,68 @@ void InAppWebView::takeScreenshot(std::function>&)> callback; + OtzariaPrintToPdfFinishFn finish; +}; + +} // namespace + +void InAppWebView::createPdf(double pageWidthPt, double pageHeightPt, + double marginTopPt, double marginRightPt, + double marginBottomPt, double marginLeftPt, + std::function>&)> callback) { + if (webview_ == nullptr || callback == nullptr) { + if (callback) { + callback(std::nullopt); + } + return; + } + + // הסימבולים קיימים רק ב-libWPEWebKit מה-runtime המטולא של אוצריא; + // בבנייה רגילה dlsym מחזיר null ואנחנו מדווחים "אין תמיכה" בשקט. + auto print_fn = reinterpret_cast( + dlsym(RTLD_DEFAULT, "webkit_otzaria_web_view_print_to_pdf")); + auto finish_fn = reinterpret_cast( + dlsym(RTLD_DEFAULT, "webkit_otzaria_web_view_print_to_pdf_finish")); + if (print_fn == nullptr || finish_fn == nullptr) { + callback(std::nullopt); + return; + } + + auto* call = new CreatePdfCall{std::move(callback), finish_fn}; + print_fn( + webview_, pageWidthPt, pageHeightPt, marginTopPt, marginRightPt, marginBottomPt, + marginLeftPt, nullptr, + [](GObject* source, GAsyncResult* result, gpointer user_data) { + std::unique_ptr call(static_cast(user_data)); + GError* error = nullptr; + GBytes* bytes = call->finish(WEBKIT_WEB_VIEW(source), result, &error); + if (error != nullptr || bytes == nullptr) { + if (error != nullptr) { + g_warning("createPdf failed: %s", error->message); + g_error_free(error); + } + call->callback(std::nullopt); + return; + } + gsize size = 0; + const auto* data = static_cast(g_bytes_get_data(bytes, &size)); + std::vector pdf(data, data + size); + g_bytes_unref(bytes); + call->callback(pdf); + }, + call); +} + // === Session State === std::optional> InAppWebView::saveState() const { diff --git a/flutter_inappwebview_linux/linux/in_app_webview/in_app_webview.h b/flutter_inappwebview_linux/linux/in_app_webview/in_app_webview.h index 23ad471ad9..eb94730ab5 100644 --- a/flutter_inappwebview_linux/linux/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_linux/linux/in_app_webview/in_app_webview.h @@ -219,6 +219,14 @@ class InAppWebView { // Screenshot - captures the current visible content as PNG data void takeScreenshot(std::function>&)> callback); + // PDF export via the Otzaria WPE runtime patch (webkit_otzaria_web_view_print_to_pdf, + // resolved with dlsym). Sizes are in PDF points; yields nullopt when the + // running libWPEWebKit lacks the symbol (unpatched build). + void createPdf(double pageWidthPt, double pageHeightPt, + double marginTopPt, double marginRightPt, + double marginBottomPt, double marginLeftPt, + std::function>&)> callback); + // Session state - save and restore navigation state std::optional> saveState() const; bool restoreState(const std::vector& stateData); diff --git a/flutter_inappwebview_linux/linux/in_app_webview/webview_channel_delegate.cc b/flutter_inappwebview_linux/linux/in_app_webview/webview_channel_delegate.cc index 62f7709c94..56243afa36 100644 --- a/flutter_inappwebview_linux/linux/in_app_webview/webview_channel_delegate.cc +++ b/flutter_inappwebview_linux/linux/in_app_webview/webview_channel_delegate.cc @@ -1,6 +1,7 @@ #include "webview_channel_delegate.h" #include +#include #include "../in_app_browser/in_app_browser.h" #include "../types/client_cert_challenge.h" @@ -621,6 +622,62 @@ void WebViewChannelDelegate::HandleMethodCall(FlMethodCall* method_call) { return; } + if (string_equals(methodName, "createPdf")) { + // ברירת מחדל: A4 עם שוליים 1 ס"מ. pdfConfiguration.settings עוקף — + // אותם שדות ויחידות (אינצ'ים) כמו במימוש של Windows. + double pageWidthIn = 8.2677, pageHeightIn = 11.6929; + double marginTopIn = 0.3937, marginRightIn = 0.3937; + double marginBottomIn = 0.3937, marginLeftIn = 0.3937; + bool landscape = false; + + auto asDouble = [](FlValue* v, double fallback) -> double { + if (v == nullptr) return fallback; + if (fl_value_get_type(v) == FL_VALUE_TYPE_FLOAT) return fl_value_get_float(v); + if (fl_value_get_type(v) == FL_VALUE_TYPE_INT) return static_cast(fl_value_get_int(v)); + return fallback; + }; + + if (args != nullptr && fl_value_get_type(args) == FL_VALUE_TYPE_MAP) { + FlValue* config = fl_value_lookup_string(args, "pdfConfiguration"); + FlValue* settings = (config != nullptr && fl_value_get_type(config) == FL_VALUE_TYPE_MAP) + ? fl_value_lookup_string(config, "settings") + : nullptr; + if (settings != nullptr && fl_value_get_type(settings) == FL_VALUE_TYPE_MAP) { + pageWidthIn = asDouble(fl_value_lookup_string(settings, "pageWidth"), pageWidthIn); + pageHeightIn = asDouble(fl_value_lookup_string(settings, "pageHeight"), pageHeightIn); + FlValue* orientation = fl_value_lookup_string(settings, "orientation"); + if (orientation != nullptr && fl_value_get_type(orientation) == FL_VALUE_TYPE_INT) { + landscape = fl_value_get_int(orientation) == 1; + } + FlValue* margins = fl_value_lookup_string(settings, "margins"); + if (margins != nullptr && fl_value_get_type(margins) == FL_VALUE_TYPE_MAP) { + marginTopIn = asDouble(fl_value_lookup_string(margins, "top"), marginTopIn); + marginRightIn = asDouble(fl_value_lookup_string(margins, "right"), marginRightIn); + marginBottomIn = asDouble(fl_value_lookup_string(margins, "bottom"), marginBottomIn); + marginLeftIn = asDouble(fl_value_lookup_string(margins, "left"), marginLeftIn); + } + } + } + if (landscape) { + std::swap(pageWidthIn, pageHeightIn); + } + + g_object_ref(method_call); + webView->createPdf( + pageWidthIn * 72.0, pageHeightIn * 72.0, marginTopIn * 72.0, marginRightIn * 72.0, + marginBottomIn * 72.0, marginLeftIn * 72.0, + [method_call](const std::optional>& result) { + if (result.has_value() && !result->empty()) { + g_autoptr(FlValue) val = fl_value_new_uint8_list(result->data(), result->size()); + fl_method_call_respond_success(method_call, val, nullptr); + } else { + fl_method_call_respond_success(method_call, nullptr, nullptr); + } + g_object_unref(method_call); + }); + return; + } + if (string_equals(methodName, "getSelectedText")) { // Capture method_call for async callback g_object_ref(method_call); From 2c1902ff0c986d09065a4c9cd2f4943934cea6f0 Mon Sep 17 00:00:00 2001 From: ypl <7353755@gmail.com> Date: Wed, 2 Sep 2026 13:44:00 +0300 Subject: [PATCH 2/2] fix(linux): advertise createPdf support --- .../platform_inappwebview_controller.dart | 4 ++++ .../platform_inappwebview_controller.g.dart | 3 +++ ...platform_inappwebview_controller_test.dart | 24 +++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 flutter_inappwebview_platform_interface/test/in_app_webview/platform_inappwebview_controller_test.dart diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index ecad45c413..52da6cd214 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -3241,6 +3241,10 @@ abstract class PlatformInAppWebViewController extends PlatformInterface apiUrl: 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2_16#printtopdfstream', ), + LinuxPlatform( + note: + 'Requires the Otzaria-patched WPE runtime. With stock libWPEWebKit, this method returns null.', + ), ], ) Future createPdf({ diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.g.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.g.dart index 0621384ceb..39e750961f 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.g.dart @@ -473,6 +473,8 @@ enum PlatformInAppWebViewControllerMethod { ///- iOS WKWebView 14.0+ ([Official API - WKWebView.createPdf](https://developer.apple.com/documentation/webkit/wkwebview/3650490-createpdf)) ///- macOS WKWebView 11.0+ ([Official API - WKWebView.createPdf](https://developer.apple.com/documentation/webkit/wkwebview/3650490-createpdf)) ///- Windows WebView2 ([Official API - ICoreWebView2_16.PrintToPdfStream](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2_16#printtopdfstream)) + ///- Linux WPE WebKit: + /// - Requires the Otzaria-patched WPE runtime. With stock libWPEWebKit, this method returns null. /// ///**Parameters - Officially Supported Platforms/Implementations**: ///- [pdfConfiguration]: all platforms @@ -2741,6 +2743,7 @@ extension _PlatformInAppWebViewControllerMethodSupported TargetPlatform.iOS, TargetPlatform.macOS, TargetPlatform.windows, + TargetPlatform.linux, ].contains(platform ?? defaultTargetPlatform); case PlatformInAppWebViewControllerMethod.createWebArchiveData: return ((kIsWeb && platform != null) || !kIsWeb) && diff --git a/flutter_inappwebview_platform_interface/test/in_app_webview/platform_inappwebview_controller_test.dart b/flutter_inappwebview_platform_interface/test/in_app_webview/platform_inappwebview_controller_test.dart new file mode 100644 index 0000000000..c9ced4dc57 --- /dev/null +++ b/flutter_inappwebview_platform_interface/test/in_app_webview/platform_inappwebview_controller_test.dart @@ -0,0 +1,24 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class _TestInAppWebViewController extends PlatformInAppWebViewController { + _TestInAppWebViewController() + : super.implementation( + const PlatformInAppWebViewControllerCreationParams(id: null), + ); +} + +void main() { + test('createPdf is advertised for the Linux implementation', () { + final controller = _TestInAppWebViewController(); + + expect( + controller.isMethodSupported( + PlatformInAppWebViewControllerMethod.createPdf, + platform: TargetPlatform.linux, + ), + isTrue, + ); + }); +}