diff options
-rw-r--r-- | chrome/browser/printing/printing_layout_uitest.cc | 4 | ||||
-rw-r--r-- | chrome/renderer/mock_printer.cc | 29 | ||||
-rw-r--r-- | chrome/renderer/mock_printer.h | 4 | ||||
-rw-r--r-- | chrome/renderer/render_view_unittest.cc | 24 | ||||
-rw-r--r-- | printing/image.cc | 29 | ||||
-rw-r--r-- | printing/image.h | 3 | ||||
-rw-r--r-- | printing/pdf_metafile_mac.cc | 81 | ||||
-rw-r--r-- | printing/pdf_metafile_mac.h | 30 | ||||
-rw-r--r-- | printing/pdf_metafile_mac_unittest.cc | 10 | ||||
-rw-r--r-- | printing/printed_document_mac.cc | 20 |
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. } |