Open4

Marpにいれたmermaid記法をVSCodeでPreviewとExport

fuyufuyu

Marpでmermaid記法を使いたい

ゴール設定

PDFや諸々出力でも問題が出ないように調整したい
MARPを使う、ということで調べていたら、いまはMarpitに集約されていると

Marpitをmarkdown内で書く

---
marp: true
---
参考サイト
---
marp: true
---
<pre class="mermaid">
graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;
</pre>

<!-- Put this script at the end of Markdown file. -->
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>

mermaid記法を入れるための試作

参考サイト

https://mermaid.js.org/config/usage.html#using-mermaid

呼び出し

mermaid.esm.mjsまたはmermaid.esm.min.mjsを介し​​て Mermaid ライブラリをインポートしmermaid.initialize()呼び出します。
これにより、ダイアグラムの外観が決定され、レンダリング プロセスが始まる。
https://mermaid.js.org/intro/getting-started.html#requirements-for-the-mermaid-api

呼び出し時のパラメータ指定ではどうかな
https://mermaid.js.org/config/usage.html

フローチャートの指定

nodeの余白を調整できるか
https://docs.mermaidchart.com/mermaid-oss/syntax/flowchart.html

テーマの指定

フォント周りを調整できるか
https://mermaid.js.org/config/theming.html

mermaid内に書く

%%{init:{'theme':'base','themeCSS':" .cluster .label {font-size:32px;font-weight:bold;} .output {font-size:24px;}",'flowchart':{'nodeSpacing':80}}}%%

marpでstyle指定

---
marp: true
style: |
  section {
    background-color: #ccc;
  }
  .mermaid svg {
    display: block;
    min-width: 100%;
    max-width: 100%;
    max-height: 100%;
    margin: 0 auto
  }
---

実践

  • フォントが表示し切れない問題
  • previewの方の色が実際と違う

preview

export

fuyufuyu

Mermaid + 日本語 + 絵文字対応

Marp / PDF出力で安定動作させるための最終まとめ

問題の発端

  • Mermaid記法がそのまま出力される / 描画エラーになる
    • PDF化では <script> 実行が制限される
    • btoa()非ASCII(日本語・絵文字) を処理できずエラー
    • Markdownやエディタ経由で NBSP(\u00A0)ゼロ幅スペース(\u200B) が混入

改善アプローチと修正点

対策 内容 効果
NBSP除去処理 (el.textContent ?? '').replace(/\u00A0/g, ' ').trim(); ソース中の不可視文字による構文エラー防止
encodeURIComponent方式 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg) 絵文字・日本語含むSVGの安全なエンコード
一意IDの付与 m-${crypto.randomUUID()} SVG内部のmarker / defs衝突を防止
フォント指定除去/SVGテキスト対策 inline SVG+overflow: visiblehtmlLabels: true フォント見切れや行間切れを防止
PDF出力時はHTML許可 VSCode: Enable HTML / CLI: --html <script> 実行を有効化

サンプル

---
marp: true
size: 16:9
paginate: true
---

<!-- Use: marp --html --watch <this file> -->

<style>
  /* 画像にして埋め込む前提のサイズ調整用クラス */
  img.mermaid-100h { max-height: 100%; }
</style>

<!-- Mermaid v11 を ESM import で読み込み、<pre.mermaid> を画像に差し替え -->
<script type="module">
  import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';

  mermaid.initialize({
    startOnLoad: false,
    theme: 'default'
  });

  // btoaは非ASCII(日本語・絵文字)で失敗するため、URIエンコードでdata URL化
  const svgToDataURL = (svg) =>
    'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);

  window.addEventListener('DOMContentLoaded', async () => {
    const mermaidEls = document.querySelectorAll('pre.mermaid');
    let n = 0;

    for (const el of mermaidEls) {
      // NBSP(\u00A0)混入を通常スペース化し、前後空白を除去
      const code = ((el.textContent ?? '').replace(/\u00A0/g, ' ')).trim();
      if (!code) continue;

      // 一意ID(数字始まり回避&再描画でも衝突しにくい)
      const id = (crypto?.randomUUID ? `m-${crypto.randomUUID()}` : `m-${Date.now()}-${n++}`);

      try {
        const { svg } = await mermaid.render(id, code);

        const img = document.createElement('img');
        img.src = svgToDataURL(svg);
        img.className = el.className;                 // 既存クラスを引き継ぐ(例: mermaid-100h)
        const style = el.getAttribute('style');
        if (style) img.setAttribute('style', style);  // インラインstyleも引き継ぐ

        el.replaceWith(img);
      } catch (e) {
        // フォールバック:エラー内容をスライド上に表示
        const warn = document.createElement('div');
        warn.style.cssText = 'padding:.75rem 1rem;border-left:6px solid #e53935;background:#fdecea;color:#b71c1c;border-radius:.5rem;';
        warn.textContent = `Mermaid描画エラー: ${e?.message || e}`;
        el.replaceWith(warn);
      }
    }
  });
