OSC2011 Kagawa 特設サイト

osc2011_kagawa.jpg

オープンソースカンファレンス2011 Kagawa - オープンソースの文化祭! に来ていただき、ありがとうございます。 AWK Users JP :: 日本 GNU AWK ユーザー会 では「AWK で宛名書き」を実演させていただきます。

「AWK で宛名書き」というと単なるテキストでタグシートに印刷するようなイメージを持たれている方も多いと思いますが、ここでは TrueType フォントを用いて、あたかも年賀状ソフトのように表現できることを目指しました。

ここに掲載したものは、あくまでモックアップでしかありませんが、AWK で記述されているため、これらを元に加工することも簡単だと思います。

gawk + ImageMagick

最初のものは、住所録は CSV で保存し、テキスト処理のみを AWK で行い、TrueType フォントや画像として出力する部分を ImageMagick を使ったものです。 gawk も ImageMagick も一般的な Linux ディストリビューションに含まれていますので、導入することが簡単です。

#! /usr/local/bin/gawk -f
# hagaki.awk

BEGIN {
    # CSV ファイルを扱う
    FS = ",";
}

# 1 行目に自分の住所を記述
NR == 1 {
    my_name_1       = $1;       # 自分の苗字
    my_name_2       = $2;       # 自分の名前
    my_sir_1        = $3;       # 自分の敬称 (未使用)
    my_name_3       = $4;       # 連名
    my_sir_2        = $5;       # 連名の敬称 (未使用)
    my_zip          = $6;       # 自分の郵便番号
    my_address_1    = $7;       # 自分の住所 1 (市町村)
    my_address_2    = $8;       # 自分の住所 2
    my_address_3    = $9;       # 自分の住所 3 (マンション等)
    my_phone        = $10;      # 電話番号 (未使用)
}

