/// /// /// import { load } from "./load.ts"; import type { Metadata } from "https://raw.githubusercontent.com/takker99/deno-pdfjs-dist-types/0.1.0/src/display/metadata.d.ts"; export interface PdfConverterOptions { /** cmapsがあるDirectory URL * * 既定ではci7lusさんのを使わせていただいている */ cMapUrl?: string; } export interface ConvertOptions { /** 画像化するときの解像度 * * @default 150 */ printResolution?: number; /** 画像のmime type * * @default "image/png" */ mimeType?: "image/png" | "image/jpeg"; /** 画像の品質を表す指数 * * `mimeType`が`"image/jpeg"`のときのみ有効 */ quality?: number; } /** PDFを画像に変換するやつ */ export interface Converter { /** PDFのmetadata */ metadata: { info: Record; metadata: Metadata; }; /** PDFのページ数 */ count: number; /** 指定したページを画像に変換する * * @param pageNum 画像化するページのページ番号。1から始まる * @param options 変換options * @return 画像のBlobで解決されるPromise */ convert: (pageNum: number, options?: ConvertOptions) => Promise; /** ページを一枚ずつ画像に変換する * * `convert`をasync generatorにしただけ * @param options 変換options * @return 画像のBlobを返すAsyncGenerator */ read(options?: ConvertOptions): AsyncGenerator; } export const pdfConverter = async ( data: string | Uint8Array | number[], options?: PdfConverterOptions, ): Promise => { // @ts-ignore prototype汚染 const getIndexByTitleLc = Array.prototype.getIndexByTitleLc; // @ts-ignore prototype汚染 delete Array.prototype.getIndexByTitleLc; try { const pdfjsLib = await load("2.13.216"); const pdf = await pdfjsLib.getDocument({ data, cMapUrl: options?.cMapUrl ?? "https://storage.googleapis.com/chrono-lexica/ci7lus-assets/pdfjs/cmaps/", cMapPacked: true, }).promise; const metadata = await pdf.getMetadata(); const convert = async (pageNum: number, options?: ConvertOptions): Promise => { const page = await pdf.getPage(pageNum); const viewport = page.getViewport({ scale: window.devicePixelRatio ?? 1.5, }); const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); if (!ctx) throw Error("2D rendering on is not supported"); const PRINT_UNITS = (options?.printResolution ?? 150) / 72.0; const renderContext = { canvasContext: ctx, viewport: viewport, transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0], }; canvas.height = Math.floor(viewport.height * PRINT_UNITS); canvas.width = Math.floor(viewport.width * PRINT_UNITS); await page.render(renderContext).promise; return new Promise( (resolve, reject) => canvas.toBlob( (blob) => !blob ? reject(new Error("Faild to create Blob")) : resolve(blob), options?.mimeType ?? "image/png", options?.quality, ) ); }; return { metadata: { info: metadata.info as Record, metadata: metadata.metadata, }, count: pdf.numPages, convert: (pageNum: number, options?: ConvertOptions) => convert(Math.min(Math.max(1, pageNum), pdf.numPages), options), read: async function* (options?: ConvertOptions) { for (let i = 1; i <= pdf.numPages; i++) { yield await convert(i, options); } }, }; } finally { // @ts-ignore prototype汚染 Array.prototype.getIndexByTitleLc = getIndexByTitleLc; } };