1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/printing/pdf_to_emf_converter.h"
7 #include "base/bind_helpers.h"
8 #include "base/cancelable_callback.h"
9 #include "base/file_util.h"
10 #include "base/files/file.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/logging.h"
13 #include "chrome/common/chrome_utility_messages.h"
14 #include "chrome/common/chrome_utility_printing_messages.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/child_process_data.h"
17 #include "content/public/browser/utility_process_host.h"
18 #include "content/public/browser/utility_process_host_client.h"
19 #include "printing/page_range.h"
20 #include "printing/pdf_render_settings.h"
26 using content::BrowserThread
;
33 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
36 void Init(base::RefCountedMemory
* data
);
39 base::FilePath
GetEmfPath() const {
40 return temp_dir_
.path().AppendASCII("output.emf");
43 base::FilePath
GetPdfPath() const {
44 return temp_dir_
.path().AppendASCII("input.pdf");
47 IPC::PlatformFileForTransit
GetPdfForProcess(base::ProcessHandle process
) {
48 DCHECK(pdf_file_
.IsValid());
49 IPC::PlatformFileForTransit transit
=
50 IPC::TakeFileHandleForProcess(pdf_file_
.Pass(), process
);
54 const base::FilePath
& GetBasePath() const {
55 return temp_dir_
.path();
59 base::ScopedTempDir temp_dir_
;
63 void FileHandlers::Init(base::RefCountedMemory
* data
) {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
66 if (!temp_dir_
.CreateUniqueTempDir()) {
70 if (static_cast<int>(data
->size()) !=
71 base::WriteFile(GetPdfPath(), data
->front_as
<char>(), data
->size())) {
75 // Reopen in read only mode.
76 pdf_file_
.Initialize(GetPdfPath(),
77 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
80 bool FileHandlers::IsValid() {
81 return pdf_file_
.IsValid();
84 // Converts PDF into EMF.
85 // Class uses 3 threads: UI, IO and FILE.
86 // Internal workflow is following:
87 // 1. Create instance on the UI thread. (files_, settings_,)
88 // 2. Create file on the FILE thread.
89 // 3. Start utility process and start conversion on the IO thread.
90 // 4. Run result callback on the UI thread.
91 // 5. Instance is destroyed from any thread that has the last reference.
92 // 6. FileHandlers destroyed on the FILE thread.
93 // This step posts |FileHandlers| to be destroyed on the FILE thread.
94 // All these steps work sequentially, so no data should be accessed
95 // simultaneously by several threads.
96 class PdfToEmfUtilityProcessHostClient
97 : public content::UtilityProcessHostClient
{
99 explicit PdfToEmfUtilityProcessHostClient(
100 const printing::PdfRenderSettings
& settings
);
102 void Convert(base::RefCountedMemory
* data
,
103 const PdfToEmfConverter::ResultCallback
& callback
);
105 // UtilityProcessHostClient implementation.
106 virtual void OnProcessCrashed(int exit_code
) OVERRIDE
;
107 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
;
110 virtual ~PdfToEmfUtilityProcessHostClient();
113 void OnProcessStarted();
114 void OnSucceeded(const std::vector
<printing::PageRange
>& page_ranges
,
115 double scale_factor
);
118 void RunCallback(const std::vector
<printing::PageRange
>& page_ranges
,
119 double scale_factor
);
121 void StartProcessOnIOThread();
123 void RunCallbackOnUIThread(
124 const std::vector
<printing::PageRange
>& page_ranges
,
125 double scale_factor
);
126 void OnFilesReadyOnUIThread();
128 scoped_ptr
<FileHandlers
> files_
;
129 printing::PdfRenderSettings settings_
;
130 PdfToEmfConverter::ResultCallback callback_
;
131 base::WeakPtr
<content::UtilityProcessHost
> utility_process_host_
;
133 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient
);
136 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient(
137 const printing::PdfRenderSettings
& settings
)
138 : settings_(settings
) {}
140 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
141 // Delete temp directory.
142 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE
, files_
.release());
145 void PdfToEmfUtilityProcessHostClient::Convert(
146 base::RefCountedMemory
* data
,
147 const PdfToEmfConverter::ResultCallback
& callback
) {
148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
149 callback_
= callback
;
151 files_
.reset(new FileHandlers());
152 BrowserThread::PostTaskAndReply(
155 base::Bind(&FileHandlers::Init
,
156 base::Unretained(files_
.get()),
157 make_scoped_refptr(data
)),
158 base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread
,
162 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code
) {
166 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived(
167 const IPC::Message
& message
) {
169 IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient
, message
)
170 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted
, OnProcessStarted
)
172 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded
, OnSucceeded
)
173 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed
,
175 IPC_MESSAGE_UNHANDLED(handled
= false)
176 IPC_END_MESSAGE_MAP()
180 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
182 if (!utility_process_host_
) {
183 RunCallbackOnUIThread(std::vector
<printing::PageRange
>(), 0.0);
187 base::ProcessHandle process
= utility_process_host_
->GetData().handle
;
188 utility_process_host_
->Send(
189 new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
190 files_
->GetPdfForProcess(process
),
191 files_
->GetEmfPath(),
193 std::vector
<printing::PageRange
>()));
194 utility_process_host_
.reset();
197 void PdfToEmfUtilityProcessHostClient::OnSucceeded(
198 const std::vector
<printing::PageRange
>& page_ranges
,
199 double scale_factor
) {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
201 RunCallback(page_ranges
, scale_factor
);
204 void PdfToEmfUtilityProcessHostClient::OnFailed() {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
206 RunCallback(std::vector
<printing::PageRange
>(), 0.0);
209 void PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread() {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
211 if (!files_
->IsValid()) {
212 RunCallbackOnUIThread(std::vector
<printing::PageRange
>(), 0.0);
215 BrowserThread::PostTask(
218 base::Bind(&PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread
,
222 void PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread() {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
224 utility_process_host_
=
225 content::UtilityProcessHost::Create(
227 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
228 // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load
229 // gdiplus.dll, change how rendering happens, and not be able to correctly
230 // generate when sent to a metafile DC.
231 utility_process_host_
->SetExposedDir(files_
->GetBasePath());
232 utility_process_host_
->Send(new ChromeUtilityMsg_StartupPing
);
235 void PdfToEmfUtilityProcessHostClient::RunCallback(
236 const std::vector
<printing::PageRange
>& page_ranges
,
237 double scale_factor
) {
238 BrowserThread::PostTask(
241 base::Bind(&PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread
,
247 void PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread(
248 const std::vector
<printing::PageRange
>& page_ranges
,
249 double scale_factor
) {
250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
251 std::vector
<base::FilePath
> page_filenames
;
252 std::vector
<printing::PageRange
>::const_iterator iter
;
253 for (iter
= page_ranges
.begin(); iter
!= page_ranges
.end(); ++iter
) {
254 for (int page_number
= iter
->from
; page_number
<= iter
->to
; ++page_number
) {
255 page_filenames
.push_back(files_
->GetEmfPath().InsertBeforeExtensionASCII(
256 base::StringPrintf(".%d", page_number
)));
259 if (!callback_
.is_null()) {
260 BrowserThread::PostTask(
263 base::Bind(callback_
, scale_factor
, page_filenames
));
268 class PdfToEmfConverterImpl
: public PdfToEmfConverter
{
270 PdfToEmfConverterImpl();
272 virtual ~PdfToEmfConverterImpl();
274 virtual void Start(base::RefCountedMemory
* data
,
275 const printing::PdfRenderSettings
& conversion_settings
,
276 const ResultCallback
& callback
) OVERRIDE
;
279 scoped_refptr
<PdfToEmfUtilityProcessHostClient
> utility_client_
;
280 base::CancelableCallback
<ResultCallback::RunType
> callback_
;
282 DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl
);
285 PdfToEmfConverterImpl::PdfToEmfConverterImpl() {
288 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
291 void PdfToEmfConverterImpl::Start(
292 base::RefCountedMemory
* data
,
293 const printing::PdfRenderSettings
& conversion_settings
,
294 const ResultCallback
& callback
) {
295 // Rebind cancelable callback to avoid calling callback if
296 // PdfToEmfConverterImpl is destroyed.
297 callback_
.Reset(callback
);
298 utility_client_
= new PdfToEmfUtilityProcessHostClient(conversion_settings
);
299 utility_client_
->Convert(data
, callback_
.callback());
305 scoped_ptr
<PdfToEmfConverter
> PdfToEmfConverter::CreateDefault() {
306 return scoped_ptr
<PdfToEmfConverter
>(new PdfToEmfConverterImpl());
309 } // namespace printing