# 2 行目以降に相手の住所を記述
NR != 1 {
    name_1          = $1;       # 苗字
    name_2          = $2;       # 名前
    sir_1           = $3;       # 敬称
    name_3          = $4;       # 名前 (連名)
    sir_2           = $5;       # 敬称
    zip             = $6;       # 郵便番号
    address_1       = $7;       # 住所 1 (市町村)
    address_2       = $8;       # 住所 2
    address_3       = $9;       # 住所 3 (マンション等)
    phone           = $10;      # 電話番号 (未使用)

    # 郵便番号を処理
    gsub(/-/, "", zip);
    num_zip             = split(zip, \
                            char_zip, "");
    zip_cmd             = text2img(char_zip, \
                            "horizontal", 522, 205, 85, 48);

    # 住所 1 を処理
    gsub(/ /, "", address_1);
    num_address_1       = split(trans_kanji_wide_char(address_1), \
                            char_address_1, "");
    address_1_cmd       = text2img(char_address_1, \
                            "vertical", 1050, 400, 75, 70);

    # 住所 2 を処理
    gsub(/ /, "", address_2);
    num_address_2       = split(trans_kanji_wide_char(address_2), \
                            char_address_2, "");
    address_2_cmd       = text2img(char_address_2, \
                            "vertical", 950, 500, 75, 70);

    # 住所 3 を処理
    gsub(/ /, "", address_3);
    num_address_3       = split(trans_kanji_wide_char(address_3), \
                            char_address_3, "");
    address_3_cmd       = text2img(char_address_3, \
                            "vertical", 850, 500, 75, 70);

    # 苗字を処理
    gsub(/ /, "", name_1);
    num_name_1          = split(trans_kanji_wide_char(name_1), \
                            char_name_1, "");
    name_1_cmd          = text2img(char_name_1, \
                            "vertical", 600, 400, 160, 150);

    # 名前を処理
    gsub(/ /, "", name_2);
    num_name_2          = split(trans_kanji_wide_char(name_2), \
                            char_name_2, "");
    name_2_cmd          = text2img(char_name_2, \
                            "vertical", 600, 900, 160, 150);

    # 敬称を処理
    gsub(/ /, "", sir_1);
    num_sir_1           = split(trans_kanji_wide_char(sir_1), \
                            char_sir_1, "");
    sir_1_cmd           = text2img(char_sir_1, \
                            "vertical", 600, 1400, 160, 150);

    # 連名を処理
    gsub(/ /, "", name_3);
    num_name_3          = split(trans_kanji_wide_char(name_3), \
                            char_name_3, "");
    name_3_cmd          = text2img(char_name_3, \
                            "vertical", 400, 900, 160, 150);

    # 連名の敬称を処理
    gsub(/ /, "", sir_2);
    num_sir_2           = split(trans_kanji_wide_char(sir_2), \
                            char_sir_2, "");
    sir_2_cmd           = text2img(char_sir_2, \
                            "vertical", 400, 1400, 160, 150);

    # 自分の郵便番号を処理
    gsub(/-/, "", my_zip);
    num_my_zip          = split(my_zip, \
                            char_my_zip, "");
    my_zip_cmd          = text2img(char_my_zip, \
                            "horizontal", 50, 1470, 50, 48);

    # 自分の住所 1 を処理
    gsub(/ /, "", my_address_1);
    num_my_address_1    = split(trans_kanji_wide_char(my_address_1), \
                            char_my_address_1, "");
    my_address_1_cmd    = text2img(char_my_address_1, \
                            "vertical", 300, 700, 55, 50);

    # 自分の住所 2 を処理
    gsub(/ /, "", my_address_2);
    num_my_address_2    = split(trans_kanji_wide_char(my_address_2), \
                            char_my_address_2, "");
    my_address_2_cmd    = text2img(char_my_address_2, \
                            "vertical", 240, 800, 55, 50);

    # 自分の住所 3 を処理
    gsub(/ /, "", my_address_3);
    num_my_address_3    = split(trans_kanji_wide_char(my_address_3), \
                            char_my_address_3, "");
    my_address_3_cmd    = text2img(char_my_address_3, \
                            "vertical", 180, 800, 55, 50);

    # 自分の苗字を処理
    gsub(/ /, "", my_name_1);
    num_my_name_1       = split(trans_kanji_wide_char(my_name_1), \
                            char_my_name_1, "");
    my_name_1_cmd       = text2img(char_my_name_1, \
                            "vertical", 100, 900, 65, 60);

    # 自分の名前を処理
    gsub(/ /, "", my_name_2);
    num_my_name_2       = split(trans_kanji_wide_char(my_name_2), \
                            char_my_name_2, "");
    my_name_2_cmd       = text2img(char_my_name_2, \
                            "vertical", 100, 1100, 65, 60);

    # 自分の連名を処理
    gsub(/ /, "", my_name_3);
    num_my_name_3       = split(trans_kanji_wide_char(my_name_3), \
                            char_my_name_3, "");
    my_name_3_cmd       = text2img(char_my_name_3, \
                            "vertical", 20, 1100, 65, 60);

    system("convert postcard.jpg -font tanuki.ttf " \
            zip_cmd " " \
            address_1_cmd " " \
            address_2_cmd " " \
            address_3_cmd " " \
            name_1_cmd " " \
            name_2_cmd " " \
            sir_1_cmd " " \
            name_3_cmd " " \
            sir_2_cmd " " \
            my_zip_cmd " " \
            my_address_1_cmd " " \
            my_address_2_cmd " " \
            my_address_3_cmd " " \
            my_name_1_cmd " " \
            my_name_2_cmd " " \
            my_name_3_cmd " test.png");

}

# ImageMagick のコマンドへの変換
function text2img(arr, direction, init_pos_x, init_pos_y, pitch, font_size,\
    text, coo_x, coo_y, str_text, arr_text, i, idx, draw_str) {

    str_text = "ぁぃぅぇぉゃゅょっァィゥェォャュョッ。、";
    split(str_text, arr_text, "");

    for (i = 1; i <= length(arr); i++) {
        text = arr[i];
        coo_x = (direction == "vertical" ? init_pos_x : \
                init_pos_x + pitch * (i - 1));
        coo_y = (direction == "horizontal" ? init_pos_y : \
                init_pos_y + pitch * (i - 1));

        for (idx in arr_text) {
            if (text == arr_text[idx]) {
                coo_x = coo_x + font_size / 3;
            }
        }

        draw_str    = draw_str " -draw \"text " coo_x "," coo_y \
                        " '" text "'\"";

    }

    return " -pointsize " font_size " " draw_str;
}

