// pcettf.go
// Programmed by Taicho
// 2021/08/16

package main

import (
    "fmt"
    "os"
    "strconv"
    "strings"
	"regexp"
    "bufio"
    "flag"
    "encoding/json"
    "io/ioutil"
    "os/exec"
    "path/filepath"
)

type (
	FontSize int
)

const (
	font12 FontSize = iota
	font16
	font12a8
	font16k12
)

var font_size_name_mapping = map[FontSize]string{
    font12:		"12",
    font16:		"16",
    font12a8:	"12(a8)",
    font16k12:	"16(k12)",
}

type DataSetting struct {
	Cp932Table			string	`json:"cp932Table"`
	BdfHeader12			string	`json:"bdfHeader12"`
	BdfHeader16			string	`json:"bdfHeader16"`
	BdfFont6x12			string	`json:"bdfFont6x12"`
	BdfFont8x16			string	`json:"bdfFont8x16"`
	BdfFont8x12			string	`json:"bdfFont8x12"`
	FontForgeTemplate	string	`json:"fontForgeTemplate"`
}

type BatchSetting struct {
	PotraceExe			string	`json:"potraceExe"`
	FontForgeExe		string	`json:"fontForgeExe"`
	TtxExe				string	`json:"ttxExe"`
	TtxXml				string	`json:"ttxXml"`
	EditedTtxXml		string	`json:"editedTtxXml"`
	FontVersion			string	`json:"fontVersion"`
}

type JsonSetting struct {
	OutputBdf			string	`json:"outputBdf"`
	FontForgeScript		string	`json:"fontForgeScript"`
	FontForgeProject	string	`json:"fontForgeProject"`
	WorkTtf				string	`json:"workTtf"`
	PbmDir				string	`json:"pbmDir"`
	Data				DataSetting		`json:"data"`
	Batch				BatchSetting	`json:"batch"`
}

var c2u_mapping = make(map[uint32]uint32)
var ank_mapping = make(map[uint32]uint32)

var font_type FontSize
var font_height int
var font_width int
var font_width_ank int
var image_version string

var pbm_dir string
var json_setting JsonSetting

var verbose_mode bool

// システムカードのイメージファイルのバージョンを取得する
func getver(b []byte) string {
	if b[0x005a] != 0x4c {
		return ""
	}
	
	var jmp int = int(b[0x005b]) + int(b[0x005c]) * 256 - 0xe000

	if b[jmp] != 0xae || b[jmp + 3] != 0xac {
		return ""
	}
	
	var ver_h int = int(b[jmp + 1]) + int(b[jmp + 2]) * 256 - 0xe000
	var ver_l int = int(b[jmp + 4]) + int(b[jmp + 5]) * 256 - 0xe000

	var ver string = fmt.Sprintf("%x.%02x", b[ver_h], b[ver_l])
	var ver_len int = len(ver)
	if ver[ver_len - 1] == '0' {
		ver = ver[:ver_len - 1]
	}
	return ver
}

func (size FontSize) String() string {
    s, exist := font_size_name_mapping[size]

    if exist {
        return s
    } else {
	    panic(size)
	}
}

