📁
PDFから画像を取得する
PDF.js でPDFファイルから内部画像を取得するための備忘録です。
環境
- pdfjs-dist v4.10.38
- React 19.0.0
- Typescript 5.7.2
読み込み
今回はブラウザで動作するようにします。
Viteを使用するため、こちらを参考にworkerの設定をします。
import {
getDocument,
GlobalWorkerOptions
} from 'pdfjs-dist'
GlobalWorkerOptions.workerSrc = new URL(
"pdfjs-dist/build/pdf.worker.min.mjs",
import.meta.url,
).toString();
PDFファイル読み込み
export const loadPdf = async (file: File): Promise<PDFDocumentProxy> => {
if (!file || file.type !== 'application/pdf') {
throw new Error('Invalid file type');
}
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = async (event) => {
const buffer = event.target?.result;
if (!buffer) {
return reject('Failed to read file');
}
const loadingTask = getDocument({ data: buffer, isOffscreenCanvasSupported: false });
const pdf = await loadingTask.promise;
return resolve(pdf);
};
reader.onerror = () => reject('Failed to read file');
reader.readAsArrayBuffer(file);
});
}
isOffscreenCanvasSupported: false
後に画像データを取得する際にOffscreenCanvasが使用されているとデータが取得できないため、設定が必要です。(v3.4.120以上)
ページオブジェクトから画像データ取得
const addAlphaChannel = (imgData: Uint8ClampedArray, width: number, height: number) => {
const newImageData = new Uint8ClampedArray(width * height * 4);
for (let j = 0, k = 0, jj = width * height * 4; j < jj;) {
newImageData[j++] = imgData[k++];
newImageData[j++] = imgData[k++];
newImageData[j++] = imgData[k++];
newImageData[j++] = 255;
}
return newImageData;
}
export const extractPageImages = async (page: PDFPageProxy) => {
const { fnArray, argsArray } = await page.getOperatorList();
const targets = [OPS.paintImageXObject, OPS.paintInlineImageXObject];
const imageIndexArray = fnArray.reduce((acc, cur, index) => {
if (targets.includes(cur)) {
acc.push(index);
}
return acc;
}, [] as number[]);
const images = [];
for (let i = 0; i < imageIndexArray.length; i++) {
const imgName = argsArray[imageIndexArray[i]][0];
const imgObj = await page.objs.get(imgName);
const imageWithAlpha = addAlphaChannel(imgObj.data, imgObj.width, imgObj.height);
const imageData = new ImageData(imageWithAlpha, imgObj.width, imgObj.height);
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
if (!context) {
throw new Error('Failed to get 2d context');
}
canvas.height = imgObj.height;
canvas.width = imgObj.width;
context.putImageData(imageData, 0, 0);
images.push(canvas.toDataURL());
}
return images;
}
PDFOperatorListから画像のインデックスをfnArrayから取得し、argsArrayから画像データを取得します。
取得したデータをCanvasに書き出し、dataURLに変換し保持します。
PDFから取得した画像データにはalphaチャンネルがないことがあるようなので、追加してImageDataを作成します。(画像の種類による?)
全ページに適用する
あとはページ分、上記処理を実行し全てのページの画像を取得するようにします。
export const extractImagesFromPdf = async (file: File) => {
const pdf = await loadPdf(file);
const images = [];
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const pageImages = await extractPageImages(page);
images.push(...pageImages);
}
return images;
}
まとめ
PDF.jsは設定の周りなど調べることが結構あるイメージなので参考になれば幸いです。
参考
Discussion