# 漢数字への変換
function trans_kanji_wide_char(str) {
    str = gensub(/0/, "〇", "g", str);
    str = gensub(/1/, "一", "g", str);
    str = gensub(/2/, "二", "g", str);
    str = gensub(/3/, "三", "g", str);
    str = gensub(/4/, "四", "g", str);
    str = gensub(/5/, "五", "g", str);
    str = gensub(/6/, "六", "g", str);
    str = gensub(/7/, "七", "g", str);
    str = gensub(/8/, "八", "g", str);
    str = gensub(/9/, "九", "g", str);
    str = gensub(/-/, "|", "g", str);
    str = gensub(/ー/, "|", "g", str);

    return str;
}

実際に用いたデモをご用意しました。

xgawk + GD

次に紹介するのは開発版の gawk である xgawk の GD による記述です。 前述のものが system 関数で ImageMagick を呼び出しているのに対し、実装された GD を使っていますので、高速に処理できることがメリットです。

#! /usr/local/bin/xgawk -f
# nengajou.awk - generate nengajou from csv file

@load gd

BEGIN {
    str["my_family_name"]     = "△△△";
    str["my_given_name1"]      = "△△△";
    str["my_given_name2"]      = "△△△";
    str["my_honorific_title"] = "";
    str["my_zip_code"]        = "123-4567";
    str["my_address1"]        = "△△△県△△△市△△△区";
    str["my_address2"]        = "△△△△△-△△-△△";
    str["my_address3"]        = "△△△△△△△△△△";

    #-----------------------------------------------------------------
    # フォントファイル名を指定
    font_name   = "ryouzan.ttf";
    image_file  = "postcard.jpg";
    output_file = "test_2.png";

    FS = ",";

}

