From 996729e7c71f753ee6439ffa9adec241e01a8d42 Mon Sep 17 00:00:00 2001
From: Audrey Hamelers <hamelers@ebi.ac.uk>
Date: Mon, 11 Mar 2019 11:37:31 +0000
Subject: [PATCH] PDF viewer improvements

---
 app/components/pdf-viewer/PDFViewer.jsx | 165 ++++++++++++------------
 app/components/pdf-viewer/Page.jsx      |  29 +++--
 2 files changed, 96 insertions(+), 98 deletions(-)

diff --git a/app/components/pdf-viewer/PDFViewer.jsx b/app/components/pdf-viewer/PDFViewer.jsx
index d045f12c1..60dda8731 100755
--- a/app/components/pdf-viewer/PDFViewer.jsx
+++ b/app/components/pdf-viewer/PDFViewer.jsx
@@ -1,11 +1,19 @@
 import React from 'react'
-import styled from 'styled-components'
+import styled, { createGlobalStyle } from 'styled-components'
 import { Icon, Button } from '@pubsweet/ui'
 import { th, darken, lighten } from '@pubsweet/ui-toolkit'
 import * as PDFJS from 'pdfjs-dist/webpack.js'
-import { A } from '../ui'
 import Viewer from './Viewer'
 
+const Limit = createGlobalStyle`
+  body {
+    overflow: hidden;
+    div {
+      position: relative !important;
+    }
+  }
+`
+
 const OuterContainer = styled.div`
   max-width: 100%;
   background-color: ${th('colorBackgroundHue')};
@@ -15,11 +23,12 @@ const OuterContainer = styled.div`
   margin: 0;
   overflow: hidden;
   &.full {
-    position: absolute;
+    position: fixed !important;
     top: 0;
     right: 0;
     left: 0;
-    z-index: 2;
+    z-index: 5;
+    height: 100vh;
   }
 `
 const Toolbar = styled.div`
