diff --git a/cef.gyp b/cef.gyp index 7246aeddc..76f55a107 100644 --- a/cef.gyp +++ b/cef.gyp @@ -1038,6 +1038,8 @@ 'libcef/renderer/v8_impl.h', 'libcef/renderer/webkit_glue.cc', 'libcef/renderer/webkit_glue.h', + 'libcef/utility/content_utility_client.cc', + 'libcef/utility/content_utility_client.h', '<(DEPTH)/chrome/common/chrome_switches.cc', '<(DEPTH)/chrome/common/chrome_switches.h', # Include sources for proxy support. @@ -1099,7 +1101,25 @@ 'libcef/browser/menu_creator_runner_win.cc', 'libcef/browser/menu_creator_runner_win.h', 'libcef/browser/render_widget_host_view_osr_win.cc', - # Include sources for printing. + ], + }], + ['OS=="win" and win_pdf_metafile_for_printing==1', { + 'dependencies': [ + '<(DEPTH)/pdf/pdf.gyp:pdf', + ], + 'sources': [ + # Include sources for printing using PDF. + 'libcef/utility/printing_handler.cc', + 'libcef/utility/printing_handler.h', + '<(DEPTH)/chrome/browser/printing/pdf_to_emf_converter.cc', + '<(DEPTH)/chrome/browser/printing/pdf_to_emf_converter.h', + '<(DEPTH)/chrome/common/chrome_utility_printing_messages.h', + '<(DEPTH)/chrome/renderer/printing/print_web_view_helper_pdf_win.cc', + ], + }], + ['OS=="win" and win_pdf_metafile_for_printing!=1', { + 'sources': [ + # Include sources for printing using EMF. '<(DEPTH)/chrome/renderer/printing/print_web_view_helper_win.cc', ], }], diff --git a/cef.gypi b/cef.gypi index 591caf6c3..1cdb880f7 100644 --- a/cef.gypi +++ b/cef.gypi @@ -10,14 +10,8 @@ # Directory for CEF source files. [ 'OS=="win"', { 'cef_directory' : '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 printing_enabled_.Init( @@ -115,8 +123,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& emf_files) { + PrintedDocument* document = print_job_->document(); + if (!document) + return; + + for (size_t i = 0; i < emf_files.size(); ++i) { + scoped_ptr 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; @@ -127,9 +163,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 @@ -152,10 +189,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 raster_metafile( metafile->RasterizeMetafile(raster_size)); @@ -169,8 +206,9 @@ 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(), @@ -181,6 +219,26 @@ void PrintViewManagerBase::OnDidPrintPage( params.content_area); ShouldQuitFromInnerMessageLoop(); +#else + if (metafile_must_be_valid) { + scoped_refptr bytes = new base::RefCountedBytes( + reinterpret_cast(shared_buf.memory()), + params.data_size); + + document->DebugDumpData(bytes, FILE_PATH_LITERAL(".pdf")); + + if (!pdf_to_emf_converter_) + pdf_to_emf_converter_ = PdfToEmfConverter::CreateDefault(); + + const int kPrinterDpi = print_job_->settings().dpi(); + pdf_to_emf_converter_->Start( + bytes, + printing::PdfRenderSettings(params.content_area, kPrinterDpi, true), + base::Bind(&PrintViewManagerBase::OnPdfToEmfConverted, + base::Unretained(this), + params)); + } +#endif // !WIN_PDF_METAFILE_FOR_PRINTING } void PrintViewManagerBase::OnPrintingFailed(int cookie) { @@ -373,7 +431,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/libcef/browser/printing/print_view_manager_base.h b/libcef/browser/printing/print_view_manager_base.h index 9b07a8243..a0a42fbe7 100644 --- a/libcef/browser/printing/print_view_manager_base.h +++ b/libcef/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; @@ -129,6 +130,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& emf_file); +#endif + content::NotificationRegistrar registrar_; // Manages the low-level talk to the printer. @@ -145,11 +153,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 pdf_to_emf_converter_; +#endif + // The document cookie of the current PrinterQuery. int cookie_; diff --git a/libcef/browser/printing/printing_message_filter.cc b/libcef/browser/printing/printing_message_filter.cc index ab7e9d4b2..1563ecb9d 100644 --- a/libcef/browser/printing/printing_message_filter.cc +++ b/libcef/browser/printing/printing_message_filter.cc @@ -147,7 +147,7 @@ void PrintingMessageFilter::OnAllocateTempFileForPrinting( int* sequence_number) { #if defined(OS_CHROMEOS) // TODO(thestig): Use |render_view_id| for Chrome OS. - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(BrowserThread::FILE); temp_file_fd->fd = *sequence_number = -1; temp_file_fd->auto_close = false; @@ -170,7 +170,7 @@ void PrintingMessageFilter::OnAllocateTempFileForPrinting( } } #elif defined(OS_ANDROID) - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::WebContents* wc = GetWebContentsForRenderView(render_view_id); if (!wc) return; @@ -188,7 +188,7 @@ void PrintingMessageFilter::OnAllocateTempFileForPrinting( void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id, int sequence_number) { #if defined(OS_CHROMEOS) - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(BrowserThread::FILE); SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map; SequenceToPathMap::iterator it = map->find(sequence_number); if (it == map->end()) { @@ -204,7 +204,7 @@ void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id, // Erase the entry in the map. map->erase(it); #elif defined(OS_ANDROID) - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::WebContents* wc = GetWebContentsForRenderView(render_view_id); if (!wc) return; @@ -228,18 +228,17 @@ void PrintingMessageFilter::CreatePrintDialogForFile( return; print_dialog_cloud::CreatePrintDialogForFile( wc->GetBrowserContext(), - wc->GetView()->GetTopLevelNativeWindow(), + wc->GetTopLevelNativeWindow(), path, wc->GetTitle(), base::string16(), - std::string("application/pdf"), - false); + std::string("application/pdf")); } #endif // defined(OS_CHROMEOS) content::WebContents* PrintingMessageFilter::GetWebContentsForRenderView( int render_view_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::RenderViewHost* view = content::RenderViewHost::FromID( render_process_id_, render_view_id); return view ? content::WebContents::FromRenderViewHost(view) : NULL; @@ -257,7 +256,7 @@ void PrintingMessageFilter::GetPrintSettingsForRenderView( GetPrintSettingsForRenderViewParams params, const base::Closure& callback, scoped_refptr printer_query) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::WebContents* wc = GetWebContentsForRenderView(render_view_id); if (wc) { scoped_ptr wc_observer( @@ -279,19 +278,19 @@ void PrintingMessageFilter::GetPrintSettingsForRenderView( void PrintingMessageFilter::OnGetPrintSettingsFailed( const base::Closure& callback, scoped_refptr printer_query) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); printer_query->GetSettingsDone(printing::PrintSettings(), printing::PrintingContext::FAILED); callback.Run(); } void PrintingMessageFilter::OnIsPrintingEnabled(bool* is_enabled) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); *is_enabled = true; } void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); scoped_refptr printer_query; printer_query = queue_->PopPrinterQuery(0); if (!printer_query) @@ -398,7 +397,7 @@ void PrintingMessageFilter::OnScriptedPrintReply( #if defined(OS_ANDROID) void PrintingMessageFilter::UpdateFileDescriptor(int render_view_id, int fd) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::WebContents* wc = GetWebContentsForRenderView(render_view_id); if (!wc) return; diff --git a/libcef/common/cef_messages.h b/libcef/common/cef_messages.h index a7fb14b52..7f032d036 100644 --- a/libcef/common/cef_messages.h +++ b/libcef/common/cef_messages.h @@ -223,3 +223,7 @@ struct ParamTraits > { #include "chrome/common/prerender_messages.h" #include "chrome/common/print_messages.h" + +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) +#include "chrome/common/chrome_utility_printing_messages.h" +#endif diff --git a/libcef/common/main_delegate.cc b/libcef/common/main_delegate.cc index 0fcde99b4..00a2b42ee 100644 --- a/libcef/common/main_delegate.cc +++ b/libcef/common/main_delegate.cc @@ -9,6 +9,7 @@ #include "libcef/common/cef_switches.h" #include "libcef/common/command_line_impl.h" #include "libcef/renderer/content_renderer_client.h" +#include "libcef/utility/content_utility_client.h" #include "base/base_switches.h" #include "base/command_line.h" @@ -46,6 +47,10 @@ #include "components/breakpad/app/breakpad_linux.h" #endif +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) +#include "chrome/common/chrome_paths.h" +#endif + namespace { base::LazyInstance::Leaky g_shell_breakpad_client = @@ -76,6 +81,10 @@ base::FilePath GetResourcesFilePath() { return GetFrameworkBundlePath().Append(FILE_PATH_LITERAL("Resources")); } +base::FilePath GetLibrariesFilePath() { + return GetFrameworkBundlePath().Append(FILE_PATH_LITERAL("Libraries")); +} + void OverrideFrameworkBundlePath() { base::mac::SetOverrideFrameworkBundlePath(GetFrameworkBundlePath()); } @@ -104,8 +113,32 @@ base::FilePath GetResourcesFilePath() { return pak_dir; } +base::FilePath GetLibrariesFilePath() { + return GetResourcesFilePath(); +} + #endif // !defined(OS_MACOSX) +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) + +// File name of the internal PDF plugin on different platforms. +const base::FilePath::CharType kInternalPDFPluginFileName[] = +#if defined(OS_WIN) + FILE_PATH_LITERAL("pdf.dll"); +#elif defined(OS_MACOSX) + FILE_PATH_LITERAL("PDF.plugin"); +#else // Linux and Chrome OS + FILE_PATH_LITERAL("libpdf.so"); +#endif + +void OverridePdfPluginPath() { + base::FilePath plugin_path = GetLibrariesFilePath(); + plugin_path = plugin_path.Append(kInternalPDFPluginFileName); + PathService::Override(chrome::FILE_PDF_PLUGIN, plugin_path); +} + +#endif // defined(WIN_PDF_METAFILE_FOR_PRINTING) + // Used to run the UI on a separate thread. class CefUIThread : public base::Thread { public: @@ -347,10 +380,10 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) { void CefMainDelegate::PreSandboxStartup() { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const std::string& process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); if (command_line.HasSwitch(switches::kEnableCrashReporter)) { - const std::string& process_type = command_line.GetSwitchValueASCII( - switches::kProcessType); breakpad::SetBreakpadClient(g_shell_breakpad_client.Pointer()); #if defined(OS_MACOSX) base::mac::DisableOSCrashDumps(); @@ -375,10 +408,19 @@ void CefMainDelegate::PreSandboxStartup() { } #endif +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) + OverridePdfPluginPath(); +#endif + if (command_line.HasSwitch(switches::kDisablePackLoading)) content_client_.set_pack_loading_disabled(true); InitializeResourceBundle(); + + if (process_type == switches::kUtilityProcess || + process_type == switches::kZygoteProcess) { + CefContentUtilityClient::PreSandboxStartup(); + } } int CefMainDelegate::RunProcess( @@ -441,6 +483,11 @@ content::ContentRendererClient* return renderer_client_.get(); } +content::ContentUtilityClient* CefMainDelegate::CreateContentUtilityClient() { + utility_client_.reset(new CefContentUtilityClient); + return utility_client_.get(); +} + void CefMainDelegate::ShutdownBrowser() { if (browser_runner_.get()) { browser_runner_->Shutdown(); diff --git a/libcef/common/main_delegate.h b/libcef/common/main_delegate.h index 440d19201..da1fdbe84 100644 --- a/libcef/common/main_delegate.h +++ b/libcef/common/main_delegate.h @@ -25,7 +25,6 @@ class BrowserMainRunner; class CefContentBrowserClient; class CefContentRendererClient; -class CefContentPluginClient; class CefContentUtilityClient; class CefMainDelegate : public content::ContentMainDelegate { @@ -45,6 +44,7 @@ class CefMainDelegate : public content::ContentMainDelegate { virtual content::ContentBrowserClient* CreateContentBrowserClient() OVERRIDE; virtual content::ContentRendererClient* CreateContentRendererClient() OVERRIDE; + virtual content::ContentUtilityClient* CreateContentUtilityClient() OVERRIDE; // Shut down the browser runner. void ShutdownBrowser(); @@ -60,6 +60,7 @@ class CefMainDelegate : public content::ContentMainDelegate { scoped_ptr browser_client_; scoped_ptr renderer_client_; + scoped_ptr utility_client_; CefContentClient content_client_; DISALLOW_COPY_AND_ASSIGN(CefMainDelegate); diff --git a/libcef/utility/content_utility_client.cc b/libcef/utility/content_utility_client.cc new file mode 100644 index 000000000..0cf615ccf --- /dev/null +++ b/libcef/utility/content_utility_client.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2014 the Chromium Embedded Framework authors. +// Portions Copyright (c) 2012 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 "libcef/utility/content_utility_client.h" + +#include "chrome/common/chrome_utility_messages.h" +#include "chrome/utility/utility_message_handler.h" +#include "content/public/utility/utility_thread.h" + +#if defined(ENABLE_FULL_PRINTING) && defined(WIN_PDF_METAFILE_FOR_PRINTING) +#include "libcef/utility/printing_handler.h" +#endif + +namespace { + +bool Send(IPC::Message* message) { + return content::UtilityThread::Get()->Send(message); +} + +} // namespace + +CefContentUtilityClient::CefContentUtilityClient() { +#if defined(ENABLE_FULL_PRINTING) && defined(WIN_PDF_METAFILE_FOR_PRINTING) + handlers_.push_back(new PrintingHandler()); +#endif +} + +CefContentUtilityClient::~CefContentUtilityClient() { +} + +bool CefContentUtilityClient::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + + IPC_BEGIN_MESSAGE_MAP(CefContentUtilityClient, message) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing, OnStartupPing) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + for (Handlers::iterator it = handlers_.begin(); + !handled && it != handlers_.end(); ++it) { + handled = (*it)->OnMessageReceived(message); + } + + return handled; +} + +// static +void CefContentUtilityClient::PreSandboxStartup() { +#if defined(ENABLE_FULL_PRINTING) && defined(WIN_PDF_METAFILE_FOR_PRINTING) + PrintingHandler::PreSandboxStartup(); +#endif +} + +void CefContentUtilityClient::OnStartupPing() { + Send(new ChromeUtilityHostMsg_ProcessStarted); + // Don't release the process, we assume further messages are on the way. +} diff --git a/libcef/utility/content_utility_client.h b/libcef/utility/content_utility_client.h new file mode 100644 index 000000000..aed828481 --- /dev/null +++ b/libcef/utility/content_utility_client.h @@ -0,0 +1,33 @@ +// Copyright (c) 2014 the Chromium Embedded Framework authors. +// Portions Copyright (c) 2012 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 LIBCEF_UTILITY_CONTENT_UTILITY_CLIENT_H_ +#define LIBCEF_UTILITY_CONTENT_UTILITY_CLIENT_H_ + +#include "base/memory/scoped_vector.h" +#include "content/public/utility/content_utility_client.h" + +class UtilityMessageHandler; + +class CefContentUtilityClient : public content::ContentUtilityClient { + public: + CefContentUtilityClient(); + virtual ~CefContentUtilityClient(); + + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + static void PreSandboxStartup(); + + private: + // IPC message handlers. + void OnStartupPing(); + + typedef ScopedVector Handlers; + Handlers handlers_; + + DISALLOW_COPY_AND_ASSIGN(CefContentUtilityClient); +}; + +#endif // LIBCEF_UTILITY_CONTENT_UTILITY_CLIENT_H_ diff --git a/libcef/utility/printing_handler.cc b/libcef/utility/printing_handler.cc new file mode 100644 index 000000000..6d53e9769 --- /dev/null +++ b/libcef/utility/printing_handler.cc @@ -0,0 +1,418 @@ +// 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 "libcef/utility/printing_handler.h" + +#include "base/file_util.h" +#include "base/lazy_instance.h" +#include "base/path_service.h" +#include "base/scoped_native_library.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_utility_printing_messages.h" +#include "content/public/utility/utility_thread.h" +#include "printing/page_range.h" +#include "printing/pdf_render_settings.h" + +#if defined(OS_WIN) +#include "base/win/iat_patch_function.h" +#include "printing/emf_win.h" +#include "ui/gfx/gdi_util.h" +#endif + +#if defined(ENABLE_FULL_PRINTING) +#include "printing/backend/print_backend.h" +#endif + +namespace { + +bool Send(IPC::Message* message) { + return content::UtilityThread::Get()->Send(message); +} + +void ReleaseProcessIfNeeded() { + content::UtilityThread::Get()->ReleaseProcessIfNeeded(); +} + +class PdfFunctionsBase { + public: + PdfFunctionsBase() : render_pdf_to_bitmap_func_(NULL), + get_pdf_doc_info_func_(NULL) {} + + bool Init() { + base::FilePath pdf_module_path; + if (!PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_module_path) || + !base::PathExists(pdf_module_path)) { + return false; + } + + pdf_lib_.Reset(base::LoadNativeLibrary(pdf_module_path, NULL)); + if (!pdf_lib_.is_valid()) { + LOG(WARNING) << "Couldn't load PDF plugin"; + return false; + } + + render_pdf_to_bitmap_func_ = + reinterpret_cast( + pdf_lib_.GetFunctionPointer("RenderPDFPageToBitmap")); + LOG_IF(WARNING, !render_pdf_to_bitmap_func_) << + "Missing RenderPDFPageToBitmap"; + + get_pdf_doc_info_func_ = + reinterpret_cast( + pdf_lib_.GetFunctionPointer("GetPDFDocInfo")); + LOG_IF(WARNING, !get_pdf_doc_info_func_) << "Missing GetPDFDocInfo"; + + if (!render_pdf_to_bitmap_func_ || !get_pdf_doc_info_func_ || + !PlatformInit(pdf_module_path, pdf_lib_)) { + Reset(); + } + + return IsValid(); + } + + bool IsValid() const { + return pdf_lib_.is_valid(); + } + + void Reset() { + pdf_lib_.Reset(NULL); + } + + bool RenderPDFPageToBitmap(const void* pdf_buffer, + int pdf_buffer_size, + int page_number, + void* bitmap_buffer, + int bitmap_width, + int bitmap_height, + int dpi_x, + int dpi_y, + bool autorotate) { + if (!render_pdf_to_bitmap_func_) + return false; + return render_pdf_to_bitmap_func_(pdf_buffer, pdf_buffer_size, page_number, + bitmap_buffer, bitmap_width, + bitmap_height, dpi_x, dpi_y, autorotate); + } + + bool GetPDFDocInfo(const void* pdf_buffer, + int buffer_size, + int* page_count, + double* max_page_width) { + if (!get_pdf_doc_info_func_) + return false; + return get_pdf_doc_info_func_(pdf_buffer, buffer_size, page_count, + max_page_width); + } + + protected: + virtual bool PlatformInit( + const base::FilePath& pdf_module_path, + const base::ScopedNativeLibrary& pdf_lib) { + return true; + } + + private: + // Exported by PDF plugin. + typedef bool (*RenderPDFPageToBitmapProc)(const void* pdf_buffer, + int pdf_buffer_size, + int page_number, + void* bitmap_buffer, + int bitmap_width, + int bitmap_height, + int dpi_x, + int dpi_y, + bool autorotate); + typedef bool (*GetPDFDocInfoProc)(const void* pdf_buffer, + int buffer_size, int* page_count, + double* max_page_width); + + RenderPDFPageToBitmapProc render_pdf_to_bitmap_func_; + GetPDFDocInfoProc get_pdf_doc_info_func_; + + base::ScopedNativeLibrary pdf_lib_; + DISALLOW_COPY_AND_ASSIGN(PdfFunctionsBase); +}; + +#if defined(OS_WIN) +// The 2 below IAT patch functions are almost identical to the code in +// render_process_impl.cc. This is needed to work around specific Windows APIs +// used by the Chrome PDF plugin that will fail in the sandbox. +static base::win::IATPatchFunction g_iat_patch_createdca; +HDC WINAPI UtilityProcess_CreateDCAPatch(LPCSTR driver_name, + LPCSTR device_name, + LPCSTR output, + const DEVMODEA* init_data) { + if (driver_name && (std::string("DISPLAY") == driver_name)) { + // CreateDC fails behind the sandbox, but not CreateCompatibleDC. + return CreateCompatibleDC(NULL); + } + + NOTREACHED(); + return CreateDCA(driver_name, device_name, output, init_data); +} + +static base::win::IATPatchFunction g_iat_patch_get_font_data; +DWORD WINAPI UtilityProcess_GetFontDataPatch( + HDC hdc, DWORD table, DWORD offset, LPVOID buffer, DWORD length) { + int rv = GetFontData(hdc, table, offset, buffer, length); + if (rv == GDI_ERROR && hdc) { + HFONT font = static_cast(GetCurrentObject(hdc, OBJ_FONT)); + + LOGFONT logfont; + if (GetObject(font, sizeof(LOGFONT), &logfont)) { + content::UtilityThread::Get()->PreCacheFont(logfont); + rv = GetFontData(hdc, table, offset, buffer, length); + content::UtilityThread::Get()->ReleaseCachedFonts(); + } + } + return rv; +} + +class PdfFunctionsWin : public PdfFunctionsBase { + public: + PdfFunctionsWin() : render_pdf_to_dc_func_(NULL) { + } + + bool PlatformInit( + const base::FilePath& pdf_module_path, + const base::ScopedNativeLibrary& pdf_lib) OVERRIDE { + // Patch the IAT for handling specific APIs known to fail in the sandbox. + if (!g_iat_patch_createdca.is_patched()) { + g_iat_patch_createdca.Patch(pdf_module_path.value().c_str(), + "gdi32.dll", "CreateDCA", + UtilityProcess_CreateDCAPatch); + } + + if (!g_iat_patch_get_font_data.is_patched()) { + g_iat_patch_get_font_data.Patch(pdf_module_path.value().c_str(), + "gdi32.dll", "GetFontData", + UtilityProcess_GetFontDataPatch); + } + render_pdf_to_dc_func_ = + reinterpret_cast( + pdf_lib.GetFunctionPointer("RenderPDFPageToDC")); + LOG_IF(WARNING, !render_pdf_to_dc_func_) << "Missing RenderPDFPageToDC"; + + return render_pdf_to_dc_func_ != NULL; + } + + bool RenderPDFPageToDC(const void* pdf_buffer, + int buffer_size, + int page_number, + HDC dc, + int dpi_x, + int dpi_y, + int bounds_origin_x, + int bounds_origin_y, + int bounds_width, + int bounds_height, + bool fit_to_bounds, + bool stretch_to_bounds, + bool keep_aspect_ratio, + bool center_in_bounds, + bool autorotate) { + if (!render_pdf_to_dc_func_) + return false; + return render_pdf_to_dc_func_(pdf_buffer, buffer_size, page_number, + dc, dpi_x, dpi_y, bounds_origin_x, + bounds_origin_y, bounds_width, bounds_height, + fit_to_bounds, stretch_to_bounds, + keep_aspect_ratio, center_in_bounds, + autorotate); + } + + private: + // Exported by PDF plugin. + typedef bool (*RenderPDFPageToDCProc)( + const void* pdf_buffer, int buffer_size, int page_number, HDC dc, + int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y, + int bounds_width, int bounds_height, bool fit_to_bounds, + bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds, + bool autorotate); + RenderPDFPageToDCProc render_pdf_to_dc_func_; + + DISALLOW_COPY_AND_ASSIGN(PdfFunctionsWin); +}; + +typedef PdfFunctionsWin PdfFunctions; +#else // OS_WIN +typedef PdfFunctionsBase PdfFunctions; +#endif // OS_WIN + +base::LazyInstance g_pdf_lib = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +PrintingHandler::PrintingHandler() {} + +PrintingHandler::~PrintingHandler() {} + +// static +void PrintingHandler::PreSandboxStartup() { + g_pdf_lib.Get().Init(); +} + +bool PrintingHandler::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintingHandler, message) +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles, + OnRenderPDFPagesToMetafile) +#endif + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetPrinterCapsAndDefaults, + OnGetPrinterCapsAndDefaults) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults, + OnGetPrinterSemanticCapsAndDefaults) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) +void PrintingHandler::OnRenderPDFPagesToMetafile( + IPC::PlatformFileForTransit pdf_transit, + const base::FilePath& metafile_path, + const printing::PdfRenderSettings& settings, + const std::vector& page_ranges_const) { + bool succeeded = false; + base::File pdf_file = IPC::PlatformFileForTransitToFile(pdf_transit); + int highest_rendered_page_number = 0; + double scale_factor = 1.0; + std::vector page_ranges = page_ranges_const; + succeeded = RenderPDFToWinMetafile(pdf_file.Pass(), + metafile_path, + settings, + &page_ranges, + &highest_rendered_page_number, + &scale_factor); + if (succeeded) { + // 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)); + } else { + Send(new ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed()); + } + ReleaseProcessIfNeeded(); +} +#endif + +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) +bool PrintingHandler::RenderPDFToWinMetafile( + base::File pdf_file, + const base::FilePath& metafile_path, + const printing::PdfRenderSettings& settings, + std::vector* page_ranges, + int* highest_rendered_page_number, + double* scale_factor) { + DCHECK(page_ranges); + *highest_rendered_page_number = -1; + *scale_factor = 1.0; + + if (!g_pdf_lib.Get().IsValid()) + return false; + + // TODO(sanjeevr): Add a method to the PDF DLL that takes in a file handle + // and a page range array. That way we don't need to read the entire PDF into + // memory. + int64 length = pdf_file.GetLength(); + if (length < 0) + return false; + + std::vector buffer; + buffer.resize(length); + if (length != pdf_file.Read(0, &buffer.front(), length)) + return false; + + int total_page_count = 0; + if (!g_pdf_lib.Get().GetPDFDocInfo(&buffer.front(), buffer.size(), + &total_page_count, NULL)) { + return false; + } + + // 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::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) { + 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); + if (g_pdf_lib.Get().RenderPDFPageToDC( + &buffer.front(), buffer.size(), page_number, metafile.context(), + settings.dpi(), settings.dpi(), settings.area().x(), + settings.area().y(), settings.area().width(), + settings.area().height(), true, false, true, true, + settings.autorotate())) { + if (*highest_rendered_page_number < page_number) + *highest_rendered_page_number = page_number; + ret = true; + } + metafile.FinishPage(); + metafile.FinishDocument(); + } + } + return ret; +} +#endif // defined(WIN_PDF_METAFILE_FOR_PRINTING) + +void PrintingHandler::OnGetPrinterCapsAndDefaults( + const std::string& printer_name) { + scoped_refptr print_backend = + printing::PrintBackend::CreateInstance(NULL); + printing::PrinterCapsAndDefaults printer_info; + + if (print_backend->GetPrinterCapsAndDefaults(printer_name, &printer_info)) { + Send(new ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded( + printer_name, printer_info)); + } else { + Send(new ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed( + printer_name)); + } + ReleaseProcessIfNeeded(); +} + +void PrintingHandler::OnGetPrinterSemanticCapsAndDefaults( + const std::string& printer_name) { + scoped_refptr print_backend = + printing::PrintBackend::CreateInstance(NULL); + printing::PrinterSemanticCapsAndDefaults printer_info; + + if (print_backend->GetPrinterSemanticCapsAndDefaults(printer_name, + &printer_info)) { + Send(new ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded( + printer_name, printer_info)); + } else { + Send(new ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed( + printer_name)); + } + ReleaseProcessIfNeeded(); +} diff --git a/libcef/utility/printing_handler.h b/libcef/utility/printing_handler.h new file mode 100644 index 000000000..c8bd2f9ba --- /dev/null +++ b/libcef/utility/printing_handler.h @@ -0,0 +1,64 @@ +// 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 LIBCEF_UTILITY_PRINTING_HANDLER_H_ +#define LIBCEF_UTILITY_PRINTING_HANDLER_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "chrome/utility/utility_message_handler.h" +#include "ipc/ipc_platform_file.h" + +#if !defined(ENABLE_FULL_PRINTING) +#error "Full printing must be enabled" +#endif + +namespace printing { +class PdfRenderSettings; +struct PageRange; +} + +// Dispatches IPCs for printing. +class PrintingHandler : public UtilityMessageHandler { + public: + PrintingHandler(); + virtual ~PrintingHandler(); + + static void PreSandboxStartup(); + + // IPC::Listener: + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + private: + // IPC message handlers. +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) + void OnRenderPDFPagesToMetafile( + IPC::PlatformFileForTransit pdf_transit, + const base::FilePath& metafile_path, + const printing::PdfRenderSettings& settings, + const std::vector& page_ranges); +#endif + +#if defined(WIN_PDF_METAFILE_FOR_PRINTING) + // 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::File pdf_file, + const base::FilePath& metafile_path, + const printing::PdfRenderSettings& settings, + std::vector* page_ranges, + int* highest_rendered_page_number, + double* scale_factor); +#endif + + void OnGetPrinterCapsAndDefaults(const std::string& printer_name); + void OnGetPrinterSemanticCapsAndDefaults(const std::string& printer_name); + + DISALLOW_COPY_AND_ASSIGN(PrintingHandler); +}; + +#endif // LIBCEF_UTILITY_PRINTING_HANDLER_H_ diff --git a/tools/distrib/win/README.redistrib.txt b/tools/distrib/win/README.redistrib.txt index 9a97610e8..cf18d609d 100644 --- a/tools/distrib/win/README.redistrib.txt +++ b/tools/distrib/win/README.redistrib.txt @@ -36,6 +36,10 @@ Optional components: ffmpegsumo.dll Note: Without this component HTML5 audio and video will not function. +* PDF support + pdf.dll + Note: Without this component printing will not function. + * Angle and Direct3D support d3dcompiler_43.dll (required for Windows XP) d3dcompiler_46.dll (required for Windows Vista and newer) diff --git a/tools/make_distrib.py b/tools/make_distrib.py index ffe5d235a..6f6932335 100644 --- a/tools/make_distrib.py +++ b/tools/make_distrib.py @@ -518,6 +518,7 @@ if platform == 'windows': 'libcef.dll', 'libEGL.dll', 'libGLESv2.dll', + 'pdf.dll', ] if options.ninjabuild: