#include "chrome/browser/printing/pdf_to_emf_converter.h"
+#include <stdint.h>
+#include <windows.h>
+
+#include <memory>
#include <queue>
+#include <utility>
+#include <vector>
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "chrome/common/chrome_utility_messages.h"
#include "chrome/common/print_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/utility_process_host_client.h"
#include "printing/emf_win.h"
#include "printing/pdf_render_settings.h"
+#include "ui/base/l10n/l10n_util.h"
namespace printing {
class LazyEmf : public MetafilePlayer {
public:
LazyEmf(const scoped_refptr<RefCountedTempDir>& temp_dir, ScopedTempFile file)
- : temp_dir_(temp_dir), file_(std::move(file)) {}
+ : temp_dir_(temp_dir), file_(std::move(file)) {
+ CHECK(file_);
+ }
~LazyEmf() override { Close(); }
bool SafePlayback(HDC hdc) const override;
const PdfRenderSettings& settings);
void Start(const scoped_refptr<base::RefCountedMemory>& data,
+ bool print_text_with_gdi,
const PdfToEmfConverter::StartCallback& start_callback);
void GetPage(int page_number,
void Stop();
+ // Needs to be public to handle ChromeUtilityHostMsg_PreCacheFontCharacters
+ // sync message replies.
+ bool Send(IPC::Message* msg);
+
// UtilityProcessHostClient implementation.
- virtual void OnProcessCrashed(int exit_code) override;
- virtual void OnProcessLaunchFailed(int exit_code) override;
- virtual bool OnMessageReceived(const IPC::Message& message) override;
+ void OnProcessCrashed(int exit_code) override;
+ void OnProcessLaunchFailed(int exit_code) override;
+ bool OnMessageReceived(const IPC::Message& message) override;
private:
class GetPageCallbackData {
int page_number_;
PdfToEmfConverter::GetPageCallback callback_;
ScopedTempFile emf_;
- };
- virtual ~PdfToEmfUtilityProcessHostClient();
+ DISALLOW_COPY_AND_ASSIGN(GetPageCallbackData);
+ };
- bool Send(IPC::Message* msg);
+ ~PdfToEmfUtilityProcessHostClient() override;
// Message handlers.
- void OnProcessStarted();
void OnPageCount(int page_count);
void OnPageDone(bool success, float scale_factor);
+ void OnPreCacheFontCharacters(const LOGFONT& log_font,
+ const base::string16& characters);
void OnFailed();
- void OnTempPdfReady(ScopedTempFile pdf);
+ void OnTempPdfReady(bool print_text_with_gdi, ScopedTempFile pdf);
void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf);
scoped_refptr<RefCountedTempDir> temp_dir_;
// Used to suppress callbacks after PdfToEmfConverterImpl is deleted.
base::WeakPtr<PdfToEmfConverterImpl> converter_;
PdfRenderSettings settings_;
- scoped_refptr<base::RefCountedMemory> data_;
// Document loaded callback.
PdfToEmfConverter::StartCallback start_callback_;
public:
PdfToEmfConverterImpl();
- virtual ~PdfToEmfConverterImpl();
+ ~PdfToEmfConverterImpl() override;
- virtual void Start(const scoped_refptr<base::RefCountedMemory>& data,
- const PdfRenderSettings& conversion_settings,
- const StartCallback& start_callback) override;
+ void Start(const scoped_refptr<base::RefCountedMemory>& data,
+ const PdfRenderSettings& conversion_settings,
+ bool print_text_with_gdi,
+ const StartCallback& start_callback) override;
- virtual void GetPage(int page_number,
- const GetPageCallback& get_page_callback) override;
+ void GetPage(int page_number,
+ const GetPageCallback& get_page_callback) override;
// Helps to cancel callbacks if this object is destroyed.
void RunCallback(const base::Closure& callback);
if (!(*temp_dir)->IsValid())
return file;
base::FilePath path;
- if (!base::CreateTemporaryFileInDir((*temp_dir)->GetPath(), &path))
+ if (!base::CreateTemporaryFileInDir((*temp_dir)->GetPath(), &path)) {
+ PLOG(ERROR) << "Failed to create file in "
+ << (*temp_dir)->GetPath().value();
return file;
+ }
file.reset(new base::File(path,
base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_WRITE |
base::File::FLAG_READ |
base::File::FLAG_DELETE_ON_CLOSE |
base::File::FLAG_TEMPORARY));
- if (!file->IsValid())
+ if (!file->IsValid()) {
+ PLOG(ERROR) << "Failed to create " << path.value();
file.reset();
+ }
return file;
}
static_cast<int>(data->size()) !=
pdf_file->WriteAtCurrentPos(data->front_as<char>(), data->size())) {
pdf_file.reset();
+ return pdf_file;
}
pdf_file->Seek(base::File::FROM_BEGIN, 0);
return pdf_file;
void PdfToEmfUtilityProcessHostClient::Start(
const scoped_refptr<base::RefCountedMemory>& data,
+ bool print_text_with_gdi,
const PdfToEmfConverter::StartCallback& start_callback) {
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
- BrowserThread::PostTask(BrowserThread::IO,
- FROM_HERE,
- base::Bind(&PdfToEmfUtilityProcessHostClient::Start,
- this,
- data,
- start_callback));
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&PdfToEmfUtilityProcessHostClient::Start, this, data,
+ print_text_with_gdi, start_callback));
return;
}
- data_ = data;
// Store callback before any OnFailed() call to make it called on failure.
start_callback_ = start_callback;
// 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_ =
- content::UtilityProcessHost::Create(
- this, base::MessageLoop::current()->task_runner())->AsWeakPtr();
- if (!utility_process_host_)
- return OnFailed();
- // Should reply with OnProcessStarted().
- Send(new ChromeUtilityMsg_StartupPing);
-}
+ utility_process_host_ = content::UtilityProcessHost::Create(
+ this, base::ThreadTaskRunnerHandle::Get())
+ ->AsWeakPtr();
+ utility_process_host_->SetName(base::ASCIIToUTF16(
+ "IDS_UTILITY_PROCESS_EMF_CONVERTOR_NAME"));
-void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- if (!utility_process_host_)
- return OnFailed();
-
- scoped_refptr<base::RefCountedMemory> data = data_;
- data_ = NULL;
BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::FILE,
- FROM_HERE,
+ BrowserThread::FILE, FROM_HERE,
base::Bind(&CreateTempPdfFile, data, &temp_dir_),
- base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this));
+ base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this,
+ print_text_with_gdi));
}
-void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) {
+void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(bool print_text_with_gdi,
+ ScopedTempFile pdf) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!utility_process_host_ || !pdf)
return OnFailed();
// Should reply with OnPageCount().
Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
- IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false),
- settings_));
+ IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false), settings_,
+ print_text_with_gdi));
}
void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) {
get_page_callbacks_.pop();
}
+void PdfToEmfUtilityProcessHostClient::OnPreCacheFontCharacters(
+ const LOGFONT& font,
+ const base::string16& str) {
+ // TODO(scottmg): pdf/ppapi still require the renderer to be able to precache
+ // GDI fonts (http://crbug.com/383227), even when using DirectWrite.
+ // Eventually this shouldn't be added and should be moved to
+ // FontCacheDispatcher too. http://crbug.com/356346.
+
+ // First, comments from FontCacheDispatcher::OnPreCacheFont do apply here too.
+ // Except that for True Type fonts,
+ // GetTextMetrics will not load the font in memory.
+ // The only way windows seem to load properly, it is to create a similar
+ // device (like the one in which we print), then do an ExtTextOut,
+ // as we do in the printing thread, which is sandboxed.
+ HDC hdc = CreateEnhMetaFile(nullptr, nullptr, nullptr, nullptr);
+ HFONT font_handle = CreateFontIndirect(&font);
+ DCHECK(font_handle != nullptr);
+
+ HGDIOBJ old_font = SelectObject(hdc, font_handle);
+ DCHECK(old_font != nullptr);
+
+ ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, str.c_str(), str.length(), nullptr);
+
+ SelectObject(hdc, old_font);
+ DeleteObject(font_handle);
+
+ HENHMETAFILE metafile = CloseEnhMetaFile(hdc);
+
+ if (metafile)
+ DeleteEnhMetaFile(metafile);
+}
+
void PdfToEmfUtilityProcessHostClient::Stop() {
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
BrowserThread::PostTask(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message)
- IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
IPC_MESSAGE_HANDLER(
ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, OnPageCount)
IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone,
OnPageDone)
+ IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_PreCacheFontCharacters,
+ OnPreCacheFontCharacters)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
void PdfToEmfConverterImpl::Start(
const scoped_refptr<base::RefCountedMemory>& data,
const PdfRenderSettings& conversion_settings,
+ bool print_text_with_gdi,
const StartCallback& start_callback) {
DCHECK(!utility_client_.get());
utility_client_ = new PdfToEmfUtilityProcessHostClient(
weak_ptr_factory_.GetWeakPtr(), conversion_settings);
- utility_client_->Start(data, start_callback);
+ utility_client_->Start(data, print_text_with_gdi, start_callback);
}
void PdfToEmfConverterImpl::GetPage(int page_number,