[go: nahoru, domu]

Print only the focused frame.  This makes more sense than trying to print all frames since most of the time you end up with ugly clipping and scroll bars on the printed page.
This also fixes printing issue with print selection since we don't pick up the currently selected text if's not in the main frame.
Also did some refactoring of the printing test in render_view_unittest.  Mainly to reuse the new Image class.

BUG=http://crbug.com/15250
TEST=Print pages with frames.  Print selection when using multiple frames (one example in bug).

Review URL: http://codereview.chromium.org/149644

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20876 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index e78c383ee..e76e2f3 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -3221,8 +3221,6 @@
         'renderer/mock_keyboard_driver_win.h',
         'renderer/mock_printer.cc',
         'renderer/mock_printer.h',
-        'renderer/mock_printer_driver_win.cc',
-        'renderer/mock_printer_driver_win.h',
         'renderer/mock_render_process.h',
         'renderer/mock_render_thread.cc',
         'renderer/mock_render_thread.h',
diff --git a/chrome/renderer/mock_printer.cc b/chrome/renderer/mock_printer.cc
index 2454a0c..4462ce7 100644
--- a/chrome/renderer/mock_printer.cc
+++ b/chrome/renderer/mock_printer.cc
@@ -5,9 +5,7 @@
 #include "chrome/renderer/mock_printer.h"
 
 #include "base/file_util.h"
-#include "base/gfx/png_encoder.h"
 #include "base/logging.h"
-#include "base/md5.h"
 #include "base/shared_memory.h"
 #include "chrome/common/ipc_message_utils.h"
 #include "chrome/common/render_messages.h"
@@ -116,8 +114,12 @@
   base::SharedMemory emf_data(params.metafile_data_handle, true,
                               GetCurrentProcess());
   emf_data.Map(params.data_size);
-  MockPrinterPage* page_data = driver_.LoadSource(emf_data.memory(),
-                                                  params.data_size);
+  printing::NativeMetafile metafile;
+  metafile.CreateFromData(emf_data.memory(), params.data_size);
+  printing::Image image(metafile);
+  MockPrinterPage* page_data = new MockPrinterPage(emf_data.memory(),
+                                                   params.data_size,
+                                                   image);
   if (!page_data) {
     printer_status_ = PRINTER_ERROR;
     return;
@@ -140,6 +142,13 @@
   return page_number_;
 }
 
+const MockPrinterPage* MockPrinter::GetPrintedPage(size_t pageno) const {
+  if (pages_.size() > pageno)
+    return pages_[pageno];
+  else
+    return NULL;
+}
+
 int MockPrinter::GetWidth(size_t page) const {
   if (printer_status_ != PRINTER_READY || page >= pages_.size())
     return -1;
@@ -152,27 +161,10 @@
   return pages_[page]->height();
 }
 
-bool MockPrinter::GetSourceChecksum(size_t page, std::string* checksum) const {
-  if (printer_status_ != PRINTER_READY || page >= pages_.size())
-    return false;
-  return GetChecksum(pages_[page]->source_data(), pages_[page]->source_size(),
-                     checksum);
-}
-
 bool MockPrinter::GetBitmapChecksum(size_t page, std::string* checksum) const {
   if (printer_status_ != PRINTER_READY || page >= pages_.size())
     return false;
-  return GetChecksum(pages_[page]->bitmap_data(), pages_[page]->bitmap_size(),
-                     checksum);
-}
-
-bool MockPrinter::GetBitmap(size_t page,
-                            const void** data,
-                            size_t* size) const {
-  if (printer_status_ != PRINTER_READY || page >= pages_.size())
-    return false;
-  *data = pages_[page]->bitmap_data();
-  *size = pages_[page]->bitmap_size();
+  *checksum = pages_[page]->image().checksum();
   return true;
 }
 