// 半角文字用BDFファイルの変換テーブルを設定する
func init_ank_mapping() {
	if font_type == font12a8 || font_type == font16k12 {
		// k8x12
		ank_mapping = map[uint32]uint32{
			'　': ' ',
			'！': '!',
			'”': '"',
			'＃': '#',
			'＄': '$',
			'％': '%',
			'＆': '&',
			'’': '\'',
			'（': '(',
			'）': ')',
			'＊': '*',
			'＋': '+',
			'，': ',',
			'－': '-',
			'．': '.',
			'／': '/',
			'０': '0',
			'１': '1',
			'２': '2',
			'３': '3',
			'４': '4',
			'５': '5',
			'６': '6',
			'７': '7',
			'８': '8',
			'９': '9',
			'：': ':',
			'；': ';',
			'＜': '<',
			'＝': '=',
			'＞': '>',
			'？': '?',
			'＠': '@',
			'Ａ': 'A',
			'Ｂ': 'B',
			'Ｃ': 'C',
			'Ｄ': 'D',
			'Ｅ': 'E',
			'Ｆ': 'F',
			'Ｇ': 'G',
			'Ｈ': 'H',
			'Ｉ': 'I',
			'Ｊ': 'J',
			'Ｋ': 'K',
			'Ｌ': 'L',
			'Ｍ': 'M',
			'Ｎ': 'N',
			'Ｏ': 'O',
			'Ｐ': 'P',
			'Ｑ': 'Q',
			'Ｒ': 'R',
			'Ｓ': 'S',
			'Ｔ': 'T',
			'Ｕ': 'U',
			'Ｖ': 'V',
			'Ｗ': 'W',
			'Ｘ': 'X',
			'Ｙ': 'Y',
			'Ｚ': 'Z',
			'［': '[',
			'＼': '\\',
			'］': ']',
			'＾': '^',
			'＿': '_',
			'｀': '`',
			'ａ': 'a',
			'ｂ': 'b',
			'ｃ': 'c',
			'ｄ': 'd',
			'ｅ': 'e',
			'ｆ': 'f',
			'ｇ': 'g',
			'ｈ': 'h',
			'ｉ': 'i',
			'ｊ': 'j',
			'ｋ': 'k',
			'ｌ': 'l',
			'ｍ': 'm',
			'ｎ': 'n',
			'ｏ': 'o',
			'ｐ': 'p',
			'ｑ': 'q',
			'ｒ': 'r',
			'ｓ': 's',
			'ｔ': 't',
			'ｕ': 'u',
			'ｖ': 'v',
			'ｗ': 'w',
			'ｘ': 'x',
			'ｙ': 'y',
			'ｚ': 'z',
			'｛': '{',
			'｜': '|',
			'｝': '}',
			'～': '~',
		}
	} else {
		ank_mapping = make(map[uint32]uint32)
		for i := 0x20; i < 0x7f; i++ {
			ank_mapping[uint32(i)] = uint32(i)
		}
	}
}

// CP932→UNICODE変換テーブル（CP932.TXT）を読み込む
func read_c2u_mapping_table(table_filename string) {
	cfile, err := os.Open(table_filename)
    if err != nil {
        panic(err)
    }
    defer cfile.Close()
	cs := bufio.NewScanner(cfile)

    r1 := regexp.MustCompile(`^0x(\w+)\s+0x(\w+)`)

	for cs.Scan() {
		var s string = cs.Text()
		if !r1.MatchString(s) {
			continue
		}
		v := r1.FindStringSubmatch(s)
		c, _ := strconv.ParseInt(v[1], 16, 32)
		u, _ := strconv.ParseInt(v[2], 16, 32)
		
		var c1 uint32 = uint32((c & 0xff00) >> 8)
		var c2 uint32 = uint32(c & 0xff)
		
		// シフトJISからJISに変換
		var j uint32 = 0
		if c1 == 0 {
			j = c1
		} else {
			if c2 < 0x9f {
				if c1 <= 0x9f {
				    j = (c1 - 0x81) * 2 + 0x21
				} else {
				    j = (c1 - 0xe0) * 2 + 0x5f
				}
				if c2 > 0x7e {
				    j = j << 8 + (c2 - 0x20)
				} else {
				    j = j << 8 + (c2 - 0x1f)
				}
			} else {
				if c1 <= 0x9f {
				    j = (c1 - 0x81) * 2 + 0x21 + 1
				} else {
				    j = (c1 - 0xe0) * 2 + 0x5f + 1
				}
			    j = j << 8 + (c2 - 0x7e)
			}
		}
		
		c2u_mapping[uint32(j)] = uint32(u)
	}
}

func out_bdf_header(header_filename string, writer *bufio.Writer) {
    header, err := ioutil.ReadFile(header_filename)
    if err != nil {
        panic(err)
    }

	writer.Write(header)
}