$0 !~ /^#/ {

    image_handler = gdImageCreateFromFile(image_file);
    color = gdImageColorAllocate(image_handler, 0, 0, 0);

    str["family_name"]     = "";   # 名字
    str["given_name1"]     = "";   # 名前1
    str["given_name2"]     = "";   # 名前2
    str["zip_code"]        = "";   # 郵便番号
    str["address1"]        = "";   # 住所1
    str["address2"]        = "";   # 住所2
    str["address3"]        = "";   # 住所3
    str["telephone"]       = "";   # 電話番号

    str["family_name"]     = $1;   # 名字
    str["given_name1"]     = $2;   # 名前1
    str["given_name2"]     = $3;   # 名前2
    str["zip_code"]        = $4;   # 郵便番号
    str["address1"]        = $5;   # 住所1
    str["address2"]        = $6;   # 住所2
    str["address3"]        = $7;   # 住所3
    str["telephone"]       = $8;   # 電話番号
    str["honorific_title"] = "様";   # 敬称

    if (length(str["given_name1"]) == 1 && length(str["given_name2"]) == 0) {
        str["given_name1"] = " " str["given_name1"];
    }
    if (length(str["given_name2"]) == 1 && length(str["given_name2"]) == 0) {
        str["given_name2"] = " " str["given_name2"];
    }

    str["zip_code"] = gensub(/-/, "", 1, str["zip_code"]);
    str["zip_code"] = trans_wide_char(str["zip_code"]);
    str["address1"] = trans_kanji_wide_char(str["address1"]);
    str["address2"] = trans_kanji_wide_char(str["address2"]);
    str["address3"] = trans_kanji_wide_char(str["address3"]);

    str["my_zip_code"] = gensub(/-/, "", 1, str["my_zip_code"]);
    str["my_zip_code"] = trans_wide_char(str["my_zip_code"]);
    str["my_address1"] = trans_kanji_wide_char(str["my_address1"]);
    str["my_address2"] = trans_kanji_wide_char(str["my_address2"]);
    str["my_address3"] = trans_kanji_wide_char(str["my_address3"]);

    #-----------------------------------------------------------------
    # 郵便番号
    # 郵便番号用設定
    {
        font_size["zip_code"]  = 48;
        init_pos_x["zip_code"] = 502;
        init_pos_y["zip_code"] = 205;
        pitch["zip_code"]      = 86;
    }
    text2img("zip_code", "horizontal");

    #-----------------------------------------------------------------
    # 郵便番号 (自分)
    # 郵便番号用設定
    {
        font_size["my_zip_code"]  = 32;
        init_pos_x["my_zip_code"] = 37;
        init_pos_y["my_zip_code"] = 1500;
        pitch["my_zip_code"]      = 50;
    }
    text2img("my_zip_code", "horizontal");

    #-----------------------------------------------------------------
    # 住所1
    # 住所1用設定
    {
        font_size["address1"]  = 70;
        init_pos_x["address1"] = 1050;
        init_pos_y["address1"] = 400;
        pitch["address1"]      = font_size["address1"] * 1.2;
    }
    text2img("address1", "vertical");

    #-----------------------------------------------------------------
    # 住所1 (自分)
    # 住所1用設定
    {
        font_size["my_address1"]  = 40;
        init_pos_x["my_address1"] = 354;
        init_pos_y["my_address1"] = 700;
        pitch["my_address1"]      = font_size["my_address1"] * 1.2;
    }
    text2img("my_address1", "vertical");

    #-----------------------------------------------------------------
    # 住所2
    # 住所2用設定
    {
        font_size["address2"]  = 60;
        init_pos_x["address2"] = 950;
        init_pos_y["address2"] = 500;
        pitch["address2"]      = font_size["address2"] * 1.2;
    }
    text2img("address2", "vertical");

    #-----------------------------------------------------------------
    # 住所2 (自分)
    # 住所2用設定
    {
        font_size["my_address2"]  = 40;
        init_pos_x["my_address2"] = 267;
        init_pos_y["my_address2"] = 800;
        pitch["my_address2"]      = font_size["my_address2"] * 1.2;
    }
    text2img("my_address2", "vertical");

    #-----------------------------------------------------------------
    # 住所3
    # 住所3用設定
    {
        font_size["address3"]  = 60;
        init_pos_x["address3"] = 850;
        init_pos_y["address3"] = 600;
        pitch["address3"]      = font_size["address3"] * 1.2;
    }
    text2img("address3", "vertical");

    #-----------------------------------------------------------------
    # 住所3 (自分)
    # 住所3用設定
    {
        font_size["my_address3"]  = 40;
        init_pos_x["my_address3"] = 195;
        init_pos_y["my_address3"] = 800;
        pitch["my_address3"]      = font_size["my_address3"] * 1.2;
    }
    text2img("my_address3", "vertical");

    #-----------------------------------------------------------------
    # 名字
    # 名字用設定
    {
        font_size["family_name"]  = 120;
        init_pos_x["family_name"] = 600;
        init_pos_y["family_name"] = 400;
        pitch["family_name"]      = font_size["family_name"] * 1.7;
    }
    text2img("family_name", "vertical");

    #-----------------------------------------------------------------
    # 名字 (自分)
    # 名字用設定
    {
        font_size["my_family_name"]  = 60;
        init_pos_x["my_family_name"] = 100;
        init_pos_y["my_family_name"] = 900;
        pitch["my_family_name"]      = font_size["my_family_name"] * 1.7;
    }
    text2img("my_family_name", "vertical");

    #-----------------------------------------------------------------
    # 名前
    # 名前用設定
    {
        font_size["given_name1"]  = 120;
        init_pos_x["given_name1"] = 600;
        init_pos_y["given_name1"] =  1020;
        pitch["given_name1"]      = font_size["given_name1"] * 1.7;
    }
    text2img("given_name1", "vertical");

    #-----------------------------------------------------------------
    # 敬称
    # 敬称用設定
    {
        font_size["honorific_title"]  = 120;
        init_pos_x["honorific_title"] = 600;
        init_pos_y["honorific_title"] = 1570;
        pitch["honorific_title"]      = font_size["honorific_title"] * 1.7;
    }
    text2img("honorific_title", "vertical");

    if (str["given_name2"] != "") {
        #-----------------------------------------------------------------
        # 名前
        # 名前用設定
        {
            font_size["given_name2"]  = 120;
            init_pos_x["given_name2"] = 450;
            init_pos_y["given_name2"] =  1020;
            pitch["given_name2"]      = font_size["given_name2"] * 1.7;
        }
        if (length(str["given_name2"]) == 4) {
            font_size["given_name2"]  = font_size["given_name2"] * 0.7;
            pitch["given_name2"]      = font_size["given_name2"] * 1.7;
        }
        text2img("given_name2", "vertical");

        #-----------------------------------------------------------------
        # 敬称
        # 敬称用設定
        {
            font_size["honorific_title"]  = 120;
            init_pos_x["honorific_title"] = 450;
            init_pos_y["honorific_title"] = 1570;
            pitch["honorific_title"]      = font_size["honorific_title"] * 1.7;
        }
        text2img("honorific_title", "vertical");
    }

    #-----------------------------------------------------------------
    # 名前 (自分)
    # 名前用設定
    {
        font_size["my_given_name1"]  = 60;
        init_pos_x["my_given_name1"] = 100;
        init_pos_y["my_given_name1"] = 1230;
        pitch["my_given_name1"]      = font_size["my_given_name1"] * 1.7;
    }
    text2img("my_given_name1", "vertical");

    #-----------------------------------------------------------------
    # 名前 (自分)
    # 名前用設定
    {
        font_size["my_given_name2"]  = 60;
        init_pos_x["my_given_name2"] = 35;
        init_pos_y["my_given_name2"] = 1230;
        pitch["my_given_name2"]      = font_size["my_given_name2"] * 1.7;
    }
    text2img("my_given_name2", "vertical");


    gdImagePngName(image_handler, output_file);
    gdImageDestroy(image_handler);

    ##system("/usr/bin/eog test.png");
}