</script>

### Flowchart test (日本語/絵文字対応・フォント指定なし)

<pre class="mermaid mermaid-100h">
flowchart LR
  A["開始 😃"] --> B["処理中 🔧(日本語OK)"]
  B --> C["完了 🎉"]
  B --> D["要確認 ❓"]
  style A fill:#e0f7fa,stroke:#006064,stroke-width:2px
  style C fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
  style D fill:#fff3e0,stroke:#e65100,stroke-width:2px
</pre>

Preview

コードとVSCodeのプレビューを並べた

PDF

PDFとVSCodeのプレビューを並べた。
差があるのは色の指定のないクラス。そこも調整できた方がよいな。

fuyufuyu

不可視文字(ZWSPなど)への対応について

MermaidやMarkdownを扱う際に、コピー&ペーストやエディタの自動変換によってゼロ幅スペース(ZWSP)やノーブレークスペース(NBSP)などの不可視文字が混入することがあります。これらは見た目には分かりませんが、Mermaidの構文解析を壊す原因になります。

// 不可視文字を除去する関数
const sanitize = (s) =>
  s.replace(/\u00A0/g, ' ')            // NBSPを通常のスペースに
   .replace(/[\u200B-\u200D\uFEFF]/g, '') // ZWSP, ZWNJ, ZWJ, FEFFを削除
   .trim();

なぜ必要か

  • 文字の間に「見えない文字」があると、Mermaidがノードやエッジを正しく認識できない
  • 「Syntax Error」「Unexpected character」といった曖昧なエラーの原因になりがち
  • ZennやVSCodeでは、HTMLコピーなどで自動的に混入することもある

影響

  • 日本語・英語の文章、Markdown、Mermaidコードでは削除しても表示や意味に影響なし

  • 絵文字の結合(👨‍👩‍👧‍👦 など)アラビア語・インド系文字 では、ZWJ/ZWNJを削除すると表記が崩れることがあります

  • そのため、用途によっては以下のように“軽めの除去”:

    s.replace(/[\u200B\uFEFF]/g, '')
    

普通のスライドやMermaid図では削除して問題なし。
ただし、絵文字や特殊言語を多く使う資料では慎重に扱う。

fuyufuyu

MarpでMermaidを使うとき、theme: gaia で文字サイズが崩れる件

状況

Marpで theme: gaia(または uncover)を指定しているとき、Mermaid内の文字サイズやフォントが崩れることを確認。

  • 文字が異常に大きくなる
  • ノード内の文字がはみ出す
  • PDFではさらにズレが目立つ

原因は MarpテーマのCSSリセット が、MermaidのSVG内にまで影響しているため。

原因の整理

要因 内容
Marpテーマの section スコープ font-size, color, line-height などが全要素に継承される
Mermaidが em 基準でノードサイズを計算 Marp側のfont-sizeに引きずられる
Mermaidの themeVariables がCSSに負ける Marpテーマが後勝ちになる
PDF出力時のフォント差 Marpテーマのフォント指定がSVGに反映される

対策

SVGを独立スコープにする

Mermaidのrender()で生成したSVGに、CSS継承をリセットする。

const inline = new DOMParser().parseFromString(svg, 'image/svg+xml').documentElement;
inline.style.all = 'initial'; // Marpテーマの影響をリセット

これで theme: gaiatheme: uncover を使っても、文字の崩れが解消される。


Mermaidのフォントを明示的に指定

mermaid.initialize({
  startOnLoad: false,
  theme: 'base',
  themeVariables: {
    fontFamily: 'Arial, "Noto Sans JP", sans-serif',
    fontSize: '18px',
  },
});

theme: base を指定すると、Marpテーマとの競合が最小化される。


Marp側でSVG内の文字をリセット

MarpスライドのCSSで、SVG内テキストを強制的に指定し直す方法も有効。

<style>
svg.mermaid text {
  font-size: 18px !important;
  font-family: "Noto Sans JP", sans-serif !important;
  fill: #333 !important;
}
</style>

実測メモ

Marpテーマ 結果 備考
gaia かなり大きく崩れる 確認

まとめ

  • theme: はスライド装飾に優れるが、Mermaidの文字サイズを上書きしてしまう

  • 安定させるには

    • inline.style.all = 'initial' を追加
    • または theme: base + themeVariables 明示
  • どうしてもズレる場合は theme: none を選ぶのが無難


補足
この挙動はMermaidのバグではなく、MarpテーマCSSの継承によるもの。
Marpのテーマはスライド本文を対象として設計されており、SVGの中身まで制御対象に入ってしまう。