published on
tags: tech

使用markdown来编写一本书

有时候,我们时不时会冒出写一本书的想法。如果是小说,那么都是一些文本,写起来问题不大,而如果是技术书籍,你就会对排版这件事有一些担心,优秀的排版效果是一本优秀的书不可缺少的内容,尤其是当里面有很多数学公式或者代码的时候,排版的效果对于看书的人来说影响还是挺大的。

这种时候,你回去找一个排版工具,比如Word或者是LaTeX,但是你会发现Word用起来比较麻烦(咦?当你的内容多了,每个内容去排版保持一致性比较费事),而用LaTeX还是有点麻烦,因为你老是写成一些类似下面的代码

    \section{这是一章}
    \subsection{这是一节}

    一部分正文
    \begin{itemize}
       \item 列表项1
       \item 列表项2
    \end{itemize}

这样,这里面其实冗余比较多,和html比较类似,源文件比较繁琐,也不是很适合直接阅读。所以考虑到我们可以用markdown来生成html (不知道markdown是什么?搜索引擎可以帮助你。),于是我们也可以试着用markdown来生成pdf

用到的工具

  • pandoc 这是一个强大的格式转换工具,我们可以依靠它从markdown转换成很多别的格式。这里我靠它转换成latex再进一步生成pdf。实际上也用通过markdown产生html再产生pdf的用法,看你对哪种方式比较熟悉即可。
  • python 脚本语言,用来制作build脚本,实际上你可以用很多别的东西来代替比如ruby makefile或者是doc的批处理等。
  • texlive 用来将.tex文件转换到pdf,由于需要考虑支持中文,我们使用里面的xelatex程序,实际上还有不少tex的可用环境比如MikTex

然后开始动手

先看我们的目录规划:

    content/
       \-  chapter1.markdown
       \-  chapter2.markdown
             ...
    meta.json
    process.py
    template.tex

content下面就是我们写的内容了

meta.json是一个用来描述我们书本结构的东西。具体内容我随手定了一个:

	[
        { "name" : "Part1", 
      	  "chapters" : [ 
              { "name" : "The My First Chapter", "file" : "content/chapter1.markdown" }
              { "name" : "Other Chapter", "file" : "content/chapter2.markdown" }
          ] 
        }
        { "name" : "An other part", 
      	  "chapters" : [ 
              { "name" : "Just another Chapter", "file" : "content/chapter3.markdown" }
          ] 
        }
    ]

这里面描述了书的结构:一共两个部分,第一部分有两章,第二部分有一章。我用这样一个文件来描述这样一个结果,如果你的书结果比较简单,比如直接按章分了,那么甚至可以省略这个文件。只要构建脚本里面能够正确理解章节的结构即可(比如通过文件名的模式来理解)。

然后process.py文件是负责构建的脚本,具体内容如下:

    import subprocess
    import json
    import os
    from string import Template  

    import sys 
    reload(sys) 
    sys.setdefaultencoding('utf8') 

    def get_file_content(filename):
        f = open(filename)
        data = f.read()
        f.close()
        return data

    meta = json.loads(get_file_content("meta.json"))

    content = ""

    for part in meta:
        content += "\part{%s}" % part['name']
        for chapter in part["chapters"]:
            content += "\chapter{%s}" % chapter["name"]
            content += subprocess.check_output(["pandoc"] + [chapter["file"]] + ["-t", "latex"])
    
    t = Template(get_file_content("template.tex"))

    f = open('output.tex', 'w')
    f.write(t.substitute({'body' : content}))
    f.close()

    os.system("xelatex output.tex")

可以看到其就是根据meta里面的描述,调用pandoc生成latex格式的内容后拼起来,并替换了template.tex文件里面的body部分,然后调用xelatex进行输出。template.tex就是tex文件的模板了,用来控制具体的输出效果,这里给一个参考的:

    \documentclass{book}
    \usepackage[BoldFont,SlantFont,CJKchecksingle]{xeCJK}
    \setCJKmainfont[BoldFont=SimHei]{SimSun}
    \setCJKmonofont{SimSun}
    \parindent 2em

    \usepackage[pagestyles]{titlesec}
    \titleformat{\part}[display]{\centering\Large\bfseries}{第 \thepart 部分}{1em}{}  
    \titleformat{\chapter}[hang]{\Large\bfseries}{第 \thechapter 章}{1em}{}
    \titleformat{\section}[hang]{\bfseries}{第 \thesection 节}{1em}{}      
    \titleformat{\subsection}[hang]{\bfseries}{第 \thesubsection 小节}{1em}{}      

    \title {测试的主题}
    \author {作者}
 
    \begin{document}
    \maketitle
    \tableofcontents

    $body

    \end{document}

下一步

  • 试着自己来写一本书吧。
  • 写书的时候还要考虑一些别的章节,比如前言和附录,为了简单起见,这些内容这里面都没有处理,为你的书加上这些内容。
  • 如果你的书本里面有代码,试着写个脚本把代码抽取出来进行自动的测试,使得他们编译,运行都无误,如果注释里面有测试数据,还应该可以进行自动化的验证执行结果。这样你就避免了很多书籍里面都能见到的错误。