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/emf_win.h"
20 #include "printing/page_range.h"
21 #include "printing/pdf_render_settings.h"
27 using content::BrowserThread;
30 : public base::RefCountedThreadSafe<FileHandlers,
31 BrowserThread::DeleteOnFileThread> {
35 void Init(base::RefCountedMemory* data);
38 base::FilePath GetEmfPath() const {
39 return temp_dir_.path().AppendASCII("output.emf");
42 base::FilePath GetEmfPagePath(int page_number) const {
43 return GetEmfPath().InsertBeforeExtensionASCII(
44 base::StringPrintf(".%d", page_number));
47 base::FilePath GetPdfPath() const {
48 return temp_dir_.path().AppendASCII("input.pdf");
51 IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) {
52 DCHECK(pdf_file_.IsValid());
53 IPC::PlatformFileForTransit transit =
54 IPC::TakeFileHandleForProcess(pdf_file_.Pass(), process);
58 const base::FilePath& GetBasePath() const {
59 return temp_dir_.path();
63 friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>;
64 friend class base::DeleteHelper<FileHandlers>;
66 ~FileHandlers() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); }
68 base::ScopedTempDir temp_dir_;
72 void FileHandlers::Init(base::RefCountedMemory* data) {
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
75 if (!temp_dir_.CreateUniqueTempDir()) {
79 pdf_file_.Initialize(GetPdfPath(),
80 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
81 base::File::FLAG_READ |
82 base::File::FLAG_DELETE_ON_CLOSE);
83 if (static_cast<int>(data->size()) !=
84 pdf_file_.WriteAtCurrentPos(data->front_as<char>(), data->size())) {
88 pdf_file_.Seek(base::File::FROM_BEGIN, 0);
92 bool FileHandlers::IsValid() {
93 return pdf_file_.IsValid();
96 // Modification of Emf to keep references to |FileHandlers|.
97 // |FileHandlers| must be deleted after the last metafile is closed because
98 // Emf holds files locked.
99 // Ideally we want to use FLAG_DELETE_ON_CLOSE, but it requires large changes.
100 // It's going to be done for crbug.com/408184
101 class TempEmf : public Emf {
103 explicit TempEmf(const scoped_refptr<FileHandlers>& files) : files_(files) {}
104 virtual ~TempEmf() {}
106 virtual bool SafePlayback(HDC hdc) const OVERRIDE {
107 bool result = Emf::SafePlayback(hdc);
108 TempEmf* this_mutable = const_cast<TempEmf*>(this);
109 // TODO(vitalybuka): Fix destruction of metafiles. For some reasons
110 // instances of Emf are not deleted. crbug.com/411683
111 // |files_| must be released as soon as possible to guarantee deletion.
112 // It's know that this Emf file is going to be played just once to
113 // a printer. So just release files here.
114 this_mutable->Close();
115 this_mutable->files_ = NULL;
120 scoped_refptr<FileHandlers> files_;
121 DISALLOW_COPY_AND_ASSIGN(TempEmf);
124 // Converts PDF into EMF.
125 // Class uses 3 threads: UI, IO and FILE.
126 // Internal workflow is following:
127 // 1. Create instance on the UI thread. (files_, settings_,)
128 // 2. Create file on the FILE thread.
129 // 3. Start utility process and start conversion on the IO thread.
130 // 4. Run result callback on the UI thread.
131 // 5. Instance is destroyed from any thread that has the last reference.
132 // 6. FileHandlers destroyed on the FILE thread.
133 // This step posts |FileHandlers| to be destroyed on the FILE thread.
134 // All these steps work sequentially, so no data should be accessed
135 // simultaneously by several threads.
136 class PdfToEmfUtilityProcessHostClient
137 : public content::UtilityProcessHostClient {
139 explicit PdfToEmfUtilityProcessHostClient(
140 const printing::PdfRenderSettings& settings);
142 void Convert(base::RefCountedMemory* data,
143 const PdfToEmfConverter::ResultCallback& callback);
145 // UtilityProcessHostClient implementation.
146 virtual void OnProcessCrashed(int exit_code) OVERRIDE;
147 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
150 virtual ~PdfToEmfUtilityProcessHostClient();
153 void OnProcessStarted();
154 void OnSucceeded(const std::vector<printing::PageRange>& page_ranges,
155 double scale_factor);
158 void RunCallback(const std::vector<printing::PageRange>& page_ranges,
159 double scale_factor);
161 void StartProcessOnIOThread();
163 void RunCallbackOnUIThread(
164 const std::vector<printing::PageRange>& page_ranges,
165 double scale_factor);
166 void OnFilesReadyOnUIThread();
168 scoped_refptr<FileHandlers> files_;
169 printing::PdfRenderSettings settings_;
170 PdfToEmfConverter::ResultCallback callback_;
171 base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
173 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient);
176 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient(
177 const printing::PdfRenderSettings& settings)
178 : settings_(settings) {}
180 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
183 void PdfToEmfUtilityProcessHostClient::Convert(
184 base::RefCountedMemory* data,
185 const PdfToEmfConverter::ResultCallback& callback) {
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187 callback_ = callback;
188 CHECK(!files_.get());
189 files_ = new FileHandlers();
190 BrowserThread::PostTaskAndReply(
193 base::Bind(&FileHandlers::Init, files_, make_scoped_refptr(data)),
194 base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread,
198 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
202 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived(
203 const IPC::Message& message) {
205 IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message)
206 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
208 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, OnSucceeded)
209 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
211 IPC_MESSAGE_UNHANDLED(handled = false)
212 IPC_END_MESSAGE_MAP()
216 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
218 if (!utility_process_host_) {
219 RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
223 base::ProcessHandle process = utility_process_host_->GetData().handle;
224 utility_process_host_->Send(
225 new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
226 files_->GetPdfForProcess(process),
227 files_->GetEmfPath(),
229 std::vector<printing::PageRange>()));
230 utility_process_host_.reset();
233 void PdfToEmfUtilityProcessHostClient::OnSucceeded(
234 const std::vector<printing::PageRange>& page_ranges,
235 double scale_factor) {
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
237 RunCallback(page_ranges, scale_factor);
240 void PdfToEmfUtilityProcessHostClient::OnFailed() {
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
242 RunCallback(std::vector<printing::PageRange>(), 0.0);
245 void PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread() {
246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
247 if (!files_->IsValid()) {
248 RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
251 BrowserThread::PostTask(
254 base::Bind(&PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread,
258 void PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread() {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
260 utility_process_host_ =
261 content::UtilityProcessHost::Create(
263 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
264 // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load
265 // gdiplus.dll, change how rendering happens, and not be able to correctly
266 // generate when sent to a metafile DC.
267 utility_process_host_->SetExposedDir(files_->GetBasePath());
268 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
271 void PdfToEmfUtilityProcessHostClient::RunCallback(
272 const std::vector<printing::PageRange>& page_ranges,
273 double scale_factor) {
274 BrowserThread::PostTask(
277 base::Bind(&PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread,
283 void PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread(
284 const std::vector<printing::PageRange>& page_ranges,
285 double scale_factor) {
286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287 ScopedVector<Metafile> pages;
288 std::vector<printing::PageRange>::const_iterator iter;
289 for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) {
290 for (int page_number = iter->from; page_number <= iter->to; ++page_number) {
291 scoped_ptr<TempEmf> metafile(new TempEmf(files_));
292 if (!metafile->InitFromFile(files_->GetEmfPagePath(page_number))) {
293 NOTREACHED() << "Invalid metafile";
296 pages.push_back(metafile.release());
300 if (!callback_.is_null()) {
301 callback_.Run(scale_factor, &pages);
306 class PdfToEmfConverterImpl : public PdfToEmfConverter {
308 PdfToEmfConverterImpl();
310 virtual ~PdfToEmfConverterImpl();
312 virtual void Start(base::RefCountedMemory* data,
313 const printing::PdfRenderSettings& conversion_settings,
314 const ResultCallback& callback) OVERRIDE;
317 scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_;
318 base::CancelableCallback<ResultCallback::RunType> callback_;
320 DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl);
323 PdfToEmfConverterImpl::PdfToEmfConverterImpl() {
326 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
329 void PdfToEmfConverterImpl::Start(
330 base::RefCountedMemory* data,
331 const printing::PdfRenderSettings& conversion_settings,
332 const ResultCallback& callback) {
333 // Rebind cancelable callback to avoid calling callback if
334 // PdfToEmfConverterImpl is destroyed.
335 callback_.Reset(callback);
336 utility_client_ = new PdfToEmfUtilityProcessHostClient(conversion_settings);
337 utility_client_->Convert(data, callback_.callback());
343 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() {
344 return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl());
347 } // namespace printing