POSTS2022

新博客

tooling

花了相当相当多时间重新建设自己的内容管理系统,总算大致完成(https://github.com/huangjunwen/mdtool),记录一下

需求

大致如下

  1. 内容应该可以很容易跨平台发布,例如除了本身作为博客的网站外,也能发布到微信公众号或其他内容平台, 甚至可以单独输出成文档
  2. 写作时要有实时反馈
  3. 内容使用的格式不能过于依赖专有或特定软件
  4. 仅使用开源工具

格式

Markdown

熟悉,简单,广泛应用

Tex

功能强大,表达丰富,不过需要些时间学习;另外 markdown 中也可以嵌入 math mode 的公式,这已经解决很大一部分问题了

Svg

缩放无损,文本可编辑,众多编辑器可用1,可 apply css

PNG

普遍支持

工具

Hugo

速度快,编辑即可看到效果

Pandoc

pandoc 也是可以作为 hugo 的 markdown 渲染引擎的 2,用它有多个好处

  • 能识别 latex 式的 math mode($...$/$$...$$
  • 可以写 filter!用 外部程序 或者 lua 脚本 直接修改 pandoc 的 ast
  • pandoc 本身就是用来转换格式的(e.g. markdown -> pdf)
  • 更多扩展

整合

整合起来就是:内容为 markdown 格式,尽量少使用专有的扩展,pandoc 负责将其转换格式,其中最主要的 html 格式则交由 mdtool(hugo + js + css)进一步处理及呈现

一些实现细节:

  • hugo 调起 pandoc 的命令行很简单: pandoc --mathjax,因此需要自己写一个也叫做 pandoc 的 shell 脚本并放置到 $PATH 更优先的地方,在其中可添加更多的参数(e.g. 添加 filter)
  • 一开始是在 pandoc filter 里用 prismjs/mathjax 对语法高亮/数学公式进行服务端渲染,完成后发现太慢了,后来还是改回客户端渲染
  • 微信公众号对 css 有巨多限制,给调试样式添加了很多麻烦

assets

有时图片或其他 asset 需要由别的文件转换生成(e.g. dot 转 svg,svg 转 png 等),不想每次都手动转换的话, 可以简单写个脚本监听文件变化然后进行处理;例如下面是一个将 ipe 格式的文件转换为 svg/png 的脚本, inotifywait 监听特定文件名模式(*.svg.ipe 表示想转为 svg,*.png.ipe 表示想转换为 png),匹配到更新时, 就用 iperenderinkscape 对图片进行处理转换

注:这里用 inkscape 主要是用到它 clone-unlink-recursively 功能, svg 里为了节省空间常常会定义 defs 然后别的多个地方 use 引用(clone)它, 自然地 use 的地方需要指定 defs 的 id,但恶心的是微信公众号环境下是不允许有 id 的, 故不能用 use 而只能每一个出现的地方都复制一遍

#!/bin/bash

# gen-files.sh

ipe2svg () {
  # *.svg.ipe -> *.svg
  local src=$1
  local target=${src%.ipe}
  if [ $src -nt $target ]; then
    iperender -svg $src /dev/stdout | \
      inkscape -lp --export-filename=- --actions="select-all:all;clone-unlink-recursively" --vacuum-defs | \
      sed 's/rgb(0\%,0\%,0\%)/currentColor/g' > $target
    echo "$target is regenerated"
  else
    echo "$target is already up-to-date"
  fi
}

ipe2png () {
  # *.png.ipe -> *.png
  local src=$1
  local target=${src%.ipe}
  if [ $src -nt $target ]; then
    iperender -png -resolution 256 $src $target
    echo "$target is regenerated"
  else
    echo "$target is already up-to-date"
  fi
}

find . -name '*.svg.ipe' -print | while read file; do \
  ipe2svg $file; \
done

find . -name '*.png.ipe' -print | while read file; do \
  ipe2png $file; \
done

inotifywait -rmq -e close_write,moved_to . | while read dir action file; do \
  case $file in
    *.svg.ipe)
      ipe2svg $dir/$file
      ;;
    *.png.ipe)
      ipe2png $dir/$file
  esac
done