普段、技術的なメモは Emacs の org-mode で書いたものを 自身の Github リポジトリ に公開している。ただ、これはあくまで個人的なメモなので、それほど他人が見ることを意識したものではない。より見やすい記事として公開するために、個人のブログを、静的サイトジェネレーターの Hugo + Netlify で開始したが、力を入れて書いた技術記事は Qiita にも公開して、広く読んでもらえるようにしたい。

そこで、これまで通り、org ファイルは同じリポジトリで管理しつつ、1 つの org ファイルから個人ブログ向けと Qiita 向けの Markdown をエクスポートできないか試してみた。

生成されるファイル

まずは、結論から。個人ブログ向け・ Qiita 向け、それぞれの出力結果がこちらである。いくつかの機能差分はあるが、普段よく使う内容については概ね Hugo/Qiita どちらも Markdown として出力できそうなことがわかった。

  1. 元となる org ファイル@github
  2. 個人ブログの記事 (Markdown ファイル@github)
  3. Qiita の記事 (Markdown ファイル@github)

なお .org.md を Github 上で閲覧すると github/markup によって自動的にレンダリングされるので、ソースは「Raw」から見てほしい。

ここから、具体的に設定項目、org-mode との機能差分を見ていく。

動作確認バージョン

ソフトウェア バージョン
OS Ubuntu 18.04
Emacs 26.3
org-mode 9.2.6
Hugo 0.59.1/extended

Qiita 向けの Markdown Exporter の選択

Hugo 向けの Exporter は ox-hugo 一択でよいだろう。Qiita 向けの Markdown の生成には、いつくかの候補を検討した後に、最も機能の差分が少なかった ox-gfm を選択した。他の選択肢としては、標準の ox-md や、こちらの記事で紹介されている ox-qmd などがある。特に ox-qmd はそのものズバリで、最も org-mode から Qiita への再現性が高いと思われた。しかし、自分の設定が悪いのか、org の table が HTML として出力されてしまい、その結果、文字修飾が有効にならないという問題があったため断念した。他にも ox-pandoc からも Markdown を出力できるが、今回は試していない。

ox-hugo と ox-gfm のインストール

ox-hugoox-gfm のインストールは非常に簡単で、特に必要な設定項目はない。 load-path に追加して読み込むだけで完了である。

