aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf
diff options
context:
space:
mode:
authorab <abdullah_ahmed@brown.edu>2019-07-01 15:40:55 -0400
committerab <abdullah_ahmed@brown.edu>2019-07-01 15:40:55 -0400
commit0fc7edd5f9bd1234f8de6e5b1c7b8f23668d784c (patch)
tree2ddff97c032657fb86de37c3be2cf2d201980088 /src/client/views/pdf
parent4f8b7cecea3ceed6861c38bde9ce03c9c46e2d09 (diff)
parent6bd79baf2d9301304194d87667bb5c66c17e5298 (diff)
Merge branch 'monikasearch2' of https://github.com/browngraphicslab/Dash-Web into text_box_ab
Diffstat (limited to 'src/client/views/pdf')
-rw-r--r--src/client/views/pdf/Annotation.tsx144
-rw-r--r--src/client/views/pdf/PDFViewer.scss3
-rw-r--r--src/client/views/pdf/PDFViewer.tsx150
-rw-r--r--src/client/views/pdf/Page.tsx4
4 files changed, 167 insertions, 134 deletions
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
new file mode 100644
index 000000000..73fca229b
--- /dev/null
+++ b/src/client/views/pdf/Annotation.tsx
@@ -0,0 +1,144 @@
+import React = require("react");
+import { Doc, DocListCast, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { AnnotationTypes, Viewer, scale } from "./PDFViewer";
+import { observer } from "mobx-react";
+import { observable, IReactionDisposer, reaction, action } from "mobx";
+import { BoolCast, NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
+import PDFMenu from "./PDFMenu";
+import { DocumentManager } from "../../util/DocumentManager";
+
+interface IAnnotationProps {
+ anno: Doc;
+ index: number;
+ parent: Viewer;
+}
+
+export default class Annotation extends React.Component<IAnnotationProps> {
+ render() {
+ let annotationDocs = DocListCast(this.props.anno.annotations);
+ let res = annotationDocs.map(a => {
+ let type = NumCast(a.type);
+ switch (type) {
+ // case AnnotationTypes.Pin:
+ // return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+ case AnnotationTypes.Region:
+ return <RegionAnnotation parent={this.props.parent} document={a} index={this.props.index} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+ default:
+ return <div></div>;
+ }
+ });
+ return res;
+ }
+}
+
+interface IRegionAnnotationProps {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+ index: number;
+ parent: Viewer;
+ document: Doc;
+}
+
+@observer
+class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
+ @observable private _backgroundColor: string = "red";
+
+ private _reactionDisposer?: IReactionDisposer;
+ private _scrollDisposer?: IReactionDisposer;
+ private _mainCont: React.RefObject<HTMLDivElement>;
+
+ constructor(props: IRegionAnnotationProps) {
+ super(props);
+
+ this._mainCont = React.createRef();
+ }
+
+ componentDidMount() {
+ this._reactionDisposer = reaction(
+ () => BoolCast(this.props.document.delete),
+ () => {
+ if (BoolCast(this.props.document.delete)) {
+ if (this._mainCont.current) {
+ this._mainCont.current.style.display = "none";
+ }
+ }
+ },
+ { fireImmediately: true }
+ );
+
+ this._scrollDisposer = reaction(
+ () => this.props.parent.Index,
+ () => {
+ if (this.props.parent.Index === this.props.index) {
+ this.props.parent.scrollTo(this.props.y - 50);
+ }
+ }
+ );
+ }
+
+ componentWillUnmount() {
+ this._reactionDisposer && this._reactionDisposer();
+ this._scrollDisposer && this._scrollDisposer();
+ }
+
+ deleteAnnotation = () => {
+ let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
+ let group = FieldValue(Cast(this.props.document.group, Doc));
+ if (group && annotation.indexOf(group) !== -1) {
+ let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
+ this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
+ }
+
+ if (group) {
+ let groupAnnotations = DocListCast(group.annotations);
+ groupAnnotations.forEach(anno => anno.delete = true);
+ }
+
+ PDFMenu.Instance.fadeOut(true);
+ }
+
+ @action
+ onPointerDown = (e: React.PointerEvent) => {
+ if (e.button === 0) {
+ let targetDoc = Cast(this.props.document.target, Doc, null);
+ if (targetDoc) {
+ DocumentManager.Instance.jumpToDocument(targetDoc, true);
+ }
+ }
+ if (e.button === 2) {
+ PDFMenu.Instance.Status = "annotation";
+ PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+ PDFMenu.Instance.Pinned = false;
+ PDFMenu.Instance.AddTag = this.addTag.bind(this);
+ PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+ }
+ }
+
+ addTag = (key: string, value: string): boolean => {
+ let group = FieldValue(Cast(this.props.document.group, Doc));
+ if (group) {
+ let valNum = parseInt(value);
+ group[key] = isNaN(valNum) ? value : valNum;
+ return true;
+ }
+ return false;
+ }
+
+ render() {
+ return (
+ <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
+ style={{
+ top: this.props.y * scale,
+ left: this.props.x * scale,
+ width: this.props.width * scale,
+ height: this.props.height * scale,
+ pointerEvents: "all",
+ backgroundColor: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
+ }}></div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 11d3d7e27..0fde764d0 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -41,6 +41,9 @@
}
}
}
+.pdfViewer-viewerCont {
+ width:100%;
+}
.page-cont {
.textLayer {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bafc3cbae..bb148e738 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -23,6 +23,7 @@ import { UndoManager } from "../../util/UndoManager";
import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
import { ScriptField } from "../../../new_fields/ScriptField";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import Annotation from "./Annotation";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
export const scale = 2;
@@ -48,7 +49,7 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
render() {
return (
- <div ref={this._mainDiv}>
+ <div className="pdfViewer-viewerCont" ref={this._mainDiv}>
{!this._pdf ? (null) :
<Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} url={this.props.url} />}
</div>
@@ -69,7 +70,7 @@ interface IViewerProps {
* Handles rendering and virtualization of the pdf
*/
@observer
-class Viewer extends React.Component<IViewerProps> {
+export class Viewer extends React.Component<IViewerProps> {
// _visibleElements is the array of JSX elements that gets rendered
@observable.shallow private _visibleElements: JSX.Element[] = [];
// _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder
@@ -192,10 +193,14 @@ class Viewer extends React.Component<IViewerProps> {
let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
this._isPage = Array<string>(this.props.pdf.numPages);
// this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages);
+ const proms: Pdfjs.PDFPromise<any>[] = [];
for (let i = 0; i < this.props.pdf.numPages; i++) {
- await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
- // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
- let x = page.getViewport(scale);
+ proms.push(this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
+ pageSizes[i] = {
+ width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]) * scale,
+ height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) * scale
+ };
+ // let x = page.getViewport(scale);
// page.getTextContent().then((text: Pdfjs.TextContent) => {
// // let tc = new Pdfjs.TextContentItem()
// // let tc = {str: }
@@ -204,9 +209,10 @@ class Viewer extends React.Component<IViewerProps> {
// // tcStr += t.str;
// // })
// });
- pageSizes[i] = { width: x.width, height: x.height };
- }));
+ // pageSizes[i] = { width: x.width, height: x.height };
+ })));
}
+ await Promise.all(proms);
runInAction(() =>
Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
@@ -226,7 +232,8 @@ class Viewer extends React.Component<IViewerProps> {
let annoDocs: Doc[] = [];
let mainAnnoDoc = Docs.CreateInstance(new Doc(), "", {});
- mainAnnoDoc.page = Math.round(Math.random());
+ mainAnnoDoc.title = "Annotation on " + StrCast(this.props.parent.Document.title);
+ mainAnnoDoc.pdfDoc = this.props.parent.Document;
this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
for (let anno of value) {
let annoDoc = new Doc();
@@ -244,6 +251,7 @@ class Viewer extends React.Component<IViewerProps> {
}
});
+ mainAnnoDoc.y = Math.max((NumCast(annoDocs[0].y) * scale) - 100, 0);
mainAnnoDoc.annotations = new List<Doc>(annoDocs);
if (sourceDoc) {
DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
@@ -417,20 +425,8 @@ class Viewer extends React.Component<IViewerProps> {
}
}
- renderAnnotation = (anno: Doc, index: number): JSX.Element[] => {
- let annotationDocs = DocListCast(anno.annotations);
- let res = annotationDocs.map(a => {
- let type = NumCast(a.type);
- switch (type) {
- // case AnnotationTypes.Pin:
- // return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
- case AnnotationTypes.Region:
- return <RegionAnnotation parent={this} document={a} index={index} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
- default:
- return <div></div>;
- }
- });
- return res;
+ renderAnnotation = (anno: Doc, index: number): JSX.Element => {
+ return <Annotation anno={anno} index={index} parent={this} />;
}
@action
@@ -676,116 +672,6 @@ export enum AnnotationTypes {
Region
}
-interface IAnnotationProps {
- x: number;
- y: number;
- width: number;
- height: number;
- index: number;
- parent: Viewer;
- document: Doc;
-}
-
-@observer
-class RegionAnnotation extends React.Component<IAnnotationProps> {
- @observable private _backgroundColor: string = "red";
-
- private _reactionDisposer?: IReactionDisposer;
- private _scrollDisposer?: IReactionDisposer;
- private _mainCont: React.RefObject<HTMLDivElement>;
-
- constructor(props: IAnnotationProps) {
- super(props);
-
- this._mainCont = React.createRef();
- }
-
- componentDidMount() {
- this._reactionDisposer = reaction(
- () => BoolCast(this.props.document.delete),
- () => {
- if (BoolCast(this.props.document.delete)) {
- if (this._mainCont.current) {
- this._mainCont.current.style.display = "none";
- }
- }
- },
- { fireImmediately: true }
- );
-
- this._scrollDisposer = reaction(
- () => this.props.parent.Index,
- () => {
- if (this.props.parent.Index === this.props.index) {
- this.props.parent.scrollTo(this.props.y - 50);
- }
- }
- );
- }
-
- componentWillUnmount() {
- this._reactionDisposer && this._reactionDisposer();
- this._scrollDisposer && this._scrollDisposer();
- }
-
- deleteAnnotation = () => {
- let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
- let group = FieldValue(Cast(this.props.document.group, Doc));
- if (group && annotation.indexOf(group) !== -1) {
- let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
- this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
- }
-
- if (group) {
- let groupAnnotations = DocListCast(group.annotations);
- groupAnnotations.forEach(anno => anno.delete = true);
- }
-
- PDFMenu.Instance.fadeOut(true);
- }
-
- @action
- onPointerDown = (e: React.PointerEvent) => {
- if (e.button === 0) {
- let targetDoc = Cast(this.props.document.target, Doc, null);
- if (targetDoc) {
- DocumentManager.Instance.jumpToDocument(targetDoc, true);
- }
- }
- if (e.button === 2) {
- PDFMenu.Instance.Status = "annotation";
- PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
- PDFMenu.Instance.Pinned = false;
- PDFMenu.Instance.AddTag = this.addTag.bind(this);
- PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
- }
- }
-
- addTag = (key: string, value: string): boolean => {
- let group = FieldValue(Cast(this.props.document.group, Doc));
- if (group) {
- let valNum = parseInt(value);
- group[key] = isNaN(valNum) ? value : valNum;
- return true;
- }
- return false;
- }
-
- render() {
- return (
- <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
- style={{
- top: this.props.y * scale,
- left: this.props.x * scale,
- width: this.props.width * scale,
- height: this.props.height * scale,
- pointerEvents: "all",
- backgroundColor: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
- }}></div>
- );
- }
-}
-
class SimpleLinkService {
externalLinkTarget: any = null;
externalLinkRel: any = null;
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 01aa96705..57e36be43 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -31,7 +31,7 @@ interface IPageProps {
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) => Doc;
+ makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string, linkTo: boolean) => Doc;
getScrollFromPage: (page: number) => number;
}
@@ -137,7 +137,7 @@ export default class Page extends React.Component<IPageProps> {
@action
highlight = (targetDoc?: Doc, color: string = "red") => {
// creates annotation documents for current highlights
- let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color);
+ let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color, false);
let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
if (targetAnnotations === undefined) {
Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]);