diff options
Diffstat (limited to 'printing')
-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 |
6 files changed, 150 insertions, 23 deletions
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. } |