@@ -191,17 +183,8 @@
                              const std::wstring& filename) const {
   if (printer_status_ != PRINTER_READY || page >= pages_.size())
     return false;
-  int width = pages_[page]->width();
-  int height = pages_[page]->height();
-  int row_byte_width = width * 4;
-  const uint8* bitmap_data = pages_[page]->bitmap_data();
-  std::vector<unsigned char> compressed;
-  PNGEncoder::Encode(bitmap_data,
-                     PNGEncoder::FORMAT_BGRA, width, height,
-                     row_byte_width, true, &compressed);
-  file_util::WriteFile(filename,
-                       reinterpret_cast<char*>(&*compressed.begin()),
-                       compressed.size());
+
+  pages_[page]->image().SaveToPng(filename);
   return true;
 }
 
@@ -209,11 +192,3 @@
   return ++current_document_cookie_;
 }
 
-bool MockPrinter::GetChecksum(const void* data,
-                              size_t size,
-                              std::string* checksum) const {
-  MD5Digest digest;
-  MD5Sum(data, size, &digest);
-  checksum->assign(HexEncode(&digest, sizeof(digest)));
-  return true;
-}
diff --git a/chrome/renderer/mock_printer.h b/chrome/renderer/mock_printer.h
index 16577cb..ba3f0deb 100644
--- a/chrome/renderer/mock_printer.h
+++ b/chrome/renderer/mock_printer.h
@@ -11,10 +11,7 @@
 #include "base/basictypes.h"
 #include "base/ref_counted.h"
 #include "base/scoped_ptr.h"
-
-#if defined(OS_WIN)
-#include "chrome/renderer/mock_printer_driver_win.h"
-#endif
+#include "printing/image.h"
 
 struct ViewMsg_Print_Params;
 struct ViewMsg_PrintPages_Params;
@@ -26,49 +23,30 @@
 // a smart pointer of this object (i.e. scoped_refptr<>).
 class MockPrinterPage : public base::RefCounted<MockPrinterPage> {
  public:
-  MockPrinterPage()
-      : width_(0),
-        height_(0),
-        source_size_(0),
-        bitmap_size_(0) {
-  }
-
-  MockPrinterPage(int width,
-                  int height,
-                  const void* source_data,
+  MockPrinterPage(const void* source_data,
                   size_t source_size,
-                  const void* bitmap_data,
-                  size_t bitmap_size)
-      : width_(width),
-        height_(height),
-        source_size_(source_size),
-        bitmap_size_(bitmap_size) {
-    // Create copies of the source data and the bitmap data.
+                  const printing::Image& image)
+        : source_size_(source_size),
+          image_(image) {
+    // Create copies of the source data
     source_data_.reset(new uint8[source_size]);
     if (source_data_.get())
       memcpy(source_data_.get(), source_data, source_size);
-    bitmap_data_.reset(new uint8[bitmap_size]);
-    if (bitmap_data_.get())
-      memcpy(bitmap_data_.get(), bitmap_data, bitmap_size);
   }
 
   ~MockPrinterPage() {
   }
 
-  int width() { return width_; }
-  int height() { return height_; }
-  const uint8* source_data() { return source_data_.get(); }
-  const size_t source_size() { return source_size_; }
-  const uint8* bitmap_data() { return bitmap_data_.get(); }
-  const size_t bitmap_size() { return bitmap_size_; }
+  int width() const { return image_.size().width(); }
+  int height() const { return image_.size().height(); }
+  const uint8* source_data() const { return source_data_.get(); }
+  const size_t source_size() const { return source_size_; }
+  const printing::Image& image() const { return image_; }
 
  private:
-  int width_;
-  int height_;
   size_t source_size_;
   scoped_array<uint8> source_data_;
-  size_t bitmap_size_;
-  scoped_array<uint8> bitmap_data_;
+  printing::Image image_;
 
   DISALLOW_COPY_AND_ASSIGN(MockPrinterPage);
 };
@@ -108,9 +86,13 @@
   // Functions that retrieve the output pages.
   Status GetPrinterStatus() const { return printer_status_; }
   int GetPrintedPages() const;
+
+  // Get a pointer to the printed page, returns NULL if pageno has not been
+  // printed.  The pointer is for read only view and should not be deleted.
+  const MockPrinterPage* GetPrintedPage(size_t pageno) const;
+
   int GetWidth(size_t page) const;
   int GetHeight(size_t page) const;
-  bool GetSourceChecksum(size_t page, std::string* checksum) const;
   bool GetBitmapChecksum(size_t page, std::string* checksum) const;
   bool GetSource(size_t page, const void** data, size_t* size) const;
   bool GetBitmap(size_t page, const void** data, size_t* size) const;
@@ -149,10 +131,6 @@
   int page_number_;
   std::vector<scoped_refptr<MockPrinterPage> > pages_;
 
-#if defined(OS_WIN)
-  MockPrinterDriverWin driver_;
-#endif
-
   DISALLOW_COPY_AND_ASSIGN(MockPrinter);
 };
 
