Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2477)

Unified Diff: chrome/browser/printing/pdf_to_emf_converter.cc

Issue 2633573002: Add Postscript Printing (Closed)
Patch Set: Fix Linux compile error Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/printing/pdf_to_emf_converter.cc
diff --git a/chrome/browser/printing/pdf_to_emf_converter.cc b/chrome/browser/printing/pdf_to_emf_converter.cc
index fb0be329e76d0325e4b0c20645adf832fb1d3303..e79b5c86d2b9adb9ce48ee4218c572ac62ac5d56 100644
--- a/chrome/browser/printing/pdf_to_emf_converter.cc
+++ b/chrome/browser/printing/pdf_to_emf_converter.cc
@@ -30,12 +30,12 @@
#include "printing/pdf_render_settings.h"
#include "ui/base/l10n/l10n_util.h"
+using content::BrowserThread;
+
namespace printing {
namespace {
-using content::BrowserThread;
-
class PdfToEmfConverterImpl;
// Allows to delete temporary directory after all temporary files created inside
@@ -59,8 +59,8 @@ class RefCountedTempDir
DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir);
};
-typedef std::unique_ptr<base::File, BrowserThread::DeleteOnFileThread>
- ScopedTempFile;
+using ScopedTempFile =
+ std::unique_ptr<base::File, BrowserThread::DeleteOnFileThread>;
// Wrapper for Emf to keep only file handle in memory, and load actual data only
// on playback. Emf::InitFromFile() can play metafile directly from disk, but it
@@ -74,11 +74,12 @@ class LazyEmf : public MetafilePlayer {
}
~LazyEmf() override { Close(); }
+ private:
+ // MetafilePlayer:
bool SafePlayback(HDC hdc) const override;
bool GetDataAsVector(std::vector<char>* buffer) const override;
bool SaveTo(base::File* file) const override;
- private:
void Close() const;
bool LoadEmf(Emf* emf) const;
@@ -109,7 +110,6 @@ class PdfToEmfUtilityProcessHostClient
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,
@@ -168,7 +168,7 @@ class PdfToEmfUtilityProcessHostClient
const base::string16& characters);
void OnFailed();
- void OnTempPdfReady(bool print_text_with_gdi, ScopedTempFile pdf);
+ void OnTempPdfReady(ScopedTempFile pdf);
void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf);
scoped_refptr<RefCountedTempDir> temp_dir_;
@@ -186,7 +186,7 @@ class PdfToEmfUtilityProcessHostClient
// Queue of callbacks for GetPage() requests. Utility process should reply
// with PageDone in the same order as requests were received.
// Use containers that keeps element pointers valid after push() and pop().
- typedef std::queue<GetPageCallbackData> GetPageCallbacks;
+ using GetPageCallbacks = std::queue<GetPageCallbackData>;
GetPageCallbacks get_page_callbacks_;
DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient);
@@ -200,7 +200,6 @@ class PdfToEmfConverterImpl : public PdfToEmfConverter {
void Start(const scoped_refptr<base::RefCountedMemory>& data,
const PdfRenderSettings& conversion_settings,
- bool print_text_with_gdi,
const StartCallback& start_callback) override;
void GetPage(int page_number,
@@ -260,10 +259,10 @@ ScopedTempFile CreateTempPdfFile(
bool LazyEmf::SafePlayback(HDC hdc) const {
Emf emf;
bool result = LoadEmf(&emf) && emf.SafePlayback(hdc);
- // TODO(vitalybuka): Fix destruction of metafiles. For some reasons
- // instances of Emf are not deleted. crbug.com/411683
+ // TODO(thestig): Fix destruction of metafiles. For some reasons
+ // instances of Emf are not deleted. https://crbug.com/260806
// It's known that the Emf going to be played just once to a printer. So just
- // release file here.
+ // release |file_| here.
Close();
return result;
}
@@ -280,7 +279,7 @@ bool LazyEmf::SaveTo(base::File* file) const {
void LazyEmf::Close() const {
file_.reset();
- temp_dir_ = NULL;
+ temp_dir_ = nullptr;
}
bool LazyEmf::LoadEmf(Emf* emf) const {
@@ -305,13 +304,11 @@ PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
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,
- print_text_with_gdi, start_callback));
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&PdfToEmfUtilityProcessHostClient::Start,
+ this, data, start_callback));
return;
}
@@ -330,19 +327,17 @@ void PdfToEmfUtilityProcessHostClient::Start(
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::FILE, FROM_HERE,
base::Bind(&CreateTempPdfFile, data, &temp_dir_),
- base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this,
- print_text_with_gdi));
+ base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this));
}
-void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(bool print_text_with_gdi,
- ScopedTempFile pdf) {
+void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(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_,
- print_text_with_gdi));
+ IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false),
+ settings_));
}
void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) {
@@ -519,12 +514,11 @@ PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
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, print_text_with_gdi, start_callback);
+ utility_client_->Start(data, start_callback);
}
void PdfToEmfConverterImpl::GetPage(int page_number,
@@ -537,14 +531,427 @@ void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) {
callback.Run();
}
-} // namespace
+// See LazyEmf/RefCountedTempDir comments for why it is also needed here.
+// Maybe combine the two classes since they are so similar.
+class PostScriptMetaFile : public MetafilePlayer {
+ public:
Vitaly Buka (NO REVIEWS) 2017/01/17 19:29:16 To much of duplicated code. Could you just do publ
+ PostScriptMetaFile(const scoped_refptr<RefCountedTempDir>& temp_dir,
+ ScopedTempFile file)
+ : temp_dir_(temp_dir), file_(std::move(file)) {
+ CHECK(file_);
+ }
+ ~PostScriptMetaFile() override { Close(); }
+
+ private:
+ // MetafilePlayer:
+ bool SafePlayback(HDC hdc) const override;
+ bool GetDataAsVector(std::vector<char>* buffer) const override;
+ bool SaveTo(base::File* file) const override;
+
+ void Close() const;
+ bool LoadEmf(Emf* emf) const;
+
+ mutable scoped_refptr<RefCountedTempDir> temp_dir_;
+ mutable ScopedTempFile file_; // Mutable because of consts in base class.
+
+ DISALLOW_COPY_AND_ASSIGN(PostScriptMetaFile);
+};
+
+bool PostScriptMetaFile::SafePlayback(HDC hdc) const {
+ // TODO(thestig): Fix destruction of metafiles. For some reasons
+ // instances of Emf are not deleted. https://crbug.com/260806
+ // It's known that the Emf going to be played just once to a printer. So just
+ // release |file_| before returning.
+ Emf emf;
+ if (!LoadEmf(&emf)) {
+ Close();
+ return false;
+ }
+
+ {
+ // Ensure enumerator destruction before calling Close() below.
+ Emf::Enumerator emf_enum(emf, nullptr, nullptr);
+ for (const Emf::Record& record : emf_enum) {
+ auto* emf_record = record.record();
+ if (emf_record->iType != EMR_GDICOMMENT)
+ continue;
+
+ const EMRGDICOMMENT* comment =
+ reinterpret_cast<const EMRGDICOMMENT*>(emf_record);
+ const char* data = reinterpret_cast<const char*>(comment->Data);
+ const uint16_t* ptr = reinterpret_cast<const uint16_t*>(data);
+ int ret = ExtEscape(hdc, PASSTHROUGH, 2 + *ptr, data, 0, nullptr);
+ DCHECK_EQ(*ptr, ret);
+ }
+ }
+ Close();
+ return true;
+}
+
+bool PostScriptMetaFile::GetDataAsVector(std::vector<char>* buffer) const {
+ NOTREACHED();
+ return false;
+}
+
+bool PostScriptMetaFile::SaveTo(base::File* file) const {
+ Emf emf;
+ return LoadEmf(&emf) && emf.SaveTo(file);
+}
-PdfToEmfConverter::~PdfToEmfConverter() {
+void PostScriptMetaFile::Close() const {
+ file_.reset();
+ temp_dir_ = nullptr;
}
+bool PostScriptMetaFile::LoadEmf(Emf* emf) const {
+ file_->Seek(base::File::FROM_BEGIN, 0);
+ int64_t size = file_->GetLength();
+ if (size <= 0)
+ return false;
+ std::vector<char> data(size);
+ if (file_->ReadAtCurrentPos(data.data(), data.size()) != size)
+ return false;
+ return emf->InitFromData(data.data(), data.size());
+}
+
+class PdfToPostScriptConverterImpl;
+
+// Converts PDF into PostScript.
+// Class uses 3 threads: UI, IO and FILE.
+// Internal workflow is following:
+// 1. Create instance on the UI thread. (files_, settings_,)
+// 2. Create pdf file on the FILE thread.
+// 3. Start utility process and start conversion on the IO thread.
+// 4. Utility process returns page count.
+// 5. For each page:
+// 1. Clients requests page with file handle to a temp file.
+// 2. Utility converts the page, save it to the file and reply.
+//
+// All these steps work sequentially, so no data should be accessed
+// simultaneously by several threads.
+class PdfToPostScriptUtilityProcessHostClient
+ : public content::UtilityProcessHostClient {
+ public:
+ PdfToPostScriptUtilityProcessHostClient(
+ base::WeakPtr<PdfToPostScriptConverterImpl> converter,
+ const PdfRenderSettings& settings);
+
+ void Start(const scoped_refptr<base::RefCountedMemory>& data,
+ const PdfToPostScriptConverter::StartCallback& start_callback);
+
+ void GetPage(
+ int page_number,
+ const PdfToPostScriptConverter::GetPageCallback& get_page_callback);
+
+ void Stop();
+
+ // UtilityProcessHostClient implementation.
+ void OnProcessCrashed(int exit_code) override;
+ void OnProcessLaunchFailed(int exit_code) override;
+ bool OnMessageReceived(const IPC::Message& message) override;
+
+ private:
+ class GetPageCallbackData {
+ public:
+ GetPageCallbackData(
+ int page_number,
+ const PdfToPostScriptConverter::GetPageCallback& callback)
+ : page_number_(page_number), callback_(callback) {}
+
+ GetPageCallbackData(GetPageCallbackData&& other) {
+ *this = std::move(other);
+ }
+
+ GetPageCallbackData& operator=(GetPageCallbackData&& rhs) {
+ page_number_ = rhs.page_number_;
+ callback_ = rhs.callback_;
+ ps_ = std::move(rhs.ps_);
+ return *this;
+ }
+
+ int page_number() const { return page_number_; }
+ const PdfToPostScriptConverter::GetPageCallback& callback() const {
+ return callback_;
+ }
+ ScopedTempFile TakePostScript() { return std::move(ps_); }
+ void set_ps(ScopedTempFile ps) { ps_ = std::move(ps); }
+
+ private:
+ int page_number_;
+ PdfToPostScriptConverter::GetPageCallback callback_;
+ ScopedTempFile ps_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetPageCallbackData);
+ };
+
+ ~PdfToPostScriptUtilityProcessHostClient() override;
+
+ bool Send(IPC::Message* msg);
+
+ // Message handlers.
+ void OnPageCount(int page_count);
+ void OnPageDone(bool success);
+
+ void OnFailed();
+ void OnTempPdfReady(ScopedTempFile pdf);
+ void OnTempPostScriptReady(GetPageCallbackData* callback_data,
+ ScopedTempFile ps);
+
+ scoped_refptr<RefCountedTempDir> temp_dir_;
+
+ // Used to suppress callbacks after PdfToPostScriptConverterImpl is deleted.
+ base::WeakPtr<PdfToPostScriptConverterImpl> converter_;
+ PdfRenderSettings settings_;
+
+ // Document loaded callback.
+ PdfToPostScriptConverter::StartCallback start_callback_;
+
+ // Process host for IPC.
+ base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
+
+ // Queue of callbacks for GetPage() requests. Utility process should reply
+ // with PageDone in the same order as requests were received.
+ // Use containers that keeps element pointers valid after push() and pop().
+ using GetPageCallbacks = std::queue<GetPageCallbackData>;
+ GetPageCallbacks get_page_callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(PdfToPostScriptUtilityProcessHostClient);
+};
+
+class PdfToPostScriptConverterImpl : public PdfToPostScriptConverter {
+ public:
+ PdfToPostScriptConverterImpl();
+ ~PdfToPostScriptConverterImpl() override;
+
+ void Start(const scoped_refptr<base::RefCountedMemory>& data,
+ const PdfRenderSettings& conversion_settings,
+ const StartCallback& start_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);
+
+ private:
+ scoped_refptr<PdfToPostScriptUtilityProcessHostClient> utility_client_;
+ base::WeakPtrFactory<PdfToPostScriptConverterImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PdfToPostScriptConverterImpl);
+};
+
+PdfToPostScriptUtilityProcessHostClient::
+ PdfToPostScriptUtilityProcessHostClient(
+ base::WeakPtr<PdfToPostScriptConverterImpl> converter,
+ const PdfRenderSettings& settings)
+ : converter_(converter), settings_(settings) {}
+
+PdfToPostScriptUtilityProcessHostClient::
+ ~PdfToPostScriptUtilityProcessHostClient() {}
+
+void PdfToPostScriptUtilityProcessHostClient::Start(
+ const scoped_refptr<base::RefCountedMemory>& data,
+ const PdfToPostScriptConverter::StartCallback& start_callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&PdfToPostScriptUtilityProcessHostClient::Start, this, data,
+ start_callback));
+ return;
+ }
+ // 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::ThreadTaskRunnerHandle::Get())
+ ->AsWeakPtr();
+ utility_process_host_->SetName(
+ l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_PS_CONVERTOR_NAME));
+
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&CreateTempPdfFile, data, &temp_dir_),
+ base::Bind(&PdfToPostScriptUtilityProcessHostClient::OnTempPdfReady,
+ this));
+}
+
+void PdfToPostScriptUtilityProcessHostClient::OnTempPdfReady(
+ ScopedTempFile pdf) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!utility_process_host_ || !pdf)
+ return OnFailed();
+ // Should reply with OnPageCount().
+ Send(new ChromeUtilityMsg_RenderPDFPagesToPostScript_Start(
+ IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false),
+ settings_));
+}
+
+void PdfToPostScriptUtilityProcessHostClient::OnPageCount(int page_count) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (start_callback_.is_null())
+ return OnFailed();
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&PdfToPostScriptConverterImpl::RunCallback, converter_,
+ base::Bind(start_callback_, page_count)));
+ start_callback_.Reset();
+}
+
+void PdfToPostScriptUtilityProcessHostClient::GetPage(
+ int page_number,
+ const PdfToPostScriptConverter::GetPageCallback& get_page_callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&PdfToPostScriptUtilityProcessHostClient::GetPage, this,
+ page_number, get_page_callback));
+ return;
+ }
+ // Store callback before any OnFailed() call to make it called on failure.
+ get_page_callbacks_.push(GetPageCallbackData(page_number, get_page_callback));
+
+ if (!utility_process_host_)
+ return OnFailed();
+
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::FILE, FROM_HERE, base::Bind(&CreateTempFile, &temp_dir_),
+ base::Bind(
+ &PdfToPostScriptUtilityProcessHostClient::OnTempPostScriptReady, this,
+ &get_page_callbacks_.back()));
+}
+
+void PdfToPostScriptUtilityProcessHostClient::OnTempPostScriptReady(
+ GetPageCallbackData* callback_data,
+ ScopedTempFile ps) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!utility_process_host_ || !ps)
+ return OnFailed();
+ IPC::PlatformFileForTransit transit =
+ IPC::GetPlatformFileForTransit(ps->GetPlatformFile(), false);
+ callback_data->set_ps(std::move(ps));
+ // Should reply with OnPageDone().
+ Send(new ChromeUtilityMsg_RenderPDFPagesToPostScript_GetPage(
+ callback_data->page_number(), transit));
+}
+
+void PdfToPostScriptUtilityProcessHostClient::OnPageDone(bool success) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (get_page_callbacks_.empty())
+ return OnFailed();
+ GetPageCallbackData& data = get_page_callbacks_.front();
+ std::unique_ptr<PostScriptMetaFile> ps_metafile;
+
+ if (success) {
+ ScopedTempFile temp_ps = data.TakePostScript();
+ if (!temp_ps) // Unexpected message from utility process.
+ return OnFailed();
+ ps_metafile =
+ base::MakeUnique<PostScriptMetaFile>(temp_dir_, std::move(temp_ps));
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&PdfToPostScriptConverterImpl::RunCallback, converter_,
+ base::Bind(data.callback(), data.page_number(),
+ base::Passed(&ps_metafile))));
+ get_page_callbacks_.pop();
+}
+
+void PdfToPostScriptUtilityProcessHostClient::Stop() {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&PdfToPostScriptUtilityProcessHostClient::Stop, this));
+ return;
+ }
+ Send(new ChromeUtilityMsg_RenderPDFPagesToPostScript_Stop());
+}
+
+void PdfToPostScriptUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
+ OnFailed();
+}
+
+void PdfToPostScriptUtilityProcessHostClient::OnProcessLaunchFailed(
+ int exit_code) {
+ OnFailed();
+}
+
+bool PdfToPostScriptUtilityProcessHostClient::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(PdfToPostScriptUtilityProcessHostClient, message)
+ IPC_MESSAGE_HANDLER(
+ ChromeUtilityHostMsg_RenderPDFPagesToPostScript_PageCount, OnPageCount)
+ IPC_MESSAGE_HANDLER(
+ ChromeUtilityHostMsg_RenderPDFPagesToPostScript_PageDone, OnPageDone)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+bool PdfToPostScriptUtilityProcessHostClient::Send(IPC::Message* msg) {
+ if (utility_process_host_)
+ return utility_process_host_->Send(msg);
+ delete msg;
+ return false;
+}
+
+void PdfToPostScriptUtilityProcessHostClient::OnFailed() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!start_callback_.is_null())
+ OnPageCount(0);
+ while (!get_page_callbacks_.empty())
+ OnPageDone(false);
+ utility_process_host_.reset();
+}
+
+PdfToPostScriptConverterImpl::PdfToPostScriptConverterImpl()
+ : weak_ptr_factory_(this) {}
+
+PdfToPostScriptConverterImpl::~PdfToPostScriptConverterImpl() {
+ if (utility_client_.get())
+ utility_client_->Stop();
+}
+
+void PdfToPostScriptConverterImpl::Start(
+ const scoped_refptr<base::RefCountedMemory>& data,
+ const PdfRenderSettings& conversion_settings,
+ const StartCallback& start_callback) {
+ DCHECK(!utility_client_.get());
+ utility_client_ = new PdfToPostScriptUtilityProcessHostClient(
+ weak_ptr_factory_.GetWeakPtr(), conversion_settings);
+ utility_client_->Start(data, start_callback);
+}
+
+void PdfToPostScriptConverterImpl::GetPage(
+ int page_number,
+ const GetPageCallback& get_page_callback) {
+ utility_client_->GetPage(page_number, get_page_callback);
+}
+
+void PdfToPostScriptConverterImpl::RunCallback(const base::Closure& callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ callback.Run();
+}
+
+} // namespace
+
+PdfToEmfConverter::~PdfToEmfConverter() {}
+
// static
std::unique_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() {
- return std::unique_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl());
+ return base::MakeUnique<PdfToEmfConverterImpl>();
+}
+
+PdfToPostScriptConverter::~PdfToPostScriptConverter() {}
+
+// static
+std::unique_ptr<PdfToPostScriptConverter>
+PdfToPostScriptConverter::CreateDefault() {
+ return base::MakeUnique<PdfToPostScriptConverterImpl>();
}
} // namespace printing

Powered by Google App Engine
This is Rietveld 408576698