UNPKG

lichen-annotate-pdf

Version:

Annotation layer for pdf.js in vue fork of Aaron Leong pdf-annotate-vue

298 lines (280 loc) 11.7 kB
import PDFJSAnnotate from '../PDFJSAnnotate'; import config from '../config'; import renderScreenReaderHints from '../a11y/renderScreenReaderHints'; import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer.js'; // import { DefaultTextLayerFactory } from 'pdfjs-dist/web/pdf_viewer.js'; // Template for creating a new page const PAGE_TEMPLATE = ` <div style="visibility: hidden;" class="page" data-loaded="false"> <div id="wrappanotate" class="canvasWrapper"> <canvas id='canvannotate'></canvas> </div> <div class="` + config.textLayerName + `"></div> <svg id="svgannotate" class="` + config.annotationLayerName + `"></svg> </div> `; const PAGE_TEMPLATE_CORRECTION_AI = ` <div style="visibility: hidden;" class="page" data-loaded="false"> <div id="wrappanotate" class="canvasWrapper"> <canvas id='canvannotate'></canvas> </div> <div class="` + config.textLayerName + `"></div> <svg id="svgannotate" class="` + config.annotationLayerName + `"></svg> <div style="margin-left: 18px"> <div style="display: flex; background-color: white; width: 740px; margin: auto"> <div id="svgannotateAI" style="white-space: pre-line;background-color: #00063d; font-size:18px ; color: white;overflow-y: auto; padding:60px 18px; height: 300px;"></div> <div id="svgannotateAI" style="flex: 1 0 150px; margin: auto; padding: 20px; font-size:20px; color: #00063d; font-weight: bold; line-height: 20px;">Ceci est une analyse automatique générée par différentes Intelligences artificielles que Dokoma a agrégées.</div> </div> </div> `; /** * Create a new page to be appended to the DOM. * * @param {Number} pageNumber The page number that is being created * @return {HTMLElement} */ export function createPage (pageNumber, isCorrectionAI) { let temp = window.document.createElement('div'); temp.innerHTML = PAGE_TEMPLATE; let page = temp.children[0]; let canvas = page.querySelector('canvas#canvannotate'); page.setAttribute('id', `pageContainer${pageNumber}`); page.setAttribute('data-page-number', pageNumber); canvas.mozOpaque = true; canvas.setAttribute('id', `page${pageNumber}`); return page; } /** * Render a page that has already been created. * * @param {Number} pageNumber The page number to be rendered * @param {Object} renderOptions The options for rendering * @return {Promise} Settled once rendering has completed * A settled Promise will be either: * - fulfilled: [pdfPage, annotations] * - rejected: Error */ export function renderPage (pageNumber, renderOptions) { let { documentId, pdfDocument, scale, rotate } = renderOptions; // this.eventBus = new pdfjsViewer.EventBus(); // Load the page and annotations return Promise.all([ pdfDocument.getPage(pageNumber), PDFJSAnnotate.getAnnotations(documentId, pageNumber) ]).then(([pdfPage, annotations]) => { let page = document.getElementById(`pageContainer${pageNumber}`); let svg = page.querySelector(config.annotationClassQuery()); let canvas = page.querySelector('#wrappanotate.canvasWrapper canvas'); let canvasContext = canvas.getContext('2d', { alpha: false }); let totalRotation = (rotate + pdfPage.rotate) % 360; let viewport = pdfPage.getViewport({ scale: scale }); let transform = scalePage(pageNumber, viewport, canvasContext); // Render the page return Promise.all([ pdfPage.render({ canvasContext, viewport, transform }), // PDFJSAnnotate.render(page, viewport, annotations), PDFJSAnnotate.render(svg, viewport, annotations) ]).then(() => { // Text content is needed for a11y, but is also necessary for creating // highlight and strikeout annotations which require selecting text. // NECESSAIRE POUR METTRE LES DONNES DE USER if (pdfPage) { // const scaleCalc = page.clientWidth / (viewport.width * (96 / 72)) const temp = new pdfjsViewer.PDFPageView({ container: page, id: pageNumber, scale, // defaultViewport: viewport, defaultViewport: pdfPage.getViewport({ scale: 1 }), textLayerFactory: new pdfjsViewer.DefaultTextLayerFactory(), eventBus: new pdfjsViewer.EventBus(), annotationLayerFactory: new pdfjsViewer.DefaultAnnotationLayerFactory(), renderInteractiveForms: true }) temp.setPdfPage(pdfPage) temp.draw() setTimeout(() => { temp.update(scale) temp.draw() const container1 = window.document.getElementById('pageContainer1') const content = window.document.getElementById('contentId') // const textLayer = window.document.querySelectorAll('.textLayer') // console.log('testLayer', textLayer) // console.log('CONTENT HEI', window.document.querySelectorAll('.annotationLayer')) const annotation = window.document.querySelectorAll('.annotationLayer') if (annotation[1]) { annotation[1].setAttribute('style', 'width:' + content.offsetWidth + 'px') annotation[1].setAttribute('style', 'height:' + content.offsetHeight + 'px') } container1.setAttribute('style', 'width:' + content.offsetWidth + 'px') container1.setAttribute('style', 'height:' + content.offsetHeight + 'px') const svg = window.document.querySelector('#svgannotate') svg.setAttribute("height", content.offsetHeight + 'px') svg.setAttribute('width', content.offsetWidth + 'px') svg.setAttribute('style', 'width:' + content.offsetWidth + 'px') svg.setAttribute('style', 'height:' + content.offsetHeight + 'px') return null }) } const parent = document.getElementById('pageContainer1') const elem = document.querySelectorAll('div[data-page-number]') const wrapper = document.querySelectorAll('#wrappanotate.canvasWrapper') const index = elem.length > 2 ? elem.length - 1 : 1 if (elem[index]) { elem[index].setAttribute('id', 'contentId') } parent.insertBefore(elem[index], wrapper[0]) const widthtoPut = wrapper[0].style.width const heighttoPut = wrapper[0].style.height elem[index].style.width = widthtoPut elem[index].style.border = 0 elem[index].style.margin = 0 elem[index].style.height = heighttoPut for (let i = 1; i < 3; i++) { elem[index].children[i].style.width = widthtoPut elem[index].children[i].style.height = heighttoPut } const canvasToUpdate = document.getElementById('page1') // canvasToUpdate.width = widthtoPut // canvasToUpdate.height = heighttoPut canvasToUpdate.style.width = widthtoPut canvasToUpdate.style.height = heighttoPut // console.log(document.getElementById('page1')) if (wrapper[0]) { // console.log(wrapper[0].style) wrapper[0].style.display = 'none' } return pdfPage.getTextContent({ normalizeWhitespace: true }).then((textContent) => { return new Promise((resolve, reject) => { // Render text layer for a11y of text content let textLayer = page.querySelector(config.textClassQuery()); let textLayerFactory = new pdfjsViewer.DefaultTextLayerFactory(); let textLayerBuilder = textLayerFactory.createTextLayerBuilder(textLayer, pageNumber - 1, viewport, false, new pdfjsViewer.EventBus()); textLayerBuilder.setTextContent(textContent); textLayerBuilder.render(); // let annotationLayer = new DefaultAnnotationLayerFactory(); // annotationLayer.render(); // Enable a11y for annotations // Timeout is needed to wait for `textLayerBuilder.render` setTimeout(() => { try { renderScreenReaderHints(annotations.annotations); resolve(); } catch (e) { reject(e); } }); }); }); }).then(() => { // Indicate that the page was loaded page.setAttribute('data-loaded', 'true'); return [pdfPage, annotations]; }); }); } /** * Scale the elements of a page. * * @param {Number} pageNumber The page number to be scaled * @param {Object} viewport The viewport of the PDF page (see pdfPage.getViewport(scale, rotate)) * @param {Object} context The canvas context that the PDF page is rendered to * @return {Array} The transform data for rendering the PDF page */ function scalePage (pageNumber, viewport, context) { let page = document.getElementById(`pageContainer${pageNumber}`); let canvas = page.querySelector('#wrappanotate.canvasWrapper canvas'); let svg = page.querySelector(config.annotationClassQuery()); let wrapper = page.querySelector('#wrappanotate.canvasWrapper'); let textLayer = page.querySelector(config.textClassQuery()); let outputScale = getOutputScale(context); let transform = !outputScale.scaled ? null : [outputScale.sx, 0, 0, outputScale.sy, 0, 0]; let sfx = approximateFraction(outputScale.sx); let sfy = approximateFraction(outputScale.sy); // Adjust width/height for scale page.style.visibility = ''; canvas.width = roundToDivide(viewport.width * outputScale.sx, sfx[0]); canvas.height = roundToDivide(viewport.height * outputScale.sy, sfy[0]); canvas.style.width = roundToDivide(viewport.width, sfx[1]) + 'px'; canvas.style.height = roundToDivide(viewport.height, sfx[1]) + 'px'; svg.setAttribute('width', viewport.width); svg.setAttribute('height', viewport.height); svg.style.width = `${viewport.width}px`; svg.style.height = `${viewport.height}px`; page.style.width = `${viewport.width}px`; page.style.height = `${viewport.height}px`; wrapper.style.width = `${viewport.width}px`; wrapper.style.height = `${viewport.height}px`; textLayer.style.width = `${viewport.width}px`; textLayer.style.height = `${viewport.height}px`; return transform; } /** * Approximates a float number as a fraction using Farey sequence (max order of 8). * * @param {Number} x Positive float number * @return {Array} Estimated fraction: the first array item is a numerator, * the second one is a denominator. */ function approximateFraction (x) { // Fast path for int numbers or their inversions. if (Math.floor(x) === x) { return [x, 1]; } const xinv = 1 / x; const limit = 8; if (xinv > limit) { return [1, limit]; } else if (Math.floor(xinv) === xinv) { return [1, xinv]; } const x_ = x > 1 ? xinv : x; // a/b and c/d are neighbours in Farey sequence. let a = 0; let b = 1; let c = 1; let d = 1; // Limit search to order 8. while (true) { // Generating next term in sequence (order of q). let p = a + c; let q = b + d; if (q > limit) { break; } if (x_ <= p / q) { c = p; d = q; } else { a = p; b = q; } } // Select closest of neighbours to x. if (x_ - a / b < c / d - x_) { return x_ === x ? [a, b] : [b, a]; } else { return x_ === x ? [c, d] : [d, c]; } } function getOutputScale (ctx) { let devicePixelRatio = window.devicePixelRatio || 1; let backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; let pixelRatio = devicePixelRatio / backingStoreRatio; return { sx: pixelRatio, sy: pixelRatio, scaled: pixelRatio !== 1 }; } function roundToDivide (x, div) { let r = x % div; return r === 0 ? x : Math.round(x - r + div); }