1 // Copyright 2013 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/local_discovery/pwg_raster_converter.h"
9 #include "base/bind_helpers.h"
10 #include "base/cancelable_callback.h"
11 #include "base/files/file.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/logging.h"
15 #include "chrome/common/chrome_utility_messages.h"
16 #include "chrome/common/chrome_utility_printing_messages.h"
17 #include "chrome/grit/generated_resources.h"
18 #include "components/cloud_devices/common/cloud_device_description.h"
19 #include "components/cloud_devices/common/printer_description.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/child_process_data.h"
22 #include "content/public/browser/utility_process_host.h"
23 #include "content/public/browser/utility_process_host_client.h"
24 #include "printing/pdf_render_settings.h"
25 #include "printing/pwg_raster_settings.h"
26 #include "printing/units.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/gfx/geometry/rect.h"
29 #include "ui/gfx/geometry/size.h"
31 namespace local_discovery
{
35 using content::BrowserThread
;
42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
45 void Init(base::RefCountedMemory
* data
);
48 base::FilePath
GetPwgPath() const {
49 return temp_dir_
.path().AppendASCII("output.pwg");
52 base::FilePath
GetPdfPath() const {
53 return temp_dir_
.path().AppendASCII("input.pdf");
56 IPC::PlatformFileForTransit
GetPdfForProcess(base::ProcessHandle process
) {
57 DCHECK(pdf_file_
.IsValid());
58 IPC::PlatformFileForTransit transit
=
59 IPC::TakeFileHandleForProcess(pdf_file_
.Pass(), process
);
63 IPC::PlatformFileForTransit
GetPwgForProcess(base::ProcessHandle process
) {
64 DCHECK(pwg_file_
.IsValid());
65 IPC::PlatformFileForTransit transit
=
66 IPC::TakeFileHandleForProcess(pwg_file_
.Pass(), process
);
71 base::ScopedTempDir temp_dir_
;
76 void FileHandlers::Init(base::RefCountedMemory
* data
) {
77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
79 if (!temp_dir_
.CreateUniqueTempDir()) {
83 if (static_cast<int>(data
->size()) !=
84 base::WriteFile(GetPdfPath(), data
->front_as
<char>(), data
->size())) {
88 // Reopen in read only mode.
89 pdf_file_
.Initialize(GetPdfPath(),
90 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
91 pwg_file_
.Initialize(GetPwgPath(),
92 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
95 bool FileHandlers::IsValid() {
96 return pdf_file_
.IsValid() && pwg_file_
.IsValid();
99 // Converts PDF into PWG raster.
100 // Class uses 3 threads: UI, IO and FILE.
101 // Internal workflow is following:
102 // 1. Create instance on the UI thread. (files_, settings_,)
103 // 2. Create file on the FILE thread.
104 // 3. Start utility process and start conversion on the IO thread.
105 // 4. Run result callback on the UI thread.
106 // 5. Instance is destroyed from any thread that has the last reference.
107 // 6. FileHandlers destroyed on the FILE thread.
108 // This step posts |FileHandlers| to be destroyed on the FILE thread.
109 // All these steps work sequentially, so no data should be accessed
110 // simultaneously by several threads.
111 class PwgUtilityProcessHostClient
: public content::UtilityProcessHostClient
{
113 explicit PwgUtilityProcessHostClient(
114 const printing::PdfRenderSettings
& settings
,
115 const printing::PwgRasterSettings
& bitmap_settings
);
117 void Convert(base::RefCountedMemory
* data
,
118 const PWGRasterConverter::ResultCallback
& callback
);
120 // UtilityProcessHostClient implementation.
121 void OnProcessCrashed(int exit_code
) override
;
122 bool OnMessageReceived(const IPC::Message
& message
) override
;
125 ~PwgUtilityProcessHostClient() override
;
128 void OnProcessStarted();
132 void RunCallback(bool success
);
134 void StartProcessOnIOThread();
136 void RunCallbackOnUIThread(bool success
);
137 void OnFilesReadyOnUIThread();
139 scoped_ptr
<FileHandlers
, BrowserThread::DeleteOnFileThread
> files_
;
140 printing::PdfRenderSettings settings_
;
141 printing::PwgRasterSettings bitmap_settings_
;
142 PWGRasterConverter::ResultCallback callback_
;
143 base::WeakPtr
<content::UtilityProcessHost
> utility_process_host_
;
145 DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient
);
148 PwgUtilityProcessHostClient::PwgUtilityProcessHostClient(
149 const printing::PdfRenderSettings
& settings
,
150 const printing::PwgRasterSettings
& bitmap_settings
)
151 : settings_(settings
), bitmap_settings_(bitmap_settings
) {}
153 PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() {
156 void PwgUtilityProcessHostClient::Convert(
157 base::RefCountedMemory
* data
,
158 const PWGRasterConverter::ResultCallback
& callback
) {
159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
160 callback_
= callback
;
162 files_
.reset(new FileHandlers());
163 BrowserThread::PostTaskAndReply(
164 BrowserThread::FILE, FROM_HERE
,
165 base::Bind(&FileHandlers::Init
, base::Unretained(files_
.get()),
166 make_scoped_refptr(data
)),
167 base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread
, this));
170 void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code
) {
174 bool PwgUtilityProcessHostClient::OnMessageReceived(
175 const IPC::Message
& message
) {
177 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient
, message
)
178 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted
, OnProcessStarted
)
180 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded
, OnSucceeded
)
181 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed
,
183 IPC_MESSAGE_UNHANDLED(handled
= false)
184 IPC_END_MESSAGE_MAP()
188 void PwgUtilityProcessHostClient::OnProcessStarted() {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
190 if (!utility_process_host_
) {
191 RunCallbackOnUIThread(false);
195 base::ProcessHandle process
= utility_process_host_
->GetData().handle
;
196 utility_process_host_
->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
197 files_
->GetPdfForProcess(process
),
200 files_
->GetPwgForProcess(process
)));
201 utility_process_host_
.reset();
204 void PwgUtilityProcessHostClient::OnSucceeded() {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
209 void PwgUtilityProcessHostClient::OnFailed() {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
214 void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
216 if (!files_
->IsValid()) {
217 RunCallbackOnUIThread(false);
220 BrowserThread::PostTask(
221 BrowserThread::IO
, FROM_HERE
,
222 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread
, this));
225 void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
227 utility_process_host_
=
228 content::UtilityProcessHost::Create(
230 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
231 utility_process_host_
->SetName(l10n_util::GetStringUTF16(
232 IDS_UTILITY_PROCESS_PWG_RASTER_CONVERTOR_NAME
));
233 utility_process_host_
->Send(new ChromeUtilityMsg_StartupPing
);
236 void PwgUtilityProcessHostClient::RunCallback(bool success
) {
237 BrowserThread::PostTask(
238 BrowserThread::UI
, FROM_HERE
,
239 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread
, this,
243 void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success
) {
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
245 if (!callback_
.is_null()) {
246 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
247 base::Bind(callback_
, success
,
248 files_
->GetPwgPath()));
253 class PWGRasterConverterImpl
: public PWGRasterConverter
{
255 PWGRasterConverterImpl();
257 ~PWGRasterConverterImpl() override
;
259 void Start(base::RefCountedMemory
* data
,
260 const printing::PdfRenderSettings
& conversion_settings
,
261 const printing::PwgRasterSettings
& bitmap_settings
,
262 const ResultCallback
& callback
) override
;
265 scoped_refptr
<PwgUtilityProcessHostClient
> utility_client_
;
266 base::CancelableCallback
<ResultCallback::RunType
> callback_
;
268 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl
);
271 PWGRasterConverterImpl::PWGRasterConverterImpl() {
274 PWGRasterConverterImpl::~PWGRasterConverterImpl() {
277 void PWGRasterConverterImpl::Start(
278 base::RefCountedMemory
* data
,
279 const printing::PdfRenderSettings
& conversion_settings
,
280 const printing::PwgRasterSettings
& bitmap_settings
,
281 const ResultCallback
& callback
) {
282 // Rebind cancelable callback to avoid calling callback if
283 // PWGRasterConverterImpl is destroyed.
284 callback_
.Reset(callback
);
286 new PwgUtilityProcessHostClient(conversion_settings
, bitmap_settings
);
287 utility_client_
->Convert(data
, callback_
.callback());
293 scoped_ptr
<PWGRasterConverter
> PWGRasterConverter::CreateDefault() {
294 return scoped_ptr
<PWGRasterConverter
>(new PWGRasterConverterImpl());
298 printing::PdfRenderSettings
PWGRasterConverter::GetConversionSettings(
299 const cloud_devices::CloudDeviceDescription
& printer_capabilities
,
300 const gfx::Size
& page_size
) {
301 int dpi
= printing::kDefaultPdfDpi
;
302 cloud_devices::printer::DpiCapability dpis
;
303 if (dpis
.LoadFrom(printer_capabilities
))
304 dpi
= std::max(dpis
.GetDefault().horizontal
, dpis
.GetDefault().vertical
);
307 scale
/= printing::kPointsPerInch
;
309 // Make vertical rectangle to optimize streaming to printer. Fix orientation
311 gfx::Rect
area(std::min(page_size
.width(), page_size
.height()) * scale
,
312 std::max(page_size
.width(), page_size
.height()) * scale
);
313 return printing::PdfRenderSettings(area
, dpi
, true /* autorotate */);
317 printing::PwgRasterSettings
PWGRasterConverter::GetBitmapSettings(
318 const cloud_devices::CloudDeviceDescription
& printer_capabilities
,
319 const cloud_devices::CloudDeviceDescription
& ticket
) {
320 printing::PwgRasterSettings result
;
321 cloud_devices::printer::PwgRasterConfigCapability raster_capability
;
322 // If the raster capability fails to load, raster_capability will contain
323 // the default value.
324 raster_capability
.LoadFrom(printer_capabilities
);
326 cloud_devices::printer::DuplexTicketItem duplex_item
;
327 cloud_devices::printer::DuplexType duplex_value
=
328 cloud_devices::printer::NO_DUPLEX
;
330 cloud_devices::printer::DocumentSheetBack document_sheet_back
=
331 raster_capability
.value().document_sheet_back
;
333 if (duplex_item
.LoadFrom(ticket
)) {
334 duplex_value
= duplex_item
.value();
337 result
.odd_page_transform
= printing::TRANSFORM_NORMAL
;
338 switch (duplex_value
) {
339 case cloud_devices::printer::NO_DUPLEX
:
340 result
.odd_page_transform
= printing::TRANSFORM_NORMAL
;
342 case cloud_devices::printer::LONG_EDGE
:
343 if (document_sheet_back
== cloud_devices::printer::ROTATED
) {
344 result
.odd_page_transform
= printing::TRANSFORM_ROTATE_180
;
345 } else if (document_sheet_back
== cloud_devices::printer::FLIPPED
) {
346 result
.odd_page_transform
= printing::TRANSFORM_FLIP_VERTICAL
;
349 case cloud_devices::printer::SHORT_EDGE
:
350 if (document_sheet_back
== cloud_devices::printer::MANUAL_TUMBLE
) {
351 result
.odd_page_transform
= printing::TRANSFORM_ROTATE_180
;
352 } else if (document_sheet_back
== cloud_devices::printer::FLIPPED
) {
353 result
.odd_page_transform
= printing::TRANSFORM_FLIP_HORIZONTAL
;
357 result
.rotate_all_pages
= raster_capability
.value().rotate_all_pages
;
359 result
.reverse_page_order
= raster_capability
.value().reverse_order_streaming
;
363 } // namespace local_discovery