summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-14 20:02:27 +0000
committerstuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-14 20:02:27 +0000
commit72f966bbd75a095ef73a121e16f5713172b51d09 (patch)
tree6f7404eb02a40fa2337d59a3d5a65b2fe4368d28
parentd5ceb91979a8f47ca1e7e22a608eeaa7db933a14 (diff)
downloadchromium_src-72f966bbd75a095ef73a121e16f5713172b51d09.zip
chromium_src-72f966bbd75a095ef73a121e16f5713172b51d09.tar.gz
chromium_src-72f966bbd75a095ef73a121e16f5713172b51d09.tar.bz2
Enable the RenderViewTest printing tests on the Mac.
Migrates some test APIs from wstring path names to FilePath objects, and fixes some gcc compilation issues, to allow the tests to build on Mac. Moves rendering logic and some other pdf logic into PdfMetafile to avoid duplication with unit test code. Switches rendering from the deprecated CGContextDrawPDFDocument to the newer (but less convenient) CGContextDrawPDFPage. Added debugging helpers to PdfMetafile: SaveTo, matching the other platform metafiles, and context retain count checking to get early warning of issues that will cause printing failure. BUG=24750 TEST=N/A Review URL: http://codereview.chromium.org/274052 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29003 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/printing/printing_layout_uitest.cc4
-rw-r--r--chrome/renderer/mock_printer.cc29
-rw-r--r--chrome/renderer/mock_printer.h4
-rw-r--r--chrome/renderer/render_view_unittest.cc24
-rw-r--r--printing/image.cc29
-rw-r--r--printing/image.h3
-rw-r--r--printing/pdf_metafile_mac.cc81
-rw-r--r--printing/pdf_metafile_mac.h30
-rw-r--r--printing/pdf_metafile_mac_unittest.cc10
-rw-r--r--printing/printed_document_mac.cc20
10 files changed, 185 insertions, 49 deletions
diff --git a/chrome/browser/printing/printing_layout_uitest.cc b/chrome/browser/printing/printing_layout_uitest.cc
index fb0b6c0..a5de41a 100644
--- a/chrome/browser/printing/printing_layout_uitest.cc
+++ b/chrome/browser/printing/printing_layout_uitest.cc
@@ -82,7 +82,7 @@ class PrintingLayoutTest : public PrintingTest<UITest> {
// Copy the .emf and generate an .png.
file_util::CopyFile(test_result, emf);
Image emf_content(emf.value());
- emf_content.SaveToPng(png.value());
+ emf_content.SaveToPng(png);
// Saving is always fine.
return 0;
} else {
@@ -109,7 +109,7 @@ class PrintingLayoutTest : public PrintingTest<UITest> {
L" result size:" << test_content.size();
if (diff_png) {
// Backup the rendered emf file to detect the rendering difference.
- emf_content.SaveToPng(verification_file + L"_rendering.png");
+ emf_content.SaveToPng(FilePath(verification_file + L"_rendering.png"));
}
return std::max(diff_png, diff_emf);
}
diff --git a/chrome/renderer/mock_printer.cc b/chrome/renderer/mock_printer.cc
index 2da66ae..e8fbac0 100644
--- a/chrome/renderer/mock_printer.cc
+++ b/chrome/renderer/mock_printer.cc
@@ -105,19 +105,22 @@ void MockPrinter::PrintPage(const ViewHostMsg_DidPrintPage_Params& params) {
EXPECT_EQ(page_number_, params.page_number);
EXPECT_LE(params.page_number, number_pages_);
-#if defined(OS_WIN)
- // Load the EMF data sent from a RenderView object and create a PageData
- // object.
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ // Load the data sent from a RenderView object and create a PageData object.
// We duplicate the given file handle when creating a base::SharedMemory
// instance so that its destructor closes the copy.
EXPECT_GT(params.data_size, 0U);
- base::SharedMemory emf_data(params.metafile_data_handle, true,
- GetCurrentProcess());
- emf_data.Map(params.data_size);
+#if defined(OS_WIN)
+ base::SharedMemory metafile_data(params.metafile_data_handle, true,
+ GetCurrentProcess());
+#elif defined(OS_MACOSX)
+ base::SharedMemory metafile_data(params.metafile_data_handle, true);
+#endif
+ metafile_data.Map(params.data_size);
printing::NativeMetafile metafile;
- metafile.CreateFromData(emf_data.memory(), params.data_size);
+ metafile.CreateFromData(metafile_data.memory(), params.data_size);
printing::Image image(metafile);
- MockPrinterPage* page_data = new MockPrinterPage(emf_data.memory(),
+ MockPrinterPage* page_data = new MockPrinterPage(metafile_data.memory(),
params.data_size,
image);
if (!page_data) {
@@ -168,23 +171,21 @@ bool MockPrinter::GetBitmapChecksum(size_t page, std::string* checksum) const {
return true;
}
-bool MockPrinter::SaveSource(size_t page,
- const std::wstring& filename) const {
+bool MockPrinter::SaveSource(size_t page, const FilePath& filepath) const {
if (printer_status_ != PRINTER_READY || page >= pages_.size())
return false;
const uint8* source_data = pages_[page]->source_data();
size_t source_size = pages_[page]->source_size();
- file_util::WriteFile(filename, reinterpret_cast<const char*>(source_data),
+ file_util::WriteFile(filepath, reinterpret_cast<const char*>(source_data),
source_size);
return true;
}
-bool MockPrinter::SaveBitmap(size_t page,
- const std::wstring& filename) const {
+bool MockPrinter::SaveBitmap(size_t page, const FilePath& filepath) const {
if (printer_status_ != PRINTER_READY || page >= pages_.size())
return false;
- pages_[page]->image().SaveToPng(filename);
+ pages_[page]->image().SaveToPng(filepath);
return true;
}
diff --git a/chrome/renderer/mock_printer.h b/chrome/renderer/mock_printer.h
index ba3f0de..80f6c4b 100644
--- a/chrome/renderer/mock_printer.h
+++ b/chrome/renderer/mock_printer.h
@@ -96,8 +96,8 @@ class MockPrinter {
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;
- bool SaveSource(size_t page, const std::wstring& filename) const;
- bool SaveBitmap(size_t page, const std::wstring& filename) const;
+ bool SaveSource(size_t page, const FilePath& filepath) const;
+ bool SaveBitmap(size_t page, const FilePath& filepath) const;
protected:
int CreateDocumentCookie();
diff --git a/chrome/renderer/render_view_unittest.cc b/chrome/renderer/render_view_unittest.cc
index 980ba38..2626096 100644
--- a/chrome/renderer/render_view_unittest.cc
+++ b/chrome/renderer/render_view_unittest.cc
@@ -372,7 +372,7 @@ TEST_F(RenderViewTest, PrintWithJavascript) {
}
TEST_F(RenderViewTest, PrintWithIframe) {
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_MACOSX)
// Document that populates an iframe..
const char html[] =
"<html><body>Lorem Ipsum:"
@@ -416,14 +416,16 @@ TEST_F(RenderViewTest, PrintWithIframe) {
// i.e. a simplified version of the PrintingLayoutTextTest UI test.
namespace {
// Test cases used in this test.
-const struct {
+struct TestPageData {
const char* page;
- int printed_pages;
+ size_t printed_pages;
int width;
int height;
const char* checksum;
const wchar_t* file;
-} kTestPages[] = {
+};
+
+const TestPageData kTestPages[] = {
{"<html>"
"<head>"
"<meta"
@@ -434,7 +436,13 @@ const struct {
"<body style=\"background-color: white;\">"
"<p style=\"font-family: arial;\">Hello World!</p>"
"</body>",
+#if defined(OS_MACOSX)
+ // Mac printing code compensates for the WebKit scale factor while generating
+ // the metafile, so we expect smaller pages.
+ 1, 612, 792,
+#else
1, 764, 972,
+#endif
NULL,
NULL,
},
@@ -442,7 +450,7 @@ const struct {
} // namespace
TEST_F(RenderViewTest, PrintLayoutTest) {
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_MACOSX)
bool baseline = false;
EXPECT_TRUE(render_thread_.printer() != NULL);
@@ -484,11 +492,11 @@ TEST_F(RenderViewTest, PrintLayoutTest) {
// create base-line results.
FilePath source_path;
file_util::CreateTemporaryFile(&source_path);
- render_thread_.printer()->SaveSource(0, source_path.value());
+ render_thread_.printer()->SaveSource(0, source_path);
FilePath bitmap_path;
file_util::CreateTemporaryFile(&bitmap_path);
- render_thread_.printer()->SaveBitmap(0, bitmap_path.value());
+ render_thread_.printer()->SaveBitmap(0, bitmap_path);
}
}
#else
@@ -498,7 +506,7 @@ TEST_F(RenderViewTest, PrintLayoutTest) {
// Print page as bitmap test.
TEST_F(RenderViewTest, OnPrintPageAsBitmap) {
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_MACOSX)
// Lets simulate a print pages with Hello world.
LoadHTML("<body><p>Hello world!</p></body>");
diff --git a/printing/image.cc b/printing/image.cc
index beff637..ba52567 100644
--- a/printing/image.cc
+++ b/printing/image.cc
@@ -13,6 +13,9 @@
#if defined(OS_WIN)
#include "app/gfx/gdi_util.h" // EMF support
+#elif defined(OS_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#include "base/scoped_cftyperef.h"
#endif
namespace {
@@ -97,7 +100,7 @@ std::string Image::checksum() const {
return HexEncode(&digest, sizeof(digest));
}
-bool Image::SaveToPng(const std::wstring& filename) const {
+bool Image::SaveToPng(const FilePath& filepath) const {
DCHECK(!data_.empty());
std::vector<unsigned char> compressed;
bool success = gfx::PNGCodec::Encode(&*data_.begin(),
@@ -110,7 +113,7 @@ bool Image::SaveToPng(const std::wstring& filename) const {
DCHECK(success && compressed.size());
if (success) {
int write_bytes = file_util::WriteFile(
- filename,
+ filepath,
reinterpret_cast<char*>(&*compressed.begin()), compressed.size());
success = (write_bytes == static_cast<int>(compressed.size()));
DCHECK(success);
@@ -188,7 +191,7 @@ bool Image::LoadPng(const std::string& compressed) {
bool Image::LoadMetafile(const std::string& data) {
DCHECK(!data.empty());
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_MACOSX)
NativeMetafile metafile;
metafile.CreateFromData(data.data(), data.size());
return LoadMetafile(metafile);
@@ -229,6 +232,26 @@ bool Image::LoadMetafile(const NativeMetafile& metafile) {
DeleteObject(bitmap);
return success;
}
+#elif defined(OS_MACOSX)
+ // The printing system uses single-page metafiles (page indexes are 1-based).
+ const unsigned int page_number = 1;
+ gfx::Rect rect(metafile.GetPageBounds(page_number));
+ if (rect.width() > 0 && rect.height() > 0) {
+ size_ = rect.size();
+ row_length_ = size_.width() * sizeof(uint32);
+ size_t bytes = row_length_ * size_.height();
+ DCHECK(bytes);
+ data_.resize(bytes);
+ scoped_cftyperef<CGColorSpaceRef> color_space(
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
+ scoped_cftyperef<CGContextRef> bitmap_context(
+ CGBitmapContextCreate(&*data_.begin(), size_.width(), size_.height(),
+ 8, row_length_, color_space,
+ kCGImageAlphaPremultipliedLast));
+ DCHECK(bitmap_context.get());
+ metafile.RenderPage(page_number, bitmap_context,
+ CGRectMake(0, 0, size_.width(), size_.height()));
+ }
#else
NOTIMPLEMENTED();
#endif
diff --git a/printing/image.h b/printing/image.h
index a927549..f8755275 100644
--- a/printing/image.h
+++ b/printing/image.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/file_path.h"
#include "base/gfx/size.h"
#include "base/logging.h"
#include "printing/native_metafile.h"
@@ -39,7 +40,7 @@ class Image {
std::string checksum() const;
// Save image as PNG.
- bool SaveToPng(const std::wstring& filename) const;
+ bool SaveToPng(const FilePath& filepath) const;
// Returns % of pixels different
double PercentageDifferent(const Image& rhs) const;
diff --git a/printing/pdf_metafile_mac.cc b/printing/pdf_metafile_mac.cc
index 6a8cd55..381c6a3 100644
--- a/printing/pdf_metafile_mac.cc
+++ b/printing/pdf_metafile_mac.cc
@@ -4,7 +4,11 @@
#include "printing/pdf_metafile_mac.h"
+#include "base/file_path.h"
+#include "base/gfx/rect.h"
#include "base/logging.h"
+#include "base/scoped_cftyperef.h"
+#include "base/sys_string_conversions.h"
namespace printing {
@@ -85,9 +89,60 @@ void PdfMetafile::Close() {
DCHECK(context_.get());
DCHECK(!page_is_open_);
+#ifndef NDEBUG
+ // Check that the context will be torn down properly; if it's not, pdf_data_
+ // will be incomplete and generate invalid PDF files/documents.
+ if (context_.get()) {
+ CFIndex extra_retain_count = CFGetRetainCount(context_.get()) - 1;
+ if (extra_retain_count > 0) {
+ LOG(ERROR) << "Metafile context has " << extra_retain_count
+ << " extra retain(s) on Close";
+ }
+ }
+#endif
context_.reset(NULL);
}
+bool PdfMetafile::RenderPage(unsigned int page_number, CGContextRef context,
+ const CGRect rect) const {
+ CGPDFDocumentRef pdf_doc = GetPDFDocument();
+ if (!pdf_doc) {
+ LOG(ERROR) << "Unable to create PDF document from data";
+ return false;
+ }
+ CGPDFPageRef pdf_page = CGPDFDocumentGetPage(pdf_doc, page_number);
+ CGRect source_rect = CGPDFPageGetBoxRect(pdf_page, kCGPDFMediaBox);
+
+ CGContextSaveGState(context);
+ CGContextTranslateCTM(context, rect.origin.x, rect.origin.y);
+ CGContextScaleCTM(context, rect.size.width / source_rect.size.width,
+ rect.size.height / source_rect.size.height);
+ CGContextDrawPDFPage(context, pdf_page);
+ CGContextRestoreGState(context);
+
+ return true;
+}
+
+size_t PdfMetafile::GetPageCount() const {
+ CGPDFDocumentRef pdf_doc = GetPDFDocument();
+ return pdf_doc ? CGPDFDocumentGetNumberOfPages(pdf_doc) : 0;
+}
+
+gfx::Rect PdfMetafile::GetPageBounds(unsigned int page_number) const {
+ CGPDFDocumentRef pdf_doc = GetPDFDocument();
+ if (!pdf_doc) {
+ LOG(ERROR) << "Unable to create PDF document from data";
+ return gfx::Rect();
+ }
+ if (page_number > GetPageCount()) {
+ LOG(ERROR) << "Invalid page number: " << page_number;
+ return gfx::Rect();
+ }
+ CGPDFPageRef pdf_page = CGPDFDocumentGetPage(pdf_doc, page_number);
+ CGRect page_rect = CGPDFPageGetBoxRect(pdf_page, kCGPDFMediaBox);
+ return gfx::Rect(page_rect);
+}
+
unsigned int PdfMetafile::GetDataSize() const {
// PDF data is only valid/complete once the context is released.
DCHECK(!context_);
@@ -114,4 +169,30 @@ bool PdfMetafile::GetData(void* dst_buffer, size_t dst_buffer_size) const {
return true;
}
+bool PdfMetafile::SaveTo(const FilePath& file_path) const {
+ DCHECK(pdf_data_.get());
+ DCHECK(!context_.get());
+
+ std::string path_string = file_path.value();
+ scoped_cftyperef<CFURLRef> path_url(CFURLCreateFromFileSystemRepresentation(
+ kCFAllocatorDefault, reinterpret_cast<const UInt8*>(path_string.c_str()),
+ path_string.length(), false));
+ SInt32 error_code;
+ CFURLWriteDataAndPropertiesToResource(path_url, pdf_data_, NULL, &error_code);
+ return error_code == 0;
+}
+
+CGPDFDocumentRef PdfMetafile::GetPDFDocument() const {
+ // Make sure that we have data, and that it's not being modified any more.
+ DCHECK(pdf_data_.get());
+ DCHECK(!context_.get());
+
+ if (!pdf_doc_.get()) {
+ scoped_cftyperef<CGDataProviderRef> pdf_data_provider(
+ CGDataProviderCreateWithCFData(pdf_data_));
+ pdf_doc_.reset(CGPDFDocumentCreateWithProvider(pdf_data_provider));
+ }
+ return pdf_doc_.get();
+}
+
} // namespace printing
diff --git a/printing/pdf_metafile_mac.h b/printing/pdf_metafile_mac.h
index 607abf5..61ba98e 100644
--- a/printing/pdf_metafile_mac.h
+++ b/printing/pdf_metafile_mac.h
@@ -5,12 +5,17 @@
#ifndef PRINTING_PDF_METAFILE_MAC_H_
#define PRINTING_PDF_METAFILE_MAC_H_
-#import <ApplicationServices/ApplicationServices.h>
-#import <CoreFoundation/CoreFoundation.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreFoundation/CoreFoundation.h>
#include "base/basictypes.h"
#include "base/scoped_cftyperef.h"
+namespace gfx {
+class Rect;
+}
+class FilePath;
+
namespace printing {
// This class creates a graphics context that renders into a PDF data stream.
@@ -45,6 +50,17 @@ class PdfMetafile {
// Closes the PDF file; no further rendering is allowed.
void Close();
+ // Renders the given page into |rect| in the given context.
+ // Pages use a 1-based index.
+ bool RenderPage(unsigned int page_number, CGContextRef context,
+ const CGRect rect) const;
+
+ size_t GetPageCount() const;
+
+ // Returns the bounds of the given page.
+ // Pages use a 1-based index.
+ gfx::Rect GetPageBounds(unsigned int page_number) const;
+
// Returns the size of the underlying PDF data. Only valid after Close() has
// been called.
unsigned int GetDataSize() const;
@@ -54,13 +70,23 @@ class PdfMetafile {
// Returns true if the copy succeeds.
bool GetData(void* dst_buffer, size_t dst_buffer_size) const;
+ // Saves the raw PDF data to the given file. For testing only.
+ // Returns true if writing succeeded.
+ bool SaveTo(const FilePath& file_path) const;
+
private:
+ // Returns a CGPDFDocumentRef version of pdf_data_.
+ CGPDFDocumentRef GetPDFDocument() const;
+
// Context for rendering to the pdf.
scoped_cftyperef<CGContextRef> context_;
// PDF backing store.
scoped_cftyperef<CFMutableDataRef> pdf_data_;
+ // Lazily-created CGPDFDocument representation of pdf_data_.
+ mutable scoped_cftyperef<CGPDFDocumentRef> pdf_doc_;
+
// Whether or not a page is currently open.
bool page_is_open_;
diff --git a/printing/pdf_metafile_mac_unittest.cc b/printing/pdf_metafile_mac_unittest.cc
index 1a1bf44..3a38e68 100644
--- a/printing/pdf_metafile_mac_unittest.cc
+++ b/printing/pdf_metafile_mac_unittest.cc
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
+#include "base/gfx/rect.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(PdfMetafileTest, Pdf) {
@@ -46,4 +47,13 @@ TEST(PdfMetafileTest, Pdf) {
// Test that the header begins with "%PDF".
std::string header(&buffer2.front(), 4);
EXPECT_EQ(0U, header.find("%PDF", 0));
+
+ // Test that the PDF is correctly reconstructed.
+ EXPECT_EQ(2U, pdf2.GetPageCount());
+ gfx::Size page_size = pdf2.GetPageBounds(1).size();
+ EXPECT_EQ(540, page_size.width());
+ EXPECT_EQ(720, page_size.height());
+ page_size = pdf2.GetPageBounds(2).size();
+ EXPECT_EQ(720, page_size.width());
+ EXPECT_EQ(540, page_size.height());
}
diff --git a/printing/printed_document_mac.cc b/printing/printed_document_mac.cc
index ace57f3..e9bd044 100644
--- a/printing/printed_document_mac.cc
+++ b/printing/printed_document_mac.cc
@@ -24,28 +24,14 @@ void PrintedDocument::RenderPrintedPage(
}
#endif
- const printing::NativeMetafile* metafile = page.native_metafile();
- unsigned int data_length = metafile->GetDataSize();
- scoped_cftyperef<CFMutableDataRef> pdf_data(
- CFDataCreateMutable(kCFAllocatorDefault, data_length));
- CFDataIncreaseLength(pdf_data, data_length);
- metafile->GetData(CFDataGetMutableBytePtr(pdf_data), data_length);
- scoped_cftyperef<CGDataProviderRef> pdf_data_provider(
- CGDataProviderCreateWithCFData(pdf_data));
- scoped_cftyperef<CGPDFDocumentRef> pdf_doc(
- CGPDFDocumentCreateWithProvider(pdf_data_provider));
- if (!pdf_doc.get()) {
- NOTREACHED() << "Unable to create PDF document from print data";
- return;
- }
-
const printing::PageSetup& page_setup(
immutable_.settings_.page_setup_pixels());
CGRect target_rect = page_setup.content_area().ToCGRect();
- // Each NativeMetafile is a one-page PDF.
+ const printing::NativeMetafile* metafile = page.native_metafile();
+ // Each NativeMetafile is a one-page PDF, and pages use 1-based indexing.
const int page_number = 1;
- CGContextDrawPDFDocument(context, target_rect, pdf_doc, page_number);
+ metafile->RenderPage(page_number, context, target_rect);
// TODO(stuartmorgan): Print the header and footer.
}