Mercurial > hg > orthanc-stone
changeset 1652:fa9e6bf84958
integrating pdf.js into Stone Web viewer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 16 Nov 2020 20:47:53 +0100 |
parents | 00674f3695f2 |
children | 2e3b2ed239b9 |
files | Applications/StoneWebViewer/WebApplication/index.html Applications/StoneWebViewer/WebApplication/pdf-viewer.js Applications/StoneWebViewer/WebAssembly/CMakeLists.txt Applications/StoneWebViewer/WebAssembly/JavaScriptLibraries.cmake |
diffstat | 4 files changed, 250 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebApplication/index.html Fri Nov 13 18:29:17 2020 +0100 +++ b/Applications/StoneWebViewer/WebApplication/index.html Mon Nov 16 20:47:53 2020 +0100 @@ -586,6 +586,10 @@ <div v-if="status == 'waiting'" class="wvPaneOverlay"> [ drop a series here ] </div> + + <div v-if="status == 'pdf'" > + <pdf-viewer v-bind:prefix="canvasId + '-pdf'"></pdf-viewer> + </div> <!--div v-if="status == 'video'" class="wvPaneOverlay"> <video class="wvVideo" autoplay="" loop="" controls="" preload="auto" type="video/mp4" @@ -606,14 +610,45 @@ </script> + <script type="text/x-template" id="pdf-viewer"> + <div style="position:absolute; left:0; top:0; width:100%; height:100%;"> + <!-- "line-height: 0px" to fit height: https://stackoverflow.com/a/12616341/881731 --> + <div v-bind:id="prefix + '-container'" + style="position: absolute; left: 0; top: 0; width:100%;height:100%;overflow:auto;line-height: 0px;"> + <canvas v-bind:id="prefix + '-canvas'" + style="position: absolute; top:0px; left:0px;"></canvas> + </div> + + <div class="wv-overlay"> + <div class="wv-overlay-bottomleft wvPrintExclude"> + <button class="btn btn-primary" @click="FitWidth()"><i class="fas fa-text-width"></i></button> + <button class="btn btn-primary" @click="FitHeight()"><i class="fas fa-text-height"></i></button> + <button class="btn btn-primary" @click="ZoomIn()"><i class="fas fa-search-plus"></i></button> + <button class="btn btn-primary" @click="ZoomOut()"><i class="fas fa-search-minus"></i></button> + <button class="btn btn-primary" @click="PreviousPage()"> + <i class="fa fa-chevron-circle-left"></i> + </button> + {{currentPage}} / {{countPages}} + <button class="btn btn-primary" @click="NextPage()"> + <i class="fa fa-chevron-circle-right"></i> + </button> + </div> + </div> + </div> + </script> + + + <script src="js/jquery-3.4.1.min.js"></script> <script src="js/bootstrap.min.js"></script> <script src="js/vue.min.js"></script> <script src="js/axios.min.js"></script> + <script src="js/pdf.js"></script> <script src="ua-parser.js"></script> <script src="stone.js"></script> + <script src="pdf-viewer.js"></script> <!-- Must be before inclusion of "app.js" --> <script src="app.js"></script> <script src="print.js"></script> </body>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/StoneWebViewer/WebApplication/pdf-viewer.js Mon Nov 16 20:47:53 2020 +0100 @@ -0,0 +1,198 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + + + +// Loaded via <script> tag, create shortcut to access PDF.js exports. +var pdfjsLib = window['pdfjs-dist/build/pdf']; + +// The workerSrc property shall be specified. +pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.5.207/build/pdf.worker.min.js'; + + + +var ZOOM_FACTOR = 1.3; +var FIT_MARGIN = 10; + +Vue.component('pdf-viewer', { + props: [ 'prefix', 'pdf' ], // "pdf" must correspond to a "Uint8Array" + template: '#pdf-viewer', + data: function() { + return { + container: null, + canvas: null, + ctx: null, + + scale: 1, + countPages: 0, + currentPage: 0, + pdfDoc: null, + isRendering: false, + pageNumPending: null + } + }, + watch: { + pdf: function(newVal, oldVal) { + this.LoadPdf(); + } + }, + mounted: function() { + this.container = document.getElementById(this.prefix + '-container'); + this.canvas = document.getElementById(this.prefix + '-canvas'); + this.ctx = this.canvas.getContext('2d'); + + if (this.container === null || + this.canvas === null || + this.ctx === null) { + alert('Bad viewer configuration'); + } + + var that = this; + this.container.addEventListener('wheel', function(event) { + that.MouseWheel(event); + }); + }, + methods: { + NextPage: function() { + if (this.pdfDoc !== null && + this.currentPage < this.pdfDoc.numPages) { + this.QueueRenderPage(this.currentPage + 1); + } + }, + PreviousPage: function() { + if (this.pdfDoc !== null && + this.currentPage > 1) { + this.QueueRenderPage(this.currentPage - 1); + } + }, + FitWidth: function() { + if (this.pdfDoc !== null) { + var that = this; + this.pdfDoc.getPage(this.currentPage).then(function(page) { + // https://github.com/mozilla/pdf.js/issues/5628 + // https://stackoverflow.com/a/21064102/881731 + // https://stackoverflow.com/a/60008044/881731 + var scrollbarWidth = window.innerWidth - document.body.clientWidth + FIT_MARGIN; + that.scale = (that.container.offsetWidth - scrollbarWidth) / page.getViewport({ scale: 1.0 }).width; + that.QueueRenderPage(that.currentPage); + }); + } + }, + FitHeight: function() { + if (this.pdfDoc !== null) { + var that = this; + this.pdfDoc.getPage(this.currentPage).then(function(page) { + // The computation below assumes that "line-height: 0px" CSS + // on the parent element of the canvas. + + // https://github.com/mozilla/pdf.js/issues/5628 + var scrollbarHeight = window.innerHeight - document.body.clientHeight + FIT_MARGIN; + that.scale = (that.container.offsetHeight - scrollbarHeight) / page.getViewport({ scale: 1.0 }).height; + //that.scale = that.container.clientHeight / page.getViewport({ scale: 1.0 }).height; + that.QueueRenderPage(that.currentPage); + }); + } + }, + ZoomIn: function() { + this.scale *= ZOOM_FACTOR; + this.QueueRenderPage(this.currentPage); + }, + ZoomOut: function() { + this.scale /= ZOOM_FACTOR; + this.QueueRenderPage(this.currentPage); + }, + LoadPdf: function(pdf) { + var that = this; + pdfjsLib.getDocument(new Uint8Array(this.pdf)).promise.then(function(pdfDoc_) { + that.pdfDoc = pdfDoc_; + that.currentPage = 0; + that.countPages = pdfDoc_.numPages; + that.scale = 1; + that.isRendering = false; + that.pageNumPending = null; + + // Initial/first page rendering + that.RenderPage(1); + }); + }, + RenderPage: function(pageNum) { + var that = this; + + if (this.pdfDoc !== null && + pageNum >= 1 && + pageNum <= this.countPages) { + this.isRendering = true; + this.pdfDoc.getPage(pageNum).then(function(page) { + var viewport = page.getViewport({scale: that.scale}); + + that.canvas.height = viewport.height; + that.canvas.width = viewport.width; + + // Horizontal centering of the canvas. This requires CSS + // "position: relative" on the canvas element. + if (that.canvas.width < that.container.clientWidth) { + that.canvas.style.left = Math.floor((that.container.clientWidth - viewport.width) / 2) + 'px'; + } else { + that.canvas.style.left = '0px'; + } + + // Render PDF page into canvas context + var renderContext = { + canvasContext: that.ctx, + viewport: viewport + }; + + var renderTask = page.render(renderContext); + + // Wait for rendering to finish + renderTask.promise.then(function() { + that.isRendering = false; + that.currentPage = pageNum; + if (that.pageNumPending !== null) { + // New page rendering is pending + that.currentPage = that.pageNumPending; + that.pageNumPending = null; + that.RenderPage(); + } + }); + }); + } + }, + QueueRenderPage: function(pageNum) { + if (this.isRendering) { + this.pageNumPending = pageNum; + } else { + this.RenderPage(pageNum); + } + }, + MouseWheel: function(event) { + if (event.ctrlKey) { + if (event.deltaY < 0) { + this.ZoomIn(); + } else if (event.deltaY > 0) { + this.ZoomOut(); + } + + event.preventDefault(); + } + } + } +});
--- a/Applications/StoneWebViewer/WebAssembly/CMakeLists.txt Fri Nov 13 18:29:17 2020 +0100 +++ b/Applications/StoneWebViewer/WebAssembly/CMakeLists.txt Mon Nov 16 20:47:53 2020 +0100 @@ -160,6 +160,7 @@ ${CMAKE_SOURCE_DIR}/../WebApplication/app.css ${CMAKE_SOURCE_DIR}/../WebApplication/app.js ${CMAKE_SOURCE_DIR}/../WebApplication/index.html + ${CMAKE_SOURCE_DIR}/../WebApplication/pdf-viewer.js ${CMAKE_SOURCE_DIR}/../WebApplication/print.js ${CMAKE_SOURCE_DIR}/../WebApplication/ua-parser.js # TODO => Package this from https://github.com/faisalman/ua-parser-js/releases ${STONE_WRAPPER}
--- a/Applications/StoneWebViewer/WebAssembly/JavaScriptLibraries.cmake Fri Nov 13 18:29:17 2020 +0100 +++ b/Applications/StoneWebViewer/WebAssembly/JavaScriptLibraries.cmake Mon Nov 16 20:47:53 2020 +0100 @@ -43,6 +43,18 @@ "220afd743d9e9643852e31a135a9f3ae" "${BASE_URL}/jquery-3.4.1.min.js") +if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/pdfjs) + DownloadPackage( + "f2e0f7eacd8946bd3111a2d10dceaa72" + "${BASE_URL}/web-viewer/pdfjs-2.5.207-dist.zip" + "${CMAKE_CURRENT_BINARY_DIR}/build") + + # Reorganize the PDF.js package + file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/LICENSE) + file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/web) + file(RENAME ${CMAKE_CURRENT_BINARY_DIR}/build ${CMAKE_CURRENT_BINARY_DIR}/pdfjs) +endif() + install( FILES @@ -59,6 +71,10 @@ ${CMAKE_CURRENT_BINARY_DIR}/vue-2.6.10/dist/vue.min.js ${CMAKE_CURRENT_BINARY_DIR}/axios-0.19.0/dist/axios.min.js ${CMAKE_CURRENT_BINARY_DIR}/axios-0.19.0/dist/axios.min.map + ${CMAKE_CURRENT_BINARY_DIR}/pdfjs/pdf.js + ${CMAKE_CURRENT_BINARY_DIR}/pdfjs/pdf.js.map + ${CMAKE_CURRENT_BINARY_DIR}/pdfjs/pdf.worker.js + ${CMAKE_CURRENT_BINARY_DIR}/pdfjs/pdf.worker.js.map DESTINATION ${ORTHANC_STONE_INSTALL_PREFIX}/js )