📰

notionのPDF出力で改ページを入れる

に公開

notionのPDF出力で改ページを入れる

Notionの標準PDF出力機能では、ページの切り替えが適切に行われないことがあります。
本記事では、Node.jsのパッケージを活用して、NotionページをPDF化する際に任意の箇所で改ページを挿入する方法を解説します。

概要

以下の3つの手順で改ページを含んだPDFを生成します。

  • Notionページ内で改ページタグを設定する
  • Node.jsを用いてNotionページをMarkdown形式に変換する
  • MarkdownファイルをPDFとして出力する

markdownへの変換を挟むことによってhtmlタグの解釈が入り、改ページが挿入されるようになります。

これらのステップの前に、Node.jsのインストールや各種パッケージのインストール、notion apiのキーの発行などが必要になりますが、それらの手順は簡潔にコマンドやリンクを貼る形で記載します。
より詳細な手順や説明は各自で調べていただければと思います。

制限事項

  • Node.jsおよびnpmがインストール済みであること
  • Notion APIのキーを発行していること
  • 変換対象のNotionページがMarkdown形式で適切に表現できる構造であること
    横並びの画像やテーブルなどはレイアウトが崩れるので、markdown形式で表現できるページのみが対象になっています。

処理詳細

各種インストール

Node.jsをインストール

Node.jsのインストール手順はこちらを参考にしてください
https://qiita.com/sefoo0104/items/0653c935ea4a4db9dc2b

パッケージのインストール

以下のコマンドで必要なパッケージをインストールします

npm install notion-to-md md-to-pdf @notionhq/client

notion apiのキーの発行

APIキーの取得方法はこちらを参考にしてください
https://programming-zero.net/notion-api-setting/

改ページ設定

notionページのhtmlタグ記述

改ページしたい箇所に以下のHTMLタグを挿入します。

<div style="page-break-after: always;"></div>

例:
alt text

markdown変換

notionページのmarkdown変換

以下のコードを notion_convert.js として保存してください。
その際、TODOコメントの部分を書き換えてください。

const { Client } = require("@notionhq/client");

// TODO: 実際のAPIキーに置き換えてください
const notion = new Client({
    auth: "YOUR_NOTION_API_KEY",
});

const os = require("os");
const path = require("path");
const { NotionToMarkdown } = require("notion-to-md");
const fs = require("fs").promises;

// NotionからMarkdownへのコンバーターを初期化
const n2m = new NotionToMarkdown({ notionClient: notion });

// TODO: 実際のNotionページIDに置き換えてください
pageId = "CHANGE_HERE_TO_YOUR_PAGE_ID";

async function fetchData(pageId) {
    // Notionページを取得
    const page = await notion.pages.retrieve({ page_id: pageId });
    const title = page.properties.title.title[0].plain_text;

    // NotionページをMarkdownに変換
    const mdblocks = await n2m.pageToMarkdown(pageId);
    const mdString = n2m.toMarkdownString(mdblocks);

    // 過剰な改行を削除してMarkdown文字列をクリーンアップ
    let newMdString = mdString.parent.replace(/\n{3,}/g, "\n\n");

    // Markdownファイルを保存するパスを定義
    const mdPath = path.join(os.homedir(), "Downloads", `/${title}.md`);

    // Markdown文字列をファイルに書き込む
    fs.writeFile(mdPath, newMdString);
}

// Notionページを取得して変換
fetchData(pageId);

コマンド実行:

node notion_convert.js

PDF変換

markdownのPDF変換

notion_convert.js の末尾に以下のコードを追加してください

const { Client } = require("@notionhq/client");

// TODO: 実際のAPIキーに置き換えてください
const notion = new Client({
    auth: "YOUR_NOTION_API_KEY",
});

const os = require("os");
const path = require("path");
const { NotionToMarkdown } = require("notion-to-md");
const fs = require("fs").promises;

// NotionからMarkdownへのコンバーターを初期化
const n2m = new NotionToMarkdown({ notionClient: notion });

