技術同人誌執筆中のコンテンツの抜け漏れ対策スクリプト

この記事は技術同人誌 Advent Calendar 2019の12月8日分の記事となります。

私は、今年の9月に技術書典7に初参加し、技術同人誌『RustではじめるOpenGL』を頒布させていただきました。 現在もPDF形式のダウンロード版と、紙版の両方をBOOTH.pmで販売させていただいてます。

toyamaguchi.booth.pm

ただし、紙版のほうは一部コンテンツが抜けていたことがあとから発覚しまして、別記事にて内容を通知させていただいております。

このようなコンテンツの抜け漏れがあった場合、PDF形式のダウンロード版ではすぐにアップデートをかけることができたのですが、紙版では対応が難しいところが難点です。

今回、私がAdvent Calendarのためにお伝えする内容は、このような抜け漏れ対策のためのものです。

コンテンツの抜け漏れ対策スクリプト

私がやらかしてしまったのは、章や節のタイトルは書いてあるのに、その中の本文を書き忘れた、というものです。 Re:Viewの記法でいえば、「= 章のキャプション」もしくは「== 節のキャプション」だけしか書いていなかったわけです。

印刷するまえに、より周到な確認ができていたらよかったのですが、さすがに一人で執筆していると気が回らないこともあります。 こんな状況下で書き忘れに気づくためには、ことあるごとに警告を出すようにするしかないと思いつきました。

そこでできあがったのが、コンテンツの抜け漏れ対策スクリプトです。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import yaml


def read_catalog_yaml():
    fp = open('articles/catalog.yml')
    yaml_data = yaml.load(fp)
    fp.close()

    file_name_list = list()
    for item in ['PREDEF', 'CHAPS', 'APPENDIX', 'POSTDEF']:
        if not isinstance(yaml_data[item], type(None)):
            file_name_list.extend(yaml_data[item])

    return file_name_list


def read_contents(file_name):
    contents_list = list()

    fp = open(os.path.join('articles', file_name))
    for line in fp:
        line = line.strip()
        if 0 == len(line):
            continue
        if line.startswith('=') and not line.startswith('===['):
            contents_list.append([line, 0])
        elif 0 != len(contents_list):
            contents_list[-1][1] = contents_list[-1][1] + 1
    fp.close()

    return contents_list


def main():
    file_name_list = read_catalog_yaml()
    contents_list = list()
    for file_name in file_name_list:
        contents_list.extend(read_contents(file_name))

    error_flag = False
    for contents in contents_list:
        if 0 == contents[1]:
            if False == error_flag:
                print('#########################################')
                print('# content checker found empty contents! #')
                print('#########################################')
                error_flag = True
            print('{} is empty'.format(contents[0]))

    if error_flag:
        exit(1)

    print('content checker found no issue!')
    exit(0)


if __name__ == '__main__':
    main()

解説

まずこのスクリプトはarticles/catalog.ymlを読み込み、すべての記事ファイルのリスト作ります。

そのあと、それぞれの記事ファイルを読み込み、各章・節の中に何行のコンテンツがあるのか数えていきます。 空行はカウントしないようにします。

すべてのファイルのコンテンツ(文字が存在する行の数)を数え上げ、まったくコンテンツを含まない章・節が存在した場合には、警告して終了ステータス1で終わります。 空コンテンツを発見したときの出力例はこちらです。

#########################################
# content checker found empty contents! #
#########################################
= はじめに is empty
= 著者紹介 is empty

問題なければ、その旨を表示して終了ステータス0で終わります。 空コンテンツがなかったときの出力例はこちらです。

content checker found no issue!

このアイデアは以前から持っていたのですが、Advent Calendarのために昨晩サクッと作りました。 もっとシンプルに書ける人は、自分で自作するとよいのではないでしょうか。 自作のツールはカスタマイズも楽ですし。

注意するところといえば、「=」が連続して始まる行が章・節の始まりですが、コラムの始まりと終わりは「===[column]」「===[/column]」と表せるようなので、その処理だけ区別しましょう。

まとめ

どうしても一人で書籍の執筆をしていると、執筆するほうに集中して、文章の確認作業がおろそかになったりします。 自分の代わりに随時内容をチェックしてくれる人がいたら、どれだけ助かることかと思います。

しかし、それもかなわない場合は、せめてあらかじめ予防線を張っておくことも大切です。 特にプログラムでのチェックは苦も無く確認してくれるので、ビルドするたびにチェックしたらよいと思います。

もしかすると技術同人誌を書いている多くの人が、文章校正ツールの「textlint」や、表記揺れのための「prh」を使っているかもしれません。 使っていない方は、後悔先に立たずですので、使うことを強くおすすめします。

ちなみに、この記事は私のやらかしちゃった話が元になっているわけですが、「本番環境でやらかしちゃった人 Advent Calendar 2019」というものもあって、興味深い記事もありますのでよろしければどうぞ。