本記事の内容について
ポートフォリオ「MATRIXFLOW」の機能拡充としてpdf作成機能を実装しリリースしたので、本記事に投稿いたします!
実装Before/After
実装前
マトリクスの横にあるサイドバー風ドキュメントに、マトリクスの内容が自動で反映されます。
実装後
サイドバー風ドキュメント部分にpdfプレビューが表示され、ダウンロードできるようにしました。
実装方法について
使用技術
- React 18.3.1
- Redux
- react-pdf/renderer 4.0.0
- Vite 5.4.8
実装手順
react-pdf/renderer のインストール
公式を参考にreact-pdf/rendererをインストールします。
npmを使っているため下記コマンドにて
npm install @react-pdf/renderer --save
公式のサンプルpdfを表示できるようにする
まず、公式のサンプルpdfと同じ内容をアプリ上で表示できるかトライしました。
pdfを表示するページコンポーネントCreateMatrixFlowPage.jsx
上の設定
-
PDFViewer
を設定 - pdfとして表示するコンポーネントを
DocumentPDF
コンポーネントとする
import React from 'react';
// PDFViewerコンポーネントをreact-pdf/rendererからインポート
import { PDFViewer } from '@react-pdf/renderer';
// コンポーネントの読み込み
import DocumentPDF from '../Components/DocumentPDF';
const CreateMatrixFlowPage = () => {
return (
<>
<div className="sidebar-on-PDFViewerMode">
<PDFViewer style={{ width: '100%', height: '80vh' }}>
// pdf表示したいコンポーネント(DocumentPDF)をPDFViewerでラップ
<DocumentPDF/>
</PDFViewer>
</div>
</>
);
};
export default CreateMatrixFlowPage;
DocumentPDF
コンポーネントの設定
公式のサンプルpdfに対してImage
コンポーネントを除外した内容をDocumentPDF
コンポーネントへ
公式のサンプルpdfは下記のように画像パスが設定されていますが、MATRIXFLOWでも同様にこのパスに画像ファイルを配置しないとエラーになるため除外しました。
<Image
style={styles.image}
src="/images/quijote1.jpg"
/>
import React from 'react';
import { Page, Text, Font, View, Document, StyleSheet } from '@react-pdf/renderer';
const DocumentPDF = () => (
<Document>
<Page style={styles.body}>
<Text style={styles.header} fixed>
~ Created with react-pdf ~
</Text>
<Text style={styles.title}>Don Quijote de la Mancha</Text>
<Text style={styles.author}>Miguel de Cervantes</Text>
// ..省略(公式のサンプルpdf内容とほぼ同様、Imageは除外)
</Page>
</Document>
);
Font.register({
family: 'Oswald',
src: 'https://fonts.gstatic.com/s/oswald/v13/Y_TKV6o8WovbUd3m_X9aAA.ttf'
});
const styles = StyleSheet.create({
body: {
paddingTop: 35,
paddingBottom: 65,
paddingHorizontal: 35,
},
title: {
fontSize: 24,
textAlign: 'center',
fontFamily: 'Oswald'
},
author: {
fontSize: 12,
textAlign: 'center',
marginBottom: 40,
},
subtitle: {
fontSize: 18,
margin: 12,
fontFamily: 'Oswald'
},
text: {
margin: 12,
fontSize: 14,
textAlign: 'justify',
fontFamily: 'Times-Roman'
},
image: {
marginVertical: 15,
marginHorizontal: 100,
},
header: {
fontSize: 12,
marginBottom: 20,
textAlign: 'center',
color: 'grey',
},
pageNumber: {
position: 'absolute',
fontSize: 12,
bottom: 30,
left: 0,
right: 0,
textAlign: 'center',
color: 'grey',
},
});
export default DocumentPDF;
公式サンプルのpdfが表示されることを確認
公式サンプルのpdfが表示されることを確認。(ここまでは順調...だった...)
バグ対処
公式サンプルpdfが表示できて順調と思いきや、いろいろバグ対処に見舞われたので記します...😭
バグ対処①:Reduxから参照した内容をpdfに表示できない
実装した内容
サイドバー風のドキュメントは、Reduxを使ってstoreに保持された状態を参照した内容を表示しています。
DocumentPDF
コンポーネントに対して同様にReduxによりstoreの状態を参照しようとしました。
コードは次のようにしました。
import React from 'react';
import { useSelector } from 'react-redux';
import { Page, Text, Font, View, Document, StyleSheet } from '@react-pdf/renderer';
const DocumentPDF = () => {
// Reduxからworkflowsを参照
const workflows = useSelector((state) => state.workflows.workflow);
// Reduxから参照したworkflowsからなる定数
const workflowName = workflows[0]?.name;
<Document>
<Page style={styles.body}>
<Text style={styles.header} fixed>
~ Created with react-pdf ~
</Text>
// Reduxから参照したworkflowsからworkflowNameが表示できるかトライ
<Text style={styles.title}>{workflowName}</Text>
</Page>
</Document>
};
Font.register({
family: 'Oswald',
src: 'https://fonts.gstatic.com/s/oswald/v13/Y_TKV6o8WovbUd3m_X9aAA.ttf'
});
const styles = StyleSheet.create({
// ...(省略)
});
export default DocumentPDF;
バグ発生
...がエラー発生(下記)によりReduxから参照できませんでした。
CreateMatrixFlowPage-0nmkX1Hh.js:218 TypeError: Cannot destructure property 'store' of 't(...)' as it is null.
対処
Reduxに参照するのではなく、親コンポーネントから参照したい内容をpropsとして渡すことでエラー発生せず表示することができました。
import React from 'react';
import { useSelector } from 'react-redux';
import { PDFViewer } from '@react-pdf/renderer';
const CreateMatrixFlowPage = () => {
// DocumentPDFコンポーネントに表示したいstateをReduxから参照
const flowsteps = useSelector((state) => state.flowsteps);
const checklists = useSelector((state) => state.checkLists);
const workflows = useSelector((state) => state.workflow);
const workflowId = useSelector((state) => state.workflow.workflowId);
return (
<div>
<PDFViewer style={{ width: '100%', height: '80vh' }}>
<DocumentPDF
// propsとしてDocumentPDFコンポーネントに渡す
flowsteps={flowsteps}
checklists={checklists}
workflowId={workflowId}
workflows={workflows}
/>
</PDFViewer>
</div>
);
};
export default CreateMatrixFlowPage;
バグ対処②:日本語で入力した内容が文字化けする
実装内容
DocumentPDF
コンポーネントに、pdfのタイトルに日本語をハードコーディング
(タイトルとして「申請」)
import React from 'react';
import { useSelector } from 'react-redux';
import { Page, Text, Font, View, Document, StyleSheet } from '@react-pdf/renderer';
const DocumentPDF = () => {
<Document>
<Page style={styles.body}>
<Text style={styles.header} fixed>
~ Created with react-pdf ~
</Text>
// 試しにタイトルとして「申請」と表示させたい
<Text style={styles.title}>申請</Text>
</Page>
</Document>
};
Font.register({
family: 'Oswald',
src: 'https://fonts.gstatic.com/s/oswald/v13/Y_TKV6o8WovbUd3m_X9aAA.ttf'
});
const styles = StyleSheet.create({
// ...(省略)
});
export default DocumentPDF;
バグ発生
対処
- kashimuraryo /font-nasuからNasuフォントをダウンロード
- Nasuフォントを保存
import React from 'react';
import { Page, Text, Font, View, Document, StyleSheet } from '@react-pdf/renderer';
// Nasuフォントのインポート
import fontRegular from '../../assets/fonts/Nasu-Regular.ttf'; //Nasuフォントを保存したパスを指定
import fontBold from '../../assets/fonts/Nasu-Bold.ttf'; //Nasuフォントを保存したパスを指定
const DocumentPDF = () => {
return (
<Document>
<Page style={styles.body}>
<Text style={styles.title}>申請</Text>
</Page>
</Document>
);
};
// Nasuフォントを登録
Font.register({
family: 'Nasu-Regular',
src: fontRegular
});
Font.register({
family: 'Nasu-Bold',
src: fontBold
});
// Nasuフォントを指定
const styles = StyleSheet.create({
body: { padding: 30 },
title: {
fontSize: 24, textAlign: 'center', marginBottom: 10,
fontFamily: 'Nasu-Bold' //font-familyに'Nasu-Bold'を指定
},
});
export default DocumentPDF;
参考記事
下記の記事を参考にNasuフォントを使用いたしました。
終わりに
自分のスタイリングしたサイドバー風ドキュメントだと、本格的なドキュメントができている感じがあまりしないなぁ、と物足りなかったのですがpdf作成機能を実装して本格的に近づき嬉しいです!
もう1つのポートフォリオ「PlotForge」について
もう1つポートフォリオ「PlotForge」もリリースしております!
こちらもご覧いただけますと幸いです。