Commit 1752f776 authored by Douglas Stockwell's avatar Douglas Stockwell Committed by Commit Bot

pdf: Move page to screen coordinate transformation to JS

The motivation to do this is to be able to convert in annotation mode
when the plugin is not active.

Fortunately it turns out that there is enough information to do this
without communicating with the plugin -- the dimensions of each page
are available and the translation from points to pixels is straightforward.

This new implementation also deals with the case where the
document has been rotated.

Bug: 902646
Change-Id: Id2814f96f6020c44cee07f725f5170494f1b512b
Reviewed-on: https://chromium-review.googlesource.com/c/1347646Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Reviewed-by: default avatarHenrique Nakashima <hnakashima@chromium.org>
Commit-Queue: dstockwell <dstockwell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611940}
parent 7eed0fa8
......@@ -110,7 +110,6 @@
<include name="IDR_PDF_GESTURE_DETECTOR_JS" file="pdf/gesture_detector.js" type="BINDATA" />
<include name="IDR_PDF_BROWSER_API_JS" file="pdf/browser_api.js" type="BINDATA" />
<include name="IDR_PDF_METRICS_JS" file="pdf/metrics.js" type="BINDATA" />
<include name="IDR_PDF_COORDS_TRANSFORMER_JS" file="pdf/coords_transformer.js" type="BINDATA" />
<include name="IDR_PDF_SHARED_VARS_HTML" file="pdf/elements/shared-vars.html" type="BINDATA" />
<include name="IDR_PDF_ICONS_HTML" file="pdf/elements/icons.html" type="BINDATA" />
......
......@@ -26,9 +26,6 @@ js_library("browser_api") {
js_library("pdf_fitting_type") {
}
js_library("coords_transformer") {
}
js_library("gesture_detector") {
}
......@@ -50,7 +47,6 @@ js_library("viewport_scroller") {
js_type_check("pdf_resources") {
deps = [
":browser_api",
":coords_transformer",
":gesture_detector",
":open_pdf_params_parser",
":pdf_fitting_type",
......
// Copyright 2018 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.
/**
* @typedef {{
* callback: function(Object, Object):void,
* params: Object
* }}
*/
let TransformPagePointRequest;
/**
* @typedef {{
* type: string,
* id: number,
* page: number,
* x: number,
* y: number
* }}
*/
let TransformPagePointMessage;
(function() {
'use strict';
/**
* Transforms page to screen coordinates using messages to the plugin.
*/
window.PDFCoordsTransformer = class {
constructor(postMessageCallback) {
/** @private {!Map<number,!TransformPagePointRequest>} */
this.outstandingTransformPagePointRequests_ = new Map();
/** @private {function(TransformPagePointMessage):void} */
this.postMessageCallback_ = postMessageCallback;
/** @private {number} */
this.nextId_ = 0;
}
/**
* Send a 'transformPagePoint' message to the plugin.
*
* @param {function(Object, Object):void} callback Function to call when the
* response is received.
* @param {Object} params User parameters to be used in |callback|.
* @param {number} page 0-based page number of the page where the point is.
* @param {number} x x coordinate of the point.
* @param {number} y y coordinate of the point.
*/
request(callback, params, page, x, y) {
this.outstandingTransformPagePointRequests_.set(
this.nextId_, {callback: callback, params: params});
this.postMessageCallback_(
{type: 'transformPagePoint', id: this.nextId_, page: page, x: x, y: y});
this.nextId_++;
}
/**
* Call when 'transformPagePointReply' is received from the plugin.
*
* @param {Object} message The message received from the plugin.
*/
onReplyReceived(message) {
const outstandingRequest =
this.outstandingTransformPagePointRequests_.get(message.data.id);
this.outstandingTransformPagePointRequests_.delete(message.data.id);
outstandingRequest.callback(message.data, outstandingRequest.params);
}
};
}());
......@@ -39,7 +39,6 @@
<script src="chrome://resources/js/load_time_data.js"></script>
<script src="chrome://resources/js/util.js"></script>
<script src="browser_api.js"></script>
<script src="coords_transformer.js"></script>
<script src="metrics.js"></script>
<script src="pdf_viewer.js"></script>
<script src="main.js"></script>
......
......@@ -140,12 +140,6 @@ function PDFViewer(browserApi) {
(chrome.metricsPrivate ? new PDFMetricsImpl() : new PDFMetricsDummy());
this.metrics.onDocumentOpened();
/**
* @private {!PDFCoordsTransformer}
*/
this.coordsTransformer_ =
new PDFCoordsTransformer(this.postMessage_.bind(this));
// Parse open pdf parameters.
this.paramsParser_ = new OpenPDFParamsParser(this.postMessage_.bind(this));
var toolbarEnabled =
......@@ -267,11 +261,8 @@ function PDFViewer(browserApi) {
});
document.body.addEventListener('change-page-and-xy', e => {
// The coordinates received in |e| are in page coordinates and need to be
// transformed to screen coordinates.
this.coordsTransformer_.request(
this.goToPageAndXY_.bind(this, e.detail.origin, e.detail.page), {},
e.detail.page, e.detail.x, e.detail.y);
const point = this.viewport_.convertPageToScreen(e.detail.page, e.detail);
this.goToPageAndXY_(e.detail.origin, e.detail.page, point);
});
document.body.addEventListener('navigate', e => {
......@@ -484,6 +475,7 @@ PDFViewer.prototype = {
*/
rotateClockwise_: function() {
this.metrics.onRotation();
this.viewport_.rotateClockwise(1);
this.postMessage_({type: 'rotateClockwise'});
},
......@@ -494,6 +486,7 @@ PDFViewer.prototype = {
*/
rotateCounterClockwise_: function() {
this.metrics.onRotation();
this.viewport_.rotateClockwise(3);
this.postMessage_({type: 'rotateCounterclockwise'});
},
......@@ -781,9 +774,6 @@ PDFViewer.prototype = {
case 'formFocusChange':
this.isFormFieldFocused_ = message.data.focused;
break;
case 'transformPagePointReply':
this.coordsTransformer_.onReplyReceived(message);
break;
case 'saveData':
this.saveData_(message.data);
break;
......
......@@ -12,17 +12,27 @@ let Point;
/**
* @typedef {{
* x: number | undefined,
* y: number | undefined
* x: (number|undefined),
* y: (number|undefined),
* }}
*/
let PartialPoint;
/**
* @typedef {{
* x: number,
* y: number,
* width: number,
* heigh: number,
* }}
*/
let Rect;
/**
* Returns the height of the intersection of two rectangles.
*
* @param {Object} rect1 the first rect
* @param {Object} rect2 the second rect
* @param {Rect} rect1 the first rect
* @param {Rect} rect2 the second rect
* @return {number} the height of the intersection of the rects
*/
function getIntersectionHeight(rect1, rect2) {
......@@ -51,6 +61,7 @@ function frameToPluginCoordinate(coordinateInFrame) {
};
}
// TODO: convert Viewport to ES6 class syntax
/**
* Create a new viewport.
*
......@@ -92,6 +103,7 @@ function Viewport(
this.pinchPanVector_ = null;
this.pinchCenter_ = null;
this.firstPinchCenterInFrame_ = null;
this.rotations_ = 0;
window.addEventListener('scroll', this.updateViewport_.bind(this));
window.addEventListener('resize', this.resizeWrapper_.bind(this));
......@@ -160,6 +172,68 @@ Viewport.PAGE_SHADOW = {
};
Viewport.prototype = {
/**
* @param {number} n the number of clockwise 90-degree rotations to
* increment by.
*/
rotateClockwise: function(n) {
this.rotations_ = (this.rotations_ + n) % 4;
},
/**
* @return {number} the number of clockwise 90-degree rotations that have been
* applied.
*/
getClockwiseRotations: function() {
return this.rotations_;
},
/**
* Converts a page position (e.g. the location of a bookmark) to a screen
* position.
*
* @param {number} page
* @param {Point} point The position on `page`.
* @return The screen position.
*/
convertPageToScreen: function(page, point) {
const dimensions = this.getPageInsetDimensions(page);
// width & height are already rotated.
const height = dimensions.height;
const width = dimensions.width;
const matrix = new DOMMatrix();
const rotation = this.rotations_ * 90;
// Set origin for rotation.
if (rotation == 90) {
matrix.translateSelf(width, 0);
} else if (rotation == 180) {
matrix.translateSelf(width, height);
} else if (rotation == 270) {
matrix.translateSelf(0, height);
}
matrix.rotateSelf(0, 0, rotation);
// Invert Y position with respect to height as page coordinates are
// measured from the bottom left.
matrix.translateSelf(0, height);
matrix.scaleSelf(1, -1);
const pointsToPixels = 96 / 72;
const result = matrix.transformPoint({
x: point.x * pointsToPixels,
y: point.y * pointsToPixels,
});
return {
x: result.x + Viewport.PAGE_SHADOW.left,
y: result.y + Viewport.PAGE_SHADOW.top,
};
},
/**
* Returns the zoomed and rounded document dimensions for the given zoom.
* Rounding is necessary when interacting with the renderer which tends to
......@@ -272,7 +346,7 @@ Viewport.prototype = {
},
/**
* @type {Object} the scroll position of the viewport.
* @type {Point} the scroll position of the viewport.
*/
get position() {
return {
......@@ -284,7 +358,7 @@ Viewport.prototype = {
/**
* Scroll the viewport to the specified position.
*
* @type {Object} position The position to scroll to.
* @type {Point} position The position to scroll to.
*/
set position(position) {
this.window_.scrollTo(position.x, position.y + this.topToolbarHeight_);
......@@ -348,7 +422,7 @@ Viewport.prototype = {
* so that while zooming is taking place it can stop reacting to scroll events
* from the viewport. This is to avoid flickering.
*
* @param {function} f Function to wrap
* @param {Function} f Function to wrap
* @private
*/
mightZoom_: function(f) {
......@@ -917,6 +991,21 @@ Viewport.prototype = {
});
},
/**
* @param {number} page
* @return {Rect} The bounds for page `page` minus the shadows.
*/
getPageInsetDimensions: function(page) {
const pageDimensions = this.pageDimensions_[page];
const shadow = Viewport.PAGE_SHADOW;
return {
x: pageDimensions.x + shadow.left,
y: pageDimensions.y + shadow.top,
width: pageDimensions.width - shadow.left - shadow.right,
height: pageDimensions.height - shadow.top - shadow.bottom,
};
},
/**
* Get the coordinates of the page contents (excluding the page shadow)
* relative to the screen.
......@@ -934,14 +1023,7 @@ Viewport.prototype = {
var pageDimensions = this.pageDimensions_[page];
// Compute the page dimensions minus the shadows.
var insetDimensions = {
x: pageDimensions.x + Viewport.PAGE_SHADOW.left,
y: pageDimensions.y + Viewport.PAGE_SHADOW.top,
width: pageDimensions.width - Viewport.PAGE_SHADOW.left -
Viewport.PAGE_SHADOW.right,
height: pageDimensions.height - Viewport.PAGE_SHADOW.top -
Viewport.PAGE_SHADOW.bottom
};
var insetDimensions = this.getPageInsetDimensions(page);
// Compute the x-coordinate of the page within the document.
// TODO(raymes): This should really be set when the PDF plugin passes the
......
......@@ -449,8 +449,6 @@ void SetupPrintPreviewPlugin(content::WebUIDataSource* source) {
IDR_PDF_GESTURE_DETECTOR_JS);
source->AddResourcePath("pdf/browser_api.js", IDR_PDF_BROWSER_API_JS);
source->AddResourcePath("pdf/metrics.js", IDR_PDF_METRICS_JS);
source->AddResourcePath("pdf/coords_transformer.js",
IDR_PDF_COORDS_TRANSFORMER_JS);
source->AddResourcePath("pdf/elements/shared-vars.html",
IDR_PDF_SHARED_VARS_HTML);
......
......@@ -57,7 +57,6 @@ constexpr char kChromeExtension[] =
// Constants used in handling postMessage() messages.
constexpr char kType[] = "type";
constexpr char kJSId[] = "id";
// Beep messge arguments. (Plugin -> Page).
constexpr char kJSBeepType[] = "beep";
// Viewport message arguments. (Page -> Plugin).
......@@ -158,9 +157,6 @@ constexpr char kJSGetNamedDestination[] = "namedDestination";
constexpr char kJSGetNamedDestinationReplyType[] = "getNamedDestinationReply";
constexpr char kJSNamedDestinationPageNumber[] = "pageNumber";
constexpr char kJSTransformPagePointType[] = "transformPagePoint";
constexpr char kJSTransformPagePointReplyType[] = "transformPagePointReply";
// Selecting text in document (Plugin -> Page)
constexpr char kJSSetIsSelectingType[] = "setIsSelecting";
constexpr char kJSIsSelecting[] = "isSelecting";
......@@ -771,25 +767,6 @@ void OutOfProcessInstance::HandleMessage(const pp::Var& message) {
pp::Var(kJSNamedDestinationPageNumber),
named_destination ? static_cast<int>(named_destination->page) : -1);
PostMessage(reply);
} else if (type == kJSTransformPagePointType) {
if (!(dict.Get(pp::Var(kJSPageNumber)).is_int() &&
dict.Get(pp::Var(kJSPageX)).is_int() &&
dict.Get(pp::Var(kJSPageY)).is_int() &&
dict.Get(pp::Var(kJSId)).is_int())) {
NOTREACHED();
return;
}
gfx::PointF page_xy(dict.Get(pp::Var(kJSPageX)).AsInt(),
dict.Get(pp::Var(kJSPageY)).AsInt());
gfx::PointF device_xy = engine_->TransformPagePoint(
dict.Get(pp::Var(kJSPageNumber)).AsInt(), page_xy);
pp::VarDictionary reply;
reply.Set(pp::Var(kType), pp::Var(kJSTransformPagePointReplyType));
reply.Set(pp::Var(kJSPositionX), device_xy.x());
reply.Set(pp::Var(kJSPositionY), device_xy.y());
reply.Set(pp::Var(kJSId), dict.Get(pp::Var(kJSId)).AsInt());
PostMessage(reply);
} else {
NOTREACHED();
}
......
......@@ -343,9 +343,6 @@ class PDFEngine {
// Gets the named destination by name.
virtual base::Optional<PDFEngine::NamedDestination> GetNamedDestination(
const std::string& destination) = 0;
// Transforms an (x, y) point in page coordinates to screen coordinates.
virtual gfx::PointF TransformPagePoint(int page_index,
const gfx::PointF& page_xy) = 0;
// Gets the index of the most visible page, or -1 if none are visible.
virtual int GetMostVisiblePage() = 0;
// Gets the rectangle of the page including shadow.
......
......@@ -2438,12 +2438,6 @@ base::Optional<PDFEngine::NamedDestination> PDFiumEngine::GetNamedDestination(
return result;
}
gfx::PointF PDFiumEngine::TransformPagePoint(int page_index,
const gfx::PointF& page_xy) {
DCHECK(PageIndexInBounds(page_index));
return pages_[page_index]->TransformPageToScreenXY(page_xy);
}
int PDFiumEngine::GetMostVisiblePage() {
if (in_flight_visible_page_)
return *in_flight_visible_page_;
......
......@@ -95,8 +95,6 @@ class PDFiumEngine : public PDFEngine,
pp::VarArray GetBookmarks() override;
base::Optional<PDFEngine::NamedDestination> GetNamedDestination(
const std::string& destination) override;
gfx::PointF TransformPagePoint(int page_index,
const gfx::PointF& page_xy) override;
int GetMostVisiblePage() override;
pp::Rect GetPageRect(int index) override;
pp::Rect GetPageBoundsRect(int index) override;
......
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