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.
4
5#include "chrome/utility/printing_handler.h"
6
7#include "base/files/file_util.h"
8#include "base/lazy_instance.h"
9#include "base/path_service.h"
10#include "base/scoped_native_library.h"
11#include "chrome/common/chrome_paths.h"
12#include "chrome/common/chrome_utility_printing_messages.h"
13#include "chrome/utility/cloud_print/bitmap_image.h"
14#include "chrome/utility/cloud_print/pwg_encoder.h"
15#include "content/public/utility/utility_thread.h"
16#include "printing/page_range.h"
17#include "printing/pdf_render_settings.h"
18
19#if defined(OS_WIN)
20#include "base/win/iat_patch_function.h"
21#include "printing/emf_win.h"
22#include "ui/gfx/gdi_util.h"
23#endif
24
25#if defined(ENABLE_FULL_PRINTING)
26#include "chrome/common/crash_keys.h"
27#include "printing/backend/print_backend.h"
28#endif
29
30namespace {
31
32bool Send(IPC::Message* message) {
33  return content::UtilityThread::Get()->Send(message);
34}
35
36void ReleaseProcessIfNeeded() {
37  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
38}
39
40class PdfFunctionsBase {
41 public:
42  PdfFunctionsBase() : render_pdf_to_bitmap_func_(NULL),
43                       get_pdf_doc_info_func_(NULL) {}
44
45  bool Init() {
46    base::FilePath pdf_module_path;
47    if (!PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_module_path) ||
48        !base::PathExists(pdf_module_path)) {
49      return false;
50    }
51
52    pdf_lib_.Reset(base::LoadNativeLibrary(pdf_module_path, NULL));
53    if (!pdf_lib_.is_valid()) {
54      LOG(WARNING) << "Couldn't load PDF plugin";
55      return false;
56    }
57
58    render_pdf_to_bitmap_func_ =
59        reinterpret_cast<RenderPDFPageToBitmapProc>(
60            pdf_lib_.GetFunctionPointer("RenderPDFPageToBitmap"));
61    LOG_IF(WARNING, !render_pdf_to_bitmap_func_) <<
62        "Missing RenderPDFPageToBitmap";
63
64    get_pdf_doc_info_func_ =
65        reinterpret_cast<GetPDFDocInfoProc>(
66            pdf_lib_.GetFunctionPointer("GetPDFDocInfo"));
67    LOG_IF(WARNING, !get_pdf_doc_info_func_) << "Missing GetPDFDocInfo";
68
69    if (!render_pdf_to_bitmap_func_ || !get_pdf_doc_info_func_ ||
70        !PlatformInit(pdf_module_path, pdf_lib_)) {
71      Reset();
72    }
73
74    return IsValid();
75  }
76
77  bool IsValid() const {
78    return pdf_lib_.is_valid();
79  }
80
81  void Reset() {
82    pdf_lib_.Reset(NULL);
83  }
84
85  bool RenderPDFPageToBitmap(const void* pdf_buffer,
86                             int pdf_buffer_size,
87                             int page_number,
88                             void* bitmap_buffer,
89                             int bitmap_width,
90                             int bitmap_height,
91                             int dpi_x,
92                             int dpi_y,
93                             bool autorotate) {
94    if (!render_pdf_to_bitmap_func_)
95      return false;
96    return render_pdf_to_bitmap_func_(pdf_buffer, pdf_buffer_size, page_number,
97                                      bitmap_buffer, bitmap_width,
98                                      bitmap_height, dpi_x, dpi_y, autorotate);
99  }
100
101  bool GetPDFDocInfo(const void* pdf_buffer,
102                     int buffer_size,
103                     int* page_count,
104                     double* max_page_width) {
105    if (!get_pdf_doc_info_func_)
106      return false;
107    return get_pdf_doc_info_func_(pdf_buffer, buffer_size, page_count,
108                                  max_page_width);
109  }
110
111 protected:
112  virtual bool PlatformInit(
113      const base::FilePath& pdf_module_path,
114      const base::ScopedNativeLibrary& pdf_lib) {
115    return true;
116  }
117
118 private:
119  // Exported by PDF plugin.
120  typedef bool (*RenderPDFPageToBitmapProc)(const void* pdf_buffer,
121                                            int pdf_buffer_size,
122                                            int page_number,
123                                            void* bitmap_buffer,
124                                            int bitmap_width,
125                                            int bitmap_height,
126                                            int dpi_x,
127                                            int dpi_y,
128                                            bool autorotate);
129  typedef bool (*GetPDFDocInfoProc)(const void* pdf_buffer,
130                                    int buffer_size, int* page_count,
131                                    double* max_page_width);
132
133  RenderPDFPageToBitmapProc render_pdf_to_bitmap_func_;
134  GetPDFDocInfoProc get_pdf_doc_info_func_;
135
136  base::ScopedNativeLibrary pdf_lib_;
137  DISALLOW_COPY_AND_ASSIGN(PdfFunctionsBase);
138};
139
140#if defined(OS_WIN)
141// The 2 below IAT patch functions are almost identical to the code in
142// render_process_impl.cc. This is needed to work around specific Windows APIs
143// used by the Chrome PDF plugin that will fail in the sandbox.
144static base::win::IATPatchFunction g_iat_patch_createdca;
145HDC WINAPI UtilityProcess_CreateDCAPatch(LPCSTR driver_name,
146                                         LPCSTR device_name,
147                                         LPCSTR output,
148                                         const DEVMODEA* init_data) {
149  if (driver_name && (std::string("DISPLAY") == driver_name)) {
150    // CreateDC fails behind the sandbox, but not CreateCompatibleDC.
151    return CreateCompatibleDC(NULL);
152  }
153
154  NOTREACHED();
155  return CreateDCA(driver_name, device_name, output, init_data);
156}
157
158static base::win::IATPatchFunction g_iat_patch_get_font_data;
159DWORD WINAPI UtilityProcess_GetFontDataPatch(
160    HDC hdc, DWORD table, DWORD offset, LPVOID buffer, DWORD length) {
161  int rv = GetFontData(hdc, table, offset, buffer, length);
162  if (rv == GDI_ERROR && hdc) {
163    HFONT font = static_cast<HFONT>(GetCurrentObject(hdc, OBJ_FONT));
164
165    LOGFONT logfont;
166    if (GetObject(font, sizeof(LOGFONT), &logfont)) {
167      content::UtilityThread::Get()->PreCacheFont(logfont);
168      rv = GetFontData(hdc, table, offset, buffer, length);
169      content::UtilityThread::Get()->ReleaseCachedFonts();
170    }
171  }
172  return rv;
173}
174
175class PdfFunctionsWin : public PdfFunctionsBase {
176 public:
177  PdfFunctionsWin() : render_pdf_to_dc_func_(NULL) {
178  }
179
180  bool PlatformInit(
181      const base::FilePath& pdf_module_path,
182      const base::ScopedNativeLibrary& pdf_lib) OVERRIDE {
183    // Patch the IAT for handling specific APIs known to fail in the sandbox.
184    if (!g_iat_patch_createdca.is_patched()) {
185      g_iat_patch_createdca.Patch(pdf_module_path.value().c_str(),
186                                  "gdi32.dll", "CreateDCA",
187                                  UtilityProcess_CreateDCAPatch);
188    }
189
190    if (!g_iat_patch_get_font_data.is_patched()) {
191      g_iat_patch_get_font_data.Patch(pdf_module_path.value().c_str(),
192                                      "gdi32.dll", "GetFontData",
193                                      UtilityProcess_GetFontDataPatch);
194    }
195    render_pdf_to_dc_func_ =
196      reinterpret_cast<RenderPDFPageToDCProc>(
197          pdf_lib.GetFunctionPointer("RenderPDFPageToDC"));
198    LOG_IF(WARNING, !render_pdf_to_dc_func_) << "Missing RenderPDFPageToDC";
199
200    return render_pdf_to_dc_func_ != NULL;
201  }
202
203  bool RenderPDFPageToDC(const void* pdf_buffer,
204                         int buffer_size,
205                         int page_number,
206                         HDC dc,
207                         int dpi_x,
208                         int dpi_y,
209                         int bounds_origin_x,
210                         int bounds_origin_y,
211                         int bounds_width,
212                         int bounds_height,
213                         bool fit_to_bounds,
214                         bool stretch_to_bounds,
215                         bool keep_aspect_ratio,
216                         bool center_in_bounds,
217                         bool autorotate) {
218    if (!render_pdf_to_dc_func_)
219      return false;
220    return render_pdf_to_dc_func_(pdf_buffer, buffer_size, page_number,
221                                  dc, dpi_x, dpi_y, bounds_origin_x,
222                                  bounds_origin_y, bounds_width, bounds_height,
223                                  fit_to_bounds, stretch_to_bounds,
224                                  keep_aspect_ratio, center_in_bounds,
225                                  autorotate);
226  }
227
228 private:
229  // Exported by PDF plugin.
230  typedef bool (*RenderPDFPageToDCProc)(
231      const void* pdf_buffer, int buffer_size, int page_number, HDC dc,
232      int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y,
233      int bounds_width, int bounds_height, bool fit_to_bounds,
234      bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds,
235      bool autorotate);
236  RenderPDFPageToDCProc render_pdf_to_dc_func_;
237
238  DISALLOW_COPY_AND_ASSIGN(PdfFunctionsWin);
239};
240
241typedef PdfFunctionsWin PdfFunctions;
242#else  // OS_WIN
243typedef PdfFunctionsBase PdfFunctions;
244#endif  // OS_WIN
245
246base::LazyInstance<PdfFunctions> g_pdf_lib = LAZY_INSTANCE_INITIALIZER;
247
248}  // namespace
249
250PrintingHandler::PrintingHandler() {}
251
252PrintingHandler::~PrintingHandler() {}
253
254// static
255void PrintingHandler::PreSandboxStartup() {
256  g_pdf_lib.Get().Init();
257}
258
259bool PrintingHandler::OnMessageReceived(const IPC::Message& message) {
260  bool handled = true;
261  IPC_BEGIN_MESSAGE_MAP(PrintingHandler, message)
262#if defined(OS_WIN)
263    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles,
264                        OnRenderPDFPagesToMetafile)
265    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage,
266                        OnRenderPDFPagesToMetafileGetPage)
267    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop,
268                        OnRenderPDFPagesToMetafileStop)
269#endif  // OS_WIN
270#if defined(ENABLE_FULL_PRINTING)
271    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToPWGRaster,
272                        OnRenderPDFPagesToPWGRaster)
273    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetPrinterCapsAndDefaults,
274                        OnGetPrinterCapsAndDefaults)
275    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults,
276                        OnGetPrinterSemanticCapsAndDefaults)
277#endif  // ENABLE_FULL_PRINTING
278    IPC_MESSAGE_UNHANDLED(handled = false)
279  IPC_END_MESSAGE_MAP()
280  return handled;
281}
282
283#if defined(OS_WIN)
284void PrintingHandler::OnRenderPDFPagesToMetafile(
285    IPC::PlatformFileForTransit pdf_transit,
286    const printing::PdfRenderSettings& settings) {
287  pdf_rendering_settings_ = settings;
288  base::File pdf_file = IPC::PlatformFileForTransitToFile(pdf_transit);
289  int page_count = LoadPDF(pdf_file.Pass());
290  Send(
291      new ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount(page_count));
292}
293
294void PrintingHandler::OnRenderPDFPagesToMetafileGetPage(
295    int page_number,
296    IPC::PlatformFileForTransit output_file) {
297  base::File emf_file = IPC::PlatformFileForTransitToFile(output_file);
298  double scale_factor = 1.0;
299  bool success =
300      RenderPdfPageToMetafile(page_number, emf_file.Pass(), &scale_factor);
301  Send(new ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone(
302      success, scale_factor));
303}
304
305void PrintingHandler::OnRenderPDFPagesToMetafileStop() {
306  ReleaseProcessIfNeeded();
307}
308
309#endif  // OS_WIN
310
311#if defined(ENABLE_FULL_PRINTING)
312void PrintingHandler::OnRenderPDFPagesToPWGRaster(
313    IPC::PlatformFileForTransit pdf_transit,
314    const printing::PdfRenderSettings& settings,
315    const printing::PwgRasterSettings& bitmap_settings,
316    IPC::PlatformFileForTransit bitmap_transit) {
317  base::File pdf = IPC::PlatformFileForTransitToFile(pdf_transit);
318  base::File bitmap = IPC::PlatformFileForTransitToFile(bitmap_transit);
319  if (RenderPDFPagesToPWGRaster(pdf.Pass(), settings, bitmap_settings,
320                                bitmap.Pass())) {
321    Send(new ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded());
322  } else {
323    Send(new ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed());
324  }
325  ReleaseProcessIfNeeded();
326}
327#endif  // ENABLE_FULL_PRINTING
328
329#if defined(OS_WIN)
330int PrintingHandler::LoadPDF(base::File pdf_file) {
331  if (!g_pdf_lib.Get().IsValid())
332    return 0;
333
334  int64 length = pdf_file.GetLength();
335  if (length < 0)
336    return 0;
337
338  pdf_data_.resize(length);
339  if (length != pdf_file.Read(0, pdf_data_.data(), pdf_data_.size()))
340    return 0;
341
342  int total_page_count = 0;
343  if (!g_pdf_lib.Get().GetPDFDocInfo(
344          &pdf_data_.front(), pdf_data_.size(), &total_page_count, NULL)) {
345    return 0;
346  }
347  return total_page_count;
348}
349
350bool PrintingHandler::RenderPdfPageToMetafile(int page_number,
351                                              base::File output_file,
352                                              double* scale_factor) {
353  printing::Emf metafile;
354  metafile.Init();
355
356  // We need to scale down DC to fit an entire page into DC available area.
357  // Current metafile is based on screen DC and have current screen size.
358  // Writing outside of those boundaries will result in the cut-off output.
359  // On metafiles (this is the case here), scaling down will still record
360  // original coordinates and we'll be able to print in full resolution.
361  // Before playback we'll need to counter the scaling up that will happen
362  // in the service (print_system_win.cc).
363  *scale_factor =
364      gfx::CalculatePageScale(metafile.context(),
365                              pdf_rendering_settings_.area().right(),
366                              pdf_rendering_settings_.area().bottom());
367  gfx::ScaleDC(metafile.context(), *scale_factor);
368
369  // The underlying metafile is of type Emf and ignores the arguments passed
370  // to StartPage.
371  metafile.StartPage(gfx::Size(), gfx::Rect(), 1);
372  if (!g_pdf_lib.Get().RenderPDFPageToDC(
373          &pdf_data_.front(),
374          pdf_data_.size(),
375          page_number,
376          metafile.context(),
377          pdf_rendering_settings_.dpi(),
378          pdf_rendering_settings_.dpi(),
379          pdf_rendering_settings_.area().x(),
380          pdf_rendering_settings_.area().y(),
381          pdf_rendering_settings_.area().width(),
382          pdf_rendering_settings_.area().height(),
383          true,
384          false,
385          true,
386          true,
387          pdf_rendering_settings_.autorotate())) {
388    return false;
389  }
390  metafile.FinishPage();
391  metafile.FinishDocument();
392  return metafile.SaveTo(&output_file);
393}
394
395#endif  // OS_WIN
396
397#if defined(ENABLE_FULL_PRINTING)
398bool PrintingHandler::RenderPDFPagesToPWGRaster(
399    base::File pdf_file,
400    const printing::PdfRenderSettings& settings,
401    const printing::PwgRasterSettings& bitmap_settings,
402    base::File bitmap_file) {
403  bool autoupdate = true;
404  if (!g_pdf_lib.Get().IsValid())
405    return false;
406
407  base::File::Info info;
408  if (!pdf_file.GetInfo(&info) || info.size <= 0)
409    return false;
410
411  std::string data(info.size, 0);
412  int data_size = pdf_file.Read(0, &data[0], data.size());
413  if (data_size != static_cast<int>(data.size()))
414    return false;
415
416  int total_page_count = 0;
417  if (!g_pdf_lib.Get().GetPDFDocInfo(data.data(), data.size(),
418                                     &total_page_count, NULL)) {
419    return false;
420  }
421
422  cloud_print::PwgEncoder encoder;
423  std::string pwg_header;
424  encoder.EncodeDocumentHeader(&pwg_header);
425  int bytes_written = bitmap_file.WriteAtCurrentPos(pwg_header.data(),
426                                                    pwg_header.size());
427  if (bytes_written != static_cast<int>(pwg_header.size()))
428    return false;
429
430  cloud_print::BitmapImage image(settings.area().size(),
431                                 cloud_print::BitmapImage::BGRA);
432  for (int i = 0; i < total_page_count; ++i) {
433    int page_number = i;
434
435    if (bitmap_settings.reverse_page_order) {
436      page_number = total_page_count - 1 - page_number;
437    }
438
439    if (!g_pdf_lib.Get().RenderPDFPageToBitmap(data.data(),
440                                               data.size(),
441                                               page_number,
442                                               image.pixel_data(),
443                                               image.size().width(),
444                                               image.size().height(),
445                                               settings.dpi(),
446                                               settings.dpi(),
447                                               autoupdate)) {
448      return false;
449    }
450
451    cloud_print::PwgHeaderInfo header_info;
452    header_info.dpi = settings.dpi();
453    header_info.total_pages = total_page_count;
454
455    // Transform odd pages.
456    if (page_number % 2) {
457      switch (bitmap_settings.odd_page_transform) {
458        case printing::TRANSFORM_NORMAL:
459          break;
460        case printing::TRANSFORM_ROTATE_180:
461          header_info.flipx = true;
462          header_info.flipy = true;
463          break;
464        case printing::TRANSFORM_FLIP_HORIZONTAL:
465          header_info.flipx = true;
466          break;
467        case printing::TRANSFORM_FLIP_VERTICAL:
468          header_info.flipy = true;
469          break;
470      }
471    }
472
473    if (bitmap_settings.rotate_all_pages) {
474      header_info.flipx = !header_info.flipx;
475      header_info.flipy = !header_info.flipy;
476    }
477
478    std::string pwg_page;
479    if (!encoder.EncodePage(image, header_info, &pwg_page))
480      return false;
481    bytes_written = bitmap_file.WriteAtCurrentPos(pwg_page.data(),
482                                                  pwg_page.size());
483    if (bytes_written != static_cast<int>(pwg_page.size()))
484      return false;
485  }
486  return true;
487}
488
489void PrintingHandler::OnGetPrinterCapsAndDefaults(
490    const std::string& printer_name) {
491  scoped_refptr<printing::PrintBackend> print_backend =
492      printing::PrintBackend::CreateInstance(NULL);
493  printing::PrinterCapsAndDefaults printer_info;
494
495  crash_keys::ScopedPrinterInfo crash_key(
496      print_backend->GetPrinterDriverInfo(printer_name));
497
498  if (print_backend->GetPrinterCapsAndDefaults(printer_name, &printer_info)) {
499    Send(new ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded(
500        printer_name, printer_info));
501  } else  {
502    Send(new ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed(
503        printer_name));
504  }
505  ReleaseProcessIfNeeded();
506}
507
508void PrintingHandler::OnGetPrinterSemanticCapsAndDefaults(
509    const std::string& printer_name) {
510  scoped_refptr<printing::PrintBackend> print_backend =
511      printing::PrintBackend::CreateInstance(NULL);
512  printing::PrinterSemanticCapsAndDefaults printer_info;
513
514  crash_keys::ScopedPrinterInfo crash_key(
515      print_backend->GetPrinterDriverInfo(printer_name));
516
517  if (print_backend->GetPrinterSemanticCapsAndDefaults(printer_name,
518                                                       &printer_info)) {
519    Send(new ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded(
520        printer_name, printer_info));
521  } else {
522    Send(new ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed(
523        printer_name));
524  }
525  ReleaseProcessIfNeeded();
526}
527#endif  // ENABLE_FULL_PRINTING
528