diff options
author | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-11 21:38:30 +0000 |
---|---|---|
committer | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-11 21:38:30 +0000 |
commit | d1192bcc5436ea4c57b3317a1951060bdd9d4215 (patch) | |
tree | d7736733e1af4adc3840ecadc125eb6ab4bb68de | |
parent | 34bffb27918ff7ab2bf6d9a2cfd958a075a2deaa (diff) | |
download | chromium_src-d1192bcc5436ea4c57b3317a1951060bdd9d4215.zip chromium_src-d1192bcc5436ea4c57b3317a1951060bdd9d4215.tar.gz chromium_src-d1192bcc5436ea4c57b3317a1951060bdd9d4215.tar.bz2 |
Added functions to the Pepper interface to allow plugins to participate in the browser's print workflow. For now, added an interface for raster print output.
Also modified vector_platform_device_win.cc to allow the caller to set a bitmap compression mode for use in the internalDrawBitmap method. Supported compression modes are JPEG and PNG.
BUG=none
TEST=Test printing with new plugins that support this interface.
Review URL: http://codereview.chromium.org/669280
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41321 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/renderer/webplugin_delegate_pepper.cc | 195 | ||||
-rw-r--r-- | chrome/renderer/webplugin_delegate_pepper.h | 24 | ||||
-rw-r--r-- | third_party/npapi/bindings/npapi_extensions.h | 58 | ||||
-rw-r--r-- | webkit/glue/plugins/webplugin_print_delegate.h | 51 | ||||
-rw-r--r-- | webkit/glue/webplugin_delegate.h | 5 |
5 files changed, 331 insertions, 2 deletions
diff --git a/chrome/renderer/webplugin_delegate_pepper.cc b/chrome/renderer/webplugin_delegate_pepper.cc index e934737..31073e0 100644 --- a/chrome/renderer/webplugin_delegate_pepper.cc +++ b/chrome/renderer/webplugin_delegate_pepper.cc @@ -16,6 +16,8 @@ #include "app/gfx/blit.h" #if defined(OS_WIN) +#include "app/gfx/codec/jpeg_codec.h" +#include "app/gfx/gdi_util.h" #include "app/gfx/native_theme_win.h" #endif #include "base/file_util.h" @@ -25,12 +27,16 @@ #include "base/scoped_ptr.h" #include "base/stats_counters.h" #include "base/string_util.h" +#include "base/time.h" #if defined(OS_WIN) #include "base/win_util.h" #endif #include "chrome/common/render_messages.h" #include "chrome/renderer/render_thread.h" #include "chrome/renderer/webplugin_delegate_proxy.h" +#if defined(OS_WIN) +#include "skia/ext/vector_platform_device.h" +#endif #include "third_party/npapi/bindings/npapi_extensions.h" #include "third_party/npapi/bindings/npapi_extensions_private.h" #include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h" @@ -864,6 +870,116 @@ NPError WebPluginDelegatePepper::DeviceAudioDestroyContext( return NPERR_NO_ERROR; } +bool WebPluginDelegatePepper::PrintSupportsPrintExtension() { + return GetPrintExtensions() != NULL; +} + +int WebPluginDelegatePepper::PrintBegin(const gfx::Rect& printable_area, + int printer_dpi) { + int32 num_pages = 0; + NPPPrintExtensions* print_extensions = GetPrintExtensions(); + if (print_extensions) { + NPRect np_printable_area = {0}; + np_printable_area.left = printable_area.x(); + np_printable_area.top = printable_area.y(); + np_printable_area.right = np_printable_area.left + printable_area.width(); + np_printable_area.bottom = np_printable_area.top + printable_area.height(); + print_extensions->printBegin(instance()->npp(), + &np_printable_area, + printer_dpi, + &num_pages); + } + return num_pages; +} + +bool WebPluginDelegatePepper::PrintPage(int page_number, + const gfx::Rect& printable_area, + int printer_dpi, + WebKit::WebCanvas* canvas) { +#if defined(OS_WIN) || defined(OS_LINUX) + NPPPrintExtensions* print_extensions = GetPrintExtensions(); + if (!print_extensions) + return false; + + // Calculate the width and height needed for the raster image. + NPRect np_printable_area = {0}; + np_printable_area.left = printable_area.x(); + np_printable_area.top = printable_area.y(); + np_printable_area.right = np_printable_area.left + printable_area.width(); + np_printable_area.bottom = np_printable_area.top + printable_area.height(); + gfx::Size size_in_pixels; + if (!CalculatePrintedPageDimensions(page_number, &np_printable_area, + printer_dpi, print_extensions, + &size_in_pixels)) { + return false; + } + + // Now print the page onto a 2d device context. + scoped_ptr<Graphics2DDeviceContext> g2d(new Graphics2DDeviceContext(this)); + NPDeviceContext2DConfig config; + NPDeviceContext2D context; + gfx::Rect surface_rect(gfx::Point(0, 0), size_in_pixels); + NPError err = g2d->Initialize(surface_rect, &config, &context); + if (err != NPERR_NO_ERROR) { + NOTREACHED(); + return false; + } + err = print_extensions->printPageRaster( + instance()->npp(), page_number, &np_printable_area, printer_dpi, + &context); + if (err != NPERR_NO_ERROR) + return false; + + SkBitmap committed; + committed.setConfig(SkBitmap::kARGB_8888_Config, size_in_pixels.width(), + size_in_pixels.height()); + committed.allocPixels(); + err = g2d->Flush(&committed, &context, NULL, instance()->npp(), NULL); + if (err != NPERR_NO_ERROR) { + NOTREACHED(); + return false; + } + // Draw the printed image into the supplied canvas. + SkIRect src_rect; + src_rect.set(0, 0, size_in_pixels.width(), size_in_pixels.height()); + SkRect dest_rect; + dest_rect.set(SkIntToScalar(printable_area.x()), + SkIntToScalar(printable_area.y()), + SkIntToScalar(printable_area.x() + printable_area.width()), + SkIntToScalar(printable_area.y() + printable_area.height())); + bool draw_to_canvas = true; +#if defined(OS_WIN) + // Since this is a raster output, the size of the bitmap can be + // huge (especially at high printer DPIs). On Windows, this can + // result in a HUGE EMF (on Mac and Linux the output goes to PDF + // which appears to Flate compress the bitmap). So, if this bitmap + // is larger than 20 MB, we save the bitmap as a JPEG into the EMF + // DC. Note: We chose JPEG over PNG because JPEG compression seems + // way faster (about 4 times faster). + static const int kCompressionThreshold = 20 * 1024 * 1024; + if (committed.getSize() > kCompressionThreshold) { + DrawJPEGToPlatformDC(committed, printable_area, canvas); + draw_to_canvas = false; + } +#endif // OS_WIN + + if (draw_to_canvas) + canvas->drawBitmapRect(committed, &src_rect, dest_rect); + + return true; +#else // defined(OS_WIN) || defined(OS_LINUX) + NOTIMPLEMENTED(); + return false; +#endif // defined(OS_WIN) || defined(OS_LINUX) +} + +void WebPluginDelegatePepper::PrintEnd() { + NPPPrintExtensions* print_extensions = GetPrintExtensions(); + if (print_extensions) + print_extensions->printEnd(instance()->npp()); +} + + WebPluginDelegatePepper::WebPluginDelegatePepper( const base::WeakPtr<RenderView>& render_view, NPAPI::PluginInstance *instance) @@ -1119,3 +1235,82 @@ void WebPluginDelegatePepper::SendNestedDelegateGeometryToBrowser( render_view_->DidMovePlugin(geom); } +bool WebPluginDelegatePepper::CalculatePrintedPageDimensions( + int page_number, + NPRect* printable_area, + int printer_dpi, + NPPPrintExtensions* print_extensions, + gfx::Size* page_dimensions) { + int32 width_in_pixels = 0; + int32 height_in_pixels = 0; + NPError err = print_extensions->getRasterDimensions( + instance()->npp(), printable_area, printer_dpi, &width_in_pixels, + &height_in_pixels); + if (err != NPERR_NO_ERROR) + return false; + + DCHECK(width_in_pixels && height_in_pixels); + page_dimensions->SetSize(width_in_pixels, height_in_pixels); + return true; +} + +NPPPrintExtensions* WebPluginDelegatePepper::GetPrintExtensions() { + NPPPrintExtensions* ret = NULL; + NPPExtensions* extensions = NULL; + instance()->NPP_GetValue(NPPVPepperExtensions, &extensions); + if (extensions && extensions->getPrintExtensions) + ret = extensions->getPrintExtensions(instance()->npp()); + return ret; +} + +#if defined(OS_WIN) +bool WebPluginDelegatePepper::DrawJPEGToPlatformDC( + const SkBitmap& bitmap, + const gfx::Rect& printable_area, + WebKit::WebCanvas* canvas) { + skia::VectorPlatformDevice& device = + static_cast<skia::VectorPlatformDevice&>( + canvas->getTopPlatformDevice()); + HDC dc = device.getBitmapDC(); + // TODO(sanjeevr): This is a temporary hack. If we output a JPEG + // to the EMF, the EnumEnhMetaFile call fails in the browser + // process. The failure also happens if we output nothing here. + // We need to investigate the reason for this failure and fix it. + // In the meantime this temporary hack of drawing an empty + // rectangle in the DC gets us by. + Rectangle(dc, 0, 0, 0, 0); + + // Ideally we should add JPEG compression to the VectorPlatformDevice class + // However, Skia currently has no JPEG compression code and we cannot + // depend on app/gfx/jpeg_codec.h in Skia. So we do the compression here. + SkAutoLockPixels lock(bitmap); + DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); + const uint32_t* pixels = + static_cast<const uint32_t*>(bitmap.getPixels()); + std::vector<unsigned char> compressed_image; + base::TimeTicks start_time = base::TimeTicks::Now(); + bool encoded = gfx::JPEGCodec::Encode( + reinterpret_cast<const unsigned char*>(pixels), + gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), bitmap.height(), + static_cast<int>(bitmap.rowBytes()), 100, &compressed_image); + UMA_HISTOGRAM_TIMES("PepperPluginPrint.RasterBitmapCompressTime", + base::TimeTicks::Now() - start_time); + if (!encoded) { + NOTREACHED(); + return false; + } + BITMAPINFOHEADER bmi = {0}; + gfx::CreateBitmapHeader(bitmap.width(), bitmap.height(), &bmi); + bmi.biCompression = BI_JPEG; + bmi.biSizeImage = compressed_image.size(); + bmi.biHeight = -bmi.biHeight; + StretchDIBits(dc, printable_area.x(), printable_area.y(), + printable_area.width(), printable_area.height(), + 0, 0, bitmap.width(), bitmap.height(), + &compressed_image.front(), + reinterpret_cast<const BITMAPINFO*>(&bmi), + DIB_RGB_COLORS, SRCCOPY); + return true; +} +#endif // OS_WIN + diff --git a/chrome/renderer/webplugin_delegate_pepper.h b/chrome/renderer/webplugin_delegate_pepper.h index 789c6712..d316d2d 100644 --- a/chrome/renderer/webplugin_delegate_pepper.h +++ b/chrome/renderer/webplugin_delegate_pepper.h @@ -143,6 +143,13 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { NPDeviceFlushContextCallbackPtr callback, void* user_data); virtual NPError DeviceAudioDestroyContext(NPDeviceContextAudio* context); + // WebPluginPrintDelegate implementation. + virtual bool PrintSupportsPrintExtension(); + virtual int PrintBegin(const gfx::Rect& printable_area, int printer_dpi); + virtual bool PrintPage(int page_number, const gfx::Rect& printable_area, + int printer_dpi, WebKit::WebCanvas* canvas); + virtual void PrintEnd(); + // End of WebPluginDelegate implementation. gfx::Rect GetRect() const { return window_rect_; } @@ -175,6 +182,23 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { void ForwardSetWindow(); + // A helper method that invokes the plugin's Print extensions to calculate + // the size needed in pixels to render the given page in a raster format. + bool CalculatePrintedPageDimensions(int page_number, + NPRect* printable_area, + int printer_dpi, + NPPPrintExtensions* print_extensions, + gfx::Size* page_dimensions); + NPPPrintExtensions* GetPrintExtensions(); + +#if defined(OS_WIN) + // Compresses the given bitmap as JPEG and draws it into the backing platform + // DC (Windows-only). + bool DrawJPEGToPlatformDC(const SkBitmap& bitmap, + const gfx::Rect& printable_area, + WebKit::WebCanvas* canvas); +#endif // OS_WIN + #if defined(ENABLE_GPU) void ForwardHandleRepaint(NPP npp, NPDeviceContext3D* context); diff --git a/third_party/npapi/bindings/npapi_extensions.h b/third_party/npapi/bindings/npapi_extensions.h index 1f18a10..4c22ff0 100644 --- a/third_party/npapi/bindings/npapi_extensions.h +++ b/third_party/npapi/bindings/npapi_extensions.h @@ -11,11 +11,17 @@ #include "npapi.h" /* - * A fake "enum" value for getting Pepper extensions. + * A fake "enum" value for getting browser-implemented Pepper extensions. * The variable returns a pointer to an NPPepperExtensions structure */ #define NPNVPepperExtensions ((NPNVariable) 4000) +/* + * A fake "enum" value for getting plugin-implemented Pepper extensions. + * The variable returns a pointer to an NPPPepperExtensions structure + */ +#define NPPVPepperExtensions ((NPPVariable) 4001) + typedef void NPDeviceConfig; typedef void NPDeviceContext; typedef void NPUserData; @@ -482,4 +488,54 @@ struct _NPDeviceContextAudio { void *reserved; }; +/* Printing related APIs ---------------------------------------------------*/ + +/* Being a print operation. Returns the total number of pages to print at the + * given printableArea size and DPI. printableArea is in points (a point is 1/72 + * of an inch). */ +typedef NPError (*NPPPrintBeginPtr) ( + NPP instance, + NPRect* printableArea, + int32 printerDPI, + int32* numPages); +/* Returns the required raster dimensions for the given printableArea + * size and DPI. printableArea is in points (a point is 1/72 of an inch). */ +typedef NPError (*NPPGetRasterDimensionsPtr) ( + NPP instance, + NPRect* printableArea, + int32 printerDPI, + int32* widthInPixels, + int32* heightInPixels); +/* Prints the specified page on the given printableArea size and DPI. + * printableArea is in points (a point is 1/72 of an inch). This allows the + * plugin to print a raster output*/ +typedef NPError (*NPPPrintPageRasterPtr) ( + NPP instance, + int32 pageNumber, + NPRect* printableArea, + int32 printerDPI, + NPDeviceContext2D* printSurface); + +/* Ends the print operation */ +typedef NPError (*NPPPrintEndPtr) (NPP instance); + +/* TODO(sanjeevr) : Provide a vector interface for printing. We need to decide + * on a vector format that can support embedded fonts. A vector format will + * greatly reduce the size of the required output buffer +*/ + +typedef struct _NPPPrintExtensions { + NPPPrintBeginPtr printBegin; + NPPGetRasterDimensionsPtr getRasterDimensions; + NPPPrintPageRasterPtr printPageRaster; + NPPPrintEndPtr printEnd; +} NPPPrintExtensions; + +/* Returns NULL if the plugin does not support print extensions */ +typedef NPPPrintExtensions* (*NPPGetPrintExtensionsPtr)(NPP instance); + +typedef struct _NPPExtensions { + NPPGetPrintExtensionsPtr getPrintExtensions; +} NPPExtensions; + #endif /* _NP_EXTENSIONS_H_ */ diff --git a/webkit/glue/plugins/webplugin_print_delegate.h b/webkit/glue/plugins/webplugin_print_delegate.h new file mode 100644 index 0000000..1cd5b6d --- /dev/null +++ b/webkit/glue/plugins/webplugin_print_delegate.h @@ -0,0 +1,51 @@ +// Copyright (c) 2010 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 WEBKIT_GLUE_PLUGINS_WEBPLUGIN_PRINT_DELEGATE_H_ +#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_PRINT_DELEGATE_H_ + +#include "base/basictypes.h" +#include "third_party/npapi/bindings/npapi_extensions.h" + +namespace gfx { +class Rect; +} + +namespace webkit_glue { + +// Interface for the NPAPI print extension. This class implements "NOP" +// versions of all these functions so it can be used seamlessly by the +// "regular" plugin delegate while being overridden by the "pepper" one. +class WebPluginPrintDelegate { + public: + // If a plugin supports print extensions, then it gets to participate fully + // in the browser's print workflow by specifying the number of pages to be + // printed and providing a print output for specified pages. + virtual bool PrintSupportsPrintExtension() { + return false; + } + + // Note: printable_area is in points (a point is 1/72 of an inch). + virtual int PrintBegin(const gfx::Rect& printable_area, int printer_dpi) { + return 0; + } + + // Note: printable_area is in points (a point is 1/72 of an inch). + virtual bool PrintPage(int page_number, const gfx::Rect& printable_area, + int printer_dpi, WebKit::WebCanvas* canvas) { + return false; + } + + virtual void PrintEnd() { + } + + protected: + WebPluginPrintDelegate() {} + virtual ~WebPluginPrintDelegate() {} +}; + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_PRINT_DELEGATE_H_ + diff --git a/webkit/glue/webplugin_delegate.h b/webkit/glue/webplugin_delegate.h index 52c7186..8285d6c 100644 --- a/webkit/glue/webplugin_delegate.h +++ b/webkit/glue/webplugin_delegate.h @@ -17,6 +17,8 @@ #include "webkit/glue/plugins/webplugin_2d_device_delegate.h" #include "webkit/glue/plugins/webplugin_3d_device_delegate.h" #include "webkit/glue/plugins/webplugin_audio_device_delegate.h" +#include "webkit/glue/plugins/webplugin_print_delegate.h" + class FilePath; class GURL; @@ -39,7 +41,8 @@ class WebPluginResourceClient; // This is the interface that a plugin implementation needs to provide. class WebPluginDelegate : public WebPlugin2DDeviceDelegate, public WebPlugin3DDeviceDelegate, - public WebPluginAudioDeviceDelegate { + public WebPluginAudioDeviceDelegate, + public WebPluginPrintDelegate { public: virtual ~WebPluginDelegate() {} |