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