diff options
author | thestig <thestig@chromium.org> | 2015-10-28 12:04:26 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-28 19:05:46 +0000 |
commit | 64c8e26697caef0ef26cf604da6747408efd7526 (patch) | |
tree | d261b8ec6be915f19c0ed6cb2c6cacea4e6f8a49 /printing | |
parent | 6e5ffd8ef4270016defc11948338eba827dc2acf (diff) | |
download | chromium_src-64c8e26697caef0ef26cf604da6747408efd7526.zip chromium_src-64c8e26697caef0ef26cf604da6747408efd7526.tar.gz chromium_src-64c8e26697caef0ef26cf604da6747408efd7526.tar.bz2 |
Printing: Move some printing code out of pdf/ and into printing/.
This code was stuck in pdf/ previously because the PDF plugin was
proprietary.
Add unit tests now that the code has been moved out.
Review URL: https://codereview.chromium.org/1421493002
Cr-Commit-Position: refs/heads/master@{#356600}
Diffstat (limited to 'printing')
-rw-r--r-- | printing/BUILD.gn | 19 | ||||
-rw-r--r-- | printing/pdf_transform.cc | 123 | ||||
-rw-r--r-- | printing/pdf_transform.h | 104 | ||||
-rw-r--r-- | printing/pdf_transform_unittest.cc | 282 | ||||
-rw-r--r-- | printing/printing.gyp | 8 |
5 files changed, 531 insertions, 5 deletions
diff --git a/printing/BUILD.gn b/printing/BUILD.gn index ca35327..d8255be 100644 --- a/printing/BUILD.gn +++ b/printing/BUILD.gn @@ -184,6 +184,11 @@ component("printing") { ] deps += [ ":printing_jni_headers" ] + } else { + sources += [ + "pdf_transform.cc", + "pdf_transform.h", + ] } } @@ -212,11 +217,6 @@ test("printing_unittests") { configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] - if (use_cups) { - configs += [ ":cups" ] - sources += [ "backend/cups_helper_unittest.cc" ] - } - deps = [ ":printing", "//base/allocator", @@ -228,6 +228,15 @@ test("printing_unittests") { "//ui/gfx:test_support", "//ui/gfx/geometry", ] + + if (!is_android) { + sources += [ "pdf_transform_unittest.cc" ] + } + + if (use_cups) { + configs += [ ":cups" ] + sources += [ "backend/cups_helper_unittest.cc" ] + } } if (use_cups) { diff --git a/printing/pdf_transform.cc b/printing/pdf_transform.cc new file mode 100644 index 0000000..f13a639 --- /dev/null +++ b/printing/pdf_transform.cc @@ -0,0 +1,123 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "printing/pdf_transform.h" + +#include <algorithm> + +#include "base/logging.h" +#include "ui/gfx/geometry/rect.h" + +namespace printing { + +double CalculateScaleFactor(const gfx::Rect& content_rect, + double src_width, + double src_height, + bool rotated) { + if (src_width == 0 || src_height == 0) + return 1.0; + + double actual_source_page_width = rotated ? src_height : src_width; + double actual_source_page_height = rotated ? src_width : src_height; + double ratio_x = static_cast<double>(content_rect.width()) / + actual_source_page_width; + double ratio_y = static_cast<double>(content_rect.height()) / + actual_source_page_height; + return std::min(ratio_x, ratio_y); +} + +void SetDefaultClipBox(bool rotated, ClipBox* clip_box) { + const int kDpi = 72; + const float kPaperWidth = 8.5 * kDpi; + const float kPaperHeight = 11 * kDpi; + clip_box->left = 0; + clip_box->bottom = 0; + clip_box->right = rotated ? kPaperHeight : kPaperWidth; + clip_box->top = rotated ? kPaperWidth : kPaperHeight; +} + +void CalculateMediaBoxAndCropBox(bool rotated, + bool has_media_box, + bool has_crop_box, + printing::ClipBox* media_box, + printing::ClipBox* crop_box) { + if (!has_media_box && !has_crop_box) { + SetDefaultClipBox(rotated, crop_box); + SetDefaultClipBox(rotated, media_box); + } else if (has_crop_box && !has_media_box) { + *media_box = *crop_box; + } else if (has_media_box && !has_crop_box) { + *crop_box = *media_box; + } +} + +ClipBox CalculateClipBoxBoundary(const ClipBox& media_box, + const ClipBox& crop_box) { + ClipBox clip_box; + + // Clip |media_box| to the size of |crop_box|, but ignore |crop_box| if it is + // bigger than |media_box|. + clip_box.left = + (crop_box.left < media_box.left) ? media_box.left : crop_box.left; + clip_box.right = + (crop_box.right > media_box.right) ? media_box.right : crop_box.right; + clip_box.top = (crop_box.top > media_box.top) ? media_box.top : crop_box.top; + clip_box.bottom = + (crop_box.bottom < media_box.bottom) ? media_box.bottom : crop_box.bottom; + return clip_box; +} + +void ScaleClipBox(double scale_factor, ClipBox* box) { + box->left *= scale_factor; + box->right *= scale_factor; + box->bottom *= scale_factor; + box->top *= scale_factor; +} + +void CalculateScaledClipBoxOffset(const gfx::Rect& content_rect, + const ClipBox& source_clip_box, + double* offset_x, + double* offset_y) { + const float clip_box_width = source_clip_box.right - source_clip_box.left; + const float clip_box_height = source_clip_box.top - source_clip_box.bottom; + + // Center the intended clip region to real clip region. + *offset_x = (content_rect.width() - clip_box_width) / 2 + content_rect.x() - + source_clip_box.left; + *offset_y = (content_rect.height() - clip_box_height) / 2 + content_rect.y() - + source_clip_box.bottom; +} + +void CalculateNonScaledClipBoxOffset(const gfx::Rect& content_rect, + int rotation, + int page_width, + int page_height, + const ClipBox& source_clip_box, + double* offset_x, + double* offset_y) { + // Align the intended clip region to left-top corner of real clip region. + switch (rotation) { + case 0: + *offset_x = -1 * source_clip_box.left; + *offset_y = page_height - source_clip_box.top; + break; + case 1: + *offset_x = 0; + *offset_y = -1 * source_clip_box.bottom; + break; + case 2: + *offset_x = page_width - source_clip_box.right; + *offset_y = 0; + break; + case 3: + *offset_x = page_height - source_clip_box.right; + *offset_y = page_width - source_clip_box.top; + break; + default: + NOTREACHED(); + break; + } +} + +} // namespace printing diff --git a/printing/pdf_transform.h b/printing/pdf_transform.h new file mode 100644 index 0000000..2cc244b --- /dev/null +++ b/printing/pdf_transform.h @@ -0,0 +1,104 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PRINTING_PDF_TRANSFORM_H_ +#define PRINTING_PDF_TRANSFORM_H_ + +#include "printing/printing_export.h" + +namespace gfx { +class Rect; +} + +namespace printing { + +// A rect struct for use with FPDF bounding box functions. +// With PDFs, origin is bottom-left. +struct PRINTING_EXPORT ClipBox { + float left; + float right; + float top; + float bottom; +}; + +// Calculate the scale factor between |content_rect| and a page of size +// |src_width| x |src_height|. +// +// |content_rect| specifies the printable area of the destination page, with +// origin at left-bottom. Values are in points. +// |src_width| specifies the source page width in points. +// |src_height| specifies the source page height in points. +// |rotated| True if source page is rotated 90 degree or 270 degree. +PRINTING_EXPORT double CalculateScaleFactor(const gfx::Rect& content_rect, + double src_width, + double src_height, + bool rotated); + +// Make the default size to be letter size (8.5" X 11"). We are just following +// the PDFium way of handling these corner cases. PDFium always consider +// US-Letter as the default page size. +PRINTING_EXPORT void SetDefaultClipBox(bool rotated, ClipBox* clip_box); + +// Set the media box and/or crop box as needed. If both boxes are there, then +// nothing needs to be done. If one box is missing, then fill it with the value +// from the other box. If both boxes are missing, then they both get the default +// value from SetDefaultClipBox(), based on |rotated|. +PRINTING_EXPORT void CalculateMediaBoxAndCropBox(bool rotated, + bool has_media_box, + bool has_crop_box, + printing::ClipBox* media_box, + printing::ClipBox* crop_box); + +// Compute source clip box boundaries based on the crop box / media box of +// source page and scale factor. +// Returns the computed source clip box values. +// +// |media_box| The PDF's media box. +// |crop_box| The PDF's crop box. +PRINTING_EXPORT ClipBox CalculateClipBoxBoundary(const ClipBox& media_box, + const ClipBox& crop_box); + +// Scale |box| by |scale_factor|. +PRINTING_EXPORT void ScaleClipBox(double scale_factor, ClipBox* box); + +// Calculate the clip box translation offset for a page that does need to be +// scaled. All parameters are in points. +// +// |content_rect| specifies the printable area of the destination page, with +// origin at left-bottom. +// |source_clip_box| specifies the source clip box positions, relative to +// origin at left-bottom. +// |offset_x| and |offset_y| will contain the final translation offsets for the +// source clip box, relative to origin at left-bottom. +PRINTING_EXPORT void CalculateScaledClipBoxOffset( + const gfx::Rect& content_rect, + const ClipBox& source_clip_box, + double* offset_x, + double* offset_y); + +// Calculate the clip box offset for a page that does not need to be scaled. +// All parameters are in points. +// +// |content_rect| specifies the printable area of the destination page, with +// origin at left-bottom. +// |rotation| specifies the source page rotation values which are N / 90 +// degrees. +// |page_width| specifies the screen destination page width. +// |page_height| specifies the screen destination page height. +// |source_clip_box| specifies the source clip box positions, relative to origin +// at left-bottom. +// |offset_x| and |offset_y| will contain the final translation offsets for the +// source clip box, relative to origin at left-bottom. +PRINTING_EXPORT void CalculateNonScaledClipBoxOffset( + const gfx::Rect& content_rect, + int rotation, + int page_width, + int page_height, + const ClipBox& source_clip_box, + double* offset_x, + double* offset_y); + +} // namespace printing + +#endif // PRINTING_PDF_TRANSFORM_H_ diff --git a/printing/pdf_transform_unittest.cc b/printing/pdf_transform_unittest.cc new file mode 100644 index 0000000..3c91c78 --- /dev/null +++ b/printing/pdf_transform_unittest.cc @@ -0,0 +1,282 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "printing/pdf_transform.h" + +#include "printing/units.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/rect.h" + +namespace printing { + +namespace { + +const float kDefaultWidth = 8.5 * kPointsPerInch; +const float kDefaultHeight = 11.0 * kPointsPerInch; +const float kDefaultRatio = kDefaultWidth / kDefaultHeight; +const double kTolerance = 0.0001; + +void ExpectDefaultPortraitBox(const ClipBox& box) { + EXPECT_FLOAT_EQ(0, box.left); + EXPECT_FLOAT_EQ(kDefaultWidth, box.right); + EXPECT_FLOAT_EQ(kDefaultHeight, box.top); + EXPECT_FLOAT_EQ(0, box.bottom); +} + +void ExpectDefaultLandscapeBox(const ClipBox& box) { + EXPECT_FLOAT_EQ(0, box.left); + EXPECT_FLOAT_EQ(kDefaultHeight, box.right); + EXPECT_FLOAT_EQ(kDefaultWidth, box.top); + EXPECT_FLOAT_EQ(0, box.bottom); +} + +void ExpectBoxesAreEqual(const ClipBox& expected, const ClipBox& actual) { + EXPECT_FLOAT_EQ(expected.left, actual.left); + EXPECT_FLOAT_EQ(expected.right, actual.right); + EXPECT_FLOAT_EQ(expected.top, actual.top); + EXPECT_FLOAT_EQ(expected.bottom, actual.bottom); +} + +void InitializeBoxToInvalidValues(ClipBox* box) { + box->left = box->right = box->top = box->bottom = -1; +} + +void InitializeBoxToDefaultPortraitValues(ClipBox* box) { + box->left = 0; + box->right = kDefaultWidth; + box->top = kDefaultHeight; + box->bottom = 0; +} + +void InitializeBoxToDefaultLandscapeValue(ClipBox* box) { + box->left = 0; + box->right = kDefaultHeight; + box->top = kDefaultWidth; + box->bottom = 0; +} + +} // namespace + +TEST(PdfTransformTest, CalculateScaleFactor) { + gfx::Rect rect(kDefaultWidth, kDefaultHeight); + double scale; + + // 1:1 + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, false); + EXPECT_NEAR(1, scale, kTolerance); + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, true); + EXPECT_NEAR(kDefaultRatio, scale, kTolerance); + + // 1:2 + rect = gfx::Rect(kDefaultWidth / 2, kDefaultHeight / 2); + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, false); + EXPECT_NEAR(0.5, scale, kTolerance); + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, true); + EXPECT_NEAR(kDefaultRatio / 2, scale, kTolerance); + + // 3:1 + rect = gfx::Rect(kDefaultWidth * 3, kDefaultHeight * 3); + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, false); + EXPECT_NEAR(3, scale, kTolerance); + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, true); + EXPECT_NEAR(kDefaultRatio * 3, scale, kTolerance); + + // 3:1, rotated. + rect = gfx::Rect(kDefaultHeight * 3, kDefaultWidth * 3); + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, false); + EXPECT_NEAR(kDefaultRatio * 3, scale, kTolerance); + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, true); + EXPECT_NEAR(3, scale, kTolerance); + + // Odd size + rect = gfx::Rect(10, 1000); + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, false); + EXPECT_NEAR(0.01634, scale, kTolerance); + scale = CalculateScaleFactor(rect, kDefaultWidth, kDefaultHeight, true); + EXPECT_NEAR(0.01263, scale, kTolerance); +} + +TEST(PdfTransformTest, SetDefaultClipBox) { + ClipBox box; + + SetDefaultClipBox(false, &box); + ExpectDefaultPortraitBox(box); + + SetDefaultClipBox(true, &box); + ExpectDefaultLandscapeBox(box); +} + +TEST(PdfTransformTest, CalculateMediaBoxAndCropBox) { + ClipBox media_box; + ClipBox crop_box; + + // Assume both boxes are there. + InitializeBoxToDefaultPortraitValues(&media_box); + InitializeBoxToDefaultLandscapeValue(&crop_box); + CalculateMediaBoxAndCropBox(true, true, true, &media_box, &crop_box); + ExpectDefaultPortraitBox(media_box); + ExpectDefaultLandscapeBox(crop_box); + + // Assume both boxes are missing. + InitializeBoxToInvalidValues(&media_box); + InitializeBoxToInvalidValues(&crop_box); + CalculateMediaBoxAndCropBox(false, false, false, &media_box, &crop_box); + ExpectDefaultPortraitBox(media_box); + ExpectDefaultPortraitBox(crop_box); + CalculateMediaBoxAndCropBox(true, false, false, &media_box, &crop_box); + ExpectDefaultLandscapeBox(media_box); + ExpectDefaultLandscapeBox(crop_box); + + // Assume crop box is missing. + ClipBox expected_box; + expected_box.left = 0; + expected_box.right = 42; + expected_box.top = 420; + expected_box.bottom = 0; + media_box = expected_box; + InitializeBoxToInvalidValues(&crop_box); + CalculateMediaBoxAndCropBox(false, true, false, &media_box, &crop_box); + ExpectBoxesAreEqual(expected_box, media_box); + ExpectBoxesAreEqual(expected_box, crop_box); + + // Assume media box is missing. + InitializeBoxToInvalidValues(&media_box); + CalculateMediaBoxAndCropBox(false, false, true, &media_box, &crop_box); + ExpectBoxesAreEqual(expected_box, media_box); + ExpectBoxesAreEqual(expected_box, crop_box); +} + +TEST(PdfTransformTest, CalculateClipBoxBoundary) { + ClipBox media_box; + ClipBox crop_box; + ClipBox result; + + // media box and crop box are the same. + InitializeBoxToDefaultPortraitValues(&media_box); + InitializeBoxToDefaultPortraitValues(&crop_box); + result = CalculateClipBoxBoundary(media_box, crop_box); + ExpectDefaultPortraitBox(result); + + // media box is portrait and crop box is landscape. + InitializeBoxToDefaultLandscapeValue(&crop_box); + result = CalculateClipBoxBoundary(media_box, crop_box); + EXPECT_FLOAT_EQ(0, result.left); + EXPECT_FLOAT_EQ(kDefaultWidth, result.right); + EXPECT_FLOAT_EQ(kDefaultWidth, result.top); + EXPECT_FLOAT_EQ(0, result.bottom); + + // crop box is smaller than media box. + crop_box.left = 0; + crop_box.right = 100; + crop_box.bottom = 0; + crop_box.top = 200; + result = CalculateClipBoxBoundary(media_box, crop_box); + EXPECT_FLOAT_EQ(0, result.left); + EXPECT_FLOAT_EQ(100, result.right); + EXPECT_FLOAT_EQ(200, result.top); + EXPECT_FLOAT_EQ(0, result.bottom); + + // crop box is smaller than the media box in one dimension and longer in the + // other. + crop_box.left = 0; + crop_box.right = 100; + crop_box.bottom = 0; + crop_box.top = 2000; + result = CalculateClipBoxBoundary(media_box, crop_box); + EXPECT_FLOAT_EQ(0, result.left); + EXPECT_FLOAT_EQ(100, result.right); + EXPECT_FLOAT_EQ(kDefaultHeight, result.top); + EXPECT_FLOAT_EQ(0, result.bottom); +} + +TEST(PdfTransformTest, CalculateScaledClipBoxOffset) { + gfx::Rect rect(kDefaultWidth, kDefaultHeight); + ClipBox clip_box; + double offset_x; + double offset_y; + + // |rect| and |clip_box| are the same size. + InitializeBoxToDefaultPortraitValues(&clip_box); + CalculateScaledClipBoxOffset(rect, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(0, offset_x); + EXPECT_DOUBLE_EQ(0, offset_y); + + // |rect| is larger than |clip_box|. + clip_box.top /= 2; + clip_box.right /= 4; + CalculateScaledClipBoxOffset(rect, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(229.5, offset_x); + EXPECT_DOUBLE_EQ(198, offset_y); +} + +TEST(PdfTransformTest, CalculateNonScaledClipBoxOffset) { + int page_width = kDefaultWidth; + int page_height = kDefaultHeight; + gfx::Rect rect(kDefaultWidth, kDefaultHeight); + ClipBox clip_box; + double offset_x; + double offset_y; + + // |rect|, page size and |clip_box| are the same. + InitializeBoxToDefaultPortraitValues(&clip_box); + CalculateNonScaledClipBoxOffset( + rect, 0, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(0, offset_x); + EXPECT_DOUBLE_EQ(0, offset_y); + CalculateNonScaledClipBoxOffset( + rect, 1, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(0, offset_x); + EXPECT_DOUBLE_EQ(0, offset_y); + CalculateNonScaledClipBoxOffset( + rect, 2, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(0, offset_x); + EXPECT_DOUBLE_EQ(0, offset_y); + CalculateNonScaledClipBoxOffset( + rect, 3, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(180, offset_x); + EXPECT_DOUBLE_EQ(-180, offset_y); + + // Smaller |clip_box|. + clip_box.top /= 4; + clip_box.right /= 2; + CalculateNonScaledClipBoxOffset( + rect, 0, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(0, offset_x); + EXPECT_DOUBLE_EQ(594, offset_y); + CalculateNonScaledClipBoxOffset( + rect, 1, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(0, offset_x); + EXPECT_DOUBLE_EQ(0, offset_y); + CalculateNonScaledClipBoxOffset( + rect, 2, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(306, offset_x); + EXPECT_DOUBLE_EQ(0, offset_y); + CalculateNonScaledClipBoxOffset( + rect, 3, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(486, offset_x); + EXPECT_DOUBLE_EQ(414, offset_y); + + // Larger page size. + InitializeBoxToDefaultPortraitValues(&clip_box); + page_width += 10; + page_height += 20; + CalculateNonScaledClipBoxOffset( + rect, 0, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(0, offset_x); + EXPECT_DOUBLE_EQ(20, offset_y); + CalculateNonScaledClipBoxOffset( + rect, 1, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(0, offset_x); + EXPECT_DOUBLE_EQ(0, offset_y); + CalculateNonScaledClipBoxOffset( + rect, 2, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(10, offset_x); + EXPECT_DOUBLE_EQ(0, offset_y); + CalculateNonScaledClipBoxOffset( + rect, 3, page_width, page_height, clip_box, &offset_x, &offset_y); + EXPECT_DOUBLE_EQ(200, offset_x); + EXPECT_DOUBLE_EQ(-170, offset_y); +} + +} // namespace printing diff --git a/printing/printing.gyp b/printing/printing.gyp index e87fe9c..e2c2883 100644 --- a/printing/printing.gyp +++ b/printing/printing.gyp @@ -201,6 +201,11 @@ 'dependencies': [ 'printing_jni_headers', ], + }, { + 'sources': [ + 'pdf_transform.cc', + 'pdf_transform.h', + ], }], ], }, @@ -232,6 +237,9 @@ 'conditions': [ ['OS!="mac"', {'sources/': [['exclude', '_mac_unittest\\.(cc|mm?)$']]}], ['OS!="win"', {'sources/': [['exclude', '_win_unittest\\.cc$']]}], + ['OS!="android"', { + 'sources': ['pdf_transform_unittest.cc'] + }], ['use_cups==1', { 'defines': [ 'USE_CUPS', |