diff options
author | tc@google.com <tc@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 20:26:41 +0000 |
---|---|---|
committer | tc@google.com <tc@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 20:26:41 +0000 |
commit | 6862ac6cbec1c9a3d7e332b3e8c696c9a3d172d6 (patch) | |
tree | 7b810a3eb33cb63e404c8f4d67e6f3676168b13b | |
parent | e7ef7150c893a1ff2fe4197ce9d76ccb21001caa (diff) | |
download | chromium_src-6862ac6cbec1c9a3d7e332b3e8c696c9a3d172d6.zip chromium_src-6862ac6cbec1c9a3d7e332b3e8c696c9a3d172d6.tar.gz chromium_src-6862ac6cbec1c9a3d7e332b3e8c696c9a3d172d6.tar.bz2 |
Original change by Min-Yu Huang <minyu.huang@gmail.com> in
http://codereview.chromium.org/160347
This is the very preliminary implementation to support printing on Linux and it
has not been finished yet. For each page to be printed, we convert rendering
actions on canvas into cairo APIs and generate a PS/PDF file.
chrome/chrome.gyp:
Include our newly added and renamed files.
chrome/browser/browser.h:
chrome/browser/browser.cc:
Allow the user print the web page by hitting ctrl-p.
chrome/browser/gtk/standard_menus.cc:
Show "Print" in the menu.
chrome/renderer/print_web_view_helper.cc:
chrome/renderer/print_web_view_helper.h:
chrome/renderer/print_web_view_helper_mac.cc
chrome/renderer/print_web_view_helper_win.cc
Move the class PrepareFrameAndViewForPrint to the header file and move
platform dependent parts to their corresponding files.
chrome/renderer/print_web_view_helper_linux.cc:
Hard-coded parameters for printing. Only print the first page now.
skia/ext/vector_canvas.cc:
skia/ext/vector_canvas.h:
skia/ext/vector_canvas_linux.cc:
skia/ext/vector_canvas_win.cc:
Move platform dependent parts to their corresponding files.
skia/ext/vector_platform_device.h:
skia/ext/vector_platform_device_linux.cc:
skia/ext/vector_platform_device_linux.h
We translate skia APIs into Cairo APIs here. A PDF file is also created and
saved to the disk at this moment for testing purpose (you have to run chrome
without the sandbox to save the file). There are still lots of bugs.
skia/skia.gyp:
Include our newly added files when compiling skia package on Linux.
BUG=9847
Review URL: http://codereview.chromium.org/160673
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22522 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/browser.cc | 9 | ||||
-rw-r--r-- | chrome/browser/browser.h | 6 | ||||
-rw-r--r-- | chrome/browser/gtk/standard_menus.cc | 7 | ||||
-rw-r--r-- | chrome/chrome.gyp | 3 | ||||
-rw-r--r-- | chrome/renderer/print_web_view_helper.cc | 337 | ||||
-rw-r--r-- | chrome/renderer/print_web_view_helper.h | 38 | ||||
-rw-r--r-- | chrome/renderer/print_web_view_helper_linux.cc | 85 | ||||
-rw-r--r-- | chrome/renderer/print_web_view_helper_mac.cc | 20 | ||||
-rw-r--r-- | chrome/renderer/print_web_view_helper_win.cc | 199 | ||||
-rw-r--r-- | skia/ext/vector_canvas.cc | 58 | ||||
-rw-r--r-- | skia/ext/vector_canvas.h | 18 | ||||
-rw-r--r-- | skia/ext/vector_canvas_linux.cc | 49 | ||||
-rw-r--r-- | skia/ext/vector_canvas_win.cc | 49 | ||||
-rw-r--r-- | skia/ext/vector_platform_device.h | 17 | ||||
-rw-r--r-- | skia/ext/vector_platform_device_linux.cc | 498 | ||||
-rw-r--r-- | skia/ext/vector_platform_device_linux.h | 112 | ||||
-rw-r--r-- | skia/skia.gyp | 9 |
17 files changed, 900 insertions, 614 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index 712ef35..562f3b0 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -964,17 +964,20 @@ bool Browser::SupportsWindowFeature(WindowFeature feature) const { } #if defined(OS_WIN) - void Browser::ClosePopups() { UserMetrics::RecordAction(L"CloseAllSuppressedPopups", profile_); GetSelectedTabContents()->CloseAllSuppressedPopups(); } +#endif void Browser::Print() { UserMetrics::RecordAction(L"PrintPreview", profile_); +#if defined(OS_WIN) || defined(OS_LINUX) GetSelectedTabContents()->PrintPreview(); +#else + NOTIMPLEMENTED(); +#endif } -#endif // #if defined(OS_WIN) void Browser::ToggleEncodingAutoDetect() { UserMetrics::RecordAction(L"AutoDetectChange", profile_); @@ -1334,8 +1337,8 @@ void Browser::ExecuteCommandWithDisposition( case IDC_VIEW_SOURCE: ViewSource(); break; #if defined(OS_WIN) case IDC_CLOSE_POPUPS: ClosePopups(); break; - case IDC_PRINT: Print(); break; #endif + case IDC_PRINT: Print(); break; case IDC_ENCODING_AUTO_DETECT: ToggleEncodingAutoDetect(); break; case IDC_ENCODING_UTF8: case IDC_ENCODING_UTF16LE: diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h index 5044810..fe309e0 100644 --- a/chrome/browser/browser.h +++ b/chrome/browser/browser.h @@ -134,8 +134,8 @@ class Browser : public TabStripModelDelegate, const SessionID& session_id() const { return session_id_; } CommandUpdater* command_updater() { return &command_updater_; } FindBarController* find_bar() { return find_bar_controller_.get(); } - ExtensionShelfModel* extension_shelf_model() { - return extension_shelf_model_.get(); + ExtensionShelfModel* extension_shelf_model() { + return extension_shelf_model_.get(); } // Setters ///////////////////////////////////////////////////////////////// @@ -336,8 +336,8 @@ class Browser : public TabStripModelDelegate, #if defined(OS_WIN) // Page-related commands. void ClosePopups(); - void Print(); #endif + void Print(); void ToggleEncodingAutoDetect(); void OverrideEncoding(int encoding_id); diff --git a/chrome/browser/gtk/standard_menus.cc b/chrome/browser/gtk/standard_menus.cc index be4355b..72a66ac 100644 --- a/chrome/browser/gtk/standard_menus.cc +++ b/chrome/browser/gtk/standard_menus.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -50,10 +50,7 @@ struct MenuCreateMaterial standard_page_menu_materials[] = { { MENU_NORMAL, IDC_FIND, IDS_FIND, 0, NULL, GDK_f, GDK_CONTROL_MASK }, { MENU_NORMAL, IDC_SAVE_PAGE, IDS_SAVE_PAGE, 0, NULL, GDK_s, GDK_CONTROL_MASK }, - // Printing hasn't been implemented yet. Remove it from the menu until - // someone implements it. - // http://code.google.com/p/chromium/issues/detail?id=9847 - //{ MENU_NORMAL, IDC_PRINT, IDS_PRINT, 0, NULL, GDK_p, GDK_CONTROL_MASK }, + { MENU_NORMAL, IDC_PRINT, IDS_PRINT, 0, NULL, GDK_p, GDK_CONTROL_MASK }, { MENU_SEPARATOR }, { MENU_NORMAL, IDC_ZOOM_MENU, IDS_ZOOM_MENU, 0, zoom_menu_materials }, // The encoding menu submenu is filled in by code below. diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index d535d8b..c2eb3ea 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -2600,6 +2600,9 @@ 'renderer/plugin_channel_host.h', 'renderer/print_web_view_helper.cc', 'renderer/print_web_view_helper.h', + 'renderer/print_web_view_helper_linux.cc', + 'renderer/print_web_view_helper_mac.cc', + 'renderer/print_web_view_helper_win.cc', 'renderer/render_process.cc', 'renderer/render_process.h', 'renderer/render_thread.cc', diff --git a/chrome/renderer/print_web_view_helper.cc b/chrome/renderer/print_web_view_helper.cc index 1e9a0fe..a0803ba 100644 --- a/chrome/renderer/print_web_view_helper.cc +++ b/chrome/renderer/print_web_view_helper.cc @@ -6,220 +6,64 @@ #include "app/l10n_util.h" #include "base/logging.h" -#include "base/gfx/size.h" #include "chrome/common/render_messages.h" #include "chrome/renderer/render_view.h" #include "grit/generated_resources.h" -#include "printing/native_metafile.h" #include "printing/units.h" -#include "webkit/api/public/WebConsoleMessage.h" #include "webkit/api/public/WebRect.h" #include "webkit/api/public/WebScreenInfo.h" #include "webkit/api/public/WebSize.h" -#include "webkit/api/public/WebURL.h" #include "webkit/api/public/WebURLRequest.h" #include "webkit/glue/webframe.h" -#if defined(OS_WIN) -#include "skia/ext/vector_canvas.h" -#endif - -using WebKit::WebConsoleMessage; using WebKit::WebRect; using WebKit::WebScreenInfo; using WebKit::WebString; using WebKit::WebURLRequest; -namespace { - -const int kMinSecondsToIgnoreJavascriptInitiatedPrint = 2; -const int kMaxSecondsToIgnoreJavascriptInitiatedPrint = 2 * 60; // 2 Minutes. - -// Class that calls the Begin and End print functions on the frame and changes -// the size of the view temporarily to support full page printing.. -// Do not serve any events in the time between construction and destruction of -// this class because it will cause flicker. -class PrepareFrameAndViewForPrint { - public: - PrepareFrameAndViewForPrint(const ViewMsg_Print_Params& print_params, - WebFrame* frame, - WebView* web_view) - : frame_(frame), - web_view_(web_view), - expected_pages_count_(0) { - print_canvas_size_.set_width( - printing::ConvertUnit(print_params.printable_size.width(), - static_cast<int>(print_params.dpi), - print_params.desired_dpi)); - print_canvas_size_.set_height( - printing::ConvertUnit(print_params.printable_size.height(), - static_cast<int>(print_params.dpi), - print_params.desired_dpi)); - - // Layout page according to printer page size. Since WebKit shrinks the - // size of the page automatically (from 125% to 200%) we trick it to - // think the page is 125% larger so the size of the page is correct for - // minimum (default) scaling. - // This is important for sites that try to fill the page. - gfx::Size print_layout_size(print_canvas_size_); - print_layout_size.set_height(static_cast<int>( - static_cast<double>(print_layout_size.height()) * 1.25)); +PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint( + const ViewMsg_Print_Params& print_params, + WebFrame* frame, + WebView* web_view) + : frame_(frame), web_view_(web_view), expected_pages_count_(0) { - prev_view_size_ = web_view->size(); - - web_view->resize(print_layout_size); - - expected_pages_count_ = frame->PrintBegin(print_canvas_size_); - } - - int GetExpectedPageCount() const { - return expected_pages_count_; - } - - gfx::Size GetPrintCanvasSize() const { - return print_canvas_size_; - } - - ~PrepareFrameAndViewForPrint() { - frame_->PrintEnd(); - web_view_->resize(prev_view_size_); - } + print_canvas_size_.set_width( + printing::ConvertUnit(print_params.printable_size.width(), + static_cast<int>(print_params.dpi), + print_params.desired_dpi)); - private: - WebFrame* frame_; - WebView* web_view_; - gfx::Size print_canvas_size_; - gfx::Size prev_view_size_; - int expected_pages_count_; + print_canvas_size_.set_height( + printing::ConvertUnit(print_params.printable_size.height(), + static_cast<int>(print_params.dpi), + print_params.desired_dpi)); - DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint); -}; - -} // namespace - -void PrintWebViewHelper::Print(WebFrame* frame, bool script_initiated) { -#if defined(OS_WIN) - - // If still not finished with earlier print request simply ignore. - if (IsPrinting()) - return; - - // Check if there is script repeatedly trying to print and ignore it if too - // frequent. We use exponential wait time so for a page that calls print() in - // a loop the user will need to cancel the print dialog after 2 seconds, 4 - // seconds, 8, ... up to the maximum of 2 minutes. - // This gives the user time to navigate from the page. - if (script_initiated && (user_cancelled_scripted_print_count_ > 0)) { - base::TimeDelta diff = base::Time::Now() - last_cancelled_script_print_; - int min_wait_seconds = std::min( - kMinSecondsToIgnoreJavascriptInitiatedPrint << - (user_cancelled_scripted_print_count_ - 1), - kMaxSecondsToIgnoreJavascriptInitiatedPrint); - if (diff.InSeconds() < min_wait_seconds) { - WebString message(WebString::fromUTF8( - "Ignoring too frequent calls to print().")); - frame->AddMessageToConsole(WebConsoleMessage( - WebConsoleMessage::LevelWarning, - message)); - return; - } - } - - // Retrieve the default print settings to calculate the expected number of - // pages. - ViewMsg_Print_Params default_settings; - bool user_cancelled_print = false; - - IPC::SyncMessage* msg = - new ViewHostMsg_GetDefaultPrintSettings(routing_id(), &default_settings); - if (Send(msg)) { - msg = NULL; - // Check if the printer returned any settings, if the settings is empty, we - // can safely assume there are no printer drivers configured. So we safely - // terminate. - if (default_settings.IsEmpty()) { - // TODO: Create an async alert (http://crbug.com/14918). - render_view_->RunJavaScriptAlert(frame, - l10n_util::GetString(IDS_DEFAULT_PRINTER_NOT_FOUND_WARNING)); - return; - } - - // Continue only if the settings are valid. - if (default_settings.dpi && default_settings.document_cookie) { - int expected_pages_count = 0; - - // Prepare once to calculate the estimated page count. This must be in - // a scope for itself (see comments on PrepareFrameAndViewForPrint). - { - PrepareFrameAndViewForPrint prep_frame_view(default_settings, - frame, - frame->GetView()); - expected_pages_count = prep_frame_view.GetExpectedPageCount(); - DCHECK(expected_pages_count); - } + // Layout page according to printer page size. Since WebKit shrinks the + // size of the page automatically (from 125% to 200%) we trick it to + // think the page is 125% larger so the size of the page is correct for + // minimum (default) scaling. + // This is important for sites that try to fill the page. + gfx::Size print_layout_size(print_canvas_size_); + print_layout_size.set_height(static_cast<int>( + static_cast<double>(print_layout_size.height()) * 1.25)); - // Ask the browser to show UI to retrieve the final print settings. - ViewMsg_PrintPages_Params print_settings; + prev_view_size_ = web_view->size(); - ViewHostMsg_ScriptedPrint_Params params; + web_view->resize(print_layout_size); - // The routing id is sent across as it is needed to look up the - // corresponding RenderViewHost instance to signal and reset the - // pump messages event. - params.routing_id = routing_id(); - // host_window_ may be NULL at this point if the current window is a popup - // and the print() command has been issued from the parent. The receiver - // of this message has to deal with this. - params.host_window_id = render_view_->host_window(); - params.cookie = default_settings.document_cookie; - params.has_selection = frame->HasSelection(); - params.expected_pages_count = expected_pages_count; + expected_pages_count_ = frame->PrintBegin(print_canvas_size_); +} - msg = new ViewHostMsg_ScriptedPrint(params, - &print_settings); - msg->set_pump_messages_event(render_view_->modal_dialog_event()); +PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() { + frame_->PrintEnd(); + web_view_->resize(prev_view_size_); +} - if (Send(msg)) { - msg = NULL; - // If the settings are invalid, early quit. - if (print_settings.params.dpi && - print_settings.params.document_cookie) { - if (print_settings.params.selection_only) { - CopyAndPrint(print_settings, frame); - } else { - // TODO: Always copy before printing. - PrintPages(print_settings, frame); - } +PrintWebViewHelper::PrintWebViewHelper(RenderView* render_view) + : render_view_(render_view), + user_cancelled_scripted_print_count_(0) {} - // Reset cancel counter on first successful print. - user_cancelled_scripted_print_count_ = 0; - return; // All went well. - } else { - user_cancelled_print = true; - } - } else { - // Send() failed. - NOTREACHED(); - } - } else { - // Failed to get default settings. - NOTREACHED(); - } - } else { - // Send() failed. - NOTREACHED(); - } - if (script_initiated && user_cancelled_print) { - ++user_cancelled_scripted_print_count_; - last_cancelled_script_print_ = base::Time::Now(); - } - DidFinishPrinting(user_cancelled_print); -#else // defined(OS_WIN) - // TODO(port): print not implemented - NOTIMPLEMENTED(); -#endif -} +PrintWebViewHelper::~PrintWebViewHelper() {} void PrintWebViewHelper::DidFinishPrinting(bool success) { if (!success) { @@ -237,7 +81,6 @@ void PrintWebViewHelper::DidFinishPrinting(bool success) { print_web_view_.release(); // Close deletes object. print_pages_params_.reset(); } - } bool PrintWebViewHelper::CopyAndPrint(const ViewMsg_PrintPages_Params& params, @@ -292,120 +135,6 @@ void PrintWebViewHelper::PrintPages(const ViewMsg_PrintPages_Params& params, } } -void PrintWebViewHelper::PrintPage(const ViewMsg_PrintPage_Params& params, - const gfx::Size& canvas_size, - WebFrame* frame) { -#if defined(OS_WIN) - // Generate a memory-based metafile. It will use the current screen's DPI. - printing::NativeMetafile metafile; - - metafile.CreateDc(NULL, NULL); - HDC hdc = metafile.hdc(); - DCHECK(hdc); - skia::PlatformDevice::InitializeDC(hdc); - // Since WebKit extends the page width depending on the magical shrink - // factor we make sure the canvas covers the worst case scenario - // (x2.0 currently). PrintContext will then set the correct clipping region. - int size_x = static_cast<int>(canvas_size.width() * params.params.max_shrink); - int size_y = static_cast<int>(canvas_size.height() * - params.params.max_shrink); - // Calculate the dpi adjustment. - float shrink = static_cast<float>(canvas_size.width()) / - params.params.printable_size.width(); -#if 0 - // TODO(maruel): This code is kept for testing until the 100% GDI drawing - // code is stable. maruels use this code's output as a reference when the - // GDI drawing code fails. - - // Mix of Skia and GDI based. - skia::PlatformCanvas canvas(size_x, size_y, true); - canvas.drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode); - float webkit_shrink = frame->PrintPage(params.page_number, &canvas); - if (shrink <= 0) { - NOTREACHED() << "Printing page " << params.page_number << " failed."; - } else { - // Update the dpi adjustment with the "page shrink" calculated in webkit. - shrink /= webkit_shrink; - } - - // Create a BMP v4 header that we can serialize. - BITMAPV4HEADER bitmap_header; - gfx::CreateBitmapV4Header(size_x, size_y, &bitmap_header); - const SkBitmap& src_bmp = canvas.getDevice()->accessBitmap(true); - SkAutoLockPixels src_lock(src_bmp); - int retval = StretchDIBits(hdc, - 0, - 0, - size_x, size_y, - 0, 0, - size_x, size_y, - src_bmp.getPixels(), - reinterpret_cast<BITMAPINFO*>(&bitmap_header), - DIB_RGB_COLORS, - SRCCOPY); - DCHECK(retval != GDI_ERROR); -#else - // 100% GDI based. - skia::VectorCanvas canvas(hdc, size_x, size_y); - float webkit_shrink = frame->PrintPage(params.page_number, &canvas); - if (shrink <= 0) { - NOTREACHED() << "Printing page " << params.page_number << " failed."; - } else { - // Update the dpi adjustment with the "page shrink" calculated in webkit. - shrink /= webkit_shrink; - } -#endif - - // Done printing. Close the device context to retrieve the compiled metafile. - if (!metafile.CloseDc()) { - NOTREACHED() << "metafile failed"; - } - - // Get the size of the compiled metafile. - unsigned buf_size = metafile.GetDataSize(); - DCHECK_GT(buf_size, 128u); - ViewHostMsg_DidPrintPage_Params page_params; - page_params.data_size = 0; - page_params.metafile_data_handle = NULL; - page_params.page_number = params.page_number; - page_params.document_cookie = params.params.document_cookie; - page_params.actual_shrink = shrink; - base::SharedMemory shared_buf; - - // http://msdn2.microsoft.com/en-us/library/ms535522.aspx - // Windows 2000/XP: When a page in a spooled file exceeds approximately 350 - // MB, it can fail to print and not send an error message. - if (buf_size < 350*1024*1024) { - // Allocate a shared memory buffer to hold the generated metafile data. - if (shared_buf.Create(L"", false, false, buf_size) && - shared_buf.Map(buf_size)) { - // Copy the bits into shared memory. - if (metafile.GetData(shared_buf.memory(), buf_size)) { - page_params.metafile_data_handle = shared_buf.handle(); - page_params.data_size = buf_size; - } else { - NOTREACHED() << "GetData() failed"; - } - shared_buf.Unmap(); - } else { - NOTREACHED() << "Buffer allocation failed"; - } - } else { - NOTREACHED() << "Buffer too large: " << buf_size; - } - metafile.CloseEmf(); - if (Send(new ViewHostMsg_DuplicateSection( - routing_id(), - page_params.metafile_data_handle, - &page_params.metafile_data_handle))) { - Send(new ViewHostMsg_DidPrintPage(routing_id(), page_params)); - } -#else // defined(OS_WIN) - // TODO(port) implement printing - NOTIMPLEMENTED(); -#endif -} - bool PrintWebViewHelper::Send(IPC::Message* msg) { return render_view_->Send(msg); } diff --git a/chrome/renderer/print_web_view_helper.h b/chrome/renderer/print_web_view_helper.h index 13d0747..efa6ee0 100644 --- a/chrome/renderer/print_web_view_helper.h +++ b/chrome/renderer/print_web_view_helper.h @@ -7,6 +7,7 @@ #include <vector> +#include "base/gfx/size.h" #include "base/scoped_ptr.h" #include "base/time.h" #include "webkit/glue/webview_delegate.h" @@ -26,16 +27,43 @@ struct ViewMsg_PrintPage_Params; struct ViewMsg_PrintPages_Params; +// Class that calls the Begin and End print functions on the frame and changes +// the size of the view temporarily to support full page printing.. +// Do not serve any events in the time between construction and destruction of +// this class because it will cause flicker. +class PrepareFrameAndViewForPrint { + public: + PrepareFrameAndViewForPrint(const ViewMsg_Print_Params& print_params, + WebFrame* frame, + WebView* web_view); + ~PrepareFrameAndViewForPrint(); + + int GetExpectedPageCount() const { + return expected_pages_count_; + } + + const gfx::Size& GetPrintCanvasSize() const { + return print_canvas_size_; + } + + private: + WebFrame* frame_; + WebView* web_view_; + gfx::Size print_canvas_size_; + gfx::Size prev_view_size_; + int expected_pages_count_; + + DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint); +}; + + // PrintWebViewHelper handles most of the printing grunt work for RenderView. // We plan on making print asynchronous and that will require copying the DOM // of the document and creating a new WebView with the contents. class PrintWebViewHelper : public WebViewDelegate { public: - explicit PrintWebViewHelper(RenderView * render_view) - : render_view_(render_view), - user_cancelled_scripted_print_count_(0) {} - - virtual ~PrintWebViewHelper() {} + explicit PrintWebViewHelper(RenderView* render_view); + virtual ~PrintWebViewHelper(); void Print(WebFrame* frame, bool script_initiated); diff --git a/chrome/renderer/print_web_view_helper_linux.cc b/chrome/renderer/print_web_view_helper_linux.cc new file mode 100644 index 0000000..52b997d --- /dev/null +++ b/chrome/renderer/print_web_view_helper_linux.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/print_web_view_helper.h" + +#include "base/logging.h" +#include "chrome/common/render_messages.h" +#include "skia/ext/vector_canvas.h" +#include "webkit/glue/webframe.h" + + +void PrintWebViewHelper::Print(WebFrame* frame, bool script_initiated) { + // If still not finished with earlier print request simply ignore. + if (IsPrinting()) + return; + + // TODO(myhuang): Get printing parameters via IPC. + // For testing purpose, we hard-coded printing parameters here. + + // The paper size is US Letter (8.5 in. by 11 in.). + // Using default margins: + // Left = 0.25 in. + // Right = 0.25 in. + // Top = 0.25 in. + // Bottom = 0.56 in. + const int kDPI = 72; + const int kWidth = (8.5-0.25-0.25) * kDPI; + const int kHeight = (11-0.25-0.56) * kDPI; + ViewMsg_Print_Params default_settings; + default_settings.printable_size = gfx::Size(kWidth, kHeight); + default_settings.dpi = kDPI; + default_settings.min_shrink = 1.25; + default_settings.max_shrink = 2.0; + default_settings.desired_dpi = kDPI; + default_settings.document_cookie = NULL; + default_settings.selection_only = false; + + // Calculate the estimated page count. + PrepareFrameAndViewForPrint prep_frame_view(default_settings, + frame, + frame->GetView()); + int expected_pages_count = prep_frame_view.GetExpectedPageCount(); + DCHECK(expected_pages_count); + + ViewMsg_PrintPage_Params page_params; + page_params.params = default_settings; + + // TODO(myhuang): Get final printing settings via IPC. + // For testing purpose, we hard-coded printing settings here. + + // Print the first page only. + expected_pages_count = 1; + for (int i = 0; i < expected_pages_count; ++i) { + page_params.page_number = i; + PrintPage(page_params, prep_frame_view.GetPrintCanvasSize(), frame); + } +} + +void PrintWebViewHelper::PrintPage(const ViewMsg_PrintPage_Params& params, + const gfx::Size& canvas_size, + WebFrame* frame) { + // Since WebKit extends the page width depending on the magical shrink + // factor we make sure the canvas covers the worst case scenario + // (x2.0 currently). PrintContext will then set the correct clipping region. + int size_x = static_cast<int>(canvas_size.width() * params.params.max_shrink); + int size_y = static_cast<int>(canvas_size.height() * + params.params.max_shrink); + // Calculate the dpi adjustment. + float shrink = static_cast<float>(canvas_size.width()) / + params.params.printable_size.width(); + + // TODO(myhuang): We now use VectorCanvas to generate a PS/PDF file for + // each page in printing. We might also need to create a metafile class + // on Linux. + skia::VectorCanvas canvas(size_x, size_y); + float webkit_shrink = frame->PrintPage(params.page_number, &canvas); + if (shrink <= 0) { + NOTREACHED() << "Printing page " << params.page_number << " failed."; + } else { + // Update the dpi adjustment with the "page shrink" calculated in webkit. + shrink /= webkit_shrink; + } +} + diff --git a/chrome/renderer/print_web_view_helper_mac.cc b/chrome/renderer/print_web_view_helper_mac.cc new file mode 100644 index 0000000..ce07add --- /dev/null +++ b/chrome/renderer/print_web_view_helper_mac.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/print_web_view_helper.h" + +#include "base/logging.h" + +void PrintWebViewHelper::Print(WebFrame* frame, bool script_initiated) { + // TODO(port): print not implemented + NOTIMPLEMENTED(); +} + +void PrintWebViewHelper::PrintPage(const ViewMsg_PrintPage_Params& params, + const gfx::Size& canvas_size, + WebFrame* frame) { + // TODO(port) implement printing + NOTIMPLEMENTED(); +} + diff --git a/chrome/renderer/print_web_view_helper_win.cc b/chrome/renderer/print_web_view_helper_win.cc index 1e9a0fe..e4e372a 100644 --- a/chrome/renderer/print_web_view_helper_win.cc +++ b/chrome/renderer/print_web_view_helper_win.cc @@ -5,105 +5,29 @@ #include "chrome/renderer/print_web_view_helper.h" #include "app/l10n_util.h" -#include "base/logging.h" #include "base/gfx/size.h" +#include "base/logging.h" #include "chrome/common/render_messages.h" #include "chrome/renderer/render_view.h" #include "grit/generated_resources.h" #include "printing/native_metafile.h" -#include "printing/units.h" #include "webkit/api/public/WebConsoleMessage.h" -#include "webkit/api/public/WebRect.h" -#include "webkit/api/public/WebScreenInfo.h" -#include "webkit/api/public/WebSize.h" -#include "webkit/api/public/WebURL.h" -#include "webkit/api/public/WebURLRequest.h" #include "webkit/glue/webframe.h" -#if defined(OS_WIN) -#include "skia/ext/vector_canvas.h" -#endif - using WebKit::WebConsoleMessage; -using WebKit::WebRect; -using WebKit::WebScreenInfo; using WebKit::WebString; -using WebKit::WebURLRequest; - -namespace { - -const int kMinSecondsToIgnoreJavascriptInitiatedPrint = 2; -const int kMaxSecondsToIgnoreJavascriptInitiatedPrint = 2 * 60; // 2 Minutes. - -// Class that calls the Begin and End print functions on the frame and changes -// the size of the view temporarily to support full page printing.. -// Do not serve any events in the time between construction and destruction of -// this class because it will cause flicker. -class PrepareFrameAndViewForPrint { - public: - PrepareFrameAndViewForPrint(const ViewMsg_Print_Params& print_params, - WebFrame* frame, - WebView* web_view) - : frame_(frame), - web_view_(web_view), - expected_pages_count_(0) { - print_canvas_size_.set_width( - printing::ConvertUnit(print_params.printable_size.width(), - static_cast<int>(print_params.dpi), - print_params.desired_dpi)); - print_canvas_size_.set_height( - printing::ConvertUnit(print_params.printable_size.height(), - static_cast<int>(print_params.dpi), - print_params.desired_dpi)); - - // Layout page according to printer page size. Since WebKit shrinks the - // size of the page automatically (from 125% to 200%) we trick it to - // think the page is 125% larger so the size of the page is correct for - // minimum (default) scaling. - // This is important for sites that try to fill the page. - gfx::Size print_layout_size(print_canvas_size_); - print_layout_size.set_height(static_cast<int>( - static_cast<double>(print_layout_size.height()) * 1.25)); - - prev_view_size_ = web_view->size(); - - web_view->resize(print_layout_size); - - expected_pages_count_ = frame->PrintBegin(print_canvas_size_); - } - - int GetExpectedPageCount() const { - return expected_pages_count_; - } - gfx::Size GetPrintCanvasSize() const { - return print_canvas_size_; - } - - ~PrepareFrameAndViewForPrint() { - frame_->PrintEnd(); - web_view_->resize(prev_view_size_); - } - - private: - WebFrame* frame_; - WebView* web_view_; - gfx::Size print_canvas_size_; - gfx::Size prev_view_size_; - int expected_pages_count_; - - DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint); -}; - -} // namespace +#include "skia/ext/vector_canvas.h" void PrintWebViewHelper::Print(WebFrame* frame, bool script_initiated) { -#if defined(OS_WIN) + const int kMinSecondsToIgnoreJavascriptInitiatedPrint = 2; + const int kMaxSecondsToIgnoreJavascriptInitiatedPrint = 2 * 60; // 2 Minutes. // If still not finished with earlier print request simply ignore. if (IsPrinting()) return; + // TODO(maruel): Move this out of platform specific code. // Check if there is script repeatedly trying to print and ignore it if too // frequent. We use exponential wait time so for a page that calls print() in // a loop the user will need to cancel the print dialog after 2 seconds, 4 @@ -215,87 +139,11 @@ void PrintWebViewHelper::Print(WebFrame* frame, bool script_initiated) { last_cancelled_script_print_ = base::Time::Now(); } DidFinishPrinting(user_cancelled_print); -#else // defined(OS_WIN) - // TODO(port): print not implemented - NOTIMPLEMENTED(); -#endif -} - -void PrintWebViewHelper::DidFinishPrinting(bool success) { - if (!success) { - WebView* web_view = print_web_view_.get(); - if (!web_view) - web_view = render_view_->webview(); - - // TODO: Create an async alert (http://crbug.com/14918). - render_view_->RunJavaScriptAlert(web_view->GetMainFrame(), - l10n_util::GetString(IDS_PRINT_SPOOL_FAILED_ERROR_TEXT)); - } - - if (print_web_view_.get()) { - print_web_view_->close(); - print_web_view_.release(); // Close deletes object. - print_pages_params_.reset(); - } - -} - -bool PrintWebViewHelper::CopyAndPrint(const ViewMsg_PrintPages_Params& params, - WebFrame* web_frame) { - // Create a new WebView with the same settings as the current display one. - // Except that we disable javascript (don't want any active content running - // on the page). - WebPreferences prefs = web_frame->GetView()->GetPreferences(); - prefs.javascript_enabled = false; - prefs.java_enabled = false; - print_web_view_.reset(WebView::Create(this, prefs)); - - print_pages_params_.reset(new ViewMsg_PrintPages_Params(params)); - print_pages_params_->pages.clear(); // Print all pages of selection. - - std::string html = web_frame->GetSelection(true); - std::string url_str = "data:text/html;charset=utf-8,"; - url_str.append(html); - GURL url(url_str); - - // When loading is done this will call DidStopLoading that will do the - // actual printing. - print_web_view_->GetMainFrame()->LoadRequest(WebURLRequest(url)); - - return true; -} - -void PrintWebViewHelper::PrintPages(const ViewMsg_PrintPages_Params& params, - WebFrame* frame) { - PrepareFrameAndViewForPrint prep_frame_view(params.params, - frame, - frame->GetView()); - int page_count = prep_frame_view.GetExpectedPageCount(); - - Send(new ViewHostMsg_DidGetPrintedPagesCount(routing_id(), - params.params.document_cookie, - page_count)); - if (page_count) { - ViewMsg_PrintPage_Params page_params; - page_params.params = params.params; - if (params.pages.empty()) { - for (int i = 0; i < page_count; ++i) { - page_params.page_number = i; - PrintPage(page_params, prep_frame_view.GetPrintCanvasSize(), frame); - } - } else { - for (size_t i = 0; i < params.pages.size(); ++i) { - page_params.page_number = params.pages[i]; - PrintPage(page_params, prep_frame_view.GetPrintCanvasSize(), frame); - } - } - } } void PrintWebViewHelper::PrintPage(const ViewMsg_PrintPage_Params& params, const gfx::Size& canvas_size, WebFrame* frame) { -#if defined(OS_WIN) // Generate a memory-based metafile. It will use the current screen's DPI. printing::NativeMetafile metafile; @@ -400,42 +248,5 @@ void PrintWebViewHelper::PrintPage(const ViewMsg_PrintPage_Params& params, &page_params.metafile_data_handle))) { Send(new ViewHostMsg_DidPrintPage(routing_id(), page_params)); } -#else // defined(OS_WIN) - // TODO(port) implement printing - NOTIMPLEMENTED(); -#endif -} - -bool PrintWebViewHelper::Send(IPC::Message* msg) { - return render_view_->Send(msg); -} - -int32 PrintWebViewHelper::routing_id() { - return render_view_->routing_id(); -} - -WebRect PrintWebViewHelper::windowRect() { - NOTREACHED(); - return WebRect(); } -WebRect PrintWebViewHelper::windowResizerRect() { - NOTREACHED(); - return WebRect(); -} - -WebRect PrintWebViewHelper::rootWindowRect() { - NOTREACHED(); - return WebRect(); -} - -WebScreenInfo PrintWebViewHelper::screenInfo() { - NOTREACHED(); - return WebScreenInfo(); -} - -void PrintWebViewHelper::DidStopLoading(WebView* webview) { - DCHECK(print_pages_params_.get() != NULL); - DCHECK_EQ(webview, print_web_view_.get()); - PrintPages(*print_pages_params_.get(), print_web_view_->GetMainFrame()); -} diff --git a/skia/ext/vector_canvas.cc b/skia/ext/vector_canvas.cc index 75b7310..c80d0c3 100644 --- a/skia/ext/vector_canvas.cc +++ b/skia/ext/vector_canvas.cc @@ -1,36 +1,17 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "skia/ext/vector_canvas.h" -#include "skia/ext/bitmap_platform_device_win.h" -#include "skia/ext/vector_platform_device_win.h" - namespace skia { VectorCanvas::VectorCanvas() { } -VectorCanvas::VectorCanvas(HDC dc, int width, int height) { - bool initialized = initialize(dc, width, height); - if (!initialized) - __debugbreak(); -} - VectorCanvas::~VectorCanvas() { } -bool VectorCanvas::initialize(HDC context, int width, int height) { - SkDevice* device = createPlatformDevice(width, height, true, context); - if (!device) - return false; - - setDevice(device); - device->unref(); // was created with refcount 1, and setDevice also refs - return true; -} - SkBounder* VectorCanvas::setBounder(SkBounder* bounder) { if (!IsTopDeviceVectorial()) return PlatformCanvas::setBounder(bounder); @@ -40,49 +21,12 @@ SkBounder* VectorCanvas::setBounder(SkBounder* bounder) { return NULL; } -SkDevice* VectorCanvas::createDevice(SkBitmap::Config config, - int width, int height, - bool is_opaque, bool isForLayer) { - SkASSERT(config == SkBitmap::kARGB_8888_Config); - return createPlatformDevice(width, height, is_opaque, NULL); -} - SkDrawFilter* VectorCanvas::setDrawFilter(SkDrawFilter* filter) { // This function isn't used in the code. Verify this assumption. SkASSERT(false); return NULL; } -SkDevice* VectorCanvas::createPlatformDevice(int width, - int height, bool is_opaque, - HANDLE shared_section) { - if (!is_opaque) { - // TODO(maruel): http://b/1184002 1184002 When restoring a semi-transparent - // layer, i.e. merging it, we need to rasterize it because GDI doesn't - // support transparency except for AlphaBlend(). Right now, a - // BitmapPlatformDevice is created when VectorCanvas think a saveLayers() - // call is being done. The way to save a layer would be to create an - // EMF-based VectorDevice and have this device registers the drawing. When - // playing back the device into a bitmap, do it at the printer's dpi instead - // of the layout's dpi (which is much lower). - return BitmapPlatformDevice::create(width, height, - is_opaque, shared_section); - } - - // TODO(maruel): http://b/1183870 Look if it would be worth to increase the - // resolution by ~10x (any worthy factor) to increase the rendering precision - // (think about printing) while using a relatively low dpi. This happens - // because we receive float as input but the GDI functions works with - // integers. The idea is to premultiply the matrix with this factor and - // multiply each SkScalar that are passed to SkScalarRound(value) as - // SkScalarRound(value * 10). Safari is already doing the same for text - // rendering. - SkASSERT(shared_section); - PlatformDevice* device = VectorPlatformDevice::create( - reinterpret_cast<HDC>(shared_section), width, height); - return device; -} - bool VectorCanvas::IsTopDeviceVectorial() const { return getTopPlatformDevice().IsVectorial(); } diff --git a/skia/ext/vector_canvas.h b/skia/ext/vector_canvas.h index 510e8f3..27f7598 100644 --- a/skia/ext/vector_canvas.h +++ b/skia/ext/vector_canvas.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,7 +6,7 @@ #define SKIA_EXT_VECTOR_CANVAS_H_ #include "skia/ext/platform_canvas.h" -#include "skia/ext/vector_platform_device_win.h" +#include "skia/ext/vector_platform_device.h" namespace skia { @@ -17,22 +17,36 @@ namespace skia { class VectorCanvas : public PlatformCanvas { public: VectorCanvas(); +#if defined(WIN32) VectorCanvas(HDC dc, int width, int height); +#elif defined(__linux__) + VectorCanvas(int width, int height); +#endif virtual ~VectorCanvas(); // For two-part init, call if you use the no-argument constructor above +#if defined(WIN32) bool initialize(HDC context, int width, int height); +#elif defined(__linux__) + bool initialize(int width, int height); +#endif virtual SkBounder* setBounder(SkBounder*); +#if defined(WIN32) || defined(__linux__) virtual SkDevice* createDevice(SkBitmap::Config config, int width, int height, bool is_opaque, bool isForLayer); +#endif virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter); private: // |is_opaque| is unused. |shared_section| is in fact the HDC used for output. +#if defined(WIN32) virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque, HANDLE shared_section); +#elif defined(__linux__) + virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque); +#endif // Returns true if the top device is vector based and not bitmap based. bool IsTopDeviceVectorial() const; diff --git a/skia/ext/vector_canvas_linux.cc b/skia/ext/vector_canvas_linux.cc new file mode 100644 index 0000000..95722c9 --- /dev/null +++ b/skia/ext/vector_canvas_linux.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "skia/ext/vector_canvas.h" + +#include "skia/ext/bitmap_platform_device.h" +#include "skia/ext/vector_platform_device.h" + +namespace skia { + +VectorCanvas::VectorCanvas(int width, int height) { + bool initialized = initialize(width, height); + + SkASSERT(initialized); +} + +bool VectorCanvas::initialize(int width, int height) { + SkDevice* device = createPlatformDevice(width, height, true); + if (!device) + return false; + + setDevice(device); + device->unref(); // was created with refcount 1, and setDevice also refs + return true; +} + +SkDevice* VectorCanvas::createDevice(SkBitmap::Config config, + int width, int height, + bool is_opaque, bool isForLayer) { + SkASSERT(config == SkBitmap::kARGB_8888_Config); + return createPlatformDevice(width, height, is_opaque); +} + +SkDevice* VectorCanvas::createPlatformDevice(int width, + int height, bool is_opaque) { + // TODO(myhuang): Here we might also have similar issues as those on Windows + // (vector_canvas_win.cc, http://crbug.com/18382 & http://crbug.com/18383). + // Please note that is_opaque is true when we use this class for printing. + if (!is_opaque) { + return BitmapPlatformDevice::Create(width, height, is_opaque); + } + + PlatformDevice* device = VectorPlatformDevice::create(width, height); + return device; +} + +} // namespace skia + diff --git a/skia/ext/vector_canvas_win.cc b/skia/ext/vector_canvas_win.cc index 75b7310..3fe14b0 100644 --- a/skia/ext/vector_canvas_win.cc +++ b/skia/ext/vector_canvas_win.cc @@ -1,26 +1,20 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "skia/ext/vector_canvas.h" -#include "skia/ext/bitmap_platform_device_win.h" -#include "skia/ext/vector_platform_device_win.h" +#include "skia/ext/bitmap_platform_device.h" +#include "skia/ext/vector_platform_device.h" namespace skia { -VectorCanvas::VectorCanvas() { -} - VectorCanvas::VectorCanvas(HDC dc, int width, int height) { bool initialized = initialize(dc, width, height); if (!initialized) __debugbreak(); } -VectorCanvas::~VectorCanvas() { -} - bool VectorCanvas::initialize(HDC context, int width, int height) { SkDevice* device = createPlatformDevice(width, height, true, context); if (!device) @@ -31,15 +25,6 @@ bool VectorCanvas::initialize(HDC context, int width, int height) { return true; } -SkBounder* VectorCanvas::setBounder(SkBounder* bounder) { - if (!IsTopDeviceVectorial()) - return PlatformCanvas::setBounder(bounder); - - // This function isn't used in the code. Verify this assumption. - SkASSERT(false); - return NULL; -} - SkDevice* VectorCanvas::createDevice(SkBitmap::Config config, int width, int height, bool is_opaque, bool isForLayer) { @@ -47,17 +32,11 @@ SkDevice* VectorCanvas::createDevice(SkBitmap::Config config, return createPlatformDevice(width, height, is_opaque, NULL); } -SkDrawFilter* VectorCanvas::setDrawFilter(SkDrawFilter* filter) { - // This function isn't used in the code. Verify this assumption. - SkASSERT(false); - return NULL; -} - SkDevice* VectorCanvas::createPlatformDevice(int width, int height, bool is_opaque, HANDLE shared_section) { if (!is_opaque) { - // TODO(maruel): http://b/1184002 1184002 When restoring a semi-transparent + // TODO(maruel): http://crbug.com/18382 When restoring a semi-transparent // layer, i.e. merging it, we need to rasterize it because GDI doesn't // support transparency except for AlphaBlend(). Right now, a // BitmapPlatformDevice is created when VectorCanvas think a saveLayers() @@ -69,23 +48,19 @@ SkDevice* VectorCanvas::createPlatformDevice(int width, is_opaque, shared_section); } - // TODO(maruel): http://b/1183870 Look if it would be worth to increase the - // resolution by ~10x (any worthy factor) to increase the rendering precision - // (think about printing) while using a relatively low dpi. This happens - // because we receive float as input but the GDI functions works with - // integers. The idea is to premultiply the matrix with this factor and - // multiply each SkScalar that are passed to SkScalarRound(value) as - // SkScalarRound(value * 10). Safari is already doing the same for text - // rendering. + // TODO(maruel): http://crbug.com/18383 Look if it would be worth to + // increase the resolution by ~10x (any worthy factor) to increase the + // rendering precision (think about printing) while using a relatively + // low dpi. This happens because we receive float as input but the GDI + // functions works with integers. The idea is to premultiply the matrix + // with this factor and multiply each SkScalar that are passed to + // SkScalarRound(value) as SkScalarRound(value * 10). Safari is already + // doing the same for text rendering. SkASSERT(shared_section); PlatformDevice* device = VectorPlatformDevice::create( reinterpret_cast<HDC>(shared_section), width, height); return device; } -bool VectorCanvas::IsTopDeviceVectorial() const { - return getTopPlatformDevice().IsVectorial(); -} - } // namespace skia diff --git a/skia/ext/vector_platform_device.h b/skia/ext/vector_platform_device.h new file mode 100644 index 0000000..d77e565 --- /dev/null +++ b/skia/ext/vector_platform_device.h @@ -0,0 +1,17 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SKIA_EXT_VECTOR_PLATFORM_DEVICE_H_ +#define SKIA_EXT_VECTOR_PLATFORM_DEVICE_H_ + +// This file provides an easy way to include the appropriate +// VectorPlatformDevice header file for your platform. +#if defined(WIN32) +#include "skia/ext/vector_platform_device_win.h" +#elif defined(__linux__) +#include "skia/ext/vector_platform_device_linux.h" +#endif + +#endif + diff --git a/skia/ext/vector_platform_device_linux.cc b/skia/ext/vector_platform_device_linux.cc new file mode 100644 index 0000000..8c1b108 --- /dev/null +++ b/skia/ext/vector_platform_device_linux.cc @@ -0,0 +1,498 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "skia/ext/vector_platform_device.h" + +// TODO(myhuang): We have to decide or allow the user the choose the type +// of the surface in the future. +#include <cairo-pdf.h> + +#include "third_party/skia/include/core/SkTypeface.h" + +namespace skia { + +VectorPlatformDevice* VectorPlatformDevice::create(int width, int height) { + SkASSERT(width > 0); + SkASSERT(height > 0); + + // TODO(myhuang): Can we get rid of the bitmap? In this vetorial device, + // the content of this bitmap is meaningless. However, it does occupy + // lots of memory space. + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + + return new VectorPlatformDevice(bitmap); +} + +VectorPlatformDevice::VectorPlatformDevice(const SkBitmap& bitmap) + : PlatformDevice(bitmap) { + SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); + + // FIXME(myhuang): At this moment, we write the PDF file to the disk + // for testing when we run chromium without sanboxing. + surface_ = cairo_pdf_surface_create("chrome_printing_test.pdf", + width(), height()); + SkASSERT(surface); + context_ = cairo_create(surface_); + SkASSERT(context_); + + transform_.reset(); +} + +VectorPlatformDevice::~VectorPlatformDevice() { + SkASSERT(surface); + SkASSERT(context_); + + cairo_destroy(context_); + cairo_surface_destroy(surface_); +} + +void VectorPlatformDevice::drawBitmap(const SkDraw& draw, + const SkBitmap& bitmap, + const SkMatrix& matrix, + const SkPaint& paint) { + SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); + + // Load the temporary matrix. This is what will translate, rotate and resize + // the bitmap. + SkMatrix actual_transform(transform_); + actual_transform.preConcat(matrix); + LoadTransformToContext(actual_transform); + + InternalDrawBitmap(bitmap, 0, 0, paint); + + // Restore the original matrix. + LoadTransformToContext(transform_); +} + +void VectorPlatformDevice::drawDevice(const SkDraw& draw, + SkDevice* device, + int x, + int y, + const SkPaint& paint) { + SkASSERT(device); + SkASSERT(device->accessBitmap(false)); + + // TODO(myhuang): We may also have to consider http://b/1183870 . + drawSprite(draw, device->accessBitmap(false), x, y, paint); +} + +void VectorPlatformDevice::drawPaint(const SkDraw& draw, + const SkPaint& paint) { + // Bypass the current transformation matrix. + LoadIdentityTransformToContext(); + + // FIXME(myhuang): Is there a better way to do this? + SkRect rect; + rect.fLeft = 0; + rect.fTop = 0; + rect.fRight = SkIntToScalar(width() + 1); + rect.fBottom = SkIntToScalar(height() + 1); + drawRect(draw, rect, paint); + + // Restore the original matrix. + LoadTransformToContext(transform_); +} + +void VectorPlatformDevice::drawPath(const SkDraw& draw, + const SkPath& path, + const SkPaint& paint) { + if (paint.getPathEffect()) { + // Apply the path effect forehand. + SkPath path_modified; + paint.getFillPath(path, &path_modified); + + // Removes the path effect from the temporary SkPaint object. + SkPaint paint_no_effet(paint); + paint_no_effet.setPathEffect(NULL)->safeUnref(); + + // Draw the calculated path. + drawPath(draw, path_modified, paint_no_effet); + return; + } + + // Setup paint color. + ApplyPaintColor(paint); + + SkPaint::Style style = paint.getStyle(); + // Setup fill style. + if (style & SkPaint::kFill_Style) { + ApplyFillStyle(path); + } + + // Setup stroke style. + if (style & SkPaint::kStroke_Style) { + ApplyStrokeStyle(paint); + } + + // Iterate path verbs. + // TODO(myhuang): Is there a better way to do this? + SkPoint current_points[4]; + SkPath::Iter iter(path, false); + for (SkPath::Verb verb = iter.next(current_points); + verb != SkPath::kDone_Verb; + verb = iter.next(current_points)) { + switch (verb) { + case SkPath::kMove_Verb: { // iter.next returns 1 point + cairo_move_to(context_, current_points[0].fX, current_points[0].fY); + } break; + + case SkPath::kLine_Verb: { // iter.next returns 2 points + cairo_line_to(context_, current_points[1].fX, current_points[1].fY); + } break; + + case SkPath::kQuad_Verb: { // iter.next returns 3 points + cairo_curve_to(context_, + current_points[1].fX, current_points[1].fY, + current_points[2].fX, current_points[2].fY, + current_points[2].fX, current_points[2].fY); + } break; + + case SkPath::kCubic_Verb: { // iter.next returns 4 points + cairo_curve_to(context_, + current_points[1].fX, current_points[1].fY, + current_points[2].fX, current_points[2].fY, + current_points[3].fX, current_points[3].fY); + } break; + + case SkPath::kClose_Verb: { // iter.next returns 1 point (the last point) + cairo_close_path(context_); + } break; + + case SkPath::kDone_Verb: { // iter.next returns 0 points + } break; + + default: { + // Should not reach here! + SkASSERT(false); + } break; + } + } + + DoPaintStyle(paint); +} + +void VectorPlatformDevice::drawPoints(const SkDraw& draw, + SkCanvas::PointMode mode, + size_t count, + const SkPoint pts[], + const SkPaint& paint) { + SkASSERT(pts); + + if (!count) + return; + + // Setup paint color. + ApplyPaintColor(paint); + + // Setup stroke style. + ApplyStrokeStyle(paint); + + switch (mode) { + case SkCanvas::kPoints_PointMode: { + // There is a bug in Cairo that it won't draw anything when using some + // specific caps, e.g. SkPaint::kSquare_Cap. This is because Cairo does + // not have enough/ambiguous direction information. One possible work- + // around is to draw a really short line. + for (size_t i = 0; i < count; ++i) { + double x = pts[i].fX; + double y = pts[i].fY; + cairo_move_to(context_, x, y); + cairo_line_to(context_, x+.01, y); + } + } break; + + case SkCanvas::kLines_PointMode: { + if (count % 2) { + SkASSERT(false); + return; + } + + for (size_t i = 0; i < count >> 1; ++i) { + double x1 = pts[i << 1].fX; + double y1 = pts[i << 1].fY; + double x2 = pts[(i << 1) + 1].fX; + double y2 = pts[(i << 1) + 1].fY; + cairo_move_to(context_, x1, y1); + cairo_line_to(context_, x2, y2); + } + } break; + + case SkCanvas::kPolygon_PointMode: { + double x = pts[0].fX; + double y = pts[0].fY; + cairo_move_to(context_, x, y); + for (size_t i = 1; i < count; ++i) { + x = pts[i].fX; + y = pts[i].fY; + cairo_line_to(context_, x, y); + } + } break; + + default: + SkASSERT(false); + return; + } + cairo_stroke(context_); +} + +// TODO(myhuang): Support font family. +// TODO(myhuang): Support Stroke/Fill better. +void VectorPlatformDevice::drawPosText(const SkDraw& draw, + const void* text, + size_t len, + const SkScalar pos[], + SkScalar constY, + int scalarsPerPos, + const SkPaint& paint) { + SkASSERT(text); + SkASSERT(pos); + SkASSERT(paint.gettextEncoding() == SkPaint::kGlyphID_TextEncoding); + SkASSERT(scalarsPerPos == 2); // Each pos contains x and y. + + if (!len) + return; + + // Text color. + ApplyPaintColor(paint); + + cairo_set_font_size(context_, paint.getTextSize()); + + SkTypeface* typeface = paint.getTypeface(); + SkASSERT(typeface); + + cairo_font_slant_t font_slant = + typeface->isItalic() ? CAIRO_FONT_SLANT_ITALIC : CAIRO_FONT_SLANT_NORMAL; + + cairo_font_weight_t font_weight = + typeface->isBold() ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL; + + cairo_select_font_face(context_, "", font_slant, font_weight); + + // FIXME(myhuang): We now draw glyphs one by one. + // Maybe we should draw them altogether in the future. + const uint16_t* glyphIDs = reinterpret_cast<const uint16_t*>(text); + // scalarsPerPos should be 2 here in the loop! + for (size_t i = 0; i < len / scalarsPerPos; ++i) { + uint16_t glyphID = glyphIDs[i]; + + cairo_glyph_t glyph; + glyph.index = glyphID; + glyph.x = pos[i * scalarsPerPos + 0]; + glyph.y = pos[i * scalarsPerPos + 1]; + cairo_glyph_path(context_, &glyph, 1); + } + DoPaintStyle(paint); +} + +void VectorPlatformDevice::drawRect(const SkDraw& draw, + const SkRect& rect, + const SkPaint& paint) { + if (paint.getPathEffect()) { + // Draw a path instead. + SkPath path_orginal; + path_orginal.addRect(rect); + + // Apply the path effect to the rect. + SkPath path_modified; + paint.getFillPath(path_orginal, &path_modified); + + // Removes the path effect from the temporary SkPaint object. + SkPaint paint_no_effet(paint); + paint_no_effet.setPathEffect(NULL)->safeUnref(); + + // Draw the calculated path. + drawPath(draw, path_modified, paint_no_effet); + return; + } + + // Setup color. + ApplyPaintColor(paint); + + // Setup stroke style. + ApplyStrokeStyle(paint); + + // Draw rectangle. + cairo_rectangle(context_, + rect.fLeft, rect.fTop, + rect.fRight - rect.fLeft, rect.fBottom - rect.fTop); + + DoPaintStyle(paint); +} + +void VectorPlatformDevice::drawSprite(const SkDraw& draw, + const SkBitmap& bitmap, + int x, int y, + const SkPaint& paint) { + SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); + + LoadIdentityTransformToContext(); + + InternalDrawBitmap(bitmap, x, y, paint); + + // Restore the original matrix. + LoadTransformToContext(transform_); +} + +void VectorPlatformDevice::drawText(const SkDraw& draw, + const void* text, + size_t byteLength, + SkScalar x, + SkScalar y, + const SkPaint& paint) { + // This function isn't used in the code. Verify this assumption. + SkASSERT(false); +} + + +void VectorPlatformDevice::drawTextOnPath(const SkDraw& draw, + const void* text, + size_t len, + const SkPath& path, + const SkMatrix* matrix, + const SkPaint& paint) { + // This function isn't used in the code. Verify this assumption. + SkASSERT(false); +} + +void VectorPlatformDevice::drawVertices(const SkDraw& draw, + SkCanvas::VertexMode vmode, + int vertexCount, + const SkPoint vertices[], + const SkPoint texs[], + const SkColor colors[], + SkXfermode* xmode, + const uint16_t indices[], + int indexCount, + const SkPaint& paint) { + // This function isn't used in the code. Verify this assumption. + SkASSERT(false); +} + +void VectorPlatformDevice::setMatrixClip(const SkMatrix& transform, + const SkRegion& region) { + clip_region_ = region; + if (!clip_region_.isEmpty()) + LoadClipRegion(clip_region_); + + transform_ = transform; + LoadTransformToContext(transform_); +} + +void VectorPlatformDevice::ApplyPaintColor(const SkPaint& paint) { + SkColor color = paint.getColor(); + double a = static_cast<double>(SkColorGetA(color)) / 255.; + double r = static_cast<double>(SkColorGetR(color)) / 255.; + double g = static_cast<double>(SkColorGetG(color)) / 255.; + double b = static_cast<double>(SkColorGetB(color)) / 255.; + + cairo_set_source_rgba(context_, r, g, b, a); +} + +void VectorPlatformDevice::ApplyFillStyle(const SkPath& path) { + // Setup fill style. + // TODO(myhuang): Cairo does NOT support all skia fill rules!! + cairo_set_fill_rule(context_, + static_cast<cairo_fill_rule_t>(path.getFillType())); +} + +void VectorPlatformDevice::ApplyStrokeStyle(const SkPaint& paint) { + // Line width. + cairo_set_line_width(context_, paint.getStrokeWidth()); + + // Line join. + cairo_set_line_join(context_, + static_cast<cairo_line_join_t>(paint.getStrokeJoin())); + + // Line cap. + cairo_set_line_cap(context_, + static_cast<cairo_line_cap_t>(paint.getStrokeCap())); +} + +void VectorPlatformDevice::DoPaintStyle(const SkPaint& paint) { + SkPaint::Style style = paint.getStyle(); + + switch (style) { + case SkPaint::kFill_Style: { + cairo_fill(context_); + } break; + + case SkPaint::kStroke_Style: { + cairo_stroke(context_); + } break; + + case SkPaint::kStrokeAndFill_Style: { + cairo_fill_preserve(context_); + cairo_stroke(context_); + } break; + + default: + SkASSERT(false); + } +} + +void VectorPlatformDevice::InternalDrawBitmap(const SkBitmap& bitmap, + int x, int y, + const SkPaint& paint) { + SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); + + unsigned char alpha = paint.getAlpha(); + + if (alpha == 0) + return; + + int src_size_x = bitmap.width(); + int src_size_y = bitmap.height(); + + if (!src_size_x || !src_size_y) + return; + + SkAutoLockPixels image_lock(bitmap); + + cairo_surface_t* bitmap_surface = + cairo_image_surface_create_for_data( + reinterpret_cast<unsigned char*>(bitmap.getPixels()), + CAIRO_FORMAT_ARGB32, src_size_x, src_size_y, bitmap.rowBytes()); + + cairo_set_source_surface(context_, bitmap_surface, x, y); + cairo_paint_with_alpha(context_, static_cast<double>(alpha) / 255.); + + cairo_surface_destroy(bitmap_surface); +} + +void VectorPlatformDevice::LoadClipRegion(const SkRegion& clip) { + cairo_reset_clip(context_); + + LoadIdentityTransformToContext(); + + // TODO(myhuang): Support non-rect clips. + SkIRect bounding = clip.getBounds(); + cairo_rectangle(context_, bounding.fLeft, bounding.fTop, + bounding.fRight - bounding.fLeft, + bounding.fBottom - bounding.fTop); + cairo_clip(context_); + + // Restore the original matrix. + LoadTransformToContext(transform_); +} + +void VectorPlatformDevice::LoadIdentityTransformToContext() { + SkMatrix identity; + identity.reset(); + LoadTransformToContext(identity); +} + +void VectorPlatformDevice::LoadTransformToContext(const SkMatrix& matrix) { + cairo_matrix_t m; + m.xx = matrix[SkMatrix::kMScaleX]; + m.xy = matrix[SkMatrix::kMSkewX]; + m.x0 = matrix[SkMatrix::kMTransX]; + m.yx = matrix[SkMatrix::kMSkewY]; + m.yy = matrix[SkMatrix::kMScaleY]; + m.y0 = matrix[SkMatrix::kMTransY]; + cairo_set_matrix(context_, &m); +} + +} // namespace skia + diff --git a/skia/ext/vector_platform_device_linux.h b/skia/ext/vector_platform_device_linux.h new file mode 100644 index 0000000..0cd9512 --- /dev/null +++ b/skia/ext/vector_platform_device_linux.h @@ -0,0 +1,112 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SKIA_EXT_VECTOR_PLATFORM_DEVICE_LINUX_H_ +#define SKIA_EXT_VECTOR_PLATFORM_DEVICE_LINUX_H_ + +#include "skia/ext/platform_device.h" +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkRegion.h" + +typedef struct _cairo_surface cairo_surface_t; + +namespace skia { + +// This device is basically a wrapper that provides a surface for SkCanvas +// to draw into. It is basically an adaptor which converts skia APIs into +// cooresponding Cairo APIs and outputs to a Cairo PDF surface. Please NOTE +// that since it is completely vectorial, the bitmap content in it is thus +// meaningless. +class VectorPlatformDevice : public PlatformDevice { + public: + // Factory function. + static VectorPlatformDevice* create(int width, int height); + + virtual ~VectorPlatformDevice(); + + virtual bool IsVectorial() { return true; } + virtual PlatformSurface beginPlatformPaint() { return context_; } + + // We translate following skia APIs into corresponding Cairo APIs. + virtual void drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint); + virtual void drawDevice(const SkDraw& draw, SkDevice*, int x, int y, + const SkPaint&); + virtual void drawPaint(const SkDraw& draw, const SkPaint& paint); + virtual void drawPath(const SkDraw& draw, const SkPath& path, + const SkPaint& paint); + virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, + size_t count, const SkPoint[], const SkPaint& paint); + virtual void drawPosText(const SkDraw& draw, const void* text, size_t len, + const SkScalar pos[], SkScalar constY, + int scalarsPerPos, const SkPaint& paint); + virtual void drawRect(const SkDraw& draw, const SkRect& r, + const SkPaint& paint); + virtual void drawSprite(const SkDraw& draw, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint); + virtual void drawText(const SkDraw& draw, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint); + virtual void drawTextOnPath(const SkDraw& draw, const void* text, size_t len, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint); + virtual void drawVertices(const SkDraw& draw, SkCanvas::VertexMode, + int vertexCount, + const SkPoint verts[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint); + virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region); + + protected: + explicit VectorPlatformDevice(const SkBitmap& bitmap); + + private: + // Apply paint's color in the context. + void ApplyPaintColor(const SkPaint& paint); + + // Apply path's fill style in the context. + void ApplyFillStyle(const SkPath& path); + + // Apply paint's stroke style in the context. + void ApplyStrokeStyle(const SkPaint& paint); + + // Perform painting. + void DoPaintStyle(const SkPaint& paint); + + // Draws a bitmap in the the device, using the currently loaded matrix. + void InternalDrawBitmap(const SkBitmap& bitmap, int x, int y, + const SkPaint& paint); + + // Set up the clipping region for the context. Please note that now we only + // use the bounding box of the region for clipping. + // TODO(myhuang): Support non-rectangular clipping. + void LoadClipRegion(const SkRegion& clip); + + // Use identity matrix to set up context's transformation. + void LoadIdentityTransformToContext(); + + // Use matrix to set up context's transformation. + void LoadTransformToContext(const SkMatrix& matrix); + + // Transformation assigned to the context. + SkMatrix transform_; + + // The current clipping region. + SkRegion clip_region_; + + // Context's target surface. It is a PS/PDF surface. + cairo_surface_t* surface_; + + // Device context. + cairo_t* context_; + + // Copy & assign are not supported. + VectorPlatformDevice(const VectorPlatformDevice&); + const VectorPlatformDevice& operator=(const VectorPlatformDevice&); +}; + +} // namespace skia + +#endif // SKIA_EXT_VECTOR_PLATFORM_DEVICE_LINUX_H_ + diff --git a/skia/skia.gyp b/skia/skia.gyp index 9c33f6a..45e4281 100644 --- a/skia/skia.gyp +++ b/skia/skia.gyp @@ -529,6 +529,11 @@ 'ext/skia_utils_win.h', 'ext/vector_canvas.cc', 'ext/vector_canvas.h', + 'ext/vector_canvas_linux.cc', + 'ext/vector_canvas_win.cc', + 'ext/vector_platform_device.h', + 'ext/vector_platform_device_linux.cc', + 'ext/vector_platform_device_linux.h', 'ext/vector_platform_device_win.cc', 'ext/vector_platform_device_win.h', ], @@ -569,10 +574,6 @@ }], [ 'OS != "win"', { 'sources/': [ ['exclude', '_win\\.(cc|cpp)$'] ], - 'sources!': [ - 'ext/vector_canvas.cc', - 'ext/vector_device.cc', - ], }], [ 'OS == "linux"', { 'dependencies': [ |