1
2
3
4
5
(add-to-list 'load-path "/path/to/ox-hugo")
(require 'ox-hugo)

(add-to-list 'load-path "/path/to/ox-gfm")
(require 'ox-gfm)

M-x org-export-dispatch のメニューに “Export to Hugo-compatible Markdown”“Export to Github Flavored Markdown” が表示されていれば、インストール成功だ。

ox-hugo の設定

ox-hugo から Hugo ブログを生成する方法として、2 つの方式がある

  1. “One post per Org subtree” 方式: 1 つの org ファイルから、複数の Markdown を生成する
  2. “One post oer Org file” 方式: 1 つの org ファイルから 1 つの Markdown を生成する

1 の方式が ox-hugo の売りのひとつでもあるが ox-gfm との併用を考えると 2 の方式を選択せざるを得ない。

また ox-hugo のサンプルには、見出しの下に :PROPERTIES: を設定している例が見られるが、そうしてしまうと ox-gfm でエクスポートした際に、そのまま記事として出力されてしまうため、必要な設定項目は全て最初の見出しより上に記載する必要がある。

この記事を例にヘッダーでの Hugo に関連する設定項目を見てみよう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#+OPTIONS: author:nil H:6 toc:nil
#+HUGO_BASE_DIR: ~/Dropbox/repos/github/five-dots/blog
#+HUGO_SECTION: post/2019/11/

#+TITLE: 1 つの org ファイルから Hugo と Qiita の Markdown を生成する
#+DATE: 2019-11-05
#+HUGO_CATEGORIES: emacs
#+HUGO_TAGS: org-mode markdown hugo
#+HUGO_CUSTOM_FRONT_MATTER: :toc true

以下本文...
  • :OPTIONS: author:nil H:6 toc:nil
    • ここに記載されているように auther:nil にすることで “著者名が配列として cast できないエラー” を回避している
    • 後述するように、デフォルトでは h4 までしか出力されないため H:6 で h6 までの出力に変更している
    • Qiita では自動で ToC が生成されるので、 toc:nil に設定することで、不要な ox-gfm 側での ToC を生成を止めている
  • #+HUGO_BASE_DIR: ~/Dropbox/repos/github/five-dots/blog
    • org ファイルと blog 自体のリポジトリを分けているので、絶対パスで出力場所を指定している
  • #+HUGO_CUSTOM_FRONT_MATTER: :toc true
    • Hugo 側にはこの項目で ToC が自動で生成されるようにしている

Qiita への投稿フロー

現状は、org ファイルがある同じディレクトリに同名の .md としてエクスポートし、Qiita の投稿画面に手動で貼り付けている。こちらの記事で紹介されていた qiita.el はまだきちんと試すことができていないが、Qiita への投稿頻度が増えてきたら、導入を検討したい。

org-mode の出力確認

以降の章で、org-mode の記法毎に出力結果を確認していく。両者に共通して利用できるものに絞って記事を作成することで、二重管理を避けていくことが主目的だ。

Hugo テーマは Even を利用している。その他のテーマでは、この記事と異なる結果になる可能性もあるので、その点は注意が必要だ。

見出し

Hugo=h2 / Qiita=h1

Hugo=h3 / Qiita=h2

Hugo=h4 / Qiita=h3

Hugo=h5 / Qiita=h4
Hugo=h6 / Qiita=h5

メモ

  • Hugo
    • Hugo では * がタイトルとして扱われるため、 ** (h2) から見出しとして機能する。そのため Qiita とは 1 階層ずれるが、大きな問題にはならない
    • org-mode のデフォルトでは、h4 までしか出力されないため、それ以上の深さが必要な場合は #+OPTIONS: H:6 のように追加で設定が必要
    • Even theme では h6 が最小

表・文字修飾

項目 org-mode 結果
太字 *bold* bold
イタリック /italic/ italic
下線 _underline_ underline
取り消し線 +strikethrough+ strikethrough
コード ~code~ code
逐語 =verbatim= verbatim
上付き hoge^{super} hogesuper
下付き hoge_{sub} hogesub
ギリシャ文字 \alpha α

メモ

  • Hugo
    • 下線は span.underline {text-decoration: underline;}static/css/custom.css に追加した場合の表示結果
    • コードは (setq org-hugo-use-code-for-kbd t) に設定した場合の表示結果
  • Qiita
    • 下線・コードは有効でない

リスト

順序なしリスト

  • hoge
    • hoge
    • fuga
    • piyo
  • fuga
  • piyo

チェックボックス

  • fuga

順序ありリスト

  1. hoge
    1. hoge
    2. fuga
    3. piyo
  2. fuga
  3. piyo

定義リスト

リンゴ
赤いフルーツ
オレンジ
橙色フルーツ

メモ

  • Hugo
    • チェックボックスは、チェックをつけてしまうとなぜか表示されなくなってしまう
  • Qiita
    • チェックボックス自体が有効にならない

引用

Everything should be made as simple as possible, but not any simpler —Albert Einstein

メモ

  • Hugo/Qiita ともに問題なく表示されている

数式

インライン

$y=f(x)$

y=f(x)

ブロック

$$y=f(x)$$

y=f(x)

メモ

  • Qiita
    • ox-gfm のエクスポート時に Latex Fragment の $ ... $\( ... \)$$ ... $$\[ ... \] に変換されてしまう
    • Qiita の数式記法 では、この記法に対応していないので、公式ここここを参考にフィルタを作成して再変換する
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
(require 's)
(require 'dash)

(defun my/org-replace-latex-wrap (text backend _info)
  (when (org-export-derived-backend-p backend 'gfm)
    (cond
     ((s-starts-with? "\\(" text)
      (--> text
           (s-chop-prefix "\\(" it)
           (s-chop-suffix "\\)" it)
           (s-wrap it "$")))
     ((s-starts-with? "\\[" text)
      (--> text
           (s-chop-prefix "\\[" it)
           (s-chop-suffix "\\]" it)
           (s-wrap it "$$"))))))
(add-to-list 'org-export-filter-latex-fragment-functions 'my/org-replace-latex-wrap)

脚注

  • org-mode1 [fn:name]

メモ

  • Qiita
    • Qiita では有効でない

水平線

-----


メモ

  • Hugo
    • 5 つの - で Markdown 側では --- に変換される

コードブロック

Emacs Lisp

1
(emacs-version)
1
2
GNU Emacs 26.3 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.22.30)
 of 2019-09-17

R

コード出力

1
R.version
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
               _
platform       x86_64-pc-linux-gnu
arch           x86_64
os             linux-gnu
system         x86_64, linux-gnu
status
major          3
minor          6.3
year           2020
month          02
day            29
svn rev        77875
language       R
version.string R version 3.6.3 (2020-02-29)
nickname       Holding the Windsock

1
2
library(tidyverse)
head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

プロット

1
2
library(ggplot2)
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point()

メモ

  • Qiita
    • Hugo 向けには ox-hugo が自動で画像ファイルを static/ox-hugo/ へ移動してくれるが、Qiita へは手動で画像をアップロードする必要がある
    • そのため org-babel からプロット画像を出力する先を Dropbox フォルダに設定し、共有リンク機能を利用して画像を公開する
    • org-babel は :exports code に設定することで、ファイルリンク出力を抑制しつつ、手元ではプロットをインライン画像で確認できる
    • ここの記事を参考に、Dropbox の直リンクに変換し、以下のように HTML として出力する
    • 将来的には Dropbox API + Emacs Lisp で自動化したい
1
2
#+attr_html:
[[https://dl.dropboxusercontent.com/s/4j5jstkg1fsvdiw/iris.png]]

Python

1
2
import sys
sys.version
1
2
3.8.2 (default, Apr  8 2020, 12:38:13)
[GCC 7.5.0]

まとめ

一部、Qiita 側で有効にならない記法が見られたが、無いと致命的、という項目はなかった。また、不足している項目も (org-export-filter-TYPE-funstions) を利用すれば、それほど苦労もなくカスタマイズしていけそう、ということもわかった。これから快適なブログライフを送っていきたい。

それでは Happy blogging !!

参考