// 12ドットフォントを取得する
func create_pattern_pce12(b []byte, pos *int, c [][]byte) {
		var pos2 int = *(pos)
		for y := 0; y < 12; y++ {
			var bit uint32
			if ((y % 2) == 0) {
				bit = uint32(b[pos2]) << 4 | uint32(b[pos2 + 1] & 0xf0) >> 4
			} else {
				bit = uint32(b[pos2 + 1] & 0x0f) << 8 | uint32(b[pos2 + 2])
				pos2 = pos2 + 3
			}
			var mask uint32 = 0x800
			for x := 0; x < 12; x++ {
				if ((bit & mask) != 0){
					c[y][x] = 1
				} else {
					c[y][x] = 0
				}
				mask = mask >> 1
			}
		}
		
		if (verbose_mode) {
			for y := 0; y < 12; y++ {
				for x := 0; x < 12; x++ {
					fmt.Print(c[y][x])
				}
				fmt.Println()
			}
			fmt.Println()
		}
}

// 16ドットフォントを取得する
func create_pattern_pce16(b []byte, pos *int, c [][]byte) {
		var pos2 int = *(pos)
		for y := 0; y < 16; y++ {
			var bit uint32
			bit = uint32(b[pos2]) << 8 | uint32(b[pos2 + 1])
			pos2 = pos2 + 2
			var mask uint32 = 0x8000
			for x := 0; x < 16; x++ {
				if ((bit & mask) != 0){
					c[y][x] = 1
				} else {
					c[y][x] = 0
				}
				mask = mask >> 1
			}
		}

		if (verbose_mode) {
			for y := 0; y < 16; y++ {
				for x := 0; x < 16; x++ {
					fmt.Print(c[y][x])
				}
				fmt.Println()
			}
			fmt.Println()
		}
}

// BDFファイルに1文字出力する
func out_bdf(code int, c [][]byte, f *bufio.Writer) {
	var ysize = len(c)
	var xsize = len(c[0])
	var swidth int
	
	switch (font_type) {
	case font12:
		if xsize == 6 {
			swidth = 512
		} else {
			swidth = 1024
		}
	case font12a8:
		if xsize == 8 {
			swidth = 341 * 2
		} else {
			swidth = 1024
		}
	case font16, font16k12:
		if xsize == 8 {
			swidth = 512
		} else {
			swidth = 1024
		}
	}

    fmt.Fprintf(f, "STARTCHAR %04x\n", code)
    fmt.Fprint(f, "ENCODING ", code, "\n")
    fmt.Fprintf(f, "SWIDTH %d 0%s", swidth, "\n")
    fmt.Fprintf(f, "DWIDTH %d 0%s", xsize, "\n")
    fmt.Fprintf(f, "BBX %d %d 0 -2%s", xsize, ysize, "\n")
    fmt.Fprint(f, "BITMAP", "\n")

	for y := 0; y < ysize; y++ {
		var bit uint32 = 0
		for x := 0; x < len(c[y]); x++ {
			bit = bit << 1
			if c[y][x] != 0 {
				bit = bit | 1
			}
		}
		if (len(c[y]) % 8) != 0 {
			bit = bit << (8 - (len(c[y]) % 8))
		}
		fmt.Fprintf(f, "%0" + strconv.Itoa(int((len(c[y]) + 7) / 8) * 2)  + "x\n", bit)
	}

    fmt.Fprint(f, "ENDCHAR", "\n")
}

