niszetの日記

アナログCMOS系雑用エンジニアが頑張る備忘録系日記

(Pandoc) docx -> md への逆変換のメモ(2回目)

CodeBlockまわりが面倒くさい…

さて、前回の続きです。CodeBlock周りの扱いをどうにかしないといけないのですが…長くなりそうなので複数回にまたがる予定です。

前回の記事にも訂正を入れましたが、docxからMarkdownに変換する際、スタイルがSource CodeになっているものはCodeBlockになっていない、というのは間違いで、ちゃんとCodeBlockとなっています。これは、-t native等で変換すれば確認できます。

しかし、backtick_code_blocksが効かないのはなぜかという疑問が残ります。

これは実際にMarkdown Writerのコードを読めばわかります。

github.com

以下、コード引用。。。

blockToMarkdown' opts (CodeBlock attribs str) = return $
  case attribs == nullAttr of
     False | isEnabled Ext_backtick_code_blocks opts ->
          backticks <> attrs <> cr <> literal str <> cr <> backticks <> blankline
           | isEnabled Ext_fenced_code_blocks opts ->
          tildes <> attrs <> cr <> literal str <> cr <> tildes <> blankline
     _ -> nest (writerTabStop opts) (literal str) <> blankline

これを見ると、attributeがnullの場合はisEnabled Ext_backtick_code_blocks optsを見てくれないことがわかりますね。つまり、属性ナシのCodeBlockはindented code blockになるということです。

実際、前回の出力を見てもCodeBlockはインデントされた状態で書かれていましたが、あれが素のCodeBlockとして必要十分ということでした。

なので、docxに再変換するとSourceCodeになります(たとえ-f docx+stylesから変換したmarkdownファイルでなくても)

しかし、rなどの言語の指定がないので素のコードになります。

また、再変換時は最初に変換したときと同じように --highlight-style tango をつけてあげないと仮に言語の指定があっても再生成したdocxと初めに生成したdocxは等価にはなりません。注意。

pandoc word.docx -t markdown+fenced_code_blocks+fenced_code_attributes -o word_nowrap.md -f docx --atx-headers --wrap=none --extract-media=hoge --highlight-style tango

Lua filterの力を借りる。

ということで、docxからは正しくCodeBlockに変換されるけども、docxに生成した時点で言語情報が失われているので生成したmarkdownのCodeBlockはインデントつきのコードブロックになってしまうのでした。

ということは、Pandocデータ構造(Pandoc's AST)に変換されたあと、CodeBlockに対して言語情報を付与すれば、backtickやfenced codeの形に出来そうだということです。

ということで、まずお試しのコードを作ります。こんな感じ。

function CodeBlock(e)
  e.attr = {class ="r"}
  return(e)
end

attrの与え方はマニュアルを参照。

pandoc.org

これをcodeBlock.luaというファイル名にしたとして、markdownへ書き戻し、

pandoc word.docx -t markdown -o word_rev.md -f docx+styles -s --atx-headers --wrap=none --extract-media=hoge -L codeBlock.lua

すると、すべてのCodeBlockにrというclassが付与されます。

---
author: niszet
date: 2020/5/20
title: word
---

## R Markdown

::: {custom-style="First Paragraph"}
This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see [[http://rmarkdown.rstudio.com]{custom-style="Hyperlink"}](http://rmarkdown.rstudio.com).
:::

::: {custom-style="Body Text"}
When you click the **Knit** button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:
:::

::: {custom-style="Source Code"}
\``` {.r}
summary(cars)
\```
:::

::: {custom-style="Source Code"}
\``` {.r}
##      speed           dist       
##  Min.   : 4.0   Min.   :  2.00  
##  1st Qu.:12.0   1st Qu.: 26.00  
##  Median :15.0   Median : 36.00  
##  Mean   :15.4   Mean   : 42.98  
##  3rd Qu.:19.0   3rd Qu.: 56.00  
##  Max.   :25.0   Max.   :120.00
\```
:::

## Including Plots

::: {custom-style="First Paragraph"}
You can also embed plots, for example:
:::

::: {custom-style="Body Text"}
![](hoge/media/rId23.png){width="5.0526312335958in" height="4.0421052055993in"}
:::

::: {custom-style="Body Text"}
Note that the `echo = FALSE` parameter was added to the code chunk to prevent printing of the R code that generated the plot.
:::

あとは再度docxに変換すればよいのです

pandoc -o word_rev.docx -f markdown  word_rev.md -t docx --highlight-style tango

が、この通りやると、元々Rのコードでない部分もあわせてRのコード扱いになってしまい、等価ではなくなります。つまり、一度docxに変換してしまうと元のコードが何だったのか知るすべがないということですね。

なので、これに対応するためには、docx出力時に生成されるCodeBlockに対して細工をしておく必要がありそうです。例えば、スタイルをSource Code hogeのような名前にして、hogeにその言語名を入れるなどです。

ただし今度はdocxから書き戻す際に、Readerはこれを無視するので、Divでcustom-styleがSource Code hogeである場合はCodeBlockにclass hogeを与えたものを生成するようなフィルタが必要となります。

つまり、

  • md -> docx でCodeBlockをDivに置き換えるLF
  • 対応するスタイルを指定したreference.docx
  • docx -> md でDivをCodeBlockに置き換えるLF

の準備が必要…ということになりますね。面倒ではありますが…。docx -> docx をかませる方法もありますが…(スタイルを置き換える処理などはそのまま残りますが...)

ということで、ちょっと手間が増えるのでやるかどうかは不明ですが、一応道筋はみえてきたかなと。

さて、ほかの課題も見ていかねばなので、続きはまたある程度結果がまとまった時に。