[PDF Ink Signatures] Handle rotations
Similar to scrolling and zooming, the text box needs to look like an
annotation in the real PDF when the viewport changes via rotations,
i.e. the box should rotate with the PDF. Existing textboxes that the
user re-activates for editing also need to maintain their original
rotation relative to the PDF, to represent what they would look like
if still being rendered as part of the PDF itself.
Textboxes will be created with a rotation relative to the PDF if they
are created when the PDF is rotated - i.e. new textboxes are always
created with no rotation, even if the PDF is in a rotated state.
Bug: 402546155
Include-Ci-Only-Tests: chromium.mac:mac14-tests|browser_tests
Change-Id: Ia34ffcf50d24b29a3d6fb9bc5d35fd78c2e40bf9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6514014
Reviewed-by: Andy Phan <andyphan@chromium.org>
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1459104}
diff --git a/chrome/browser/resources/pdf/constants.ts b/chrome/browser/resources/pdf/constants.ts
index d50ea68..ab45b06 100644
--- a/chrome/browser/resources/pdf/constants.ts
+++ b/chrome/browser/resources/pdf/constants.ts
@@ -44,6 +44,9 @@
// and is in page coordinates when this annotation is sent or received in
// a message to/from the plugin.
textBoxRect: TextBoxRect;
+ // Orientation of the text in the box relative to the PDF page, in number of
+ // clockwise rotations from 0 to 3.
+ textOrientation: number;
}
export enum TextAlignment {
diff --git a/chrome/browser/resources/pdf/elements/ink_text_box.css b/chrome/browser/resources/pdf/elements/ink_text_box.css
index dbf7752..f3ef6c22 100644
--- a/chrome/browser/resources/pdf/elements/ink_text_box.css
+++ b/chrome/browser/resources/pdf/elements/ink_text_box.css
@@ -93,3 +93,15 @@
padding: calc(var(--handle-size) / 2 + 2px);
resize: none;
}
+
+:host([text-rotations_="1"]) textarea {
+ writing-mode: vertical-rl;
+}
+
+:host([text-rotations_="2"]) textarea {
+ transform: rotate(180deg);
+}
+
+:host([text-rotations_="3"]) textarea {
+ writing-mode: sideways-lr;
+}
diff --git a/chrome/browser/resources/pdf/elements/ink_text_box.ts b/chrome/browser/resources/pdf/elements/ink_text_box.ts
index 5dd8bc3..3dcee636 100644
--- a/chrome/browser/resources/pdf/elements/ink_text_box.ts
+++ b/chrome/browser/resources/pdf/elements/ink_text_box.ts
@@ -8,7 +8,7 @@
import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
import type {TextAttributes, TextBoxRect} from '../constants.js';
-import {colorsEqual, Ink2Manager, stylesEqual} from '../ink2_manager.js';
+import {colorsEqual, convertRotatedCoordinates, Ink2Manager, stylesEqual} from '../ink2_manager.js';
import type {TextBoxInit, ViewportParams} from '../ink2_manager.js';
import {colorToHex} from '../pdf_viewer_utils.js';
@@ -57,7 +57,13 @@
locationY_: {type: Number},
minHeight_: {type: Number},
state_: {type: Number},
+ textOrientation_: {type: Number},
+ textRotations_: {
+ type: Number,
+ reflect: true,
+ },
textValue_: {type: String},
+ viewportRotations_: {type: Number},
width_: {type: Number},
zoom_: {type: Number},
};
@@ -69,8 +75,11 @@
private accessor locationY_: number = 0;
private accessor minHeight_: number = 0;
private accessor height_: number = 0;
- protected accessor textValue_: string = '';
private accessor state_: TextBoxState = TextBoxState.INACTIVE;
+ private accessor textOrientation_: number = 0;
+ protected accessor textRotations_: number = 0;
+ protected accessor textValue_: string = '';
+ private accessor viewportRotations_: number = 0;
private accessor width_: number = 0;
private accessor zoom_: number = 1.0;
@@ -133,6 +142,12 @@
this.hidden = this.state_ === TextBoxState.INACTIVE;
this.fire('state-changed', this.state_);
}
+
+ if (changedPrivateProperties.has('viewportRotations_') ||
+ changedPrivateProperties.has('textOrientation_')) {
+ this.textRotations_ =
+ (this.viewportRotations_ + this.textOrientation_) % 4;
+ }
}
override updated(changedProperties: PropertyValues<this>) {
@@ -212,6 +227,7 @@
locationY: this.locationY_,
width: this.width_,
},
+ textOrientation: this.textOrientation_,
},
this.state_ === TextBoxState.EDITED);
@@ -239,34 +255,35 @@
data.annotation.text === '' ? 'Sample Text' : data.annotation.text;
this.id_ = data.annotation.id;
this.pageNumber_ = data.annotation.pageNumber;
+ this.textOrientation_ = data.annotation.textOrientation;
this.updateTextAttributes_(data.annotation.textAttributes);
}
private onViewportChanged_(update: ViewportParams) {
// Convert width, height, locationX, locationY to the new screen
// coordinates.
- if (update.zoom !== this.zoom_) {
- this.width_ =
- Math.max(this.width_ * update.zoom / this.zoom_, MIN_WIDTH_PX);
- this.height_ = this.height_ * update.zoom / this.zoom_;
- }
- if (update.zoom !== this.zoom_ || update.pageX !== this.pageX_ ||
- update.pageY !== this.pageY_) {
- // Note that this.pageX_ and this.pageY_ are in the old screen
- // coordinates, i.e. they were using the old zoom value.
- this.locationX_ =
- (this.locationX_ - this.pageX_) * update.zoom / this.zoom_ +
- update.pageX;
- this.locationY_ =
- (this.locationY_ - this.pageY_) * update.zoom / this.zoom_ +
- update.pageY;
- }
+ // Note that this.pageX_ and this.pageY_ are in the old screen
+ // coordinates, i.e. they were using the old zoom value.
+ const adjusted = {
+ locationX: (this.locationX_ - this.pageX_) * update.zoom / this.zoom_,
+ locationY: (this.locationY_ - this.pageY_) * update.zoom / this.zoom_,
+ width: Math.max(this.width_ * update.zoom / this.zoom_, MIN_WIDTH_PX),
+ height: this.height_ * update.zoom / this.zoom_,
+ };
+ const rotated = convertRotatedCoordinates(
+ adjusted, this.viewportRotations_, update.clockwiseRotations,
+ update.pageDimensions.width, update.pageDimensions.height);
+ this.locationX_ = rotated.locationX + update.pageDimensions.x;
+ this.locationY_ = rotated.locationY + update.pageDimensions.y;
+ this.width_ = rotated.width;
+ this.height_ = rotated.height;
// Update properties to the new values.
+ this.viewportRotations_ = update.clockwiseRotations;
this.zoom_ = update.zoom;
- this.pageX_ = update.pageX;
- this.pageY_ = update.pageY;
+ this.pageX_ = update.pageDimensions.x;
+ this.pageY_ = update.pageDimensions.y;
}
protected onPointerDown_(e: PointerEvent) {
diff --git a/chrome/browser/resources/pdf/ink2_manager.ts b/chrome/browser/resources/pdf/ink2_manager.ts
index cd6d5fd6..ad92a061 100644
--- a/chrome/browser/resources/pdf/ink2_manager.ts
+++ b/chrome/browser/resources/pdf/ink2_manager.ts
@@ -2,18 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import {assert} from 'chrome://resources/js/assert.js';
+import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
import {isRTL} from 'chrome://resources/js/util.js';
import type {AnnotationBrush, Color, Point, TextAnnotation, TextAttributes, TextBoxRect, TextStyles} from './constants.js';
import {AnnotationBrushType, TextAlignment, TextStyle, TextTypeface} from './constants.js';
import {PluginController, PluginControllerEventType} from './controller.js';
-import type {Viewport} from './viewport.js';
+import type {Viewport, ViewportRect} from './viewport.js';
export interface ViewportParams {
- pageX: number;
- pageY: number;
+ clockwiseRotations: number;
+ pageDimensions: ViewportRect;
zoom: number;
}
@@ -34,6 +34,74 @@
return style1.bold === style2.bold && style1.italic === style2.italic;
}
+/**
+ * Converts `rect` from `oldRotations` clockwise rotations to `newRotations`
+ * clockwise rotations. `newPageWidth` should be the page width in
+ * `newRotations` coordinates, and `newPageHeight` should be the page height in
+ * `newRotations` coordinates.
+ */
+export function convertRotatedCoordinates(
+ rect: TextBoxRect, oldRotations: number, newRotations: number,
+ newPageWidth: number, newPageHeight: number): TextBoxRect {
+ const pageWidthNR = newRotations % 2 === 0 ? newPageWidth : newPageHeight;
+ const pageHeightNR = newRotations % 2 === 0 ? newPageHeight : newPageWidth;
+ const nonRotated: TextBoxRect = {
+ locationX: rect.locationX,
+ locationY: rect.locationY,
+ width: oldRotations % 2 === 0 ? rect.width : rect.height,
+ height: oldRotations % 2 === 0 ? rect.height : rect.width,
+ };
+ switch (oldRotations % 4) {
+ case 0:
+ // Already populated correctly.
+ break;
+ case 1:
+ nonRotated.locationX = rect.locationY;
+ nonRotated.locationY = pageHeightNR - rect.locationX - rect.width;
+ break;
+ case 2:
+ nonRotated.locationX = pageWidthNR - rect.locationX - rect.width;
+ nonRotated.locationY = pageHeightNR - rect.locationY - rect.height;
+ break;
+ case 3:
+ nonRotated.locationX = pageWidthNR - rect.locationY - rect.height;
+ nonRotated.locationY = rect.locationX;
+ break;
+ default:
+ assertNotReached();
+ }
+
+ const newRotated = {
+ locationX: nonRotated.locationX,
+ locationY: nonRotated.locationY,
+ width: newRotations % 2 === 0 ? nonRotated.width : nonRotated.height,
+ height: newRotations % 2 === 0 ? nonRotated.height : nonRotated.width,
+ };
+ switch (newRotations % 4) {
+ case 0:
+ break;
+ case 1:
+ newRotated.locationX =
+ pageHeightNR - nonRotated.locationY - nonRotated.height;
+ newRotated.locationY = nonRotated.locationX;
+ break;
+ case 2:
+ newRotated.locationX =
+ pageWidthNR - nonRotated.locationX - nonRotated.width;
+ newRotated.locationY =
+ pageHeightNR - nonRotated.locationY - nonRotated.height;
+ break;
+ case 3:
+ newRotated.locationX = nonRotated.locationY;
+ newRotated.locationY =
+ pageWidthNR - nonRotated.locationX - nonRotated.width;
+ break;
+ default:
+ assertNotReached();
+ }
+ return newRotated;
+}
+
export class Ink2Manager extends EventTarget {
private brush_: AnnotationBrush = {type: AnnotationBrushType.PEN};
// Map from page numbers to annotations on that page.
@@ -58,7 +126,11 @@
private pageNumber_: number = -1;
private pluginController_: PluginController = PluginController.getInstance();
private viewport_: Viewport|null = null;
- private viewportParams_: ViewportParams = {pageX: 0, pageY: 0, zoom: 1.0};
+ private viewportParams_: ViewportParams = {
+ clockwiseRotations: 0,
+ pageDimensions: {x: 0, y: 0, width: 0, height: 0},
+ zoom: 1.0,
+ };
private nextAnnotationId_: number = 0;
setViewport(viewport: Viewport) {
@@ -90,7 +162,6 @@
return;
}
- const zoom = this.viewport_.getZoom();
const pageDimensions = this.viewport_.getPageScreenRect(page);
// Is the click in an existing box?
let existing = null;
@@ -100,16 +171,16 @@
annotationsMap ? Array.from(annotationsMap.values()) : [];
for (const annotation of annotations) {
// Convert box to screen coordinates.
- const x = annotation.textBoxRect.locationX * zoom + pageDimensions.x;
- const width = annotation.textBoxRect.width * zoom;
- const y = annotation.textBoxRect.locationY * zoom + pageDimensions.y;
- const height = annotation.textBoxRect.height * zoom;
- if (location.x >= x && location.x <= (x + width) && location.y >= y &&
- location.y <= (y + height)) {
+ const screenBox =
+ this.pageToScreenCoordinates_(page, annotation.textBoxRect);
+ if (location.x >= screenBox.locationX &&
+ location.x <= (screenBox.locationX + screenBox.width) &&
+ location.y >= screenBox.locationY &&
+ location.y <= (screenBox.locationY + screenBox.height)) {
// Don't update the original. Create a new object and update its
// rectangle to use the computed screen coordinates.
existing = structuredClone(annotation);
- existing.textBoxRect = {height, locationX: x, locationY: y, width};
+ existing.textBoxRect = screenBox;
break;
}
}
@@ -126,6 +197,7 @@
locationY: location.y,
width: DEFAULT_TEXTBOX_WIDTH,
},
+ textOrientation: (4 - this.viewport_.getClockwiseRotations()) % 4,
};
if (existing) {
@@ -158,17 +230,21 @@
const zoom = this.viewport_.getZoom();
const page = this.pageNumber_ !== -1 ? this.pageNumber_ :
this.viewport_.getMostVisiblePage();
- const visiblePageDimensions = this.viewport_.getPageScreenRect(page);
- if (visiblePageDimensions.x === this.viewportParams_.pageX &&
- visiblePageDimensions.y === this.viewportParams_.pageY &&
+ const pageDimensions = this.viewport_.getPageScreenRect(page);
+ const rotations = this.viewport_.getClockwiseRotations();
+ if (rotations === this.viewportParams_.clockwiseRotations &&
+ pageDimensions.x === this.viewportParams_.pageDimensions.x &&
+ pageDimensions.y === this.viewportParams_.pageDimensions.y &&
+ pageDimensions.width === this.viewportParams_.pageDimensions.width &&
+ pageDimensions.height === this.viewportParams_.pageDimensions.height &&
zoom === this.viewportParams_.zoom) {
// Early return to avoid firing unnecessary events.
return;
}
this.viewportParams_ = {
- pageX: visiblePageDimensions.x,
- pageY: visiblePageDimensions.y,
+ clockwiseRotations: rotations,
+ pageDimensions: pageDimensions,
zoom,
};
this.dispatchEvent(
@@ -286,16 +362,65 @@
this.fireAttributesChanged_();
}
- private screenToPageCoordinates_(pageNumber: number, screenRect: TextBoxRect):
+ private pageToScreenCoordinates_(pageNumber: number, pageRect: TextBoxRect):
TextBoxRect {
assert(this.viewport_);
const pageDimensions = this.viewport_.getPageScreenRect(pageNumber);
const zoom = this.viewport_.getZoom();
+
+ // Apply zoom.
+ const zoomed = {
+ locationX: pageRect.locationX * zoom,
+ locationY: pageRect.locationY * zoom,
+ width: pageRect.width * zoom,
+ height: pageRect.height * zoom,
+ };
+
+ // Apply rotation
+ const rotated = convertRotatedCoordinates(
+ zoomed, 0, this.viewport_.getClockwiseRotations(), pageDimensions.width,
+ pageDimensions.height);
+
+ // Apply offsets.
return {
- height: screenRect.height / zoom,
- locationX: (screenRect.locationX - pageDimensions.x) / zoom,
- locationY: (screenRect.locationY - pageDimensions.y) / zoom,
- width: screenRect.width / zoom,
+ locationX: rotated.locationX + pageDimensions.x,
+ locationY: rotated.locationY + pageDimensions.y,
+ height: rotated.height,
+ width: rotated.width,
+ };
+ }
+
+ private screenToPageCoordinates_(pageNumber: number, screenRect: TextBoxRect):
+ TextBoxRect {
+ assert(this.viewport_);
+ const zoom = this.viewport_.getZoom();
+ const pageDimensions = this.viewport_.getPageScreenRect(pageNumber);
+
+ // Undo offset
+ const noOffset = {
+ locationX: screenRect.locationX - pageDimensions.x,
+ locationY: screenRect.locationY - pageDimensions.y,
+ width: screenRect.width,
+ height: screenRect.height,
+ };
+
+ // Undo rotation
+ const rotations = this.viewport_.getClockwiseRotations();
+ // Need to pass the width and height for the new number of desired rotations
+ // (0 in this case) to convertRotatedCoordinates().
+ const pageWidth =
+ rotations % 2 === 0 ? pageDimensions.width : pageDimensions.height;
+ const pageHeight =
+ rotations % 2 === 0 ? pageDimensions.height : pageDimensions.width;
+ const noRotation = convertRotatedCoordinates(
+ noOffset, rotations, 0, pageWidth, pageHeight);
+
+ // Undo zoom.
+ return {
+ height: noRotation.height / zoom,
+ locationX: noRotation.locationX / zoom,
+ locationY: noRotation.locationY / zoom,
+ width: noRotation.width / zoom,
};
}
diff --git a/chrome/test/data/pdf/ink2_manager_test.ts b/chrome/test/data/pdf/ink2_manager_test.ts
index 8753340..9ccbfef 100644
--- a/chrome/test/data/pdf/ink2_manager_test.ts
+++ b/chrome/test/data/pdf/ink2_manager_test.ts
@@ -7,12 +7,12 @@
import {assert} from 'chrome://resources/js/assert.js';
import {eventToPromise} from 'chrome://webui-test/test_util.js';
-import {assertAnnotationBrush, assertDeepEquals, setGetAnnotationBrushReply, setupTestViewportAndMockPluginForInk} from './test_util.js';
+import {assertAnnotationBrush, assertDeepEquals, MockDocumentDimensions, setGetAnnotationBrushReply, setupTestViewportAndMockPluginForInk} from './test_util.js';
const {viewport, mockPlugin} = setupTestViewportAndMockPluginForInk();
const manager = Ink2Manager.getInstance();
-function getTestAnnotation(): TextAnnotation {
+function getTestAnnotation(id: number): TextAnnotation {
return {
textAttributes: {
typeface: TextTypeface.SANS_SERIF,
@@ -25,17 +25,51 @@
},
},
text: 'Hello World',
- id: 0,
+ id: id,
pageNumber: 0,
textBoxRect: {
- height: 50,
- locationX: 15,
+ height: 35,
+ locationX: 20,
locationY: 25,
width: 50,
},
+ textOrientation: 0,
};
}
+// Verifies that the plugin received a startTextAnnotation message for
+// annotation with id 0.
+function verifyStartTextAnnotationMessage(expected: boolean) {
+ const startTextAnnotationMessage =
+ mockPlugin.findMessage('startTextAnnotation');
+ chrome.test.assertEq(expected, startTextAnnotationMessage !== undefined);
+ if (expected) {
+ chrome.test.assertEq(
+ 'startTextAnnotation', startTextAnnotationMessage.type);
+ chrome.test.assertEq(0, startTextAnnotationMessage.data);
+ }
+}
+
+// Simulates the way the viewport is rotated from the plugin by setting updated
+// DocumentDimensions. Assumes a non-rotated pageWidth of 80 and pageHeight of
+// 100.
+function rotateViewport(orientation: number) {
+ const rotatedDocumentDimensions = new MockDocumentDimensions(0, 0);
+ // When the plugin notifies the viewport of new dimensions for a rotation,
+ // it swaps the width and height if the page is oriented sideways.
+ if (orientation === 0 || orientation === 2) {
+ rotatedDocumentDimensions.addPage(80, 100);
+ } else {
+ rotatedDocumentDimensions.addPage(100, 80);
+ }
+ rotatedDocumentDimensions.layoutOptions = {
+ defaultPageOrientation: orientation, // 90 degree CCW rotation
+ direction: 2, // LTR
+ twoUpViewEnabled: false,
+ };
+ viewport.setDocumentDimensions(rotatedDocumentDimensions);
+}
+
chrome.test.runTests([
async function testInitializeBrush() {
chrome.test.assertFalse(manager.isInitializationStarted());
@@ -207,9 +241,15 @@
},
async function testInitializeTextBox() {
+ // Create a new mock document dimensions that has different width from
+ // height. This is relevant for testing different rotations.
+ const documentDimensions = new MockDocumentDimensions(0, 0);
+ documentDimensions.addPage(80, 100);
+ viewport.setDocumentDimensions(documentDimensions);
+
// Add listeners for the expected events that fire in response to an
// initializeTextAnnotation call.
- const eventsDispatched: Array<{name: string, detail: any}> = [];
+ let eventsDispatched: Array<{name: string, detail: any}> = [];
['initialize-text-box', 'attributes-changed'].forEach(eventName => {
manager.addEventListener(eventName, e => {
eventsDispatched.push(
@@ -218,52 +258,65 @@
});
const attributes = manager.getCurrentTextAttributes();
- const whenUpdateEvent = eventToPromise('initialize-text-box', manager);
- Ink2Manager.getInstance().initializeTextAnnotation({x: 20, y: 23});
- await whenUpdateEvent;
- chrome.test.assertEq(2, eventsDispatched.length);
- chrome.test.assertEq('initialize-text-box', eventsDispatched[0]!.name);
- const initData = eventsDispatched[0]!.detail as TextBoxInit;
- chrome.test.assertEq('', initData.annotation.text);
- assertDeepEquals(attributes, initData.annotation.textAttributes);
- chrome.test.assertEq(
- DEFAULT_TEXTBOX_HEIGHT, initData.annotation.textBoxRect.height);
- chrome.test.assertEq(20, initData.annotation.textBoxRect.locationX);
- chrome.test.assertEq(23, initData.annotation.textBoxRect.locationY);
- chrome.test.assertEq(
- DEFAULT_TEXTBOX_WIDTH, initData.annotation.textBoxRect.width);
- chrome.test.assertEq(0, initData.annotation.pageNumber);
- chrome.test.assertEq(0, initData.annotation.id);
- // Placeholder viewport has a 90x90 page and 100x100 window. This creates
- // pageX and pageY offsets of 10px = (100 - 90)/2 + 5px and 3px
- // respectively.
- chrome.test.assertEq(10, initData.pageCoordinates.x);
- chrome.test.assertEq(3, initData.pageCoordinates.y);
- chrome.test.assertEq('attributes-changed', eventsDispatched[1]!.name);
- assertDeepEquals(attributes, eventsDispatched[1]!.detail);
+ async function verifyTextboxInit(
+ x: number, y: number, rotation: number, id: number) {
+ const whenUpdateEvent = eventToPromise('initialize-text-box', manager);
+ Ink2Manager.getInstance().initializeTextAnnotation({x, y});
+ await whenUpdateEvent;
+ chrome.test.assertEq(2, eventsDispatched.length);
+ chrome.test.assertEq('initialize-text-box', eventsDispatched[0]!.name);
+ const initData = eventsDispatched[0]!.detail as TextBoxInit;
+ chrome.test.assertEq('', initData.annotation.text);
+ assertDeepEquals(attributes, initData.annotation.textAttributes);
+ chrome.test.assertEq(
+ DEFAULT_TEXTBOX_HEIGHT, initData.annotation.textBoxRect.height);
+ chrome.test.assertEq(x, initData.annotation.textBoxRect.locationX);
+ chrome.test.assertEq(y, initData.annotation.textBoxRect.locationY);
+ chrome.test.assertEq(
+ DEFAULT_TEXTBOX_WIDTH, initData.annotation.textBoxRect.width);
+ chrome.test.assertEq(0, initData.annotation.pageNumber);
+ chrome.test.assertEq(id, initData.annotation.id);
+ chrome.test.assertEq(rotation, initData.annotation.textOrientation);
+ // Placeholder viewport has a 80x100 page and 100x100 window.
+ // The y offset is always 3px, because the page is always positioned
+ // 3px from the top. When the page is oriented vertically, it is centered
+ // in the viewport with an additional 5px margin in x, creating pageX =
+ // (100 - 80)/2 + 5 = 15px offset. When the page is oriented horizontally,
+ // it is as wide as the viewport, so it uses the minimum 5px margin for
+ // pageX.
+ chrome.test.assertEq(
+ rotation % 2 === 0 ? 15 : 5, initData.pageCoordinates.x);
+ chrome.test.assertEq(3, initData.pageCoordinates.y);
+ chrome.test.assertEq('attributes-changed', eventsDispatched[1]!.name);
+ assertDeepEquals(attributes, eventsDispatched[1]!.detail);
+ eventsDispatched = [];
- // Since this is a new annotation, it shouldn't have sent a message to the
- // plugin.
- const startTextAnnotationMessage =
- mockPlugin.findMessage('startTextAnnotation');
- chrome.test.assertEq(undefined, startTextAnnotationMessage);
+ // Since this is a new annotation, it shouldn't have sent a message to the
+ // plugin.
+ verifyStartTextAnnotationMessage(false);
+ }
+
+ // Test initialization in different positions and different viewport
+ // rotations. id should increment with each new textbox.
+ rotateViewport(/* clockwiseRotations= */ 3);
+ await verifyTextboxInit(/* x= */ 15, /* y= */ 10, /* rotations= */ 1,
+ /* id= */ 0);
+ rotateViewport(/* clockwiseRotations= */ 2);
+ await verifyTextboxInit(/* x= */ 50, /* y= */ 60, /* rotations= */ 2,
+ /* id= */ 1);
+ rotateViewport(/* clockwiseRotations= */ 1);
+ await verifyTextboxInit(/* x= */ 80, /* y = */ 20, /* rotations= */ 3,
+ /* id= */ 2);
+ rotateViewport(/* clockwiseRotations= */ 0);
+ await verifyTextboxInit(/* x= */ 20, /* y= */ 23, /* rotations= */ 0,
+ /* id= */ 3);
+
chrome.test.succeed();
},
function testCommitTextAnnotation() {
- // Listen for PluginControllerEventType.FINISH_INK_STROKE events. The
- // manager dispatches these on PluginController's eventTarget.
- let finishInkStrokeEvents = 0;
- PluginController.getInstance().getEventTarget().addEventListener(
- PluginControllerEventType.FINISH_INK_STROKE, () => {
- finishInkStrokeEvents++;
- });
-
- const annotationPageCoords = getTestAnnotation();
- // Adjust by the x and y offsets to get to page coordinates.
- annotationPageCoords.textBoxRect.locationX = 5;
- annotationPageCoords.textBoxRect.locationY = 22;
- function verifyFinishTextAnnotationMessage() {
+ function verifyFinishTextAnnotationMessage(
+ annotationPageCoords: TextAnnotation) {
const finishTextAnnotationMessage =
mockPlugin.findMessage('finishTextAnnotation');
chrome.test.assertTrue(finishTextAnnotationMessage !== undefined);
@@ -272,17 +325,103 @@
assertDeepEquals(annotationPageCoords, finishTextAnnotationMessage.data);
}
- // Committing with edited = true should fire an event.
- manager.commitTextAnnotation(getTestAnnotation(), true);
- chrome.test.assertEq(1, finishInkStrokeEvents);
- verifyFinishTextAnnotationMessage();
+ function testCommitAnnotation(
+ annotationScreenCoords: TextAnnotation,
+ annotationPageCoords: TextAnnotation) {
+ // Listen for PluginControllerEventType.FINISH_INK_STROKE events. The
+ // manager dispatches these on PluginController's eventTarget.
+ let finishInkStrokeEvents = 0;
+ PluginController.getInstance().getEventTarget().addEventListener(
+ PluginControllerEventType.FINISH_INK_STROKE, () => {
+ finishInkStrokeEvents++;
+ });
+
+ // Committing with edited = true should fire an event.
+ // Use structuredClone since the manager edits the object in place,
+ // and we want to reuse this below.
+ manager.commitTextAnnotation(
+ structuredClone(annotationScreenCoords), true);
+ chrome.test.assertEq(1, finishInkStrokeEvents);
+ verifyFinishTextAnnotationMessage(annotationPageCoords);
+ mockPlugin.clearMessages();
+
+ // Committing with edited = false should not fire an event.
+ manager.commitTextAnnotation(
+ structuredClone(annotationScreenCoords), false);
+ chrome.test.assertEq(1, finishInkStrokeEvents);
+ verifyFinishTextAnnotationMessage(annotationPageCoords);
+ mockPlugin.clearMessages();
+ }
+
+ // Test committing annotations at different rotations to ensure the
+ // conversion back to page coordinates works correctly. Note that the
+ // page screen rectangle will be 70x90 since there are 10px of page
+ // shadow.
+
+ // 90 degrees CCW
+ rotateViewport(/* clockwiseRotations= */ 3);
+ let annotationScreenCoords = getTestAnnotation(3);
+ let annotationPageCoords = getTestAnnotation(3);
+ annotationPageCoords.textBoxRect = {
+ height: 50,
+ width: 35,
+ locationX: 13,
+ locationY: 15,
+ };
+ testCommitAnnotation(annotationScreenCoords, annotationPageCoords);
+ // Delete to clear state.
+ annotationScreenCoords.text = '';
+ manager.commitTextAnnotation(annotationScreenCoords, true);
mockPlugin.clearMessages();
- // Committing with edited = false should not fire an event.
- manager.commitTextAnnotation(getTestAnnotation(), false);
- chrome.test.assertEq(1, finishInkStrokeEvents);
- verifyFinishTextAnnotationMessage();
+ // 180 degrees
+ rotateViewport(/* clockwiseRotations= */ 2);
+ annotationScreenCoords = getTestAnnotation(2);
+ annotationPageCoords = getTestAnnotation(2);
+ // Adjust by the x and y offsets to get to page coordinates.
+ annotationPageCoords.textBoxRect = {
+ height: 35,
+ width: 50,
+ locationX: 15,
+ locationY: 33,
+ };
+ testCommitAnnotation(annotationScreenCoords, annotationPageCoords);
+ // Delete to clear state.
+ annotationScreenCoords.text = '';
+ manager.commitTextAnnotation(annotationScreenCoords, true);
+ mockPlugin.clearMessages();
+ // 90 degrees CW
+ rotateViewport(/* clockwiseRotations= */ 1);
+ annotationScreenCoords = getTestAnnotation(1);
+ annotationPageCoords = getTestAnnotation(1);
+ // Adjust by the x and y offsets to get to page coordinates.
+ annotationPageCoords.textBoxRect = {
+ height: 50,
+ width: 35,
+ locationX: 22,
+ locationY: 25,
+ };
+ testCommitAnnotation(annotationScreenCoords, annotationPageCoords);
+ // Delete to clear state.
+ annotationScreenCoords.text = '';
+ manager.commitTextAnnotation(annotationScreenCoords, true);
+ mockPlugin.clearMessages();
+
+ // Normal orientation (0 degrees).
+ rotateViewport(/* clockwiseRotations= */ 0);
+ annotationScreenCoords = getTestAnnotation(0);
+ annotationPageCoords = getTestAnnotation(0);
+ // Adjust by the x and y offsets to get to page coordinates.
+ annotationPageCoords.textBoxRect = {
+ height: 35,
+ width: 50,
+ locationX: 5,
+ locationY: 22,
+ };
+ testCommitAnnotation(annotationScreenCoords, annotationPageCoords);
+ // Note: not deleting since we re-activate this annotation in the next
+ // test.
chrome.test.succeed();
},
@@ -304,9 +443,10 @@
chrome.test.assertEq(2, eventsDispatched.length);
chrome.test.assertEq('initialize-text-box', eventsDispatched[0]!.name);
const initData = eventsDispatched[0]!.detail as TextBoxInit;
- const testAnnotation = getTestAnnotation();
+ const testAnnotation = getTestAnnotation(0);
assertDeepEquals(testAnnotation, initData.annotation);
- chrome.test.assertEq(10, initData.pageCoordinates.x);
+ // Still using the 80x100 page from the previous test.
+ chrome.test.assertEq(15, initData.pageCoordinates.x);
chrome.test.assertEq(3, initData.pageCoordinates.y);
chrome.test.assertEq('attributes-changed', eventsDispatched[1]!.name);
assertDeepEquals(
@@ -314,12 +454,7 @@
// Since this is an existing annotation, it should send a start message to
// the plugin.
- const startTextAnnotationMessage =
- mockPlugin.findMessage('startTextAnnotation');
- chrome.test.assertTrue(startTextAnnotationMessage !== undefined);
- chrome.test.assertEq(
- 'startTextAnnotation', startTextAnnotationMessage.type);
- chrome.test.assertEq(0, startTextAnnotationMessage.data);
+ verifyStartTextAnnotationMessage(true);
chrome.test.succeed();
},
@@ -327,10 +462,26 @@
const initialParams = manager.getViewportParams();
chrome.test.assertEq(1.0, initialParams.zoom);
// pageMarginY * zoom = 3 * 1
- chrome.test.assertEq(3, initialParams.pageY);
+ chrome.test.assertEq(3, initialParams.pageDimensions.y);
// (windowWidth - docWidth * zoom)/2 + pageMarginX * zoom =
- // (100 - 90 * 1)/2 + 5 * 1
- chrome.test.assertEq(10, initialParams.pageX);
+ // (100 - 80 * 1)/2 + 5 * 1
+ chrome.test.assertEq(15, initialParams.pageDimensions.x);
+ // 10px of width are taken up by PAGE_SHADOW.
+ chrome.test.assertEq(70, initialParams.pageDimensions.width);
+ // 20px of height are also taken up by PAGE_SHADOW.
+ chrome.test.assertEq(90, initialParams.pageDimensions.height);
+ chrome.test.assertEq(0, initialParams.clockwiseRotations);
+
+ // In this new layout, the existing 50x35 annotation at page coordinate
+ // 5, 22 has its top left corner at 20, 25 in screen coordinates. Make
+ // sure clicking there creates the box, and clicking just outside of this
+ // does not.
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 20, y: 25});
+ verifyStartTextAnnotationMessage(true);
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 19, y: 24});
+ verifyStartTextAnnotationMessage(false);
// Zoom out should fire an event.
let whenViewportChanged = eventToPromise('viewport-changed', manager);
@@ -338,10 +489,22 @@
let changedEvent = await whenViewportChanged;
chrome.test.assertEq(0.5, changedEvent.detail.zoom);
// pageMarginY * zoom = 3 * .5
- chrome.test.assertEq(1.5, changedEvent.detail.pageY);
+ chrome.test.assertEq(1.5, changedEvent.detail.pageDimensions.y);
// (windowWidth - docWidth * zoom)/2 + pageMarginX * zoom =
- // (100 - 90 * .5)/2 + 5 * .5
- chrome.test.assertEq(30, changedEvent.detail.pageX);
+ // (100 - 80 * .5)/2 + 5 * .5
+ chrome.test.assertEq(32.5, changedEvent.detail.pageDimensions.x);
+ chrome.test.assertEq(35, changedEvent.detail.pageDimensions.width);
+ chrome.test.assertEq(45, changedEvent.detail.pageDimensions.height);
+ chrome.test.assertEq(0, changedEvent.detail.clockwiseRotations);
+
+ // In this new layout, the existing 50x35 annotation at page coordinate
+ // 5, 22 has its top left corner at 35, 12.5 in screen coordinates.
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 35, y: 13});
+ verifyStartTextAnnotationMessage(true);
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 34, y: 12});
+ verifyStartTextAnnotationMessage(false);
// Zoom in should fire an event.
whenViewportChanged = eventToPromise('viewport-changed', manager);
@@ -349,9 +512,21 @@
changedEvent = await whenViewportChanged;
chrome.test.assertEq(2, changedEvent.detail.zoom);
// pageMarginY * zoom = 3 * 2
- chrome.test.assertEq(6, changedEvent.detail.pageY);
+ chrome.test.assertEq(6, changedEvent.detail.pageDimensions.y);
// docWidth * zoom > windowWidth, so this is now pageMarginX * zoom = 5 * 2
- chrome.test.assertEq(10, changedEvent.detail.pageX);
+ chrome.test.assertEq(10, changedEvent.detail.pageDimensions.x);
+ chrome.test.assertEq(140, changedEvent.detail.pageDimensions.width);
+ chrome.test.assertEq(180, changedEvent.detail.pageDimensions.height);
+ chrome.test.assertEq(0, changedEvent.detail.clockwiseRotations);
+
+ // In this new layout, the existing 50x35 annotation at page coordinate
+ // 5, 22 has its top left corner at 25, 50 in screen coordinates.
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 25, y: 50});
+ verifyStartTextAnnotationMessage(true);
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 24, y: 49});
+ verifyStartTextAnnotationMessage(false);
// Translation.
whenViewportChanged = eventToPromise('viewport-changed', manager);
@@ -359,9 +534,48 @@
changedEvent = await whenViewportChanged;
chrome.test.assertEq(2, changedEvent.detail.zoom);
// Shifts by -20 * zoom = -40 from previous position.
- chrome.test.assertEq(-34, changedEvent.detail.pageY);
+ chrome.test.assertEq(-34, changedEvent.detail.pageDimensions.y);
// Shifts by -20 * zoom = -40 from previous position.
- chrome.test.assertEq(-30, changedEvent.detail.pageX);
+ chrome.test.assertEq(-30, changedEvent.detail.pageDimensions.x);
+ chrome.test.assertEq(140, changedEvent.detail.pageDimensions.width);
+ chrome.test.assertEq(180, changedEvent.detail.pageDimensions.height);
+ chrome.test.assertEq(0, changedEvent.detail.clockwiseRotations);
+
+ // In this new layout, the existing 50x35 annotation at page coordinate
+ // 5, 22 has its top left corner at -15, 10 in screen coordinates.
+ // It has width 100 and height 70 so (0, 81) should be just outside the box
+ // and (0, 80) just inside.
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 0, y: 80});
+ verifyStartTextAnnotationMessage(true);
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 0, y: 81});
+ verifyStartTextAnnotationMessage(false);
+
+ // Rotation
+ whenViewportChanged = eventToPromise('viewport-changed', manager);
+ rotateViewport(/* clockwiseRotations= */ 3); // 90 degree CCW rotation.
+ changedEvent = await whenViewportChanged;
+ chrome.test.assertEq(2, changedEvent.detail.zoom);
+ chrome.test.assertEq(-34, changedEvent.detail.pageDimensions.y);
+ chrome.test.assertEq(-30, changedEvent.detail.pageDimensions.x);
+ // Width and height are switched.
+ chrome.test.assertEq(180, changedEvent.detail.pageDimensions.width);
+ chrome.test.assertEq(140, changedEvent.detail.pageDimensions.height);
+ // Rotations now non-zero.
+ chrome.test.assertEq(3, changedEvent.detail.clockwiseRotations);
+
+ // In this new layout, the existing 50x35 annotation at page coordinate
+ // 5, 22 has its top left corner at 14, -4 in screen coordinates.
+ // It has width 70 and height 100 so (85, 0) should be just outside the box
+ // and (84, 0) just inside.
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 84, y: 0});
+ verifyStartTextAnnotationMessage(true);
+ mockPlugin.clearMessages();
+ Ink2Manager.getInstance().initializeTextAnnotation({x: 85, y: 0});
+ verifyStartTextAnnotationMessage(false);
+
chrome.test.succeed();
},
]);
diff --git a/chrome/test/data/pdf/ink2_text_box_test.ts b/chrome/test/data/pdf/ink2_text_box_test.ts
index f52418e..cacbc04b 100644
--- a/chrome/test/data/pdf/ink2_text_box_test.ts
+++ b/chrome/test/data/pdf/ink2_text_box_test.ts
@@ -15,7 +15,8 @@
document.body.appendChild(textbox);
function initializeBox(
- width: number, height: number, x: number, y: number, existing?: boolean) {
+ width: number, height: number, x: number, y: number, existing?: boolean,
+ orientation?: number) {
manager.dispatchEvent(new CustomEvent('initialize-text-box', {
detail: {
annotation: {
@@ -31,6 +32,7 @@
color: hexToColor(TEXT_COLORS[0]!.color),
},
textBoxRect: {height, locationX: x, locationY: y, width},
+ textOrientation: orientation ? orientation : 0,
id: 0,
pageNumber: 0,
},
@@ -325,8 +327,13 @@
// Simulate a zoom change to 0.5. This also comes with x and y changes
// simulating production.
- manager.dispatchEvent(new CustomEvent(
- 'viewport-changed', {detail: {pageX: 30, pageY: 1.5, zoom: 0.5}}));
+ manager.dispatchEvent(new CustomEvent('viewport-changed', {
+ detail: {
+ clockwiseRotations: 0,
+ pageDimensions: {x: 30, y: 1.5, width: 45, height: 45},
+ zoom: 0.5,
+ },
+ }));
await microtasksFinished();
assertPositionAndSize(textbox, '50px', '50px', '230px', '151.5px');
chrome.test.assertEq(
@@ -335,8 +342,13 @@
// Simulate a zoom change to 2.0. This also comes with x and y changes
// simulating production.
- manager.dispatchEvent(new CustomEvent(
- 'viewport-changed', {detail: {pageX: 10, pageY: 6, zoom: 2.0}}));
+ manager.dispatchEvent(new CustomEvent('viewport-changed', {
+ detail: {
+ clockwiseRotations: 0,
+ pageDimensions: {x: 10, y: 6, width: 180, height: 180},
+ zoom: 2.0,
+ },
+ }));
await microtasksFinished();
assertPositionAndSize(textbox, '200px', '200px', '810px', '606px');
chrome.test.assertEq(
@@ -344,8 +356,13 @@
getComputedStyle(textbox.$.textbox).getPropertyValue('font-size'));
// Simulate a scroll + resetting zoom to 1.0.
- manager.dispatchEvent(new CustomEvent(
- 'viewport-changed', {detail: {pageX: 100, pageY: 100, zoom: 1.0}}));
+ manager.dispatchEvent(new CustomEvent('viewport-changed', {
+ detail: {
+ clockwiseRotations: 0,
+ pageDimensions: {x: 100, y: 100, width: 90, height: 90},
+ zoom: 1.0,
+ },
+ }));
await microtasksFinished();
assertPositionAndSize(textbox, '100px', '100px', '500px', '400px');
chrome.test.assertEq(
@@ -353,8 +370,13 @@
getComputedStyle(textbox.$.textbox).getPropertyValue('font-size'));
// Scroll where start of page is no longer in the viewport.
- manager.dispatchEvent(new CustomEvent(
- 'viewport-changed', {detail: {pageX: -100, pageY: -100, zoom: 1.0}}));
+ manager.dispatchEvent(new CustomEvent('viewport-changed', {
+ detail: {
+ clockwiseRotations: 0,
+ pageDimensions: {x: -100, y: -100, width: 90, height: 90},
+ zoom: 1.0,
+ },
+ }));
await microtasksFinished();
assertPositionAndSize(textbox, '100px', '100px', '300px', '200px');
chrome.test.assertEq(
@@ -362,8 +384,13 @@
getComputedStyle(textbox.$.textbox).getPropertyValue('font-size'));
// Scroll where textbox ends up off screen.
- manager.dispatchEvent(new CustomEvent(
- 'viewport-changed', {detail: {pageX: -500, pageY: -500, zoom: 1.0}}));
+ manager.dispatchEvent(new CustomEvent('viewport-changed', {
+ detail: {
+ clockwiseRotations: 0,
+ pageDimensions: {x: -500, y: -500, width: 90, height: 90},
+ zoom: 1.0,
+ },
+ }));
await microtasksFinished();
assertPositionAndSize(textbox, '100px', '100px', '-100px', '-200px');
chrome.test.assertEq(
@@ -372,6 +399,134 @@
chrome.test.succeed();
},
+ async function testViewportRotationChanges() {
+ // Custom init with different x offsets to simulate a rectangular page with
+ // rotations.
+ function initializeBoxWithOrientation(
+ width: number, height: number, x: number, y: number,
+ orientation: number) {
+ manager.dispatchEvent(new CustomEvent('initialize-text-box', {
+ detail: {
+ annotation: {
+ text: '',
+ textAttributes: {
+ size: 12,
+ typeface: TextTypeface.SANS_SERIF,
+ styles: {
+ [TextStyle.BOLD]: false,
+ [TextStyle.ITALIC]: false,
+ },
+ alignment: TextAlignment.LEFT,
+ color: hexToColor(TEXT_COLORS[0]!.color),
+ },
+ textBoxRect: {height, locationX: x, locationY: y, width},
+ textOrientation: orientation,
+ id: 0,
+ pageNumber: 0,
+ },
+ pageCoordinates: orientation % 2 === 0 ? {x: 15, y: 3} : {x: 5, y: 3},
+ },
+ }));
+ }
+
+ // Helper to update the viewport to the specified number of clockwise
+ // rotations.
+ function updateViewportWithClockwiseRotations(rotations: number):
+ Promise<void> {
+ // Simulating real viewport changes. The x offset reduces when the
+ // page is flipped horizontally, since it takes the whole window.
+ // width and height flip when the page is horizontal.
+ const x = rotations % 2 === 0 ? 15 : 5;
+ const width = rotations % 2 === 0 ? 80 : 100;
+ const height = rotations % 2 === 0 ? 100 : 80;
+ manager.dispatchEvent(new CustomEvent('viewport-changed', {
+ detail: {
+ clockwiseRotations: rotations,
+ pageDimensions: {x, y: 3, width, height},
+ zoom: 1.0,
+ },
+ }));
+ return microtasksFinished();
+ }
+
+ // Helper to check that the textbox styles match the expected rotation of
+ // the text (in number of 90 degree clockwise rotations).
+ function assertTextboxStyles(expectedTextRotation: number) {
+ const expectedTransform =
+ expectedTextRotation === 2 ? 'matrix(-1, 0, 0, -1, 0, 0)' : 'none';
+ let expectedWritingMode = 'horizontal-tb';
+ if (expectedTextRotation === 1) {
+ expectedWritingMode = 'vertical-rl';
+ } else if (expectedTextRotation === 3) {
+ expectedWritingMode = 'sideways-lr';
+ }
+ const styles = getComputedStyle(textbox.$.textbox);
+ chrome.test.assertEq(
+ expectedTransform, styles.getPropertyValue('transform'));
+ chrome.test.assertEq(
+ expectedWritingMode, styles.getPropertyValue('writing-mode'));
+ }
+
+ // Initialize to a 50x48 box at 20, 30 + page offsets. Make box rotated
+ // by 90 degrees clockwise compared to the PDF. This happens when the
+ // viewport is rotated by 90 degrees CCW and the user creates a new
+ // annotation, so simulate that scenario here.
+ await updateViewportWithClockwiseRotations(3);
+ initializeBoxWithOrientation(50, 48, 25, 33, 1);
+ await microtasksFinished();
+ // Position and size are in viewport coordinates, so the box is 50x48 in
+ // the rotated viewport.
+ assertPositionAndSize(textbox, '50px', '48px', '25px', '33px');
+ // Textbox is non-rotated relative to the current viewport orientation.
+ assertTextboxStyles(0);
+
+ await updateViewportWithClockwiseRotations(0);
+ assertPositionAndSize(textbox, '48px', '50px', '17px', '23px');
+ assertTextboxStyles(1);
+
+ await updateViewportWithClockwiseRotations(1);
+ assertPositionAndSize(textbox, '50px', '48px', '35px', '5px');
+ assertTextboxStyles(2);
+
+ await updateViewportWithClockwiseRotations(2);
+ assertPositionAndSize(textbox, '48px', '50px', '45px', '33px');
+ assertTextboxStyles(3);
+
+ // Back to the original position, size and style since we've now rotated
+ // all the way around.
+ await updateViewportWithClockwiseRotations(3);
+ assertPositionAndSize(textbox, '50px', '48px', '25px', '33px');
+ assertTextboxStyles(0);
+
+ // Now initialize a box with no rotation relative to the PDF, at the same
+ // location. This happens when the viewport has no rotation when the box is
+ // created.
+ await updateViewportWithClockwiseRotations(0);
+ initializeBoxWithOrientation(50, 48, 35, 33, 0);
+ await microtasksFinished();
+ assertPositionAndSize(textbox, '50px', '48px', '35px', '33px');
+ assertTextboxStyles(0);
+
+ await updateViewportWithClockwiseRotations(1);
+ assertPositionAndSize(textbox, '48px', '50px', '27px', '23px');
+ assertTextboxStyles(1);
+
+ await updateViewportWithClockwiseRotations(2);
+ assertPositionAndSize(textbox, '50px', '48px', '25px', '25px');
+ assertTextboxStyles(2);
+
+ await updateViewportWithClockwiseRotations(3);
+ assertPositionAndSize(textbox, '48px', '50px', '35px', '13px');
+ assertTextboxStyles(3);
+
+ // Back to 0 rotation should get us back to the original location and style.
+ await updateViewportWithClockwiseRotations(0);
+ assertPositionAndSize(textbox, '50px', '48px', '35px', '33px');
+ assertTextboxStyles(0);
+
+ chrome.test.succeed();
+ },
+
async function testCommit() {
// Initialize to a 100x100 box at 400, 300.
initializeBox(100, 100, 400, 300);
@@ -411,6 +566,7 @@
},
// Messages to the backend are in page coordinates.
textBoxRect: {locationX: 195, locationY: 147, height: 50, width: 50},
+ textOrientation: 0,
};
function startNewAnnotationAndVerifyMessage(existing: boolean = false) {