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 "content/public/browser/browser_thread.h"
15 #include "content/public/browser/child_process_data.h"
16 #include "content/public/browser/utility_process_host.h"
17 #include "content/public/browser/utility_process_host_client.h"
23 using content::BrowserThread
;
30 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
33 void Init(base::RefCountedMemory
* data
);
36 base::FilePath
GetEmfPath() const {
37 return temp_dir_
.path().AppendASCII("output.emf");
40 base::FilePath
GetPdfPath() const {
41 return temp_dir_
.path().AppendASCII("input.pdf");
44 IPC::PlatformFileForTransit
GetPdfForProcess(base::ProcessHandle process
) {
45 DCHECK(pdf_file_
.IsValid());
46 IPC::PlatformFileForTransit transit
=
47 IPC::TakeFileHandleForProcess(pdf_file_
.Pass(), process
);
51 const base::FilePath
& GetBasePath() const {
52 return temp_dir_
.path();
56 base::ScopedTempDir temp_dir_
;
60 void FileHandlers::Init(base::RefCountedMemory
* data
) {
61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
63 if (!temp_dir_
.CreateUniqueTempDir()) {
67 if (static_cast<int>(data
->size()) !=
68 base::WriteFile(GetPdfPath(), data
->front_as
<char>(), data
->size())) {
72 // Reopen in read only mode.
73 pdf_file_
.Initialize(GetPdfPath(),
74 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
77 bool FileHandlers::IsValid() {
78 return pdf_file_
.IsValid();
81 // Converts PDF into EMF.
82 // Class uses 3 threads: UI, IO and FILE.
83 // Internal workflow is following:
84 // 1. Create instance on the UI thread. (files_, settings_,)
85 // 2. Create file on the FILE thread.
86 // 3. Start utility process and start conversion on the IO thread.
87 // 4. Run result callback on the UI thread.
88 // 5. Instance is destroyed from any thread that has the last reference.
89 // 6. FileHandlers destroyed on the FILE thread.
90 // This step posts |FileHandlers| to be destroyed on the FILE thread.
91 // All these steps work sequentially, so no data should be accessed
92 // simultaneously by several threads.
93 class PdfToEmfUtilityProcessHostClient
94 : public content::UtilityProcessHostClient
{
96 explicit PdfToEmfUtilityProcessHostClient(
97 const printing::PdfRenderSettings
& settings
);
99 void Convert(base::RefCountedMemory
* data
,
100 const PdfToEmfConverter::ResultCallback
& callback
);
102 // UtilityProcessHostClient implementation.
103 virtual void OnProcessCrashed(int exit_code
) OVERRIDE
;
104 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
;
107 virtual ~PdfToEmfUtilityProcessHostClient();
110 void OnProcessStarted();
111 void OnSucceeded(const std::vector
<printing::PageRange
>& page_ranges
,
112 double scale_factor
);
115 void RunCallback(const std::vector
<printing::PageRange
>& page_ranges
,
116 double scale_factor
);
118 void StartProcessOnIOThread();
120 void RunCallbackOnUIThread(
121 const std::vector
<printing::PageRange
>& page_ranges
,
122 double scale_factor
);
123 void OnFilesReadyOnUIThread();
125 scoped_ptr
<FileHandlers
> files_
;
126 printing::PdfRenderSettings settings_
;
127 PdfToEmfConverter::ResultCallback callback_
;
128 base::WeakPtr
<content::UtilityProcessHost
> utility_process_host_
;
130 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient
);
133 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient(
134 const printing::PdfRenderSettings
& settings
)
135 : settings_(settings
) {}
137 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
138 // Delete temp directory.
139 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE
, files_
.release());
142 void PdfToEmfUtilityProcessHostClient::Convert(
143 base::RefCountedMemory
* data
,
144 const PdfToEmfConverter::ResultCallback
& callback
) {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
146 callback_
= callback
;
148 files_
.reset(new FileHandlers());
149 BrowserThread::PostTaskAndReply(
152 base::Bind(&FileHandlers::Init
,
153 base::Unretained(files_
.get()),
154 make_scoped_refptr(data
)),
155 base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread
,
159 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code
) {
163 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived(
164 const IPC::Message
& message
) {
166 IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient
, message
)
167 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted
, OnProcessStarted
)
169 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded
, OnSucceeded
)
170 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed
,
172 IPC_MESSAGE_UNHANDLED(handled
= false)
173 IPC_END_MESSAGE_MAP()
177 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
179 if (!utility_process_host_
) {
180 RunCallbackOnUIThread(std::vector
<printing::PageRange
>(), 0.0);
184 base::ProcessHandle process
= utility_process_host_
->GetData().handle
;
185 utility_process_host_
->Send(
186 new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
187 files_
->GetPdfForProcess(process
),
188 files_
->GetEmfPath(),
190 std::vector
<printing::PageRange
>()));
191 utility_process_host_
.reset();
194 void PdfToEmfUtilityProcessHostClient::OnSucceeded(
195 const std::vector
<printing::PageRange
>& page_ranges
,
196 double scale_factor
) {
197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
198 RunCallback(page_ranges
, scale_factor
);
201 void PdfToEmfUtilityProcessHostClient::OnFailed() {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
203 RunCallback(std::vector
<printing::PageRange
>(), 0.0);
206 void PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread() {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
208 if (!files_
->IsValid()) {
209 RunCallbackOnUIThread(std::vector
<printing::PageRange
>(), 0.0);
212 BrowserThread::PostTask(
215 base::Bind(&PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread
,
219 void PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread() {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
221 utility_process_host_
=
222 content::UtilityProcessHost::Create(
224 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
225 // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load
226 // gdiplus.dll, change how rendering happens, and not be able to correctly
227 // generate when sent to a metafile DC.
228 utility_process_host_
->SetExposedDir(files_
->GetBasePath());
229 utility_process_host_
->Send(new ChromeUtilityMsg_StartupPing
);
232 void PdfToEmfUtilityProcessHostClient::RunCallback(
233 const std::vector
<printing::PageRange
>& page_ranges
,
234 double scale_factor
) {
235 BrowserThread::PostTask(
238 base::Bind(&PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread
,
244 void PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread(
245 const std::vector
<printing::PageRange
>& page_ranges
,
246 double scale_factor
) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
248 std::vector
<base::FilePath
> page_filenames
;
249 std::vector
<printing::PageRange
>::const_iterator iter
;
250 for (iter
= page_ranges
.begin(); iter
!= page_ranges
.end(); ++iter
) {
251 for (int page_number
= iter
->from
; page_number
<= iter
->to
; ++page_number
) {
252 page_filenames
.push_back(files_
->GetEmfPath().InsertBeforeExtensionASCII(
253 base::StringPrintf(".%d", page_number
)));
256 if (!callback_
.is_null()) {
257 BrowserThread::PostTask(
260 base::Bind(callback_
, scale_factor
, page_filenames
));
265 class PdfToEmfConverterImpl
: public PdfToEmfConverter
{
267 PdfToEmfConverterImpl();
269 virtual ~PdfToEmfConverterImpl();
271 virtual void Start(base::RefCountedMemory
* data
,
272 const printing::PdfRenderSettings
& conversion_settings
,
273 const ResultCallback
& callback
) OVERRIDE
;
276 scoped_refptr
<PdfToEmfUtilityProcessHostClient
> utility_client_
;
277 base::CancelableCallback
<ResultCallback::RunType
> callback_
;
279 DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl
);
282 PdfToEmfConverterImpl::PdfToEmfConverterImpl() {
285 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
288 void PdfToEmfConverterImpl::Start(
289 base::RefCountedMemory
* data
,
290 const printing::PdfRenderSettings
& conversion_settings
,
291 const ResultCallback
& callback
) {
292 // Rebind cancelable callback to avoid calling callback if
293 // PdfToEmfConverterImpl is destroyed.
294 callback_
.Reset(callback
);
295 utility_client_
= new PdfToEmfUtilityProcessHostClient(conversion_settings
);
296 utility_client_
->Convert(data
, callback_
.callback());
302 scoped_ptr
<PdfToEmfConverter
> PdfToEmfConverter::CreateDefault() {
303 return scoped_ptr
<PdfToEmfConverter
>(new PdfToEmfConverterImpl());
306 } // namespace printing