#---------------------------------------------------------------------
# 1 byte 文字幅を 2 byte 幅文字 (漢数字) に変更
function trans_kanji_wide_char(str) {
    str = gensub(/0/, "〇", "g", str);
    str = gensub(/1/, "一", "g", str);
    str = gensub(/2/, "二", "g", str);
    str = gensub(/3/, "三", "g", str);
    str = gensub(/4/, "四", "g", str);
    str = gensub(/5/, "五", "g", str);
    str = gensub(/6/, "六", "g", str);
    str = gensub(/7/, "七", "g", str);
    str = gensub(/8/, "八", "g", str);
    str = gensub(/9/, "九", "g", str);
    str = gensub(/-/, "|", "g", str);
    str = gensub(/ー/, "|", "g", str);

    return str;
}

#---------------------------------------------------------------------
# 1 byte 文字幅を 2 byte 幅文字 (漢数字) に変更
function trans_wide_char(str) {
    str = gensub(/0/, "0", "g", str);
    str = gensub(/1/, "1", "g", str);
    str = gensub(/2/, "2", "g", str);
    str = gensub(/3/, "3", "g", str);
    str = gensub(/4/, "4", "g", str);
    str = gensub(/5/, "5", "g", str);
    str = gensub(/6/, "6", "g", str);
    str = gensub(/7/, "7", "g", str);
    str = gensub(/8/, "8", "g", str);
    str = gensub(/9/, "9", "g", str);
    str = gensub(/-/, "|", "g", str);
    str = gensub(/ー/, "|", "g", str);

    return str;
}

#---------------------------------------------------------------------
# 文字を画像に変換
function text2img(key, direction, \
    text, coo_x, coo_y, str_text, arr_text, i, idx) {

    str_text = "ぁぃぅぇぉゃゅょっァィゥェォャュョッ。、";
    split(str_text, arr_text, "");

    for (i = 1; i <= length(str[key]); i++) {
        text = substr(str[key], i, 1);
        coo_x = (direction == "vertical" ? init_pos_x[key] : \
                init_pos_x[key] + pitch[key] * (i - 1));
        coo_y = (direction == "horizontal" ? init_pos_y[key] : \
                init_pos_y[key] + pitch[key] * (i - 1));

        for (idx in arr_text) {
            if (text == arr_text[idx]) {
                coo_x = coo_x + font_size[key] / 3;
            }
        }
        gdImageStringFT(image_handler, brect, color, font_name, \
                    font_size[key], 0, coo_x, coo_y, text);
    }
}