summaryrefslogtreecommitdiffstats
path: root/printing
diff options
context:
space:
mode:
Diffstat (limited to 'printing')
-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
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.
}