diff options
author | scottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-20 22:02:25 +0000 |
---|---|---|
committer | scottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-20 22:02:25 +0000 |
commit | 75280ad2f6013db0a504650801fa8134a707a49c (patch) | |
tree | 09812e68bfc5304b95372d6c59c0599a3e5d98cf | |
parent | ed3eb462ba5584dabfa47461e676cb89da921838 (diff) | |
download | chromium_src-75280ad2f6013db0a504650801fa8134a707a49c.zip chromium_src-75280ad2f6013db0a504650801fa8134a707a49c.tar.gz chromium_src-75280ad2f6013db0a504650801fa8134a707a49c.tar.bz2 |
Printing on Windows via PDF
Currently:
- Based on #if, switches renderer rendering to PDF for preview
- Add PdfToEmfConvert based on PWGRasterConverter
- Various plumbing.
Preview works the same as other PDF based platforms. For getting
to EMF, the when PRINTING_WIN_USES_PDF_AS_METAFILE is on, the
renderer generates a PDF instead of EMF directly (this contains
all the pages in the desired range). This is returned to
PrintViewManagerBase, where it's passed to PdfToEmfConverter which
uses the sandboxed utility process and pdf.dll to convert via
RenderPDFPageToDC. The utility process renderers one emf page at
at time (into numbered files) to avoid having a very large emf file.
As this uses pdf.dll this is currently off-by-default controlled by
the gyp variable win_pdf_metafile_for_printing.
R=vitalybuka@chromium.org
BUG=170859
Review URL: https://codereview.chromium.org/255543006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271772 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | build/common.gypi | 7 | ||||
-rw-r--r-- | chrome/browser/printing/pdf_to_emf_converter.cc | 306 | ||||
-rw-r--r-- | chrome/browser/printing/pdf_to_emf_converter.h | 38 | ||||
-rw-r--r-- | chrome/browser/printing/print_view_manager_base.cc | 85 | ||||
-rw-r--r-- | chrome/browser/printing/print_view_manager_base.h | 15 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_renderer.gypi | 10 | ||||
-rw-r--r-- | chrome/common/chrome_utility_messages.h | 15 | ||||
-rw-r--r-- | chrome/renderer/printing/print_web_view_helper.cc | 5 | ||||
-rw-r--r-- | chrome/renderer/printing/print_web_view_helper.h | 11 | ||||
-rw-r--r-- | chrome/renderer/printing/print_web_view_helper_pdf_win.cc | 294 | ||||
-rw-r--r-- | chrome/service/service_utility_process_host.cc | 52 | ||||
-rw-r--r-- | chrome/service/service_utility_process_host.h | 7 | ||||
-rw-r--r-- | chrome/utility/chrome_content_utility_client.cc | 62 | ||||
-rw-r--r-- | chrome/utility/chrome_content_utility_client.h | 7 | ||||
-rw-r--r-- | content/renderer/pepper/pepper_plugin_instance_impl.cc | 3 | ||||
-rw-r--r-- | printing/metafile_impl.h | 4 |
17 files changed, 838 insertions, 85 deletions
diff --git a/build/common.gypi b/build/common.gypi index a515bc8..504596c 100644 --- a/build/common.gypi +++ b/build/common.gypi @@ -419,6 +419,9 @@ # print, UI, etc. 'enable_printing%': 1, + # Windows prints using a PDF as the metafile from the renderer. + 'win_pdf_metafile_for_printing%': 0, + # Set the version of CLD. # 0: Don't specify the version. This option is for the Finch testing. # 1: Use only CLD1. @@ -1046,6 +1049,7 @@ 'test_isolation_outdir%': '<(test_isolation_outdir)', 'test_isolation_fail_on_missing': '<(test_isolation_fail_on_missing)', 'enable_printing%': '<(enable_printing)', + 'win_pdf_metafile_for_printing%': '<(win_pdf_metafile_for_printing)', 'enable_spellcheck%': '<(enable_spellcheck)', 'enable_google_now%': '<(enable_google_now)', 'cld_version%': '<(cld_version)', @@ -2618,6 +2622,9 @@ ['enable_printing==2', { 'defines': ['ENABLE_PRINTING=1'], }], + ['win_pdf_metafile_for_printing==1', { + 'defines': ['WIN_PDF_METAFILE_FOR_PRINTING=1'], + }], ['enable_spellcheck==1', { 'defines': ['ENABLE_SPELLCHECK=1'], }], diff --git a/chrome/browser/printing/pdf_to_emf_converter.cc b/chrome/browser/printing/pdf_to_emf_converter.cc new file mode 100644 index 0000000..bce69a6 --- /dev/null +++ b/chrome/browser/printing/pdf_to_emf_converter.cc @@ -0,0 +1,306 @@ +// Copyright 2014 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 "chrome/browser/printing/pdf_to_emf_converter.h" + +#include "base/bind_helpers.h" +#include "base/cancelable_callback.h" +#include "base/file_util.h" +#include "base/files/file.h" +#include "base/files/scoped_temp_dir.h" +#include "base/logging.h" +#include "chrome/common/chrome_utility_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/child_process_data.h" +#include "content/public/browser/utility_process_host.h" +#include "content/public/browser/utility_process_host_client.h" + +namespace printing { + +namespace { + +using content::BrowserThread; + +class FileHandlers { + public: + FileHandlers() {} + + ~FileHandlers() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + } + + void Init(base::RefCountedMemory* data); + bool IsValid(); + + base::FilePath GetEmfPath() const { + return temp_dir_.path().AppendASCII("output.emf"); + } + + base::FilePath GetPdfPath() const { + return temp_dir_.path().AppendASCII("input.pdf"); + } + + IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) { + DCHECK(pdf_file_.IsValid()); + IPC::PlatformFileForTransit transit = + IPC::TakeFileHandleForProcess(pdf_file_.Pass(), process); + return transit; + } + + const base::FilePath& GetBasePath() const { + return temp_dir_.path(); + } + + private: + base::ScopedTempDir temp_dir_; + base::File pdf_file_; +}; + +void FileHandlers::Init(base::RefCountedMemory* data) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + if (!temp_dir_.CreateUniqueTempDir()) { + return; + } + + if (static_cast<int>(data->size()) != + base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) { + return; + } + + // Reopen in read only mode. + pdf_file_.Initialize(GetPdfPath(), + base::File::FLAG_OPEN | base::File::FLAG_READ); +} + +bool FileHandlers::IsValid() { + return pdf_file_.IsValid(); +} + +// Converts PDF into EMF. +// Class uses 3 threads: UI, IO and FILE. +// Internal workflow is following: +// 1. Create instance on the UI thread. (files_, settings_,) +// 2. Create file on the FILE thread. +// 3. Start utility process and start conversion on the IO thread. +// 4. Run result callback on the UI thread. +// 5. Instance is destroyed from any thread that has the last reference. +// 6. FileHandlers destroyed on the FILE thread. +// This step posts |FileHandlers| to be destroyed on the FILE thread. +// All these steps work sequentially, so no data should be accessed +// simultaneously by several threads. +class PdfToEmfUtilityProcessHostClient + : public content::UtilityProcessHostClient { + public: + explicit PdfToEmfUtilityProcessHostClient( + const printing::PdfRenderSettings& settings); + + void Convert(base::RefCountedMemory* data, + const PdfToEmfConverter::ResultCallback& callback); + + // UtilityProcessHostClient implementation. + virtual void OnProcessCrashed(int exit_code) OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + private: + virtual ~PdfToEmfUtilityProcessHostClient(); + + // Message handlers. + void OnProcessStarted(); + void OnSucceeded(const std::vector<printing::PageRange>& page_ranges, + double scale_factor); + void OnFailed(); + + void RunCallback(const std::vector<printing::PageRange>& page_ranges, + double scale_factor); + + void StartProcessOnIOThread(); + + void RunCallbackOnUIThread( + const std::vector<printing::PageRange>& page_ranges, + double scale_factor); + void OnFilesReadyOnUIThread(); + + scoped_ptr<FileHandlers> files_; + printing::PdfRenderSettings settings_; + PdfToEmfConverter::ResultCallback callback_; + base::WeakPtr<content::UtilityProcessHost> utility_process_host_; + + DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); +}; + +PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( + const printing::PdfRenderSettings& settings) + : settings_(settings) {} + +PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { + // Delete temp directory. + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release()); +} + +void PdfToEmfUtilityProcessHostClient::Convert( + base::RefCountedMemory* data, + const PdfToEmfConverter::ResultCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + callback_ = callback; + CHECK(!files_); + files_.reset(new FileHandlers()); + BrowserThread::PostTaskAndReply( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&FileHandlers::Init, + base::Unretained(files_.get()), + make_scoped_refptr(data)), + base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread, + this)); +} + +void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) { + OnFailed(); +} + +bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message) + IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) + IPC_MESSAGE_HANDLER( + ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, OnSucceeded) + IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed, + OnFailed) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void PdfToEmfUtilityProcessHostClient::OnProcessStarted() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!utility_process_host_) { + RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0); + return; + } + + base::ProcessHandle process = utility_process_host_->GetData().handle; + utility_process_host_->Send( + new ChromeUtilityMsg_RenderPDFPagesToMetafiles( + files_->GetPdfForProcess(process), + files_->GetEmfPath(), + settings_, + std::vector<printing::PageRange>())); + utility_process_host_.reset(); +} + +void PdfToEmfUtilityProcessHostClient::OnSucceeded( + const std::vector<printing::PageRange>& page_ranges, + double scale_factor) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + RunCallback(page_ranges, scale_factor); +} + +void PdfToEmfUtilityProcessHostClient::OnFailed() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + RunCallback(std::vector<printing::PageRange>(), 0.0); +} + +void PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!files_->IsValid()) { + RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0); + return; + } + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread, + this)); +} + +void PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + utility_process_host_ = + content::UtilityProcessHost::Create( + this, + base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr(); + // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load + // gdiplus.dll, change how rendering happens, and not be able to correctly + // generate when sent to a metafile DC. + utility_process_host_->SetExposedDir(files_->GetBasePath()); + utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); +} + +void PdfToEmfUtilityProcessHostClient::RunCallback( + const std::vector<printing::PageRange>& page_ranges, + double scale_factor) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread, + this, + page_ranges, + scale_factor)); +} + +void PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread( + const std::vector<printing::PageRange>& page_ranges, + double scale_factor) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + std::vector<base::FilePath> page_filenames; + std::vector<printing::PageRange>::const_iterator iter; + for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) { + for (int page_number = iter->from; page_number <= iter->to; ++page_number) { + page_filenames.push_back(files_->GetEmfPath().InsertBeforeExtensionASCII( + base::StringPrintf(".%d", page_number))); + } + } + if (!callback_.is_null()) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(callback_, scale_factor, page_filenames)); + callback_.Reset(); + } +} + +class PdfToEmfConverterImpl : public PdfToEmfConverter { + public: + PdfToEmfConverterImpl(); + + virtual ~PdfToEmfConverterImpl(); + + virtual void Start(base::RefCountedMemory* data, + const printing::PdfRenderSettings& conversion_settings, + const ResultCallback& callback) OVERRIDE; + + private: + scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_; + base::CancelableCallback<ResultCallback::RunType> callback_; + + DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl); +}; + +PdfToEmfConverterImpl::PdfToEmfConverterImpl() { +} + +PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { +} + +void PdfToEmfConverterImpl::Start( + base::RefCountedMemory* data, + const printing::PdfRenderSettings& conversion_settings, + const ResultCallback& callback) { + // Rebind cancelable callback to avoid calling callback if + // PdfToEmfConverterImpl is destroyed. + callback_.Reset(callback); + utility_client_ = new PdfToEmfUtilityProcessHostClient(conversion_settings); + utility_client_->Convert(data, callback_.callback()); +} + +} // namespace + +// static +scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() { + return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl()); +} + +} // namespace printing diff --git a/chrome/browser/printing/pdf_to_emf_converter.h b/chrome/browser/printing/pdf_to_emf_converter.h new file mode 100644 index 0000000..86afb05 --- /dev/null +++ b/chrome/browser/printing/pdf_to_emf_converter.h @@ -0,0 +1,38 @@ +// Copyright 2014 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. + +#ifndef CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_ +#define CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_ + +#include "base/callback.h" +#include "base/memory/ref_counted_memory.h" + +namespace base { +class FilePath; +} + +namespace printing { +class PdfRenderSettings; +} + +namespace printing { + +class PdfToEmfConverter { + public: + // Callback for when the PDF is converted to an EMF. + typedef base::Callback<void(double /*scale_factor*/, + const std::vector<base::FilePath>& /*emf_files*/)> + ResultCallback; + virtual ~PdfToEmfConverter() {} + + static scoped_ptr<PdfToEmfConverter> CreateDefault(); + + virtual void Start(base::RefCountedMemory* data, + const printing::PdfRenderSettings& conversion_settings, + const ResultCallback& callback) = 0; +}; + +} // namespace printing + +#endif // CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_ diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc index 53598aa..f101808 100644 --- a/chrome/browser/printing/print_view_manager_base.cc +++ b/chrome/browser/printing/print_view_manager_base.cc @@ -40,10 +40,17 @@ #include "chrome/browser/printing/print_error_dialog.h" #endif +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) +#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_memory.h" +#include "chrome/browser/printing/pdf_to_emf_converter.h" +#include "printing/pdf_render_settings.h" +#endif + using base::TimeDelta; using content::BrowserThread; -#if defined(OS_WIN) +#if defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING) // Limits memory usage by raster to 64 MiB. const int kMaxRasterSizeInPixels = 16*1024*1024; #endif @@ -58,7 +65,8 @@ PrintViewManagerBase::PrintViewManagerBase(content::WebContents* web_contents) cookie_(0), queue_(g_browser_process->print_job_manager()->queue()) { DCHECK(queue_); -#if defined(OS_POSIX) && !defined(OS_MACOSX) +#if (defined(OS_POSIX) && !defined(OS_MACOSX)) || \ + defined(WIN_PDF_METAFILE_FOR_PRINTING) expecting_first_page_ = true; #endif Profile* profile = @@ -124,8 +132,36 @@ void PrintViewManagerBase::OnDidGetDocumentCookie(int cookie) { cookie_ = cookie; } +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) +void PrintViewManagerBase::OnPdfToEmfConverted( + const PrintHostMsg_DidPrintPage_Params& params, + double scale_factor, + const std::vector<base::FilePath>& emf_files) { + PrintedDocument* document = print_job_->document(); + if (!document) + return; + + for (size_t i = 0; i < emf_files.size(); ++i) { + scoped_ptr<printing::Emf> metafile(new printing::Emf); + if (!metafile->InitFromFile(emf_files[i])) { + NOTREACHED() << "Invalid metafile"; + web_contents()->Stop(); + return; + } + // Update the rendered document. It will send notifications to the listener. + document->SetPage(i, + metafile.release(), + scale_factor, + params.page_size, + params.content_area); + } + + ShouldQuitFromInnerMessageLoop(); +} +#endif // WIN_PDF_METAFILE_FOR_PRINTING + void PrintViewManagerBase::OnDidPrintPage( - const PrintHostMsg_DidPrintPage_Params& params) { + const PrintHostMsg_DidPrintPage_Params& params) { if (!OpportunisticallyCreatePrintJob(params.document_cookie)) return; @@ -136,9 +172,10 @@ void PrintViewManagerBase::OnDidPrintPage( return; } -#if defined(OS_WIN) || defined(OS_MACOSX) +#if (defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING)) || \ + defined(OS_MACOSX) const bool metafile_must_be_valid = true; -#elif defined(OS_POSIX) +#elif defined(OS_POSIX) || defined(WIN_PDF_METAFILE_FOR_PRINTING) const bool metafile_must_be_valid = expecting_first_page_; expecting_first_page_ = false; #endif @@ -161,10 +198,10 @@ void PrintViewManagerBase::OnDidPrintPage( } } -#if defined(OS_WIN) +#if defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING) bool big_emf = (params.data_size && params.data_size >= kMetafileMaxSize); - int raster_size = std::min(params.page_size.GetArea(), - kMaxRasterSizeInPixels); + int raster_size = + std::min(params.page_size.GetArea(), kMaxRasterSizeInPixels); if (big_emf) { scoped_ptr<NativeMetafile> raster_metafile( metafile->RasterizeMetafile(raster_size)); @@ -178,16 +215,35 @@ void PrintViewManagerBase::OnDidPrintPage( return; } } -#endif +#endif // OS_WIN && !WIN_PDF_METAFILE_FOR_PRINTING +#if !defined(WIN_PDF_METAFILE_FOR_PRINTING) // Update the rendered document. It will send notifications to the listener. document->SetPage(params.page_number, - metafile.release(), - params.actual_shrink, - params.page_size, - params.content_area); + metafile.release(), + params.actual_shrink, + params.page_size, + params.content_area); ShouldQuitFromInnerMessageLoop(); +#else + if (metafile_must_be_valid) { + scoped_refptr<base::RefCountedBytes> bytes = new base::RefCountedBytes( + reinterpret_cast<const unsigned char*>(shared_buf.memory()), + params.data_size); + + if (!pdf_to_emf_converter_) + pdf_to_emf_converter_ = PdfToEmfConverter::CreateDefault(); + + const int kPrinterDpi = 600; + pdf_to_emf_converter_->Start( + bytes, + printing::PdfRenderSettings(params.content_area, kPrinterDpi, false), + base::Bind(&PrintViewManagerBase::OnPdfToEmfConverted, + base::Unretained(this), + params)); + } +#endif // !WIN_PDF_METAFILE_FOR_PRINTING } void PrintViewManagerBase::OnPrintingFailed(int cookie) { @@ -394,7 +450,8 @@ void PrintViewManagerBase::DisconnectFromCurrentPrintJob() { // DO NOT wait for the job to finish. ReleasePrintJob(); } -#if defined(OS_POSIX) && !defined(OS_MACOSX) +#if (defined(OS_POSIX) && !defined(OS_MACOSX)) || \ + defined(WIN_PDF_METAFILE_FOR_PRINTING) expecting_first_page_ = true; #endif } diff --git a/chrome/browser/printing/print_view_manager_base.h b/chrome/browser/printing/print_view_manager_base.h index bd627a2..ef48ed5 100644 --- a/chrome/browser/printing/print_view_manager_base.h +++ b/chrome/browser/printing/print_view_manager_base.h @@ -23,6 +23,7 @@ class RenderViewHost; namespace printing { class JobEventDetails; +class PdfToEmfConverter; class PrintJob; class PrintJobWorkerOwner; class PrintQueriesQueue; @@ -130,6 +131,13 @@ class PrintViewManagerBase : public content::NotificationObserver, // Release the PrinterQuery associated with our |cookie_|. void ReleasePrinterQuery(); +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) + // Called on completion of converting the pdf to emf. + void OnPdfToEmfConverted(const PrintHostMsg_DidPrintPage_Params& params, + double scale_factor, + const std::vector<base::FilePath>& emf_file); +#endif + content::NotificationRegistrar registrar_; // Manages the low-level talk to the printer. @@ -146,11 +154,16 @@ class PrintViewManagerBase : public content::NotificationObserver, // print settings are being loaded. bool inside_inner_message_loop_; -#if defined(OS_POSIX) && !defined(OS_MACOSX) +#if (defined(OS_POSIX) && !defined(OS_MACOSX)) || \ + defined(WIN_PDF_METAFILE_FOR_PRINTING) // Set to true when OnDidPrintPage() should be expecting the first page. bool expecting_first_page_; #endif +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) + scoped_ptr<PdfToEmfConverter> pdf_to_emf_converter_; +#endif + // The document cookie of the current PrinterQuery. int cookie_; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index c311ef5..9ebd268 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1648,6 +1648,8 @@ 'browser/printing/cloud_print/cloud_print_proxy_service.h', 'browser/printing/cloud_print/cloud_print_proxy_service_factory.cc', 'browser/printing/cloud_print/cloud_print_proxy_service_factory.h', + 'browser/printing/pdf_to_emf_converter.cc', + 'browser/printing/pdf_to_emf_converter.h', 'browser/printing/print_dialog_cloud.cc', 'browser/printing/print_dialog_cloud.h', 'browser/printing/print_error_dialog.cc', diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index f90fe39..d262d54 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -242,7 +242,6 @@ 'renderer/printing/print_web_view_helper_android.cc', 'renderer/printing/print_web_view_helper_linux.cc', 'renderer/printing/print_web_view_helper_mac.mm', - 'renderer/printing/print_web_view_helper_win.cc', 'renderer/safe_browsing/feature_extractor_clock.cc', 'renderer/safe_browsing/feature_extractor_clock.h', 'renderer/safe_browsing/features.cc', @@ -357,6 +356,15 @@ ['exclude', '^renderer/printing/'] ] }], + ['win_pdf_metafile_for_printing', { + 'sources': [ + 'renderer/printing/print_web_view_helper_pdf_win.cc', + ], + }, { + 'sources': [ + 'renderer/printing/print_web_view_helper_win.cc', + ], + }], ['OS=="android"', { 'sources!': [ 'renderer/extensions/app_window_custom_bindings.cc', diff --git a/chrome/common/chrome_utility_messages.h b/chrome/common/chrome_utility_messages.h index c6da23b..1bb2c46 100644 --- a/chrome/common/chrome_utility_messages.h +++ b/chrome/common/chrome_utility_messages.h @@ -186,10 +186,11 @@ IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_DecodeImageBase64, std::string) // base64 encoded image contents // Tell the utility process to render the given PDF into a metafile. -// TODO(vitalybuka): switch to IPC::PlatformFileForTransit. -IPC_MESSAGE_CONTROL4(ChromeUtilityMsg_RenderPDFPagesToMetafile, - base::PlatformFile, // PDF file - base::FilePath, // Location for output metafile +// The metafile path will have ".%d" inserted where the %d is the page number. +// If no page range is specified, all pages will be converted. +IPC_MESSAGE_CONTROL4(ChromeUtilityMsg_RenderPDFPagesToMetafiles, + IPC::PlatformFileForTransit, // PDF file + base::FilePath, // Base location for output metafile printing::PdfRenderSettings, // PDF render settings std::vector<printing::PageRange>) @@ -374,9 +375,9 @@ IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_DecodeImage_Succeeded, IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_DecodeImage_Failed) // Reply when the utility process has succeeded in rendering the PDF. -IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded, - int, // Highest rendered page number - double) // Scale factor +IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, + std::vector<printing::PageRange>, // Pages rendered + double) // Scale factor // Reply when an error occurred rendering the PDF. IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed) diff --git a/chrome/renderer/printing/print_web_view_helper.cc b/chrome/renderer/printing/print_web_view_helper.cc index 29340a3..6c08d58 100644 --- a/chrome/renderer/printing/print_web_view_helper.cc +++ b/chrome/renderer/printing/print_web_view_helper.cc @@ -1368,7 +1368,8 @@ void PrintWebViewHelper::FinishFramePrinting() { prep_frame_view_.reset(); } -#if defined(OS_MACOSX) || defined(OS_WIN) +#if defined(OS_MACOSX) || \ + (defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING)) bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, int page_count, const gfx::Size& canvas_size) { @@ -1393,7 +1394,7 @@ bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, return true; } -#endif // OS_MACOSX || OS_WIN +#endif // OS_MACOSX || !WIN_PDF_METAFILE_FOR_PRINTING // static - Not anonymous so that platform implementations can use it. void PrintWebViewHelper::ComputePageLayoutInPointsForCss( diff --git a/chrome/renderer/printing/print_web_view_helper.h b/chrome/renderer/printing/print_web_view_helper.h index e63a0a3..f5fb10e 100644 --- a/chrome/renderer/printing/print_web_view_helper.h +++ b/chrome/renderer/printing/print_web_view_helper.h @@ -206,6 +206,15 @@ class PrintWebViewHelper const gfx::Size& canvas_size, blink::WebFrame* frame, Metafile* metafile); +#elif defined(WIN_PDF_METAFILE_FOR_PRINTING) + void PrintPageInternal(const PrintMsg_PrintPage_Params& params, + const gfx::Size& canvas_size, + blink::WebFrame* frame, + Metafile* metafile, + bool is_preview, + double* actual_shrink, + gfx::Size* page_size_in_dpi, + gfx::Rect* content_area_in_dpi); #else void PrintPageInternal(const PrintMsg_PrintPage_Params& params, const gfx::Size& canvas_size, @@ -217,7 +226,7 @@ class PrintWebViewHelper const blink::WebNode& node); // Platform specific helper function for rendering page(s) to |metafile|. -#if defined(OS_WIN) +#if defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING) void RenderPage(const PrintMsg_Print_Params& params, int page_number, blink::WebFrame* frame, diff --git a/chrome/renderer/printing/print_web_view_helper_pdf_win.cc b/chrome/renderer/printing/print_web_view_helper_pdf_win.cc new file mode 100644 index 0000000..5d365ea --- /dev/null +++ b/chrome/renderer/printing/print_web_view_helper_pdf_win.cc @@ -0,0 +1,294 @@ +// Copyright 2014 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 "chrome/renderer/printing/print_web_view_helper.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/process/process_handle.h" +#include "chrome/common/print_messages.h" +#include "content/public/renderer/render_thread.h" +#include "printing/metafile.h" +#include "printing/metafile_impl.h" +#include "printing/metafile_skia_wrapper.h" +#include "printing/page_size_margins.h" +#include "printing/units.h" +#include "skia/ext/platform_device.h" +#include "skia/ext/vector_canvas.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" + + +namespace printing { + +using blink::WebFrame; + +bool PrintWebViewHelper::RenderPreviewPage( + int page_number, + const PrintMsg_Print_Params& print_params) { + PrintMsg_PrintPage_Params page_params; + page_params.params = print_params; + page_params.page_number = page_number; + scoped_ptr<Metafile> draft_metafile; + Metafile* initial_render_metafile = print_preview_context_.metafile(); + if (print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_) { + draft_metafile.reset(new PreviewMetafile); + initial_render_metafile = draft_metafile.get(); + } + + base::TimeTicks begin_time = base::TimeTicks::Now(); + double actual_shrink = + static_cast<float>(print_params.desired_dpi / print_params.dpi); + PrintPageInternal(page_params, + print_preview_context_.GetPrintCanvasSize(), + print_preview_context_.prepared_frame(), + initial_render_metafile, + true, + &actual_shrink, + NULL, + NULL); + print_preview_context_.RenderedPreviewPage( + base::TimeTicks::Now() - begin_time); + if (draft_metafile.get()) { + draft_metafile->FinishDocument(); + } else if (print_preview_context_.IsModifiable() && + print_preview_context_.generate_draft_pages()) { + DCHECK(!draft_metafile.get()); + draft_metafile.reset( + print_preview_context_.metafile()->GetMetafileForCurrentPage()); + } + return PreviewPageRendered(page_number, draft_metafile.get()); +} + +bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, + int page_count, + const gfx::Size& canvas_size) { + NativeMetafile metafile; + if (!metafile.Init()) + return false; + + const PrintMsg_PrintPages_Params& params = *print_pages_params_; + std::vector<int> printed_pages; + std::vector<double> shrink; + std::vector<gfx::Size> page_size_in_dpi; + std::vector<gfx::Rect> content_area_in_dpi; + double dpi_shrink = + static_cast<float>(params.params.desired_dpi / params.params.dpi); + + if (params.pages.empty()) { + for (int i = 0; i < page_count; ++i) { + printed_pages.push_back(i); + } + } else { + // TODO(vitalybuka): redesign to make more code cross platform. + for (size_t i = 0; i < params.pages.size(); ++i) { + if (params.pages[i] >= 0 && params.pages[i] < page_count) { + printed_pages.push_back(params.pages[i]); + } + } + } + if (printed_pages.empty()) + return false; + + for (size_t i = 0; i < printed_pages.size(); ++i) { + shrink.push_back(dpi_shrink); + page_size_in_dpi.push_back(gfx::Size()); + content_area_in_dpi.push_back(gfx::Rect()); + } + + PrintMsg_PrintPage_Params page_params; + page_params.params = params.params; + for (size_t i = 0; i < printed_pages.size(); ++i) { + page_params.page_number = printed_pages[i]; + PrintPageInternal(page_params, + canvas_size, + frame, + &metafile, + false, + &shrink[i], + &page_size_in_dpi[i], + &content_area_in_dpi[i]); + } + + // blink::printEnd() for PDF should be called before metafile is closed. + FinishFramePrinting(); + + metafile.FinishDocument(); + + // Get the size of the resulting metafile. + uint32 buf_size = metafile.GetDataSize(); + DCHECK_GT(buf_size, 0u); + + PrintHostMsg_DidPrintPage_Params printed_page_params; + printed_page_params.data_size = 0; + printed_page_params.document_cookie = params.params.document_cookie; + printed_page_params.page_size = params.params.page_size; + printed_page_params.content_area = params.params.printable_area; + + { + base::SharedMemory shared_buf; + // Allocate a shared memory buffer to hold the generated metafile data. + if (!shared_buf.CreateAndMapAnonymous(buf_size)) { + NOTREACHED() << "Buffer allocation failed"; + return false; + } + + // Copy the bits into shared memory. + if (!metafile.GetData(shared_buf.memory(), buf_size)) { + NOTREACHED() << "GetData() failed"; + shared_buf.Unmap(); + return false; + } + shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), + &printed_page_params.metafile_data_handle); + shared_buf.Unmap(); + + printed_page_params.data_size = buf_size; + Send(new PrintHostMsg_DuplicateSection( + routing_id(), + printed_page_params.metafile_data_handle, + &printed_page_params.metafile_data_handle)); + } + + for (size_t i = 0; i < printed_pages.size(); ++i) { + printed_page_params.page_number = printed_pages[i]; + printed_page_params.actual_shrink = shrink[i]; + printed_page_params.page_size = page_size_in_dpi[i]; + printed_page_params.content_area = content_area_in_dpi[i]; + Send(new PrintHostMsg_DidPrintPage(routing_id(), printed_page_params)); + printed_page_params.metafile_data_handle = INVALID_HANDLE_VALUE; + } + return true; +} + +void PrintWebViewHelper::PrintPageInternal( + const PrintMsg_PrintPage_Params& params, + const gfx::Size& canvas_size, + WebFrame* frame, + Metafile* metafile, + bool is_preview, + double* actual_shrink, + gfx::Size* page_size_in_dpi, + gfx::Rect* content_area_in_dpi) { + PageSizeMargins page_layout_in_points; + double css_scale_factor = 1.0f; + ComputePageLayoutInPointsForCss(frame, params.page_number, params.params, + ignore_css_margins_, &css_scale_factor, + &page_layout_in_points); + gfx::Size page_size; + gfx::Rect content_area; + GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, &page_size, + &content_area); + int dpi = static_cast<int>(params.params.dpi); + // Calculate the actual page size and content area in dpi. + if (page_size_in_dpi) { + *page_size_in_dpi = + gfx::Size(static_cast<int>(ConvertUnitDouble( + page_size.width(), kPointsPerInch, dpi)), + static_cast<int>(ConvertUnitDouble( + page_size.height(), kPointsPerInch, dpi))); + } + + if (content_area_in_dpi) { + *content_area_in_dpi = + gfx::Rect(static_cast<int>( + ConvertUnitDouble(content_area.x(), kPointsPerInch, dpi)), + static_cast<int>( + ConvertUnitDouble(content_area.y(), kPointsPerInch, dpi)), + static_cast<int>(ConvertUnitDouble( + content_area.width(), kPointsPerInch, dpi)), + static_cast<int>(ConvertUnitDouble( + content_area.height(), kPointsPerInch, dpi))); + } + + if (!is_preview) { + page_size = + gfx::Size(static_cast<int>(page_layout_in_points.content_width * + params.params.max_shrink), + static_cast<int>(page_layout_in_points.content_height * + params.params.max_shrink)); + } + + gfx::Rect canvas_area = + params.params.display_header_footer ? gfx::Rect(page_size) : content_area; + + float webkit_page_shrink_factor = + frame->getPrintPageShrink(params.page_number); + float scale_factor = css_scale_factor * webkit_page_shrink_factor; + + SkBaseDevice* device = metafile->StartPageForVectorCanvas(page_size, + canvas_area, + scale_factor); + if (!device) + return; + + // The printPage method take a reference to the canvas we pass down, so it + // can't be a stack object. + skia::RefPtr<skia::VectorCanvas> canvas = + skia::AdoptRef(new skia::VectorCanvas(device)); + MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); + skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_); + + if (params.params.display_header_footer) { + // |page_number| is 0-based, so 1 is added. + PrintHeaderAndFooter(canvas.get(), params.page_number + 1, + print_preview_context_.total_page_count(), + scale_factor, + page_layout_in_points, *header_footer_info_, + params.params); + } + + float webkit_scale_factor = RenderPageContent(frame, + params.page_number, + canvas_area, + content_area, + scale_factor, + canvas.get()); + + if (*actual_shrink <= 0 || webkit_scale_factor <= 0) { + NOTREACHED() << "Printing page " << params.page_number << " failed."; + } else { + // While rendering certain plugins (PDF) to metafile, we might need to + // set custom scale factor. Update |actual_shrink| with custom scale + // if it is set on canvas. + // TODO(gene): We should revisit this solution for the next versions. + // Consider creating metafile of the right size (or resizable) + // https://code.google.com/p/chromium/issues/detail?id=126037 + if (!MetafileSkiaWrapper::GetCustomScaleOnCanvas( + *canvas, actual_shrink)) { + // Update the dpi adjustment with the "page |actual_shrink|" calculated in + // webkit. + *actual_shrink /= (webkit_scale_factor * css_scale_factor); + } + } + + // Done printing. Close the device context to retrieve the compiled metafile. + if (!metafile->FinishPage()) + NOTREACHED() << "metafile failed"; +} + +bool PrintWebViewHelper::CopyMetafileDataToSharedMem( + Metafile* metafile, base::SharedMemoryHandle* shared_mem_handle) { + uint32 buf_size = metafile->GetDataSize(); + base::SharedMemory shared_buf; + // Allocate a shared memory buffer to hold the generated metafile data. + if (!shared_buf.CreateAndMapAnonymous(buf_size)) { + NOTREACHED() << "Buffer allocation failed"; + return false; + } + + // Copy the bits into shared memory. + if (!metafile->GetData(shared_buf.memory(), buf_size)) { + NOTREACHED() << "GetData() failed"; + shared_buf.Unmap(); + return false; + } + shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), shared_mem_handle); + shared_buf.Unmap(); + + Send(new PrintHostMsg_DuplicateSection(routing_id(), *shared_mem_handle, + shared_mem_handle)); + return true; +} + +} // namespace printing diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc index 3054adc..d8be0b1 100644 --- a/chrome/service/service_utility_process_host.cc +++ b/chrome/service/service_utility_process_host.cc @@ -107,35 +107,18 @@ bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile( scratch_metafile_dir_.reset(new base::ScopedTempDir); if (!scratch_metafile_dir_->CreateUniqueTempDir()) return false; - if (!base::CreateTemporaryFileInDir(scratch_metafile_dir_->path(), - &metafile_path_)) { - return false; - } - + metafile_path_ = scratch_metafile_dir_->path().AppendASCII("output.emf"); if (!StartProcess(false, scratch_metafile_dir_->path())) return false; - base::win::ScopedHandle pdf_file( - ::CreateFile(pdf_path.value().c_str(), - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL)); - if (pdf_file == INVALID_HANDLE_VALUE) - return false; - HANDLE pdf_file_in_utility_process = NULL; - ::DuplicateHandle(::GetCurrentProcess(), pdf_file, handle(), - &pdf_file_in_utility_process, 0, false, - DUPLICATE_SAME_ACCESS); - if (!pdf_file_in_utility_process) - return false; + base::File pdf_file( + pdf_path, + base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE); DCHECK(!waiting_for_reply_); waiting_for_reply_ = true; return child_process_host_->Send( - new ChromeUtilityMsg_RenderPDFPagesToMetafile( - pdf_file_in_utility_process, + new ChromeUtilityMsg_RenderPDFPagesToMetafiles( + IPC::TakeFileHandleForProcess(pdf_file.Pass(), handle()), metafile_path_, render_settings, page_ranges)); @@ -248,8 +231,8 @@ bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message) IPC_MESSAGE_HANDLER( - ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded, - OnRenderPDFPagesToMetafileSucceeded) + ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, + OnRenderPDFPagesToMetafilesSucceeded) IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed, OnRenderPDFPagesToMetafileFailed) IPC_MESSAGE_HANDLER( @@ -272,8 +255,8 @@ base::ProcessHandle ServiceUtilityProcessHost::GetHandle() const { return handle_; } -void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileSucceeded( - int highest_rendered_page_number, +void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesSucceeded( + const std::vector<printing::PageRange>& page_ranges, double scale_factor) { UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent", SERVICE_UTILITY_METAFILE_SUCCEEDED, @@ -286,10 +269,21 @@ void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileSucceeded( // scratch metafile directory. The client will delete it when it is done with // metafile. scratch_metafile_dir_->Take(); + + // TODO(vitalybuka|scottmg): http://crbug.com/170859: Currently, only one + // page is printed at a time. This would need to be refactored to change + // this. + CHECK_EQ(1u, page_ranges.size()); + CHECK_EQ(page_ranges[0].from, page_ranges[0].to); + int page_number = page_ranges[0].from; client_message_loop_proxy_->PostTask( FROM_HERE, - base::Bind(&Client::MetafileAvailable, client_.get(), metafile_path_, - highest_rendered_page_number, scale_factor)); + base::Bind(&Client::MetafileAvailable, + client_.get(), + metafile_path_.InsertBeforeExtensionASCII( + base::StringPrintf(".%d", page_number)), + page_number, + scale_factor)); } void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() { diff --git a/chrome/service/service_utility_process_host.h b/chrome/service/service_utility_process_host.h index 855e2a7..ac37bdb 100644 --- a/chrome/service/service_utility_process_host.h +++ b/chrome/service/service_utility_process_host.h @@ -139,8 +139,9 @@ class ServiceUtilityProcessHost : public content::ChildProcessHostDelegate { base::ProcessHandle handle() const { return handle_; } // Messages handlers: - void OnRenderPDFPagesToMetafileSucceeded(int highest_rendered_page_number, - double scale_factor); + void OnRenderPDFPagesToMetafilesSucceeded( + const std::vector<printing::PageRange>& page_ranges, + double scale_factor); void OnRenderPDFPagesToMetafileFailed(); void OnGetPrinterCapsAndDefaultsSucceeded( const std::string& printer_name, @@ -158,7 +159,7 @@ class ServiceUtilityProcessHost : public content::ChildProcessHostDelegate { scoped_refptr<Client> client_; scoped_refptr<base::MessageLoopProxy> client_message_loop_proxy_; bool waiting_for_reply_; - // The path to the temp file where the metafile will be written to. + // The base path to the temp file where the metafile will be written to. base::FilePath metafile_path_; // The temporary folder created for the metafile. scoped_ptr<base::ScopedTempDir> scratch_metafile_dir_; diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc index 777149d..302e8a2 100644 --- a/chrome/utility/chrome_content_utility_client.cc +++ b/chrome/utility/chrome_content_utility_client.cc @@ -365,7 +365,7 @@ bool ChromeContentUtilityClient::OnMessageReceived( OnParseUpdateManifest) IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImage, OnDecodeImage) IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImageBase64, OnDecodeImageBase64) - IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafile, + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles, OnRenderPDFPagesToMetafile) IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToPWGRaster, OnRenderPDFPagesToPWGRaster) @@ -567,23 +567,29 @@ void ChromeContentUtilityClient::OnCreateZipFile( #endif // defined(OS_CHROMEOS) void ChromeContentUtilityClient::OnRenderPDFPagesToMetafile( - base::PlatformFile pdf_file, + IPC::PlatformFileForTransit pdf_transit, const base::FilePath& metafile_path, const printing::PdfRenderSettings& settings, - const std::vector<printing::PageRange>& page_ranges) { + const std::vector<printing::PageRange>& page_ranges_const) { bool succeeded = false; #if defined(OS_WIN) + base::PlatformFile pdf_file = + IPC::PlatformFileForTransitToPlatformFile(pdf_transit); int highest_rendered_page_number = 0; double scale_factor = 1.0; + std::vector<printing::PageRange> page_ranges = page_ranges_const; succeeded = RenderPDFToWinMetafile(pdf_file, metafile_path, settings, - page_ranges, + &page_ranges, &highest_rendered_page_number, &scale_factor); if (succeeded) { - Send(new ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded( - highest_rendered_page_number, scale_factor)); + // TODO(vitalybuka|scottmg): http://crbug.com/170859. These could + // potentially be sent as each page is converted so that the spool could + // start sooner. + Send(new ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded( + page_ranges, scale_factor)); } #endif // defined(OS_WIN) if (!succeeded) { @@ -614,9 +620,10 @@ bool ChromeContentUtilityClient::RenderPDFToWinMetafile( base::PlatformFile pdf_file, const base::FilePath& metafile_path, const printing::PdfRenderSettings& settings, - const std::vector<printing::PageRange>& page_ranges, + std::vector<printing::PageRange>* page_ranges, int* highest_rendered_page_number, double* scale_factor) { + DCHECK(page_ranges); *highest_rendered_page_number = -1; *scale_factor = 1.0; base::win::ScopedHandle file(pdf_file); @@ -645,26 +652,37 @@ bool ChromeContentUtilityClient::RenderPDFToWinMetafile( return false; } - printing::Emf metafile; - metafile.InitToFile(metafile_path); - // We need to scale down DC to fit an entire page into DC available area. - // Current metafile is based on screen DC and have current screen size. - // Writing outside of those boundaries will result in the cut-off output. - // On metafiles (this is the case here), scaling down will still record - // original coordinates and we'll be able to print in full resolution. - // Before playback we'll need to counter the scaling up that will happen - // in the service (print_system_win.cc). - *scale_factor = gfx::CalculatePageScale(metafile.context(), - settings.area().right(), - settings.area().bottom()); - gfx::ScaleDC(metafile.context(), *scale_factor); + // If no range supplied, do all pages. + if (page_ranges->empty()) { + printing::PageRange page_range_all; + page_range_all.from = 0; + page_range_all.to = total_page_count - 1; + page_ranges->push_back(page_range_all); + } bool ret = false; std::vector<printing::PageRange>::const_iterator iter; - for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) { + for (iter = page_ranges->begin(); iter != page_ranges->end(); ++iter) { for (int page_number = iter->from; page_number <= iter->to; ++page_number) { if (page_number >= total_page_count) break; + + printing::Emf metafile; + metafile.InitToFile(metafile_path.InsertBeforeExtensionASCII( + base::StringPrintf(".%d", page_number))); + + // We need to scale down DC to fit an entire page into DC available area. + // Current metafile is based on screen DC and have current screen size. + // Writing outside of those boundaries will result in the cut-off output. + // On metafiles (this is the case here), scaling down will still record + // original coordinates and we'll be able to print in full resolution. + // Before playback we'll need to counter the scaling up that will happen + // in the service (print_system_win.cc). + *scale_factor = gfx::CalculatePageScale(metafile.context(), + settings.area().right(), + settings.area().bottom()); + gfx::ScaleDC(metafile.context(), *scale_factor); + // The underlying metafile is of type Emf and ignores the arguments passed // to StartPage. metafile.StartPage(gfx::Size(), gfx::Rect(), 1); @@ -679,9 +697,9 @@ bool ChromeContentUtilityClient::RenderPDFToWinMetafile( ret = true; } metafile.FinishPage(); + metafile.FinishDocument(); } } - metafile.FinishDocument(); return ret; } #endif // defined(OS_WIN) diff --git a/chrome/utility/chrome_content_utility_client.h b/chrome/utility/chrome_content_utility_client.h index 8e6ba97..96e467f 100644 --- a/chrome/utility/chrome_content_utility_client.h +++ b/chrome/utility/chrome_content_utility_client.h @@ -56,7 +56,7 @@ class ChromeContentUtilityClient : public content::ContentUtilityClient { void OnDecodeImage(const std::vector<unsigned char>& encoded_data); void OnDecodeImageBase64(const std::string& encoded_data); void OnRenderPDFPagesToMetafile( - base::PlatformFile pdf_file, + IPC::PlatformFileForTransit pdf_transit, const base::FilePath& metafile_path, const printing::PdfRenderSettings& settings, const std::vector<printing::PageRange>& page_ranges); @@ -78,11 +78,14 @@ class ChromeContentUtilityClient : public content::ContentUtilityClient { #if defined(OS_WIN) // Helper method for Windows. // |highest_rendered_page_number| is set to -1 on failure to render any page. + // |page_ranges| is both input and output. If supplied as input, only the + // specified pages will be rendered. If an empty vector is supplied it will + // be filled with a range of all pages that were rendered. bool RenderPDFToWinMetafile( base::PlatformFile pdf_file, const base::FilePath& metafile_path, const printing::PdfRenderSettings& settings, - const std::vector<printing::PageRange>& page_ranges, + std::vector<printing::PageRange>* page_ranges, int* highest_rendered_page_number, double* scale_factor); #endif // defined(OS_WIN) diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc index 7d4670f..c92e694 100644 --- a/content/renderer/pepper/pepper_plugin_instance_impl.cc +++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc @@ -1637,7 +1637,8 @@ bool PepperPluginInstanceImpl::PrintPage(int page_number, // The canvas only has a metafile on it for print preview. bool save_for_later = (printing::MetafileSkiaWrapper::GetMetafileFromCanvas(*canvas) != NULL); -#if defined(OS_MACOSX) || defined(OS_WIN) +#if defined(OS_MACOSX) || \ + (defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING)) save_for_later = save_for_later && skia::IsPreviewMetafile(*canvas); #endif if (save_for_later) { diff --git a/printing/metafile_impl.h b/printing/metafile_impl.h index 840a1ce..9fb2307 100644 --- a/printing/metafile_impl.h +++ b/printing/metafile_impl.h @@ -13,10 +13,10 @@ namespace printing { -#if defined(OS_WIN) +#if defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING) typedef Emf NativeMetafile; typedef PdfMetafileSkia PreviewMetafile; -#elif defined(OS_POSIX) +#else typedef PdfMetafileSkia NativeMetafile; typedef PdfMetafileSkia PreviewMetafile; #endif |