@@ -57,29 +66,20 @@ const Info = styled.span`
   font-size: ${th('fontSizeBaseSmall')};
 `
 class PDFViewer extends React.Component {
-  constructor(props) {
-    super(props)
-    this.state = {
-      pdf: null,
-      text: [],
-      page: 1,
-      scale: 1,
-      screenSize: 'normal',
-    }
-    this.scaleToFit = this.scaleToFit.bind(this)
-    this.zoomIn = this.zoomIn.bind(this)
-    this.zoomOut = this.zoomOut.bind(this)
-    this.adjustScreen = this.adjustScreen.bind(this)
-    this.countPages = this.countPages.bind(this)
-    this.deliverText = this.deliverText.bind(this)
-    this.viewerRef = React.createRef()
+  state = {
+    pdf: null,
+    text: [],
+    page: 1,
+    scale: 1,
+    fullscreen: false,
   }
-  componentDidMount() {
-    PDFJS.getDocument(this.props.url).then(pdf => {
+  async componentDidMount() {
+    const pdf = await PDFJS.getDocument(this.props.url)
+    if (this.viewer) {
       this.setState({ pdf })
-    })
+    }
   }
-  scaleToFit() {
+  scaleToFit = () => {
     const { scale } = this.state
     const desiredWidth = this.viewerRef.current.offsetWidth
     const viewBoxWidth = this.viewerRef.current.getElementsByClassName(
@@ -89,7 +89,7 @@ class PDFViewer extends React.Component {
       parseInt((desiredWidth * scale) / viewBoxWidth / 0.25, 10) * 0.25
     this.setState({ scale: newScale })
   }
-  zoomIn() {
+  zoomIn = () => {
     const { scale } = this.state
     if (scale < 5) {
       if (scale >= 2) {
@@ -99,7 +99,7 @@ class PDFViewer extends React.Component {
       }
     }
   }
-  zoomOut() {
+  zoomOut = () => {
     const { scale } = this.state
     if (scale > 0.5) {
       if (scale >= 3) {
@@ -109,13 +109,12 @@ class PDFViewer extends React.Component {
       }
     }
   }
-  adjustScreen() {
-    const { screenSize } = this.state
-    this.setState({ screenSize: screenSize === 'normal' ? 'full' : 'normal' })
+  setRef = viewer => {
+    this.viewer = viewer
   }
-  countPages() {
-    const { top } = this.viewerRef.current.getBoundingClientRect()
-    const pages = this.viewerRef.current.getElementsByClassName('pdf-page')
+  countPages = () => {
+    const { top } = this.viewer.getBoundingClientRect()
+    const pages = this.viewer.getElementsByClassName('pdf-page')
     const topPages = Array.prototype.filter.call(
       pages,
       page => page.getBoundingClientRect().top <= top,
@@ -127,8 +126,8 @@ class PDFViewer extends React.Component {
       }
     }
   }
-  deliverText(pageText) {
-    if (this.props.textContent || this.props.loaded) {
+  deliverText = pageText => {
+    if (this.viewer && (this.props.textContent || this.props.loaded)) {
       this.setState({ text: [...this.state.text, pageText] }, () => {
         const { text, pdf } = this.state
         if (text.length === pdf._pdfInfo.numPages) {
@@ -144,58 +143,56 @@ class PDFViewer extends React.Component {
     }
   }
   render() {
-    const { pdf, scale, page, screenSize } = this.state
-    const { url } = this.props
+    const { pdf, scale, page, fullscreen } = this.state
     return (
-      <OuterContainer
-        className={`pdf-viewer ${screenSize}`}
-        data-pages={pdf && pdf._pdfInfo && pdf._pdfInfo.numPages}
-        ref={this.viewerRef}
-      >
-        <Toolbar>
-          {pdf && pdf._pdfInfo && (
-            <Info>
-              Page {page} of {pdf._pdfInfo.numPages}
-            </Info>
-          )}
-          <div>
-            <Zoom
-              onClick={this.scaleToFit}
-              style={{ padding: '0 4px', marginRight: '4px' }}
-            >
-              Scale to fit
-            </Zoom>
-            <Zoom onClick={this.zoomOut}>
-              <Icon size={2}>minus</Icon>
-            </Zoom>
-            <Zoom onClick={this.zoomIn}>
-              <Icon size={2}>plus</Icon>
-            </Zoom>
-            <Info>{scale * 100}%</Info>
-          </div>
-          <div>
-            <A href={url} target="_blank">
-              <Zoom>
-                <Icon size={2}>download</Icon>
+      <React.Fragment>
+        {fullscreen && <Limit />}
+        <OuterContainer
+          className={`pdf-viewer ${fullscreen && 'full'}`}
+          data-pages={pdf && pdf._pdfInfo && pdf._pdfInfo.numPages}
+          ref={this.setRef}
+        >
+          <Toolbar>
+            {pdf && pdf._pdfInfo && (
+              <Info>
+                Page {page} of {pdf._pdfInfo.numPages}
+              </Info>
+            )}
+            <div>
+              <Zoom
+                onClick={this.scaleToFit}
+                style={{ padding: '0 4px', marginRight: '4px' }}
+              >
+                Scale to fit
+              </Zoom>
+              <Zoom onClick={this.zoomOut}>
+                <Icon size={2}>minus</Icon>
+              </Zoom>
+              <Zoom onClick={this.zoomIn}>
+                <Icon size={2}>plus</Icon>
               </Zoom>
-            </A>
-            <Zoom onClick={this.adjustScreen} style={{ padding: '0 4px' }}>
-              {screenSize === 'normal' ? 'Full screen' : 'Exit full screen'}
-              <Icon size={2}>
-                {screenSize === 'normal' ? 'maximize-2' : 'minimize-2'}
-              </Icon>
-            </Zoom>
-          </div>
-        </Toolbar>
-        {pdf && (
-          <Viewer
-            onScroll={this.countPages}
-            pdf={pdf}
-            scale={scale}
-            textContent={this.deliverText}
-          />
-        )}
-      </OuterContainer>
+              <Info>{scale * 100}%</Info>
+            </div>
+            <div>
+              <Zoom
+                onClick={() => this.setState({ fullscreen: !fullscreen })}
+                style={{ padding: '0 4px' }}
+              >
+                {fullscreen ? 'Exit full screen' : 'Full screen'}
+                <Icon size={2}>{fullscreen ? 'minimize-2' : 'maximize-2'}</Icon>
+              </Zoom>
+            </div>
+          </Toolbar>
+          {pdf && (
+            <Viewer
+              onScroll={this.countPages}
+              pdf={pdf}
+              scale={scale}
+              textContent={this.deliverText}
+            />
+          )}
+        </OuterContainer>
+      </React.Fragment>
     )
   }
 }
diff --git a/app/components/pdf-viewer/Page.jsx b/app/components/pdf-viewer/Page.jsx
index 4f579a45f..92c50b32a 100755
--- a/app/components/pdf-viewer/Page.jsx
+++ b/app/components/pdf-viewer/Page.jsx
@@ -15,10 +15,13 @@ const PDFPage = styled.div`
     margin 0 auto;
     position: relative;
     & > * {
-      position: absolute;
+      position: absolute !important;
       top: 0;
       left: 0;
     }
+    .textLayer > div {
+      position: absolute !important;
+    }
   }
 `
 const Loading = styled.div`
@@ -33,7 +36,9 @@ class Page extends React.Component {
   }
   componentDidMount() {
     const { pdf } = this.props
-    this.update(pdf)
+    if (pdf) {
+      this.update(pdf)
+    }
   }
   shouldComponentUpdate(nextProps, nextState) {
     return (
@@ -49,21 +54,17 @@ class Page extends React.Component {
   update = pdf => {
     if (pdf) {
       this.loadPage(pdf)
-    } else {
-      this.setState({ status: 'loading' })
     }
   }
-  loadPage(pdf) {
+  async loadPage(pdf) {
     if (this.state.status === 'rendering' || this.state.page !== null) return
-    pdf.getPage(this.props.index).then(pdfPage => {
-      this.setState({ status: 'rendering' })
-      this.renderPage(pdfPage)
-      pdfPage.getTextContent().then(content => {
-        this.props.textContent({
-          page: this.props.index,
-          text: content.items.map(item => item.str).join(''),
-        })
-      })
+    const pdfPage = await pdf.getPage(this.props.index)
+    this.setState({ status: 'rendering' })
+    this.renderPage(pdfPage)
+    const content = await pdfPage.getTextContent()
+    this.props.textContent({
+      page: this.props.index,
+      text: content.items.map(item => item.str).join(''),
     })
   }
   renderPage(pdfPage) {
-- 
GitLab