// ↓ここから追加
// PDFのヘッダーとフッターの設定
const currentDate = new Date().toLocaleDateString("ja-JP", { year: "numeric", month: "2-digit", day: "2-digit" });
const Op = {
  pdf_options: {
    format: "a4",
    margin: "10mm 5mm",
    printBackground: true,
    headerTemplate: `<style>
              section {
                  margin: 0 auto;
                  font-family: system-ui;
                  font-size: 11px;
              }
          </style>
          <section>
              <div>${currentDate}</div>
          </section>`,
    footerTemplate: `
          <section>
              <div>
                  Page <span class="pageNumber"></span>
                  of <span class="totalPages"></span>
              </div>
          </section>`,
  },
  launch_options: {
    args: ["--no-sandbox"],
  },
};
// ↑ここまでを追加

// TODO: 実際のNotionページIDに置き換えてください
pageId = "CHANGE_HERE_TO_YOUR_PAGE_ID";

async function fetchData(pageId) {
    // Notionページを取得
    const page = await notion.pages.retrieve({ page_id: pageId });
    const title = page.properties.title.title[0].plain_text;

    // NotionページをMarkdownに変換
    const mdblocks = await n2m.pageToMarkdown(pageId);
    const mdString = n2m.toMarkdownString(mdblocks);

    // 過剰な改行を削除してMarkdown文字列をクリーンアップ
    let newMdString = mdString.parent.replace(/\n{3,}/g, "\n\n");

    // Markdownファイルを保存するパスを定義
    const mdPath = path.join(os.homedir(), "Downloads", `/${title}.md`);

    // Markdown文字列をファイルに書き込む
    fs.writeFile(mdPath, newMdString);
  //   ↓ここから追加
  const pdfPath = path.join(os.homedir(), "Downloads", `/${title}.pdf`);
  const pdf = await mdToPdf({ path: mdPath }, Op).catch(console.error);
  if (pdf && pdf.content) {
    await fs.writeFile(pdfPath, pdf.content);
    console.log("PDF file written");
  } else {
    console.log("PDF file not written");
  }
  //   ↑ここまでを追加
}

// Notionページを取得して変換
fetchData(pageId);

再度コマンドを実行してPDFを生成します

node notion_convert.js

PDFファイルがDownloadsディレクトリに出力されます。

まとめ

notionのPDF出力で改ページを入れる方法を紹介しました。
markdownを挟むため複雑なレイアウトができないなど制約は多いですがよかったら試してみてください。

おまけ1

自分はExpressを使って超簡易的な社内用のAPIサーバーとして使っています。
以下の記事などを参考にしてやってもいいかもしれません。
(APIサーバーとして扱う場合、apiキーの扱いや権限の設定等は気をつけてください。)

Express(Node.js)チュートリアル
https://qiita.com/marchin_1989/items/856172b2958701d42808
Express.jsでファイルダウンロード
https://qiita.com/watatakahashi/items/4b456971ae6dc3038569

おまけ2

おまけ1でAPIサーバーを作った場合、IT部門以外の人にも使いやすいようにnotionからワンボタンでPDF出力できると便利です。
ただ、notionのボタン機能はリクエストを直接投げるようなことはできないので Google拡張機能などからAPIサーバーにリクエストを投げるようにするといいかもしれません。

Chrome拡張の作り方 (超概要)
https://qiita.com/RyBB/items/32b2a7b879f21b3edefc
Chrome拡張機能開発の公式チュートリアルを解説+補足 前編
https://qiita.com/tkawa15/items/15af26bdb2b797534fad

単純な拡張なので、いずれ記事に書きます。
➤Chrome拡張機能でリクエストを投げる記事を書きました。
https://zenn.dev/hph/articles/google-extension-for-request

参考

MarkdownをPDFに変換する「md-to-pdf」は痒いところに手が届く素敵ツール
https://dev.classmethod.jp/articles/md-to-pdf/
Notionの本文をMarkdownに変換するライブラリの現状
https://fand.jp/notion/the-library-converts-the-concept-body-to-markdown-format/

GitHubで編集を提案

Discussion