diff --git a/chrome/renderer/mock_printer_driver_win.cc b/chrome/renderer/mock_printer_driver_win.cc
deleted file mode 100644
index 8fefd8e..0000000
--- a/chrome/renderer/mock_printer_driver_win.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-// 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/mock_printer_driver_win.h"
-
-#include "base/gfx/gdi_util.h"
-#include "base/logging.h"
-#include "printing/emf_win.h"
-#include "chrome/renderer/mock_printer.h"
-#include "skia/ext/platform_device.h"
-
-namespace {
-
-// A simple class which temporarily overrides system settings.
-// The bitmap image rendered via the PlayEnhMetaFile() function depends on
-// some system settings.
-// As a workaround for such dependency, this class saves the system settings
-// and changes them. This class also restore the saved settings in its
-// destructor.
-class SystemSettingsOverride {
- public:
-  SystemSettingsOverride() : font_smoothing_(0) {
-  }
-
-  ~SystemSettingsOverride() {
-    SystemParametersInfo(SPI_SETFONTSMOOTHING, font_smoothing_, NULL, 0);
-  }
-
-  BOOL Init(BOOL font_smoothing) {
-    if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing_, 0))
-      return FALSE;
-    return SystemParametersInfo(SPI_SETFONTSMOOTHING, font_smoothing, NULL, 0);
-  }
-
- private:
-  BOOL font_smoothing_;
-};
-
-// A class which renders an EMF data and returns a raw bitmap data.
-// The bitmap data returned from Create() is deleted in the destructor of this
-// class. So, we need to create a copy of this bitmap data if it is used after
-// this object is deleted,
-class EmfRenderer {
- public:
-  EmfRenderer() : dc_(NULL), bitmap_(NULL) {
-  }
-
-  ~EmfRenderer() {
-    if (bitmap_) {
-      DeleteObject(bitmap_);
-      bitmap_ = NULL;
-    }
-    if (dc_) {
-      DeleteDC(dc_);
-      dc_ = NULL;
-    }
-  }
-
-  const void* Create(int width, int height, const printing::Emf* emf) {
-    CHECK(!dc_ && !bitmap_);
-
-    BITMAPV4HEADER header;
-    gfx::CreateBitmapV4Header(width, height, &header);
-
-    dc_ = CreateCompatibleDC(NULL);
-    if (!dc_)
-      return NULL;
-
-    void* bits;
-    bitmap_ = CreateDIBSection(dc_, reinterpret_cast<BITMAPINFO*>(&header), 0,
-                               &bits, NULL, 0);
-    if (!bitmap_ || !bits)
-      return NULL;
-
-    SelectObject(dc_, bitmap_);
-
-    skia::PlatformDevice::InitializeDC(dc_);
-    emf->Playback(dc_, NULL);
-
-    return reinterpret_cast<uint8*>(bits);
-  }
-
- private:
-  HDC dc_;
-  HBITMAP bitmap_;
-};
-
-}  // namespace
-
-MockPrinterDriverWin::MockPrinterDriverWin() {
-}
-
-MockPrinterDriverWin::~MockPrinterDriverWin() {
-}
-
-MockPrinterPage* MockPrinterDriverWin::LoadSource(const void* source_data,
-                                                  size_t source_size) {
-  // This code is mostly copied from the Image::LoadEMF() function in
-  // "src/chrome/browser/printing/printing_layout_uitest.cc".
-  printing::Emf emf;
-  emf.CreateFromData(source_data, source_size);
-  gfx::Rect rect(emf.GetBounds());
-
-  // Create a temporary DC and bitmap to retrieve the renderered data.
-  if (rect.width() <= 0 || rect.height() <= 0)
-    return NULL;
-
-  // Disable the font-smoothing feature of Windows.
-  SystemSettingsOverride system_settings;
-  system_settings.Init(FALSE);
-
-  // Render the EMF data and create a bitmap.
-  EmfRenderer renderer;
-  const void* bitmap_data = renderer.Create(rect.width(), rect.height(), &emf);
-  if (!bitmap_data)
-    return NULL;
-
-  // Create a new MockPrinterPage instance and return it.
-  size_t row_byte_width = rect.width() * 4;
-  size_t bitmap_size = row_byte_width * rect.height();
-  return new MockPrinterPage(rect.width(), rect.height(),
-                             source_data, source_size,
-                             bitmap_data, bitmap_size);
-}
diff --git a/chrome/renderer/mock_printer_driver_win.h b/chrome/renderer/mock_printer_driver_win.h
deleted file mode 100644
index 117aa16..0000000
--- a/chrome/renderer/mock_printer_driver_win.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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 CHROME_RENDERER_MOCK_PRINTER_DRIVER_WIN_H_
-#define CHROME_RENDERER_MOCK_PRINTER_DRIVER_WIN_H_
-
-#include "base/basictypes.h"
-
-class MockPrinterPage;
-
-// Implements the platform-dependent part of a pseudo printer object.
-// This class renders a platform-dependent input and creates a MockPrinterPage
-// instance.
-class MockPrinterDriverWin {
- public:
-  MockPrinterDriverWin();
-  ~MockPrinterDriverWin();
-
-  MockPrinterPage* LoadSource(const void* data, size_t size);
-
-  DISALLOW_COPY_AND_ASSIGN(MockPrinterDriverWin);
-};
-
-#endif  // CHROME_RENDERER_MOCK_PRINTER_DRIVER_WIN_H_
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 96cf524a..9967ca9 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -451,8 +451,14 @@
 
 void RenderView::OnPrintPages() {
   DCHECK(webview());
-  if (webview())
-    Print(webview()->GetMainFrame(), false);
+  if (webview()) {
+    // If the user has selected text in the currently focused frame we print
+    // only that frame (this makes print selection work for multiple frames).
+    if (webview()->GetFocusedFrame()->HasSelection())
+      Print(webview()->GetFocusedFrame(), false);
+    else
+      Print(webview()->GetMainFrame(), false);
+  }
 }
 
 void RenderView::OnPrintingDone(int document_cookie, bool success) {
@@ -2309,7 +2315,11 @@
 }
 
 void RenderView::ScriptedPrint(WebFrame* frame) {
-  Print(frame, true);
+  DCHECK(webview());
+  if (webview()) {
+    // Print the full page - not just the frame the javascript is running from.
+    Print(webview()->GetMainFrame(), true);
+  }
 }
 
 void RenderView::UserMetricsRecordAction(const std::wstring& action) {
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index c8afcd2..4310d47 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -390,6 +390,7 @@
   FRIEND_TEST(RenderViewTest, ImeComposition);
   FRIEND_TEST(RenderViewTest, OnSetTextDirection);
   FRIEND_TEST(RenderViewTest, OnPrintPages);
+  FRIEND_TEST(RenderViewTest, PrintWithIframe);
   FRIEND_TEST(RenderViewTest, PrintLayoutTest);
   FRIEND_TEST(RenderViewTest, OnHandleKeyboardEvent);
   FRIEND_TEST(RenderViewTest, InsertCharacters);
diff --git a/chrome/renderer/render_view_unittest.cc b/chrome/renderer/render_view_unittest.cc
index 4b4ed60..135c78d 100644
--- a/chrome/renderer/render_view_unittest.cc
+++ b/chrome/renderer/render_view_unittest.cc
@@ -3,10 +3,13 @@
 // found in the LICENSE file.
 
 #include "base/file_util.h"
+#include "base/shared_memory.h"
 #include "chrome/common/native_web_keyboard_event.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/test/render_view_test.h"
 #include "net/base/net_errors.h"
+#include "printing/image.h"
+#include "printing/native_metafile.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "webkit/api/public/WebURLError.h"
 
@@ -363,6 +366,45 @@
 #endif
 }
 
