aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf/Page.tsx
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2019-08-06 14:08:59 -0400
committerbob <bcz@cs.brown.edu>2019-08-06 14:08:59 -0400
commit9419693f37ad53fcc0763a118d2cb865659a2ff4 (patch)
treee2743c6905caa688520a53c531a160f22e2f13f0 /src/client/views/pdf/Page.tsx
parent298d1c9b29d6ce2171fd9ac8274b64583b73f6f5 (diff)
multiple changes to PDF related code -- mostly clean up
Diffstat (limited to 'src/client/views/pdf/Page.tsx')
-rw-r--r--src/client/views/pdf/Page.tsx246
1 files changed, 61 insertions, 185 deletions
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index dea4e0da1..1da41bca1 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -3,12 +3,9 @@ import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import { Doc, DocListCastAsync, Opt, WidthSym } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { Docs, DocUtils } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
-import { PDFBox } from "../nodes/PDFBox";
import PDFMenu from "./PDFMenu";
import { scale } from "./PDFViewer";
import "./PDFViewer.scss";
@@ -17,14 +14,14 @@ import React = require("react");
interface IPageProps {
size: { width: number, height: number };
- pdf: Opt<Pdfjs.PDFDocumentProxy>;
+ pdf: Pdfjs.PDFDocumentProxy;
name: string;
numPages: number;
page: number;
- pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void;
- parent: PDFBox;
+ pageLoaded: (page: Pdfjs.PDFPageViewport) => void;
+ fieldExtensionDoc: Doc,
+ Document: Doc,
renderAnnotations: (annotations: Doc[], removeOld: boolean) => void;
- makePin: (x: number, y: number, page: number) => void;
sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
createAnnotation: (div: HTMLDivElement, page: number) => void;
makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string, linkTo: boolean) => Doc;
@@ -34,7 +31,7 @@ interface IPageProps {
@observer
export default class Page extends React.Component<IPageProps> {
- @observable private _state: string = "N/A";
+ @observable private _state: "N/A" | "rendering" = "N/A";
@observable private _width: number = this.props.size.width;
@observable private _height: number = this.props.size.height;
@observable private _page: Opt<Pdfjs.PDFPageProxy>;
@@ -43,90 +40,54 @@ export default class Page extends React.Component<IPageProps> {
@observable private _marqueeY: number = 0;
@observable private _marqueeWidth: number = 0;
@observable private _marqueeHeight: number = 0;
- @observable private _rotate: string = "";
- private _canvas: React.RefObject<HTMLCanvasElement>;
- private _textLayer: React.RefObject<HTMLDivElement>;
- private _annotationLayer: React.RefObject<HTMLDivElement>;
- private _marquee: React.RefObject<HTMLDivElement>;
- // private _curly: React.RefObject<HTMLImageElement>;
+ private _canvas: React.RefObject<HTMLCanvasElement> = React.createRef();
+ private _textLayer: React.RefObject<HTMLDivElement> = React.createRef();
+ private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+ private _marquee: React.RefObject<HTMLDivElement> = React.createRef();
private _marqueeing: boolean = false;
private _reactionDisposer?: IReactionDisposer;
private _startY: number = 0;
private _startX: number = 0;
- constructor(props: IPageProps) {
- super(props);
- this._canvas = React.createRef();
- this._textLayer = React.createRef();
- this._annotationLayer = React.createRef();
- this._marquee = React.createRef();
- // this._curly = React.createRef();
- }
-
componentDidMount = (): void => {
- if (this.props.pdf) {
- this.update(this.props.pdf);
- }
- }
-
- componentWillUnmount = (): void => {
- if (this._reactionDisposer) {
- this._reactionDisposer();
- }
+ this.loadPage(this.props.pdf);
}
componentDidUpdate = (): void => {
- if (this.props.pdf) {
- this.update(this.props.pdf);
- }
+ this.loadPage(this.props.pdf);
}
- private update = (pdf: Pdfjs.PDFDocumentProxy): void => {
- if (pdf) {
- this.loadPage(pdf);
- }
- else {
- this._state = "loading";
- }
+ componentWillUnmount = (): void => {
+ this._reactionDisposer && this._reactionDisposer();
}
private loadPage = (pdf: Pdfjs.PDFDocumentProxy): void => {
- if (this._state === "rendering" || this._page) return;
-
- pdf.getPage(this._currPage).then(
+ this._state !== "rendering" && !this._page && pdf.getPage(this._currPage).then(
(page: Pdfjs.PDFPageProxy): void => {
this._state = "rendering";
this.renderPage(page);
- }
- );
+ });
}
@action
private renderPage = (page: Pdfjs.PDFPageProxy): void => {
// lower scale = easier to read at small sizes, higher scale = easier to read at large sizes
- let viewport = page.getViewport(scale);
- let canvas = this._canvas.current;
- let textLayer = this._textLayer.current;
- if (canvas && textLayer) {
- let ctx = canvas.getContext("2d");
- canvas.width = viewport.width;
- this._width = viewport.width;
- canvas.height = viewport.height;
- this._height = viewport.height;
- this.props.pageLoaded(this._currPage, viewport);
+ if (this._canvas.current && this._textLayer.current) {
+ let viewport = page.getViewport(scale);
+ this._canvas.current.width = this._width = viewport.width;
+ this._canvas.current.height = this._height = viewport.height;
+ this.props.pageLoaded(viewport);
+ let ctx = this._canvas.current.getContext("2d");
if (ctx) {
- // renders the page onto the canvas context
- page.render({ canvasContext: ctx, viewport: viewport });
- // renders text onto the text container
- page.getTextContent().then((res: Pdfjs.TextContent): void => {
+ page.render({ canvasContext: ctx, viewport: viewport }); // renders the page onto the canvas context
+ page.getTextContent().then(res => // renders text onto the text container
//@ts-ignore
Pdfjs.renderTextLayer({
textContent: res,
- container: textLayer,
+ container: this._textLayer.current,
viewport: viewport
- });
- });
+ }));
this._page = page;
}
@@ -137,12 +98,8 @@ export default class Page extends React.Component<IPageProps> {
highlight = (targetDoc?: Doc, color: string = "red") => {
// creates annotation documents for current highlights
let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color, false);
- let targetAnnotations = Cast(this.props.parent.fieldExtensionDoc.annotations, listSpec(Doc));
- if (targetAnnotations === undefined) {
- Doc.GetProto(this.props.parent.fieldExtensionDoc).annotations = new List([annotationDoc]);
- } else {
- targetAnnotations.push(annotationDoc);
- }
+ Doc.GetProto(annotationDoc).annotationOn = this.props.Document;
+ Doc.AddDocToList(this.props.fieldExtensionDoc, "annotations", annotationDoc);
return annotationDoc;
}
@@ -154,16 +111,12 @@ export default class Page extends React.Component<IPageProps> {
startDrag = (e: PointerEvent, ele: HTMLElement): void => {
e.preventDefault();
e.stopPropagation();
- let thisDoc = this.props.parent.Document;
- // document that this annotation is linked to
- let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "New Annotation" });
- targetDoc.targetPage = this.props.page;
- let annotationDoc = this.highlight(undefined, "red");
- Doc.GetProto(annotationDoc).annotationOn = thisDoc;
- annotationDoc.linkedToDoc = false;
- // create dragData and star tdrag
- let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc);
if (this._textLayer.current) {
+ let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "New Annotation" });
+ targetDoc.targetPage = this.props.page;
+ let annotationDoc = this.highlight(undefined, "red");
+ annotationDoc.linkedToDoc = false;
+ let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
handlers: {
dragComplete: async () => {
@@ -171,9 +124,7 @@ export default class Page extends React.Component<IPageProps> {
let annotations = await DocListCastAsync(annotationDoc.annotations);
annotations && annotations.forEach(anno => anno.target = targetDoc);
let pdfDoc = await Cast(annotationDoc.pdfDoc, Doc);
- if (pdfDoc) {
- DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title));
- }
+ pdfDoc && DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title))
}
}
},
@@ -184,24 +135,20 @@ export default class Page extends React.Component<IPageProps> {
// cleans up events and boolean
endDrag = (e: PointerEvent): void => {
- // document.removeEventListener("pointermove", this.startDrag);
- // document.removeEventListener("pointerup", this.endDrag);
e.stopPropagation();
}
createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => {
- let doc = this.props.parent.Document;
- let view = Doc.MakeAlias(doc);
- let data = Doc.MakeDelegate(doc.proto!);
+ let view = Doc.MakeAlias(this.props.Document);
+ let data = Doc.MakeDelegate(Doc.GetProto(this.props.Document));
data.title = StrCast(data.title) + "_snippet";
view.proto = data;
view.nativeHeight = marquee.height;
- view.height = (doc[WidthSym]() / NumCast(doc.nativeWidth)) * marquee.height;
- view.nativeWidth = doc.nativeWidth;
+ view.height = (this.props.Document[WidthSym]() / NumCast(this.props.Document.nativeWidth)) * marquee.height;
+ view.nativeWidth = this.props.Document.nativeWidth;
view.startY = marquee.top + this.props.getScrollFromPage(this.props.page);
- view.width = doc[WidthSym]();
- let dragData = new DragManager.DocumentDragData([view], [undefined]);
- DragManager.StartDocumentDrag([], dragData, 0, 0);
+ view.width = this.props.Document[WidthSym]();
+ DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view], [undefined]), 0, 0);
}
@action
@@ -209,11 +156,6 @@ export default class Page extends React.Component<IPageProps> {
// if alt+left click, drag and annotate
if (e.altKey && e.button === 0) {
e.stopPropagation();
-
- // document.removeEventListener("pointermove", this.startDrag);
- // document.addEventListener("pointermove", this.startDrag);
- // document.removeEventListener("pointerup", this.endDrag);
- // document.addEventListener("pointerup", this.endDrag);
}
else if (e.button === 0) {
PDFMenu.Instance.StartDrag = this.startDrag;
@@ -221,20 +163,18 @@ export default class Page extends React.Component<IPageProps> {
PDFMenu.Instance.Snippet = this.createSnippet;
PDFMenu.Instance.Status = "pdf";
PDFMenu.Instance.fadeOut(true);
- let target: any = e.target;
- if (target && target.parentElement === this._textLayer.current) {
+ if (e.target && (e.target as any).parentElement === this._textLayer.current) {
e.stopPropagation();
}
else {
// set marquee x and y positions to the spatially transformed position
- let current = this._textLayer.current;
- if (current) {
- let boundingRect = current.getBoundingClientRect();
- this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width);
- this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height);
+ if (this._textLayer.current) {
+ let boundingRect = this._textLayer.current.getBoundingClientRect();
+ this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width);
+ this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height);
}
this._marqueeing = true;
- if (this._marquee.current) this._marquee.current.style.opacity = "0.2";
+ this._marquee.current && (this._marquee.current.style.opacity = "0.2");
}
document.removeEventListener("pointermove", this.onSelectStart);
document.addEventListener("pointermove", this.onSelectStart);
@@ -248,63 +188,23 @@ export default class Page extends React.Component<IPageProps> {
@action
onSelectStart = (e: PointerEvent): void => {
- let target: any = e.target;
- if (this._marqueeing) {
- let current = this._textLayer.current;
- if (current) {
- // transform positions and find the width and height to set the marquee to
- let boundingRect = current.getBoundingClientRect();
- this._marqueeWidth = ((e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width)) - this._startX;
- this._marqueeHeight = ((e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height)) - this._startY;
- this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth);
- this._marqueeY = Math.min(this._startY, this._startY + this._marqueeHeight);
- this._marqueeWidth = Math.abs(this._marqueeWidth);
- this._marqueeHeight = Math.abs(this._marqueeHeight);
- let { background, opacity, transform: transform } = this.getCurlyTransform();
- if (this._marquee.current /*&& this._curly.current*/) {
- this._marquee.current.style.background = background;
- // this._curly.current.style.opacity = opacity;
- this._rotate = transform;
- }
- }
+ if (this._marqueeing && this._textLayer.current) {
+ // transform positions and find the width and height to set the marquee to
+ let boundingRect = this._textLayer.current.getBoundingClientRect();
+ this._marqueeWidth = ((e.clientX - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)) - this._startX;
+ this._marqueeHeight = ((e.clientY - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)) - this._startY;
+ this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth);
+ this._marqueeY = Math.min(this._startY, this._startY + this._marqueeHeight);
+ this._marqueeWidth = Math.abs(this._marqueeWidth);
+ this._marquee.current && (this._marquee.current.style.background = "red");
e.stopPropagation();
e.preventDefault();
}
- else if (target && target.parentElement === this._textLayer.current) {
+ else if (e.target && (e.target as any).parentElement === this._textLayer.current) {
e.stopPropagation();
}
}
- getCurlyTransform = (): { background: string, opacity: string, transform: string } => {
- // let background = "", opacity = "", transform = "";
- // if (this._marquee.current && this._curly.current) {
- // if (this._marqueeWidth > 100 && this._marqueeHeight > 100) {
- // background = "red";
- // opacity = "0";
- // }
- // else {
- // background = "transparent";
- // opacity = "1";
- // }
-
- // // split up for simplicity, could be done in a nested ternary. please do not. -syip2
- // let ratio = this._marqueeWidth / this._marqueeHeight;
- // if (ratio > 1.5) {
- // // vertical
- // transform = "rotate(90deg) scale(1, 5)";
- // }
- // else if (ratio < 0.5) {
- // // horizontal
- // transform = "scale(2, 1)";
- // }
- // else {
- // // diagonal
- // transform = "rotate(45deg) scale(1.5, 1.5)";
- // }
- // }
- return { background: "red", opacity: "0.5", transform: "" };
- }
-
@action
onSelectEnd = (e: PointerEvent): void => {
if (this._marqueeing) {
@@ -319,20 +219,9 @@ export default class Page extends React.Component<IPageProps> {
copy.style.height = style.height;
// apply the appropriate background, opacity, and transform
- let { background, opacity, transform } = this.getCurlyTransform();
- copy.style.background = background;
- // if curly bracing, add a curly brace
- // if (opacity === "1" && this._curly.current) {
- // copy.style.opacity = opacity;
- // let img = this._curly.current.cloneNode();
- // (img as any).style.opacity = opacity;
- // (img as any).style.transform = transform;
- // copy.appendChild(img);
- // }
- // else {
+ copy.style.background = "red";
copy.style.border = style.border;
copy.style.opacity = style.opacity;
- // }
copy.className = this._marquee.current.className;
this.props.createAnnotation(copy, this.props.page);
this._marquee.current.style.opacity = "0";
@@ -357,7 +246,6 @@ export default class Page extends React.Component<IPageProps> {
}
}
-
if (PDFMenu.Instance.Highlighting) {
this.highlight(undefined, "goldenrod");
}
@@ -371,9 +259,9 @@ export default class Page extends React.Component<IPageProps> {
@action
createTextAnnotation = (sel: Selection, selRange: Range) => {
- let clientRects = selRange.getClientRects();
if (this._textLayer.current) {
let boundingRect = this._textLayer.current.getBoundingClientRect();
+ let clientRects = selRange.getClientRects();
for (let i = 0; i < clientRects.length; i++) {
let rect = clientRects.item(i);
if (rect && rect.width !== this._textLayer.current.getBoundingClientRect().width && rect.height !== this._textLayer.current.getBoundingClientRect().height) {
@@ -389,9 +277,8 @@ export default class Page extends React.Component<IPageProps> {
}
}
let text = selRange.extractContents().textContent;
- if (text) {
- this.props.setSelectionText(text);
- }
+ text && this.props.setSelectionText(text);
+
// clear selection
if (sel.empty) { // Chrome
sel.empty();
@@ -401,19 +288,9 @@ export default class Page extends React.Component<IPageProps> {
}
doubleClick = (e: React.MouseEvent) => {
- let target: any = e.target;
- // if double clicking text
- if (target && target.parentElement === this._textLayer.current) {
+ if (e.target && (e.target as any).parentElement === this._textLayer.current) {
// do something to select the paragraph ideally
}
-
- let current = this._textLayer.current;
- if (current) {
- let boundingRect = current.getBoundingClientRect();
- let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width);
- let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height);
- this.props.makePin(x, y, this.props.page);
- }
}
render() {
@@ -425,7 +302,6 @@ export default class Page extends React.Component<IPageProps> {
<div className="pdfInkingLayer-cont" ref={this._annotationLayer} style={{ width: "100%", height: "100%", position: "relative", top: "-100%" }}>
<div className="pdfViewer-annotationBox" ref={this._marquee}
style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "red", border: `${this._marqueeWidth === 0 ? "" : "10px dashed black"}` }}>
- {/* <img ref={this._curly} src="https://static.thenounproject.com/png/331760-200.png" style={{ width: "100%", height: "100%", transform: `${this._rotate}` }} /> */}
</div>
</div>
<div className="textlayer" ref={this._textLayer} style={{ "position": "relative", "top": `-${2 * this._height}px`, "height": `${this._height}px` }} />