2007-12-12 Alp Toker <alp@atoker.com>
authoralp@webkit.org <alp@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Dec 2007 16:14:48 +0000 (16:14 +0000)
committeralp@webkit.org <alp@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Dec 2007 16:14:48 +0000 (16:14 +0000)
        Reviewed by Holger Freyther.

        http://bugs.webkit.org/show_bug.cgi?id=15576
        [GTK] Printing support

        Add printing support.

        The API will be kept internal for the time being, but printing can now
        be triggered by Web pages or the JSC API using JavaScript.

        The print spooler and pagination code is fairly abstract and could be
        shared by other ports including Win and Qt once complete. It doesn't
        have header/footer support yet.

        * WebCoreSupport/ChromeClientGtk.cpp:
        (WebKit::ChromeClient::print):
        * WebView/webkitprivate.h:
        * WebView/webkitwebframe.cpp:
        (PrintContext::begin_print):
        (PrintContext::draw_page):
        (PrintContext::end_print):
        (PrintContext::webkit_web_frame_print):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@28648 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebKit/gtk/ChangeLog
WebKit/gtk/WebCoreSupport/ChromeClientGtk.cpp
WebKit/gtk/WebView/webkitprivate.h
WebKit/gtk/WebView/webkitwebframe.cpp

index 64af2fa8f44302e69d81a72f21db4765dc8625d8..d12cc8f8c9392ce26a6f3fd287b36dc625c63e5d 100644 (file)
@@ -1,3 +1,28 @@
+2007-12-12  Alp Toker  <alp@atoker.com>
+
+        Reviewed by Holger Freyther.
+
+        http://bugs.webkit.org/show_bug.cgi?id=15576
+        [GTK] Printing support
+
+        Add printing support.
+
+        The API will be kept internal for the time being, but printing can now
+        be triggered by Web pages or the JSC API using JavaScript.
+
+        The print spooler and pagination code is fairly abstract and could be
+        shared by other ports including Win and Qt once complete. It doesn't
+        have header/footer support yet.
+
+        * WebCoreSupport/ChromeClientGtk.cpp:
+        (WebKit::ChromeClient::print):
+        * WebView/webkitprivate.h:
+        * WebView/webkitwebframe.cpp:
+        (PrintContext::begin_print):
+        (PrintContext::draw_page):
+        (PrintContext::end_print):
+        (PrintContext::webkit_web_frame_print):
+
 2007-12-12  Sam Weinig  <sam@webkit.org>
 
         Build fix.
index 8ec91b8662ba73f635a607cd0c5aca001477fb35..b7c006274d4226c106a2fc56942975cf61d78b7b 100644 (file)
@@ -297,9 +297,9 @@ void ChromeClient::setToolTip(const String& toolTip)
 #endif
 }
 
-void ChromeClient::print(Frame*)
+void ChromeClient::print(Frame* frame)
 {
-    notImplemented();
+    webkit_web_frame_print(kit(frame));
 }
 
 unsigned long long ChromeClient::requestQuotaIncreaseForNewDatabase(Frame*, const SecurityOriginData&, const String&, unsigned long long)
index df70be47a081a9599239e0d1a5bb2670354114d4..83f9dd0eaafb19e520cb5aa9b8902814d4a06ed8 100644 (file)
@@ -93,6 +93,9 @@ extern "C" {
 
     WEBKIT_API gchar*
     webkit_web_frame_get_inner_text (WebKitWebFrame* frame);
+
+    WEBKIT_API void
+    webkit_web_frame_print (WebKitWebFrame* frame);
 }
 
 #endif
index c2515b28d0803ff713db9ccd26d88358617cb3da..fbef85968e3c3d2d48f01a8d83c7a867618daeeb 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2007 Holger Hans Peter Freyther
  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2007 Apple Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -30,7 +31,9 @@
 #include "FrameLoaderClientGtk.h"
 #include "FrameTree.h"
 #include "FrameView.h"
+#include "GraphicsContext.h"
 #include "HTMLFrameOwnerElement.h"
+#include "RenderView.h"
 #include "kjs_binding.h"
 #include "kjs_proxy.h"
 #include "kjs_window.h"
@@ -415,4 +418,195 @@ gchar* webkit_web_frame_get_inner_text(WebKitWebFrame* frame)
     return g_strdup(string.utf8().data());
 }
 
