aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf/PDFViewer.tsx
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2019-08-19 10:11:59 -0400
committerbob <bcz@cs.brown.edu>2019-08-19 10:11:59 -0400
commite37bf9124c952aa26c3e29deb9e4faa01cad1a7e (patch)
treebe44ae9bd5e2eb6c5ce392383d41505b5863d061 /src/client/views/pdf/PDFViewer.tsx
parent07482c3bf435748140addfd4fd338fc668657798 (diff)
parentb037aa89fb564812f880994453ce002054a0ad82 (diff)
Merge branch 'master' into presentation_f
Diffstat (limited to 'src/client/views/pdf/PDFViewer.tsx')
-rw-r--r--src/client/views/pdf/PDFViewer.tsx743
1 files changed, 230 insertions, 513 deletions
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 5eb02a6da..08674720d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,167 +1,114 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import * as rp from "request-promise";
import { Dictionary } from "typescript-collections";
-import { Doc, DocListCast, Opt } from "../../../new_fields/Doc";
+import { Doc, DocListCast, FieldResult } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
+import { ScriptField } from "../../../new_fields/ScriptField";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, Utils } from "../../../Utils";
+import { Utils, numberRange } from "../../../Utils";
+import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from "../../documents/Documents";
-import { DragManager } from "../../util/DragManager";
-import { PDFBox } from "../nodes/PDFBox";
+import { KeyCodes } from "../../northstar/utils/KeyCodes";
+import { CompileScript, CompiledScript } from "../../util/Scripting";
+import Annotation from "./Annotation";
import Page from "./Page";
import "./PDFViewer.scss";
import React = require("react");
-import { CompileScript, CompileResult } from "../../util/Scripting";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import Annotation from "./Annotation";
-import { KeyCodes } from "../../northstar/utils/KeyCodes";
-import { DocServer } from "../../DocServer";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
export const scale = 2;
-interface IPDFViewerProps {
- url: string;
- loaded: (nw: number, nh: number, np: number) => void;
- scrollY: number;
- parent: PDFBox;
-}
-
-/**
- * Wrapper that loads the PDF and cascades the pdf down
- */
-@observer
-export class PDFViewer extends React.Component<IPDFViewerProps> {
- @observable _pdf: Opt<Pdfjs.PDFDocumentProxy>;
- private _mainDiv = React.createRef<HTMLDivElement>();
-
- @action
- componentDidMount() {
- Pdfjs.getDocument(this.props.url).promise.then(pdf => runInAction(() => this._pdf = pdf));
- }
-
- render() {
- return (
- <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>
- );
- }
-}
interface IViewerProps {
pdf: Pdfjs.PDFDocumentProxy;
- loaded: (nw: number, nh: number, np: number) => void;
- scrollY: number;
- parent: PDFBox;
- mainCont: React.RefObject<HTMLDivElement>;
url: string;
+ Document: Doc;
+ DataDoc?: Doc;
+ fieldExtensionDoc: Doc;
+ fieldKey: string;
+ loaded: (nw: number, nh: number, np: number) => void;
+ panY: number;
+ scrollTo: (y: number) => void;
+ active: () => boolean;
+ setPanY?: (n: number) => void;
+ addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+ addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
}
/**
* Handles rendering and virtualization of the pdf
*/
@observer
-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
- @observable private _isPage: string[] = [];
+export class PDFViewer extends React.Component<IViewerProps> {
+ @observable.shallow private _visibleElements: JSX.Element[] = []; // _visibleElements is the array of JSX elements that gets rendered
+ @observable private _isPage: string[] = [];// _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder
@observable private _pageSizes: { width: number, height: number }[] = [];
@observable private _annotations: Doc[] = [];
@observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
- @observable private _script: CompileResult | undefined;
+ @observable private _script: CompiledScript = CompileScript("return true") as CompiledScript;
@observable private _searching: boolean = false;
-
- @observable public Index: number = -1;
+ @observable private Index: number = -1;
private _pageBuffer: number = 1;
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _reactionDisposer?: IReactionDisposer;
private _annotationReactionDisposer?: IReactionDisposer;
- private _dropDisposer?: DragManager.DragDropDisposer;
private _filterReactionDisposer?: IReactionDisposer;
- private _viewer: React.RefObject<HTMLDivElement>;
- private _mainCont: React.RefObject<HTMLDivElement>;
+ private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
+ private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _pdfViewer: any;
- // private _textContent: Pdfjs.TextContent[] = [];
private _pdfFindController: any;
private _searchString: string = "";
private _selectionText: string = "";
- constructor(props: IViewerProps) {
- super(props);
+ @computed get panY(): number { return this.props.panY; }
- let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
- this._script = scriptfield ? scriptfield.script : CompileScript("return true");
- this._viewer = React.createRef();
- this._mainCont = React.createRef();
- }
+ // startIndex: where to start rendering pages
+ @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.panY) - this._pageBuffer); }
- setSelectionText = (text: string) => {
- this._selectionText = text;
+ // endIndex: where to end rendering pages
+ @computed get endIndex(): number {
+ return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.panY + (this._pageSizes[0] ? this._pageSizes[0].height : 0)) + this._pageBuffer);
}
- componentDidUpdate = (prevProps: IViewerProps) => {
- if (this.scrollY !== prevProps.scrollY) {
- this.renderPages();
- }
+ @computed get filteredAnnotations() {
+ return this._annotations.filter(anno => {
+ let run = this._script.run({ this: anno });
+ return run.success ? run.result : true;
+ });
}
- @action
- componentDidMount = () => {
- this._reactionDisposer = reaction(
+ componentDidUpdate = (prevProps: IViewerProps) => this.panY !== prevProps.panY && this.renderPages();
- () => [this.props.parent.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0],
- async () => {
- await this.initialLoad();
- this.renderPages();
- }, { fireImmediately: true });
+ componentDidMount = async () => {
+ await this.initialLoad();
- this._annotationReactionDisposer = reaction(
- () => {
- return this.props.parent && this.props.parent.fieldExtensionDoc && DocListCast(this.props.parent.fieldExtensionDoc.annotations);
- },
- (annotations: Doc[]) => {
- annotations && annotations.length && this.renderAnnotations(annotations, true);
- },
+ this._reactionDisposer = reaction(
+ () => [this.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0],
+ () => this.renderPages(),
{ fireImmediately: true });
+ this._annotationReactionDisposer = reaction(
+ () => this.props.fieldExtensionDoc && DocListCast(this.props.fieldExtensionDoc.annotations),
+ annotations => annotations && annotations.length && this.renderAnnotations(annotations, true),
+ { fireImmediately: true });
- if (this.props.parent.props.ContainingCollectionView) {
- this._filterReactionDisposer = reaction(
- () => this.props.parent.Document.filterScript,
- () => {
- runInAction(() => {
- let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
- this._script = scriptfield ? scriptfield.script : CompileScript("return true");
- if (this.props.parent.props.ContainingCollectionView) {
- let fieldDoc = Doc.resolvedFieldDataDoc(this.props.parent.props.ContainingCollectionView.props.DataDoc ?
- this.props.parent.props.ContainingCollectionView.props.DataDoc : this.props.parent.props.ContainingCollectionView.props.Document, this.props.parent.props.ContainingCollectionView.props.fieldKey, "true");
- let ccvAnnos = DocListCast(fieldDoc.annotations);
- ccvAnnos.forEach(d => {
- if (this._script && this._script.compiled) {
- let run = this._script.run(d);
- if (run.success) {
- d.opacity = run.result ? 1 : 0;
- }
- }
- });
- }
- this.Index = -1;
- });
- }
- );
- }
-
- if (this._mainCont.current) {
- this._dropDisposer = this._mainCont.current && DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
- }
+ this._filterReactionDisposer = reaction(
+ () => ({ scriptField: Cast(this.props.Document.filterScript, ScriptField), annos: this._annotations.slice() }),
+ action(({ scriptField, annos }: { scriptField: FieldResult<ScriptField>, annos: Doc[] }) => {
+ this._script = scriptField && scriptField.script.compiled ? scriptField.script : CompileScript("return true") as CompiledScript;
+ annos.forEach(d => {
+ let run = this._script.run(d);
+ d.opacity = !run.success || run.result ? 1 : 0;
+ });
+ this.Index = -1;
+ }),
+ { fireImmediately: true }
+ );
document.removeEventListener("copy", this.copy);
document.addEventListener("copy", this.copy);
@@ -171,162 +118,115 @@ export class Viewer extends React.Component<IViewerProps> {
this._reactionDisposer && this._reactionDisposer();
this._annotationReactionDisposer && this._annotationReactionDisposer();
this._filterReactionDisposer && this._filterReactionDisposer();
- this._dropDisposer && this._dropDisposer();
document.removeEventListener("copy", this.copy);
}
- private copy = (e: ClipboardEvent) => {
- if (this.props.parent.props.active()) {
- let text = this._selectionText;
- if (e.clipboardData) {
- e.clipboardData.setData("text/plain", text);
- e.clipboardData.setData("dash/pdfOrigin", this.props.parent.props.Document[Id]);
- let annoDoc = this.makeAnnotationDocument(undefined, 0, "#0390fc");
- e.clipboardData.setData("dash/pdfRegion", annoDoc[Id]);
- e.preventDefault();
- }
+ copy = (e: ClipboardEvent) => {
+ if (this.props.active() && e.clipboardData) {
+ e.clipboardData.setData("text/plain", this._selectionText);
+ e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]);
+ e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument(undefined, "#0390fc")[Id]);
+ e.preventDefault();
}
- // let targetAnnotations = DocListCast(this.props.parent.fieldExtensionDoc.annotations);
- // if (targetAnnotations) {
- // targetAnnotations.push(destDoc);
- // }
}
paste = (e: ClipboardEvent) => {
- if (e.clipboardData) {
- if (e.clipboardData.getData("dash/pdfOrigin") === this.props.parent.props.Document[Id]) {
- let linkDocId = e.clipboardData.getData("dash/linkDoc");
- if (linkDocId) {
- DocServer.GetRefField(linkDocId).then(async (link) => {
- if (!(link instanceof Doc)) {
- return;
- }
- let proto = Doc.GetProto(link);
- let source = await Cast(proto.anchor1, Doc);
- proto.anchor2 = this.makeAnnotationDocument(source, 0, "#0390fc", false);
- });
- }
- }
+ if (e.clipboardData && e.clipboardData.getData("dash/pdfOrigin") === this.props.Document[Id]) {
+ let linkDocId = e.clipboardData.getData("dash/linkDoc");
+ linkDocId && DocServer.GetRefField(linkDocId).then(async (link) =>
+ (link instanceof Doc) && (Doc.GetProto(link).anchor2 = this.makeAnnotationDocument(await Cast(Doc.GetProto(link), Doc), "#0390fc", false)));
}
}
- scrollTo(y: number) {
- if (this.props.mainCont.current) {
- this.props.parent.scrollTo(y);
- }
- }
+ searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
+
+ pageLoaded = (page: Pdfjs.PDFPageViewport): void => this.props.loaded(page.width, page.height, this.props.pdf.numPages);
+
+ setSelectionText = (text: string) => this._selectionText = text;
+
+ getIndex = () => this.Index;
@action
initialLoad = async () => {
if (this._pageSizes.length === 0) {
- 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++) {
- proms.push(this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
- pageSizes[i] = {
+ this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
+ await Promise.all(this._pageSizes.map<Pdfjs.PDFPromise<any>>((val, i) =>
+ this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => {
+ this._pageSizes.splice(i, 1, {
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: }
- // this._textContent[i] = text;
- // // text.items.forEach(t => {
- // // tcStr += t.str;
- // // })
- // });
- // 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);
- // this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
-
- let startY = NumCast(this.props.parent.Document.startY);
- let ccv = this.props.parent.props.ContainingCollectionView;
- if (ccv) {
- ccv.props.Document.panY = startY;
- }
- this.props.parent.Document.scrollY = 0;
- this.props.parent.Document.scrollY = startY + 1;
+ });
+ this.getPlaceholderPage(i);
+ }))));
+ this.props.loaded(Math.max(...this._pageSizes.map(i => i.width)), this._pageSizes[0].height, this.props.pdf.numPages);
+
+ let startY = NumCast(this.props.Document.startY, NumCast(this.props.Document.panY));
+ this.props.setPanY && this.props.setPanY(startY);
}
}
@action
- makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string, createLink: boolean = true): Doc => {
- let annoDocs: Doc[] = [];
+ makeAnnotationDocument = (sourceDoc: Doc | undefined, color: string, createLink: boolean = true): Doc => {
let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {});
-
- mainAnnoDoc.title = "Annotation on " + StrCast(this.props.parent.Document.title);
- mainAnnoDoc.pdfDoc = this.props.parent.props.Document;
+ let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc);
+ let annoDocs: Doc[] = [];
let minY = Number.MAX_VALUE;
- this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
- for (let anno of value) {
+ if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1 && !createLink) {
+ let anno = this._savedAnnotations.values()[0][0];
+ let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: "rgba(255, 0, 0, 0.1)", title: "Annotation on " + StrCast(this.props.Document.title) });
+ if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
+ if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
+ if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
+ if (anno.style.width) annoDoc.width = parseInt(anno.style.width);
+ annoDoc.target = sourceDoc;
+ annoDoc.group = mainAnnoDoc;
+ annoDoc.color = color;
+ annoDoc.type = AnnotationTypes.Region;
+ annoDocs.push(annoDoc);
+ annoDoc.isButton = true;
+ anno.remove();
+ this.props.addDocument && this.props.addDocument(annoDoc, false);
+ mainAnnoDoc = annoDoc;
+ mainAnnoDocProto = Doc.GetProto(annoDoc);
+ } else {
+ this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => {
let annoDoc = new Doc();
- if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale;
- if (anno.style.top) {
- annoDoc.y = parseInt(anno.style.top) / scale;
- minY = Math.min(parseInt(anno.style.top), minY);
- }
- if (anno.style.height) annoDoc.height = parseInt(anno.style.height) / scale;
- if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale;
- annoDoc.page = key;
+ if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
+ if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
+ if (anno.style.height) annoDoc.height = parseInt(anno.style.height);
+ if (anno.style.width) annoDoc.width = parseInt(anno.style.width);
annoDoc.target = sourceDoc;
annoDoc.group = mainAnnoDoc;
annoDoc.color = color;
annoDoc.type = AnnotationTypes.Region;
annoDocs.push(annoDoc);
anno.remove();
- }
- });
+ (annoDoc.y !== undefined) && (minY = Math.min(NumCast(annoDoc.y), minY));
+ }));
- mainAnnoDoc.y = Math.max(minY, 0);
- mainAnnoDoc.annotations = new List<Doc>(annoDocs);
+ mainAnnoDocProto.y = Math.max(minY, 0);
+ mainAnnoDocProto.annotations = new List<Doc>(annoDocs);
+ }
+ mainAnnoDocProto.title = "Annotation on " + StrCast(this.props.Document.title);
+ mainAnnoDocProto.annotationOn = this.props.Document;
if (sourceDoc && createLink) {
- DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
+ DocUtils.MakeLink(sourceDoc, mainAnnoDocProto, undefined, `Annotation from ${StrCast(this.props.Document.title)}`);
}
this._savedAnnotations.clear();
this.Index = -1;
return mainAnnoDoc;
}
- drop = async (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.LinkDragData) {
- let sourceDoc = de.data.linkSourceDocument;
- let destDoc = this.makeAnnotationDocument(sourceDoc, 1, "red");
- de.data.droppedDocuments.push(destDoc);
- let targetAnnotations = DocListCast(this.props.parent.fieldExtensionDoc.annotations);
- if (targetAnnotations) {
- targetAnnotations.push(destDoc);
- }
- else {
- this.props.parent.fieldExtensionDoc.annotations = new List<Doc>([destDoc]);
- }
- e.stopPropagation();
- }
- }
- /**
- * Called by the Page class when it gets rendered, initializes the lists and
- * puts a placeholder with all of the correct page sizes when all of the pages have been loaded.
- */
- @action
- pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => {
- this.props.loaded(page.width, page.height, this.props.pdf.numPages);
- }
-
@action
getPlaceholderPage = (page: number) => {
if (this._isPage[page] !== "none") {
this._isPage[page] = "none";
this._visibleElements[page] = (
<div key={`${this.props.url}-placeholder-${page + 1}`} className="pdfviewer-placeholder"
- style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }} />
- );
+ style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }}>
+ "PAGE IS LOADING... "
+ </div>);
}
}
@@ -334,25 +234,19 @@ export class Viewer extends React.Component<IViewerProps> {
getRenderedPage = (page: number) => {
if (this._isPage[page] !== "page") {
this._isPage[page] = "page";
- this._visibleElements[page] = (
- <Page
- setSelectionText={this.setSelectionText}
- size={this._pageSizes[page]}
- pdf={this.props.pdf}
- page={page}
- numPages={this.props.pdf.numPages}
- key={`${this.props.url}-rendered-${page + 1}`}
- name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`}
- pageLoaded={this.pageLoaded}
- parent={this.props.parent}
- makePin={emptyFunction}
- renderAnnotations={this.renderAnnotations}
- createAnnotation={this.createAnnotation}
- sendAnnotations={this.receiveAnnotations}
- makeAnnotationDocuments={this.makeAnnotationDocument}
- getScrollFromPage={this.getScrollFromPage}
- {...this.props} />
- );
+ this._visibleElements[page] = (<Page {...this.props}
+ size={this._pageSizes[page]}
+ numPages={this.props.pdf.numPages}
+ setSelectionText={this.setSelectionText}
+ page={page}
+ key={`${this.props.url}-rendered-${page + 1}`}
+ name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`}
+ pageLoaded={this.pageLoaded}
+ renderAnnotations={this.renderAnnotations}
+ createAnnotation={this.createAnnotation}
+ sendAnnotations={this.receiveAnnotations}
+ makeAnnotationDocuments={this.makeAnnotationDocument}
+ getScrollFromPage={this.getScrollFromPage} />);
}
}
@@ -360,14 +254,12 @@ export class Viewer extends React.Component<IViewerProps> {
// file address of the pdf
@action
getPageImage = async (page: number) => {
- let handleError = () => this.getRenderedPage(page);
if (this._isPage[page] !== "image") {
this._isPage[page] = "image";
- const address = this.props.url;
try {
- let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
+ let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${page + 1}.PNG`)));
runInAction(() => this._visibleElements[page] =
- <img key={res.path} src={res.path} onError={handleError}
+ <img key={res.path} src={res.path} onError={() => this.getRenderedPage(page)}
style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />);
} catch (e) {
console.log(e);
@@ -375,33 +267,14 @@ export class Viewer extends React.Component<IViewerProps> {
}
}
- @computed get scrollY(): number { return this.props.scrollY; }
-
- // startIndex: where to start rendering pages
- @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.scrollY) - this._pageBuffer); }
-
- // endIndex: where to end rendering pages
- @computed get endIndex(): number {
- return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY + this._pageSizes[0].height) + this._pageBuffer);
- }
-
- @action
renderPages = () => {
- for (let i = 0; i < this.props.pdf.numPages; i++) {
- if (i < this.startIndex || i > this.endIndex) {
- this.getPlaceholderPage(i); // pages outside of the pdf use empty stand-in divs
- } else {
- if (this.props.parent.props.active()) {
- this.getRenderedPage(i);
- } else {
- this.getPageImage(i);
- }
- }
- }
+ numberRange(this.props.pdf.numPages).filter(p => this._isPage[p] !== undefined).map(i =>
+ (i < this.startIndex || i > this.endIndex) ? this.getPlaceholderPage(i) : // pages outside of the pdf use empty stand-in divs
+ this.props.active() ? this.getRenderedPage(i) : this.getPageImage(i));
}
@action
- private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => {
+ renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => {
if (removeOldAnnotations) {
this._annotations = annotations;
}
@@ -412,6 +285,21 @@ export class Viewer extends React.Component<IViewerProps> {
}
@action
+ prevAnnotation = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ this.Index = Math.max(this.Index - 1, 0);
+ }
+
+ @action
+ nextAnnotation = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ this.Index = Math.min(this.Index + 1, this.filteredAnnotations.length - 1);
+ }
+
+ sendAnnotations = (page: number) => {
+ return this._savedAnnotations.getValue(page);
+ }
+
receiveAnnotations = (annotations: HTMLDivElement[], page: number) => {
if (page === -1) {
this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
@@ -422,28 +310,21 @@ export class Viewer extends React.Component<IViewerProps> {
}
}
- sendAnnotations = (page: number): HTMLDivElement[] | undefined => {
- return this._savedAnnotations.getValue(page);
- }
-
// get the page index that the vertical offset passed in is on
getPageFromScroll = (vOffset: number) => {
let index = 0;
let currOffset = vOffset;
- while (index < this._pageSizes.length && currOffset - this._pageSizes[index].height > 0) {
+ while (index < this._pageSizes.length && this._pageSizes[index] && currOffset - this._pageSizes[index].height > 0) {
currOffset -= this._pageSizes[index++].height;
}
return index;
}
getScrollFromPage = (index: number): number => {
- let counter = 0;
- for (let i = 0; i < Math.min(this.props.pdf.numPages, index); i++) {
- counter += this._pageSizes[i].height;
- }
- return counter;
+ return numberRange(Math.min(this.props.pdf.numPages, index)).reduce((counter, i) => counter + this._pageSizes[i].height, 0);
}
+ @action
createAnnotation = (div: HTMLDivElement, page: number) => {
if (this._annotationLayer.current) {
if (div.style.top) {
@@ -461,101 +342,30 @@ export class Viewer extends React.Component<IViewerProps> {
}
}
- renderAnnotation = (anno: Doc, index: number): JSX.Element => {
- return <Annotation anno={anno} index={index} parent={this} key={`${anno[Id]}-annotation`} />;
- }
-
- @action
- pointerDown = () => {
- // this._searching = false;
- }
-
@action
search = (searchString: string) => {
if (this._pdfViewer._pageViewsReady) {
- this._pdfFindController.executeCommand('find',
- {
- caseSensitive: false,
- findPrevious: undefined,
- highlightAll: true,
- phraseSearch: true,
- query: searchString
- });
+ this._pdfFindController.executeCommand('find', {
+ caseSensitive: false,
+ findPrevious: undefined,
+ highlightAll: true,
+ phraseSearch: true,
+ query: searchString
+ });
}
- else {
- let container = this._mainCont.current;
- if (container) {
- container.addEventListener("pagesloaded", () => {
- console.log("rendered");
- this._pdfFindController.executeCommand('find',
- {
- caseSensitive: false,
- findPrevious: undefined,
- highlightAll: true,
- phraseSearch: true,
- query: searchString
- });
- });
- container.addEventListener("pagerendered", () => {
- console.log("rendered");
- this._pdfFindController.executeCommand('find',
- {
- caseSensitive: false,
- findPrevious: undefined,
- highlightAll: true,
- phraseSearch: true,
- query: searchString
- });
- });
- }
+ else if (this._mainCont.current) {
+ let executeFind = () => this._pdfFindController.executeCommand('find', {
+ caseSensitive: false,
+ findPrevious: undefined,
+ highlightAll: true,
+ phraseSearch: true,
+ query: searchString
+ });
+ this._mainCont.current.addEventListener("pagesloaded", executeFind);
+ this._mainCont.current.addEventListener("pagerendered", executeFind);
}
-
- // let viewer = this._viewer.current;
-
- // if (!this._pdfFindController) {
- // if (container && viewer) {
- // let simpleLinkService = new SimpleLinkService();
- // let pdfViewer = new PDFJSViewer.PDFViewer({
- // container: container,
- // viewer: viewer,
- // linkService: simpleLinkService
- // });
- // simpleLinkService.setPdf(this.props.pdf);
- // container.addEventListener("pagesinit", () => {
- // pdfViewer.currentScaleValue = 1;
- // });
- // container.addEventListener("pagerendered", () => {
- // console.log("rendered");
- // this._pdfFindController.executeCommand('find',
- // {
- // caseSensitive: false,
- // findPrevious: undefined,
- // highlightAll: true,
- // phraseSearch: true,
- // query: searchString
- // });
- // });
- // pdfViewer.setDocument(this.props.pdf);
- // this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer);
- // // this._pdfFindController._linkService = pdfLinkService;
- // pdfViewer.findController = this._pdfFindController;
- // }
- // }
- // else {
- // this._pdfFindController.executeCommand('find',
- // {
- // caseSensitive: false,
- // findPrevious: undefined,
- // highlightAll: true,
- // phraseSearch: true,
- // query: searchString
- // });
- // }
}
- searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
- this._searchString = e.currentTarget.value;
- }
@action
toggleSearch = (e: React.MouseEvent) => {
@@ -563,29 +373,19 @@ export class Viewer extends React.Component<IViewerProps> {
this._searching = !this._searching;
if (this._searching) {
- let container = this._mainCont.current;
- let viewer = this._viewer.current;
-
- if (!this._pdfFindController) {
- if (container && viewer) {
- let simpleLinkService = new SimpleLinkService();
- this._pdfViewer = new PDFJSViewer.PDFViewer({
- container: container,
- viewer: viewer,
- linkService: simpleLinkService
- });
- simpleLinkService.setPdf(this.props.pdf);
- container.addEventListener("pagesinit", () => {
- this._pdfViewer.currentScaleValue = 1;
- });
- container.addEventListener("pagerendered", () => {
- console.log("rendered");
- });
- this._pdfViewer.setDocument(this.props.pdf);
- this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer);
- // this._pdfFindController._linkService = pdfLinkService;
- this._pdfViewer.findController = this._pdfFindController;
- }
+ if (!this._pdfFindController && this._mainCont.current && this._viewer.current) {
+ let simpleLinkService = new SimpleLinkService();
+ this._pdfViewer = new PDFJSViewer.PDFViewer({
+ container: this._mainCont.current,
+ viewer: this._viewer.current,
+ linkService: simpleLinkService
+ });
+ simpleLinkService.setPdf(this.props.pdf);
+ this._mainCont.current.addEventListener("pagesinit", () => this._pdfViewer.currentScaleValue = 1);
+ this._mainCont.current.addEventListener("pagerendered", () => console.log("rendered"));
+ this._pdfViewer.setDocument(this.props.pdf);
+ this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer);
+ this._pdfViewer.findController = this._pdfFindController;
}
}
else {
@@ -599,120 +399,45 @@ export class Viewer extends React.Component<IViewerProps> {
}
}
- @action
- prevAnnotation = (e: React.MouseEvent) => {
- e.stopPropagation();
-
- // if (this.Index > 0) {
- // this.Index--;
- // }
- this.Index = Math.max(this.Index - 1, 0);
- }
-
- @action
- nextAnnotation = (e: React.MouseEvent) => {
- e.stopPropagation();
-
- let compiled = this._script;
- let filtered = this._annotations.filter(anno => {
- if (compiled && compiled.compiled) {
- let run = compiled.run({ this: anno });
- if (run.success) {
- return run.result;
- }
- }
- return true;
- });
- this.Index = Math.min(this.Index + 1, filtered.length - 1);
- }
-
- nextResult = () => {
- // if (this._viewer.current) {
- // let results = this._pdfFindController.pageMatches;
- // if (results && results.length) {
- // if (this._pageIndex === this.props.pdf.numPages && this._matchIndex === results[this._pageIndex].length - 1) {
- // return;
- // }
- // if (this._pageIndex === -1 || this._matchIndex === results[this._pageIndex].length - 1) {
- // this._matchIndex = 0;
- // this._pageIndex++;
- // }
- // else {
- // this._matchIndex++;
- // }
- // this._pdfFindController._nextMatch()
- // let nextMatch = this._viewer.current.children[this._pageIndex].children[1].children[results[this._pageIndex][this._matchIndex]];
- // rconsole.log(nextMatch);
- // this.props.parent.scrollTo(nextMatch.getBoundingClientRect().top);
- // nextMatch.setAttribute("style", nextMatch.getAttribute("style") ? nextMatch.getAttribute("style") + ", background-color: green" : "background-color: green");
- // }
- // }
- }
-
render() {
- let compiled = this._script;
- return (
- <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}>
- <div className="viewer" style={this._searching ? { position: "absolute", top: 0 } : {}}>
- {this._visibleElements}
- </div>
- <div className="pdfViewer-text" ref={this._viewer} onCopy={() => console.log("gello world")} style={{ transform: "scale(1.5)", transformOrigin: "top left" }} />
- <div className="pdfViewer-annotationLayer"
- style={{
- height: this.props.parent.Document.nativeHeight, width: `100%`,
- pointerEvents: this.props.parent.props.active() ? "none" : "all"
- }}>
- <div className="pdfViewer-annotationLayer-subCont" ref={this._annotationLayer}>
- {this._annotations.filter(anno => {
- if (compiled && compiled.compiled) {
- let run = compiled.run({ this: anno });
- if (run.success) {
- return run.result;
- }
- }
- return true;
- }).sort((a: Doc, b: Doc) => NumCast(a.y) - NumCast(b.y))
- .map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
- </div>
- </div>
- <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()}
- style={{
- bottom: -this.props.scrollY,
- left: `${this._searching ? 0 : 100}%`
- }}>
- <button className="pdfViewer-overlayButton" title="Open Search Bar"></button>
- {/* <button title="Previous Result" onClick={() => this.search(this._searchString)}><FontAwesomeIcon icon="arrow-up" size="3x" color="white" /></button>
- <button title="Next Result" onClick={this.nextResult}><FontAwesomeIcon icon="arrow-down" size="3x" color="white" /></button> */}
- <input onKeyDown={(e: React.KeyboardEvent) => e.keyCode === KeyCodes.ENTER ? this.search(this._searchString) : e.keyCode === KeyCodes.BACKSPACE ? e.stopPropagation() : true} placeholder="Search" className="pdfViewer-overlaySearchBar" onChange={this.searchStringChanged} />
- <button title="Search" onClick={() => this.search(this._searchString)}><FontAwesomeIcon icon="search" size="3x" color="white" /></button>
- </div>
- <button className="pdfViewer-overlayButton" onClick={this.prevAnnotation} title="Previous Annotation"
- style={{ bottom: -this.props.scrollY + 280, right: 10, display: this.props.parent.props.active() ? "flex" : "none" }}>
- <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
- <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="3x" />
- </div>
- </button>
- <button className="pdfViewer-overlayButton" onClick={this.nextAnnotation} title="Next Annotation"
- style={{ bottom: -this.props.scrollY + 200, right: 10, display: this.props.parent.props.active() ? "flex" : "none" }}>
- <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
- <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="3x" />
- </div>
- </button>
- <button className="pdfViewer-overlayButton" onClick={this.toggleSearch} title="Open Search Bar"
- style={{ bottom: -this.props.scrollY + 10, right: 0, display: this.props.parent.props.active() ? "flex" : "none" }}>
- <div className="pdfViewer-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div>
- <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
- <FontAwesomeIcon style={{ color: "white" }} icon={this._searching ? "times" : "search"} size="3x" />
- </div>
- </button>
- </div >
- );
+ return (<div className="pdfViewer-viewer" ref={this._mainCont} >
+ <div className="pdfViewer-visibleElements" style={this._searching ? { position: "absolute", top: 0 } : {}}>
+ {this._visibleElements}
+ </div>
+ <div className="pdfViewer-text" ref={this._viewer} />
+ <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.props.Document.nativeHeight) }} ref={this._annotationLayer}>
+ {this.filteredAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) =>
+ <Annotation {...this.props} ParentIndex={this.getIndex} anno={anno} index={index} key={`${anno[Id]}-annotation`} />)}
+ </div>
+ <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()}
+ style={{ bottom: -this.props.panY, left: `${this._searching ? 0 : 100}%` }}>
+ <button className="pdfViewer-overlayButton" title="Open Search Bar" />
+ <input className="pdfViewer-overlaySearchBar" placeholder="Search" onChange={this.searchStringChanged}
+ onKeyDown={(e: React.KeyboardEvent) => e.keyCode === KeyCodes.ENTER ? this.search(this._searchString) : e.keyCode === KeyCodes.BACKSPACE ? e.stopPropagation() : true} />
+ <button title="Search" onClick={() => this.search(this._searchString)}>
+ <FontAwesomeIcon icon="search" size="3x" color="white" /></button>
+ </div>
+ <button className="pdfViewer-overlayButton" onClick={this.prevAnnotation} title="Previous Annotation"
+ style={{ bottom: -this.props.panY + 280, right: 10, display: this.props.active() ? "flex" : "none" }}>
+ <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
+ <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="3x" /></div>
+ </button>
+ <button className="pdfViewer-overlayButton" onClick={this.nextAnnotation} title="Next Annotation"
+ style={{ bottom: -this.props.panY + 200, right: 10, display: this.props.active() ? "flex" : "none" }}>
+ <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
+ <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="3x" /></div>
+ </button>
+ <button className="pdfViewer-overlayButton" onClick={this.toggleSearch} title="Open Search Bar"
+ style={{ bottom: -this.props.panY + 10, right: 0, display: this.props.active() ? "flex" : "none" }}>
+ <div className="pdfViewer-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div>
+ <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
+ <FontAwesomeIcon style={{ color: "white" }} icon={this._searching ? "times" : "search"} size="3x" /></div>
+ </button>
+ </div >);
}
}
-export enum AnnotationTypes {
- Region
-}
+export enum AnnotationTypes { Region }
class SimpleLinkService {
externalLinkTarget: any = null;
@@ -731,20 +456,12 @@ class SimpleLinkService {
cachePageRef() { }
- get pagesCount() {
- return this.pdf ? this.pdf.numPages : 0;
- }
+ get pagesCount() { return this.pdf ? this.pdf.numPages : 0; }
- get page() {
- return 0;
- }
+ get page() { return 0; }
- setPdf(pdf: any) {
- this.pdf = pdf;
- }
+ setPdf(pdf: any) { this.pdf = pdf; }
- get rotation() {
- return 0;
- }
+ get rotation() { return 0; }
set rotation(value: any) { }
} \ No newline at end of file