From 6df3143ead642356b7823946e9710f840f3faa5a Mon Sep 17 00:00:00 2001 From: _stanleyyip <33562077+yipstanley@users.noreply.github.com> Date: Tue, 14 May 2019 16:02:37 -0400 Subject: argh --- src/client/documents/Documents.ts | 3 +- src/client/views/nodes/DocumentContentsView.tsx | 1 + src/client/views/nodes/PDFBox.tsx | 4 +- src/client/views/pdf/PDFBox2.tsx | 26 ++++ src/client/views/pdf/PDFViewer.tsx | 183 ++++++++++++++++++++++++ 5 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 src/client/views/pdf/PDFBox2.tsx create mode 100644 src/client/views/pdf/PDFViewer.tsx (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 11929455c..654431905 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -33,6 +33,7 @@ import { DocServer } from "../DocServer"; import { StrokeData, InkField } from "../../new_fields/InkField"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; +import { PDFBox2 } from "../views/pdf/PDFBox2"; export interface DocumentOptions { x?: number; @@ -136,7 +137,7 @@ export namespace Docs { } function CreatePdfPrototype(): Doc { let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 1200, width: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); + { x: 0, y: 0, nativeWidth: 1200, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); return pdfProto; } function CreateWebPrototype(): Doc { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index f404b7bc6..11df9162a 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -23,6 +23,7 @@ import { FieldViewProps } from "./FieldView"; import { Without, OmitKeys } from "../../../Utils"; import { Cast, StrCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; +import { PDFBox2 } from "../pdf/PDFBox2"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? type BindingProps = Without; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index cb27b3f1b..55a37883a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -21,6 +21,7 @@ import { positionSchema } from "./DocumentView"; import { pageSchema } from "./ImageBox"; import { ImageField, PdfField } from "../../../new_fields/URLField"; import { InkingControl } from "../InkingControl"; +import { PDFViewer } from "../pdf/PDFViewer"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, @@ -354,10 +355,11 @@ export class PDFBox extends DocComponent(PdfDocumen } render() { trace(); + const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
- {this.pdfRenderer} +
); } diff --git a/src/client/views/pdf/PDFBox2.tsx b/src/client/views/pdf/PDFBox2.tsx new file mode 100644 index 000000000..c3f5c19d8 --- /dev/null +++ b/src/client/views/pdf/PDFBox2.tsx @@ -0,0 +1,26 @@ +import React = require("react"); +import { FieldViewProps, FieldView } from "../nodes/FieldView"; +import { DocComponent } from "../DocComponent"; +import { makeInterface } from "../../../new_fields/Schema"; +import { positionSchema } from "../nodes/DocumentView"; +import { pageSchema } from "../nodes/ImageBox"; +import { PDFViewer } from "./PDFViewer"; +import { RouteStore } from "../../../server/RouteStore"; +import { InkingControl } from "../InkingControl"; +import { observer } from "mobx-react"; + +type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; +const PdfDocument = makeInterface(positionSchema, pageSchema); + +@observer +export class PDFBox2 extends DocComponent(PdfDocument) { + public static LayoutString() { return FieldView.LayoutString(PDFBox2); } + + render() { + const pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; + let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool); + return ( + + ) + } +} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx new file mode 100644 index 000000000..558a4fab6 --- /dev/null +++ b/src/client/views/pdf/PDFViewer.tsx @@ -0,0 +1,183 @@ +import { observer } from "mobx-react"; +import React = require("react"); +import { observable, action, runInAction } from "mobx"; +import { RouteStore } from "../../../server/RouteStore"; +import * as Pdfjs from "pdfjs-dist"; +import { Opt } from "../../../new_fields/Doc"; + +interface IPDFViewerProps { + url: string; +} + +@observer +export class PDFViewer extends React.Component { + @observable _pdf: Opt; + + @action + componentDidMount() { + // const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; + const pdfUrl = this.props.url; + let promise = Pdfjs.getDocument(pdfUrl).promise; + + promise.then((pdf: Pdfjs.PDFDocumentProxy) => { + runInAction(() => this._pdf = pdf); + }); + } + + render() { + console.log("PDFVIEWER"); + console.log(this._pdf); + return ( +
+ +
+ ); + } +} + +interface IViewerProps { + pdf: Opt; +} + +class Viewer extends React.Component { + render() { + console.log("VIEWER"); + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + console.log(numPages); + return ( +
+ {/* {Array.from(Array(numPages).keys()).map((i) => ( */} + + {/* ))} */} +
+ ); + } +} + +interface IPageProps { + pdf: Opt; + name: string; + numPages: number; +} + +@observer +class Page extends React.Component { + @observable _state: string = "N/A"; + @observable _width: number = 0; + @observable _height: number = 0; + @observable _page: Opt; + canvas: React.RefObject; + textLayer: React.RefObject; + @observable _currPage: number = 1; + + constructor(props: IPageProps) { + super(props); + this.canvas = React.createRef(); + this.textLayer = React.createRef(); + } + + componentDidMount() { + console.log(this.props.pdf); + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + componentDidUpdate() { + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + private update = (pdf: Pdfjs.PDFDocumentProxy) => { + if (pdf) { + this.loadPage(pdf); + } + else { + this._state = "loading"; + } + } + + private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { + if (this._state === "rendering" || this._page) return; + + pdf.getPage(this._currPage).then( + (page: Pdfjs.PDFPageProxy) => { + console.log("PAGE"); + console.log(page); + this._state = "rendering"; + this.renderPage(page); + } + ) + } + + @action + private renderPage = (page: Pdfjs.PDFPageProxy) => { + let scale = 1; + let viewport = page.getViewport(scale); + let canvas = this.canvas.current; + if (canvas) { + let context = canvas.getContext("2d"); + canvas.width = viewport.width; + this._width = viewport.width; + canvas.height = viewport.height; + this._height = viewport.height; + if (context) { + page.render({ canvasContext: context, viewport: viewport }); + page.getTextContent().then((res: Pdfjs.TextContent) => { + //@ts-ignore + let textLayer = Pdfjs.renderTextLayer({ + textContent: res, + container: this.textLayer.current, + viewport: viewport + }); + // textLayer._render(); + this._state = "rendered"; + }); + + this._page = page; + } + } + } + + @action + prevPage = (e: React.MouseEvent) => { + if (this._currPage > 2 && this._state !== "rendering") { + this._currPage = Math.max(this._currPage - 1, 1); + this._page = undefined; + this.loadPage(this.props.pdf!); + this._state = "rendering"; + } + } + + @action + nextPage = (e: React.MouseEvent) => { + if (this._currPage < this.props.numPages - 1 && this._state !== "rendering") { + this._currPage = Math.min(this._currPage + 1, this.props.numPages) + this._page = undefined; + this.loadPage(this.props.pdf!); + this._state = "rendering"; + } + } + + render() { + return ( +
+
+ +
+
+
+
<
+
>
+
+
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f043c60ff537667021a76e716b6e6465aec44525 Mon Sep 17 00:00:00 2001 From: _stanleyyip <33562077+yipstanley@users.noreply.github.com> Date: Sun, 19 May 2019 12:30:09 -0700 Subject: scrolling ? --- src/client/views/pdf/PDFViewer.scss | 19 +++++++++++++++++++ src/client/views/pdf/PDFViewer.tsx | 29 ++++++++++++++++------------- 2 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 src/client/views/pdf/PDFViewer.scss (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss new file mode 100644 index 000000000..d8ff06406 --- /dev/null +++ b/src/client/views/pdf/PDFViewer.scss @@ -0,0 +1,19 @@ +.canvasContainer {} + +.viewer-button-cont { + position: absolute; + display: flex; + justify-content: space-evenly; + align-items: center; +} + +.viewer-previousPage, +.viewer-nextPage { + background: grey; + font-weight: bold; + opacity: 0.5; + padding: 0 10px; + border-radius: 5px; +} + +.viewer {} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 558a4fab6..1b445eae4 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -4,6 +4,7 @@ import { observable, action, runInAction } from "mobx"; import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; import { Opt } from "../../../new_fields/Doc"; +import "./PDFViewer.scss"; interface IPDFViewerProps { url: string; @@ -46,15 +47,16 @@ class Viewer extends React.Component { console.log(numPages); return (
- {/* {Array.from(Array(numPages).keys()).map((i) => ( */} - - {/* ))} */} + {Array.from(Array(numPages).keys()).map((i) => ( + + ))} }
); } @@ -64,6 +66,7 @@ interface IPageProps { pdf: Opt; name: string; numPages: number; + page: number; } @observer @@ -74,7 +77,7 @@ class Page extends React.Component { @observable _page: Opt; canvas: React.RefObject; textLayer: React.RefObject; - @observable _currPage: number = 1; + @observable _currPage: number = this.props.page + 1; constructor(props: IPageProps) { super(props); @@ -172,11 +175,11 @@ class Page extends React.Component {
-
-
+
+ {/*
<
>
-
+
*/}
); } -- cgit v1.2.3-70-g09d2 From 2cc62cd88688ccdec8275fcaaba939d448f9baf5 Mon Sep 17 00:00:00 2001 From: Stanley Date: Sun, 19 May 2019 13:56:30 -0700 Subject: ugh --- src/client/views/nodes/PDFBox.tsx | 18 +++++++++--------- src/client/views/pdf/PDFBox2.tsx | 2 ++ 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 55a37883a..bf3f299bc 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -70,15 +70,15 @@ export class PDFBox extends DocComponent(PdfDocumen @computed private get thumbnailPage() { return Cast(this.props.Document.thumbnailPage, "number", -1); } componentDidMount() { - this._reactionDisposer = reaction( - () => [SelectionManager.SelectedDocuments().slice()], - () => { - if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage !== this.thumbnailPage && !this.props.isSelected()) { - this.saveThumbnail(); - this._interactive = true; - } - }, - { fireImmediately: true }); + // this._reactionDisposer = reaction( + // () => [SelectionManager.SelectedDocuments().slice()], + // () => { + // if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage !== this.thumbnailPage && !this.props.isSelected()) { + // this.saveThumbnail(); + // this._interactive = true; + // } + // }, + // { fireImmediately: true }); } diff --git a/src/client/views/pdf/PDFBox2.tsx b/src/client/views/pdf/PDFBox2.tsx index c3f5c19d8..71825c260 100644 --- a/src/client/views/pdf/PDFBox2.tsx +++ b/src/client/views/pdf/PDFBox2.tsx @@ -8,6 +8,7 @@ import { PDFViewer } from "./PDFViewer"; import { RouteStore } from "../../../server/RouteStore"; import { InkingControl } from "../InkingControl"; import { observer } from "mobx-react"; +import { trace } from "mobx"; type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -17,6 +18,7 @@ export class PDFBox2 extends DocComponent(PdfDocume public static LayoutString() { return FieldView.LayoutString(PDFBox2); } render() { + trace(); const pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool); return ( -- cgit v1.2.3-70-g09d2 From 3a9f1a40cb6bcd35783aa83e66c3e253812aa39d Mon Sep 17 00:00:00 2001 From: yipstanley Date: Sun, 19 May 2019 19:10:44 -0400 Subject: stacking ! --- src/client/documents/Documents.ts | 7 +- src/client/views/nodes/PDFBox.tsx | 296 ++----------------------------------- src/client/views/pdf/PDFViewer.tsx | 31 +--- 3 files changed, 21 insertions(+), 313 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5752bb096..2df733fd5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -33,12 +33,9 @@ import { DocServer } from "../DocServer"; import { StrokeData, InkField } from "../../new_fields/InkField"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; -<<<<<<< HEAD import { PDFBox2 } from "../views/pdf/PDFBox2"; -======= import { schema } from "prosemirror-schema-basic"; import { UndoManager } from "../util/UndoManager"; ->>>>>>> 01a223f2e6685506cc1e5db69e9062d5ff0d3246 export interface DocumentOptions { x?: number; @@ -173,8 +170,8 @@ export namespace Docs { return textProto; } function CreatePdfPrototype(): Doc { - let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 1200, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); + let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", PDFBox.LayoutString(), + { x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); return pdfProto; } function CreateWebPrototype(): Doc { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 9b0207d0c..d74dc53c4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -4,8 +4,8 @@ import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; import Measure from "react-measure"; //@ts-ignore -import { Document, Page } from "react-pdf"; -import 'react-pdf/dist/Page/AnnotationLayer.css'; +// import { Document, Page } from "react-pdf"; +// import 'react-pdf/dist/Page/AnnotationLayer.css'; import { RouteStore } from "../../../server/RouteStore"; import { Utils } from '../../../Utils'; import { Annotation } from './Annotation'; @@ -14,7 +14,7 @@ import "./PDFBox.scss"; import React = require("react"); import { SelectionManager } from "../../util/SelectionManager"; import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, HeightSym, Doc } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; import { makeInterface } from "../../../new_fields/Schema"; import { positionSchema } from "./DocumentView"; @@ -53,300 +53,26 @@ const PdfDocument = makeInterface(positionSchema, pageSchema); export class PDFBox extends DocComponent(PdfDocument) { public static LayoutString() { return FieldView.LayoutString(PDFBox); } - private _mainDiv = React.createRef(); - private renderHeight = 2400; - - @observable private _renderAsSvg = true; @observable private _alt = false; - - private _reactionDisposer?: IReactionDisposer; - - @observable private _perPageInfo: Object[] = []; //stores pageInfo - @observable private _pageInfo: any = { area: [], divs: [], anno: [] }; //divs is array of objects linked to anno - - @observable private _currAnno: any = []; @observable private _interactive: boolean = false; - @observable private _loaded: boolean = false; - - @computed private get curPage() { return NumCast(this.Document.curPage, 1); } - @computed private get thumbnailPage() { return NumCast(this.props.Document.thumbnailPage, -1); } - - componentDidMount() { - let wasSelected = false; - this._reactionDisposer = reaction( - () => this.props.isSelected(), - () => { - if (this.curPage > 0 && this.curPage !== this.thumbnailPage && wasSelected && !this.props.isSelected()) { - this.saveThumbnail(); - } - wasSelected = this._interactive = this.props.isSelected(); - }, - { fireImmediately: true }); - - } - - componentWillUnmount() { - if (this._reactionDisposer) this._reactionDisposer(); - } - - /** - * highlighting helper function - */ - makeEditableAndHighlight = (colour: string) => { - var range, sel = window.getSelection(); - if (sel && sel.rangeCount && sel.getRangeAt) { - range = sel.getRangeAt(0); - } - document.designMode = "on"; - if (!document.execCommand("HiliteColor", false, colour)) { - document.execCommand("HiliteColor", false, colour); - } - - if (range && sel) { - sel.removeAllRanges(); - sel.addRange(range); - - let obj: Object = { parentDivs: [], spans: [] }; - //@ts-ignore - if (range.commonAncestorContainer.className === 'react-pdf__Page__textContent') { //multiline highlighting case - obj = this.highlightNodes(range.commonAncestorContainer.childNodes); - } else { //single line highlighting case - let parentDiv = range.commonAncestorContainer.parentElement; - if (parentDiv) { - if (parentDiv.className === 'react-pdf__Page__textContent') { //when highlight is overwritten - obj = this.highlightNodes(parentDiv.childNodes); - } else { - parentDiv.childNodes.forEach((child) => { - if (child.nodeName === 'SPAN') { - //@ts-ignore - obj.parentDivs.push(parentDiv); - //@ts-ignore - child.id = "highlighted"; - //@ts-ignore - obj.spans.push(child); - // child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler - } - }); - } - } - } - this._pageInfo.divs.push(obj); - - } - document.designMode = "off"; - } - - highlightNodes = (nodes: NodeListOf) => { - let temp = { parentDivs: [], spans: [] }; - nodes.forEach((div) => { - div.childNodes.forEach((child) => { - if (child.nodeName === 'SPAN') { - //@ts-ignore - temp.parentDivs.push(div); - //@ts-ignore - child.id = "highlighted"; - //@ts-ignore - temp.spans.push(child); - // child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler - } - }); - - }); - return temp; - } - - /** - * when the cursor enters the highlight, it pops out annotation. ONLY WORKS FOR SINGLE DIV LINES - */ - @action - onEnter = (e: any) => { - let span: HTMLSpanElement = e.toElement; - let index: any; - this._pageInfo.divs.forEach((obj: any) => { - obj.spans.forEach((element: any) => { - if (element === span && !index) { - index = this._pageInfo.divs.indexOf(obj); - } - }); - }); - - if (this._pageInfo.anno.length >= index + 1) { - if (this._currAnno.length === 0) { - this._currAnno.push(this._pageInfo.anno[index]); - } - } else { - if (this._currAnno.length === 0) { //if there are no current annotation - let div = span.offsetParent; - //@ts-ignore - let divX = div.style.left; - //@ts-ignore - let divY = div.style.top; - //slicing "px" from the end - divX = divX.slice(0, divX.length - 2); //gets X of the DIV element (parent of Span) - divY = divY.slice(0, divY.length - 2); //gets Y of the DIV element (parent of Span) - let annotation = ; - this._pageInfo.anno.push(annotation); - this._currAnno.push(annotation); - } - } - - } - /** - * highlight function for highlighting actual text. This works fine. - */ - highlight = (color: string) => { - if (window.getSelection()) { - try { - if (!document.execCommand("hiliteColor", false, color)) { - this.makeEditableAndHighlight(color); - } - } catch (ex) { - this.makeEditableAndHighlight(color); - } + getHeight = (): number => { + if (this.props.Document) { + let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + console.log(doc); + return NumCast(doc.height); } + return 0; } - /** - * controls the area highlighting (stickies) Kinda temporary - */ - onPointerDown = (e: React.PointerEvent) => { - if (this.props.isSelected() && !InkingControl.Instance.selectedTool && e.buttons === 1) { - if (e.altKey) { - this._alt = true; - } else { - if (e.metaKey) { - e.stopPropagation(); - } - } - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } - if (this.props.isSelected() && e.buttons === 2) { - runInAction(() => this._alt = true); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } - } - - /** - * controls area highlighting and partially highlighting. Kinda temporary - */ - @action - onPointerUp = (e: PointerEvent) => { - this._alt = false; - document.removeEventListener("pointerup", this.onPointerUp); - if (this.props.isSelected()) { - this.highlight("rgba(76, 175, 80, 0.3)"); //highlights to this default color. - } - this._interactive = true; - } - - - @action - saveThumbnail = () => { - this._renderAsSvg = false; - setTimeout(() => { - let nwidth = FieldValue(this.Document.nativeWidth, 0); - let nheight = FieldValue(this.Document.nativeHeight, 0); - htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 }) - .then(action((dataUrl: string) => { - this.props.Document.thumbnail = new ImageField(new URL(dataUrl)); - this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1); - this._renderAsSvg = true; - })) - .catch(function (error: any) { - console.error('oops, something went wrong!', error); - }); - }, 1250); - } - - @action - onLoaded = (page: any) => { - // bcz: the number of pages should really be set when the document is imported. - this.props.Document.numPages = page._transport.numPages; - if (this._perPageInfo.length === 0) { //Makes sure it only runs once - this._perPageInfo = [...Array(page._transport.numPages)]; - } - this._loaded = true; - } - - @action - setScaling = (r: any) => { - // bcz: the nativeHeight should really be set when the document is imported. - // also, the native dimensions could be different for different pages of the canvas - // so this design is flawed. - var nativeWidth = FieldValue(this.Document.nativeWidth, 0); - if (!FieldValue(this.Document.nativeHeight, 0)) { - var nativeHeight = nativeWidth * r.offset.height / r.offset.width; - this.props.Document.height = nativeHeight / nativeWidth * FieldValue(this.Document.width, 0); - this.props.Document.nativeHeight = nativeHeight; - } - } - @computed - get pdfPage() { - return ; - } - @computed - get pdfContent() { - trace(); - let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField); - if (!pdfUrl) { - return

No pdf url to render

; - } - let pdfpage = this.pdfPage; - let body = this.Document.nativeHeight ? - pdfpage : - - {({ measureRef }) => -
- {pdfpage} -
- } -
; - let xf = (this.Document.nativeHeight || 0) / this.renderHeight; - return
- - {body} - -
; - } - - @computed - get pdfRenderer() { - let proxy = this._loaded ? (null) : this.imageProxyRenderer; - let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField); - if ((!this._interactive && proxy) || !pdfUrl) { - return proxy; - } - return [ - this._pageInfo.area.filter(() => this._pageInfo.area).map((element: any) => element), - this._currAnno.map((element: any) => element), - this.pdfContent, - proxy - ]; - } - - @computed - get imageProxyRenderer() { - let thumbField = this.props.Document.thumbnail; - if (thumbField) { - let path = this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : - thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; - return ; - } - return (null); - } - @action onKeyDown = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = true); - @action onKeyUp = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = false); render() { trace(); const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
+
e.stopPropagation()} className={classname}> -
+
); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1b445eae4..26becebf1 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -5,6 +5,7 @@ import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; +import "pdfjs-dist/web/pdf_viewer.css"; interface IPDFViewerProps { url: string; @@ -52,8 +53,8 @@ class Viewer extends React.Component { pdf={this.props.pdf} page={i} numPages={numPages} - key={`${this.props.pdf ? this.props.pdf.fingerprint + `page${i + 1}` : "undefined"}`} - name={`${this.props.pdf ? this.props.pdf.fingerprint + `page${i + 1}` : "undefined"}`} + key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} + name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} {...this.props} /> ))} } @@ -149,24 +150,12 @@ class Page extends React.Component { } } - @action - prevPage = (e: React.MouseEvent) => { - if (this._currPage > 2 && this._state !== "rendering") { - this._currPage = Math.max(this._currPage - 1, 1); - this._page = undefined; - this.loadPage(this.props.pdf!); - this._state = "rendering"; - } + onPointerDown = (e: React.PointerEvent) => { + e.stopPropagation(); } - @action - nextPage = (e: React.MouseEvent) => { - if (this._currPage < this.props.numPages - 1 && this._state !== "rendering") { - this._currPage = Math.min(this._currPage + 1, this.props.numPages) - this._page = undefined; - this.loadPage(this.props.pdf!); - this._state = "rendering"; - } + onPointerMove = (e: React.PointerEvent) => { + e.stopPropagation(); } render() { @@ -175,11 +164,7 @@ class Page extends React.Component {
-
- {/*
-
<
-
>
-
*/} +
); } -- cgit v1.2.3-70-g09d2 From e815a45a50e215e1a5c3ada6404226326a8530a9 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 20 May 2019 04:02:23 -0400 Subject: YES --- src/client/views/nodes/PDFBox.tsx | 21 ++++- src/client/views/pdf/PDFViewer.scss | 4 + src/client/views/pdf/PDFViewer.tsx | 165 +++++++++++++++++++++++++++++++++--- 3 files changed, 176 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index d74dc53c4..62c8e1c99 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -54,7 +54,7 @@ export class PDFBox extends DocComponent(PdfDocumen public static LayoutString() { return FieldView.LayoutString(PDFBox); } @observable private _alt = false; - @observable private _interactive: boolean = false; + @observable private _scrollY: number = 0; getHeight = (): number => { if (this.props.Document) { @@ -65,13 +65,28 @@ export class PDFBox extends DocComponent(PdfDocumen return 0; } + loaded = (nw: number, nh: number) => { + if (this.props.Document) { + let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + doc.nativeWidth = nw; + doc.nativeHeight = nh; + } + } + + @action + onScroll = (e: React.UIEvent) => { + if (e.currentTarget) { + this._scrollY = e.currentTarget.scrollTop; + } + } + render() { trace(); const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
e.stopPropagation()} className={classname}> - +
e.stopPropagation()} className={classname}> +
); } diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index d8ff06406..3a6045317 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -16,4 +16,8 @@ border-radius: 5px; } +.textLayer { + user-select: auto; +} + .viewer {} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 26becebf1..6201f6330 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,14 +1,18 @@ import { observer } from "mobx-react"; import React = require("react"); -import { observable, action, runInAction } from "mobx"; +import { observable, action, runInAction, computed } from "mobx"; import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; +import { number } from "prop-types"; +import { JSXElement } from "babel-types"; interface IPDFViewerProps { url: string; + loaded: (nw: number, nh: number) => void; + scrollY: number; } @observer @@ -27,11 +31,9 @@ export class PDFViewer extends React.Component { } render() { - console.log("PDFVIEWER"); - console.log(this._pdf); return (
- +
); } @@ -39,25 +41,166 @@ export class PDFViewer extends React.Component { interface IViewerProps { pdf: Opt; + loaded: (nw: number, nh: number) => void; + scrollY: number; } +type PDFItem = React.Component | HTMLDivElement; + +@observer class Viewer extends React.Component { + @observable.shallow private _visibleElements: JSX.Element[] = []; + @observable private _isPage: boolean[] = []; + @observable private _pageSizes: { width: number, height: number }[] = []; + @observable private _startIndex: number = 0; + @observable private _endIndex: number = 1; + @observable private _loaded: boolean = false; + @observable private _pdf: Opt; + + private _pageBuffer: number = 1; + + @computed get scrollY(): number { + return this.props.scrollY; + } + + @computed get startIndex(): number { + return Math.max(0, this.getIndex(this.scrollY) - this._pageBuffer); + } + + @computed get endIndex(): number { + let width = this._pageSizes.map(i => i.width); + return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer); + } + + componentDidMount = () => { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + this.renderPages(0, numPages - 1, true); + } + + componentDidUpdate = (prevProps: IViewerProps) => { + if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) { + this._pdf = this.props.pdf; + this.renderPages(this.startIndex, this.endIndex); + } + } + + @action + renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + + if (this._visibleElements.length !== numPages) { + let divs = Array.from(Array(numPages).keys()).map(i => ( + + )); + let arr = Array.from(Array(numPages).keys()).map(i => false); + this._visibleElements.push(...divs); + this._isPage.push(...arr); + } + + if (startIndex === this._startIndex && endIndex === this._endIndex && !forceRender) { + return; + } + + for (let i = startIndex; i <= endIndex; i++) { + if (this._isPage[i] && forceRender) { + this._visibleElements[i] = ( + + ); + this._isPage[i] = true; + } + else if (!this._isPage[i]) { + this._visibleElements[i] = ( + + ); + this._isPage[i] = true; + } + } + + for (let i = 0; i < numPages; i++) { + if (i < startIndex || i > endIndex) { + if (this._isPage[i]) { + this._visibleElements[i] = ( +
+ ); + this._isPage[i] = false; + } + } + } + + return; + } + + getIndex = (vOffset: number) => { + if (this._loaded) { + let index = 0; + let currOffset = vOffset; + while (currOffset - this._pageSizes[index].height > 0) { + currOffset -= this._pageSizes[index].height; + index++; + } + return index; + } + return 0; + } + + @action + pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + this.props.loaded(page.width, page.height); + if (index > this._pageSizes.length) { + this._pageSizes.push({ width: page.width, height: page.height }); + } + else { + this._pageSizes[index - 1] = { width: page.width, height: page.height }; + } + if (index === numPages) { + this._loaded = true; + let divs = Array.from(Array(numPages).keys()).map(i => ( +
+ )); + this._visibleElements = new Array(...divs); + } + } + render() { - console.log("VIEWER"); + console.log(`START: ${this.startIndex}`); + console.log(`END: ${this.endIndex}`) let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - console.log(numPages); return (
- {Array.from(Array(numPages).keys()).map((i) => ( + {/* {Array.from(Array(numPages).keys()).map((i) => ( - ))} } + ))} */} + {this._visibleElements}
); } @@ -68,6 +211,7 @@ interface IPageProps { name: string; numPages: number; page: number; + pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; } @observer @@ -113,12 +257,10 @@ class Page extends React.Component { pdf.getPage(this._currPage).then( (page: Pdfjs.PDFPageProxy) => { - console.log("PAGE"); - console.log(page); this._state = "rendering"; this.renderPage(page); } - ) + ); } @action @@ -132,6 +274,7 @@ class Page extends React.Component { this._width = viewport.width; canvas.height = viewport.height; this._height = viewport.height; + this.props.pageLoaded(this._currPage, viewport); if (context) { page.render({ canvasContext: context, viewport: viewport }); page.getTextContent().then((res: Pdfjs.TextContent) => { -- cgit v1.2.3-70-g09d2 From 1fb7a7bc185c1ba9bbe0f21ad5e16cf19235b2da Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 4 Jun 2019 13:59:04 -0400 Subject: updatesss --- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFViewer.scss | 6 +++- src/client/views/pdf/PDFViewer.tsx | 59 +++++++++++++++++++++++++++++-------- 3 files changed, 53 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 62c8e1c99..dd945b030 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -86,7 +86,7 @@ export class PDFBox extends DocComponent(PdfDocumen let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
e.stopPropagation()} className={classname}> - +
); } diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 3a6045317..9d41a1bb0 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -1,4 +1,8 @@ -.canvasContainer {} +.textLayer { + div { + user-select: text; + } +} .viewer-button-cont { position: absolute; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 6201f6330..d510ba91c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,18 +1,24 @@ import { observer } from "mobx-react"; import React = require("react"); -import { observable, action, runInAction, computed } from "mobx"; +import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; +import * as htmlToImage from "html-to-image"; import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { number } from "prop-types"; import { JSXElement } from "babel-types"; +import { PDFBox } from "../nodes/PDFBox"; +import { NumCast, FieldValue } from "../../../new_fields/Types"; +import { SearchBox } from "../SearchBox"; +import { Utils } from "../../../Utils"; interface IPDFViewerProps { url: string; loaded: (nw: number, nh: number) => void; scrollY: number; + parent: PDFBox; } @observer @@ -33,7 +39,7 @@ export class PDFViewer extends React.Component { render() { return (
- +
); } @@ -43,10 +49,9 @@ interface IViewerProps { pdf: Opt; loaded: (nw: number, nh: number) => void; scrollY: number; + parent: PDFBox; } -type PDFItem = React.Component | HTMLDivElement; - @observer class Viewer extends React.Component { @observable.shallow private _visibleElements: JSX.Element[] = []; @@ -56,8 +61,41 @@ class Viewer extends React.Component { @observable private _endIndex: number = 1; @observable private _loaded: boolean = false; @observable private _pdf: Opt; + @observable private _renderAsSvg = true; private _pageBuffer: number = 1; + private _reactionDisposer?: IReactionDisposer; + private _mainDiv = React.createRef(); + + @computed private get thumbnailPage() { return NumCast(this.props.parent.Document.thumbnailPage, -1); } + + componentDidMount() { + let wasSelected = this.props.parent.props.isSelected(); + this._reactionDisposer = reaction( + () => [this.props.parent.props.isSelected(), this.startIndex], + () => { + if (this.startIndex > 0 && !this.props.parent.props.isTopMost && this.startIndex !== this.thumbnailPage && wasSelected && !this.props.parent.props.isSelected()) { + this.saveThumbnail(); + } + wasSelected = this.props.parent.props.isSelected(); + }, + { fireImmediately: true } + ); + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + this.renderPages(0, numPages - 1, true); + } + + saveThumbnail = () => { + this.props.parent.props.Document.thumbnailPage = FieldValue(this.props.parent.Document.curPage, -1); + this._renderAsSvg = false; + setTimeout(() => { + let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); + let nheight = FieldValue(this.props.parent.Document.nativeHeight, 0); + htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 0.8 }) + .then(action((dataUrl: string) => { + })); + }, 1250); + } @computed get scrollY(): number { return this.props.scrollY; @@ -72,11 +110,6 @@ class Viewer extends React.Component { return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer); } - componentDidMount = () => { - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.renderPages(0, numPages - 1, true); - } - componentDidUpdate = (prevProps: IViewerProps) => { if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) { this._pdf = this.props.pdf; @@ -188,7 +221,7 @@ class Viewer extends React.Component { console.log(`END: ${this.endIndex}`) let numPages = this.props.pdf ? this.props.pdf.numPages : 0; return ( -
+
{/* {Array.from(Array(numPages).keys()).map((i) => ( { } onPointerDown = (e: React.PointerEvent) => { + console.log("down"); e.stopPropagation(); } onPointerMove = (e: React.PointerEvent) => { + console.log("move") e.stopPropagation(); } render() { return ( -
+
-
+
); } -- cgit v1.2.3-70-g09d2 From e93304523b10f7c6da85cebdfae05a3b1b6eb84c Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 4 Jun 2019 14:03:51 -0400 Subject: merge^ --- src/client/documents/Documents.ts | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index be7356a09..973c90ff4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -33,11 +33,6 @@ import { DocServer } from "../DocServer"; import { StrokeData, InkField } from "../../new_fields/InkField"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; -<<<<<<< HEAD -import { PDFBox2 } from "../views/pdf/PDFBox2"; -import { schema } from "prosemirror-schema-basic"; -======= ->>>>>>> 7d3ef1c914cc1cc0b6c05b14773a8b83e1b95c96 import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; var requestImageSize = require('request-image-size'); -- cgit v1.2.3-70-g09d2 From cffe1d2d45e241a21fb071573399200567738163 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 4 Jun 2019 15:45:33 -0400 Subject: additionsss --- src/client/views/nodes/PDFBox.tsx | 10 +----- src/client/views/pdf/PDFViewer.tsx | 63 ++++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 83f69f7f9..217d7b5e1 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -7,23 +7,15 @@ import Measure from "react-measure"; // import { Document, Page } from "react-pdf"; // import 'react-pdf/dist/Page/AnnotationLayer.css'; import { RouteStore } from "../../../server/RouteStore"; -import { Utils } from '../../../Utils'; -import { DocServer } from "../../DocServer"; import { DocComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; -import { SearchBox } from "../SearchBox"; -import { Annotation } from './Annotation'; import { positionSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; -var path = require('path'); import React = require("react"); -import { SelectionManager } from "../../util/SelectionManager"; -import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; -import { Opt, HeightSym, Doc } from "../../../new_fields/Doc"; +import { NumCast } from "../../../new_fields/Types"; import { makeInterface } from "../../../new_fields/Schema"; -import { ImageField, PdfField } from "../../../new_fields/URLField"; import { PDFViewer } from "../pdf/PDFViewer"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d510ba91c..999cb6378 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -13,6 +13,10 @@ import { PDFBox } from "../nodes/PDFBox"; import { NumCast, FieldValue } from "../../../new_fields/Types"; import { SearchBox } from "../SearchBox"; import { Utils } from "../../../Utils"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { DocServer } from "../../DocServer"; +import { ImageField } from "../../../new_fields/URLField"; +var path = require("path"); interface IPDFViewerProps { url: string; @@ -24,6 +28,7 @@ interface IPDFViewerProps { @observer export class PDFViewer extends React.Component { @observable _pdf: Opt; + private _mainDiv = React.createRef(); @action componentDidMount() { @@ -38,8 +43,8 @@ export class PDFViewer extends React.Component { render() { return ( -
- +
+
); } @@ -50,6 +55,7 @@ interface IViewerProps { loaded: (nw: number, nh: number) => void; scrollY: number; parent: PDFBox; + mainCont: React.RefObject; } @observer @@ -61,20 +67,19 @@ class Viewer extends React.Component { @observable private _endIndex: number = 1; @observable private _loaded: boolean = false; @observable private _pdf: Opt; - @observable private _renderAsSvg = true; + @observable private _renderAsSvg = false; private _pageBuffer: number = 1; private _reactionDisposer?: IReactionDisposer; - private _mainDiv = React.createRef(); - @computed private get thumbnailPage() { return NumCast(this.props.parent.Document.thumbnailPage, -1); } + @computed private get thumbnailY() { return NumCast(this.props.parent.Document.thumbnailY, -1); } - componentDidMount() { + componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); this._reactionDisposer = reaction( () => [this.props.parent.props.isSelected(), this.startIndex], () => { - if (this.startIndex > 0 && !this.props.parent.props.isTopMost && this.startIndex !== this.thumbnailPage && wasSelected && !this.props.parent.props.isSelected()) { + if (this.startIndex >= 0 && !this.props.parent.props.isTopMost && this.scrollY !== this.thumbnailY && wasSelected && !this.props.parent.props.isSelected()) { this.saveThumbnail(); } wasSelected = this.props.parent.props.isSelected(); @@ -86,14 +91,23 @@ class Viewer extends React.Component { } saveThumbnail = () => { - this.props.parent.props.Document.thumbnailPage = FieldValue(this.props.parent.Document.curPage, -1); + this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0); this._renderAsSvg = false; setTimeout(() => { let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); - let nheight = FieldValue(this.props.parent.Document.nativeHeight, 0); - htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 0.8 }) + htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, }) .then(action((dataUrl: string) => { - })); + SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => { + if (returnedFilename) { + let url = DocServer.prepend(returnedFilename); + this.props.parent.props.Document.thumbnail = new ImageField(new URL(url)); + } + runInAction(() => this._renderAsSvg = true); + }); + })) + .catch(function (error: any) { + console.error("Oops, something went wrong!", error); + }); }, 1250); } @@ -101,6 +115,29 @@ class Viewer extends React.Component { return this.props.scrollY; } + @computed get imageProxyRenderer() { + let thumbField = this.props.parent.props.Document.thumbnail; + if (thumbField && this._renderAsSvg && NumCast(this.props.parent.props.Document.startY, 0) === this.scrollY) { + let pw = typeof this.props.parent.props.PanelWidth === "function" ? this.props.parent.props.PanelWidth() : typeof this.props.parent.props.PanelWidth === "number" ? (this.props.parent.props.PanelWidth as any) as number : 50; + let path = thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; + let field = thumbField; + if (field instanceof ImageField) path = this.choosePath(field.url); + return ; + } + } + + @action onError = () => { + } + + choosePath(url: URL) { + if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) { + return url.href; + } + let ext = path.extname(url.href); + ///TODO: Not done lol - syip2 + return url.href; + } + @computed get startIndex(): number { return Math.max(0, this.getIndex(this.scrollY) - this._pageBuffer); } @@ -221,7 +258,7 @@ class Viewer extends React.Component { console.log(`END: ${this.endIndex}`) let numPages = this.props.pdf ? this.props.pdf.numPages : 0; return ( -
+
{/* {Array.from(Array(numPages).keys()).map((i) => ( { {...this.props} /> ))} */} - {this._visibleElements} + {this._renderAsSvg ? this.imageProxyRenderer : this._visibleElements}
); } -- cgit v1.2.3-70-g09d2 From 1708f2b2a19d3d4efc081bcc4ee82b4d5149da08 Mon Sep 17 00:00:00 2001 From: loudonclear Date: Tue, 4 Jun 2019 19:08:01 -0400 Subject: YES --- package.json | 1 + src/client/views/nodes/PDFBox.tsx | 4 +-- src/client/views/pdf/PDFViewer.tsx | 50 ++++++++++++++++++-------------- src/server/Search.ts | 3 +- src/server/index.ts | 58 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/package.json b/package.json index 58b2cb049..648ebdbf9 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "bluebird": "^3.5.3", "body-parser": "^1.18.3", "bootstrap": "^4.3.1", + "canvas": "^2.5.0", "class-transformer": "^0.2.0", "connect-flash": "^0.1.1", "connect-mongo": "^2.0.3", diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 217d7b5e1..7e335e75e 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -14,7 +14,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { NumCast } from "../../../new_fields/Types"; +import { NumCast, StrCast } from "../../../new_fields/Types"; import { makeInterface } from "../../../new_fields/Schema"; import { PDFViewer } from "../pdf/PDFViewer"; @@ -77,7 +77,7 @@ export class PDFBox extends DocComponent(PdfDocumen render() { trace(); - const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; + const pdfUrl = window.origin + RouteStore.corsProxy + "/" + StrCast(this.props.Document.title); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
e.stopPropagation()} className={classname}> diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 999cb6378..374e598a4 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -91,24 +91,24 @@ class Viewer extends React.Component { } saveThumbnail = () => { - this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0); - this._renderAsSvg = false; - setTimeout(() => { - let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); - htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, }) - .then(action((dataUrl: string) => { - SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => { - if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); - this.props.parent.props.Document.thumbnail = new ImageField(new URL(url)); - } - runInAction(() => this._renderAsSvg = true); - }); - })) - .catch(function (error: any) { - console.error("Oops, something went wrong!", error); - }); - }, 1250); + // this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0); + // this._renderAsSvg = false; + // setTimeout(() => { + // let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); + // htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, }) + // .then(action((dataUrl: string) => { + // SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => { + // if (returnedFilename) { + // let url = DocServer.prepend(returnedFilename); + // this.props.parent.props.Document.thumbnail = new ImageField(new URL(url)); + // } + // runInAction(() => this._renderAsSvg = true); + // }); + // })) + // .catch(function (error: any) { + // console.error("Oops, something went wrong!", error); + // }); + // }, 1250); } @computed get scrollY(): number { @@ -338,15 +338,23 @@ class Page extends React.Component { let scale = 1; let viewport = page.getViewport(scale); let canvas = this.canvas.current; - if (canvas) { - let context = canvas.getContext("2d"); + let tempCanvas = document.createElement('canvas') + if (tempCanvas && canvas) { + let ctx = canvas.getContext("2d"); + let context = tempCanvas.getContext("2d"); + tempCanvas.width = viewport.width; canvas.width = viewport.width; this._width = viewport.width; + tempCanvas.height = viewport.height; canvas.height = viewport.height; this._height = viewport.height; this.props.pageLoaded(this._currPage, viewport); if (context) { - page.render({ canvasContext: context, viewport: viewport }); + page.render({ canvasContext: context, viewport: viewport }).promise.then(() => { + if (context && ctx) { + ctx.putImageData(context.getImageData(0, 0, tempCanvas.width, tempCanvas.height), 0, 0); + } + }); page.getTextContent().then((res: Pdfjs.TextContent) => { //@ts-ignore let textLayer = Pdfjs.renderTextLayer({ diff --git a/src/server/Search.ts b/src/server/Search.ts index 5ca5578a7..fd6ef36a6 100644 --- a/src/server/Search.ts +++ b/src/server/Search.ts @@ -7,6 +7,7 @@ export class Search { private url = 'http://localhost:8983/solr/'; public async updateDocument(document: any) { + return; try { const res = await rp.post(this.url + "dash/update", { headers: { 'content-type': 'application/json' }, @@ -14,7 +15,7 @@ export class Search { }); return res; } catch (e) { - console.warn("Search error: " + e + document); + // console.warn("Search error: " + e + document); } } diff --git a/src/server/index.ts b/src/server/index.ts index fd66c90b4..9f95df4e0 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -7,6 +7,7 @@ import * as expressValidator from 'express-validator'; import * as formidable from 'formidable'; import * as fs from 'fs'; import * as sharp from 'sharp'; +import * as Pdfjs from 'pdfjs-dist'; const imageDataUri = require('image-data-uri'); import * as mobileDetect from 'mobile-detect'; import { ObservableMap } from 'mobx'; @@ -28,6 +29,7 @@ import { MessageStore, Transferable, Types, Diff } from "./Message"; import { RouteStore } from './RouteStore'; const app = express(); const config = require('../../webpack.config'); +import { createCanvas, loadImage, Canvas } from "canvas"; const compiler = webpack(config); const port = 1050; // default port to listen const serverPort = 4321; @@ -168,7 +170,31 @@ addSecureRoute( RouteStore.getCurrUser ); +class NodeCanvasFactory { + create = (width: number, height: number) => { + var canvas = createCanvas(width, height); + var context = canvas.getContext('2d'); + return { + canvas: canvas, + context: context, + }; + } + + reset = (canvasAndContext: any, width: number, height: number) => { + canvasAndContext.canvas.width = width; + canvasAndContext.canvas.height = height; + } + + destroy = (canvasAndContext: any) => { + canvasAndContext.canvas.width = 0; + canvasAndContext.canvas.height = 0; + canvasAndContext.canvas = null; + canvasAndContext.context = null; + } +} + const pngTypes = [".png", ".PNG"]; +const pdfTypes = [".pdf", ".PDF"]; const jpgTypes = [".jpg", ".JPG", ".jpeg", ".JPEG"]; const uploadDir = __dirname + "/public/files/"; // SETTERS @@ -203,6 +229,38 @@ app.post( }); isImage = true; } + else if (pdfTypes.includes(ext)) { + Pdfjs.getDocument(uploadDir + file).promise + .then((pdf: Pdfjs.PDFDocumentProxy) => { + let numPages = pdf.numPages; + let factory = new NodeCanvasFactory(); + for (let pageNum = 0; pageNum < numPages; pageNum++) { + console.log(pageNum); + pdf.getPage(pageNum + 1).then((page: Pdfjs.PDFPageProxy) => { + console.log("reading " + pageNum); + let viewport = page.getViewport(1); + let canvasAndContext = factory.create(viewport.width, viewport.height); + let renderContext = { + canvasContext: canvasAndContext.context, + viewport: viewport, + canvasFactory: factory + } + console.log("read " + pageNum); + + page.render(renderContext).promise + .then(() => { + console.log("saving " + pageNum); + let stream = canvasAndContext.canvas.createPNGStream(); + let out = fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`); + stream.pipe(out); + out.on("finish", () => console.log(`Success! Saved to ${uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`}`)); + }, (reason: string) => { + console.error(reason + ` ${pageNum}`); + }); + }); + } + }); + } if (isImage) { resizers.forEach(resizer => { fs.createReadStream(uploadDir + file).pipe(resizer.resizer).pipe(fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + resizer.suffix + ext)); -- cgit v1.2.3-70-g09d2 From f774314ddae00f2d2ceda886f0b6699f01f80718 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 5 Jun 2019 16:33:57 -0400 Subject: better virtualization and thumbnails git status --- src/client/views/nodes/PDFBox.tsx | 12 ++- src/client/views/pdf/PDFViewer.scss | 4 + src/client/views/pdf/PDFViewer.tsx | 158 ++++++++++++++++-------------------- 3 files changed, 82 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 7e335e75e..6d604ec75 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -14,9 +14,11 @@ import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { NumCast, StrCast } from "../../../new_fields/Types"; +import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; import { makeInterface } from "../../../new_fields/Schema"; import { PDFViewer } from "../pdf/PDFViewer"; +import { PdfField } from "../../../new_fields/URLField"; +import { HeightSym, WidthSym } from "../../../new_fields/Doc"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, @@ -65,6 +67,7 @@ export class PDFBox extends DocComponent(PdfDocumen let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; doc.nativeWidth = nw; doc.nativeHeight = nh; + doc.height = nh * (doc[WidthSym]() / nw); } } @@ -77,11 +80,12 @@ export class PDFBox extends DocComponent(PdfDocumen render() { trace(); - const pdfUrl = window.origin + RouteStore.corsProxy + "/" + StrCast(this.props.Document.title); + const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); + console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
e.stopPropagation()} className={classname}> - +
e.stopPropagation()} className={classname}> +
); } diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 9d41a1bb0..42bf023d6 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -24,4 +24,8 @@ user-select: auto; } +.pdfBox-cont-interactive { + overflow-x: visible; +} + .viewer {} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 374e598a4..84d9f1ad0 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -4,18 +4,18 @@ import { observable, action, runInAction, computed, IReactionDisposer, reaction import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; import * as htmlToImage from "html-to-image"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, WidthSym } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { number } from "prop-types"; import { JSXElement } from "babel-types"; import { PDFBox } from "../nodes/PDFBox"; -import { NumCast, FieldValue } from "../../../new_fields/Types"; +import { NumCast, FieldValue, Cast } from "../../../new_fields/Types"; import { SearchBox } from "../SearchBox"; import { Utils } from "../../../Utils"; import { Id } from "../../../new_fields/FieldSymbols"; import { DocServer } from "../../DocServer"; -import { ImageField } from "../../../new_fields/URLField"; +import { ImageField, PdfField } from "../../../new_fields/URLField"; var path = require("path"); interface IPDFViewerProps { @@ -44,7 +44,7 @@ export class PDFViewer extends React.Component { render() { return (
- +
); } @@ -56,6 +56,7 @@ interface IViewerProps { scrollY: number; parent: PDFBox; mainCont: React.RefObject; + url: string; } @observer @@ -71,71 +72,63 @@ class Viewer extends React.Component { private _pageBuffer: number = 1; private _reactionDisposer?: IReactionDisposer; - - @computed private get thumbnailY() { return NumCast(this.props.parent.Document.thumbnailY, -1); } + private _widthReactionDisposer?: IReactionDisposer; + private _width: number = 0; componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); this._reactionDisposer = reaction( () => [this.props.parent.props.isSelected(), this.startIndex], () => { - if (this.startIndex >= 0 && !this.props.parent.props.isTopMost && this.scrollY !== this.thumbnailY && wasSelected && !this.props.parent.props.isSelected()) { + if (wasSelected && !this.props.parent.props.isSelected()) { this.saveThumbnail(); } + else if (!wasSelected && this.props.parent.props.isSelected()) { + this.renderPages(this.startIndex, this.endIndex, true); + } wasSelected = this.props.parent.props.isSelected(); }, { fireImmediately: true } ); - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.renderPages(0, numPages - 1, true); - } - saveThumbnail = () => { - // this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0); - // this._renderAsSvg = false; - // setTimeout(() => { - // let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); - // htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, }) - // .then(action((dataUrl: string) => { - // SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => { - // if (returnedFilename) { - // let url = DocServer.prepend(returnedFilename); - // this.props.parent.props.Document.thumbnail = new ImageField(new URL(url)); - // } - // runInAction(() => this._renderAsSvg = true); - // }); - // })) - // .catch(function (error: any) { - // console.error("Oops, something went wrong!", error); - // }); - // }, 1250); - } + // this._widthReactionDisposer = reaction( + // () => [this._docWidth], + // () => { + // if (this._width !== this._docWidth) { + // this._width = this._docWidth; + // this.renderPages(this.startIndex, this.endIndex, true); + // console.log(this._width); + // } + // }, + // { fireImmediately: true } + // ) - @computed get scrollY(): number { - return this.props.scrollY; + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + this.renderPages(0, numPages - 1, true); } - @computed get imageProxyRenderer() { - let thumbField = this.props.parent.props.Document.thumbnail; - if (thumbField && this._renderAsSvg && NumCast(this.props.parent.props.Document.startY, 0) === this.scrollY) { - let pw = typeof this.props.parent.props.PanelWidth === "function" ? this.props.parent.props.PanelWidth() : typeof this.props.parent.props.PanelWidth === "number" ? (this.props.parent.props.PanelWidth as any) as number : 50; - let path = thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; - let field = thumbField; - if (field instanceof ImageField) path = this.choosePath(field.url); - return ; + componentWillUnmount = () => { + if (this._reactionDisposer) { + this._reactionDisposer(); } } - @action onError = () => { + @action + saveThumbnail = () => { + const address: string = this.props.url; + console.log(address); + for (let i = 0; i < this._visibleElements.length; i++) { + if (this._isPage[i]) { + let thisAddress = `${address.substring(0, address.length - ".pdf".length)}-${i + 1}.PNG`; + let nWidth = this._pageSizes[i].width; + let nHeight = this._pageSizes[i].height; + this._visibleElements[i] = ; + } + } } - choosePath(url: URL) { - if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) { - return url.href; - } - let ext = path.extname(url.href); - ///TODO: Not done lol - syip2 - return url.href; + @computed get scrollY(): number { + return this.props.scrollY; } @computed get startIndex(): number { @@ -157,6 +150,9 @@ class Viewer extends React.Component { @action renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => { let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + if (!this.props.pdf) { + return; + } if (this._visibleElements.length !== numPages) { let divs = Array.from(Array(numPages).keys()).map(i => ( @@ -178,21 +174,19 @@ class Viewer extends React.Component { return; } - for (let i = startIndex; i <= endIndex; i++) { - if (this._isPage[i] && forceRender) { - this._visibleElements[i] = ( - - ); - this._isPage[i] = true; + for (let i = 0; i < numPages; i++) { + if (i < startIndex || i > endIndex) { + if (this._isPage[i]) { + this._visibleElements[i] = ( +
+ ); + } + this._isPage[i] = false; } - else if (!this._isPage[i]) { + } + + for (let i = startIndex; i <= endIndex; i++) { + if (!this._isPage[i] || forceRender) { this._visibleElements[i] = ( { } } - for (let i = 0; i < numPages; i++) { - if (i < startIndex || i > endIndex) { - if (this._isPage[i]) { - this._visibleElements[i] = ( -
- ); - this._isPage[i] = false; - } - } - } + this._startIndex = startIndex; + this._endIndex = endIndex; return; } getIndex = (vOffset: number) => { if (this._loaded) { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; let index = 0; let currOffset = vOffset; - while (currOffset - this._pageSizes[index].height > 0) { + while (index < numPages && currOffset - this._pageSizes[index].height > 0) { currOffset -= this._pageSizes[index].height; index++; } @@ -236,6 +223,9 @@ class Viewer extends React.Component { @action pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { + if (this._loaded) { + return; + } let numPages = this.props.pdf ? this.props.pdf.numPages : 0; this.props.loaded(page.width, page.height); if (index > this._pageSizes.length) { @@ -270,7 +260,7 @@ class Viewer extends React.Component { {...this.props} /> ))} */} - {this._renderAsSvg ? this.imageProxyRenderer : this._visibleElements} + {this._visibleElements}
); } @@ -338,32 +328,24 @@ class Page extends React.Component { let scale = 1; let viewport = page.getViewport(scale); let canvas = this.canvas.current; - let tempCanvas = document.createElement('canvas') - if (tempCanvas && canvas) { + let textLayer = this.textLayer.current; + if (canvas && textLayer) { let ctx = canvas.getContext("2d"); - let context = tempCanvas.getContext("2d"); - tempCanvas.width = viewport.width; canvas.width = viewport.width; this._width = viewport.width; - tempCanvas.height = viewport.height; canvas.height = viewport.height; this._height = viewport.height; this.props.pageLoaded(this._currPage, viewport); - if (context) { - page.render({ canvasContext: context, viewport: viewport }).promise.then(() => { - if (context && ctx) { - ctx.putImageData(context.getImageData(0, 0, tempCanvas.width, tempCanvas.height), 0, 0); - } - }); + if (ctx) { + page.render({ canvasContext: ctx, viewport: viewport }) page.getTextContent().then((res: Pdfjs.TextContent) => { //@ts-ignore - let textLayer = Pdfjs.renderTextLayer({ + Pdfjs.renderTextLayer({ textContent: res, - container: this.textLayer.current, + container: textLayer, viewport: viewport }); // textLayer._render(); - this._state = "rendered"; }); this._page = page; -- cgit v1.2.3-70-g09d2 From c4180929255db3bc7a75938e07afd86522f0a15e Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 5 Jun 2019 18:09:39 -0400 Subject: bug fixess --- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFViewer.scss | 4 ---- src/client/views/pdf/PDFViewer.tsx | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 6d604ec75..4ee3ae098 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -84,7 +84,7 @@ export class PDFBox extends DocComponent(PdfDocumen console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
e.stopPropagation()} className={classname}> +
e.stopPropagation()} className={classname}>
); diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 42bf023d6..9d41a1bb0 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -24,8 +24,4 @@ user-select: auto; } -.pdfBox-cont-interactive { - overflow-x: visible; -} - .viewer {} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 84d9f1ad0..95f31bb89 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -104,7 +104,7 @@ class Viewer extends React.Component { // ) let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.renderPages(0, numPages - 1, true); + setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); } componentWillUnmount = () => { -- cgit v1.2.3-70-g09d2 From 727c8be8d51766f483a90edf07366b5840f5a4f6 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 5 Jun 2019 23:00:10 -0400 Subject: screen transform remaining --- src/client/views/pdf/PDFViewer.scss | 6 ++++- src/client/views/pdf/PDFViewer.tsx | 48 ++++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 9d41a1bb0..484d9dc04 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -24,4 +24,8 @@ user-select: auto; } -.viewer {} \ No newline at end of file +.pdfViewer-annotationBox { + position: absolute; + background-color: red; + opacity: 0.5; +} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 95f31bb89..a76527618 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -354,18 +354,54 @@ class Page extends React.Component { } onPointerDown = (e: React.PointerEvent) => { - console.log("down"); - e.stopPropagation(); + if (e.button === 0) { + e.stopPropagation(); + document.addEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointerup", this.onPointerUp); + } + } + + onPointerMove = (e: PointerEvent) => { + if (e.button === 0) { + e.stopPropagation(); + } } - onPointerMove = (e: React.PointerEvent) => { - console.log("move") - e.stopPropagation(); + onPointerUp = (e: PointerEvent) => { + let sel = window.getSelection(); + if (sel && sel.type === "Range") { + // console.log(sel.getRangeAt(0)); + let commonContainer = sel.getRangeAt(0).commonAncestorContainer; + let startContainer = sel.getRangeAt(0).startContainer; + let endContainer = sel.getRangeAt(0).endContainer; + let clientRects = sel.getRangeAt(0).getClientRects(); + console.log(sel.getRangeAt(0)); + let annoBoxes = []; + if (this.textLayer.current) { + // let transform = Utils.GetScreenTransform(this.textLayer.current); + console.log(transform); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + annoBox.style.top = rect.top.toString(); + annoBox.style.left = rect.left.toString(); + annoBox.style.width = rect.width.toString(); + annoBox.style.height = rect.height.toString(); + // annoBox.style.transform = `scale(${1 / transform.scale}) translate(-${transform.translateX * transform.scale}px, -${transform.translateY * transform.scale}px)`; + this.textLayer.current.appendChild(annoBox); + } + } + } + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); } render() { return ( -
+
-- cgit v1.2.3-70-g09d2 From a37629f55ef279167a5ef2fec88dc548f36f4938 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Thu, 6 Jun 2019 14:13:55 -0400 Subject: commentss --- src/client/views/nodes/PDFBox.tsx | 38 +--------- src/client/views/pdf/PDFViewer.tsx | 147 ++++++++++++++++++------------------- 2 files changed, 77 insertions(+), 108 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 4ee3ae098..de75c67cf 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -20,29 +20,6 @@ import { PDFViewer } from "../pdf/PDFViewer"; import { PdfField } from "../../../new_fields/URLField"; import { HeightSym, WidthSym } from "../../../new_fields/Doc"; -/** ALSO LOOK AT: Annotation.tsx, Sticky.tsx - * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, - * area selection (I call it stickies), embedded ink node for directly annotating using a pen or - * mouse, and pagination. - * - * - * HOW TO USE: - * AREA selection: - * 1) Click on Area button. - * 2) click on any part of the PDF, and drag to get desired sized area shape - * 3) You can write on the area (hence the reason why it's called sticky) - * 4) to make another area, you need to click on area button AGAIN. - * - * HIGHLIGHT: (Buggy. No multiline/multidiv text highlighting for now...) - * 1) just click and drag on a text - * 2) click highlight - * 3) for annotation, just pull your cursor over to that text - * 4) another method: click on highlight first and then drag on your desired text - * 5) To make another highlight, you need to reclick on the button - * - * written by: Andrew Kim - */ - type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -53,15 +30,6 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _alt = false; @observable private _scrollY: number = 0; - getHeight = (): number => { - if (this.props.Document) { - let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; - console.log(doc); - return NumCast(doc.height); - } - return 0; - } - loaded = (nw: number, nh: number) => { if (this.props.Document) { let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; @@ -80,11 +48,13 @@ export class PDFBox extends DocComponent(PdfDocumen render() { trace(); + // uses mozilla pdf as default const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); - console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
e.stopPropagation()} className={classname}> +
e.stopPropagation()} className={classname}>
); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index a76527618..d5a0a7aa1 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,22 +1,11 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; -import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; -import * as htmlToImage from "html-to-image"; -import { Opt, WidthSym } from "../../../new_fields/Doc"; +import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; -import { number } from "prop-types"; -import { JSXElement } from "babel-types"; import { PDFBox } from "../nodes/PDFBox"; -import { NumCast, FieldValue, Cast } from "../../../new_fields/Types"; -import { SearchBox } from "../SearchBox"; -import { Utils } from "../../../Utils"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { DocServer } from "../../DocServer"; -import { ImageField, PdfField } from "../../../new_fields/URLField"; -var path = require("path"); interface IPDFViewerProps { url: string; @@ -25,6 +14,9 @@ interface IPDFViewerProps { parent: PDFBox; } +/** + * Wrapper that loads the PDF and cascades the pdf down + */ @observer export class PDFViewer extends React.Component { @observable _pdf: Opt; @@ -32,7 +24,6 @@ export class PDFViewer extends React.Component { @action componentDidMount() { - // const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; const pdfUrl = this.props.url; let promise = Pdfjs.getDocument(pdfUrl).promise; @@ -59,30 +50,35 @@ interface IViewerProps { url: string; } +/** + * Handles rendering and virtualization of the pdf + */ @observer class Viewer extends React.Component { + // _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: boolean[] = []; @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _startIndex: number = 0; @observable private _endIndex: number = 1; @observable private _loaded: boolean = false; @observable private _pdf: Opt; - @observable private _renderAsSvg = false; private _pageBuffer: number = 1; private _reactionDisposer?: IReactionDisposer; - private _widthReactionDisposer?: IReactionDisposer; - private _width: number = 0; componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); + // reaction for when document gets (de)selected this._reactionDisposer = reaction( () => [this.props.parent.props.isSelected(), this.startIndex], () => { + // if deselected, render images in place of pdf if (wasSelected && !this.props.parent.props.isSelected()) { this.saveThumbnail(); } + // if selected, render pdf else if (!wasSelected && this.props.parent.props.isSelected()) { this.renderPages(this.startIndex, this.endIndex, true); } @@ -91,19 +87,7 @@ class Viewer extends React.Component { { fireImmediately: true } ); - // this._widthReactionDisposer = reaction( - // () => [this._docWidth], - // () => { - // if (this._width !== this._docWidth) { - // this._width = this._docWidth; - // this.renderPages(this.startIndex, this.endIndex, true); - // console.log(this._width); - // } - // }, - // { fireImmediately: true } - // ) - - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + // On load, render pdf setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); } @@ -115,13 +99,15 @@ class Viewer extends React.Component { @action saveThumbnail = () => { + // file address of the pdf const address: string = this.props.url; - console.log(address); for (let i = 0; i < this._visibleElements.length; i++) { if (this._isPage[i]) { + // change the address to be the file address of the PNG version of each page let thisAddress = `${address.substring(0, address.length - ".pdf".length)}-${i + 1}.PNG`; let nWidth = this._pageSizes[i].width; let nHeight = this._pageSizes[i].height; + // replace page with image this._visibleElements[i] = ; } } @@ -143,10 +129,16 @@ class Viewer extends React.Component { componentDidUpdate = (prevProps: IViewerProps) => { if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) { this._pdf = this.props.pdf; + // render pages if the scorll position changes this.renderPages(this.startIndex, this.endIndex); } } + /** + * @param startIndex: where to start rendering pages + * @param endIndex: where to end rendering pages + * @param forceRender: (optional), force pdfs to re-render, even if the page already exists + */ @action renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => { let numPages = this.props.pdf ? this.props.pdf.numPages : 0; @@ -154,6 +146,7 @@ class Viewer extends React.Component { return; } + // this is only for an initial render to get all of the pages rendered if (this._visibleElements.length !== numPages) { let divs = Array.from(Array(numPages).keys()).map(i => ( { this._isPage.push(...arr); } + // if nothing changed, return if (startIndex === this._startIndex && endIndex === this._endIndex && !forceRender) { return; } + // unrender pages outside of the pdf by replacing them with empty stand-in divs for (let i = 0; i < numPages; i++) { if (i < startIndex || i > endIndex) { if (this._isPage[i]) { @@ -185,6 +180,7 @@ class Viewer extends React.Component { } } + // render pages for any indices that don't already have pages (force rerender will make these render regardless) for (let i = startIndex; i <= endIndex; i++) { if (!this._isPage[i] || forceRender) { this._visibleElements[i] = ( @@ -207,6 +203,7 @@ class Viewer extends React.Component { return; } + // get the page index that the vertical offset passed in is on getIndex = (vOffset: number) => { if (this._loaded) { let numPages = this.props.pdf ? this.props.pdf.numPages : 0; @@ -221,6 +218,10 @@ class Viewer extends React.Component { return 0; } + /** + * 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 => { if (this._loaded) { @@ -244,22 +245,8 @@ class Viewer extends React.Component { } render() { - console.log(`START: ${this.startIndex}`); - console.log(`END: ${this.endIndex}`) - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; return (
- {/* {Array.from(Array(numPages).keys()).map((i) => ( - - ))} */} {this._visibleElements}
); @@ -276,22 +263,23 @@ interface IPageProps { @observer class Page extends React.Component { - @observable _state: string = "N/A"; - @observable _width: number = 0; - @observable _height: number = 0; - @observable _page: Opt; - canvas: React.RefObject; - textLayer: React.RefObject; - @observable _currPage: number = this.props.page + 1; + @observable private _state: string = "N/A"; + @observable private _width: number = 0; + @observable private _height: number = 0; + @observable private _page: Opt; + @observable private _currPage: number = this.props.page + 1; + + private _canvas: React.RefObject; + private _currentAnnotations: HTMLDivElement[] = []; + private _textLayer: React.RefObject; constructor(props: IPageProps) { super(props); - this.canvas = React.createRef(); - this.textLayer = React.createRef(); + this._canvas = React.createRef(); + this._textLayer = React.createRef(); } componentDidMount() { - console.log(this.props.pdf); if (this.props.pdf) { this.update(this.props.pdf); } @@ -327,8 +315,8 @@ class Page extends React.Component { private renderPage = (page: Pdfjs.PDFPageProxy) => { let scale = 1; let viewport = page.getViewport(scale); - let canvas = this.canvas.current; - let textLayer = this.textLayer.current; + let canvas = this._canvas.current; + let textLayer = this._textLayer.current; if (canvas && textLayer) { let ctx = canvas.getContext("2d"); canvas.width = viewport.width; @@ -337,7 +325,9 @@ class Page extends React.Component { this._height = viewport.height; this.props.pageLoaded(this._currPage, viewport); 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) => { //@ts-ignore Pdfjs.renderTextLayer({ @@ -345,7 +335,6 @@ class Page extends React.Component { container: textLayer, viewport: viewport }); - // textLayer._render(); }); this._page = page; @@ -358,6 +347,11 @@ class Page extends React.Component { e.stopPropagation(); document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); + if (!e.ctrlKey) { + for (let anno of this._currentAnnotations) { + anno.remove(); + } + } } } @@ -367,30 +361,35 @@ class Page extends React.Component { } } + startAnnotation = (e: DragEvent) => { + console.log("drag starting"); + } + + pointerDownCancel = (e: PointerEvent) => { + e.stopPropagation(); + } + onPointerUp = (e: PointerEvent) => { let sel = window.getSelection(); + // if selecting over a range of things if (sel && sel.type === "Range") { - // console.log(sel.getRangeAt(0)); - let commonContainer = sel.getRangeAt(0).commonAncestorContainer; - let startContainer = sel.getRangeAt(0).startContainer; - let endContainer = sel.getRangeAt(0).endContainer; let clientRects = sel.getRangeAt(0).getClientRects(); - console.log(sel.getRangeAt(0)); - let annoBoxes = []; - if (this.textLayer.current) { - // let transform = Utils.GetScreenTransform(this.textLayer.current); - console.log(transform); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); for (let i = 0; i < clientRects.length; i++) { let rect = clientRects.item(i); if (rect) { let annoBox = document.createElement("div"); annoBox.className = "pdfViewer-annotationBox"; - annoBox.style.top = rect.top.toString(); - annoBox.style.left = rect.left.toString(); - annoBox.style.width = rect.width.toString(); - annoBox.style.height = rect.height.toString(); - // annoBox.style.transform = `scale(${1 / transform.scale}) translate(-${transform.translateX * transform.scale}px, -${transform.translateY * transform.scale}px)`; - this.textLayer.current.appendChild(annoBox); + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + annoBox.ondragstart = this.startAnnotation; + annoBox.onpointerdown = this.pointerDownCancel; + this._textLayer.current.appendChild(annoBox); + this._currentAnnotations.push(annoBox); } } } @@ -403,9 +402,9 @@ class Page extends React.Component { return (
- +
-
+
); } -- cgit v1.2.3-70-g09d2 From 0a14ecf9b9fa5e15bc3e5373c8f042f9cd876c8a Mon Sep 17 00:00:00 2001 From: yipstanley Date: Thu, 6 Jun 2019 18:26:57 -0400 Subject: region annotation basics --- src/client/views/collections/CollectionPDFView.tsx | 3 + src/client/views/pdf/PDFAnnotationLayer.tsx | 24 ++++ src/client/views/pdf/PDFViewer.scss | 2 +- src/client/views/pdf/PDFViewer.tsx | 125 +++++++++++++++++---- 4 files changed, 129 insertions(+), 25 deletions(-) create mode 100644 src/client/views/pdf/PDFAnnotationLayer.tsx (limited to 'src') diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 5e51437a4..773e4ae60 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -13,6 +13,9 @@ import { Id } from "../../../new_fields/FieldSymbols"; @observer export class CollectionPDFView extends React.Component { + constructor(props: FieldViewProps) { + super(props); + } public static LayoutString(fieldKey: string = "data") { return FieldView.LayoutString(CollectionPDFView, fieldKey); diff --git a/src/client/views/pdf/PDFAnnotationLayer.tsx b/src/client/views/pdf/PDFAnnotationLayer.tsx new file mode 100644 index 000000000..e92dcacbf --- /dev/null +++ b/src/client/views/pdf/PDFAnnotationLayer.tsx @@ -0,0 +1,24 @@ +import React = require("react"); +import { observer } from "mobx-react"; + +interface IAnnotationProps { + +} + +@observer +export class PDFAnnotationLayer extends React.Component { + onPointerDown = (e: React.PointerEvent) => { + if (e.ctrlKey) { + console.log("annotating"); + e.stopPropagation(); + } + } + + render() { + return ( +
+ +
+ ) + } +} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 484d9dc04..cb1aef410 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -27,5 +27,5 @@ .pdfViewer-annotationBox { position: absolute; background-color: red; - opacity: 0.5; + opacity: 0.1; } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d5a0a7aa1..5c480090c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -6,6 +6,9 @@ import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; +import { PDFAnnotationLayer } from "./PDFAnnotationLayer"; +import { TSMethodSignature } from "babel-types"; +import { checkPropTypes } from "prop-types"; interface IPDFViewerProps { url: string; @@ -268,15 +271,24 @@ class Page extends React.Component { @observable private _height: number = 0; @observable private _page: Opt; @observable private _currPage: number = this.props.page + 1; + @observable private _marqueeX: number = 0; + @observable private _marqueeY: number = 0; + @observable private _marqueeWidth: number = 0; + @observable private _marqueeHeight: number = 0; private _canvas: React.RefObject; - private _currentAnnotations: HTMLDivElement[] = []; private _textLayer: React.RefObject; + private _annotationLayer: React.RefObject; + private _marquee: React.RefObject; + private _currentAnnotations: HTMLDivElement[] = []; + private _marqueeing: boolean = false; constructor(props: IPageProps) { super(props); this._canvas = React.createRef(); this._textLayer = React.createRef(); + this._annotationLayer = React.createRef(); + this._marquee = React.createRef(); } componentDidMount() { @@ -342,9 +354,26 @@ class Page extends React.Component { } } + @action onPointerDown = (e: React.PointerEvent) => { if (e.button === 0) { - e.stopPropagation(); + let target: any = e.target; + if (target && target.parentElement === this._textLayer.current) { + e.stopPropagation(); + } + else { + e.stopPropagation(); + runInAction(() => { + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + } + }); + this._marqueeing = true; + if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; + } document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); if (!e.ctrlKey) { @@ -355,8 +384,22 @@ class Page extends React.Component { } } + @action onPointerMove = (e: PointerEvent) => { - if (e.button === 0) { + let target: any = e.target; + if (this._marqueeing) { + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; + this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; + console.log(this._marqueeWidth); + console.log(this._marqueeHeight); + } + e.stopPropagation(); + e.preventDefault(); + } + else if (target && target.parentElement === this._textLayer.current) { e.stopPropagation(); } } @@ -369,42 +412,76 @@ class Page extends React.Component { e.stopPropagation(); } + @action onPointerUp = (e: PointerEvent) => { - let sel = window.getSelection(); - // if selecting over a range of things - if (sel && sel.type === "Range") { - let clientRects = sel.getRangeAt(0).getClientRects(); - if (this._textLayer.current) { - let boundingRect = this._textLayer.current.getBoundingClientRect(); - for (let i = 0; i < clientRects.length; i++) { - let rect = clientRects.item(i); - if (rect) { - let annoBox = document.createElement("div"); - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); - annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); - annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); - annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - annoBox.ondragstart = this.startAnnotation; - annoBox.onpointerdown = this.pointerDownCancel; - this._textLayer.current.appendChild(annoBox); - this._currentAnnotations.push(annoBox); + if (this._marqueeing) { + this._marqueeing = false; + if (this._marquee.current) { + let copy = document.createElement("div"); + copy.style.left = this._marquee.current.style.left; + copy.style.top = this._marquee.current.style.top; + copy.style.width = this._marquee.current.style.width; + copy.style.height = this._marquee.current.style.height; + copy.style.opacity = this._marquee.current.style.opacity; + copy.className = this._marquee.current.className; + if (this._annotationLayer.current) { + this._annotationLayer.current.append(copy); + this._currentAnnotations.push(copy); + } + this._marquee.current.style.opacity = "0"; + } + + this._marqueeHeight = this._marqueeWidth = 0; + } + else { + let sel = window.getSelection(); + // if selecting over a range of things + if (sel && sel.type === "Range") { + let clientRects = sel.getRangeAt(0).getClientRects(); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + annoBox.ondragstart = this.startAnnotation; + annoBox.onpointerdown = this.pointerDownCancel; + if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); + this._currentAnnotations.push(annoBox); + } } } + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } } } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); } + annotationPointerDown = (e: React.PointerEvent) => { + console.log("annotation"); + } + render() { return (
-
+
+
+
+
); } -- cgit v1.2.3-70-g09d2 From e96d0be82b19a4e108447ba5afc6cdc5deb07110 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 7 Jun 2019 15:28:25 -0400 Subject: starting annotation dragss --- src/client/util/DragManager.ts | 20 ++++++++ src/client/views/pdf/PDFViewer.tsx | 102 ++++++++++++++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 1e84a0db0..a6c3dfa7a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -153,6 +153,22 @@ export namespace DragManager { [id: string]: any; } + export class AnnotationDragData { + constructor(dragDoc: Doc, annotationDocs: Doc[], dropDoc: Doc) { + this.dragDocument = dragDoc; + this.dropDocument = dropDoc; + this.annotationDocuments = annotationDocs; + this.xOffset = this.yOffset = 0; + } + dragDocument: Doc; + annotationDocuments: Doc[]; + dropDocument: Doc; + xOffset: number; + yOffset: number; + dropAction: dropActionType; + userDropAction: dropActionType; + } + export let StartDragFunctions: (() => void)[] = []; export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { @@ -166,6 +182,10 @@ export namespace DragManager { dragData.draggedDocuments)); } + export function StartAnnotationDrag(eles: HTMLElement[], dragData: AnnotationDragData, downX: number, downY: number, options?: DragOptions) { + StartDrag(eles, dragData, downX, downY, options); + } + export class LinkDragData { constructor(linkSourceDoc: Doc, blacklist: Doc[] = []) { this.linkSourceDocument = linkSourceDoc; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 5c480090c..b0a48ac84 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,13 +2,19 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, Doc, HeightSym, WidthSym, Field, DocListCast } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import { PDFAnnotationLayer } from "./PDFAnnotationLayer"; import { TSMethodSignature } from "babel-types"; import { checkPropTypes } from "prop-types"; +import { DragManager } from "../../util/DragManager"; +import { Docs } from "../../documents/Documents"; +import { List } from "../../../new_fields/List"; +import { Cast } from "../../../new_fields/Types"; +import { emptyFunction } from "../../../Utils"; +const Curly = require("./curly.png"); interface IPDFViewerProps { url: string; @@ -159,6 +165,7 @@ class Viewer extends React.Component { key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} + parent={this.props.parent} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(i => false); @@ -194,6 +201,7 @@ class Viewer extends React.Component { key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} + parent={this.props.parent} {...this.props} /> ); this._isPage[i] = true; @@ -262,6 +270,7 @@ interface IPageProps { numPages: number; page: number; pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; + parent: PDFBox; } @observer @@ -275,11 +284,13 @@ class Page extends React.Component { @observable private _marqueeY: number = 0; @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; + @observable private _rotate: string = ""; private _canvas: React.RefObject; private _textLayer: React.RefObject; private _annotationLayer: React.RefObject; private _marquee: React.RefObject; + private _curly: React.RefObject; private _currentAnnotations: HTMLDivElement[] = []; private _marqueeing: boolean = false; @@ -289,6 +300,7 @@ class Page extends React.Component { this._textLayer = React.createRef(); this._annotationLayer = React.createRef(); this._marquee = React.createRef(); + this._curly = React.createRef(); } componentDidMount() { @@ -338,7 +350,7 @@ class Page extends React.Component { this.props.pageLoaded(this._currPage, viewport); if (ctx) { // renders the page onto the canvas context - page.render({ canvasContext: ctx, viewport: viewport }) + page.render({ canvasContext: ctx, viewport: viewport }); // renders text onto the text container page.getTextContent().then((res: Pdfjs.TextContent) => { //@ts-ignore @@ -354,9 +366,56 @@ class Page extends React.Component { } } + makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { + let annoDocs: Doc[] = []; + for (let anno of this._currentAnnotations) { + let annoDoc = new Doc(); + annoDoc.x = anno.offsetLeft; + annoDoc.y = anno.offsetTop; + annoDoc.height = anno.offsetHeight; + annoDoc.width = anno.offsetWidth; + annoDoc.target = targetDoc; + annoDocs.push(annoDoc); + } + return annoDocs; + } + + startDrag = (e: PointerEvent) => { + console.log("start drag"); + e.preventDefault(); + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + let thisDoc = this.props.parent.Document; + let targetDoc = Docs.TextDocument(); + let annotationDocs = this.makeAnnotationDocuments(targetDoc); + targetDoc.annotations = new List(annotationDocs); + let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); + DragManager.StartAnnotationDrag(this._currentAnnotations, dragData, e.pageX, e.pageY, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + e.stopPropagation(); + } + + endDrag = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + e.stopPropagation(); + } + @action onPointerDown = (e: React.PointerEvent) => { - if (e.button === 0) { + if (e.shiftKey && 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) { let target: any = e.target; if (target && target.parentElement === this._textLayer.current) { e.stopPropagation(); @@ -374,7 +433,9 @@ class Page extends React.Component { this._marqueeing = true; if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; } + document.removeEventListener("pointermove", this.onPointerMove); document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointerup", this.onPointerUp); if (!e.ctrlKey) { for (let anno of this._currentAnnotations) { @@ -393,8 +454,30 @@ class Page extends React.Component { let boundingRect = current.getBoundingClientRect(); this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; - console.log(this._marqueeWidth); - console.log(this._marqueeHeight); + if (this._marquee.current && this._curly.current) { + if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { + this._marquee.current.style.background = "red"; + this._curly.current.style.opacity = "0"; + } + else { + this._marquee.current.style.background = "transparent"; + this._curly.current.style.opacity = "1"; + } + + let ratio = this._marqueeWidth / this._marqueeHeight; + if (ratio > 1.5) { + // vertical + this._rotate = "rotate(90deg) scale(1, 2)"; + } + else if (ratio < 0.5) { + // horizontal + this._rotate = "scale(2, 1)"; + } + else { + // diagonal + this._rotate = "rotate(45deg) scale(1.5, 1.5)"; + } + } } e.stopPropagation(); e.preventDefault(); @@ -472,6 +555,10 @@ class Page extends React.Component { console.log("annotation"); } + // imgVisible = () => { + // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } + // } + render() { return (
@@ -479,7 +566,10 @@ class Page extends React.Component {
-
+
+ +
-- cgit v1.2.3-70-g09d2 From 5d799d1a26fa12d666b11b80776151edcbbd67f8 Mon Sep 17 00:00:00 2001 From: loudonclear Date: Fri, 7 Jun 2019 18:14:39 -0400 Subject: some comments, switching computers --- src/client/util/DragManager.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 5 + src/client/views/pdf/PDFBox2.tsx | 28 -- src/client/views/pdf/PDFViewer.tsx | 327 +----------------- src/client/views/pdf/Page.tsx | 382 +++++++++++++++++++++ 5 files changed, 391 insertions(+), 353 deletions(-) delete mode 100644 src/client/views/pdf/PDFBox2.tsx create mode 100644 src/client/views/pdf/Page.tsx (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index a6c3dfa7a..e92ed9b4a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -218,7 +218,7 @@ export namespace DragManager { let ys: number[] = []; const docs: Doc[] = - dragData instanceof DocumentDragData ? dragData.draggedDocuments : []; + dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : []; let dragElements = eles.map(ele => { const w = ele.offsetWidth, h = ele.offsetHeight; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index be37efd3d..1ced6a426 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -107,6 +107,11 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { e.stopPropagation(); return added; } + else if (de.data instanceof DragManager.AnnotationDragData) { + console.log("dropped!"); + console.log(de.data); + return this.props.addDocument(de.data.dropDocument); + } return false; } diff --git a/src/client/views/pdf/PDFBox2.tsx b/src/client/views/pdf/PDFBox2.tsx deleted file mode 100644 index 71825c260..000000000 --- a/src/client/views/pdf/PDFBox2.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React = require("react"); -import { FieldViewProps, FieldView } from "../nodes/FieldView"; -import { DocComponent } from "../DocComponent"; -import { makeInterface } from "../../../new_fields/Schema"; -import { positionSchema } from "../nodes/DocumentView"; -import { pageSchema } from "../nodes/ImageBox"; -import { PDFViewer } from "./PDFViewer"; -import { RouteStore } from "../../../server/RouteStore"; -import { InkingControl } from "../InkingControl"; -import { observer } from "mobx-react"; -import { trace } from "mobx"; - -type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; -const PdfDocument = makeInterface(positionSchema, pageSchema); - -@observer -export class PDFBox2 extends DocComponent(PdfDocument) { - public static LayoutString() { return FieldView.LayoutString(PDFBox2); } - - render() { - trace(); - const pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; - let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool); - return ( - - ) - } -} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index b0a48ac84..0711ead23 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,19 +2,11 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt, Doc, HeightSym, WidthSym, Field, DocListCast } from "../../../new_fields/Doc"; +import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; -import { PDFAnnotationLayer } from "./PDFAnnotationLayer"; -import { TSMethodSignature } from "babel-types"; -import { checkPropTypes } from "prop-types"; -import { DragManager } from "../../util/DragManager"; -import { Docs } from "../../documents/Documents"; -import { List } from "../../../new_fields/List"; -import { Cast } from "../../../new_fields/Types"; -import { emptyFunction } from "../../../Utils"; -const Curly = require("./curly.png"); +import Page from "./Page"; interface IPDFViewerProps { url: string; @@ -168,7 +160,7 @@ class Viewer extends React.Component { parent={this.props.parent} {...this.props} /> )); - let arr = Array.from(Array(numPages).keys()).map(i => false); + let arr = Array.from(Array(numPages).keys()).map(() => false); this._visibleElements.push(...divs); this._isPage.push(...arr); } @@ -263,316 +255,3 @@ class Viewer extends React.Component { ); } } - -interface IPageProps { - pdf: Opt; - name: string; - numPages: number; - page: number; - pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; - parent: PDFBox; -} - -@observer -class Page extends React.Component { - @observable private _state: string = "N/A"; - @observable private _width: number = 0; - @observable private _height: number = 0; - @observable private _page: Opt; - @observable private _currPage: number = this.props.page + 1; - @observable private _marqueeX: number = 0; - @observable private _marqueeY: number = 0; - @observable private _marqueeWidth: number = 0; - @observable private _marqueeHeight: number = 0; - @observable private _rotate: string = ""; - - private _canvas: React.RefObject; - private _textLayer: React.RefObject; - private _annotationLayer: React.RefObject; - private _marquee: React.RefObject; - private _curly: React.RefObject; - private _currentAnnotations: HTMLDivElement[] = []; - private _marqueeing: boolean = false; - - 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() { - if (this.props.pdf) { - this.update(this.props.pdf); - } - } - - componentDidUpdate() { - if (this.props.pdf) { - this.update(this.props.pdf); - } - } - - private update = (pdf: Pdfjs.PDFDocumentProxy) => { - if (pdf) { - this.loadPage(pdf); - } - else { - this._state = "loading"; - } - } - - private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { - if (this._state === "rendering" || this._page) return; - - pdf.getPage(this._currPage).then( - (page: Pdfjs.PDFPageProxy) => { - this._state = "rendering"; - this.renderPage(page); - } - ); - } - - @action - private renderPage = (page: Pdfjs.PDFPageProxy) => { - let scale = 1; - 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 (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) => { - //@ts-ignore - Pdfjs.renderTextLayer({ - textContent: res, - container: textLayer, - viewport: viewport - }); - }); - - this._page = page; - } - } - } - - makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { - let annoDocs: Doc[] = []; - for (let anno of this._currentAnnotations) { - let annoDoc = new Doc(); - annoDoc.x = anno.offsetLeft; - annoDoc.y = anno.offsetTop; - annoDoc.height = anno.offsetHeight; - annoDoc.width = anno.offsetWidth; - annoDoc.target = targetDoc; - annoDocs.push(annoDoc); - } - return annoDocs; - } - - startDrag = (e: PointerEvent) => { - console.log("start drag"); - e.preventDefault(); - document.removeEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); - let thisDoc = this.props.parent.Document; - let targetDoc = Docs.TextDocument(); - let annotationDocs = this.makeAnnotationDocuments(targetDoc); - targetDoc.annotations = new List(annotationDocs); - let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); - DragManager.StartAnnotationDrag(this._currentAnnotations, dragData, e.pageX, e.pageY, { - handlers: { - dragComplete: action(emptyFunction), - }, - hideSource: false - }); - e.stopPropagation(); - } - - endDrag = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); - e.stopPropagation(); - } - - @action - onPointerDown = (e: React.PointerEvent) => { - if (e.shiftKey && 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) { - let target: any = e.target; - if (target && target.parentElement === this._textLayer.current) { - e.stopPropagation(); - } - else { - e.stopPropagation(); - runInAction(() => { - let current = this._textLayer.current; - if (current) { - let boundingRect = current.getBoundingClientRect(); - this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); - this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); - } - }); - this._marqueeing = true; - if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; - } - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - if (!e.ctrlKey) { - for (let anno of this._currentAnnotations) { - anno.remove(); - } - } - } - } - - @action - onPointerMove = (e: PointerEvent) => { - let target: any = e.target; - if (this._marqueeing) { - let current = this._textLayer.current; - if (current) { - let boundingRect = current.getBoundingClientRect(); - this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; - this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; - if (this._marquee.current && this._curly.current) { - if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { - this._marquee.current.style.background = "red"; - this._curly.current.style.opacity = "0"; - } - else { - this._marquee.current.style.background = "transparent"; - this._curly.current.style.opacity = "1"; - } - - let ratio = this._marqueeWidth / this._marqueeHeight; - if (ratio > 1.5) { - // vertical - this._rotate = "rotate(90deg) scale(1, 2)"; - } - else if (ratio < 0.5) { - // horizontal - this._rotate = "scale(2, 1)"; - } - else { - // diagonal - this._rotate = "rotate(45deg) scale(1.5, 1.5)"; - } - } - } - e.stopPropagation(); - e.preventDefault(); - } - else if (target && target.parentElement === this._textLayer.current) { - e.stopPropagation(); - } - } - - startAnnotation = (e: DragEvent) => { - console.log("drag starting"); - } - - pointerDownCancel = (e: PointerEvent) => { - e.stopPropagation(); - } - - @action - onPointerUp = (e: PointerEvent) => { - if (this._marqueeing) { - this._marqueeing = false; - if (this._marquee.current) { - let copy = document.createElement("div"); - copy.style.left = this._marquee.current.style.left; - copy.style.top = this._marquee.current.style.top; - copy.style.width = this._marquee.current.style.width; - copy.style.height = this._marquee.current.style.height; - copy.style.opacity = this._marquee.current.style.opacity; - copy.className = this._marquee.current.className; - if (this._annotationLayer.current) { - this._annotationLayer.current.append(copy); - this._currentAnnotations.push(copy); - } - this._marquee.current.style.opacity = "0"; - } - - this._marqueeHeight = this._marqueeWidth = 0; - } - else { - let sel = window.getSelection(); - // if selecting over a range of things - if (sel && sel.type === "Range") { - let clientRects = sel.getRangeAt(0).getClientRects(); - if (this._textLayer.current) { - let boundingRect = this._textLayer.current.getBoundingClientRect(); - for (let i = 0; i < clientRects.length; i++) { - let rect = clientRects.item(i); - if (rect) { - let annoBox = document.createElement("div"); - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); - annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); - annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); - annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - annoBox.ondragstart = this.startAnnotation; - annoBox.onpointerdown = this.pointerDownCancel; - if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); - this._currentAnnotations.push(annoBox); - } - } - } - if (sel.empty) { // Chrome - sel.empty(); - } else if (sel.removeAllRanges) { // Firefox - sel.removeAllRanges(); - } - } - } - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - } - - annotationPointerDown = (e: React.PointerEvent) => { - console.log("annotation"); - } - - // imgVisible = () => { - // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } - // } - - render() { - return ( -
-
- -
-
-
- -
-
-
-
- ); - } -} \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx new file mode 100644 index 000000000..7cd72b27f --- /dev/null +++ b/src/client/views/pdf/Page.tsx @@ -0,0 +1,382 @@ +import { observer } from "mobx-react"; +import React = require("react"); +import { observable, action, runInAction } from "mobx"; +import * as Pdfjs from "pdfjs-dist"; +import { Opt, Doc, FieldResult, Field } 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 } from "../../documents/Documents"; +import { List } from "../../../new_fields/List"; +import { emptyFunction } from "../../../Utils"; +import { Cast, NumCast } from "../../../new_fields/Types"; +import { listSpec } from "../../../new_fields/Schema"; + +interface IPageProps { + pdf: Opt; + name: string; + numPages: number; + page: number; + pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; + parent: PDFBox; +} + +@observer +export default class Page extends React.Component { + @observable private _state: string = "N/A"; + @observable private _width: number = 0; + @observable private _height: number = 0; + @observable private _page: Opt; + @observable private _currPage: number = this.props.page + 1; + @observable private _marqueeX: number = 0; + @observable private _marqueeY: number = 0; + @observable private _marqueeWidth: number = 0; + @observable private _marqueeHeight: number = 0; + @observable private _rotate: string = ""; + @observable private _annotations: List = new List(); + + private _canvas: React.RefObject; + private _textLayer: React.RefObject; + private _annotationLayer: React.RefObject; + private _marquee: React.RefObject; + private _curly: React.RefObject; + private _currentAnnotations: HTMLDivElement[] = []; + private _marqueeing: boolean = false; + private _dragging: boolean = false; + + 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() { + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + componentDidUpdate() { + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + private update = (pdf: Pdfjs.PDFDocumentProxy) => { + if (pdf) { + this.loadPage(pdf); + } + else { + this._state = "loading"; + } + } + + private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { + if (this._state === "rendering" || this._page) return; + + pdf.getPage(this._currPage).then( + (page: Pdfjs.PDFPageProxy) => { + this._state = "rendering"; + this.renderPage(page); + } + ); + } + + @action + private renderPage = (page: Pdfjs.PDFPageProxy) => { + // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes + let scale = 2; + 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 (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) => { + //@ts-ignore + Pdfjs.renderTextLayer({ + textContent: res, + container: textLayer, + viewport: viewport + }); + }); + + this._page = page; + } + } + } + + /** + * @param targetDoc: Document that annotations are linked to + * This method makes the list of current annotations into documents linked to + * the parameter passed in. + */ + makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { + let annoDocs: Doc[] = []; + for (let anno of this._currentAnnotations) { + let annoDoc = new Doc(); + annoDoc.x = anno.offsetLeft; + annoDoc.y = anno.offsetTop; + annoDoc.height = anno.offsetHeight; + annoDoc.width = anno.offsetWidth; + annoDoc.target = targetDoc; + annoDocs.push(annoDoc); + anno.remove(); + } + this._currentAnnotations = []; + return annoDocs; + } + + /** + * This is temporary for creating annotations from highlights. It will + * start a drag event and create or put the necessary info into the drag event. + */ + @action + startDrag = (e: PointerEvent) => { + // the first 5 lines is a hack to prevent text selection while dragging + e.preventDefault(); + e.stopPropagation(); + if (this._dragging) { + return; + } + this._dragging = true; + let thisDoc = this.props.parent.Document; + // document that this annotation is linked to + let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); + targetDoc.targetPage = this.props.page; + // creates annotation documents for current highlights + let annotationDocs = this.makeAnnotationDocuments(targetDoc); + let targetAnnotations = Cast(targetDoc.annotations, listSpec(Doc)); + if (targetAnnotations) { + targetAnnotations.push(...annotationDocs); + targetDoc.annotations = targetAnnotations; + } + // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf + let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); + if (thisAnnotations) { + thisAnnotations.push(...annotationDocs); + this.props.parent.Document.annotations = thisAnnotations; + let temp = new List(thisAnnotations); + temp.push(...this._annotations); + this._annotations = temp; + } + // create dragData and star tdrag + let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); + if (this._textLayer.current) { + DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + } + } + + // cleans up events and boolean + endDrag = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + this._dragging = false; + e.stopPropagation(); + } + + @action + onPointerDown = (e: React.PointerEvent) => { + // 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) { + let target: any = e.target; + if (target && target.parentElement === this._textLayer.current) { + e.stopPropagation(); + } + else { + e.stopPropagation(); + // set marquee x and y positions to the spatially transformed position + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + } + this._marqueeing = true; + if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; + } + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + if (!e.ctrlKey) { + for (let anno of this._currentAnnotations) { + anno.remove(); + } + this._currentAnnotations = []; + } + } + } + + @action + onPointerMove = (e: PointerEvent) => { + let target: any = e.target; + if (this._marqueeing) { + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; + this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; + if (this._marquee.current && this._curly.current) { + if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { + this._marquee.current.style.background = "red"; + this._curly.current.style.opacity = "0"; + } + else { + this._marquee.current.style.background = "transparent"; + this._curly.current.style.opacity = "1"; + } + + let ratio = this._marqueeWidth / this._marqueeHeight; + if (ratio > 1.5) { + // vertical + this._rotate = "rotate(90deg) scale(1, 2)"; + } + else if (ratio < 0.5) { + // horizontal + this._rotate = "scale(2, 1)"; + } + else { + // diagonal + this._rotate = "rotate(45deg) scale(1.5, 1.5)"; + } + } + } + e.stopPropagation(); + e.preventDefault(); + } + else if (target && target.parentElement === this._textLayer.current) { + e.stopPropagation(); + } + } + + startAnnotation = () => { + console.log("drag starting"); + } + + pointerDownCancel = (e: PointerEvent) => { + e.stopPropagation(); + } + + @action + onPointerUp = () => { + if (this._marqueeing) { + this._marqueeing = false; + if (this._marquee.current) { + let copy = document.createElement("div"); + copy.style.left = this._marquee.current.style.left; + copy.style.top = this._marquee.current.style.top; + copy.style.width = this._marquee.current.style.width; + copy.style.height = this._marquee.current.style.height; + copy.style.opacity = this._marquee.current.style.opacity; + copy.className = this._marquee.current.className; + if (this._annotationLayer.current) { + this._annotationLayer.current.append(copy); + this._currentAnnotations.push(copy); + } + this._marquee.current.style.opacity = "0"; + } + + this._marqueeHeight = this._marqueeWidth = 0; + } + else { + let sel = window.getSelection(); + // if selecting over a range of things + if (sel && sel.type === "Range") { + let clientRects = sel.getRangeAt(0).getClientRects(); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + annoBox.ondragstart = this.startAnnotation; + annoBox.onpointerdown = this.pointerDownCancel; + if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); + this._currentAnnotations.push(annoBox); + } + } + } + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } + } + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } + + renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { + let annoBox = document.createElement("div"); + if (anno) { + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = NumCast(anno.x).toString(); + annoBox.style.left = NumCast(anno.y).toString(); + annoBox.style.width = NumCast(anno.width).toString(); + annoBox.style.height = NumCast(anno.height).toString() + annoBox.onpointerdown = this.pointerDownCancel; + } + return annoBox; + } + + annotationPointerDown = () => { + console.log("annotation"); + } + + // imgVisible = () => { + // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } + // } + + render() { + let annotations = this._annotations; + return ( +
+
+ +
+
+
+ +
+ {annotations.map(anno => this.renderAnnotation(anno instanceof Doc ? anno : undefined))} +
+
+
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 92cdb8dae6162b1aa7e41f3392c670e7c5d377eb Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 7 Jun 2019 19:02:05 -0400 Subject: cleanup --- src/client/views/pdf/Page.tsx | 116 +++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 7cd72b27f..940f31f41 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -211,19 +211,19 @@ export default class Page extends React.Component { else { e.stopPropagation(); // set marquee x and y positions to the spatially transformed position - let current = this._textLayer.current; - if (current) { - let boundingRect = current.getBoundingClientRect(); - this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); - this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); - } + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + } this._marqueeing = true; if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; } - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); + document.removeEventListener("pointermove", this.onSelectStart); + document.addEventListener("pointermove", this.onSelectStart); + document.removeEventListener("pointerup", this.onSelectEnd); + document.addEventListener("pointerup", this.onSelectEnd); if (!e.ctrlKey) { for (let anno of this._currentAnnotations) { anno.remove(); @@ -234,37 +234,20 @@ export default class Page extends React.Component { } @action - onPointerMove = (e: PointerEvent) => { + onSelectStart = (e: PointerEvent) => { 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._marqueeX; this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; + let { background, opacity, transform: transform } = this.getCurlyTransform(); if (this._marquee.current && this._curly.current) { - if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { - this._marquee.current.style.background = "red"; - this._curly.current.style.opacity = "0"; - } - else { - this._marquee.current.style.background = "transparent"; - this._curly.current.style.opacity = "1"; - } - - let ratio = this._marqueeWidth / this._marqueeHeight; - if (ratio > 1.5) { - // vertical - this._rotate = "rotate(90deg) scale(1, 2)"; - } - else if (ratio < 0.5) { - // horizontal - this._rotate = "scale(2, 1)"; - } - else { - // diagonal - this._rotate = "rotate(45deg) scale(1.5, 1.5)"; - } + this._marquee.current.style.background = background; + this._curly.current.style.opacity = opacity; + this._rotate = transform; } } e.stopPropagation(); @@ -275,25 +258,62 @@ export default class Page extends React.Component { } } - startAnnotation = () => { - console.log("drag starting"); - } + 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"; + } - pointerDownCancel = (e: PointerEvent) => { - e.stopPropagation(); + // 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, 2)"; + } + else if (ratio < 0.5) { + // horizontal + transform = "scale(2, 1)"; + } + else { + // diagonal + transform = "rotate(45deg) scale(1.5, 1.5)"; + } + } + return { background: background, opacity: opacity, transform: transform }; } @action - onPointerUp = () => { + onSelectEnd = () => { if (this._marqueeing) { this._marqueeing = false; if (this._marquee.current) { let copy = document.createElement("div"); + // make a copy of the marquee copy.style.left = this._marquee.current.style.left; copy.style.top = this._marquee.current.style.top; copy.style.width = this._marquee.current.style.width; copy.style.height = this._marquee.current.style.height; - copy.style.opacity = this._marquee.current.style.opacity; + + // 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.opacity = this._marquee.current.style.opacity; + } copy.className = this._marquee.current.className; if (this._annotationLayer.current) { this._annotationLayer.current.append(copy); @@ -321,13 +341,12 @@ export default class Page extends React.Component { annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - annoBox.ondragstart = this.startAnnotation; - annoBox.onpointerdown = this.pointerDownCancel; if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); this._currentAnnotations.push(annoBox); } } } + // clear selection if (sel.empty) { // Chrome sel.empty(); } else if (sel.removeAllRanges) { // Firefox @@ -335,8 +354,8 @@ export default class Page extends React.Component { } } } - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); + document.removeEventListener("pointermove", this.onSelectStart); + document.removeEventListener("pointerup", this.onSelectEnd); } renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { @@ -348,19 +367,10 @@ export default class Page extends React.Component { annoBox.style.left = NumCast(anno.y).toString(); annoBox.style.width = NumCast(anno.width).toString(); annoBox.style.height = NumCast(anno.height).toString() - annoBox.onpointerdown = this.pointerDownCancel; } return annoBox; } - annotationPointerDown = () => { - console.log("annotation"); - } - - // imgVisible = () => { - // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } - // } - render() { let annotations = this._annotations; return ( -- cgit v1.2.3-70-g09d2 From 81a502f81073e7fb15c75563cd557a6a2c5a31cd Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 10:58:00 -0400 Subject: annotation loading maybe idk not workign rn --- src/client/views/pdf/PDFViewer.tsx | 2 +- src/client/views/pdf/Page.tsx | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0711ead23..0a6886878 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -184,7 +184,7 @@ class Viewer extends React.Component { // render pages for any indices that don't already have pages (force rerender will make these render regardless) for (let i = startIndex; i <= endIndex; i++) { - if (!this._isPage[i] || forceRender) { + if (!this._isPage[i] || (this._isPage[i] && forceRender)) { this._visibleElements[i] = ( { targetAnnotations.push(...annotationDocs); targetDoc.annotations = targetAnnotations; } + else { + targetDoc.annotations = new List(annotationDocs); + } // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (thisAnnotations) { -- cgit v1.2.3-70-g09d2 From bcd6960fe91008bf31d364c48b5c8765eec28701 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 11:33:36 -0400 Subject: oops --- src/client/views/pdf/Page.tsx | 86 ++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 0d86f22c1..7125c820c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -1,8 +1,8 @@ import { observer } from "mobx-react"; import React = require("react"); -import { observable, action, runInAction } from "mobx"; +import { observable, action, runInAction, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt, Doc, FieldResult, Field } from "../../../new_fields/Doc"; +import { Opt, Doc, FieldResult, Field, DocListCast, WidthSym, HeightSym } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; @@ -12,6 +12,7 @@ import { List } from "../../../new_fields/List"; import { emptyFunction } from "../../../Utils"; import { Cast, NumCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; +import { menuBar } from "prosemirror-menu"; interface IPageProps { pdf: Opt; @@ -34,7 +35,7 @@ export default class Page extends React.Component { @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; @observable private _rotate: string = ""; - @observable private _annotations: List = new List(); + @observable private _annotations: Doc[] = []; private _canvas: React.RefObject; private _textLayer: React.RefObject; @@ -44,6 +45,7 @@ export default class Page extends React.Component { private _currentAnnotations: HTMLDivElement[] = []; private _marqueeing: boolean = false; private _dragging: boolean = false; + private _reactionDisposer?: IReactionDisposer; constructor(props: IPageProps) { super(props); @@ -54,19 +56,38 @@ export default class Page extends React.Component { this._curly = React.createRef(); } - componentDidMount() { + componentDidMount = (): void => { if (this.props.pdf) { this.update(this.props.pdf); + + if (this.props.parent.Document) { + this._reactionDisposer = reaction( + () => DocListCast(this.props.parent.Document.annotations), + () => { + let annotations = DocListCast(this.props.parent.Document.annotations); + if (annotations && annotations.length) { + this.renderAnnotations(annotations, true); + } + }, + { fireImmediately: true } + ); + } + } + } + + componentWillUnmount = (): void => { + if (this._reactionDisposer) { + this._reactionDisposer(); } } - componentDidUpdate() { + componentDidUpdate = (): void => { if (this.props.pdf) { this.update(this.props.pdf); } } - private update = (pdf: Pdfjs.PDFDocumentProxy) => { + private update = (pdf: Pdfjs.PDFDocumentProxy): void => { if (pdf) { this.loadPage(pdf); } @@ -75,11 +96,11 @@ export default class Page extends React.Component { } } - private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { + private loadPage = (pdf: Pdfjs.PDFDocumentProxy): void => { if (this._state === "rendering" || this._page) return; pdf.getPage(this._currPage).then( - (page: Pdfjs.PDFPageProxy) => { + (page: Pdfjs.PDFPageProxy): void => { this._state = "rendering"; this.renderPage(page); } @@ -87,7 +108,18 @@ export default class Page extends React.Component { } @action - private renderPage = (page: Pdfjs.PDFPageProxy) => { + private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { + if (removeOldAnnotations) { + this._annotations = annotations; + } + else { + this._annotations.push(...annotations); + this._annotations = new Array(...this._annotations); + } + } + + @action + private renderPage = (page: Pdfjs.PDFPageProxy): void => { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes let scale = 2; let viewport = page.getViewport(scale); @@ -104,7 +136,7 @@ export default class Page extends React.Component { // 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) => { + page.getTextContent().then((res: Pdfjs.TextContent): void => { //@ts-ignore Pdfjs.renderTextLayer({ textContent: res, @@ -144,7 +176,7 @@ export default class Page extends React.Component { * start a drag event and create or put the necessary info into the drag event. */ @action - startDrag = (e: PointerEvent) => { + startDrag = (e: PointerEvent): void => { // the first 5 lines is a hack to prevent text selection while dragging e.preventDefault(); e.stopPropagation(); @@ -166,15 +198,6 @@ export default class Page extends React.Component { else { targetDoc.annotations = new List(annotationDocs); } - // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf - let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); - if (thisAnnotations) { - thisAnnotations.push(...annotationDocs); - this.props.parent.Document.annotations = thisAnnotations; - let temp = new List(thisAnnotations); - temp.push(...this._annotations); - this._annotations = temp; - } // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); if (this._textLayer.current) { @@ -196,7 +219,7 @@ export default class Page extends React.Component { } @action - onPointerDown = (e: React.PointerEvent) => { + onPointerDown = (e: React.PointerEvent): void => { // if alt+left click, drag and annotate if (e.altKey && e.button === 0) { e.stopPropagation(); @@ -237,7 +260,7 @@ export default class Page extends React.Component { } @action - onSelectStart = (e: PointerEvent) => { + onSelectStart = (e: PointerEvent): void => { let target: any = e.target; if (this._marqueeing) { let current = this._textLayer.current; @@ -292,7 +315,7 @@ export default class Page extends React.Component { } @action - onSelectEnd = () => { + onSelectEnd = (): void => { if (this._marqueeing) { this._marqueeing = false; if (this._marquee.current) { @@ -375,7 +398,6 @@ export default class Page extends React.Component { } render() { - let annotations = this._annotations; return (
@@ -386,10 +408,24 @@ export default class Page extends React.Component { style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "transparent" }}>
- {annotations.map(anno => this.renderAnnotation(anno instanceof Doc ? anno : undefined))} + {this._annotations.map(anno => )}
); } +} + +interface IAnnotation { + x: number; + y: number; + width: number; + height: number; +} +class RegionAnnotation extends React.Component { + render() { + return ( +
+ ); + } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 2ecd0c3486ec37d1061468cda12aa64e38d0d3d5 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 14:07:36 -0400 Subject: annotation improvements --- src/client/documents/Documents.ts | 8 +-- src/client/views/collections/CollectionSubView.tsx | 2 + .../collectionFreeForm/CollectionFreeFormView.tsx | 64 ++++++++++++++-------- src/client/views/pdf/Page.tsx | 17 ++++-- 4 files changed, 57 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 973c90ff4..fa6d19a5e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -69,15 +69,15 @@ export interface DocumentOptions { const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; export namespace DocUtils { - export function MakeLink(source: Doc, target: Doc) { + export function MakeLink(source: Doc, target: Doc, title: string = "-link name-", description: string = "", tags: string = "Default") { let protoSrc = source.proto ? source.proto : source; let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; - linkDoc.proto!.title = "-link name-"; - linkDoc.proto!.linkDescription = ""; - linkDoc.proto!.linkTags = "Default"; + linkDoc.proto!.title = title; + linkDoc.proto!.linkDescription = description; + linkDoc.proto!.linkTags = tags; linkDoc.proto!.linkedTo = target; linkDoc.proto!.linkedToPage = target.curPage; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 1ced6a426..d25d07c3d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -110,6 +110,8 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { else if (de.data instanceof DragManager.AnnotationDragData) { console.log("dropped!"); console.log(de.data); + // de.data.dropDocument.x = de.x; + // de.data.dropDocument.y = de.y; return this.props.addDocument(de.data.dropDocument); } return false; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9d19df540..d8e197cbe 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -81,31 +81,47 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) { - if (de.data.droppedDocuments.length) { - let dragDoc = de.data.droppedDocuments[0]; - let zoom = NumCast(dragDoc.zoomBasis, 1); - let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); - let x = xp - de.data.xOffset / zoom; - let y = yp - de.data.yOffset / zoom; - let dropX = NumCast(de.data.droppedDocuments[0].x); - let dropY = NumCast(de.data.droppedDocuments[0].y); - de.data.droppedDocuments.forEach(d => { - d.x = x + NumCast(d.x) - dropX; - d.y = y + NumCast(d.y) - dropY; - if (!NumCast(d.width)) { - d.width = 300; - } - if (!NumCast(d.height)) { - let nw = NumCast(d.nativeWidth); - let nh = NumCast(d.nativeHeight); - d.height = nw && nh ? nh / nw * NumCast(d.width) : 300; - } - this.bringToFront(d); - }); - SelectionManager.ReselectAll(); + if (super.drop(e, de)) { + if (de.data instanceof DragManager.DocumentDragData) { + if (de.data.droppedDocuments.length) { + let dragDoc = de.data.droppedDocuments[0]; + let zoom = NumCast(dragDoc.zoomBasis, 1); + let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); + let x = xp - de.data.xOffset / zoom; + let y = yp - de.data.yOffset / zoom; + let dropX = NumCast(de.data.droppedDocuments[0].x); + let dropY = NumCast(de.data.droppedDocuments[0].y); + de.data.droppedDocuments.forEach(d => { + d.x = x + NumCast(d.x) - dropX; + d.y = y + NumCast(d.y) - dropY; + if (!NumCast(d.width)) { + d.width = 300; + } + if (!NumCast(d.height)) { + let nw = NumCast(d.nativeWidth); + let nh = NumCast(d.nativeHeight); + d.height = nw && nh ? nh / nw * NumCast(d.width) : 300; + } + this.bringToFront(d); + }); + SelectionManager.ReselectAll(); + } + return true; + } + else if (de.data instanceof DragManager.AnnotationDragData) { + if (de.data.dropDocument) { + let dragDoc = de.data.dropDocument; + let zoom = NumCast(dragDoc.zoomBasis, 1); + let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); + let x = xp - de.data.xOffset / zoom; + let y = yp - de.data.yOffset / zoom; + let dropX = NumCast(de.data.dropDocument.x); + let dropY = NumCast(de.data.dropDocument.y); + dragDoc.x = x + NumCast(dragDoc.x) - dropX; + dragDoc.y = y + NumCast(dragDoc.y) - dropY; + this.bringToFront(dragDoc); + } } - return true; } return false; } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 7125c820c..5269f448b 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -7,10 +7,10 @@ import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import { DragManager } from "../../util/DragManager"; -import { Docs } from "../../documents/Documents"; +import { Docs, DocUtils } from "../../documents/Documents"; import { List } from "../../../new_fields/List"; import { emptyFunction } from "../../../Utils"; -import { Cast, NumCast } from "../../../new_fields/Types"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; @@ -66,6 +66,7 @@ export default class Page extends React.Component { () => { let annotations = DocListCast(this.props.parent.Document.annotations); if (annotations && annotations.length) { + annotations = annotations.filter(anno => NumCast(anno.page) === this.props.page); this.renderAnnotations(annotations, true); } }, @@ -163,8 +164,10 @@ export default class Page extends React.Component { annoDoc.y = anno.offsetTop; annoDoc.height = anno.offsetHeight; annoDoc.width = anno.offsetWidth; + annoDoc.page = this.props.page; annoDoc.target = targetDoc; annoDocs.push(annoDoc); + DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); anno.remove(); } this._currentAnnotations = []; @@ -190,13 +193,14 @@ export default class Page extends React.Component { targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights let annotationDocs = this.makeAnnotationDocuments(targetDoc); - let targetAnnotations = Cast(targetDoc.annotations, listSpec(Doc)); + console.log(annotationDocs); + let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(...annotationDocs); - targetDoc.annotations = targetAnnotations; + this.props.parent.Document.annotations = new List(targetAnnotations); } else { - targetDoc.annotations = new List(annotationDocs); + this.props.parent.Document.annotations = new List(annotationDocs); } // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); @@ -422,10 +426,11 @@ interface IAnnotation { width: number; height: number; } + class RegionAnnotation extends React.Component { render() { return ( -
+
); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 82a6a258422666fbe3db2a2cf1934f8673912b67 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 15:49:34 -0400 Subject: annotation updates blehh --- src/client/views/pdf/PDFViewer.scss | 6 +++ src/client/views/pdf/PDFViewer.tsx | 76 +++++++++++++++++++++++++++++++++++-- src/client/views/pdf/Page.tsx | 44 +-------------------- 3 files changed, 81 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index cb1aef410..831e6add1 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -28,4 +28,10 @@ position: absolute; background-color: red; opacity: 0.1; +} + +.pdfViewer-annotationLayer { + position: absolute; + top: 0; + overflow: visible hidden; } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0a6886878..1152587a4 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,11 +2,12 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, HeightSym, WidthSym, Doc, DocListCast } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; +import { NumCast } from "../../../new_fields/Types"; interface IPDFViewerProps { url: string; @@ -65,9 +66,11 @@ class Viewer extends React.Component { @observable private _endIndex: number = 1; @observable private _loaded: boolean = false; @observable private _pdf: Opt; + @observable private _annotations: Doc[] = []; private _pageBuffer: number = 1; private _reactionDisposer?: IReactionDisposer; + private _annotationReactionDisposer?: IReactionDisposer; componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); @@ -88,6 +91,19 @@ class Viewer extends React.Component { { fireImmediately: true } ); + if (this.props.parent.Document) { + this._annotationReactionDisposer = reaction( + () => DocListCast(this.props.parent.Document.annotations), + () => { + let annotations = DocListCast(this.props.parent.Document.annotations); + if (annotations && annotations.length) { + this.renderAnnotations(annotations, true); + } + }, + { fireImmediately: true } + ); + } + // On load, render pdf setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); } @@ -96,6 +112,9 @@ class Viewer extends React.Component { if (this._reactionDisposer) { this._reactionDisposer(); } + if (this._annotationReactionDisposer) { + this._annotationReactionDisposer(); + } } @action @@ -135,6 +154,17 @@ class Viewer extends React.Component { } } + @action + private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { + if (removeOldAnnotations) { + this._annotations = annotations; + } + else { + this._annotations.push(...annotations); + this._annotations = new Array(...this._annotations); + } + } + /** * @param startIndex: where to start rendering pages * @param endIndex: where to end rendering pages @@ -158,6 +188,7 @@ class Viewer extends React.Component { name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} parent={this.props.parent} + renderAnnotations={this.renderAnnotations} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(() => false); @@ -194,6 +225,7 @@ class Viewer extends React.Component { name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} parent={this.props.parent} + renderAnnotations={this.renderAnnotations} {...this.props} /> ); this._isPage[i] = true; @@ -247,11 +279,49 @@ class Viewer extends React.Component { } } + getPageHeight = (index: number): number => { + let counter = 0; + if (this.props.pdf && index < this.props.pdf.numPages) { + for (let i = 0; i < index; i++) { + if (this._pageSizes[i]) { + counter += this._pageSizes[i].height; + } + } + } + return counter; + } + render() { return ( -
- {this._visibleElements} +
+
+ {this._visibleElements} +
+
+
+ {this._annotations.map(anno => )} +
+
); } } + +interface IAnnotation { + x: number; + y: number; + width: number; + height: number; +} + +class RegionAnnotation extends React.Component { + onPointerDown = (e: React.PointerEvent) => { + console.log("clicked!"); + } + + render() { + return ( +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 5269f448b..5738889c4 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -21,6 +21,7 @@ interface IPageProps { page: number; pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; parent: PDFBox; + renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; } @observer @@ -35,7 +36,6 @@ export default class Page extends React.Component { @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; @observable private _rotate: string = ""; - @observable private _annotations: Doc[] = []; private _canvas: React.RefObject; private _textLayer: React.RefObject; @@ -60,19 +60,6 @@ export default class Page extends React.Component { if (this.props.pdf) { this.update(this.props.pdf); - if (this.props.parent.Document) { - this._reactionDisposer = reaction( - () => DocListCast(this.props.parent.Document.annotations), - () => { - let annotations = DocListCast(this.props.parent.Document.annotations); - if (annotations && annotations.length) { - annotations = annotations.filter(anno => NumCast(anno.page) === this.props.page); - this.renderAnnotations(annotations, true); - } - }, - { fireImmediately: true } - ); - } } } @@ -108,17 +95,6 @@ export default class Page extends React.Component { ); } - @action - private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { - if (removeOldAnnotations) { - this._annotations = annotations; - } - else { - this._annotations.push(...annotations); - this._annotations = new Array(...this._annotations); - } - } - @action private renderPage = (page: Pdfjs.PDFPageProxy): void => { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes @@ -407,30 +383,14 @@ export default class Page extends React.Component {
-
+
- {this._annotations.map(anno => )}
); } } - -interface IAnnotation { - x: number; - y: number; - width: number; - height: number; -} - -class RegionAnnotation extends React.Component { - render() { - return ( -
- ); - } -} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 46b9a18bf278ab17845d1082a1152d16c07b1240 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 11 Jun 2019 11:30:53 -0400 Subject: annotation navigationnn --- src/client/views/pdf/PDFViewer.tsx | 41 +++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1152587a4..092765324 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -7,7 +7,11 @@ import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; -import { NumCast } from "../../../new_fields/Types"; +import { NumCast, Cast, BoolCast } from "../../../new_fields/Types"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { DocUtils } from "../../documents/Documents"; +import { DocumentManager } from "../../util/DocumentManager"; +import { SelectionManager } from "../../util/SelectionManager"; interface IPDFViewerProps { url: string; @@ -297,9 +301,9 @@ class Viewer extends React.Component {
{this._visibleElements}
-
+
- {this._annotations.map(anno => )} + {this._annotations.map(anno => )}
@@ -307,21 +311,44 @@ class Viewer extends React.Component { } } -interface IAnnotation { +interface IAnnotationProps { x: number; y: number; width: number; height: number; + document: Doc; } -class RegionAnnotation extends React.Component { +class RegionAnnotation extends React.Component { + @observable private _backgroundColor: string = "red"; + + // private _reactionDisposer?: IReactionDisposer; + + constructor(props: IAnnotationProps) { + super(props); + + // this._reactionDisposer = reaction( + // () => { BoolCast(this.props.document.selected); }, + // () => { this._backgroundColor = BoolCast(this.props.document.selected) ? "yellow" : "red"; }, + // { fireImmediately: true } + // ) + } + onPointerDown = (e: React.PointerEvent) => { - console.log("clicked!"); + let targetDoc = Cast(this.props.document.target, Doc, null); + if (targetDoc) { + DocumentManager.Instance.jumpToDocument(targetDoc); + // let annotations = DocListCast(targetDoc.proto!.linkedFromDocs); + // if (annotations && annotations.length) { + // annotations.forEach(anno => anno.selected = true); + // } + } } render() { return ( -
+
); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 64f6a0d54656ded133af113746003d61eaa1d651 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 11 Jun 2019 15:04:47 -0400 Subject: pin annotations base --- src/client/views/pdf/PDFViewer.scss | 6 ++++ src/client/views/pdf/PDFViewer.tsx | 72 +++++++++++++++++++++++++++++++++++-- src/client/views/pdf/Page.tsx | 22 ++++++++++-- 3 files changed, 96 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 831e6add1..57be04b93 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -34,4 +34,10 @@ position: absolute; top: 0; overflow: visible hidden; +} + +.pdfViewer-pinAnnotation { + background-color: red; + position: absolute; + border-radius: 100%; } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 092765324..bc773ebef 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -9,9 +9,11 @@ import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; import { NumCast, Cast, BoolCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; -import { DocUtils } from "../../documents/Documents"; +import { DocUtils, Docs } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; import { SelectionManager } from "../../util/SelectionManager"; +import { List } from "../../../new_fields/List"; +import { DocumentContentsView } from "../nodes/DocumentContentsView"; interface IPDFViewerProps { url: string; @@ -56,6 +58,8 @@ interface IViewerProps { url: string; } +const PinRadius = 25; + /** * Handles rendering and virtualization of the pdf */ @@ -193,6 +197,7 @@ class Viewer extends React.Component { pageLoaded={this.pageLoaded} parent={this.props.parent} renderAnnotations={this.renderAnnotations} + makePin={this.createPinAnnotation} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(() => false); @@ -229,6 +234,7 @@ class Viewer extends React.Component { name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} parent={this.props.parent} + makePin={this.createPinAnnotation} renderAnnotations={this.renderAnnotations} {...this.props} /> ); @@ -242,6 +248,33 @@ class Viewer extends React.Component { return; } + createPinAnnotation = (x: number, y: number): void => { + let targetDoc = Docs.TextDocument({ title: "New Pin Annotation" }); + + let pinAnno = new Doc(); + pinAnno.x = x; + pinAnno.y = y; + pinAnno.width = pinAnno.height = PinRadius; + pinAnno.page = this.getIndex(y); + pinAnno.target = targetDoc; + pinAnno.type = AnnotationTypes.Pin; + // this._annotations.push(pinAnno); + let annotations = DocListCast(this.props.parent.Document.annotations); + if (annotations && annotations.length) { + annotations.push(pinAnno); + this.props.parent.Document.annotations = new List(annotations); + } + else { + this.props.parent.Document.annotations = new List([pinAnno]); + } + // let pinAnno = document.createElement("div"); + // pinAnno.className = "pdfViewer-pinAnnotation"; + // pinAnno.style.top = (y - (radius / 2)).toString(); + // pinAnno.style.left = (x - (radius / 2)).toString(); + // pinAnno.style.height = pinAnno.style.width = radius.toString(); + // if (this._annotationLayer.current) this._annotationLayer.current.append(pinAnno); + } + // get the page index that the vertical offset passed in is on getIndex = (vOffset: number) => { if (this._loaded) { @@ -295,6 +328,18 @@ class Viewer extends React.Component { return counter; } + renderAnnotation = (anno: Doc): JSX.Element => { + let type = NumCast(anno.type); + switch (type) { + case AnnotationTypes.Pin: + return ; + case AnnotationTypes.Region: + return ; + default: + return
; + } + } + render() { return (
@@ -303,7 +348,7 @@ class Viewer extends React.Component {
- {this._annotations.map(anno => )} + {this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -311,6 +356,10 @@ class Viewer extends React.Component { } } +export enum AnnotationTypes { + Region, Pin +} + interface IAnnotationProps { x: number; y: number; @@ -319,6 +368,25 @@ interface IAnnotationProps { document: Doc; } +class PinAnnotation extends React.Component { + @observable private _backgroundColor: string = "red"; + + pointerDown = (e: React.PointerEvent) => { + + } + + render() { + let targetDoc = Cast(this.props.document.targetDoc, Doc, Docs.TextDocument({ title: "New Pin Annotation" })); + return ( +
+ {/* */} +
+ ); + } +} + class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 5738889c4..fe7369aeb 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -13,6 +13,7 @@ import { emptyFunction } from "../../../Utils"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; +import { AnnotationTypes } from "./PDFViewer"; interface IPageProps { pdf: Opt; @@ -22,6 +23,7 @@ interface IPageProps { pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; parent: PDFBox; renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; + makePin: (x: number, y: number) => void; } @observer @@ -142,6 +144,7 @@ export default class Page extends React.Component { annoDoc.width = anno.offsetWidth; annoDoc.page = this.props.page; annoDoc.target = targetDoc; + annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); anno.remove(); @@ -169,7 +172,6 @@ export default class Page extends React.Component { targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights let annotationDocs = this.makeAnnotationDocuments(targetDoc); - console.log(annotationDocs); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(...annotationDocs); @@ -364,6 +366,22 @@ export default class Page extends React.Component { document.removeEventListener("pointerup", this.onSelectEnd); } + doubleClick = (e: React.MouseEvent) => { + let target: any = e.target; + // if double clicking text + if (target && target.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); + } + } + renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { let annoBox = document.createElement("div"); if (anno) { @@ -379,7 +397,7 @@ export default class Page extends React.Component { render() { return ( -
+
-- cgit v1.2.3-70-g09d2 From b96281d18a9c4ca0ea7f8360d7f69d12c325fada Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 11 Jun 2019 19:02:00 -0400 Subject: pin annotations --- src/client/views/nodes/PDFBox.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 136 +++++++++++++++++++++++++++++-------- src/client/views/pdf/Page.tsx | 4 +- 3 files changed, 110 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index de75c67cf..5b118185b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -50,6 +50,7 @@ export class PDFBox extends DocComponent(PdfDocumen trace(); // uses mozilla pdf as default const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); + console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
{ @action componentDidMount() { const pdfUrl = this.props.url; + console.log("pdf starting to load") let promise = Pdfjs.getDocument(pdfUrl).promise; promise.then((pdf: Pdfjs.PDFDocumentProxy) => { - runInAction(() => this._pdf = pdf); + runInAction(() => { + console.log("pdf url received"); + this._pdf = pdf; + }); }); } @@ -77,8 +85,16 @@ class Viewer extends React.Component { @observable private _annotations: Doc[] = []; private _pageBuffer: number = 1; + private _annotationLayer: React.RefObject; private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; + private _pagesLoaded: number = 0; + + constructor(props: IViewerProps) { + super(props); + + this._annotationLayer = React.createRef(); + } componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); @@ -112,8 +128,9 @@ class Viewer extends React.Component { ); } - // On load, render pdf - setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); + setTimeout(() => { + this.renderPages(this.startIndex, this.endIndex, true); + }, 1000); } componentWillUnmount = () => { @@ -150,7 +167,7 @@ class Viewer extends React.Component { } @computed get endIndex(): number { - let width = this._pageSizes.map(i => i.width); + let width = this._pageSizes.map(i => i ? i.width : 0); return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer); } @@ -185,6 +202,10 @@ class Viewer extends React.Component { return; } + if (this._pageSizes.length !== numPages) { + this._pageSizes = new Array(numPages).map(i => ({ width: 0, height: 0 })); + } + // this is only for an initial render to get all of the pages rendered if (this._visibleElements.length !== numPages) { let divs = Array.from(Array(numPages).keys()).map(i => ( @@ -248,14 +269,14 @@ class Viewer extends React.Component { return; } - createPinAnnotation = (x: number, y: number): void => { - let targetDoc = Docs.TextDocument({ title: "New Pin Annotation" }); + createPinAnnotation = (x: number, y: number, page: number): void => { + let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" }); let pinAnno = new Doc(); pinAnno.x = x; pinAnno.y = y; pinAnno.width = pinAnno.height = PinRadius; - pinAnno.page = this.getIndex(y); + pinAnno.page = page; pinAnno.target = targetDoc; pinAnno.type = AnnotationTypes.Pin; // this._annotations.push(pinAnno); @@ -301,18 +322,18 @@ class Viewer extends React.Component { } let numPages = this.props.pdf ? this.props.pdf.numPages : 0; this.props.loaded(page.width, page.height); - if (index > this._pageSizes.length) { - this._pageSizes.push({ width: page.width, height: page.height }); - } - else { - this._pageSizes[index - 1] = { width: page.width, height: page.height }; - } - if (index === numPages) { + this._pageSizes[index - 1] = { width: page.width, height: page.height }; + this._pagesLoaded++; + if (this._pagesLoaded === numPages) { this._loaded = true; let divs = Array.from(Array(numPages).keys()).map(i => (
)); this._visibleElements = new Array(...divs); + // On load, render pdf + // setTimeout(() => { + this.renderPages(this.startIndex, this.endIndex, true); + // }, 1000); } } @@ -332,21 +353,38 @@ class Viewer extends React.Component { let type = NumCast(anno.type); switch (type) { case AnnotationTypes.Pin: - return ; + return ; case AnnotationTypes.Region: - return ; + return ; default: return
; } } + onDrop = (e: React.DragEvent) => { + console.log("Dropped!"); + } + + // ScreenToLocalTransform = (): Transform => { + // if (this._annotationLayer.current) { + // let boundingRect = this._annotationLayer.current.getBoundingClientRect(); + // let x = boundingRect.left; + // let y = boundingRect.top; + // let scale = NumCast(this.props.parent.Document.nativeWidth) / boundingRect.width; + // let t = new Transform(x, y, scale); + // return t; + // } + // return Transform.Identity(); + // } + render() { + trace(); return ( -
+
{this._visibleElements}
-
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -365,25 +403,65 @@ interface IAnnotationProps { y: number; width: number; height: number; + parent: Viewer; document: Doc; } +@observer class PinAnnotation extends React.Component { - @observable private _backgroundColor: string = "red"; + @observable private _backgroundColor: string = "green"; + @observable private _display: string = "initial"; - pointerDown = (e: React.PointerEvent) => { + private _selected: boolean = true; + @action + pointerDown = (e: React.PointerEvent) => { + if (this._selected) { + this._backgroundColor = "red"; + this._display = "none"; + this._selected = false; + } + else { + this._backgroundColor = "green"; + this._display = "initial"; + this._selected = true; + } + e.preventDefault(); + e.stopPropagation(); } render() { - let targetDoc = Cast(this.props.document.targetDoc, Doc, Docs.TextDocument({ title: "New Pin Annotation" })); - return ( -
- {/* */} -
- ); + let targetDoc = Cast(this.props.document.target, Doc); + if (targetDoc instanceof Doc) { + return ( +
+
+ 1} + PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} + PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} + focus={emptyFunction} + selectOnLoad={false} + parentActive={this.props.parent.props.parent.props.active} + whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged} + bringToFront={emptyFunction} + addDocTab={this.props.parent.props.parent.props.addDocTab} + /> +
+
+ ); + } + return null; } } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index fe7369aeb..73a7a93a0 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -23,7 +23,7 @@ interface IPageProps { pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; parent: PDFBox; renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; - makePin: (x: number, y: number) => void; + makePin: (x: number, y: number, page: number) => void; } @observer @@ -378,7 +378,7 @@ export default class Page extends React.Component { 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.makePin(x, y, this.props.page); } } -- cgit v1.2.3-70-g09d2 From 9ec4a529dc886acca8f147cfe913e60f938f3bda Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 14:35:24 -0400 Subject: reverse annotating --- src/client/util/DragManager.ts | 6 +- src/client/views/pdf/PDFViewer.tsx | 112 ++++++++++++++++++++++++++++++------- src/client/views/pdf/Page.tsx | 27 ++++++--- 3 files changed, 116 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index e92ed9b4a..2ffb77e44 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -154,14 +154,14 @@ export namespace DragManager { } export class AnnotationDragData { - constructor(dragDoc: Doc, annotationDocs: Doc[], dropDoc: Doc) { + constructor(dragDoc: Doc, annotationDoc: Doc, dropDoc: Doc) { this.dragDocument = dragDoc; this.dropDocument = dropDoc; - this.annotationDocuments = annotationDocs; + this.annotationDocument = annotationDoc; this.xOffset = this.yOffset = 0; } dragDocument: Doc; - annotationDocuments: Doc[]; + annotationDocument: Doc; dropDocument: Doc; xOffset: number; yOffset: number; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 6823f640e..bfd0e036f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -7,7 +7,7 @@ import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; -import { NumCast, Cast, BoolCast } from "../../../new_fields/Types"; +import { NumCast, Cast, BoolCast, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; import { DocUtils, Docs } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; @@ -18,6 +18,8 @@ import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocum import { Transform } from "../../util/Transform"; import { emptyFunction, returnTrue, returnFalse } from "../../../Utils"; import { DocumentView } from "../nodes/DocumentView"; +import { DragManager } from "../../util/DragManager"; +import { Dictionary } from "typescript-collections"; interface IPDFViewerProps { url: string; @@ -83,12 +85,15 @@ class Viewer extends React.Component { @observable private _loaded: boolean = false; @observable private _pdf: Opt; @observable private _annotations: Doc[] = []; + @observable private _pointerEvents: "all" | "none" = "all"; + @observable private _savedAnnotations: Dictionary = new Dictionary(); private _pageBuffer: number = 1; private _annotationLayer: React.RefObject; private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; private _pagesLoaded: number = 0; + private _dropDisposer?: DragManager.DragDropDisposer; constructor(props: IViewerProps) { super(props); @@ -96,6 +101,7 @@ class Viewer extends React.Component { this._annotationLayer = React.createRef(); } + @action componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); // reaction for when document gets (de)selected @@ -105,10 +111,12 @@ class Viewer extends React.Component { // if deselected, render images in place of pdf if (wasSelected && !this.props.parent.props.isSelected()) { this.saveThumbnail(); + this._pointerEvents = "all"; } // if selected, render pdf else if (!wasSelected && this.props.parent.props.isSelected()) { this.renderPages(this.startIndex, this.endIndex, true); + this._pointerEvents = "none"; } wasSelected = this.props.parent.props.isSelected(); }, @@ -133,6 +141,57 @@ class Viewer extends React.Component { }, 1000); } + private mainCont = (div: HTMLDivElement | null) => { + if (this._dropDisposer) { + this._dropDisposer(); + } + if (div) { + this._dropDisposer = DragManager.MakeDropTarget(div, { + handlers: { drop: this.drop.bind(this) } + }); + } + } + + makeAnnotationDocuments = (sourceDoc: Doc): Doc => { + let annoDocs: Doc[] = []; + this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { + for (let anno of value) { + let annoDoc = new Doc(); + 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.page = key; + annoDoc.target = sourceDoc; + annoDoc.type = AnnotationTypes.Region; + annoDocs.push(annoDoc); + anno.remove(); + } + }); + + let annoDoc = new Doc(); + annoDoc.annotations = new List(annoDocs); + DocUtils.MakeLink(sourceDoc, annoDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + this._savedAnnotations.clear(); + return annoDoc; + } + + drop = async (e: Event, de: DragManager.DropEvent) => { + if (de.data instanceof DragManager.LinkDragData) { + let sourceDoc = de.data.linkSourceDocument; + let destDoc = this.makeAnnotationDocuments(sourceDoc); + let targetAnnotations = DocListCast(this.props.parent.Document.annotations); + if (targetAnnotations) { + targetAnnotations.push(destDoc); + this.props.parent.Document.annotations = new List(targetAnnotations); + } + else { + this.props.parent.Document.annotations = new List([destDoc]); + } + } + e.stopPropagation(); + } + componentWillUnmount = () => { if (this._reactionDisposer) { this._reactionDisposer(); @@ -219,6 +278,8 @@ class Viewer extends React.Component { parent={this.props.parent} renderAnnotations={this.renderAnnotations} makePin={this.createPinAnnotation} + sendAnnotations={this.receiveAnnotations} + receiveAnnotations={this.sendAnnotations} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(() => false); @@ -257,6 +318,8 @@ class Viewer extends React.Component { parent={this.props.parent} makePin={this.createPinAnnotation} renderAnnotations={this.renderAnnotations} + sendAnnotations={this.receiveAnnotations} + receiveAnnotations={this.sendAnnotations} {...this.props} /> ); this._isPage[i] = true; @@ -269,6 +332,15 @@ class Viewer extends React.Component { return; } + @action + receiveAnnotations = (annotations: HTMLDivElement[], page: number) => { + this._savedAnnotations.setValue(page, annotations); + } + + sendAnnotations = (page: number): HTMLDivElement[] | undefined => { + return this._savedAnnotations.getValue(page); + } + createPinAnnotation = (x: number, y: number, page: number): void => { let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" }); @@ -280,13 +352,15 @@ class Viewer extends React.Component { pinAnno.target = targetDoc; pinAnno.type = AnnotationTypes.Pin; // this._annotations.push(pinAnno); + let annoDoc = new Doc(); + annoDoc.annotations = new List([pinAnno]); let annotations = DocListCast(this.props.parent.Document.annotations); if (annotations && annotations.length) { - annotations.push(pinAnno); + annotations.push(annoDoc); this.props.parent.Document.annotations = new List(annotations); } else { - this.props.parent.Document.annotations = new List([pinAnno]); + this.props.parent.Document.annotations = new List([annoDoc]); } // let pinAnno = document.createElement("div"); // pinAnno.className = "pdfViewer-pinAnnotation"; @@ -349,20 +423,20 @@ class Viewer extends React.Component { return counter; } - renderAnnotation = (anno: Doc): JSX.Element => { - let type = NumCast(anno.type); - switch (type) { - case AnnotationTypes.Pin: - return ; - case AnnotationTypes.Region: - return ; - default: - return
; - } - } - - onDrop = (e: React.DragEvent) => { - console.log("Dropped!"); + renderAnnotation = (anno: Doc): JSX.Element[] => { + let annotationDocs = DocListCast(anno.annotations); + let res = annotationDocs.map(a => { + let type = NumCast(a.type); + switch (type) { + case AnnotationTypes.Pin: + return ; + case AnnotationTypes.Region: + return ; + default: + return
; + } + }); + return res; } // ScreenToLocalTransform = (): Transform => { @@ -380,11 +454,11 @@ class Viewer extends React.Component { render() { trace(); return ( -
+
{this._visibleElements}
-
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 73a7a93a0..908804605 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -24,6 +24,8 @@ interface IPageProps { parent: PDFBox; renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; makePin: (x: number, y: number, page: number) => void; + sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; + receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; } @observer @@ -61,11 +63,19 @@ export default class Page extends React.Component { componentDidMount = (): void => { if (this.props.pdf) { this.update(this.props.pdf); + } + let received = this.props.receiveAnnotations(this.props.page); + this._currentAnnotations = received ? received : []; + if (this._annotationLayer.current) { + this._annotationLayer.current.append(...this._currentAnnotations); } } componentWillUnmount = (): void => { + console.log(this._currentAnnotations); + this.props.sendAnnotations(this._currentAnnotations, this.props.page); + if (this._reactionDisposer) { this._reactionDisposer(); } @@ -134,8 +144,9 @@ export default class Page extends React.Component { * This method makes the list of current annotations into documents linked to * the parameter passed in. */ - makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { + makeAnnotationDocuments = (targetDoc: Doc): Doc => { let annoDocs: Doc[] = []; + for (let anno of this._currentAnnotations) { let annoDoc = new Doc(); annoDoc.x = anno.offsetLeft; @@ -146,11 +157,13 @@ export default class Page extends React.Component { annoDoc.target = targetDoc; annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); - DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); anno.remove(); } + let annoDoc = new Doc(); + annoDoc.annotations = new List(annoDocs); + DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); this._currentAnnotations = []; - return annoDocs; + return annoDoc; } /** @@ -171,17 +184,17 @@ export default class Page extends React.Component { let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights - let annotationDocs = this.makeAnnotationDocuments(targetDoc); + let annotationDoc = this.makeAnnotationDocuments(targetDoc); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { - targetAnnotations.push(...annotationDocs); + targetAnnotations.push(annotationDoc); this.props.parent.Document.annotations = new List(targetAnnotations); } else { - this.props.parent.Document.annotations = new List(annotationDocs); + this.props.parent.Document.annotations = new List([annotationDoc]); } // create dragData and star tdrag - let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); + let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); if (this._textLayer.current) { DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, { handlers: { -- cgit v1.2.3-70-g09d2 From 2cec2be7f16cf8ef656def60659e5555b75f2ce7 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 15:09:11 -0400 Subject: oops --- src/client/views/pdf/PDFViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index bfd0e036f..deacdd623 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -171,7 +171,7 @@ class Viewer extends React.Component { let annoDoc = new Doc(); annoDoc.annotations = new List(annoDocs); - DocUtils.MakeLink(sourceDoc, annoDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); this._savedAnnotations.clear(); return annoDoc; } -- cgit v1.2.3-70-g09d2 From 0f85b1b7a6198013269c19a36dc8bcf14d2a4952 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 16:02:25 -0400 Subject: Refactored some code --- src/client/views/pdf/PDFViewer.tsx | 76 +++++++++++++++++--------------------- src/client/views/pdf/Page.tsx | 67 +++------------------------------ 2 files changed, 39 insertions(+), 104 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index deacdd623..e54dfea6f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -278,7 +278,9 @@ class Viewer extends React.Component { parent={this.props.parent} renderAnnotations={this.renderAnnotations} makePin={this.createPinAnnotation} + createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} + makeAnnotationDocuments={this.makeAnnotationDocuments} receiveAnnotations={this.sendAnnotations} {...this.props} /> )); @@ -318,7 +320,9 @@ class Viewer extends React.Component { parent={this.props.parent} makePin={this.createPinAnnotation} renderAnnotations={this.renderAnnotations} + createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} + makeAnnotationDocuments={this.makeAnnotationDocuments} receiveAnnotations={this.sendAnnotations} {...this.props} /> ); @@ -334,7 +338,13 @@ class Viewer extends React.Component { @action receiveAnnotations = (annotations: HTMLDivElement[], page: number) => { - this._savedAnnotations.setValue(page, annotations); + if (page === -1) { + this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); + this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, annotations)); + } + else { + this._savedAnnotations.setValue(page, annotations); + } } sendAnnotations = (page: number): HTMLDivElement[] | undefined => { @@ -346,7 +356,7 @@ class Viewer extends React.Component { let pinAnno = new Doc(); pinAnno.x = x; - pinAnno.y = y; + pinAnno.y = y + this.getPageHeight(page); pinAnno.width = pinAnno.height = PinRadius; pinAnno.page = page; pinAnno.target = targetDoc; @@ -362,12 +372,6 @@ class Viewer extends React.Component { else { this.props.parent.Document.annotations = new List([annoDoc]); } - // let pinAnno = document.createElement("div"); - // pinAnno.className = "pdfViewer-pinAnnotation"; - // pinAnno.style.top = (y - (radius / 2)).toString(); - // pinAnno.style.left = (x - (radius / 2)).toString(); - // pinAnno.style.height = pinAnno.style.width = radius.toString(); - // if (this._annotationLayer.current) this._annotationLayer.current.append(pinAnno); } // get the page index that the vertical offset passed in is on @@ -404,10 +408,7 @@ class Viewer extends React.Component {
)); this._visibleElements = new Array(...divs); - // On load, render pdf - // setTimeout(() => { this.renderPages(this.startIndex, this.endIndex, true); - // }, 1000); } } @@ -423,15 +424,32 @@ class Viewer extends React.Component { return counter; } + createAnnotation = (div: HTMLDivElement, page: number) => { + if (this._annotationLayer.current) { + if (div.style.top) { + div.style.top = (parseInt(div.style.top) + this.getPageHeight(page)).toString(); + } + this._annotationLayer.current.append(div); + let savedPage = this._savedAnnotations.getValue(page); + if (savedPage) { + savedPage.push(div); + this._savedAnnotations.setValue(page, savedPage); + } + else { + this._savedAnnotations.setValue(page, [div]); + } + } + } + renderAnnotation = (anno: Doc): JSX.Element[] => { let annotationDocs = DocListCast(anno.annotations); let res = annotationDocs.map(a => { let type = NumCast(a.type); switch (type) { case AnnotationTypes.Pin: - return ; + return ; case AnnotationTypes.Region: - return ; + return ; default: return
; } @@ -439,18 +457,6 @@ class Viewer extends React.Component { return res; } - // ScreenToLocalTransform = (): Transform => { - // if (this._annotationLayer.current) { - // let boundingRect = this._annotationLayer.current.getBoundingClientRect(); - // let x = boundingRect.left; - // let y = boundingRect.top; - // let scale = NumCast(this.props.parent.Document.nativeWidth) / boundingRect.width; - // let t = new Transform(x, y, scale); - // return t; - // } - // return Transform.Identity(); - // } - render() { trace(); return ( @@ -458,8 +464,8 @@ class Viewer extends React.Component {
{this._visibleElements}
-
-
+
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -542,26 +548,10 @@ class PinAnnotation extends React.Component { class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; - // private _reactionDisposer?: IReactionDisposer; - - constructor(props: IAnnotationProps) { - super(props); - - // this._reactionDisposer = reaction( - // () => { BoolCast(this.props.document.selected); }, - // () => { this._backgroundColor = BoolCast(this.props.document.selected) ? "yellow" : "red"; }, - // { fireImmediately: true } - // ) - } - onPointerDown = (e: React.PointerEvent) => { let targetDoc = Cast(this.props.document.target, Doc, null); if (targetDoc) { DocumentManager.Instance.jumpToDocument(targetDoc); - // let annotations = DocListCast(targetDoc.proto!.linkedFromDocs); - // if (annotations && annotations.length) { - // annotations.forEach(anno => anno.selected = true); - // } } } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 998ac25fd..9e3bf4954 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -26,6 +26,8 @@ interface IPageProps { makePin: (x: number, y: number, page: number) => void; sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; + createAnnotation: (div: HTMLDivElement, page: number) => void; + makeAnnotationDocuments: (doc: Doc) => Doc; } @observer @@ -46,7 +48,6 @@ export default class Page extends React.Component { private _annotationLayer: React.RefObject; private _marquee: React.RefObject; private _curly: React.RefObject; - private _currentAnnotations: HTMLDivElement[] = []; private _marqueeing: boolean = false; private _dragging: boolean = false; private _reactionDisposer?: IReactionDisposer; @@ -64,18 +65,9 @@ export default class Page extends React.Component { if (this.props.pdf) { this.update(this.props.pdf); } - - let received = this.props.receiveAnnotations(this.props.page); - this._currentAnnotations = received ? received : []; - if (this._annotationLayer.current) { - this._annotationLayer.current.append(...this._currentAnnotations); - } } componentWillUnmount = (): void => { - console.log(this._currentAnnotations); - this.props.sendAnnotations(this._currentAnnotations, this.props.page); - if (this._reactionDisposer) { this._reactionDisposer(); } @@ -139,33 +131,6 @@ export default class Page extends React.Component { } } - /** - * @param targetDoc: Document that annotations are linked to - * This method makes the list of current annotations into documents linked to - * the parameter passed in. - */ - makeAnnotationDocuments = (targetDoc: Doc): Doc => { - let annoDocs: Doc[] = []; - - for (let anno of this._currentAnnotations) { - let annoDoc = new Doc(); - annoDoc.x = anno.offsetLeft; - annoDoc.y = anno.offsetTop; - annoDoc.height = anno.offsetHeight; - annoDoc.width = anno.offsetWidth; - annoDoc.page = this.props.page; - annoDoc.target = targetDoc; - annoDoc.type = AnnotationTypes.Region; - annoDocs.push(annoDoc); - anno.remove(); - } - let annoDoc = new Doc(); - annoDoc.annotations = new List(annoDocs); - DocUtils.MakeLink(annoDoc, targetDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); - this._currentAnnotations = []; - return annoDoc; - } - /** * This is temporary for creating annotations from highlights. It will * start a drag event and create or put the necessary info into the drag event. @@ -184,7 +149,7 @@ export default class Page extends React.Component { let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights - let annotationDoc = this.makeAnnotationDocuments(targetDoc); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(annotationDoc); @@ -246,10 +211,7 @@ export default class Page extends React.Component { document.removeEventListener("pointerup", this.onSelectEnd); document.addEventListener("pointerup", this.onSelectEnd); if (!e.ctrlKey) { - for (let anno of this._currentAnnotations) { - anno.remove(); - } - this._currentAnnotations = []; + this.props.sendAnnotations([], -1); } } } @@ -336,10 +298,7 @@ export default class Page extends React.Component { copy.style.opacity = this._marquee.current.style.opacity; } copy.className = this._marquee.current.className; - if (this._annotationLayer.current) { - this._annotationLayer.current.append(copy); - this._currentAnnotations.push(copy); - } + this.props.createAnnotation(copy, this.props.page); this._marquee.current.style.opacity = "0"; } @@ -362,8 +321,7 @@ export default class Page extends React.Component { annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); - this._currentAnnotations.push(annoBox); + this.props.createAnnotation(annoBox, this.props.page); } } } @@ -395,19 +353,6 @@ export default class Page extends React.Component { } } - renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { - let annoBox = document.createElement("div"); - if (anno) { - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = NumCast(anno.x).toString(); - annoBox.style.left = NumCast(anno.y).toString(); - annoBox.style.width = NumCast(anno.width).toString(); - annoBox.style.height = NumCast(anno.height).toString() - } - return annoBox; - } - render() { return (
-- cgit v1.2.3-70-g09d2 From eaa66ece6340534ad09cf83134b344ef43816cd9 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 18:30:53 -0400 Subject: deleting pins and better pin saving --- src/client/views/nodes/PDFBox.scss | 23 +++++++++++---- src/client/views/nodes/PDFBox.tsx | 3 ++ src/client/views/pdf/PDFViewer.tsx | 57 ++++++++++++++++++++++++++++++++------ 3 files changed, 70 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 449408a61..f4d455be7 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -2,39 +2,52 @@ transform-origin: left top; position: absolute; top: 0; - left:0; + left: 0; } + .react-pdf__Page__textContent span { user-select: text; } + .react-pdf__Document { position: absolute; } + .pdfBox-buttonTray { - position:absolute; + position: absolute; top: 0; - left:0; + left: 0; z-index: 25; pointer-events: all; } + .pdfBox-thumbnail { position: absolute; width: 100%; } + .pdfButton { pointer-events: all; width: 100px; - height:100px; + height: 100px; } + .pdfBox-cont { - pointer-events: none ; + pointer-events: none; + display: flex; + flex-direction: row; + span { pointer-events: none !important; } } + .pdfBox-cont-interactive { pointer-events: all; + display: flex; + flex-direction: row; } + .pdfBox-contentContainer { position: absolute; transform-origin: left top; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5b118185b..4214a6777 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -19,6 +19,7 @@ import { makeInterface } from "../../../new_fields/Schema"; import { PDFViewer } from "../pdf/PDFViewer"; import { PdfField } from "../../../new_fields/URLField"; import { HeightSym, WidthSym } from "../../../new_fields/Doc"; +import { CollectionStackingView } from "../collections/CollectionStackingView"; type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -43,6 +44,7 @@ export class PDFBox extends DocComponent(PdfDocumen onScroll = (e: React.UIEvent) => { if (e.currentTarget) { this._scrollY = e.currentTarget.scrollTop; + // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); } } @@ -57,6 +59,7 @@ export class PDFBox extends DocComponent(PdfDocumen style={{ overflowY: "scroll", overflowX: "hidden", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}> + {/*
*/}
); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index e54dfea6f..fe442c906 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -152,7 +152,7 @@ class Viewer extends React.Component { } } - makeAnnotationDocuments = (sourceDoc: Doc): Doc => { + makeAnnotationDocument = (sourceDoc: Doc): Doc => { let annoDocs: Doc[] = []; this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { @@ -179,7 +179,7 @@ class Viewer extends React.Component { drop = async (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.LinkDragData) { let sourceDoc = de.data.linkSourceDocument; - let destDoc = this.makeAnnotationDocuments(sourceDoc); + let destDoc = this.makeAnnotationDocument(sourceDoc); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(destDoc); @@ -280,7 +280,7 @@ class Viewer extends React.Component { makePin={this.createPinAnnotation} createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} - makeAnnotationDocuments={this.makeAnnotationDocuments} + makeAnnotationDocuments={this.makeAnnotationDocument} receiveAnnotations={this.sendAnnotations} {...this.props} /> )); @@ -322,7 +322,7 @@ class Viewer extends React.Component { renderAnnotations={this.renderAnnotations} createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} - makeAnnotationDocuments={this.makeAnnotationDocuments} + makeAnnotationDocuments={this.makeAnnotationDocument} receiveAnnotations={this.sendAnnotations} {...this.props} /> ); @@ -492,29 +492,70 @@ class PinAnnotation extends React.Component { @observable private _backgroundColor: string = "green"; @observable private _display: string = "initial"; - private _selected: boolean = true; + private _mainCont: React.RefObject; + + constructor(props: IAnnotationProps) { + super(props); + this._mainCont = React.createRef(); + } + + componentDidMount = () => { + let selected = this.props.document.selected; + if (selected && BoolCast(selected)) { + runInAction(() => { + this._backgroundColor = "green"; + this._display = "initial"; + }) + } + else { + runInAction(() => { + this._backgroundColor = "red"; + this._display = "none"; + }) + } + } @action pointerDown = (e: React.PointerEvent) => { - if (this._selected) { + let selected = this.props.document.selected; + if (selected && BoolCast(selected)) { this._backgroundColor = "red"; this._display = "none"; - this._selected = false; + this.props.document.selected = false; } else { this._backgroundColor = "green"; this._display = "initial"; - this._selected = true; + this.props.document.selected = true; } e.preventDefault(); e.stopPropagation(); } + @action + doubleClick = (e: React.MouseEvent) => { + if (this._mainCont.current) { + let annotations = DocListCast(this.props.parent.props.parent.Document.annotations); + if (annotations && annotations.length) { + let index = annotations.indexOf(this.props.document); + annotations.splice(index, 1); + this.props.parent.props.parent.Document.annotations = new List(annotations); + } + // this._mainCont.current.childNodes.forEach(e => e.remove()); + this._mainCont.current.style.display = "none"; + // if (this._mainCont.current.parentElement) { + // this._mainCont.current.remove(); + // } + } + e.stopPropagation(); + } + render() { let targetDoc = Cast(this.props.document.target, Doc); if (targetDoc instanceof Doc) { return (
Date: Thu, 13 Jun 2019 22:00:33 -0400 Subject: added collection back --- src/client/documents/Documents.ts | 2 +- src/client/views/collections/CollectionPDFView.tsx | 32 +++++++++++++++++++--- src/client/views/nodes/PDFBox.tsx | 13 ++++++++- src/client/views/pdf/PDFViewer.scss | 1 - src/client/views/pdf/PDFViewer.tsx | 30 ++++++++++++++------ src/client/views/pdf/Page.tsx | 2 +- 6 files changed, 64 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index dfbe2e136..91d3707f6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -170,7 +170,7 @@ export namespace Docs { return textProto; } function CreatePdfPrototype(): Doc { - let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", PDFBox.LayoutString(), + let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), { x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); return pdfProto; } diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 62e8adbec..4af89d780 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,4 +1,4 @@ -import { action, observable } from "mobx"; +import { action, observable, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { ContextMenu } from "../ContextMenu"; import "./CollectionPDFView.scss"; @@ -9,12 +9,36 @@ import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from ". import { emptyFunction } from "../../../Utils"; import { NumCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; +import { HeightSym, WidthSym } from "../../../new_fields/Doc"; @observer export class CollectionPDFView extends React.Component { + private _reactionDisposer?: IReactionDisposer; + private _buttonTray: React.RefObject; + constructor(props: FieldViewProps) { super(props); + + this._buttonTray = React.createRef(); + } + + componentDidMount() { + this._reactionDisposer = reaction( + () => NumCast(this.props.Document.scrollY), + () => { + // let transform = this.props.ScreenToLocalTransform(); + if (this._buttonTray.current) { + // console.log(this._buttonTray.current.offsetHeight); + // console.log(NumCast(this.props.Document.scrollY)); + let scale = this.nativeWidth() / this.props.Document[WidthSym](); + this.props.Document.panY = NumCast(this.props.Document.scrollY); + // console.log(scale); + } + // console.log(this.props.Document[HeightSym]()); + }, + { fireImmediately: true } + ) } public static LayoutString(fieldKey: string = "data") { @@ -52,12 +76,12 @@ export class CollectionPDFView extends React.Component { private get uIButtons() { let ratio = (this.curPage - 1) / this.numPages * 100; return ( -
+
-
+ {/*
-
+
*/}
); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 4214a6777..acb430deb 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -36,6 +36,10 @@ export class PDFBox extends DocComponent(PdfDocumen let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; doc.nativeWidth = nw; doc.nativeHeight = nh; + let ccv = this.props.ContainingCollectionView; + if (ccv) { + ccv.props.Document.pdfHeight = nh; + } doc.height = nh * (doc[WidthSym]() / nw); } } @@ -45,6 +49,10 @@ export class PDFBox extends DocComponent(PdfDocumen if (e.currentTarget) { this._scrollY = e.currentTarget.scrollTop; // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); + let ccv = this.props.ContainingCollectionView; + if (ccv) { + ccv.props.Document.scrollY = this._scrollY; + } } } @@ -56,7 +64,10 @@ export class PDFBox extends DocComponent(PdfDocumen let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
e.stopPropagation()} className={classname}> {/*
*/} diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 57be04b93..a73df2d58 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -33,7 +33,6 @@ .pdfViewer-annotationLayer { position: absolute; top: 0; - overflow: visible hidden; } .pdfViewer-pinAnnotation { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index fe442c906..144fca9e0 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -188,8 +188,8 @@ class Viewer extends React.Component { else { this.props.parent.Document.annotations = new List([destDoc]); } + e.stopPropagation(); } - e.stopPropagation(); } componentWillUnmount = () => { @@ -465,7 +465,7 @@ class Viewer extends React.Component { {this._visibleElements}
-
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -501,17 +501,31 @@ class PinAnnotation extends React.Component { componentDidMount = () => { let selected = this.props.document.selected; - if (selected && BoolCast(selected)) { + if (!BoolCast(selected)) { runInAction(() => { - this._backgroundColor = "green"; - this._display = "initial"; - }) + this._backgroundColor = "red"; + this._display = "none"; + }); + } + if (selected) { + if (BoolCast(selected)) { + runInAction(() => { + this._backgroundColor = "green"; + this._display = "initial"; + }); + } + else { + runInAction(() => { + this._backgroundColor = "red"; + this._display = "none"; + }); + } } else { runInAction(() => { this._backgroundColor = "red"; this._display = "none"; - }) + }); } } @@ -572,7 +586,7 @@ class PinAnnotation extends React.Component { PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} focus={emptyFunction} - selectOnLoad={false} + selectOnLoad={true} parentActive={this.props.parent.props.parent.props.active} whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged} bringToFront={emptyFunction} diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 9e3bf4954..1c305caa3 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -257,7 +257,7 @@ export default class Page extends React.Component { let ratio = this._marqueeWidth / this._marqueeHeight; if (ratio > 1.5) { // vertical - transform = "rotate(90deg) scale(1, 2)"; + transform = "rotate(90deg) scale(1, 5)"; } else if (ratio < 0.5) { // horizontal -- cgit v1.2.3-70-g09d2 From 6a2cb71af332d4c782c1678750ba955757eaab45 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 11:56:36 -0400 Subject: fixes --- src/client/views/nodes/PDFBox.scss | 6 ++++++ src/client/views/pdf/PDFViewer.scss | 2 ++ src/client/views/pdf/PDFViewer.tsx | 10 +++++----- src/client/views/pdf/Page.tsx | 3 +-- 4 files changed, 14 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index f4d455be7..bb1f534c6 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -41,6 +41,12 @@ pointer-events: none !important; } } +.textlayer { + span { + pointer-events: all !important; + user-select: text; + } +} .pdfBox-cont-interactive { pointer-events: all; diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index a73df2d58..53c33ce0b 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -35,6 +35,8 @@ top: 0; } + + .pdfViewer-pinAnnotation { background-color: red; position: absolute; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 144fca9e0..4b949aa3e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -103,22 +103,22 @@ class Viewer extends React.Component { @action componentDidMount = () => { - let wasSelected = this.props.parent.props.isSelected(); + let wasSelected = this.props.parent.props.active(); // reaction for when document gets (de)selected this._reactionDisposer = reaction( - () => [this.props.parent.props.isSelected(), this.startIndex], + () => [this.props.parent.props.active(), this.startIndex], () => { // if deselected, render images in place of pdf - if (wasSelected && !this.props.parent.props.isSelected()) { + if (wasSelected && !this.props.parent.props.active()) { this.saveThumbnail(); this._pointerEvents = "all"; } // if selected, render pdf - else if (!wasSelected && this.props.parent.props.isSelected()) { + else if (!wasSelected && this.props.parent.props.active()) { this.renderPages(this.startIndex, this.endIndex, true); this._pointerEvents = "none"; } - wasSelected = this.props.parent.props.isSelected(); + wasSelected = this.props.parent.props.active(); }, { fireImmediately: true } ); diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 1c305caa3..fa3f7baca 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -195,7 +195,6 @@ export default class Page extends React.Component { e.stopPropagation(); } else { - e.stopPropagation(); // set marquee x and y positions to the spatially transformed position let current = this._textLayer.current; if (current) { @@ -355,7 +354,7 @@ export default class Page extends React.Component { render() { return ( -
+
-- cgit v1.2.3-70-g09d2 From 41c290677030cde827c438d9bfda0dbeac64aa14 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 12:30:24 -0400 Subject: changed selectOnLoad --- src/client/views/pdf/PDFViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 4b949aa3e..d6081142a 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -586,7 +586,7 @@ class PinAnnotation extends React.Component { PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} focus={emptyFunction} - selectOnLoad={true} + selectOnLoad={false} parentActive={this.props.parent.props.parent.props.active} whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged} bringToFront={emptyFunction} -- cgit v1.2.3-70-g09d2 From 94ed67966e7fdc7aa36b1a8b045153d0d661ce57 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 12:50:18 -0400 Subject: early impl of pdf menu --- src/client/views/MainView.tsx | 5 ++- src/client/views/pdf/Page.tsx | 96 +++++++++++++++++++++++++++++++------------ 2 files changed, 74 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 42d5929bf..0779e1471 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell } from '@fortawesome/free-solid-svg-icons'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; @@ -32,6 +32,7 @@ import { listSpec } from '../../new_fields/Schema'; import { Id } from '../../new_fields/FieldSymbols'; import { HistoryUtil } from '../util/History'; import { CollectionBaseView } from './collections/CollectionBaseView'; +import PDFMenu from './pdf/PDFMenu'; @observer @@ -88,6 +89,7 @@ export class MainView extends React.Component { library.add(faFilm); library.add(faMusic); library.add(faTree); + library.add(faCommentAlt); this.initEventListeners(); this.initAuthenticationRouters(); } @@ -315,6 +317,7 @@ export class MainView extends React.Component { {this.nodesMenu()} {this.miscButtons} +
); diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 1c305caa3..2c237740c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -14,6 +14,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; import { AnnotationTypes } from "./PDFViewer"; +import PDFMenu from "./PDFMenu"; interface IPageProps { pdf: Opt; @@ -272,7 +273,7 @@ export default class Page extends React.Component { } @action - onSelectEnd = (): void => { + onSelectEnd = (e: PointerEvent): void => { if (this._marqueeing) { this._marqueeing = false; if (this._marquee.current) { @@ -303,36 +304,79 @@ export default class Page extends React.Component { } this._marqueeHeight = this._marqueeWidth = 0; + PDFMenu.Instance.Left = e.clientX; + PDFMenu.Instance.Top = e.clientY; } else { let sel = window.getSelection(); - // if selecting over a range of things - if (sel && sel.type === "Range") { - let clientRects = sel.getRangeAt(0).getClientRects(); - if (this._textLayer.current) { - let boundingRect = this._textLayer.current.getBoundingClientRect(); - for (let i = 0; i < clientRects.length; i++) { - let rect = clientRects.item(i); - if (rect) { - let annoBox = document.createElement("div"); - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); - annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); - annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); - annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - this.props.createAnnotation(annoBox, this.props.page); - } - } - } - // clear selection - if (sel.empty) { // Chrome - sel.empty(); - } else if (sel.removeAllRanges) { // Firefox - sel.removeAllRanges(); - } + if (sel && sel.type === "range") { + + PDFMenu.Instance.Left = e.clientX; + PDFMenu.Instance.Top = e.clientY; } } + // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + // if (this._marqueeing) { + // this._marqueeing = false; + // if (this._marquee.current) { + // let copy = document.createElement("div"); + // // make a copy of the marquee + // copy.style.left = this._marquee.current.style.left; + // copy.style.top = this._marquee.current.style.top; + // copy.style.width = this._marquee.current.style.width; + // copy.style.height = this._marquee.current.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.opacity = this._marquee.current.style.opacity; + // } + // copy.className = this._marquee.current.className; + // this.props.createAnnotation(copy, this.props.page); + // this._marquee.current.style.opacity = "0"; + // } + + // this._marqueeHeight = this._marqueeWidth = 0; + // } + // else { + // let sel = window.getSelection(); + // // if selecting over a range of things + // if (sel && sel.type === "Range") { + // let clientRects = sel.getRangeAt(0).getClientRects(); + // if (this._textLayer.current) { + // let boundingRect = this._textLayer.current.getBoundingClientRect(); + // for (let i = 0; i < clientRects.length; i++) { + // let rect = clientRects.item(i); + // if (rect) { + // let annoBox = document.createElement("div"); + // annoBox.className = "pdfViewer-annotationBox"; + // // transforms the positions from screen onto the pdf div + // annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + // annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + // annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + // annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + // this.props.createAnnotation(annoBox, this.props.page); + // } + // } + // } + // // clear selection + // if (sel.empty) { // Chrome + // sel.empty(); + // } else if (sel.removeAllRanges) { // Firefox + // sel.removeAllRanges(); + // } + // } + // } document.removeEventListener("pointermove", this.onSelectStart); document.removeEventListener("pointerup", this.onSelectEnd); } -- cgit v1.2.3-70-g09d2 From 0b4f3c25471e51d27ddb33dc5ddafafb2c0c03e5 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 13:09:15 -0400 Subject: fixes for full screen. --- src/client/views/nodes/PDFBox.tsx | 3 ++- src/client/views/pdf/PDFViewer.tsx | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index acb430deb..cce7b2631 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -65,7 +65,8 @@ export class PDFBox extends DocComponent(PdfDocumen return (
e.stopPropagation()} className={classname}> diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d6081142a..440a20e8e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -111,14 +111,13 @@ class Viewer extends React.Component { // if deselected, render images in place of pdf if (wasSelected && !this.props.parent.props.active()) { this.saveThumbnail(); - this._pointerEvents = "all"; } // if selected, render pdf else if (!wasSelected && this.props.parent.props.active()) { this.renderPages(this.startIndex, this.endIndex, true); - this._pointerEvents = "none"; } wasSelected = this.props.parent.props.active(); + this._pointerEvents = wasSelected ? "none" : "all"; }, { fireImmediately: true } ); -- cgit v1.2.3-70-g09d2 From 2ef1dd2089ad991f1d4897022f2d28c2eb130837 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 15:47:43 -0400 Subject: highlighting --- src/client/views/nodes/PDFBox.tsx | 10 +++++- src/client/views/pdf/PDFMenu.tsx | 25 +++++++++++-- src/client/views/pdf/PDFViewer.tsx | 6 ++-- src/client/views/pdf/Page.tsx | 74 ++++++++++++++++++++++++++++---------- 4 files changed, 91 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index acb430deb..6ee62bbad 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -34,8 +34,16 @@ export class PDFBox extends DocComponent(PdfDocumen loaded = (nw: number, nh: number) => { if (this.props.Document) { let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + let oldnw = NumCast(doc.nativeWidth); doc.nativeWidth = nw; - doc.nativeHeight = nh; + if (!doc.nativeHeight) { + doc.nativeHeight = nh; + } + else { + let oldnh = NumCast(doc.nativeHeight); + let aspect = oldnh / oldnw; + doc.nativeHeight = nw * aspect; + } let ccv = this.props.ContainingCollectionView; if (ccv) { ccv.props.Document.pdfHeight = nh; diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index cc5c0b77b..a0230113b 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -3,6 +3,8 @@ import "./PDFMenu.scss"; import { observable } from "mobx"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { emptyFunction } from "../../../Utils"; +import { Doc } from "../../../new_fields/Doc"; @observer export default class PDFMenu extends React.Component { @@ -10,6 +12,8 @@ export default class PDFMenu extends React.Component { @observable Top: number = 0; @observable Left: number = 0; + StartDrag: (e: PointerEvent) => void = emptyFunction; + Highlight: (d: Doc | undefined) => void = emptyFunction; constructor(props: Readonly<{}>) { super(props); @@ -17,11 +21,28 @@ export default class PDFMenu extends React.Component { PDFMenu.Instance = this; } + pointerDown = (e: React.PointerEvent) => { + document.removeEventListener("pointermove", this.StartDrag); + document.addEventListener("pointermove", this.StartDrag); + document.removeEventListener("pointerup", this.pointerUp) + document.addEventListener("pointerup", this.pointerUp) + + e.stopPropagation(); + e.preventDefault(); + } + + pointerUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.StartDrag); + document.removeEventListener("pointerup", this.pointerUp); + e.stopPropagation(); + e.preventDefault(); + } + render() { return (
- - + +
) } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d6081142a..fbad39880 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -152,7 +152,7 @@ class Viewer extends React.Component { } } - makeAnnotationDocument = (sourceDoc: Doc): Doc => { + makeAnnotationDocument = (sourceDoc: Doc | undefined): Doc => { let annoDocs: Doc[] = []; this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { @@ -171,7 +171,9 @@ class Viewer extends React.Component { let annoDoc = new Doc(); annoDoc.annotations = new List(annoDocs); - DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + if (sourceDoc) { + DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + } this._savedAnnotations.clear(); return annoDoc; } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index bdb6952cc..44c502a04 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -28,7 +28,7 @@ interface IPageProps { sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; createAnnotation: (div: HTMLDivElement, page: number) => void; - makeAnnotationDocuments: (doc: Doc) => Doc; + makeAnnotationDocuments: (doc: Doc | undefined) => Doc; } @observer @@ -132,6 +132,20 @@ export default class Page extends React.Component { } } + highlight = (targetDoc: Doc | undefined) => { + // creates annotation documents for current highlights + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); + let targetAnnotations = DocListCast(this.props.parent.Document.annotations); + if (targetAnnotations) { + targetAnnotations.push(annotationDoc); + this.props.parent.Document.annotations = new List(targetAnnotations); + } + else { + this.props.parent.Document.annotations = new List([annotationDoc]); + } + return annotationDoc; + } + /** * This is temporary for creating annotations from highlights. It will * start a drag event and create or put the necessary info into the drag event. @@ -149,16 +163,7 @@ export default class Page extends React.Component { // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; - // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); - let targetAnnotations = DocListCast(this.props.parent.Document.annotations); - if (targetAnnotations) { - targetAnnotations.push(annotationDoc); - this.props.parent.Document.annotations = new List(targetAnnotations); - } - else { - this.props.parent.Document.annotations = new List([annotationDoc]); - } + let annotationDoc = this.highlight(targetDoc); // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); if (this._textLayer.current) { @@ -173,8 +178,8 @@ export default class Page extends React.Component { // cleans up events and boolean endDrag = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); + // document.removeEventListener("pointermove", this.startDrag); + // document.removeEventListener("pointerup", this.endDrag); this._dragging = false; e.stopPropagation(); } @@ -185,10 +190,10 @@ export default class Page extends React.Component { 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); + // 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) { let target: any = e.target; @@ -299,6 +304,8 @@ export default class Page extends React.Component { } copy.className = this._marquee.current.className; this.props.createAnnotation(copy, this.props.page); + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; this._marquee.current.style.opacity = "0"; } @@ -308,8 +315,10 @@ export default class Page extends React.Component { } else { let sel = window.getSelection(); - if (sel && sel.type === "range") { - + if (sel && sel.type === "Range") { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; + this.createTextAnnotation(sel); PDFMenu.Instance.Left = e.clientX; PDFMenu.Instance.Top = e.clientY; } @@ -380,6 +389,33 @@ export default class Page extends React.Component { document.removeEventListener("pointerup", this.onSelectEnd); } + @action + createTextAnnotation = (sel: Selection) => { + let clientRects = sel.getRangeAt(0).getClientRects(); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + this.props.createAnnotation(annoBox, this.props.page); + } + } + } + // clear selection + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } + } + doubleClick = (e: React.MouseEvent) => { let target: any = e.target; // if double clicking text -- cgit v1.2.3-70-g09d2 From 15e8341334419142f4a54db23cc643f18ba0e0f1 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 15:53:13 -0400 Subject: a few tweaks to fix unfreezing documents to give them a margin --- src/client/views/collections/CollectionDockingView.tsx | 8 ++++++-- src/client/views/nodes/DocumentView.tsx | 3 ++- src/client/views/nodes/PDFBox.tsx | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 51e29cb54..235bf5ae4 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -9,7 +9,7 @@ import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc"; import { Id } from '../../../new_fields/FieldSymbols'; import { FieldId } from "../../../new_fields/RefField"; import { listSpec } from "../../../new_fields/Schema"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { emptyFunction, returnTrue, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DocumentManager } from '../../util/DocumentManager'; @@ -429,7 +429,11 @@ export class DockedFrameRenderer extends React.Component { } nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth); - nativeHeight = () => NumCast(this._document!.nativeHeight, this._panelHeight); + nativeHeight = () => { + let nh = NumCast(this._document!.nativeHeight, this._panelHeight); + let res = BoolCast(this._document!.ignoreAspect) ? this._panelHeight : nh; + return res; + } contentScaling = () => { const nativeH = this.nativeHeight(); const nativeW = this.nativeWidth(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6fe01963a..583fa3e1a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -452,8 +452,9 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeHeight = this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + let ph = this.props.PanelHeight(); + var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
(PdfDocumen loaded = (nw: number, nh: number) => { if (this.props.Document) { + if (this.props.Document.nativeWidth && this.props.Document.nativeHeight) return; let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1); doc.nativeWidth = nw; - doc.nativeHeight = nh; + if (doc.nativeHeight) doc.nativeHeight = nw * oldaspect; + else doc.nativeHeight = nh; let ccv = this.props.ContainingCollectionView; if (ccv) { ccv.props.Document.pdfHeight = nh; -- cgit v1.2.3-70-g09d2 From f6e8b7a0f8a13ddf059cf701e46b8cbb8d9228f7 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 17:27:49 -0400 Subject: added page fwd/back/goto --- src/client/views/collections/CollectionPDFView.tsx | 48 ++++++----------- src/client/views/nodes/DocumentView.tsx | 1 - src/client/views/nodes/FieldView.tsx | 2 + src/client/views/nodes/PDFBox.tsx | 62 ++++++++++++++++------ src/client/views/pdf/PDFViewer.tsx | 6 +-- src/client/views/pdf/Page.tsx | 12 ++--- 6 files changed, 72 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 4af89d780..b62d3f7bb 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,19 +1,21 @@ -import { action, observable, IReactionDisposer, reaction } from "mobx"; +import { action, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; +import { WidthSym } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { NumCast } from "../../../new_fields/Types"; +import { emptyFunction } from "../../../Utils"; import { ContextMenu } from "../ContextMenu"; +import { FieldView, FieldViewProps } from "../nodes/FieldView"; +import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView"; +import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import "./CollectionPDFView.scss"; import React = require("react"); -import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView"; -import { emptyFunction } from "../../../Utils"; -import { NumCast } from "../../../new_fields/Types"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { HeightSym, WidthSym } from "../../../new_fields/Doc"; +import { PDFBox } from "../nodes/PDFBox"; @observer export class CollectionPDFView extends React.Component { + private _pdfBox?: PDFBox; private _reactionDisposer?: IReactionDisposer; private _buttonTray: React.RefObject; @@ -46,31 +48,12 @@ export class CollectionPDFView extends React.Component { } @observable _inThumb = false; - private set curPage(value: number) { this.props.Document.curPage = value; } + private set curPage(value: number) { this._pdfBox && this._pdfBox.GotoPage(value); } private get curPage() { return NumCast(this.props.Document.curPage, -1); } private get numPages() { return NumCast(this.props.Document.numPages); } - @action onPageBack = () => this.curPage > 1 ? (this.props.Document.curPage = this.curPage - 1) : -1; - @action onPageForward = () => this.curPage < this.numPages ? (this.props.Document.curPage = this.curPage + 1) : -1; + @action onPageBack = () => this._pdfBox && this._pdfBox.BackPage(); + @action onPageForward = () => this._pdfBox && this._pdfBox.ForwardPage(); - @action - onThumbDown = (e: React.PointerEvent) => { - document.addEventListener("pointermove", this.onThumbMove, false); - document.addEventListener("pointerup", this.onThumbUp, false); - e.stopPropagation(); - this._inThumb = true; - } - @action - onThumbMove = (e: PointerEvent) => { - let pso = (e.clientY - (e as any).target.parentElement.getBoundingClientRect().top) / (e as any).target.parentElement.getBoundingClientRect().height; - this.curPage = Math.trunc(Math.min(this.numPages, pso * this.numPages + 1)); - e.stopPropagation(); - } - @action - onThumbUp = (e: PointerEvent) => { - this._inThumb = false; - document.removeEventListener("pointermove", this.onThumbMove); - document.removeEventListener("pointerup", this.onThumbUp); - } nativeWidth = () => NumCast(this.props.Document.nativeWidth); nativeHeight = () => NumCast(this.props.Document.nativeHeight); private get uIButtons() { @@ -92,11 +75,14 @@ export class CollectionPDFView extends React.Component { } } + setPdfBox = (pdfBox: PDFBox) => { this._pdfBox = pdfBox; }; + + private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; return ( <> - + {renderProps.active() ? this.uIButtons : (null)} ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 583fa3e1a..0d5df550a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -453,7 +453,6 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - let ph = this.props.PanelHeight(); var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
number; PanelHeight: () => number; setVideoBox?: (player: VideoBox) => void; + setPdfBox?: (player: PDFBox) => void; } @observer diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index b9ccd79e4..243982a3b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,25 +1,22 @@ -import * as htmlToImage from "html-to-image"; -import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; +import { action, IReactionDisposer, observable, reaction, trace, untracked } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; -import Measure from "react-measure"; +import { WidthSym } from "../../../new_fields/Doc"; +import { makeInterface } from "../../../new_fields/Schema"; +import { Cast, NumCast } from "../../../new_fields/Types"; +import { PdfField } from "../../../new_fields/URLField"; //@ts-ignore // import { Document, Page } from "react-pdf"; // import 'react-pdf/dist/Page/AnnotationLayer.css'; import { RouteStore } from "../../../server/RouteStore"; import { DocComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; +import { PDFViewer } from "../pdf/PDFViewer"; import { positionSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; -import { makeInterface } from "../../../new_fields/Schema"; -import { PDFViewer } from "../pdf/PDFViewer"; -import { PdfField } from "../../../new_fields/URLField"; -import { HeightSym, WidthSym } from "../../../new_fields/Doc"; -import { CollectionStackingView } from "../collections/CollectionStackingView"; type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -31,19 +28,50 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _alt = false; @observable private _scrollY: number = 0; private _reactionDisposer?: IReactionDisposer; - _targetDiv: any = undefined; - componentDidMount: () => void = () => { + componentDidMount() { + if (this.props.setPdfBox) this.props.setPdfBox(this); + } + + public GetPage() { + return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1; + } + public BackPage() { + let cp = Math.ceil(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1; + cp = cp - 1; + if (cp > 0) { + this.props.Document.curPage = cp; + this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight); + } + } + public GotoPage(p: number) { + if (p > 0 && p <= NumCast(this.props.Document.numPages)) { + this.props.Document.curPage = p; + this.props.Document.scrollY = (p - 1) * NumCast(this.Document.pdfHeight); + } + } + + public ForwardPage() { + let cp = this.GetPage() + 1; + if (cp <= NumCast(this.props.Document.numPages)) { + this.props.Document.curPage = cp; + this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight); + } + } + + createRef = (ele: HTMLDivElement | null) => { if (this._reactionDisposer) this._reactionDisposer(); - this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => - this._targetDiv && this._targetDiv.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }) - ); + this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => { + ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }); + }); } - loaded = (nw: number, nh: number) => { + loaded = (nw: number, nh: number, np: number) => { if (this.props.Document) { - if (this.props.Document.nativeWidth && this.props.Document.nativeHeight) return; let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + console.log("pages = " + np); + doc.numPages = np; + if (doc.nativeWidth && doc.nativeHeight) return; let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1); doc.nativeWidth = nw; if (doc.nativeHeight) doc.nativeHeight = nw * oldaspect; @@ -59,7 +87,6 @@ export class PDFBox extends DocComponent(PdfDocumen @action onScroll = (e: React.UIEvent) => { if (e.currentTarget) { - this._targetDiv = e.currentTarget; this._scrollY = e.currentTarget.scrollTop; // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); let ccv = this.props.ContainingCollectionView; @@ -82,6 +109,7 @@ export class PDFBox extends DocComponent(PdfDocumen overflowY: "scroll", overflowX: "hidden", marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px` }} + ref={this.createRef} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}> {/*
*/} diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 17f65c7a6..dee891ba6 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -23,7 +23,7 @@ import { Dictionary } from "typescript-collections"; interface IPDFViewerProps { url: string; - loaded: (nw: number, nh: number) => void; + loaded: (nw: number, nh: number, np: number) => void; scrollY: number; parent: PDFBox; } @@ -61,7 +61,7 @@ export class PDFViewer extends React.Component { interface IViewerProps { pdf: Opt; - loaded: (nw: number, nh: number) => void; + loaded: (nw: number, nh: number, np: number) => void; scrollY: number; parent: PDFBox; mainCont: React.RefObject; @@ -400,7 +400,7 @@ class Viewer extends React.Component { return; } let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.props.loaded(page.width, page.height); + this.props.loaded(page.width, page.height, numPages); this._pageSizes[index - 1] = { width: page.width, height: page.height }; this._pagesLoaded++; if (this._pagesLoaded === numPages) { diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 44c502a04..e3dbeaebe 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -132,16 +132,14 @@ export default class Page extends React.Component { } } - highlight = (targetDoc: Doc | undefined) => { + highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); - let targetAnnotations = DocListCast(this.props.parent.Document.annotations); - if (targetAnnotations) { + let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); + if (targetAnnotations === undefined) { + Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); + } else { targetAnnotations.push(annotationDoc); - this.props.parent.Document.annotations = new List(targetAnnotations); - } - else { - this.props.parent.Document.annotations = new List([annotationDoc]); } return annotationDoc; } -- cgit v1.2.3-70-g09d2 From b987e2edbf7befbe90fafbdee476ee3b6513cc50 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 17:44:17 -0400 Subject: pinning things --- src/client/views/MainView.tsx | 3 +- src/client/views/pdf/PDFMenu.scss | 13 ++++- src/client/views/pdf/PDFMenu.tsx | 119 +++++++++++++++++++++++++++++++++++--- src/client/views/pdf/Page.tsx | 21 ++++--- 4 files changed, 137 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 384cd2860..29015995f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; @@ -92,6 +92,7 @@ export class MainView extends React.Component { library.add(faMusic); library.add(faTree); library.add(faCommentAlt); + library.add(faThumbtack); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/pdf/PDFMenu.scss b/src/client/views/pdf/PDFMenu.scss index b84ebc12d..22868082a 100644 --- a/src/client/views/pdf/PDFMenu.scss +++ b/src/client/views/pdf/PDFMenu.scss @@ -1,18 +1,25 @@ .pdfMenu-cont { position: absolute; z-index: 10000; - width: 100px; - height: 30px; + height: 35px; background: #323232; box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); - border-radius: 0px 4px 4px 4px; + border-radius: 0px 6px 6px 6px; overflow: hidden; + display: flex; .pdfMenu-button { background-color: transparent; + width: 35px; + height: 35px; } .pdfMenu-button:hover { background-color: #121212; } + + .pdfMenu-dragger { + height: 100%; + transition: width .2s; + } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index a0230113b..d2a20fb6e 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -1,6 +1,6 @@ import React = require("react"); import "./PDFMenu.scss"; -import { observable } from "mobx"; +import { observable, action } from "mobx"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { emptyFunction } from "../../../Utils"; @@ -10,10 +10,18 @@ import { Doc } from "../../../new_fields/Doc"; export default class PDFMenu extends React.Component { static Instance: PDFMenu; - @observable Top: number = 0; - @observable Left: number = 0; + @observable private _top: number = 0; + @observable private _left: number = 0; + @observable private _opacity: number = 1; + @observable private _transition: string = "opacity 0.5s"; + @observable private _transitionDelay: string = ""; + @observable private _pinned: boolean = false; + StartDrag: (e: PointerEvent) => void = emptyFunction; Highlight: (d: Doc | undefined) => void = emptyFunction; + @observable Highlighting: boolean = false; + + private _timeout: NodeJS.Timeout | undefined; constructor(props: Readonly<{}>) { super(props); @@ -38,12 +46,109 @@ export default class PDFMenu extends React.Component { e.preventDefault(); } + @action + jumpTo = (x: number, y: number) => { + if (!this._pinned) { + this._transition = this._transitionDelay = ""; + this._opacity = 1; + this._left = x; + this._top = y; + } + } + + @action + fadeOut = (forceOut: boolean) => { + if (!this._pinned) { + if (this._opacity === 0.2) { + this._transition = "opacity 0.1s"; + this._transitionDelay = ""; + this._opacity = 0; + this._left = this._top = -300; + } + + if (forceOut) { + this._transition = ""; + this._transitionDelay = ""; + this._opacity = 0; + this._left = this._top = -300; + } + } + } + + @action + pointerLeave = (e: React.PointerEvent) => { + if (!this._pinned) { + this._transition = "opacity 0.5s"; + this._transitionDelay = "1s"; + this._opacity = 0.2; + setTimeout(() => this.fadeOut(false), 3000); + } + } + + @action + pointerEntered = (e: React.PointerEvent) => { + this._transition = "opacity 0.1s"; + this._transitionDelay = ""; + this._opacity = 1; + } + + @action + togglePin = (e: React.MouseEvent) => { + this._pinned = !this._pinned; + } + + @action + dragging = (e: PointerEvent) => { + this._left += e.movementX; + this._top += e.movementY; + + e.stopPropagation(); + e.preventDefault(); + } + + dragEnd = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.dragging); + document.removeEventListener("pointerup", this.dragEnd); + e.stopPropagation(); + e.preventDefault(); + } + + dragStart = (e: React.PointerEvent) => { + document.removeEventListener("pointermove", this.dragging); + document.addEventListener("pointermove", this.dragging); + document.removeEventListener("pointerup", this.dragEnd); + document.addEventListener("pointerup", this.dragEnd); + + e.stopPropagation(); + e.preventDefault(); + } + + @action + highlightClicked = (e: React.MouseEvent) => { + if (!this._pinned) { + this.Highlight(undefined); + } + else { + this.Highlighting = !this.Highlighting; + this.Highlight(undefined); + } + } + render() { return ( -
- - -
+
+ + + +
+
) } } \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 44c502a04..3bc3ac33c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -132,6 +132,7 @@ export default class Page extends React.Component { } } + @action highlight = (targetDoc: Doc | undefined) => { // creates annotation documents for current highlights let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); @@ -196,6 +197,7 @@ export default class Page extends React.Component { // document.addEventListener("pointerup", this.endDrag); } else if (e.button === 0) { + PDFMenu.Instance.fadeOut(true); let target: any = e.target; if (target && target.parentElement === this._textLayer.current) { e.stopPropagation(); @@ -304,25 +306,28 @@ export default class Page extends React.Component { } copy.className = this._marquee.current.className; this.props.createAnnotation(copy, this.props.page); - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; this._marquee.current.style.opacity = "0"; } this._marqueeHeight = this._marqueeWidth = 0; - PDFMenu.Instance.Left = e.clientX; - PDFMenu.Instance.Top = e.clientY; + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } else { let sel = window.getSelection(); if (sel && sel.type === "Range") { - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; this.createTextAnnotation(sel); - PDFMenu.Instance.Left = e.clientX; - PDFMenu.Instance.Top = e.clientY; + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } } + + + if (PDFMenu.Instance.Highlighting) { + this.highlight(undefined); + } + else { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; + } // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); // if (this._marqueeing) { -- cgit v1.2.3-70-g09d2 From 747fdb6d26afabad067f3f8d98789bfc7ca44f8d Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 18:11:41 -0400 Subject: small stuffs --- src/client/views/pdf/PDFMenu.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 13 +++++++------ src/client/views/pdf/Page.tsx | 13 ++++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index d2a20fb6e..2ba875e42 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -95,6 +95,7 @@ export default class PDFMenu extends React.Component { @action togglePin = (e: React.MouseEvent) => { this._pinned = !this._pinned; + this.Highlighting = this._pinned === false; } @action diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index dee891ba6..55d15893a 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -21,6 +21,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { DragManager } from "../../util/DragManager"; import { Dictionary } from "typescript-collections"; +export const scale = 2; interface IPDFViewerProps { url: string; loaded: (nw: number, nh: number, np: number) => void; @@ -156,10 +157,10 @@ class Viewer extends React.Component { this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { let annoDoc = new Doc(); - 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); + if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale; + if (anno.style.top) annoDoc.y = parseInt(anno.style.top) / scale; + 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; annoDoc.target = sourceDoc; annoDoc.type = AnnotationTypes.Region; @@ -572,7 +573,7 @@ class PinAnnotation extends React.Component {
{ render() { return (
+ 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._backgroundColor }}>
); } } \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 455e1d831..bd2cae749 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -13,9 +13,10 @@ import { emptyFunction } from "../../../Utils"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; -import { AnnotationTypes } from "./PDFViewer"; +import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer"; import PDFMenu from "./PDFMenu"; + interface IPageProps { pdf: Opt; name: string; @@ -28,7 +29,7 @@ interface IPageProps { sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; createAnnotation: (div: HTMLDivElement, page: number) => void; - makeAnnotationDocuments: (doc: Doc | undefined) => Doc; + makeAnnotationDocuments: (doc: Doc | undefined, scale: number) => Doc; } @observer @@ -103,7 +104,6 @@ export default class Page extends React.Component { @action private renderPage = (page: Pdfjs.PDFPageProxy): void => { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes - let scale = 2; let viewport = page.getViewport(scale); let canvas = this._canvas.current; let textLayer = this._textLayer.current; @@ -135,7 +135,7 @@ export default class Page extends React.Component { @action highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale); let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (targetAnnotations === undefined) { Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); @@ -307,8 +307,11 @@ export default class Page extends React.Component { this._marquee.current.style.opacity = "0"; } + if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); + } + this._marqueeHeight = this._marqueeWidth = 0; - PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } else { let sel = window.getSelection(); -- cgit v1.2.3-70-g09d2 From d9fba98b4022d2be68f67d830f17fea79580d2c4 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 18:13:18 -0400 Subject: small --- src/client/views/pdf/PDFMenu.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 2ba875e42..b0735f63b 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -95,7 +95,9 @@ export default class PDFMenu extends React.Component { @action togglePin = (e: React.MouseEvent) => { this._pinned = !this._pinned; - this.Highlighting = this._pinned === false; + if (!this._pinned) { + this.Highlighting = false; + } } @action -- cgit v1.2.3-70-g09d2 From 5a6a066490c09dfe21fbdacbe331de9bbe622173 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 14 Jun 2019 22:01:00 -0400 Subject: fixed hyperlink following in formattedtextbox's --- src/client/views/nodes/FormattedTextBox.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e5a43c60a..3c590bd82 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -30,6 +30,7 @@ import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); +import { DocumentManager } from '../../util/DocumentManager'; library.add(faEdit); library.add(faSmile); @@ -241,6 +242,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (href) { if (href.indexOf(DocServer.prepend("/doc/")) === 0) { this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0]; + if (this._linkClicked) { + DocServer.GetRefField(this._linkClicked).then(f => { + (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab")); + }); + e.stopPropagation(); + e.preventDefault(); + } } else { let webDoc = Docs.WebDocument(href, { x: NumCast(this.props.Document.x, 0) + NumCast(this.props.Document.width, 0), y: NumCast(this.props.Document.y) }); this.props.addDocument && this.props.addDocument(webDoc); @@ -283,6 +291,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe onClick = (e: React.MouseEvent): void => { this._proseRef!.focus(); if (this._linkClicked) { + this._linkClicked = ""; e.preventDefault(); e.stopPropagation(); } -- cgit v1.2.3-70-g09d2 From 96f30f9beda568cf4d8687d07cbdd5467ab05b1b Mon Sep 17 00:00:00 2001 From: yipstanley Date: Sun, 16 Jun 2019 19:08:08 -0400 Subject: better virtualization --- package.json | 1 + src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 47 ++++++++------ src/server/index.ts | 122 ++++++++++++++++++++++++++++--------- 4 files changed, 122 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/package.json b/package.json index 0fe26f16d..7fd6c4ba9 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,7 @@ "passport": "^0.4.0", "passport-local": "^1.0.0", "pdfjs-dist": "^2.0.943", + "probe-image-size": "^4.0.0", "prosemirror-commands": "^1.0.7", "prosemirror-example-setup": "^1.0.1", "prosemirror-history": "^1.0.4", diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 243982a3b..655c12ab3 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -111,7 +111,7 @@ export class PDFBox extends DocComponent(PdfDocumen }} ref={this.createRef} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}> - + {/*
*/}
); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 55d15893a..75c298f55 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -20,6 +20,9 @@ import { emptyFunction, returnTrue, returnFalse } from "../../../Utils"; import { DocumentView } from "../nodes/DocumentView"; import { DragManager } from "../../util/DragManager"; import { Dictionary } from "typescript-collections"; +import * as rp from "request-promise"; +import { restProperty } from "babel-types"; +import { DocServer } from "../../DocServer"; export const scale = 2; interface IPDFViewerProps { @@ -137,7 +140,8 @@ class Viewer extends React.Component { } setTimeout(() => { - this.renderPages(this.startIndex, this.endIndex, true); + // this.renderPages(this.startIndex, this.endIndex, true); + this.saveThumbnail(); }, 1000); } @@ -204,17 +208,19 @@ class Viewer extends React.Component { } @action - saveThumbnail = () => { + saveThumbnail = async () => { // file address of the pdf const address: string = this.props.url; for (let i = 0; i < this._visibleElements.length; i++) { if (this._isPage[i]) { // change the address to be the file address of the PNG version of each page - let thisAddress = `${address.substring(0, address.length - ".pdf".length)}-${i + 1}.PNG`; - let nWidth = this._pageSizes[i].width; - let nHeight = this._pageSizes[i].height; + let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${i + 1}.PNG`))); + let thisAddress = res.path; + let nWidth = parseInt(res.width); + let nHeight = parseInt(res.height); // replace page with image - this._visibleElements[i] = ; + runInAction(() => + this._visibleElements[i] = ); } } } @@ -236,6 +242,7 @@ class Viewer extends React.Component { if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) { this._pdf = this.props.pdf; // render pages if the scorll position changes + console.log(`START: ${this.startIndex}, END: ${this.endIndex}`); this.renderPages(this.startIndex, this.endIndex); } } @@ -269,7 +276,7 @@ class Viewer extends React.Component { // this is only for an initial render to get all of the pages rendered if (this._visibleElements.length !== numPages) { - let divs = Array.from(Array(numPages).keys()).map(i => ( + let divs = Array.from(Array(numPages).keys()).map(i => i < 5 ? ( { makeAnnotationDocuments={this.makeAnnotationDocument} receiveAnnotations={this.sendAnnotations} {...this.props} /> - )); - let arr = Array.from(Array(numPages).keys()).map(() => false); + ) : + (
) + ); + let arr = Array.from(Array(numPages).keys()).map(i => i < 5); this._visibleElements.push(...divs); this._isPage.push(...arr); } @@ -378,16 +387,16 @@ class Viewer extends React.Component { // get the page index that the vertical offset passed in is on getIndex = (vOffset: number) => { - if (this._loaded) { - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - let index = 0; - let currOffset = vOffset; - while (index < numPages && currOffset - this._pageSizes[index].height > 0) { - currOffset -= this._pageSizes[index].height; - index++; - } - return index; - } + // if (this._loaded) { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + let index = 0; + let currOffset = vOffset; + while (index < numPages && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { + currOffset -= this._pageSizes[index].height; + index++; + } + return index; + // } return 0; } diff --git a/src/server/index.ts b/src/server/index.ts index e22794df1..3e51eb4ff 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -38,8 +38,10 @@ import c = require("crypto"); import { Search } from './Search'; import { debug } from 'util'; import _ = require('lodash'); +import { Response } from 'express-serve-static-core'; const MongoStore = require('connect-mongo')(session); const mongoose = require('mongoose'); +const probe = require("probe-image-size"); const download = (url: string, dest: fs.PathLike) => request.get(url).pipe(fs.createWriteStream(dest)); @@ -134,6 +136,66 @@ app.get("/search", async (req, res) => { res.send(results); }); +app.get("/thumbnail/:filename", (req, res) => { + let filename = req.params.filename; + let noExt = filename.substring(0, filename.length - ".png".length); + let pagenumber = parseInt(noExt[noExt.length - 1]); + fs.exists(uploadDir + filename, (exists: boolean) => { + console.log(`${uploadDir + filename} ${exists ? "exists" : "does not exist"}`); + if (exists) { + let input = fs.createReadStream(uploadDir + filename); + probe(input, (err: any, result: any) => { + if (err) { + console.log(err); + return; + } + console.log(result.width); + console.log(result.height); + res.send({ path: "/files/" + filename, width: result.width, height: result.height }); + }); + } + else { + LoadPage(uploadDir + filename.substring(0, filename.length - "-n.png".length) + ".pdf", pagenumber, res); + } + }); +}); + +function LoadPage(file: string, pageNumber: number, res: Response) { + console.log(file); + Pdfjs.getDocument(file).promise + .then((pdf: Pdfjs.PDFDocumentProxy) => { + let factory = new NodeCanvasFactory(); + console.log(pageNumber); + pdf.getPage(pageNumber).then((page: Pdfjs.PDFPageProxy) => { + console.log("reading " + page); + let viewport = page.getViewport(1); + let canvasAndContext = factory.create(viewport.width, viewport.height); + let renderContext = { + canvasContext: canvasAndContext.context, + viewport: viewport, + canvasFactory: factory + } + console.log("read " + pageNumber); + + page.render(renderContext).promise + .then(() => { + console.log("saving " + pageNumber); + let stream = canvasAndContext.canvas.createPNGStream(); + let pngFile = `${file.substring(0, file.length - ".pdf".length)}-${pageNumber}.PNG`; + let out = fs.createWriteStream(pngFile); + stream.pipe(out); + out.on("finish", () => { + console.log(`Success! Saved to ${pngFile}`); + let name = path.basename(pngFile); + res.send({ path: "/files/" + name, width: viewport.width, height: viewport.height }); + }); + }, (reason: string) => { + console.error(reason + ` ${pageNumber}`); + }); + }); + }); +} + // anyone attempting to navigate to localhost at this port will // first have to login addSecureRoute( @@ -229,36 +291,36 @@ app.post( isImage = true; } else if (pdfTypes.includes(ext)) { - Pdfjs.getDocument(uploadDir + file).promise - .then((pdf: Pdfjs.PDFDocumentProxy) => { - let numPages = pdf.numPages; - let factory = new NodeCanvasFactory(); - for (let pageNum = 0; pageNum < numPages; pageNum++) { - console.log(pageNum); - pdf.getPage(pageNum + 1).then((page: Pdfjs.PDFPageProxy) => { - console.log("reading " + pageNum); - let viewport = page.getViewport(1); - let canvasAndContext = factory.create(viewport.width, viewport.height); - let renderContext = { - canvasContext: canvasAndContext.context, - viewport: viewport, - canvasFactory: factory - } - console.log("read " + pageNum); - - page.render(renderContext).promise - .then(() => { - console.log("saving " + pageNum); - let stream = canvasAndContext.canvas.createPNGStream(); - let out = fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`); - stream.pipe(out); - out.on("finish", () => console.log(`Success! Saved to ${uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`}`)); - }, (reason: string) => { - console.error(reason + ` ${pageNum}`); - }); - }); - } - }); + // Pdfjs.getDocument(uploadDir + file).promise + // .then((pdf: Pdfjs.PDFDocumentProxy) => { + // let numPages = pdf.numPages; + // let factory = new NodeCanvasFactory(); + // for (let pageNum = 0; pageNum < numPages; pageNum++) { + // console.log(pageNum); + // pdf.getPage(pageNum + 1).then((page: Pdfjs.PDFPageProxy) => { + // console.log("reading " + pageNum); + // let viewport = page.getViewport(1); + // let canvasAndContext = factory.create(viewport.width, viewport.height); + // let renderContext = { + // canvasContext: canvasAndContext.context, + // viewport: viewport, + // canvasFactory: factory + // } + // console.log("read " + pageNum); + + // page.render(renderContext).promise + // .then(() => { + // console.log("saving " + pageNum); + // let stream = canvasAndContext.canvas.createPNGStream(); + // let out = fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`); + // stream.pipe(out); + // out.on("finish", () => console.log(`Success! Saved to ${uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`}`)); + // }, (reason: string) => { + // console.error(reason + ` ${pageNum}`); + // }); + // }); + // } + // }); } if (isImage) { resizers.forEach(resizer => { -- cgit v1.2.3-70-g09d2 From e1f88e962cce91d9bcd6900163756934bc5a09a8 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Sun, 16 Jun 2019 20:12:33 -0400 Subject: fix --- src/server/index.ts | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/server/index.ts b/src/server/index.ts index 3e51eb4ff..b91c91282 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -149,8 +149,6 @@ app.get("/thumbnail/:filename", (req, res) => { console.log(err); return; } - console.log(result.width); - console.log(result.height); res.send({ path: "/files/" + filename, width: result.width, height: result.height }); }); } -- cgit v1.2.3-70-g09d2 From ee1e6f815b76f9c0e076c8fecc89dd4fa1f5cf4b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 16 Jun 2019 21:39:48 -0400 Subject: fixed access out-of-bounds error.. fixed event handling for unselected pdf. --- src/client/views/nodes/PDFBox.scss | 21 +++++++++++++-------- src/client/views/pdf/PDFViewer.tsx | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index bb1f534c6..8bcae4f1e 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -36,15 +36,14 @@ pointer-events: none; display: flex; flex-direction: row; - - span { - pointer-events: none !important; + .textlayer { + pointer-events: none; + span { + pointer-events: none !important; + } } -} -.textlayer { - span { - pointer-events: all !important; - user-select: text; + .page-cont { + pointer-events: none; } } @@ -52,6 +51,12 @@ pointer-events: all; display: flex; flex-direction: row; + .textlayer { + span { + pointer-events: all !important; + user-select: text; + } + } } .pdfBox-contentContainer { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 75c298f55..bc7cfecbb 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -392,7 +392,7 @@ class Viewer extends React.Component { let index = 0; let currOffset = vOffset; while (index < numPages && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { - currOffset -= this._pageSizes[index].height; + currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height; index++; } return index; -- cgit v1.2.3-70-g09d2 From 0c2e2cfea58cb95fddb2dc344eff29a8b3dcf7fa Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 17 Jun 2019 10:28:38 -0400 Subject: small change --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8e3c682c7..250b6bf5d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -25,6 +25,7 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); +import PDFMenu from "../../pdf/PDFMenu"; export const panZoomSchema = createSchema({ panX: "number", @@ -148,6 +149,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let docs = this.childDocs || []; let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); if (!this.isAnnotationOverlay) { + PDFMenu.Instance.fadeOut(true); let minx = docs.length ? NumCast(docs[0].x) : 0; let maxx = docs.length ? NumCast(docs[0].width) / NumCast(docs[0].zoomBasis, 1) + minx : minx; let miny = docs.length ? NumCast(docs[0].y) : 0; -- cgit v1.2.3-70-g09d2 From 90c14f3ad8d9e48698c516bc6549143912e19236 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 17 Jun 2019 10:47:40 -0400 Subject: highlights are a different color --- src/client/views/pdf/PDFViewer.tsx | 5 +++-- src/client/views/pdf/Page.tsx | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index bc7cfecbb..9becfb419 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -156,7 +156,7 @@ class Viewer extends React.Component { } } - makeAnnotationDocument = (sourceDoc: Doc | undefined): Doc => { + makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => { let annoDocs: Doc[] = []; this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { @@ -167,6 +167,7 @@ class Viewer extends React.Component { if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale; annoDoc.page = key; annoDoc.target = sourceDoc; + annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); anno.remove(); @@ -624,7 +625,7 @@ class RegionAnnotation extends React.Component { render() { return (
+ style={{ top: this.props.y * scale, left: this.props.x * scale, width: this.props.width * scale, height: this.props.height * scale, pointerEvents: "all", backgroundColor: StrCast(this.props.document.color) }}>
); } } \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index bd2cae749..e706a0d5c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -29,7 +29,7 @@ interface IPageProps { sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; createAnnotation: (div: HTMLDivElement, page: number) => void; - makeAnnotationDocuments: (doc: Doc | undefined, scale: number) => Doc; + makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string) => Doc; } @observer @@ -135,7 +135,7 @@ export default class Page extends React.Component { @action highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, "#f4f442"); let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (targetAnnotations === undefined) { Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); -- cgit v1.2.3-70-g09d2 From 122607408882617235af255af84ce78828b7982f Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 17 Jun 2019 13:42:21 -0400 Subject: page sizes loaded --- src/client/views/pdf/PDFMenu.tsx | 6 +++--- src/client/views/pdf/PDFViewer.tsx | 30 +++++++++++++++++++++++++++--- src/client/views/pdf/Page.tsx | 8 ++++---- 3 files changed, 34 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index b0735f63b..b44370e3d 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -18,7 +18,7 @@ export default class PDFMenu extends React.Component { @observable private _pinned: boolean = false; StartDrag: (e: PointerEvent) => void = emptyFunction; - Highlight: (d: Doc | undefined) => void = emptyFunction; + Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction; @observable Highlighting: boolean = false; private _timeout: NodeJS.Timeout | undefined; @@ -129,11 +129,11 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { if (!this._pinned) { - this.Highlight(undefined); + this.Highlight(undefined, "#f4f442"); } else { this.Highlighting = !this.Highlighting; - this.Highlight(undefined); + this.Highlight(undefined, "#f4f442"); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 9becfb419..d74a16f3f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -23,6 +23,7 @@ import { Dictionary } from "typescript-collections"; import * as rp from "request-promise"; import { restProperty } from "babel-types"; import { DocServer } from "../../DocServer"; +import { number } from "prop-types"; export const scale = 2; interface IPDFViewerProps { @@ -141,10 +142,33 @@ class Viewer extends React.Component { setTimeout(() => { // this.renderPages(this.startIndex, this.endIndex, true); - this.saveThumbnail(); + this.initialLoad(); }, 1000); } + @action + initialLoad = () => { + let pdf = this.props.pdf; + if (pdf) { + this._pageSizes = Array<{ width: number, height: number }>(pdf.numPages); + let rendered = 0; + for (let i = 0; i < pdf.numPages; i++) { + pdf.getPage(i + 1).then( + (page: Pdfjs.PDFPageProxy) => { + runInAction(() => { + this._pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }; + }); + console.log(`page ${i} size retreieved`); + rendered++; + if (rendered === pdf!.numPages - 1) { + this.saveThumbnail(); + } + } + ); + } + } + } + private mainCont = (div: HTMLDivElement | null) => { if (this._dropDisposer) { this._dropDisposer(); @@ -186,7 +210,7 @@ class Viewer extends React.Component { drop = async (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.LinkDragData) { let sourceDoc = de.data.linkSourceDocument; - let destDoc = this.makeAnnotationDocument(sourceDoc); + let destDoc = this.makeAnnotationDocument(sourceDoc, 1, "red"); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(destDoc); @@ -392,7 +416,7 @@ class Viewer extends React.Component { let numPages = this.props.pdf ? this.props.pdf.numPages : 0; let index = 0; let currOffset = vOffset; - while (index < numPages && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { + while (index < this._pageSizes.length && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height; index++; } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index e706a0d5c..bb87ec9d4 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -133,9 +133,9 @@ export default class Page extends React.Component { } @action - highlight = (targetDoc?: Doc) => { + highlight = (targetDoc?: Doc, color: string = "red") => { // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, "#f4f442"); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color); let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (targetAnnotations === undefined) { Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); @@ -162,7 +162,7 @@ export default class Page extends React.Component { // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; - let annotationDoc = this.highlight(targetDoc); + let annotationDoc = this.highlight(targetDoc, "red"); // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); if (this._textLayer.current) { @@ -323,7 +323,7 @@ export default class Page extends React.Component { if (PDFMenu.Instance.Highlighting) { - this.highlight(undefined); + this.highlight(undefined, "#f4f442"); } else { PDFMenu.Instance.StartDrag = this.startDrag; -- cgit v1.2.3-70-g09d2