aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/pdf')
-rw-r--r--src/client/views/pdf/Annotation.scss4
-rw-r--r--src/client/views/pdf/Annotation.tsx23
-rw-r--r--src/client/views/pdf/PDFAnnotationLayer.scss6
-rw-r--r--src/client/views/pdf/PDFAnnotationLayer.tsx7
-rw-r--r--src/client/views/pdf/PDFViewer.scss9
-rw-r--r--src/client/views/pdf/PDFViewer.tsx160
-rw-r--r--src/client/views/pdf/Page.tsx45
7 files changed, 137 insertions, 117 deletions
diff --git a/src/client/views/pdf/Annotation.scss b/src/client/views/pdf/Annotation.scss
new file mode 100644
index 000000000..0ea85d522
--- /dev/null
+++ b/src/client/views/pdf/Annotation.scss
@@ -0,0 +1,4 @@
+.pdfViewer-annotationBox {
+ pointer-events: all;
+ user-select: none;
+} \ No newline at end of file
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index ed7081b1d..a08ff5969 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,14 +1,15 @@
import React = require("react");
-import { Doc, DocListCast, WidthSym, HeightSym } from "../../../new_fields/Doc";
-import { AnnotationTypes, Viewer, scale } from "./PDFViewer";
+import { action, IReactionDisposer, observable, reaction } from "mobx";
import { observer } from "mobx-react";
-import { observable, IReactionDisposer, reaction, action } from "mobx";
-import { BoolCast, NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
-import PDFMenu from "./PDFMenu";
+import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
import { DocumentManager } from "../../util/DocumentManager";
import { PresentationView } from "../presentationview/PresentationView";
+import PDFMenu from "./PDFMenu";
+import "./Annotation.scss";
+import { AnnotationTypes, scale, Viewer } from "./PDFViewer";
interface IAnnotationProps {
anno: Doc;
@@ -110,11 +111,16 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
}
@action
- onPointerDown = (e: React.PointerEvent) => {
+ onPointerDown = async (e: React.PointerEvent) => {
if (e.button === 0) {
- let targetDoc = Cast(this.props.document.target, Doc, null);
+ let targetDoc = await Cast(this.props.document.target, Doc);
if (targetDoc) {
- DocumentManager.Instance.jumpToDocument(targetDoc, false);
+ let context = await Cast(targetDoc.targetContext, Doc);
+ if (context) {
+ DocumentManager.Instance.jumpToDocument(targetDoc, false, false,
+ ((doc) => this.props.parent.props.parent.props.addDocTab(targetDoc!, undefined, e.ctrlKey ? "onRight" : "inTab")),
+ undefined, undefined);
+ }
}
}
if (e.button === 2) {
@@ -145,7 +151,6 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
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 ? "green" : StrCast(this.props.document.color)
}}></div>
);
diff --git a/src/client/views/pdf/PDFAnnotationLayer.scss b/src/client/views/pdf/PDFAnnotationLayer.scss
new file mode 100644
index 000000000..733533007
--- /dev/null
+++ b/src/client/views/pdf/PDFAnnotationLayer.scss
@@ -0,0 +1,6 @@
+.pdfAnnotationLayer-cont {
+ width:100%;
+ height:100%;
+ position:relative;
+ top:-200%;
+} \ No newline at end of file
diff --git a/src/client/views/pdf/PDFAnnotationLayer.tsx b/src/client/views/pdf/PDFAnnotationLayer.tsx
index 1f49e0d2f..4f267a5c0 100644
--- a/src/client/views/pdf/PDFAnnotationLayer.tsx
+++ b/src/client/views/pdf/PDFAnnotationLayer.tsx
@@ -1,5 +1,6 @@
import React = require("react");
import { observer } from "mobx-react";
+import "./PDFAnnotationLayer.scss";
interface IAnnotationProps {
@@ -15,10 +16,6 @@ export class PDFAnnotationLayer extends React.Component {
}
render() {
- return (
- <div className="pdfAnnotationLayer-cont" style={{ width: "100%", height: "100%", position: "relative", top: "-200%" }} onPointerDown={this.onPointerDown}>
-
- </div>
- );
+ return <div className="pdfAnnotationLayer-cont" onPointerDown={this.onPointerDown} />;
}
} \ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 0fde764d0..7158aaffa 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -27,9 +27,12 @@
// position: absolute;
// top: 0;
}
-
+.pdfViewere-viewer {
+ pointer-events:inherit;
+}
.pdfViewer-text {
-
+ transform: scale(1.5);
+ transform-origin: top left;
.page {
.canvasWrapper {
display: none;
@@ -120,6 +123,8 @@
.pdfViewer-annotationLayer {
position: absolute;
top: 0;
+ width: 100%;
+ pointer-events: none;
}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 699a1ffd7..6a99cec59 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -4,27 +4,23 @@ 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, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Opt } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
-import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../new_fields/Types";
-import { emptyFunction } from "../../../Utils";
-import { DocServer } from "../../DocServer";
-import { Docs, DocUtils, DocumentOptions } from "../../documents/Documents";
-import { DocumentManager } from "../../util/DocumentManager";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { emptyFunction, Utils } from "../../../Utils";
+import { Docs, DocUtils } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
-import { DocumentView } from "../nodes/DocumentView";
-import { PDFBox, handleBackspace } from "../nodes/PDFBox";
+import { PDFBox } from "../nodes/PDFBox";
import Page from "./Page";
import "./PDFViewer.scss";
import React = require("react");
-import PDFMenu from "./PDFMenu";
-import { UndoManager } from "../../util/UndoManager";
-import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
+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;
@@ -90,16 +86,13 @@ export class Viewer extends React.Component<IViewerProps> {
private _annotationReactionDisposer?: IReactionDisposer;
private _dropDisposer?: DragManager.DragDropDisposer;
private _filterReactionDisposer?: IReactionDisposer;
- private _activeReactionDisposer?: IReactionDisposer;
private _viewer: React.RefObject<HTMLDivElement>;
private _mainCont: React.RefObject<HTMLDivElement>;
private _pdfViewer: any;
// private _textContent: Pdfjs.TextContent[] = [];
private _pdfFindController: any;
private _searchString: string = "";
- private _rendered: boolean = false;
- private _pageIndex: number = -1;
- private _matchIndex: number = 0;
+ private _selectionText: string = "";
constructor(props: IViewerProps) {
super(props);
@@ -110,6 +103,10 @@ export class Viewer extends React.Component<IViewerProps> {
this._mainCont = React.createRef();
}
+ setSelectionText = (text: string) => {
+ this._selectionText = text;
+ }
+
componentDidUpdate = (prevProps: IViewerProps) => {
if (this.scrollY !== prevProps.scrollY) {
this.renderPages();
@@ -135,23 +132,6 @@ export class Viewer extends React.Component<IViewerProps> {
},
{ fireImmediately: true });
- this._activeReactionDisposer = reaction(
- () => this.props.parent.props.active(),
- () => {
- runInAction(() => {
- if (!this.props.parent.props.active()) {
- this._searching = false;
- this._pdfFindController = null;
- if (this._viewer.current) {
- let cns = this._viewer.current.childNodes;
- for (let i = cns.length - 1; i >= 0; i--) {
- cns.item(i).remove();
- }
- }
- }
- });
- }
- );
if (this.props.parent.props.ContainingCollectionView) {
this._filterReactionDisposer = reaction(
@@ -182,6 +162,9 @@ export class Viewer extends React.Component<IViewerProps> {
if (this._mainCont.current) {
this._dropDisposer = this._mainCont.current && DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
}
+
+ document.removeEventListener("copy", this.copy);
+ document.addEventListener("copy", this.copy);
}
componentWillUnmount = () => {
@@ -189,11 +172,47 @@ export class Viewer extends React.Component<IViewerProps> {
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();
+ }
+ }
+ // 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);
+ });
+ }
+ }
+ }
}
scrollTo(y: number) {
if (this.props.mainCont.current) {
- this.props.parent.scrollTo(y - this.props.mainCont.current.clientHeight);
+ this.props.parent.scrollTo(y);
}
}
@@ -239,7 +258,7 @@ export class Viewer extends React.Component<IViewerProps> {
}
@action
- makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => {
+ makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string, createLink: boolean = true): Doc => {
let annoDocs: Doc[] = [];
let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {});
@@ -268,7 +287,7 @@ export class Viewer extends React.Component<IViewerProps> {
mainAnnoDoc.y = Math.max(minY, 0);
mainAnnoDoc.annotations = new List<Doc>(annoDocs);
- if (sourceDoc) {
+ if (sourceDoc && createLink) {
DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
}
this._savedAnnotations.clear();
@@ -277,20 +296,19 @@ export class Viewer extends React.Component<IViewerProps> {
}
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);
- this.props.parent.fieldExtensionDoc.annotations = new List<Doc>(targetAnnotations);
- }
- else {
- this.props.parent.fieldExtensionDoc.annotations = new List<Doc>([destDoc]);
- }
- e.stopPropagation();
- }
+ // 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
@@ -318,6 +336,7 @@ export class Viewer extends React.Component<IViewerProps> {
this._isPage[page] = "page";
this._visibleElements[page] = (
<Page
+ setSelectionText={this.setSelectionText}
size={this._pageSizes[page]}
pdf={this.props.pdf}
page={page}
@@ -346,7 +365,7 @@ export class Viewer extends React.Component<IViewerProps> {
this._isPage[page] = "image";
const address = this.props.url;
try {
- let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
+ let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
runInAction(() => this._visibleElements[page] =
<img key={res.path} src={res.path} onError={handleError}
style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />);
@@ -476,7 +495,6 @@ export class Viewer extends React.Component<IViewerProps> {
phraseSearch: true,
query: searchString
});
- this._rendered = true;
});
container.addEventListener("pagerendered", () => {
console.log("rendered");
@@ -488,7 +506,6 @@ export class Viewer extends React.Component<IViewerProps> {
phraseSearch: true,
query: searchString
});
- this._rendered = true;
});
}
}
@@ -563,7 +580,6 @@ export class Viewer extends React.Component<IViewerProps> {
});
container.addEventListener("pagerendered", () => {
console.log("rendered");
- this._rendered = true;
});
this._pdfViewer.setDocument(this.props.pdf);
this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer);
@@ -636,16 +652,12 @@ export class Viewer extends React.Component<IViewerProps> {
render() {
let compiled = this._script;
return (
- <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}>
+ <div className="pdfViewer-viewer" ref={this._mainCont} onPointerDown={this.pointerDown}>
<div className="viewer" style={this._searching ? { position: "absolute", top: 0 } : {}}>
{this._visibleElements}
</div>
- <div className="pdfViewer-text" ref={this._viewer} 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-text" ref={this._viewer} />
+ <div className="pdfViewer-annotationLayer" style={{ height: this.props.parent.Document.nativeHeight }}>
<div className="pdfViewer-annotationLayer-subCont" ref={this._annotationLayer}>
{this._annotations.filter(anno => {
if (compiled && compiled.compiled) {
@@ -703,32 +715,24 @@ class SimpleLinkService {
externalLinkRel: any = null;
pdf: any = null;
- navigateTo(dest: any) { }
+ navigateTo() { }
- getDestinationHash(dest: any) { return "#"; }
+ getDestinationHash() { return "#"; }
- getAnchorUrl(hash: any) { return "#"; }
+ getAnchorUrl() { return "#"; }
- setHash(hash: any) { }
+ setHash() { }
- executeNamedAction(action: any) { }
+ executeNamedAction() { }
- cachePageRef(pageNum: any, pageRef: any) { }
+ 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
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index c9d442fe5..dea4e0da1 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -1,22 +1,18 @@
+import { action, IReactionDisposer, observable } from "mobx";
import { observer } from "mobx-react";
-import React = require("react");
-import { observable, action, runInAction, IReactionDisposer, reaction } from "mobx";
import * as Pdfjs from "pdfjs-dist";
-import { Opt, Doc, FieldResult, Field, DocListCast, WidthSym, HeightSym, DocListCastAsync } from "../../../new_fields/Doc";
-import "./PDFViewer.scss";
import "pdfjs-dist/web/pdf_viewer.css";
-import { PDFBox } from "../nodes/PDFBox";
-import { DragManager } from "../../util/DragManager";
-import { Docs, DocUtils } from "../../documents/Documents";
+import { Doc, DocListCastAsync, Opt, WidthSym } from "../../../new_fields/Doc";
import { List } from "../../../new_fields/List";
-import { emptyFunction } from "../../../Utils";
-import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
import { listSpec } from "../../../new_fields/Schema";
-import { menuBar } from "prosemirror-menu";
-import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer";
+import { Cast, NumCast, StrCast, BoolCast } 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 { UndoManager } from "../../util/UndoManager";
-import { copy } from "typescript-collections/dist/lib/arrays";
+import { scale } from "./PDFViewer";
+import "./PDFViewer.scss";
+import React = require("react");
interface IPageProps {
@@ -33,6 +29,7 @@ interface IPageProps {
createAnnotation: (div: HTMLDivElement, page: number) => void;
makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string, linkTo: boolean) => Doc;
getScrollFromPage: (page: number) => number;
+ setSelectionText: (text: string) => void;
}
@observer
@@ -162,6 +159,7 @@ export default class Page extends React.Component<IPageProps> {
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);
@@ -169,16 +167,12 @@ export default class Page extends React.Component<IPageProps> {
DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
handlers: {
dragComplete: async () => {
- if (!(await annotationDoc.linkedToDoc)) {
+ if (!BoolCast(annotationDoc.linkedToDoc)) {
let annotations = await DocListCastAsync(annotationDoc.annotations);
- if (annotations) {
- annotations.forEach(anno => {
- anno.target = targetDoc;
- });
- }
+ annotations && annotations.forEach(anno => anno.target = targetDoc);
let pdfDoc = await Cast(annotationDoc.pdfDoc, Doc);
if (pdfDoc) {
- DocUtils.MakeLink(annotationDoc, targetDoc, undefined, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title));
+ DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title));
}
}
}
@@ -357,7 +351,8 @@ export default class Page extends React.Component<IPageProps> {
else {
let sel = window.getSelection();
if (sel && sel.type === "Range") {
- this.createTextAnnotation(sel);
+ let selRange = sel.getRangeAt(0);
+ this.createTextAnnotation(sel, selRange);
PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
}
}
@@ -375,8 +370,8 @@ export default class Page extends React.Component<IPageProps> {
}
@action
- createTextAnnotation = (sel: Selection) => {
- let clientRects = sel.getRangeAt(0).getClientRects();
+ createTextAnnotation = (sel: Selection, selRange: Range) => {
+ let clientRects = selRange.getClientRects();
if (this._textLayer.current) {
let boundingRect = this._textLayer.current.getBoundingClientRect();
for (let i = 0; i < clientRects.length; i++) {
@@ -393,6 +388,10 @@ export default class Page extends React.Component<IPageProps> {
}
}
}
+ let text = selRange.extractContents().textContent;
+ if (text) {
+ this.props.setSelectionText(text);
+ }
// clear selection
if (sel.empty) { // Chrome
sel.empty();