Commit 367d4889 authored by Daniel Hosseinian's avatar Daniel Hosseinian Committed by Commit Bot

PDF Viewer Update: Rotate thumbnails with PDF contents

When PDF contents are rotated, rotate the canvases holding the
thumbnail images using CSS. Resize the divs holding the canvaves so that
the layouts of the thumbnails are updated.

Bug: 652400
Change-Id: Ib17fbba2905ac71a38d310ea1dd419933d7785eb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2438476
Commit-Queue: Daniel Hosseinian <dhoss@chromium.org>
Reviewed-by: default avatarJohn Lee <johntlee@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812242}
parent aa6378ee
......@@ -9,7 +9,9 @@
}
#thumbnail {
display: inline-block;
align-items: center;
display: inline-flex;
justify-content: center;
margin-bottom: 12px;
margin-inline-end: auto;
margin-inline-start: auto;
......
......@@ -29,7 +29,7 @@ export class ViewerThumbnailElement extends PolymerElement {
return {
clockwiseRotations: {
type: Number,
observer: 'clockwiseRotationsChanged_',
value: 0,
},
isActive: {
......@@ -42,6 +42,12 @@ export class ViewerThumbnailElement extends PolymerElement {
};
}
static get observers() {
return [
'styleCanvas_(clockwiseRotations)',
];
}
constructor() {
super();
......@@ -52,25 +58,52 @@ export class ViewerThumbnailElement extends PolymerElement {
/** @param {!ImageData} imageData */
set image(imageData) {
const canvas = this.shadowRoot.querySelector('canvas');
const canvas = this.getCanvas_();
canvas.width = imageData.width;
canvas.height = imageData.height;
// Resize the canvas CSS size to maintain the resolution of the thumbnail.
const isPortrait = canvas.width < canvas.height;
const cssWidth = Math.min(
isPortrait ? PORTRAIT_WIDTH : LANDSCAPE_WIDTH,
parseInt(canvas.width / window.devicePixelRatio, 10));
const scale = cssWidth / canvas.width;
const cssHeight = parseInt(canvas.height * scale, 10);
canvas.style.width = `${cssWidth}px`;
canvas.style.height = `${cssHeight}px`;
this.styleCanvas_();
const ctx = canvas.getContext('2d');
ctx.putImageData(imageData, 0, 0);
this.removeAttribute('pending');
}
/**
* @return {!HTMLCanvasElement}
* @private
*/
getCanvas_() {
return /** @type {!HTMLCanvasElement} */ (
this.shadowRoot.querySelector('canvas'));
}
/**
* Calculates the CSS size of the thumbnail depending on the rotation, the
* dimensions of the image data, and the screen resolution. The plugin
* scales the thumbnail image data by the device to pixel ratio, so that
* scaling must be taken into account on the UI.
* @param {boolean} rotated
* @return {!{width: number, height: number}}
* @private
*/
getThumbnailCssSize_(rotated) {
const canvas = this.getCanvas_();
const isPortrait = canvas.width < canvas.height !== rotated;
const orientedWidth = rotated ? canvas.height : canvas.width;
const orientedHeight = rotated ? canvas.width : canvas.height;
// Try scaling down such that the width of thumbnail is `PORTRAIT_WIDTH` or
// `LANDSCAPE_WIDTH`, but never scale up to retain the resolution of the
// thumbnail.
const cssWidth = Math.min(
isPortrait ? PORTRAIT_WIDTH : LANDSCAPE_WIDTH,
parseInt(orientedWidth / window.devicePixelRatio, 10));
const scale = cssWidth / orientedWidth;
const cssHeight = parseInt(orientedHeight * scale, 10);
return {width: cssWidth, height: cssHeight};
}
/** @return {boolean} */
isPending() {
return this.hasAttribute('pending');
......@@ -80,12 +113,6 @@ export class ViewerThumbnailElement extends PolymerElement {
this.toggleAttribute('pending', true);
}
/** @private */
clockwiseRotationsChanged_() {
// TODO(crbug.com/652400): Respect the PDF content clockwiseRotation.
assert(this.clockwiseRotations >= 0 && this.clockwiseRotations < 4);
}
/** @private */
isActiveChanged_() {
if (this.isActive) {
......@@ -143,6 +170,33 @@ export class ViewerThumbnailElement extends PolymerElement {
break;
}
}
/**
* Sets the canvas CSS size to maintain the resolution of the thumbnail at any
* rotation.
* @private
*/
styleCanvas_() {
assert(this.clockwiseRotations >= 0 && this.clockwiseRotations < 4);
const canvas = this.getCanvas_();
const div = this.shadowRoot.querySelector('#thumbnail');
const degreesRotated = this.clockwiseRotations * 90;
canvas.style.transform = `rotate(${degreesRotated}deg)`;
// For the purposes of determining the dimensions, a rotation of 180deg is
// not rotated.
const rotated = this.clockwiseRotations % 2 !== 0;
const cssSize = this.getThumbnailCssSize_(rotated);
div.style.width = `${cssSize.width}px`;
div.style.height = `${cssSize.height}px`;
// When rotated, the canvas's height becomes the parent div's width and vice
// versa.
canvas.style.width = `${rotated ? cssSize.height : cssSize.width}px`;
canvas.style.height = `${rotated ? cssSize.width : cssSize.height}px`;
}
}
customElements.define(ViewerThumbnailElement.is, ViewerThumbnailElement);
......@@ -32,6 +32,45 @@ function testThumbnailSize(thumbnail, imageSize, canvasSize) {
chrome.test.assertEq(canvasSize[1], div.offsetHeight);
}
/**
* @param {!ViewerThumbnailElement} thumbnail
* @param {number} clockwiseRotations
* @param {!Array<number>} divSize
*/
function testThumbnailRotation(thumbnail, clockwiseRotations, divSize) {
thumbnail.clockwiseRotations = clockwiseRotations;
const canvas = thumbnail.shadowRoot.querySelector('canvas');
const halfTurn = clockwiseRotations % 2 === 0;
chrome.test.assertEq(
`${halfTurn ? divSize[0] : divSize[1]}px`, canvas.style.width);
chrome.test.assertEq(
`${halfTurn ? divSize[1] : divSize[0]}px`, canvas.style.height);
// The div containing the rotated canvas should be resized to fit.
const div = canvas.parentElement;
chrome.test.assertEq(divSize[0], div.offsetWidth);
chrome.test.assertEq(divSize[1], div.offsetHeight);
chrome.test.assertEq(
`rotate(${clockwiseRotations * 90}deg)`, canvas.style.transform);
}
/**
* @param {!Array<number>} imageSize
* @param {!Array<!Array<number>>} rotatedDivSizes
*/
function testThumbnailRotations(imageSize, rotatedDivSizes) {
const thumbnail = createThumbnail();
const imageData = new ImageData(imageSize[0], imageSize[1]);
thumbnail.image = imageData;
chrome.test.assertEq(4, rotatedDivSizes.length);
for (let rotations = 0; rotations < rotatedDivSizes.length; rotations++) {
testThumbnailRotation(thumbnail, rotations, rotatedDivSizes[rotations]);
}
}
const tests = [
function testSetNormalImageLowRes() {
window.devicePixelRatio = 1;
......@@ -105,6 +144,48 @@ const tests = [
chrome.test.succeed();
},
function testRotateNormalLowRes() {
window.devicePixelRatio = 1;
// Letter
testThumbnailRotations(
[108, 140], [[108, 140], [140, 108], [108, 140], [140, 108]]);
// A4
testThumbnailRotations(
[108, 152], [[108, 152], [140, 99], [108, 152], [140, 99]]);
chrome.test.succeed();
},
function testRotateNormalHighRes() {
window.devicePixelRatio = 2;
// Letter
testThumbnailRotations(
[216, 280], [[108, 140], [140, 108], [108, 140], [140, 108]]);
// A4
testThumbnailRotations(
[216, 304], [[108, 152], [140, 99], [108, 152], [140, 99]]);
chrome.test.succeed();
},
function testRotateNormalHighRes() {
window.devicePixelRatio = 1;
testThumbnailRotations(
[50, 1500], [[50, 1500], [140, 4], [50, 1500], [140, 4]]);
chrome.test.succeed();
},
function testRotateNormalHighRes() {
window.devicePixelRatio = 2;
testThumbnailRotations(
[50, 1500], [[25, 750], [140, 4], [25, 750], [140, 4]]);
chrome.test.succeed();
},
];
chrome.test.runTests(tests);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment