📋

PDF.js(pdfjs-dist) / react-pdfで日本語の文字が表示されない問題の解決方法

2023/11/25に公開

原因

cMapという、PDFに保存されている文字コードとフォントの文字データを紐付けるファイルが不足していることが原因です。

CDNを使う方法

PDF.js の場合

以下のようにCDNからcMapを読み込むことで表示されるようになります。

pdf-utils.ts
import * as pdfjs from "pdfjs-dist";

pdfjs.GlobalWorkerOptions.workerSrc = new URL(
  "pdfjs-dist/build/pdf.worker.min.js",
  import.meta.url,
).toString();

export async function loadPDF(path: string): Promise<pdfjs.PDFPageProxy[]> {  
  const loadingTask = pdfjs.getDocument({
    url: path,
+   cMapUrl: "https://unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/",
+   cMapPacked: true,
  });
  const pdf = await loadingTask.promise;
  const pages = [];
  for (let i = 0; i < pdf.numPages; i++) {
    pages.push(await pdf.getPage(i + 1));
  }
  return pages;
}

react-pdfの場合

pdf-viewer.tsx

        <Document
          file={pdfPath}
+         options={{
+           cMapUrl: "https://unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/",
+           cMapPacked: true,
+         }}
        >

静的アセットを読み込む方法(おすすめ)

あるいはビルドする前にnode_modulesからコピーして使う方法も考えられます。

package.json
{
  "name": "hoge",
  "license": "UNLICENSED",
  "version": "1.0.0",
  "private": true,
  "scripts": {
+   "build": "npm run copy-cmaps && next build",
+   "copy-cmaps": "cp -r node_modules/pdfjs-dist/cmaps/ public/cmaps/",
    "dev": "next dev",
    "lint": "next lint",
    "lint:fix": "next lint --fix",
    "start": "next start",
    "type-check": "tsc --noEmit"

Next.jsやRemixなどの場合は public/ 配下に静的アセットとして保存して参照します。

PDF.js の場合

pdf-utils.ts
import * as pdfjs from "pdfjs-dist";

pdfjs.GlobalWorkerOptions.workerSrc = new URL(
  "pdfjs-dist/build/pdf.worker.min.js",
  import.meta.url,
).toString();

export async function loadPDF(path: string): Promise<pdfjs.PDFPageProxy[]> {  
  const loadingTask = pdfjs.getDocument({
    url: path,
+   cMapUrl: `/cmaps/`,
+   cMapPacked: true,
  });
  const pdf = await loadingTask.promise;
  const pages = [];
  for (let i = 0; i < pdf.numPages; i++) {
    pages.push(await pdf.getPage(i + 1));
  }
  return pages;
}

react-pdfの場合

pdf-viewer.tsx

        <Document
          file={pdfPath}
+         options={{
+           cMapUrl: "/cmaps/",
+           cMapPacked: true,
+         }}
        >
Dynagon Tech Blog

Discussion