| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "chrome/browser/local_discovery/pwg_raster_converter.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/bind_helpers.h" | |
| 11 #include "base/cancelable_callback.h" | |
| 12 #include "base/files/file.h" | |
| 13 #include "base/files/file_util.h" | |
| 14 #include "base/files/scoped_temp_dir.h" | |
| 15 #include "base/location.h" | |
| 16 #include "base/logging.h" | |
| 17 #include "base/macros.h" | |
| 18 #include "base/single_thread_task_runner.h" | |
| 19 #include "chrome/common/chrome_utility_messages.h" | |
| 20 #include "chrome/common/chrome_utility_printing_messages.h" | |
| 21 #include "chrome/grit/generated_resources.h" | |
| 22 #include "components/cloud_devices/common/cloud_device_description.h" | |
| 23 #include "components/cloud_devices/common/printer_description.h" | |
| 24 #include "content/public/browser/browser_thread.h" | |
| 25 #include "content/public/browser/child_process_data.h" | |
| 26 #include "content/public/browser/utility_process_host.h" | |
| 27 #include "content/public/browser/utility_process_host_client.h" | |
| 28 #include "printing/pdf_render_settings.h" | |
| 29 #include "printing/pwg_raster_settings.h" | |
| 30 #include "printing/units.h" | |
| 31 #include "ui/base/l10n/l10n_util.h" | |
| 32 #include "ui/gfx/geometry/rect.h" | |
| 33 #include "ui/gfx/geometry/size.h" | |
| 34 | |
| 35 namespace local_discovery { | |
| 36 | |
| 37 namespace { | |
| 38 | |
| 39 using content::BrowserThread; | |
| 40 | |
| 41 class FileHandlers { | |
| 42 public: | |
| 43 FileHandlers() {} | |
| 44 | |
| 45 ~FileHandlers() { | |
| 46 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
| 47 } | |
| 48 | |
| 49 void Init(base::RefCountedMemory* data); | |
| 50 bool IsValid(); | |
| 51 | |
| 52 base::FilePath GetPwgPath() const { | |
| 53 return temp_dir_.path().AppendASCII("output.pwg"); | |
| 54 } | |
| 55 | |
| 56 base::FilePath GetPdfPath() const { | |
| 57 return temp_dir_.path().AppendASCII("input.pdf"); | |
| 58 } | |
| 59 | |
| 60 IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) { | |
| 61 DCHECK(pdf_file_.IsValid()); | |
| 62 IPC::PlatformFileForTransit transit = | |
| 63 IPC::TakeFileHandleForProcess(std::move(pdf_file_), process); | |
| 64 return transit; | |
| 65 } | |
| 66 | |
| 67 IPC::PlatformFileForTransit GetPwgForProcess(base::ProcessHandle process) { | |
| 68 DCHECK(pwg_file_.IsValid()); | |
| 69 IPC::PlatformFileForTransit transit = | |
| 70 IPC::TakeFileHandleForProcess(std::move(pwg_file_), process); | |
| 71 return transit; | |
| 72 } | |
| 73 | |
| 74 private: | |
| 75 base::ScopedTempDir temp_dir_; | |
| 76 base::File pdf_file_; | |
| 77 base::File pwg_file_; | |
| 78 }; | |
| 79 | |
| 80 void FileHandlers::Init(base::RefCountedMemory* data) { | |
| 81 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
| 82 | |
| 83 if (!temp_dir_.CreateUniqueTempDir()) { | |
| 84 return; | |
| 85 } | |
| 86 | |
| 87 if (static_cast<int>(data->size()) != | |
| 88 base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) { | |
| 89 return; | |
| 90 } | |
| 91 | |
| 92 // Reopen in read only mode. | |
| 93 pdf_file_.Initialize(GetPdfPath(), | |
| 94 base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 95 pwg_file_.Initialize(GetPwgPath(), | |
| 96 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | |
| 97 } | |
| 98 | |
| 99 bool FileHandlers::IsValid() { | |
| 100 return pdf_file_.IsValid() && pwg_file_.IsValid(); | |
| 101 } | |
| 102 | |
| 103 // Converts PDF into PWG raster. | |
| 104 // Class uses 3 threads: UI, IO and FILE. | |
| 105 // Internal workflow is following: | |
| 106 // 1. Create instance on the UI thread. (files_, settings_,) | |
| 107 // 2. Create file on the FILE thread. | |
| 108 // 3. Start utility process and start conversion on the IO thread. | |
| 109 // 4. Run result callback on the UI thread. | |
| 110 // 5. Instance is destroyed from any thread that has the last reference. | |
| 111 // 6. FileHandlers destroyed on the FILE thread. | |
| 112 // This step posts |FileHandlers| to be destroyed on the FILE thread. | |
| 113 // All these steps work sequentially, so no data should be accessed | |
| 114 // simultaneously by several threads. | |
| 115 class PwgUtilityProcessHostClient : public content::UtilityProcessHostClient { | |
| 116 public: | |
| 117 explicit PwgUtilityProcessHostClient( | |
| 118 const printing::PdfRenderSettings& settings, | |
| 119 const printing::PwgRasterSettings& bitmap_settings); | |
| 120 | |
| 121 void Convert(base::RefCountedMemory* data, | |
| 122 const PWGRasterConverter::ResultCallback& callback); | |
| 123 | |
| 124 // UtilityProcessHostClient implementation. | |
| 125 void OnProcessCrashed(int exit_code) override; | |
| 126 bool OnMessageReceived(const IPC::Message& message) override; | |
| 127 | |
| 128 private: | |
| 129 ~PwgUtilityProcessHostClient() override; | |
| 130 | |
| 131 // Message handlers. | |
| 132 void OnProcessStarted(); | |
| 133 void OnSucceeded(); | |
| 134 void OnFailed(); | |
| 135 | |
| 136 void RunCallback(bool success); | |
| 137 | |
| 138 void StartProcessOnIOThread(); | |
| 139 | |
| 140 void RunCallbackOnUIThread(bool success); | |
| 141 void OnFilesReadyOnUIThread(); | |
| 142 | |
| 143 scoped_ptr<FileHandlers, BrowserThread::DeleteOnFileThread> files_; | |
| 144 printing::PdfRenderSettings settings_; | |
| 145 printing::PwgRasterSettings bitmap_settings_; | |
| 146 PWGRasterConverter::ResultCallback callback_; | |
| 147 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; | |
| 148 | |
| 149 DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient); | |
| 150 }; | |
| 151 | |
| 152 PwgUtilityProcessHostClient::PwgUtilityProcessHostClient( | |
| 153 const printing::PdfRenderSettings& settings, | |
| 154 const printing::PwgRasterSettings& bitmap_settings) | |
| 155 : settings_(settings), bitmap_settings_(bitmap_settings) {} | |
| 156 | |
| 157 PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() { | |
| 158 } | |
| 159 | |
| 160 void PwgUtilityProcessHostClient::Convert( | |
| 161 base::RefCountedMemory* data, | |
| 162 const PWGRasterConverter::ResultCallback& callback) { | |
| 163 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 164 callback_ = callback; | |
| 165 CHECK(!files_); | |
| 166 files_.reset(new FileHandlers()); | |
| 167 BrowserThread::PostTaskAndReply( | |
| 168 BrowserThread::FILE, FROM_HERE, | |
| 169 base::Bind(&FileHandlers::Init, base::Unretained(files_.get()), | |
| 170 make_scoped_refptr(data)), | |
| 171 base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this)); | |
| 172 } | |
| 173 | |
| 174 void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) { | |
| 175 OnFailed(); | |
| 176 } | |
| 177 | |
| 178 bool PwgUtilityProcessHostClient::OnMessageReceived( | |
| 179 const IPC::Message& message) { | |
| 180 bool handled = true; | |
| 181 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message) | |
| 182 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) | |
| 183 IPC_MESSAGE_HANDLER( | |
| 184 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded) | |
| 185 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed, | |
| 186 OnFailed) | |
| 187 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 188 IPC_END_MESSAGE_MAP() | |
| 189 return handled; | |
| 190 } | |
| 191 | |
| 192 void PwgUtilityProcessHostClient::OnProcessStarted() { | |
| 193 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 194 if (!utility_process_host_) { | |
| 195 RunCallbackOnUIThread(false); | |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 base::ProcessHandle process = utility_process_host_->GetData().handle; | |
| 200 utility_process_host_->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster( | |
| 201 files_->GetPdfForProcess(process), | |
| 202 settings_, | |
| 203 bitmap_settings_, | |
| 204 files_->GetPwgForProcess(process))); | |
| 205 utility_process_host_.reset(); | |
| 206 } | |
| 207 | |
| 208 void PwgUtilityProcessHostClient::OnSucceeded() { | |
| 209 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 210 RunCallback(true); | |
| 211 } | |
| 212 | |
| 213 void PwgUtilityProcessHostClient::OnFailed() { | |
| 214 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 215 RunCallback(false); | |
| 216 } | |
| 217 | |
| 218 void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() { | |
| 219 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 220 if (!files_->IsValid()) { | |
| 221 RunCallbackOnUIThread(false); | |
| 222 return; | |
| 223 } | |
| 224 BrowserThread::PostTask( | |
| 225 BrowserThread::IO, FROM_HERE, | |
| 226 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this)); | |
| 227 } | |
| 228 | |
| 229 void PwgUtilityProcessHostClient::StartProcessOnIOThread() { | |
| 230 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 231 utility_process_host_ = | |
| 232 content::UtilityProcessHost::Create( | |
| 233 this, base::MessageLoop::current()->task_runner())->AsWeakPtr(); | |
| 234 utility_process_host_->SetName(l10n_util::GetStringUTF16( | |
| 235 IDS_UTILITY_PROCESS_PWG_RASTER_CONVERTOR_NAME)); | |
| 236 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); | |
| 237 } | |
| 238 | |
| 239 void PwgUtilityProcessHostClient::RunCallback(bool success) { | |
| 240 BrowserThread::PostTask( | |
| 241 BrowserThread::UI, FROM_HERE, | |
| 242 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this, | |
| 243 success)); | |
| 244 } | |
| 245 | |
| 246 void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) { | |
| 247 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 248 if (!callback_.is_null()) { | |
| 249 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 250 base::Bind(callback_, success, | |
| 251 files_->GetPwgPath())); | |
| 252 callback_.Reset(); | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 class PWGRasterConverterImpl : public PWGRasterConverter { | |
| 257 public: | |
| 258 PWGRasterConverterImpl(); | |
| 259 | |
| 260 ~PWGRasterConverterImpl() override; | |
| 261 | |
| 262 void Start(base::RefCountedMemory* data, | |
| 263 const printing::PdfRenderSettings& conversion_settings, | |
| 264 const printing::PwgRasterSettings& bitmap_settings, | |
| 265 const ResultCallback& callback) override; | |
| 266 | |
| 267 private: | |
| 268 scoped_refptr<PwgUtilityProcessHostClient> utility_client_; | |
| 269 base::CancelableCallback<ResultCallback::RunType> callback_; | |
| 270 | |
| 271 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl); | |
| 272 }; | |
| 273 | |
| 274 PWGRasterConverterImpl::PWGRasterConverterImpl() { | |
| 275 } | |
| 276 | |
| 277 PWGRasterConverterImpl::~PWGRasterConverterImpl() { | |
| 278 } | |
| 279 | |
| 280 void PWGRasterConverterImpl::Start( | |
| 281 base::RefCountedMemory* data, | |
| 282 const printing::PdfRenderSettings& conversion_settings, | |
| 283 const printing::PwgRasterSettings& bitmap_settings, | |
| 284 const ResultCallback& callback) { | |
| 285 // Rebind cancelable callback to avoid calling callback if | |
| 286 // PWGRasterConverterImpl is destroyed. | |
| 287 callback_.Reset(callback); | |
| 288 utility_client_ = | |
| 289 new PwgUtilityProcessHostClient(conversion_settings, bitmap_settings); | |
| 290 utility_client_->Convert(data, callback_.callback()); | |
| 291 } | |
| 292 | |
| 293 } // namespace | |
| 294 | |
| 295 // static | |
| 296 scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() { | |
| 297 return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl()); | |
| 298 } | |
| 299 | |
| 300 // static | |
| 301 printing::PdfRenderSettings PWGRasterConverter::GetConversionSettings( | |
| 302 const cloud_devices::CloudDeviceDescription& printer_capabilities, | |
| 303 const gfx::Size& page_size) { | |
| 304 int dpi = printing::kDefaultPdfDpi; | |
| 305 cloud_devices::printer::DpiCapability dpis; | |
| 306 if (dpis.LoadFrom(printer_capabilities)) | |
| 307 dpi = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical); | |
| 308 | |
| 309 double scale = dpi; | |
| 310 scale /= printing::kPointsPerInch; | |
| 311 | |
| 312 // Make vertical rectangle to optimize streaming to printer. Fix orientation | |
| 313 // by autorotate. | |
| 314 gfx::Rect area(std::min(page_size.width(), page_size.height()) * scale, | |
| 315 std::max(page_size.width(), page_size.height()) * scale); | |
| 316 return printing::PdfRenderSettings(area, dpi, true /* autorotate */); | |
| 317 } | |
| 318 | |
| 319 // static | |
| 320 printing::PwgRasterSettings PWGRasterConverter::GetBitmapSettings( | |
| 321 const cloud_devices::CloudDeviceDescription& printer_capabilities, | |
| 322 const cloud_devices::CloudDeviceDescription& ticket) { | |
| 323 printing::PwgRasterSettings result; | |
| 324 cloud_devices::printer::PwgRasterConfigCapability raster_capability; | |
| 325 // If the raster capability fails to load, raster_capability will contain | |
| 326 // the default value. | |
| 327 raster_capability.LoadFrom(printer_capabilities); | |
| 328 | |
| 329 cloud_devices::printer::DuplexTicketItem duplex_item; | |
| 330 cloud_devices::printer::DuplexType duplex_value = | |
| 331 cloud_devices::printer::NO_DUPLEX; | |
| 332 | |
| 333 cloud_devices::printer::DocumentSheetBack document_sheet_back = | |
| 334 raster_capability.value().document_sheet_back; | |
| 335 | |
| 336 if (duplex_item.LoadFrom(ticket)) { | |
| 337 duplex_value = duplex_item.value(); | |
| 338 } | |
| 339 | |
| 340 result.odd_page_transform = printing::TRANSFORM_NORMAL; | |
| 341 switch (duplex_value) { | |
| 342 case cloud_devices::printer::NO_DUPLEX: | |
| 343 result.odd_page_transform = printing::TRANSFORM_NORMAL; | |
| 344 break; | |
| 345 case cloud_devices::printer::LONG_EDGE: | |
| 346 if (document_sheet_back == cloud_devices::printer::ROTATED) { | |
| 347 result.odd_page_transform = printing::TRANSFORM_ROTATE_180; | |
| 348 } else if (document_sheet_back == cloud_devices::printer::FLIPPED) { | |
| 349 result.odd_page_transform = printing::TRANSFORM_FLIP_VERTICAL; | |
| 350 } | |
| 351 break; | |
| 352 case cloud_devices::printer::SHORT_EDGE: | |
| 353 if (document_sheet_back == cloud_devices::printer::MANUAL_TUMBLE) { | |
| 354 result.odd_page_transform = printing::TRANSFORM_ROTATE_180; | |
| 355 } else if (document_sheet_back == cloud_devices::printer::FLIPPED) { | |
| 356 result.odd_page_transform = printing::TRANSFORM_FLIP_HORIZONTAL; | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 result.rotate_all_pages = raster_capability.value().rotate_all_pages; | |
| 361 | |
| 362 result.reverse_page_order = raster_capability.value().reverse_order_streaming; | |
| 363 return result; | |
| 364 } | |
| 365 | |
| 366 } // namespace local_discovery | |
| OLD | NEW |