+TEST_F(RenderViewTest, PrintWithIframe) {
+#if defined(OS_WIN)
+  // Document that populates an iframe..
+  const char html[] =
+      "<html><body>Lorem Ipsum:"
+      "<iframe name=\"sub1\" id=\"sub1\"></iframe><script>"
+      "  document.write(frames['sub1'].name);"
+      "  frames['sub1'].document.write("
+      "      '<p>Cras tempus ante eu felis semper luctus!</p>');"
+      "</script></body></html>";
+
+  LoadHTML(html);
+
+  // Find the frame and set it as the focused one.  This should mean that that
+  // the printout should only contain the contents of that frame.
+  WebFrame* sub1_frame = view_->webview()->GetFrameWithName(L"sub1");
+  ASSERT_TRUE(sub1_frame);
+  view_->webview()->SetFocusedFrame(sub1_frame);
+  ASSERT_NE(view_->webview()->GetFocusedFrame(),
+            view_->webview()->GetMainFrame());
+
+  // Initiate printing.
+  view_->OnPrintPages();
+
+  // Verify output through MockPrinter.
+  const MockPrinter* printer(render_thread_.printer());
+  ASSERT_EQ(1, printer->GetPrintedPages());
+  const printing::Image& image1(printer->GetPrintedPage(0)->image());
+
+  // TODO(sverrir): Figure out a way to improve this test to actually print
+  // only the content of the iframe.  Currently image1 will contain the full
+  // page.
+  EXPECT_NE(0, image1.size().width());
+  EXPECT_NE(0, image1.size().height());
+#else
+  NOTIMPLEMENTED();
+#endif
+}
+
 // Tests if we can print a page and verify its results.
 // This test prints HTML pages into a pseudo printer and check their outputs,
 // i.e. a simplified version of the PrintingLayoutTextTest UI test.
