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
JavaScript
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);
}