PDF-LIBでカレンダーを作れるサイトを作ったので、PDF-LIBについて書く
この記事は、スターフェスティバル Advent Calendar 2022の 24 日目の記事です。
みなさん! クリスマスイブですね!
ユダヤ暦では日没から一日が始まっていたので、
24 日の午後(イブニング)からメリクリって言っても良いらしいですねー
前日は、まつやの2000 行の SQL が流れてた話でした!
はじめに
今年(2022 年)の 8 月ごろ、親から来年(2023 年)の PDF カレンダーが欲しいと相談されていました。
それならサクッと出来そうだなーと思って・・・気づいたら 12 月になっていました。
すでに 2023 年の PDF カレンダーは入手できたそうですが、
また来年の 8 月になったら欲しいと言われるだろうなと思うので作ってみました。
PDF カレンダー作成サイトを作った時に使った PDF-LIB について書いていこうと思います。
構成
- React: v18.2.0
- PDF-LIB: v1.17.1
まずは、PDF-LIB を使って PDF を生成する
PDF を生成するライブラリは
- TypeScript対応である
- 軽量である
- クライアント側で使える
という事を重視して、最初にヒットしたPDF-LIBを選定しました。
今回は、ライブラリの選定に時間を掛けない為に要点を絞って選びました。
(ちゃんとやる場合は、npm trendsやドキュメントを見て比較しましょう!)
インストール
npm install --save pdf-lib
初期設定
PDFDocument
を生成します
import { useEffect, FC } from "react";
import { PDFDocument } from "pdf-lib";
export default Component: FC = () => {
useEffect(() => {
(async () => {
const pdfDoc = await PDFDocument.create();
})();
}, []);
return <></>;
};
(サンプルのため、雑に useEffect を使っています!ごめんなさい!)
ページを追加
...
export default Component: FC = () => {
useEffect(() => {
(async () => {
const pdfDoc = await PDFDocument.create();
+ const page = pdfDoc.addPage([595.28, 841.89]); // A4
})();
}, []);
return <></>;
};
フォントの設定
page.drawText
でテキストを表示できます。
...
export default Component: FC = () => {
useEffect(() => {
(async () => {
const pdfDoc = await PDFDocument.create();
+ const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman);
const page = pdfDoc.addPage([595.28, 841.89]); // A4
})();
}, []);
return <></>;
};
テキストの追加
page.drawText
でテキストを表示できます。
...
+ import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
export default Component: FC = () => {
useEffect(() => {
(async () => {
const pdfDoc = await PDFDocument.create();
const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman);
const page = pdfDoc.addPage([595.28, 841.89]); // A4
+ const { height } = page.getSize();
+ const fontSize = 30;
+ page.drawText("Creating PDFs in JavaScript is awesome!", {
+ x: 50,
+ y: height - 4 * fontSize,
+ size: fontSize,
+ font: timesRomanFont,
+ color: rgb(0, 0.53, 0.71)
+ });
})();
}, []);
return <></>;
};
iframe で描画する
pdfDoc.saveAsBase64
でbase64
を取得できるのでiframe
で埋め込みます。
import { useEffect, useState, FC } from "react";
import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
export default Component: FC = () => {
+ const [uri, setUri] = useState<string | null>(null);
useEffect(() => {
(async () => {
const pdfDoc = await PDFDocument.create();
const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman);
const page = pdfDoc.addPage([595.28, 841.89]); // A4
const { height } = page.getSize();
const fontSize = 30;
page.drawText("Creating PDFs in JavaScript is awesome!", {
x: 50,
y: height - 4 * fontSize,
size: fontSize,
font: timesRomanFont,
color: rgb(0, 0.53, 0.71)
});
const dataUri = await pdfDoc.saveAsBase64({ dataUri: true });
setUri(dataUri);
})();
}, []);
+ if (uri === null) {
+ return <p>生成中</p>;
+ }
return (
+ <iframe
+ id="pdf"
+ title="pdf"
+ style={{ width: 1000, height: 500 }}
+ src={uri}
+ ></iframe>
);
};
こんな感じで PDF を生成できます。
日本語フォントに対応する
上記で紹介したフォントの設定では PDF-LIB が準備しているものだけしか使えないため、ttf
を使って日本語を使えるようにします。
ttf
自体は、Google Fonts などから好きなものを持ってきてください。
@pdf-lib/fontkit をインストール
npm install --save @pdf-lib/fontkit
フォントを適用する
fontkit を設定し、ttf
をembedFont
に食わせたら日本語フォントも使用できるようになります!
+import fontkit from "@pdf-lib/fontkit";
...
const pdfDoc = await PDFDocument.create();
+ pdfDoc.registerFontkit(fontkit);
- const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman);
+ const latoRegularBytes = await fetch("/fonts/Lato-Regular.ttf").then(
+ (res) => res.arrayBuffer()
+ );
+ const latoRegular = await pdfDoc.embedFont(latoRegularBytes);
const page = pdfDoc.addPage([595.28, 841.89]); // A4
+ page.drawText("JavaScriptでPDFを作成するのはすごい!", {
x: 50,
y: height - 4 * fontSize,
size: fontSize,
+ font: latoRegular,
color: rgb(0, 0.53, 0.71)
});
...
今回はフロント側で使用しているのでfetch
を使ってますが、node.js
であればfs
を使って読み込ませることができます。
大変だったところ/いまいちポイント
PDF-LIB の初期位置
ページの左下から x と y を指定する必要があり、
ページの height を指定して左上に 1 回持ってきてから計算するのが面倒くさかったです。
rgb()
色を指定する際にrgb(0,0,0)
のように指定するのですが、0~255 ではなく 0~1 で指定する必要があり、思った色が出せないで苦労しました。
PDF 内リンク
PDF 内のリンクを設定する関数が準備されていませんでした。
下記の issue を参考に自分で実装する必要がありました。
さらに、テキストにリンクを貼りたいだけでもリンクの範囲を指定する必要があり、微調整に時間がかかるということがありました。
この辺りは、jsPDF のtextWithLinkで簡単に実装できそうです。
この記事を書いている時にみつけてしまい、後悔しとります。
まとめ
ある程度作業終わったところでjsPDF
を見つけ、PDF-LIB でつまづいたところが簡単に実装できそうな感じがしました。
サクッと終わらせようと技術選定をサボってしまった罰ですね・・・
ということで、今回 PDF-LIB を使用して作った PDF カレンダー作成サービスが ↓ こちらになります!
今後、テーマを増やしたり UI を調整したり・・・いろいろアップデートしていこうと思っています!
採用頑張ってます!
気軽に相談、ご応募お待ちしております!
ウェルウェルカムカム!!
実は Twitter もあります!
よかったら見てみてくださいー
Discussion