4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【JS】PDFKitの日本語フォント使用に詰まった話(ブラウザ上)

Posted at

#はじめに
javascriptのPDFKitで、日本語フォントを使うのが色々とめんどくさかったので書きます。

###参考

PDFKit公式サイト
XMLHttpRequest - MDN

##開発環境

  • Node.js 14.17.0
  • PDFKit
  • blobStream (pdfのデータをblob変換して使用するため)
  • browserify (require()を使えるようにするため)
  • google chrome (ブラウザ)
  • atom (テキストエディタ)-->自分の使っているやつで結構

※プログラムはlocalhostにサーバーを立てて実行。
ここで詳しく解説されています

※**「PDFKit」「blobStream」「browserify」**はあらかじめインストールしといてください。

##目次

  1. PDFKitで日本語を出力してみる --> 文字化け
  2. 公式サイトに従ってフォントを指定。 --> エラー
  3. 解決?
  4. 本当の解決!!
  5. おわりに

#1.PDFKitで日本語を出力してみる
まず、試しにPDFを出力したいと思います。

kijiyouhtml
<!DOCTYPE html>
<html lang="ja" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>PDFKIT日本語</title>
    <style>
      html, body{
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <iframe src="" width="100%" height="100%" id='pdf_result'></iframe>
  </body>
  <!-- jsファイル読み込み -->
  <script src="BundledKijiyou.js" charset="utf-8"></script>
</html>
kijiyou.js
// pdfkit,blobStreamの読み込み
const PDFDocument = require('pdfkit');
const blobStream = require('blob-stream');

// iframeを取得
const iframe = document.getElementById('pdf_result');

// pdfドキュメントを作成
const doc = new PDFDocument({
  size: 'A4'
});
const stream = doc.pipe(blobStream());

// 適当にテキストを入力
doc.fontSize(25).text('This is TEXT!!!!', 10, 10);

// ドキュメントの編集を終了
doc.end();

// ドキュメントをオブジェクトURLに変換
stream.on('finish', function() {
  // オブジェクトURLを取得
  const url = stream.toBlobURL('application/pdf');
  iframe.src = url;
})

※「kijiyou.js」はbrowserifyでrequire()を使えるようにする。--> 「BundledKijiyou.js」

####結果
image.png

という風に、英語ならフォントが対応しているので、正常に描画されます。

###日本語を出力すると...

テキストに日本語を追加してみます。

kijiyou.js
// pdfkit,blobStreamの読み込み
const PDFDocument = require('pdfkit');
const blobStream = require('blob-stream');

// iframeを取得
const iframe = document.getElementById('pdf_result');

// pdfドキュメントを作成
const doc = new PDFDocument({
  size: 'A4'
});
const stream = doc.pipe(blobStream());

// 適当にテキストを入力
doc.fontSize(25).text('This is TEXT!!!!', 10, 10);
doc.fontSize(25).text('これはテキストです!!!!', 10, 40);

// ドキュメントの編集を終了
doc.end();

// ドキュメントをオブジェクトURLに変換
stream.on('finish', function() {
  // オブジェクトURLを取得
  const url = stream.toBlobURL('application/pdf');
  iframe.src = url;
})

####結果
image.png

このように文字化けしてしまいます。
まあ、もともとのフォントが英語用なので、日本語に対応しないのは当然ですね。

#2.フォントを指定する
PDFKit公式サイトに従ってフォントを指定してみます。
公式サイトによると、
image.png
このように、「.font(ファイルパス)」でフォントを指定できそうです。

ちなみに、ファイルのルートは、

ルート
ファイル
├── node_modules
├── kijiyou.html
├── kijiyou.js
├── font.ttf(フォントファイル)
└── BundledKijiyou.js

となっているので、「font.ttf」とパスを指定すればいいはず...

じゃあ、指定してみます。

kijiyou.js
// pdfkit,blobStreamの読み込み
const PDFDocument = require('pdfkit');
const blobStream = require('blob-stream');

// iframeを取得
const iframe = document.getElementById('pdf_result');

// pdfドキュメントを作成
const doc = new PDFDocument({
  size: 'A4'
});
const stream = doc.pipe(blobStream());

// 適当にテキストを入力
doc.fontSize(25).text('This is TEXT!!!!', 10, 10);
doc.font('font.ttf'); // <-- フォント指定
doc.fontSize(25).text('これはテキストです!!!!', 10, 40);

// ドキュメントの編集を終了
doc.end();

// ドキュメントをオブジェクトURLに変換
stream.on('finish', function() {
  // オブジェクトURLを取得
  const url = stream.toBlobURL('application/pdf');
  iframe.src = url;
})

####結果
image.png

fs.read...エラーが出やがった!!
んで、僕はこのエラーに3日を溶かしてしまいました...(ハズカシイ...)

#3.エラー解決!!
このエラーを解決するには、なぜフォントを読み込めないかを理解する必要があります。
フォントを取得するには、
image.png
こんな感じに、サーバーにリクエストを送って、サーバーからフォントをもらわなければなりません。
でも、今の状態だと、
image.png
サーバーにリクエストを送っていないので、あるはずのないフォントファイルにアクセスしようとしていることになります。

なので、サーバーにリクエストを送って、フォントファイルを貰いましょう。
じゃあ、これでいいはず...

kijiyou.js
// pdfkit,blobStreamの読み込み
const PDFDocument = require('pdfkit');
const blobStream = require('blob-stream');

// フォントを取得する関数
const getFont = function(fontPath) {
  // フォントリクエストを設定
  const fontRequest = new XMLHttpRequest();
  // フォントリクエスト初期化
  fontRequest.open('GET', fontPath);
  fontRequest.responseType = 'arraybuffer';
  // フォントを取得したら実行
  fontRequest.onload = function() {
    fontArrayBuffer = fontRequest.response;
    // fontArrayBufferをエンコード
    font = new Uint8Array(fontArrayBuffer);
    return font;
  }
  fontRequest.send();
}

// iframeを取得
const iframe = document.getElementById('pdf_result');

// pdfドキュメントを作成
const doc = new PDFDocument({
  size: 'A4'
});
const stream = doc.pipe(blobStream());

// 適当にテキストを入力
doc.fontSize(25).text('This is TEXT!!!!', 10, 10);
doc.font(getFont('font.ttf'));
doc.fontSize(25).text('これはテキストです!!!!', 10, 40);

// ドキュメントの編集を終了
doc.end();

// ドキュメントをオブジェクトURLに変換
stream.on('finish', function() {
  // オブジェクトURLを取得
  const url = stream.toBlobURL('application/pdf');
  iframe.src = url;
})

XMLHttpRequestで、サーバーにリクエストを送って、ファイルをもらう関数を作った。
すると...

####結果
image.png
はあ、、またエラーかよ、、、。
そう。ここにまた落とし穴があるんです。

この「XMLHttpRequest」の実行タイミングが、分かりづらいんです!!

僕は最初、こうだと思ってました。
image.png

でも仮に、取得するデータが大きいと、データを取得し終わるまでサイトを表示できず、閲覧者を待たせてしまいます。なので、実行タイミングはこの様になっています。(多分)
image.png

イメージとしては、プログラムが1本から2本に分岐して、同時に処理をしている感じです。

フォントを取得する前にフォントを設定してしまうので、あるはずもないデータを読み込むことになりますね。

#4.本当の解決
なので、フォントのデータを取得したあとにpdfを編集してあげればいいですね。
イメージとしてはこんな感じ。
image.png

プログラムは...

kijiyou.js
// pdfkit,blobStreamの読み込み
const PDFDocument = require('pdfkit');
const blobStream = require('blob-stream');

// iframeを取得
const iframe = document.getElementById('pdf_result');

// pdfを作成する関数を設定
const PDFProgram = function(font) {
  // pdfドキュメントを作成
  const doc = new PDFDocument({
    size: 'A4'
  });
  const stream = doc.pipe(blobStream());

  // 適当にテキストを入力
  doc.fontSize(25).text('This is TEXT!!!!', 10, 10);
  doc.font(font);
  doc.fontSize(25).text('これはテキストです!!!!', 10, 40);

  // ドキュメントの編集を終了
  doc.end();

  // ドキュメントをオブジェクトURLに変換
  stream.on('finish', function() {
    // オブジェクトURLを取得
    const url = stream.toBlobURL('application/pdf');
    iframe.src = url;
  })
}

// フォントを取得する関数
const getFont = function(fontPath) {
  // フォントリクエストを設定
  const fontRequest = new XMLHttpRequest();
  // フォントリクエスト初期化
  fontRequest.open('GET', fontPath);
  fontRequest.responseType = 'arraybuffer';
  // フォントを取得したら実行
  fontRequest.onload = function() {
    fontArrayBuffer = fontRequest.response;
    // fontArrayBufferをエンコード
    font = new Uint8Array(fontArrayBuffer);

    // pdfを作成 ------ 大事!!!
    PDFProgram(font);


  }
  fontRequest.send();
}

// 実行!!
getFont('font.ttf');

####結果
image.png

このように、日本語出力ができました!!
fontRequest.onloadの式の中でpdfを作成することで、
フォントを取得したあと、無事に日本語出力することができます。

#5.おわりに
今回は、わかりやすいように、関数を作ったりしましたが、書き方を変えても、フォントを取得してからpdfを作成することを守っていれば、日本語出力ができるので、ぜひ、好きなようにプログラミングしてください!!

###それでは、良きPDFKitライフを!

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?