📰

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

2024/09/14に公開

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

notionはPDF出力機能を持ちますが、そのままだと改ページがされない状態で出力されてしまいます。
今回はNodeのパッケージを使ってnotionページを変換することで改ページを入れる方法を紹介します。

概要

  • notionでの改ページタグ入力
  • Node.jsを使ったnotionページのmarkdown変換
  • Node.jsを使ったmarkdownのPDF変換
    の3ステップでPDFを出力します。
    (markdown変換を挟むことによってhtmlタグの解釈が入り、改ページされるようになります。)

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

制限事項

  • nodeやnpmが自由に使える環境があること。
    今回の方法ではnodeを使ってnotionページをmarkdown形式に変換します。
  • notion api のキーが発行されていること。
    notion apiを使ってnotionページの情報を取得します。
  • 変換するnotionページをmarkdown形式にしたときにレイアウト崩れが発生しないこと。
    横並びの画像やテーブルなどはレイアウトが崩れるので、markdown形式で表現できるページのみが対象になっています。

処理詳細

各種インストール

Node.jsをインストール

https://qiita.com/sefoo0104/items/0653c935ea4a4db9dc2b

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

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

notion apiのキーの発行

https://programming-zero.net/notion-api-setting/

改ページ設定

notionページのhtmlタグ記述

notionページの改行を行いたい個所に以下の記述を行います。

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

例:
alt text

markdown変換

notionページのmarkdown変換

以下の内容のファイルを作成し、実行します。
その際、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);

以下のコマンドを実行します。(ファイル名はnotion_convert.jsとします。)

node notion_convert.js

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

PDF変換

markdownのPDF変換

以下の内容のファイルを作成し、実行します。

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);

以下のコマンドを実行します。(ファイル名はnotion_convert.jsとします。)

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