From 1d06e46a5d1ddae4da256da43c01ce72a150ab71 Mon Sep 17 00:00:00 2001 From: Jack Reed <phillipjreed@gmail.com> Date: Wed, 22 Apr 2020 14:14:38 -0600 Subject: [PATCH] Support Annotations in bookview for right-to-left viewing fixes #2956 --- __tests__/integration/mirador/svg_annos.html | 2 +- .../src/lib/CanvasAnnotationDisplay.test.js | 6 +++ __tests__/src/lib/CanvasWorld.test.js | 27 +++++++------ src/components/OpenSeadragonViewer.js | 4 +- src/lib/CanvasAnnotationDisplay.js | 4 ++ src/lib/CanvasWorld.js | 39 ++++++++++++------- 6 files changed, 55 insertions(+), 27 deletions(-) diff --git a/__tests__/integration/mirador/svg_annos.html b/__tests__/integration/mirador/svg_annos.html index 201cb24af..71d87cfba 100644 --- a/__tests__/integration/mirador/svg_annos.html +++ b/__tests__/integration/mirador/svg_annos.html @@ -19,7 +19,7 @@ }, { manifestId: 'https://iiif.bodleian.ox.ac.uk/iiif/manifest/748a9d50-5a3a-440e-ab9d-567dd68b6abb.json', - canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/4a4d7347-0c32-4e3d-b517-5042eba06c25.json', + canvasIndex: 6, } ], window: { diff --git a/__tests__/src/lib/CanvasAnnotationDisplay.test.js b/__tests__/src/lib/CanvasAnnotationDisplay.test.js index e122100e8..16914ca66 100644 --- a/__tests__/src/lib/CanvasAnnotationDisplay.test.js +++ b/__tests__/src/lib/CanvasAnnotationDisplay.test.js @@ -55,13 +55,19 @@ describe('CanvasAnnotationDisplay', () => { describe('svgContext', () => { it('draws the paths with selected arguments', () => { const context = { + restore: jest.fn(), + save: jest.fn(), stroke: jest.fn(), + translate: jest.fn(), }; const subject = createSubject({ resource: new AnnotationResource(dualStrategyAnno), }); subject.svgContext(context); expect(context.stroke).toHaveBeenCalledWith({}); + expect(context.save).toHaveBeenCalledWith(); + expect(context.restore).toHaveBeenCalledWith(); + expect(context.translate).toHaveBeenCalledWith(-100, 0); expect(context.strokeStyle).toEqual('blue'); expect(context.lineWidth).toEqual(20); }); diff --git a/__tests__/src/lib/CanvasWorld.test.js b/__tests__/src/lib/CanvasWorld.test.js index 263d66fb6..10a2719bc 100644 --- a/__tests__/src/lib/CanvasWorld.test.js +++ b/__tests__/src/lib/CanvasWorld.test.js @@ -17,15 +17,27 @@ describe('CanvasWorld', () => { expect(new CanvasWorld(canvasSubset).worldBounds()).toEqual([0, 0, 9153, 4288]); }); }); + describe('contentResourceToWorldCoordinates', () => { + it('converts canvas coordinates to world offset by location', () => { + expect(new CanvasWorld([canvases[1]]).contentResourceToWorldCoordinates({ id: 'https://stacks.stanford.edu/image/iiif/fr426cg9537%2FSC1094_s3_b14_f17_Cats_1976_0005/full/full/0/default.jpg' })) + .toEqual([0, 0, 6501, 4421]); + expect(new CanvasWorld(canvasSubset).contentResourceToWorldCoordinates({ id: 'https://stacks.stanford.edu/image/iiif/rz176rt6531%2FPC0170_s3_Tree_Calendar_20081101_152516_0410/full/full/0/default.jpg' })) + .toEqual([6305, 0, 2848, 4288]); + }); + it('supports RTL orientations', () => { + expect(new CanvasWorld(canvasSubset, null, 'right-to-left').contentResourceToWorldCoordinates({ id: 'https://stacks.stanford.edu/image/iiif/rz176rt6531%2FPC0170_s3_Tree_Calendar_20081101_152516_0410/full/full/0/default.jpg' })) + .toEqual([0, 0, 2848, 4288]); + }); + }); describe('canvasToWorldCoordinates', () => { it('converts canvas coordinates to world offset by location', () => { - expect(new CanvasWorld([canvases[1]]).canvasToWorldCoordinates({ id: 'https://stacks.stanford.edu/image/iiif/fr426cg9537%2FSC1094_s3_b14_f17_Cats_1976_0005/full/full/0/default.jpg' })) + expect(new CanvasWorld([canvases[1]]).canvasToWorldCoordinates('https://purl.stanford.edu/fr426cg9537/iiif/canvas/fr426cg9537_1')) .toEqual([0, 0, 6501, 4421]); - expect(new CanvasWorld(canvasSubset).canvasToWorldCoordinates({ id: 'https://stacks.stanford.edu/image/iiif/rz176rt6531%2FPC0170_s3_Tree_Calendar_20081101_152516_0410/full/full/0/default.jpg' })) + expect(new CanvasWorld(canvasSubset).canvasToWorldCoordinates('https://purl.stanford.edu/rz176rt6531/iiif/canvas/rz176rt6531_1')) .toEqual([6305, 0, 2848, 4288]); }); it('supports RTL orientations', () => { - expect(new CanvasWorld(canvasSubset, null, 'right-to-left').canvasToWorldCoordinates({ id: 'https://stacks.stanford.edu/image/iiif/rz176rt6531%2FPC0170_s3_Tree_Calendar_20081101_152516_0410/full/full/0/default.jpg' })) + expect(new CanvasWorld(canvasSubset, null, 'right-to-left').canvasToWorldCoordinates('https://purl.stanford.edu/rz176rt6531/iiif/canvas/rz176rt6531_1')) .toEqual([0, 0, 2848, 4288]); }); }); @@ -36,14 +48,7 @@ describe('CanvasWorld', () => { ).toEqual({ x: 0, y: 0 }); expect( new CanvasWorld(canvasSubset).offsetByCanvas('https://purl.stanford.edu/rz176rt6531/iiif/canvas/rz176rt6531_1'), - ).toEqual({ x: 6501, y: 0 }); - }); - }); - describe('indexOfTarget', () => { - it('returns the index of a target in canvases', () => { - expect( - new CanvasWorld(canvasSubset).indexOfTarget('https://purl.stanford.edu/rz176rt6531/iiif/canvas/rz176rt6531_1'), - ).toEqual(1); + ).toEqual({ x: 6305, y: 0 }); }); }); diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js index 08e522071..3ab53dbbf 100644 --- a/src/components/OpenSeadragonViewer.js +++ b/src/components/OpenSeadragonViewer.js @@ -226,7 +226,7 @@ export class OpenSeadragonViewer extends Component { this.viewer.addSimpleImage({ error: event => reject(event), fitBounds: new OpenSeadragon.Rect( - ...canvasWorld.canvasToWorldCoordinates(contentResource), + ...canvasWorld.contentResourceToWorldCoordinates(contentResource), ), index: canvasWorld.layerIndexOfImageResource(contentResource), opacity: canvasWorld.layerOpacityOfImageResource(contentResource), @@ -253,7 +253,7 @@ export class OpenSeadragonViewer extends Component { this.viewer.addTiledImage({ error: event => reject(event), fitBounds: new OpenSeadragon.Rect( - ...canvasWorld.canvasToWorldCoordinates(contentResource), + ...canvasWorld.contentResourceToWorldCoordinates(contentResource), ), index: canvasWorld.layerIndexOfImageResource(contentResource), opacity: canvasWorld.layerOpacityOfImageResource(contentResource), diff --git a/src/lib/CanvasAnnotationDisplay.js b/src/lib/CanvasAnnotationDisplay.js index 72ccbcd5a..454c91cb5 100644 --- a/src/lib/CanvasAnnotationDisplay.js +++ b/src/lib/CanvasAnnotationDisplay.js @@ -36,6 +36,8 @@ export default class CanvasAnnotationDisplay { * TODO: Support multi canvas offset * One example: https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath */ + context.save(); + context.translate(this.offset.x, this.offset.y); const p = new Path2D(element.attributes.d.nodeValue); /** * Note: we could do something to return the svg styling attributes as @@ -47,6 +49,7 @@ export default class CanvasAnnotationDisplay { context.strokeStyle = this.color; // eslint-disable-line no-param-reassign context.lineWidth = this.lineWidth(); // eslint-disable-line no-param-reassign context.stroke(p); + context.restore(); }); } @@ -54,6 +57,7 @@ export default class CanvasAnnotationDisplay { fragmentContext(context) { const fragment = this.resource.fragmentSelector; fragment[0] += this.offset.x; + fragment[1] += this.offset.y; context.strokeStyle = this.color; // eslint-disable-line no-param-reassign context.lineWidth = this.lineWidth(); // eslint-disable-line no-param-reassign context.strokeRect(...fragment); diff --git a/src/lib/CanvasWorld.js b/src/lib/CanvasWorld.js index ab44caade..845c79296 100644 --- a/src/lib/CanvasWorld.js +++ b/src/lib/CanvasWorld.js @@ -21,10 +21,10 @@ export default class CanvasWorld { } /** - * canvasToWorldCoordinates - calculates the canvas coordinates respective to - * the world. + * contentResourceToWorldCoordinates - calculates the contentResource coordinates + * respective to the world. */ - canvasToWorldCoordinates(contentResource) { + contentResourceToWorldCoordinates(contentResource) { const wholeBounds = this.worldBounds(); const manifestoCanvasIndex = this.canvases.findIndex(c => ( c.imageResources.find(r => r.id === contentResource.id) @@ -43,6 +43,24 @@ export default class CanvasWorld { ]; } + /** */ + canvasToWorldCoordinates(canvasId) { + const wholeBounds = this.worldBounds(); + const manifestoCanvasIndex = this.canvases.findIndex(c => (c.id === canvasId)); + const { aspectRatio } = this.canvases[manifestoCanvasIndex]; + const scaledWidth = Math.floor(wholeBounds[3] * aspectRatio); + let x = 0; + if (manifestoCanvasIndex === this.secondCanvasIndex) { + x = wholeBounds[2] - scaledWidth; + } + return [ + x, + 0, + scaledWidth, + wholeBounds[3], + ]; + } + /** * secondCanvasIndex - index of the second canvas used for determining which * is first @@ -51,10 +69,6 @@ export default class CanvasWorld { return this.viewingDirection === 'right-to-left' ? 0 : 1; } - /** */ - indexOfTarget(canvasTarget) { - return this.canvases.map(canvas => canvas.id).indexOf(canvasTarget); - } /** Get the IIIF content resource for an image */ contentResource(infoResponseId) { @@ -115,12 +129,11 @@ export default class CanvasWorld { * assumes a horrizontal only layout. */ offsetByCanvas(canvasTarget) { - const offset = { x: 0, y: 0 }; - let i; - for (i = 0; i < this.indexOfTarget(canvasTarget); i += 1) { - offset.x += this.canvases[i].getWidth(); - } - return offset; + const coordinates = this.canvasToWorldCoordinates(canvasTarget); + return { + x: coordinates[0], + y: coordinates[1], + }; } /** -- GitLab