diff options
-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': [ |