pdf_conversion.cc 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Copyright 2021 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. #include "chromeos/utils/pdf_conversion.h"
  5. #include "base/files/file_path.h"
  6. #include "base/logging.h"
  7. #include "printing/units.h"
  8. #include "third_party/skia/include/core/SkCanvas.h"
  9. #include "third_party/skia/include/core/SkData.h"
  10. #include "third_party/skia/include/core/SkImage.h"
  11. #include "third_party/skia/include/core/SkRect.h"
  12. #include "third_party/skia/include/core/SkStream.h"
  13. #include "third_party/skia/include/core/SkTypes.h"
  14. #include "third_party/skia/include/docs/SkPDFDocument.h"
  15. #include "ui/gfx/image/buffer_w_stream.h"
  16. namespace chromeos {
  17. namespace {
  18. // The number of degrees to rotate a PDF image.
  19. constexpr int kRotationDegrees = 180;
  20. // Creates a new page for the PDF document and adds `image_data` to the page.
  21. // `rotate` indicates whether the page should be rotated 180 degrees.
  22. // Returns whether the page was successfully created.
  23. bool AddPdfPage(sk_sp<SkDocument> pdf_doc,
  24. const sk_sp<SkData>& image_data,
  25. bool rotate,
  26. absl::optional<int> dpi) {
  27. const sk_sp<SkImage> image = SkImage::MakeFromEncoded(image_data);
  28. if (!image) {
  29. LOG(ERROR) << "Unable to generate image from encoded image data.";
  30. return false;
  31. }
  32. // Convert from JPG dimensions in pixels (DPI) to PDF dimensions in points
  33. // (1/72 in).
  34. int page_width;
  35. int page_height;
  36. if (dpi.has_value() && dpi.value() > 0) {
  37. page_width = printing::ConvertUnit(image->width(), dpi.value(),
  38. printing::kPointsPerInch);
  39. page_height = printing::ConvertUnit(image->height(), dpi.value(),
  40. printing::kPointsPerInch);
  41. } else {
  42. page_width = image->width();
  43. page_height = image->height();
  44. }
  45. SkCanvas* page_canvas = pdf_doc->beginPage(page_width, page_height);
  46. if (!page_canvas) {
  47. LOG(ERROR) << "Unable to access PDF page canvas.";
  48. return false;
  49. }
  50. // Rotate pages that were flipped by an ADF scanner.
  51. if (rotate) {
  52. page_canvas->rotate(kRotationDegrees);
  53. page_canvas->translate(-page_width, -page_height);
  54. }
  55. SkRect image_bounds = SkRect::MakeIWH(page_width, page_height);
  56. page_canvas->drawImageRect(image, image_bounds, SkSamplingOptions());
  57. pdf_doc->endPage();
  58. return true;
  59. }
  60. } // namespace
  61. bool ConvertJpgImagesToPdf(const std::vector<std::string>& jpg_images,
  62. const base::FilePath& file_path,
  63. bool rotate_alternate_pages,
  64. absl::optional<int> dpi) {
  65. DCHECK(!file_path.empty());
  66. SkFILEWStream pdf_outfile(file_path.value().c_str());
  67. if (!pdf_outfile.isValid()) {
  68. LOG(ERROR) << "Unable to open output file.";
  69. return false;
  70. }
  71. sk_sp<SkDocument> pdf_doc = SkPDF::MakeDocument(&pdf_outfile);
  72. DCHECK(pdf_doc);
  73. // Never rotate first page of PDF.
  74. bool rotate_current_page = false;
  75. for (const auto& jpg_image : jpg_images) {
  76. SkDynamicMemoryWStream img_stream;
  77. if (!img_stream.write(jpg_image.c_str(), jpg_image.size())) {
  78. LOG(ERROR) << "Unable to write image to dynamic memory stream.";
  79. return false;
  80. }
  81. const sk_sp<SkData> img_data = img_stream.detachAsData();
  82. if (img_data->isEmpty()) {
  83. LOG(ERROR) << "Stream data is empty.";
  84. return false;
  85. }
  86. if (!AddPdfPage(pdf_doc, img_data, rotate_current_page, dpi)) {
  87. LOG(ERROR) << "Unable to add new PDF page.";
  88. return false;
  89. }
  90. if (rotate_alternate_pages) {
  91. rotate_current_page = !rotate_current_page;
  92. }
  93. }
  94. pdf_doc->close();
  95. return true;
  96. }
  97. bool ConvertJpgImageToPdf(const std::vector<uint8_t>& jpg_image,
  98. std::vector<uint8_t>* output) {
  99. gfx::BufferWStream output_stream;
  100. sk_sp<SkDocument> pdf_doc = SkPDF::MakeDocument(&output_stream);
  101. DCHECK(pdf_doc);
  102. SkDynamicMemoryWStream img_stream;
  103. if (!img_stream.write(jpg_image.data(), jpg_image.size())) {
  104. LOG(ERROR) << "Unable to write image to dynamic memory stream.";
  105. return false;
  106. }
  107. const sk_sp<SkData> img_data = img_stream.detachAsData();
  108. if (img_data->isEmpty()) {
  109. LOG(ERROR) << "Stream data is empty.";
  110. return false;
  111. }
  112. if (!AddPdfPage(pdf_doc, img_data, false, absl::nullopt)) {
  113. LOG(ERROR) << "Unable to add new PDF page.";
  114. return false;
  115. }
  116. pdf_doc->close();
  117. *output = output_stream.TakeBuffer();
  118. return true;
  119. }
  120. } // namespace chromeos