// pbmファイルに1文字出力する
func out_pbm(code int, c [][]byte) {
	var ysize = len(c)
	var xsize = len(c[0])

    f, err := os.OpenFile(filepath.Join(pbm_dir, fmt.Sprintf("u%04x.pbm", code)), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer f.Close()

	ofile := bufio.NewWriter(f)

    fmt.Fprint(ofile, "P1", "\n")
    fmt.Fprintf(ofile, "%d %d%s", xsize * 4, ysize * 4, "\n")

	for y := 0; y < ysize; y++ {
	    for i := 0; i < 4; i++ {
			for x := 0; x < len(c[y]); x++ {
				fmt.Fprint(ofile, c[y][x])
				fmt.Fprint(ofile, c[y][x])
				fmt.Fprint(ofile, c[y][x])
				fmt.Fprint(ofile, c[y][x])
			}
			fmt.Fprint(ofile, "\n")
		}
	}
	
	ofile.Flush()
}

// 全角データの変換を行う
func out_pce(b []byte, pos *int, code int, count int, f *bufio.Writer) {
	var font_size int
	if font_type == font16 {
		font_size = 16
	} else {
		font_size = 12
	}

    var c [][]byte = make([][]byte, font_size)
	for i := 0; i < font_size; i++ {
		c[i] = make([]byte, font_size)
	}
    
    for i := 0; i < count; i++ {
    	if font_type == font16 {
		    create_pattern_pce16(b, pos, c)
	    } else {
		    create_pattern_pce12(b, pos, c)
	    }
	    
	    var pc *[][]byte
	    
	    if font_type == font16k12 {
		    var c2 [][]byte = make([][]byte, 16)
			for i := 0; i < 2; i++ {
				c2[i] = make([]byte, 16)
				for j := 0; j < 16; j++ {
					c2[i][j] = 0
				}
			}
			for i := 0; i < 12; i++ {
				c2[i + 2] = make([]byte, 16)
				c2[i + 2][0] = 0
				c2[i + 2][1] = 0
				copy(c2[i + 2][2:13], c[i][0:11])
				c2[i + 2][14] = 0
				c2[i + 2][15] = 0
			}
			for i := 14; i < 16; i++ {
				c2[i] = make([]byte, 16)
				for j := 0; j < 16; j++ {
					c2[i][j] = 0
				}
			}
			pc = &c2
	    } else {
	    	pc = &c
	    }
	    
	    var ucode uint32 = c2u_mapping[uint32(code)]
    	out_bdf(int(ucode), *pc, f)
    	out_pbm(int(ucode), *pc)
    
		code = code + 1
        if (code & 0xff) == 0x7f {
            code = code + (0x121 - 0x7f)
        }

		if font_type == font16 {
	        *(pos) += 32
		} else {
	        *(pos) += 18
	    }
    }
}

// 半角データの変換を行う
func out_afont(ank_bdf_file string, f *bufio.Writer) {
    r1 := regexp.MustCompile(`^ENCODING (\d+)`)
    r2 := regexp.MustCompile(`^BBX (\d+) (\d+) (-?\d+) (-?\d+)`)

	afile, err := os.Open(ank_bdf_file)
    if err != nil {
        panic(err)
    }
	in := bufio.NewScanner(afile)
    
	for in.Scan() {
		var s string = in.Text()
		if !r1.MatchString(s) {
			continue
		}
		
		startchar, _ := strconv.ParseUint(r1.FindStringSubmatch(s)[1], 10, 16)
		var code int = int(startchar)

		r_code, exist := ank_mapping[uint32(code)]
		if !exist {
			continue
		}

		var bbx_x, bbx_y int
		var bbx_width int
		var bbx_height int
		bbx_x, bbx_y = 0, 0
		bbx_width = 0
		bbx_height = 0

		for in.Scan() {
			var s string = in.Text()

			if r2.MatchString(s) {
				bbx_width, _ = strconv.Atoi(r2.FindStringSubmatch(s)[1])
				bbx_height, _ = strconv.Atoi(r2.FindStringSubmatch(s)[2])
				bbx_x, _ = strconv.Atoi(r2.FindStringSubmatch(s)[3])
				bbx_y, _ = strconv.Atoi(r2.FindStringSubmatch(s)[4])
			} else if in.Text() == "BITMAP" {
				break
			}
		}

		var c [][]byte = make([][]byte, 0)
		for i := 0; i < font_height; i++ {
			c = append(c, make([]byte, font_width_ank))
			for x := 0; x < font_width_ank; x++ {
				c[i][x] = 0
			}
		}

		var y int = font_height - bbx_height - bbx_y - 1
		if y < 0 {
			y = 0
		} else if y + bbx_height >= font_height {
			y = font_height - bbx_height
		}

		for in.Scan() {
			var s string = in.Text()
			if s == "ENDCHAR" {
				break
			}
			
			h, _ := strconv.ParseInt(s, 16, 32)
			var bit uint32 = uint32(h)
			var mask uint32 = 0x80
			for x := 0; x < bbx_width; x++ {
				if ((bit & mask) != 0){
					c[y][x + bbx_x] = 1
				} else {
					c[y][x + bbx_x] = 0
				}
				mask = mask >> 1
			}
			y++
		}

	    var pc *[][]byte
	    
	    if font_type == font16k12 {
		    var c2 [][]byte = make([][]byte, 16)
			for i := 0; i < 2; i++ {
				c2[i] = make([]byte, 8)
				for j := 0; j < 8; j++ {
					c2[i][j] = 0
				}
			}
			for i := 0; i < 12; i++ {
				c2[i + 2] = make([]byte, 8)
				copy(c2[i + 2][0:7], c[i][0:7])
			}
			for i := 14; i < 16; i++ {
				c2[i] = make([]byte, 8)
				for j := 0; j < 8; j++ {
					c2[i][j] = 0
				}
			}
			pc = &c2
	    } else {
	    	pc = &c
	    }

    	out_bdf(int(r_code), *pc, f)
    	out_pbm(int(r_code), *pc)
	}
	
	afile.Close()
}

// 追加文字を出力する
func out_extra(writer *bufio.Writer) {
	var extra12 []byte = []byte {
		0x00, 0x07, 0xc2, 0x04, 0x20, 0x42, 0x04, 0x20, 0x42, 0x7f, 0xe4, 0x40, 0x44, 0x04, 0x40, 0x44, 0x04, 0x7e, // 卍
	}

	var extra16 []byte = []byte {
		0x3f, 0x80, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x7f, 0xff, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x00, 0xfe, // 卍
	}

	var extra *[]byte
	if (font_type == font16) {
		extra = &extra16
	} else {
		extra = &extra12
	}
	
	var pos_image int = 0
    out_pce(*extra, &pos_image, 0x5244, 1, writer);
}

// FontForgeのスクリプトファイルを作成する
func output_font_forge_script(font_full_name string, bdf_filename string, script_filename string) {
	var font_family_name string
	
	if image_version != "" {
		font_family_name = fmt.Sprintf("PC Engine %sdot V%s", font_type, image_version)
	} else {
		font_family_name = fmt.Sprintf("PC Engine %sdot", font_type)
	}

    f, err := os.OpenFile(script_filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer f.Close()
	ofile := bufio.NewWriter(f)

	fmt.Fprintln(ofile, `#!/usr/bin/fontforge -script
#
# createttf.pe
#
# Copyright (c) 2015 Suzuki Taro
# Released under the MIT license
# http://opensource.org/licenses/mit-license.php
#
`)

	r := regexp.MustCompile(`[\.|\(|\)| ]`)

	fmt.Fprintf(ofile, `Open("%s")%s`, json_setting.Data.FontForgeTemplate, "\n")
	fmt.Fprintf(ofile, `Import(%s)%s`, strconv.Quote(bdf_filename), "\n")
	fmt.Fprintf(ofile, `Import(%s)%s`, strconv.Quote(filepath.Join(pbm_dir, "u*.svg")), "\n")
	fmt.Fprintln(ofile, `RenameGlyphs("Adobe Glyph List")`)

	fmt.Fprintf(ofile, `SetFontNames("%s", "%s", "%s", "Regular", " ", $1)%s`, r.ReplaceAllString(font_family_name, "_"), font_family_name, font_full_name, "\n")

	fmt.Fprintln(ofile, `SelectAll()`)
	fmt.Fprintln(ofile, `Simplify()`)
	fmt.Fprintln(ofile, `CanonicalStart()`)
	fmt.Fprintln(ofile, `CanonicalContours()`)

	fmt.Fprintf(ofile, `Save("%s")%s`, json_setting.FontForgeProject, "\n")
	fmt.Fprintf(ofile, `Generate("%s", "ttf", 4)%s`, json_setting.WorkTtf, "\n")

	fmt.Fprintln(ofile, `Close()`)
	fmt.Fprintln(ofile, `Quit()`)
	
	ofile.Flush()
}

// ttxファイルを修正する
func edit_ttx_xml(ttx_xml_filename string, edited_ttx_xml_filename string) {
	inf, err := os.Open(ttx_xml_filename)
    if err != nil {
        panic(err)
    }
    defer inf.Close()
	insc := bufio.NewScanner(inf)

	outf, err := os.OpenFile(edited_ttx_xml_filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
    if err != nil {
        panic(err)
    }
    defer outf.Close()
	outsc := bufio.NewWriter(outf)
	
    r := regexp.MustCompile(`<xAvgCharWidth (.+ )*value="(\d+)"`)
    
	for insc.Scan() {
		var s string = insc.Text()
		if r.MatchString(s) {
			var value string = r.FindStringSubmatch(s)[2]
			if verbose_mode {
				fmt.Println("xAvgCharWidth value", value)
			}
			s = strings.ReplaceAll(s, value, "1024")
		}
		fmt.Fprintln(outsc, s)
	}

	outsc.Flush()
}

func main() {
	var pos_image int
	var ank_bdf_file string
	var header_file string
	var font_name string
	var ttf_filename string

	flag_font_type := flag.String("font_type", "12", "font type 12|12k8|16|16k12")
	image_filename := flag.String("image", "system_card.pce", "image filename")
	json_filename := flag.String("config", "pcettf.json", "config (json) filename ")
	flag_batch := flag.Bool("batch", false, "batch mode")
	flag_ttf_filename := flag.String("ttf", "", "ttf filename (for batch mode only)")
	flag_verbose := flag.Bool("verbose", false, "verbose mode")

	flag.Parse()
	
	verbose_mode = *flag_verbose

	if *flag_batch {
		fmt.Println("batch_mode", true)
	}

	fmt.Println("json_filename", *json_filename)

	json_file, err := ioutil.ReadFile(*json_filename)
    if err != nil {
        panic(err)
    }

    err = json.Unmarshal(json_file, &json_setting)
    if err != nil {
        panic(err)
    }

	fmt.Println("font_type", *flag_font_type)

	switch *flag_font_type {
	case "12":
		font_type = font12
		font_width = 12
		font_height = 12
		font_width_ank = 6
		ank_bdf_file = json_setting.Data.BdfFont6x12
		header_file = json_setting.Data.BdfHeader12
		font_name = "PC Engine 12dot"
	case "16":
		font_type = font16
		font_width = 16
		font_height = 16
		font_width_ank = 8
		ank_bdf_file = json_setting.Data.BdfFont8x16
		header_file = json_setting.Data.BdfHeader16
		font_name = "PC Engine 16dot"
	case "12a8":
		font_type = font12a8
		font_width = 12
		font_height = 12
		font_width_ank = 8
		ank_bdf_file = json_setting.Data.BdfFont8x12
		header_file = json_setting.Data.BdfHeader12
		font_name = "PC Engine 12dot (Ascii 8dot)"
	case "16k12":
		font_type = font16k12
		font_width = 16
		font_height = 12
		font_width_ank = 8
		ank_bdf_file = json_setting.Data.BdfFont8x12
		header_file = json_setting.Data.BdfHeader16
		font_name = "PC Engine 16dot (Kanji 12dot)"
	default:
        panic(*flag_font_type)
	}

	if font_type == font16 {
	    pos_image = 0x10000
	} else {
	    pos_image = 0x30000
	}

	pbm_dir = json_setting.PbmDir
	_, err = os.Stat(pbm_dir)
	if os.IsNotExist(err) {
		os.MkdirAll(pbm_dir, 0777)
	}

	init_ank_mapping()
    read_c2u_mapping_table(json_setting.Data.Cp932Table)

    bdf_file, err := os.OpenFile(json_setting.OutputBdf, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer bdf_file.Close()
	bdf_writer := bufio.NewWriter(bdf_file)

	out_bdf_header(header_file, bdf_writer)

    out_afont(ank_bdf_file, bdf_writer)

	fmt.Println("image_filename", *image_filename)

    b, err := ioutil.ReadFile(*image_filename)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

	image_version = getver(b)
	if image_version != "" {
		fmt.Println("image_version", image_version)
		font_name = font_name + " Ver." + image_version
	}

    out_pce(b, &pos_image, 0x2121, 94, bdf_writer)
    out_pce(b, &pos_image, 0x2221, 14, bdf_writer)
    out_pce(b, &pos_image, 0x2330, 10, bdf_writer)
    out_pce(b, &pos_image, 0x2341, 26, bdf_writer)
    out_pce(b, &pos_image, 0x2361, 26, bdf_writer)
    out_pce(b, &pos_image, 0x2421, 83, bdf_writer)
    out_pce(b, &pos_image, 0x2521, 86, bdf_writer)
    out_pce(b, &pos_image, 0x2621, 24, bdf_writer)
    out_pce(b, &pos_image, 0x2641, 24, bdf_writer)
    out_pce(b, &pos_image, 0x2721, 33, bdf_writer)
    out_pce(b, &pos_image, 0x2751, 33, bdf_writer)
    out_pce(b, &pos_image, 0x3021, 94 * 31 + 51, bdf_writer)

	out_extra(bdf_writer)

    fmt.Fprint(bdf_writer, "ENDFONT", "\n")

	bdf_writer.Flush()
	bdf_file.Close()
	
	output_font_forge_script(font_name, json_setting.OutputBdf, json_setting.FontForgeScript)
	
	if !(*flag_batch) {
		return
	}

    fmt.Println("executing", json_setting.Batch.PotraceExe)
	exec_err := exec.Command(json_setting.Batch.PotraceExe, "-s", "-z", "black", "-a", "-1", filepath.Join(pbm_dir, "*.pbm")).Run()
    if exec_err != nil {
        panic(exec_err)
    }

    fmt.Println("executing", json_setting.Batch.FontForgeExe)
	exec_err = exec.Command(json_setting.Batch.FontForgeExe, "-script", json_setting.FontForgeScript, json_setting.Batch.FontVersion).Run()
    if exec_err != nil {
        panic(exec_err)
    }

    fmt.Println("executing", json_setting.Batch.TtxExe)
	exec_err = exec.Command(json_setting.Batch.TtxExe, "-t", "OS/2", "-o", json_setting.Batch.TtxXml, json_setting.WorkTtf).Run()
    if exec_err != nil {
        panic(exec_err)
    }
    
    edit_ttx_xml(json_setting.Batch.TtxXml, json_setting.Batch.EditedTtxXml)

	if *flag_ttf_filename == "" {
		r := regexp.MustCompile(`[\(|\)]`)
		var font_type_str string = r.ReplaceAllString(font_type.String(), "")
		if image_version != "" {
			ttf_filename = fmt.Sprintf("pcengine_%s_V%s.ttf", font_type_str, image_version)
		} else {
			ttf_filename = fmt.Sprintf("pcengine_%s.ttf", font_type_str)
		}
	} else {
		ttf_filename = *flag_ttf_filename
	}

    fmt.Println("executing", json_setting.Batch.TtxExe)
	exec_err = exec.Command(json_setting.Batch.TtxExe, "-m", json_setting.WorkTtf, "-o", ttf_filename, json_setting.Batch.EditedTtxXml).Run()
    if exec_err != nil {
        panic(exec_err)
    }

    fmt.Println("generated", ttf_filename)
}