@@ -431,14 +473,6 @@
     if (kTestPages[i].checksum)
       EXPECT_EQ(kTestPages[i].checksum, bitmap_actual);
 
-    // Retrieve the bitmap data from the pseudo printer.
-    // TODO(hbono): implement a function which retrieves an expected result
-    // from a file and compares it with this bitmap data.
-    const void* bitmap_data;
-    size_t bitmap_size;
-    EXPECT_TRUE(render_thread_.printer()->GetBitmap(0, &bitmap_data,
-                                                    &bitmap_size));
-
     if (baseline) {
       // Save the source data and the bitmap data into temporary files to
       // create base-line results.
diff --git a/printing/image.cc b/printing/image.cc
index 8f397d8..667adae 100644
--- a/printing/image.cc
+++ b/printing/image.cc
@@ -8,15 +8,59 @@
 #include "base/gfx/png_decoder.h"
 #include "base/gfx/png_encoder.h"
 #include "base/gfx/rect.h"
+#include "base/md5.h"
 #include "base/string_util.h"
-#include "printing/native_metafile.h"
 #include "skia/ext/platform_device.h"
 
 #if defined(OS_WIN)
 #include "base/gfx/gdi_util.h"  // EMF support
 #endif
 
-printing::Image::Image(const std::wstring& filename) : ignore_alpha_(true) {
+namespace {
+
+// A simple class which temporarily overrides system settings.
+// The bitmap image rendered via the PlayEnhMetaFile() function depends on
+// some system settings.
+// As a workaround for such dependency, this class saves the system settings
+// and changes them. This class also restore the saved settings in its
+// destructor.
+class DisableFontSmoothing {
+ public:
+  explicit DisableFontSmoothing(bool disable) : enable_again_(false) {
+    if (disable) {
+#if defined(OS_WIN)
+      BOOL enabled;
+      if (SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &enabled, 0) &&
+          enabled) {
+        if (SystemParametersInfo(SPI_SETFONTSMOOTHING, FALSE, NULL, 0))
+          enable_again_ = true;
+      }
+#endif
+    }
+  }
+
+  ~DisableFontSmoothing() {
+    if (enable_again_) {
+#if defined(OS_WIN)
+      BOOL result = SystemParametersInfo(SPI_SETFONTSMOOTHING, TRUE, NULL, 0);
+      DCHECK(result);
+#endif
+    }
+  }
+
+ private:
+  bool enable_again_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(DisableFontSmoothing);
+};
+
+}  // namespace
+
+namespace printing {
+
+Image::Image(const std::wstring& filename)
+    : row_length_(0),
+      ignore_alpha_(true) {
   std::string data;
   file_util::ReadFileToString(filename, &data);
   std::wstring ext = file_util::GetFileExtensionFromPath(filename);
@@ -35,7 +79,26 @@
   }
 }
 
-bool printing::Image::SaveToPng(const std::wstring& filename) {
+Image::Image(const NativeMetafile& metafile)
+    : row_length_(0),
+      ignore_alpha_(true) {
+  LoadMetafile(metafile);
+}
+
+Image::Image(const Image& image)
+    : size_(image.size_),
+      row_length_(image.row_length_),
+      data_(image.data_),
+      ignore_alpha_(image.ignore_alpha_) {
+}
+
+std::string Image::checksum() const {
+  MD5Digest digest;
+  MD5Sum(&data_[0], data_.size(), &digest);
+  return HexEncode(&digest, sizeof(digest));
+}
+
+bool Image::SaveToPng(const std::wstring& filename) const {
   DCHECK(!data_.empty());
   std::vector<unsigned char> compressed;
   bool success = PNGEncoder::Encode(&*data_.begin(),
@@ -56,7 +119,7 @@
   return success;
 }
 
-double printing::Image::PercentageDifferent(const Image& rhs) const {
+double Image::PercentageDifferent(const Image& rhs) const {
   if (size_.width() == 0 || size_.height() == 0 ||
     rhs.size_.width() == 0 || rhs.size_.height() == 0)
     return 100.;
@@ -113,7 +176,7 @@
   return static_cast<double>(pixels_different) / total_pixels * 100.;
 }
 
-bool printing::Image::LoadPng(const std::string& compressed) {
+bool Image::LoadPng(const std::string& compressed) {
   int w;
   int h;
   bool success = PNGDecoder::Decode(
@@ -124,39 +187,54 @@
   return success;
 }
 
-bool printing::Image::LoadMetafile(const std::string& data) {
+bool Image::LoadMetafile(const std::string& data) {
   DCHECK(!data.empty());
 #if defined(OS_WIN)
-  printing::NativeMetafile metafile;
+  NativeMetafile metafile;
   metafile.CreateFromData(data.data(), data.size());
-  gfx::Rect rect(metafile.GetBounds());
-  // Create a temporary HDC and bitmap to retrieve the rendered data.
-  HDC hdc = CreateCompatibleDC(NULL);
-  BITMAPV4HEADER hdr;
-  DCHECK_EQ(rect.x(), 0);
-  DCHECK_EQ(rect.y(), 0);
-  DCHECK_GT(rect.width(), 0);
-  DCHECK_GT(rect.height(), 0);
-  size_ = rect.size();
-  gfx::CreateBitmapV4Header(rect.width(), rect.height(), &hdr);
-  void* bits;
-  HBITMAP bitmap = CreateDIBSection(hdc,
-                                    reinterpret_cast<BITMAPINFO*>(&hdr), 0,
-                                    &bits, NULL, 0);
-  DCHECK(bitmap);
-  DCHECK(SelectObject(hdc, bitmap));
-  skia::PlatformDevice::InitializeDC(hdc);
-  bool success = metafile.Playback(hdc, NULL);
-  row_length_ = size_.width() * sizeof(uint32);
-  size_t bytes = row_length_ * size_.height();
-  DCHECK(bytes);
-  data_.resize(bytes);
-  memcpy(&*data_.begin(), bits, bytes);
-  DeleteDC(hdc);
-  DeleteObject(bitmap);
-  return success;
+  return LoadMetafile(metafile);
 #else
   NOTIMPLEMENTED();
   return false;
 #endif
 }
+
+bool Image::LoadMetafile(const NativeMetafile& metafile) {
+#if defined(OS_WIN)
+  gfx::Rect rect(metafile.GetBounds());
+  DisableFontSmoothing disable_in_this_scope(true);
+  // Create a temporary HDC and bitmap to retrieve the rendered data.
+  HDC hdc = CreateCompatibleDC(NULL);
+  BITMAPV4HEADER hdr;
+  DCHECK_EQ(rect.x(), 0);
+  DCHECK_EQ(rect.y(), 0);
+  DCHECK_GE(rect.width(), 0);  // Metafile could be empty.
+  DCHECK_GE(rect.height(), 0);
+  if (rect.width() > 0 && rect.height() > 0) {
+    size_ = rect.size();
+    gfx::CreateBitmapV4Header(rect.width(), rect.height(), &hdr);
+    void* bits;
+    HBITMAP bitmap = CreateDIBSection(hdc,
+                                      reinterpret_cast<BITMAPINFO*>(&hdr), 0,
+                                      &bits, NULL, 0);
+    DCHECK(bitmap);
+    DCHECK(SelectObject(hdc, bitmap));
+    skia::PlatformDevice::InitializeDC(hdc);
+    bool success = metafile.Playback(hdc, NULL);
+    row_length_ = size_.width() * sizeof(uint32);
+    size_t bytes = row_length_ * size_.height();
+    DCHECK(bytes);
+    data_.resize(bytes);
+    memcpy(&*data_.begin(), bits, bytes);
+    DeleteDC(hdc);
+    DeleteObject(bitmap);
+    return success;
+  }
+#else
+  NOTIMPLEMENTED();
+#endif
+
+  return false;
+}
+
+}  // namespace printing
diff --git a/printing/image.h b/printing/image.h
index 8ef1007..a927549 100644
--- a/printing/image.h
+++ b/printing/image.h
@@ -11,10 +11,11 @@
 #include "base/basictypes.h"
 #include "base/gfx/size.h"
 #include "base/logging.h"
+#include "printing/native_metafile.h"
 
 namespace printing {
 
-// Lightweight raw-bitmap management. The image, once initialized, is immuable.
+// Lightweight raw-bitmap management. The image, once initialized, is immutable.
 // The main purpose is testing image contents.
 class Image {
  public:
@@ -23,12 +24,22 @@
   // If image loading fails size().IsEmpty() will be true.
   explicit Image(const std::wstring& filename);
 
+  // Creates the image from the metafile.  Deduces bounds based on bounds in
+  // metafile.  If loading fails size().IsEmpty() will be true.
+  explicit Image(const NativeMetafile& metafile);
+
+  // Copy constructor.
+  explicit Image(const Image& image);
+
   const gfx::Size& size() const {
     return size_;
   }
 
+  // Return a checksum of the image (MD5 over the internal data structure).
+  std::string checksum() const;
+
   // Save image as PNG.
-  bool SaveToPng(const std::wstring& filename);
+  bool SaveToPng(const std::wstring& filename) const;
 
   // Returns % of pixels different
   double PercentageDifferent(const Image& rhs) const;
@@ -50,10 +61,16 @@
   }
 
  private:
+  // Construct from metafile.  This is kept internal since it's ambiguous what
+  // kind of data is used (png, bmp, metafile etc).
+  Image(const void* data, size_t size);
+
   bool LoadPng(const std::string& compressed);
 
   bool LoadMetafile(const std::string& data);
 
+  bool LoadMetafile(const NativeMetafile& metafile);
+
   // Pixel dimensions of the image.
   gfx::Size size_;
 
@@ -67,7 +84,8 @@
   // Flag to signal if the comparison functions should ignore the alpha channel.
   const bool ignore_alpha_;  // Currently always true.
 
-  DISALLOW_EVIL_CONSTRUCTORS(Image);
+  // Prevent operator= (this function has no implementation)
+  Image& operator=(const Image& image);
 };
 
 }  // namespace printing