+
+#if GTK_CHECK_VERSION(2,10,0)
+
+// This could be shared between ports once it's complete
+class PrintContext
+{
+public:
+    PrintContext(Frame* frame)
+        : m_frame(frame)
+    {
+    }
+
+    ~PrintContext()
+    {
+        m_pageRects.clear();
+    }
+
+    int pageCount()
+    {
+        return m_pageRects.size();
+    }
+
+    void computePageRects(const FloatRect& printRect, float headerHeight, float footerHeight, float userScaleFactor, float& outPageHeight)
+    {
+        m_pageRects.clear();
+        outPageHeight = 0;
+
+        if (!m_frame->document() || !m_frame->view() || !m_frame->document()->renderer())
+            return;
+
+        RenderView* root = static_cast<RenderView*>(m_frame->document()->renderer());
+
+        if (!root) {
+            LOG_ERROR("document to be printed has no renderer");
+            return;
+        }
+
+        if (userScaleFactor <= 0) {
+            LOG_ERROR("userScaleFactor has bad value %.2f", userScaleFactor);
+            return;
+        }
+
+        float ratio = printRect.height() / printRect.width();
+
+        float pageWidth  = (float)root->docWidth();
+        float pageHeight = pageWidth * ratio;
+        outPageHeight = pageHeight;   // this is the height of the page adjusted by margins
+        pageHeight -= headerHeight + footerHeight;
+
+        if (pageHeight <= 0) {
+            LOG_ERROR("pageHeight has bad value %.2f", pageHeight);
+            return;
+        }
+
+        float currPageHeight = pageHeight / userScaleFactor;
+        float docHeight = root->layer()->height();
+        float currPageWidth = pageWidth / userScaleFactor;
+
+        // always return at least one page, since empty files should print a blank page
+        float printedPagesHeight = 0.0;
+        do {
+            float proposedBottom = min(docHeight, printedPagesHeight + pageHeight);
+            m_frame->adjustPageHeight(&proposedBottom, printedPagesHeight, proposedBottom, printedPagesHeight);
+            currPageHeight = max(1.0f, proposedBottom - printedPagesHeight);
+
+            m_pageRects.append(IntRect(0, (int)printedPagesHeight, (int)currPageWidth, (int)currPageHeight));
+            printedPagesHeight += currPageHeight;
+        } while (printedPagesHeight < docHeight);
+    }
+
+    // TODO: eliminate width param
+    void begin(float width)
+    {
+        // By imaging to a width a little wider than the available pixels,
+        // thin pages will be scaled down a little, matching the way they
+        // print in IE and Camino. This lets them use fewer sheets than they
+        // would otherwise, which is presumably why other browsers do this.
+        // Wide pages will be scaled down more than this.
+        const float PrintingMinimumShrinkFactor = 1.25f;
+
+        // This number determines how small we are willing to reduce the page content
+        // in order to accommodate the widest line. If the page would have to be
+        // reduced smaller to make the widest line fit, we just clip instead (this
+        // behavior matches MacIE and Mozilla, at least)
+        const float PrintingMaximumShrinkFactor = 2.0f;
+
+        float minLayoutWidth = width * PrintingMinimumShrinkFactor;
+        float maxLayoutWidth = width * PrintingMaximumShrinkFactor;
+
+        // FIXME: This will modify the rendering of the on-screen frame.
+        // Could lead to flicker during printing.
+        m_frame->setPrinting(true, minLayoutWidth, maxLayoutWidth, true);
+    }
+
+    // TODO: eliminate width param
+    void spoolPage(GraphicsContext& ctx, int pageNumber, float width)
+    {
+        IntRect pageRect = m_pageRects[pageNumber];
+        float scale = width / pageRect.width();
+
+        ctx.save();
+        ctx.scale(FloatSize(scale, scale));
+        ctx.translate(-pageRect.x(), -pageRect.y());
+        ctx.clip(pageRect);
+        m_frame->paint(&ctx, pageRect);
+        ctx.restore();
+    }
+
+    void end()
+    {
+        m_frame->setPrinting(false, 0, 0, true);
+    }
+
+protected:
+    Frame* m_frame;
+    Vector<IntRect> m_pageRects;
+};
+
+static void begin_print(GtkPrintOperation* op, GtkPrintContext* context, gpointer user_data)
+{
+    PrintContext* printContext = reinterpret_cast<PrintContext*>(user_data);
+
+    float width = gtk_print_context_get_width(context);
+    float height = gtk_print_context_get_height(context);
+    FloatRect printRect = FloatRect(0, 0, width, height);
+
+    printContext->begin(width);
+
+    // TODO: Margin adjustments and header/footer support
+    float headerHeight = 0;
+    float footerHeight = 0;
+    float pageHeight; // height of the page adjusted by margins
+    printContext->computePageRects(printRect, headerHeight, footerHeight, 1.0, pageHeight);
+    gtk_print_operation_set_n_pages(op, printContext->pageCount());
+}
+
+static void draw_page(GtkPrintOperation* op, GtkPrintContext* context, gint page_nr, gpointer user_data)
+{
+    PrintContext* printContext = reinterpret_cast<PrintContext*>(user_data);
+
+    cairo_t* cr = gtk_print_context_get_cairo_context(context);
+    GraphicsContext ctx(cr);
+    float width = gtk_print_context_get_width(context);
+    printContext->spoolPage(ctx, page_nr, width);
+}
+
+static void end_print(GtkPrintOperation* op, GtkPrintContext* context, gpointer user_data)
+{
+    PrintContext* printContext = reinterpret_cast<PrintContext*>(user_data);
+    printContext->end();
+}
+
+void webkit_web_frame_print(WebKitWebFrame* frame)
+{
+    GtkWidget* topLevel = gtk_widget_get_toplevel(GTK_WIDGET(webkit_web_frame_get_web_view(frame)));
+    if (!GTK_WIDGET_TOPLEVEL(topLevel))
+        topLevel = NULL;
+
+    Frame* coreFrame = core(frame);
+    PrintContext printContext(coreFrame);
+
+    GtkPrintOperation* op = gtk_print_operation_new();
+    g_signal_connect(G_OBJECT(op), "begin-print", G_CALLBACK(begin_print), &printContext);
+    g_signal_connect(G_OBJECT(op), "draw-page", G_CALLBACK(draw_page), &printContext);
+    g_signal_connect(G_OBJECT(op), "end-print", G_CALLBACK(end_print), &printContext);
+    GError *error = NULL;
+    gtk_print_operation_run(op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW(topLevel), &error);
+    g_object_unref(op);
+
+    if (error) {
+        GtkWidget* dialog = gtk_message_dialog_new(GTK_WINDOW(topLevel),
+                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                   GTK_MESSAGE_ERROR,
+                                                   GTK_BUTTONS_CLOSE,
+                                                   "%s", error->message);
+        g_error_free(error);
+
+        g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
+        gtk_widget_show(dialog);
+    }
+}
+
+#else
+
+void webkit_web_frame_print(WebKitWebFrame*)
+{
+    g_warning("Printing support is not available in older versions of GTK+");
+}
+
+#endif
+
 }