// 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 PdfRectangle& box) {
  EXPECT_FLOAT_EQ(0, box.left);
  EXPECT_FLOAT_EQ(0, box.bottom);
  EXPECT_FLOAT_EQ(kDefaultWidth, box.right);
  EXPECT_FLOAT_EQ(kDefaultHeight, box.top);
}

void ExpectDefaultLandscapeBox(const PdfRectangle& box) {
  EXPECT_FLOAT_EQ(0, box.left);
  EXPECT_FLOAT_EQ(0, box.bottom);
  EXPECT_FLOAT_EQ(kDefaultHeight, box.right);
  EXPECT_FLOAT_EQ(kDefaultWidth, box.top);
}

void ExpectBoxesAreEqual(const PdfRectangle& expected,
                         const PdfRectangle& actual) {
  EXPECT_FLOAT_EQ(expected.left, actual.left);
  EXPECT_FLOAT_EQ(expected.bottom, actual.bottom);
  EXPECT_FLOAT_EQ(expected.right, actual.right);
  EXPECT_FLOAT_EQ(expected.top, actual.top);
}

void InitializeBoxToInvalidValues(PdfRectangle* box) {
  box->left = box->bottom = box->right = box->top = -1;
}

void InitializeBoxToDefaultPortraitValues(PdfRectangle* box) {
  box->left = 0;
  box->bottom = 0;
  box->right = kDefaultWidth;
  box->top = kDefaultHeight;
}

void InitializeBoxToDefaultLandscapeValue(PdfRectangle* box) {
  box->left = 0;
  box->bottom = 0;
  box->right = kDefaultHeight;
  box->top = kDefaultWidth;
}

}  // 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) {
  PdfRectangle box;

  SetDefaultClipBox(false, &box);
  ExpectDefaultPortraitBox(box);

  SetDefaultClipBox(true, &box);
  ExpectDefaultLandscapeBox(box);
}

TEST(PdfTransformTest, CalculateMediaBoxAndCropBox) {
  PdfRectangle media_box;
  PdfRectangle 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.
  const PdfRectangle expected_box = {0, 0, 42, 420};
  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) {
  PdfRectangle media_box;
  PdfRectangle crop_box;
  PdfRectangle 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(0, result.bottom);
  EXPECT_FLOAT_EQ(kDefaultWidth, result.right);
  EXPECT_FLOAT_EQ(kDefaultWidth, result.top);

  // crop box is smaller than media box.
  crop_box.left = 0;
  crop_box.bottom = 0;
  crop_box.right = 100;
  crop_box.top = 200;
  result = CalculateClipBoxBoundary(media_box, crop_box);
  EXPECT_FLOAT_EQ(0, result.left);
  EXPECT_FLOAT_EQ(0, result.bottom);
  EXPECT_FLOAT_EQ(100, result.right);
  EXPECT_FLOAT_EQ(200, result.top);

  // crop box is smaller than the media box in one dimension and longer in the
  // other.
  crop_box.left = 0;
  crop_box.bottom = 0;
  crop_box.right = 100;
  crop_box.top = 2000;
  result = CalculateClipBoxBoundary(media_box, crop_box);
  EXPECT_FLOAT_EQ(0, result.left);
  EXPECT_FLOAT_EQ(0, result.bottom);
  EXPECT_FLOAT_EQ(100, result.right);
  EXPECT_FLOAT_EQ(kDefaultHeight, result.top);
}

TEST(PdfTransformTest, CalculateScaledClipBoxOffset) {
  const gfx::Rect rect(kDefaultWidth, kDefaultHeight);
  PdfRectangle 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;
  const gfx::Rect rect(kDefaultWidth, kDefaultHeight);
  PdfRectangle 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);
}

// https://crbug.com/491160 and https://crbug.com/588757
TEST(PdfTransformTest, ReversedMediaBox) {
  int page_width = kDefaultWidth;
  int page_height = kDefaultHeight;
  const gfx::Rect rect(kDefaultWidth, kDefaultHeight);
  PdfRectangle clip_box;
  double offset_x;
  double offset_y;

  const PdfRectangle expected_media_box_b491160 = {0, -792, 612, 0};
  PdfRectangle media_box_b491160 = {0, 0, 612, -792};
  CalculateMediaBoxAndCropBox(false, true, false, &media_box_b491160,
                              &clip_box);
  ExpectBoxesAreEqual(expected_media_box_b491160, media_box_b491160);
  ExpectBoxesAreEqual(expected_media_box_b491160, clip_box);

  CalculateScaledClipBoxOffset(rect, media_box_b491160, &offset_x, &offset_y);
  EXPECT_DOUBLE_EQ(0, offset_x);
  EXPECT_DOUBLE_EQ(792, offset_y);

  CalculateNonScaledClipBoxOffset(rect, 0, page_width, page_height,
                                  media_box_b491160, &offset_x, &offset_y);
  EXPECT_DOUBLE_EQ(0, offset_x);
  EXPECT_DOUBLE_EQ(792, offset_y);

  PdfRectangle media_box_b588757 = {0, 792, 612, 0};
  CalculateMediaBoxAndCropBox(false, true, false, &media_box_b588757,
                              &clip_box);
  ExpectDefaultPortraitBox(media_box_b588757);
  ExpectDefaultPortraitBox(clip_box);

  CalculateScaledClipBoxOffset(rect, clip_box, &offset_x, &offset_y);
  EXPECT_DOUBLE_EQ(0, offset_x);
  EXPECT_DOUBLE_EQ(0, offset_y);

  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);

  PdfRectangle media_box_left_right_flipped = {612, 792, 0, 0};
  CalculateMediaBoxAndCropBox(false, true, false, &media_box_left_right_flipped,
                              &clip_box);
  ExpectDefaultPortraitBox(media_box_left_right_flipped);
  ExpectDefaultPortraitBox(clip_box);

  CalculateScaledClipBoxOffset(rect, clip_box, &offset_x, &offset_y);
  EXPECT_DOUBLE_EQ(0, offset_x);
  EXPECT_DOUBLE_EQ(0, offset_y);

  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);
}

}  // namespace printing
