diff options
author | tony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-25 17:36:37 +0000 |
---|---|---|
committer | tony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-25 17:36:37 +0000 |
commit | 3cfa15ac25e64425a9550fa8b0f741219fe68006 (patch) | |
tree | bcfa22ea62dd1ecd06c7b0ff11ddebb9bea28514 /printing/pdf_ps_metafile_linux.cc | |
parent | 9db9a8ed63bf7d194e687151d86f47eab7292206 (diff) | |
download | chromium_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.cc | 288 |
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, + ¤t_page_, + width_in_points, + height_in_points); + } + break; + + case PS: { + page_surface_ = cairo_ps_surface_create_for_stream(WriteCairoStream, + ¤t_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 + |