mirror of
https://github.com/documenso/documenso.git
synced 2026-08-15 02:53:32 +10:00
Use our fork of `skia-canvas` for rendering which handles encoding characters correctly with the caveat font and other similar fonts that can group glyphs like ligatures. This resolves issues with pdf text extraction where characters were unable to be extracted due to lacking any data within the cmaps.
98 lines
2.5 KiB
TypeScript
98 lines
2.5 KiB
TypeScript
import { Canvas, Image, Path2D } from '@documenso/skia-canvas';
|
|
import pMap from 'p-map';
|
|
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.mjs';
|
|
|
|
// @ts-expect-error napi-rs/canvas satisfies the requirements
|
|
globalThis.Path2D = Path2D;
|
|
// @ts-expect-error napi-rs/canvas satisfies the requirements
|
|
globalThis.Image = Image;
|
|
|
|
class SkiaCanvasFactory {
|
|
_createCanvas(width: number, height: number) {
|
|
const canvas = new Canvas(width, height);
|
|
canvas.gpu = false;
|
|
|
|
return canvas;
|
|
}
|
|
|
|
create(width: number, height: number) {
|
|
const canvas = this._createCanvas(width, height);
|
|
|
|
return {
|
|
canvas,
|
|
context: canvas.getContext('2d'),
|
|
};
|
|
}
|
|
|
|
reset(canvasAndContext: { canvas: Canvas }, width: number, height: number) {
|
|
canvasAndContext.canvas.width = width;
|
|
canvasAndContext.canvas.height = height;
|
|
}
|
|
|
|
destroy(canvasAndContext: { canvas: Canvas | null; context: unknown }) {
|
|
if (canvasAndContext.canvas) {
|
|
canvasAndContext.canvas.width = 0;
|
|
canvasAndContext.canvas.height = 0;
|
|
}
|
|
|
|
canvasAndContext.canvas = null;
|
|
canvasAndContext.context = null;
|
|
}
|
|
}
|
|
|
|
export type PdfToImagesOptions = {
|
|
scale?: number;
|
|
};
|
|
|
|
export const pdfToImages = async (pdfBytes: Uint8Array, options: PdfToImagesOptions = {}) => {
|
|
const { scale = 2 } = options;
|
|
|
|
const task = await pdfjsLib.getDocument({
|
|
data: pdfBytes,
|
|
CanvasFactory: SkiaCanvasFactory,
|
|
});
|
|
|
|
const pdf = await task.promise;
|
|
|
|
const images = await pMap(
|
|
Array.from({ length: pdf.numPages }),
|
|
async (_, index) => {
|
|
const pageNumber = index + 1;
|
|
const page = await pdf.getPage(pageNumber);
|
|
|
|
const viewport = page.getViewport({ scale });
|
|
|
|
const canvas = new Canvas(viewport.width, viewport.height);
|
|
canvas.gpu = false;
|
|
|
|
const canvasContext = canvas.getContext('2d');
|
|
|
|
await page.render({
|
|
// @ts-expect-error napi-rs/canvas satifies the requirements
|
|
canvas,
|
|
// @ts-expect-error napi-rs/canvas satifies the requirements
|
|
canvasContext,
|
|
viewport,
|
|
}).promise;
|
|
|
|
const result = {
|
|
pageNumber,
|
|
image: await canvas.toBuffer('jpeg'),
|
|
width: Math.floor(viewport.width),
|
|
height: Math.floor(viewport.height),
|
|
mimeType: 'image/jpeg',
|
|
};
|
|
|
|
void page.cleanup();
|
|
|
|
return result;
|
|
},
|
|
{ concurrency: 10 },
|
|
);
|
|
|
|
void pdf.destroy().catch((e) => console.error(e));
|
|
void task.destroy().catch((e) => console.error(e));
|
|
|
|
return images;
|
|
};
|