Contexte
Chez Indy on fait de la compta :p. Nous devons donc produire différents documents (en pdf) qui seront soit télétransmis soit utilisés par nos clients afin de remplir les mêmes documents sur notre joli site des impôts.
Presque chaque année, les documents changent (dans notre jargon on parle de millésime) et on doit mettre à jour notre générateur de document. Les documents sont mis à disposition par l’État et sont des PDF sans zone de texte clairement définie.

Du coup nous prenons le pdf comme une image de fond et nous écrivons par dessus les bonnes données de chaque client. Il nous faut donc les bonnes coordonnées dans le pdf.
Nous avons plusieurs problématiques.
Différence entre PDFs
La première difficulté est de voir la différence entre 2 millésimes. Nous utilisons pour ça l’outil diff-pdf (https://vslavik.github.io/diff-pdf/).
Pour l’installer sur mac c’est facile (pour les autres OS je vous laisse regarder ici : https://github.com/vslavik/diff-pdf#obtaining-the-binaries) :
brew install diff-pdf
Ensuite on peut facilement voir la différence entre 2 pdf avec la commande suivante :
diff-pdf --view 2033-sd_3330.pdf 2033-sd_3723.pdf
La commande ouvrira une visionneuse (par contre c’est moche 😛).

Récupérer les coordonnées dans un pdf
Afin d’avoir les coordonnées, nous pouvons utiliser facilement des outils web. Pour les curieux, le lien vers le code final se trouve en bas de l’article.
Nous commençons par créer le fichier html. Nous avons besoin d’un canvas. Nous allons aussi créer un span où on mettra les coordonnées.
<div id="app"> <div class="coord">Current: <span id="coord"></span></div> <canvas id="canvas"></canvas> </div>
Un peu de css pour bien voir les positions.
.coord { background-color: black; color: cornsilk; width: 135px; position: fixed; }
Puis nous allons créer le fichier index.ts
import { pdf } from './pdf'; import * as pdfjsLib from 'pdfjs-dist'; import pdfjsWorkerEntry from 'pdfjs-dist/build/pdf.worker.entry.js'; pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorkerEntry; const canvas = document.getElementById('canvas') as HTMLCanvasElement; const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; const load = async () => { // le pdf est en base64 car le site des impôts n'accepte pas les requêtes cors const pdfDoc = await pdfjsLib.getDocument(pdf).promise; const page = await pdfDoc.getPage(1); const viewport = page.getViewport({ scale: 1.5 }); const renderContext = { canvasContext: ctx, viewport: viewport, }; canvas.height = renderContext.viewport.height; canvas.width = renderContext.viewport.width; await page.render(renderContext).promise; function getMousePos(e: MouseEvent) { var rect = canvas.getBoundingClientRect(); return { x: e.clientX - rect.left, y: e.clientY - rect.top }; } canvas.addEventListener( 'click', function (e) { const pos = getMousePos(e); /// check x and y against the grid const [x, y] = viewport.convertToPdfPoint(pos.x, pos.y) as any; const currPos = [parseInt(x, 10), parseInt(y, 10)]; document.getElementById( 'coord' )!.textContent = `${currPos[0]}, ${currPos[1]}`; }, false ); }; load();
La ligne suivante permet de faire fonctionner correctement pdfjs-dist avec un worker.
import pdfjsWorkerEntry from 'pdfjs-dist/build/pdf.worker.entry.js'; pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorkerEntry;
Le point important du code est à la ligne 33 avec viewport.convertToPdfPoint. Cette méthode permet d’avoir les bonnes coordonnées à l’intérieur du pdf. Elle va convertir le x, y du navigateur en coordonnées du pdf.
Une fois que vous aurez cliqué sur une zone du pdf, vous allez voir les coordonnées dans le bloc noir en haut à gauche.

Vous pouvez voir le résultat ici : https://typescript-qgcwq5.stackblitz.io
Et voir le code là : https://stackblitz.com/edit/typescript-qgcwq5
❗ Le fichier pdf.ts est là car :
- stackblitz (free) ne permet pas de déposer un fichier
- impossible de récupérer les fichiers pdf via fetch directement par le site des impôts (ils bloquent les requêtes cors)