0

Add supports for view destinations with 'XYZ' fit type.

This CL adds supports for navigating to view destinations with fit type
'XYZ' (See table 151 in ISO 32000-1 standard for more details about
syntax of "XYZ"), which specifies the coordinates of the page's origin
inside the view window and the zoom factor of the page.

Bug: 55776,748852
Change-Id: I4f449f006ec9dda4be282aea75c2a6e36a35b87e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2390997
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Rebekah Potter <rbpotter@chromium.org>
Commit-Queue: Hui Yingst <nigi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810919}
This commit is contained in:
Hui Yingst
2020-09-26 02:19:52 +00:00
committed by Commit Bot
parent 7247ce797e
commit 74d64c91c3
4 changed files with 120 additions and 2 deletions

@ -31,6 +31,7 @@ export const FittingType = {
/**
* @typedef {{
* messageId: string,
* namedDestinationView: (string|undefined),
* pageNumber: number,
* }}
*/

@ -10,7 +10,8 @@ import {FittingType, NamedDestinationMessageData, Point} from './constants.js';
* url: (string|undefined),
* zoom: (number|undefined),
* view: (!FittingType|undefined),
* viewPosition: (!Point|undefined)
* viewPosition: (!Point|undefined),
* position: (!Object|undefined),
* }}
*/
let OpenPdfParams;
@ -99,6 +100,42 @@ export class OpenPdfParamsParser {
return params;
}
/**
* Parse view parameters which come from nameddest.
* @param {string} paramValue view value.
* @return {!OpenPdfParams} Map with view parameters.
* @private
*/
parseNameddestViewParam_(paramValue) {
const viewModeComponents = paramValue.toLowerCase().split(',');
const viewMode = viewModeComponents[0];
const params = {};
if (viewMode === 'xyz' && viewModeComponents.length === 4) {
const x = parseFloat(viewModeComponents[1]);
const y = parseFloat(viewModeComponents[2]);
const zoom = parseFloat(viewModeComponents[3]);
// If |x|, |y| or |zoom| is NaN, the values of the current positions and
// zoom level are retained.
if (!Number.isNaN(x) && !Number.isNaN(y) && !Number.isNaN(zoom)) {
params['position'] = {x: x, y: y};
// A zoom of 0 should be treated as a zoom of null (See table 151 in ISO
// 32000-1 standard for more details about syntax of "XYZ".
if (zoom !== 0) {
params['zoom'] = zoom;
}
}
return params;
}
if (viewMode === 'fitr' && viewModeComponents.length === 5) {
// TODO(crbug.com/535978): Add support for fit type "FitR" in nameddest.
return params;
}
return this.parseViewParam_(paramValue);
}
/**
* Parse the parameters encoded in the fragment of a URL.
* @param {string} url to parse
@ -173,6 +210,12 @@ export class OpenPdfParamsParser {
if (data.pageNumber !== -1) {
params.page = data.pageNumber;
}
if (data.namedDestinationView) {
Object.assign(
params,
this.parseNameddestViewParam_(
/** @type {string} */ (data.namedDestinationView)));
}
callback(params);
});
} else {

@ -22,9 +22,27 @@ const tests = [
} else if (destination === 'UY') {
return Promise.resolve(
{messageId: 'getNamedDestination_3', pageNumber: 22});
} else if (destination === 'DestWithXYZ') {
return Promise.resolve({
messageId: 'getNamedDestination_4',
namedDestinationView: 'XYZ,111,222,1.7',
pageNumber: 10
});
} else if (destination === 'DestWithXYZAtZoom0') {
return Promise.resolve({
messageId: 'getNamedDestination_5',
namedDestinationView: 'XYZ,111,222,0',
pageNumber: 10
});
} else if (destination === 'DestWithXYZWithNullParameter') {
return Promise.resolve({
messageId: 'getNamedDestination_6',
namedDestinationView: 'XYZ,111,null,1.7',
pageNumber: 13
});
} else {
return Promise.resolve(
{messageId: 'getNamedDestination_4', pageNumber: -1});
{messageId: 'getNamedDestination_7', pageNumber: -1});
}
});
@ -92,6 +110,38 @@ const tests = [
chrome.test.assertEq(200, params.position.y);
});
// Checking #nameddest=name with a nameddest that specifies the view fit
// type is "XYZ" with multiple valid parameters.
paramsParser.getViewportFromUrlParams(
`${url}#nameddest=DestWithXYZ`, function(params) {
chrome.test.assertEq(10, params.page);
chrome.test.assertEq(1.7, params.zoom);
chrome.test.assertEq(111, params.position.x);
chrome.test.assertEq(222, params.position.y);
chrome.test.assertEq(undefined, params.viewPosition);
});
// Checking #nameddest=name with a nameddest that specifies the view fit
// type is "XYZ" with a zoom parameter of 0.
paramsParser.getViewportFromUrlParams(
`${url}#nameddest=DestWithXYZAtZoom0`, function(params) {
chrome.test.assertEq(10, params.page);
chrome.test.assertEq(undefined, params.zoom);
chrome.test.assertEq(111, params.position.x);
chrome.test.assertEq(222, params.position.y);
chrome.test.assertEq(undefined, params.viewPosition);
});
// Checking #nameddest=name with a nameddest that specifies the view fit
// type is "XYZ" and one of its parameters is null.
paramsParser.getViewportFromUrlParams(
`${url}#nameddest=DestWithXYZWithNullParameter`, function(params) {
chrome.test.assertEq(13, params.page);
chrome.test.assertEq(undefined, params.zoom);
chrome.test.assertEq(undefined, params.position);
chrome.test.assertEq(undefined, params.viewPosition);
});
// Checking #view=Fit.
paramsParser.getViewportFromUrlParams(`${url}#view=Fit`, function(params) {
chrome.test.assertEq(FittingType.FIT_TO_PAGE, params.view);
@ -142,6 +192,19 @@ const tests = [
chrome.test.assertEq(undefined, params.view);
chrome.test.assertEq(undefined, params.viewPosition);
});
// Checking #view=[wrong parameter].
paramsParser.getViewportFromUrlParams(`${url}#view=XYZ`, function(params) {
chrome.test.assertEq(undefined, params.view);
chrome.test.assertEq(undefined, params.viewPosition);
});
// Checking #view=[wrong parameter],[position].
paramsParser.getViewportFromUrlParams(
`${url}#view=XYZ,111,222,1.7`, function(params) {
chrome.test.assertEq(undefined, params.zoom);
chrome.test.assertEq(undefined, params.position);
chrome.test.assertEq(undefined, params.view);
chrome.test.assertEq(undefined, params.viewPosition);
});
// Checking #toolbar=0 to disable the toolbar.
chrome.test.assertFalse(paramsParser.shouldShowToolbar(`${url}#toolbar=0`));

@ -211,6 +211,7 @@ constexpr char kJSGetNamedDestination[] = "namedDestination";
// Reply with the page number of the named destination (Plugin -> Page)
constexpr char kJSGetNamedDestinationReplyType[] = "getNamedDestinationReply";
constexpr char kJSNamedDestinationPageNumber[] = "pageNumber";
constexpr char kJSNamedDestinationView[] = "namedDestinationView";
// Selecting text in document (Plugin -> Page)
constexpr char kJSSetIsSelectingType[] = "setIsSelecting";
@ -1652,6 +1653,16 @@ void OutOfProcessInstance::HandleGetNamedDestinationMessage(
reply.Set(pp::Var(kJSNamedDestinationPageNumber),
named_destination ? static_cast<int>(named_destination->page) : -1);
reply.Set(pp::Var(kJSMessageId), dict.Get(pp::Var(kJSMessageId)).AsString());
// Handle named destination view.
if (named_destination && !named_destination->view.empty()) {
std::ostringstream view_stream;
view_stream << named_destination->view;
for (unsigned long i = 0; i < named_destination->num_params; ++i)
view_stream << "," << named_destination->params[i];
reply.Set(pp::Var(kJSNamedDestinationView), view_stream.str());
}
PostMessage(reply);
}