内容
プロダクトの中に、PDFをアップロードする部分があり、
そこで特定のPDFアップロードすると、下記のように枠線だけ残って、文字が消えるといった自体を発見。
このときの調査の履歴と対応を記しておく。
ライブラリにreact-pdf@3.0.5を利用していた。(pdfjs-dist)
諸事情で今はライブラリのアップデートとかはしたくない!なんてときに役立つはず。
同じような問題に直面している人に少しでも助けになりますように。
前提知識
react-pdfとは
React アプリケーション内で PDF ドキュメントを表示、操作、作成するためのライブラリ。
アプリを作成するときにこのライブラリをインストールして、importして使う。
このライブラリの中でpdfjs-distも使われている
https://react-pdf.org/
pdfjs-distとは
PDF.jsのビルド成果物。これをnpm installで自分のアプリにインストールしてPDF.jsが使えるようになる。
PDF.jsとは
Mozillaが開発した、オープンソースのJavaScriptライブラリ。
ウェブブラウザ内でPDFドキュメントを表示するためのもの
pdfの文字表示のために必要なこと
cmapsの理解。フォントが持つそれぞれの文字と、文字コードを結びつけるための対応表のこと。
PDF.jsなどのPDFレンダリングライブラリはPDF内の文字を実際のUnicode文字にマッピングさせることで、ブラウザでの表示を可能にしている。つまり、PDFが持っている文字の情報をブラウザで表示するためにマッピングの情報を予め持っておく必要がある。そのマッピングを行うものの1つがcmaps。
PDF.jsはそれをライブラリ内部にこんな感じで持っている。
今回の事象
上記スクショのように、アプリ内でcmapsを持っていて、そこを参照してレンダリングされるようになっている場合、ユーザーによってアップロードされたPDFの文字コードが、今、システムに取り込まれている、このライブラリ内のcmapsにマッピングがない場合、このマッピングがうまくいかず、ブラウザでレンダリングできないというような状況になってしまう!!(ここに至るまでかなりの時間を溶かした)
参考)
対応
じゃあ、Web上に転がっている最新のやつを参照するようにすればよくね?
おそらくこのマッピングの参照元を定義している場所があって、そこをどうにかすればなんとかなるのでは
という発想。
それが下記の部分。
react-pdfはデフォルトではライブラリ内を読み込もうとする。
const options = {
cMapUrl: 'cmaps/',
cMapPacked: true,
};
ここを変更する。なんとオプションをつけることができて、読み込むソースを指定できる。
setOptionsをimportする(setOptionsはreact-pdfの3.0.5専用)
import { Document, Page, setOptions } from 'react-pdf';
利用前にsetOptionsを定義しておく
このcMapUrlというところを自分のプロダクトに適するものをチョイスする。
setOptions({
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.0.305/cmaps/',
cMapPacked: true,
});
利用する部分でoptionをもたせる
<Document
file={file}
onLoadSuccess={this.onDocumentLoadSuccess}
options={options}
>
これで表示されるようになった!!
参考)
https://github.com/wojtekmaj/react-pdf/commit/6cc17ca63d5742f23293e8379202a37787941f8f
書簡
自分が作った部分じゃなかった(ほとんどそう)ので、pdfをブラウザにどうやって出しているかというところからの調査。
後任エンジニアあるあるの、問い合わせを投げてきている人よりこの周辺を知らないところからのよーいどん。
前提知識すらいろんなドキュメントを漁って、githubに載っているライブラリの中のソースコードを読んで漸くたどり着けた。
この記事を書くために自分の作業ログを見ていたら、誕生日にこの調査やっていた。エンジニア冥利に尽きるぜ(涙)
同じような問題に直面している人に少しでも助けになりますように。と本当に心から思ってこの記事書こうと思いました。
もっといい解決は絶対あるとは思う。