summaryrefslogtreecommitdiffstats
path: root/printing/pdf_ps_metafile_linux.cc
diff options
context:
space:
mode:
authortony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-25 17:36:37 +0000
committertony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-25 17:36:37 +0000
commit3cfa15ac25e64425a9550fa8b0f741219fe68006 (patch)
treebcfa22ea62dd1ecd06c7b0ff11ddebb9bea28514 /printing/pdf_ps_metafile_linux.cc
parent9db9a8ed63bf7d194e687151d86f47eab7292206 (diff)
downloadchromium_src-3cfa15ac25e64425a9550fa8b0f741219fe68006.zip
chromium_src-3cfa15ac25e64425a9550fa8b0f741219fe68006.tar.gz
chromium_src-3cfa15ac25e64425a9550fa8b0f741219fe68006.tar.bz2
Implement native metafile for printing on Linux.
The metafile class stores the resulting PDF/PS stream in memory. BUG=9847 Original patch by Min-yu Huang <minyu.huang@gmail.com> at http://codereview.chromium.org/174042 Review URL: http://codereview.chromium.org/174405 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24243 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'printing/pdf_ps_metafile_linux.cc')
-rw-r--r--printing/pdf_ps_metafile_linux.cc288
1 files changed, 288 insertions, 0 deletions
diff --git a/printing/pdf_ps_metafile_linux.cc b/printing/pdf_ps_metafile_linux.cc
new file mode 100644
index 0000000..83c17a9
--- /dev/null
+++ b/printing/pdf_ps_metafile_linux.cc
@@ -0,0 +1,288 @@
+// 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 "printing/pdf_ps_metafile_linux.h"
+
+#include <stdio.h>
+
+#include <cairo-pdf.h>
+#include <cairo-ps.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+
+namespace printing {
+
+PdfPsMetafile::PdfPsMetafile(const FileFormat& format)
+ : format_(format),
+ surface_(NULL), context_(NULL),
+ page_surface_(NULL), page_context_(NULL) {
+
+ // Create an 1 by 1 Cairo surface for entire PDF/PS file.
+ // The size for each page will be overwritten later in StartPage().
+ switch (format_) {
+ case PDF: {
+ surface_ = cairo_pdf_surface_create_for_stream(WriteCairoStream,
+ &all_pages_, 1, 1);
+ }
+ break;
+
+ case PS: {
+ surface_ = cairo_ps_surface_create_for_stream(WriteCairoStream,
+ &all_pages_, 1, 1);
+ }
+ break;
+
+ default:
+ NOTREACHED();
+ return;
+ }
+
+ // Cairo always returns a valid pointer.
+ // Hence, we have to check if it points to a "nil" object.
+ if (!IsSurfaceValid(surface_)) {
+ DLOG(ERROR) << "Cannot create Cairo surface for PdfPsMetafile!";
+ cairo_surface_destroy(surface_);
+ return;
+ }
+
+ // Create a context.
+ context_ = cairo_create(surface_);
+ if (!IsContextValid(context_)) {
+ DLOG(ERROR) << "Cannot create Cairo context for PdfPsMetafile!";
+ cairo_destroy(context_);
+ cairo_surface_destroy(surface_);
+ return;
+ }
+
+ // Since |context_| will keep a reference of |surface_|, we can decreace
+ // surface's reference count by one safely.
+ cairo_surface_destroy(surface_);
+}
+
+PdfPsMetafile::PdfPsMetafile(const FileFormat& format,
+ const void* src_buffer,
+ size_t src_buffer_size)
+ : format_(format),
+ surface_(NULL), context_(NULL),
+ page_surface_(NULL), page_context_(NULL) {
+
+ all_pages_ = std::string(reinterpret_cast<const char*>(src_buffer),
+ src_buffer_size);
+}
+
+PdfPsMetafile::~PdfPsMetafile() {
+ // Releases resources if we forgot to do so.
+ Close();
+}
+
+bool PdfPsMetafile::StartPage(double width_in_points, double height_in_points) {
+ DCHECK(IsSurfaceValid(surface_));
+ DCHECK(IsContextValid(context_));
+ DCHECK(!page_surface_ && !page_context_);
+ DCHECK_GT(width_in_points, 0.);
+ DCHECK_GT(height_in_points, 0.);
+
+ // Create a target surface for the new page.
+ // Cairo 1.6.0 does NOT allow the first argument be NULL,
+ // but some newer versions do support NULL pointer.
+ switch (format_) {
+ case PDF: {
+ page_surface_ = cairo_pdf_surface_create_for_stream(WriteCairoStream,
+ &current_page_,
+ width_in_points,
+ height_in_points);
+ }
+ break;
+
+ case PS: {
+ page_surface_ = cairo_ps_surface_create_for_stream(WriteCairoStream,
+ &current_page_,
+ width_in_points,
+ height_in_points);
+ }
+ break;
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ // Cairo always returns a valid pointer.
+ // Hence, we have to check if it points to a "nil" object.
+ if (!IsSurfaceValid(page_surface_)) {
+ DLOG(ERROR) << "Cannot create Cairo surface for PdfPsMetafile!";
+ cairo_surface_destroy(page_surface_);
+ return false;
+ }
+
+ // Create a context.
+ page_context_ = cairo_create(page_surface_);
+ if (!IsContextValid(page_context_)) {
+ DLOG(ERROR) << "Cannot create Cairo context for PdfPsMetafile!";
+ cairo_destroy(page_context_);
+ cairo_surface_destroy(page_surface_);
+ return false;
+ }
+
+ // Since |page_context_| will keep a reference of |page_surface_|, we can
+ // decreace surface's reference count by one safely.
+ cairo_surface_destroy(page_surface_);
+
+ return true;
+}
+
+void PdfPsMetafile::FinishPage(float shrink) {
+ DCHECK(IsSurfaceValid(surface_));
+ DCHECK(IsContextValid(context_));
+ DCHECK(IsSurfaceValid(page_surface_));
+ DCHECK(IsContextValid(page_context_));
+
+ // Flush all rendering for current page.
+ cairo_surface_flush(page_surface_);
+
+ // TODO(myhuang): Use real page settings.
+ // We hard-coded page settings here for testing purpose.
+ // The paper size is US Letter (8.5 in. by 11 in.).
+ // The default margins are:
+ // Left = 0.25 in.
+ // Right = 0.25 in.
+ // Top = 0.25 in.
+ // Bottom = 0.56 in.
+ const double kDPI = 72.0; // Dots (points) per inch.
+ const double kWidthInInch = 8.5;
+ const double kHeightInInch = 11.0;
+ const double kWidthInPoint = kWidthInInch * kDPI;
+ const double kHeightInPoint = kHeightInInch * kDPI;
+ switch (format_) {
+ case PDF: {
+ cairo_pdf_surface_set_size(surface_, kWidthInPoint, kHeightInPoint);
+ }
+ break;
+
+ case PS: {
+ cairo_ps_surface_set_size(surface_, kWidthInPoint, kHeightInPoint);
+ }
+ break;
+
+ default:
+ NOTREACHED();
+ return;
+ }
+
+ // Save context's states.
+ cairo_save(context_);
+ // Copy current page onto the surface of final result.
+ // Margins are done by coordinates transformation.
+ // Please NOTE that we have to call cairo_scale() before we call
+ // cairo_set_source_surface().
+ const double scale_factor = 1. / shrink;
+ cairo_scale(context_, scale_factor, scale_factor);
+ const double kLeftMarginInInch = 0.25;
+ const double kTopMarginInInch = 0.25;
+ const double kLeftMarginInPoint = kLeftMarginInInch * kDPI;
+ const double kTopMarginInPoint = kTopMarginInInch * kDPI;
+ const double kScaledLeftMarginInPoint = kLeftMarginInPoint * shrink;
+ const double kScaledTopMarginInPoint = kTopMarginInPoint * shrink;
+ cairo_set_source_surface(context_,
+ page_surface_,
+ kScaledLeftMarginInPoint,
+ kScaledTopMarginInPoint);
+ // In Cairo 1.6.0, if we use the following API, either the renderer will
+ // crash, or we will get an empty page. This might be a bug in Cairo.
+ // cairo_set_operator(context_, CAIRO_OPERATOR_SOURCE);
+ const double kRightMarginInInch = 0.25;
+ const double kBottomMarginInInch = 0.56;
+ const double kPrintableWidthInInch =
+ kWidthInInch - kLeftMarginInInch - kRightMarginInInch;
+ const double kPrintableHeightInInch =
+ kHeightInInch - kTopMarginInInch - kBottomMarginInInch;
+ const double kScaledPrintableWidthInPoint =
+ kPrintableWidthInInch * kDPI * shrink;
+ const double kScaledPrintableHeightInPoint =
+ kPrintableHeightInInch * kDPI * shrink;
+ cairo_rectangle(context_,
+ kScaledLeftMarginInPoint,
+ kScaledTopMarginInPoint,
+ kScaledPrintableWidthInPoint,
+ kScaledPrintableHeightInPoint);
+ cairo_fill(context_);
+
+ // Finishing the duplication of current page.
+ cairo_show_page(context_);
+ cairo_surface_flush(surface_);
+
+ // Destroy resoreces for current page.
+ cairo_destroy(page_context_);
+ page_context_ = NULL;
+ page_surface_ = NULL;
+ current_page_.clear();
+
+ // Restore context's states.
+ cairo_restore(context_);
+}
+
+void PdfPsMetafile::Close() {
+ if (surface_ != NULL && IsSurfaceValid(surface_)) {
+ cairo_surface_finish(surface_);
+ surface_ = NULL;
+ }
+ if (context_ != NULL && IsContextValid(context_)) {
+ cairo_destroy(context_);
+ context_ = NULL;
+ }
+}
+
+unsigned int PdfPsMetafile::GetDataSize() const {
+ DCHECK(!surface_ && !context_);
+
+ return all_pages_.size();
+}
+
+void PdfPsMetafile::GetData(void* dst_buffer, size_t dst_buffer_size) const {
+ DCHECK(!surface_ && !context_);
+ DCHECK(dst_buffer);
+
+ size_t data_size = GetDataSize();
+ if (data_size < dst_buffer_size)
+ dst_buffer_size = data_size;
+ memcpy(dst_buffer, all_pages_.data(), dst_buffer_size);
+}
+
+bool PdfPsMetafile::SaveTo(const FilePath& filename) const {
+ DCHECK(!surface_ && !context_);
+
+ const unsigned int data_size = GetDataSize();
+ const unsigned int bytes_written =
+ file_util::WriteFile(filename, all_pages_.data(), data_size);
+ if (bytes_written != data_size) {
+ DLOG(ERROR) << "Failed to save file: " << filename.value();
+ return false;
+ }
+
+ return true;
+}
+
+cairo_status_t PdfPsMetafile::WriteCairoStream(void* dst_buffer,
+ const unsigned char* src_data,
+ unsigned int src_data_length) {
+ DCHECK(dst_buffer);
+ DCHECK(src_data);
+
+ std::string* buffer = reinterpret_cast<std::string* >(dst_buffer);
+ buffer->append(reinterpret_cast<const char*>(src_data), src_data_length);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+bool PdfPsMetafile::IsSurfaceValid(cairo_surface_t* surface) const {
+ return cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS;
+}
+
+bool PdfPsMetafile::IsContextValid(cairo_t* context) const {
+ return cairo_status(context) == CAIRO_STATUS_SUCCESS;
+}
+
+} // namespace printing
+