|
|
nono 1.7.0
//
// CGROM.DAT 生成
//
// Copyright (C) 2012-2022 Tetsuya Isaki
//
#include "header.h"
#include <limits.h>
#include <fcntl.h>
#include <iconv.h>
#include <array>
#include <vector>
#define HOWMANY(x, y) (((x) + (y - 1)) / (y))
#define warning(...) do { \
printf("Warning: "); \
printf(__VA_ARGS__); \
} while (0)
// フォント種別
enum fonttype_t {
FONT_16x16,
FONT_8x8,
FONT_8x16,
FONT_12x12,
FONT_12x24,
FONT_24x24,
FONT_6x12,
FONT_MAX,
};
// 範囲
#define R_CTRL (0x0001) // 制御文字
#define R_ASCII (0x0002) // ASCII(20-7F)
#define R_KANA (0x0004) // カナ(A0-DF)
#define R_HIRA (0x0008) // ひら(86-BF,E0-FF)
#define R_80 (0x0010) // \(0x80)
#define R_81 (0x0020) // 〜(0x81)
#define R_82 (0x0040) // |(0x82)
#define R_SWITCH (R_80 | R_81 | R_82)
#define R_ALL (0x007f) // 全部
// BDF 形式で使われるバウンディングボックス
struct BBX {
int w;
int h;
int x;
int y;
};
// 1文字分のフォントデータ
struct font_t {
int code;
std::array<uint8, 3 * 24> pattern;
};
// エラーが起きたら即終了する
class File
{
public:
bool Create(const std::string& filename_) {
fd = open(filename_.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
err(1, "open: %s", filename_.c_str());
}
return true;
}
void Close() {
close(fd);
}
ssize_t Write(const void *buf, size_t len) {
auto r = write(fd, buf, len);
if (r < 0) {
err(1, "File.Write(%d)", fd);
}
return r;
}
off_t Seek(off_t off) {
auto r = lseek(fd, off, SEEK_SET);
if (r < 0) {
err(1, "File.Seek(%d, %jd)", fd, (intmax_t)off);
}
return r;
}
private:
int fd {};
};
// グローバル変数
static std::string fontdir;
static File cgrom;
// クラス構造
//
// Font : すべてのフォントの基底
// |
// +- BDF : BDF形式ファイルを読み込むすべての基底クラス
// |
// +- BDF_1byte : 文字コードマッピングが不要な1バイトフォントを出力
// | |
// | +- BDF_1byte_unicode : Unicode から1バイトフォントを出力
// | |
// | +- BDF_1byte_jis0208 : JIS X 0208 から1バイトフォントを出力
// |
// +- BDF_kanji_jis0208 : JIS X 0208 から漢字フォントを出力する
//
// フォント基底クラス
//
class Font
{
public:
Font(const std::string& filename_, int, int, int);
virtual ~Font();
void SetRange(int flag) { range_flag = flag; }
bool debug {};
protected:
bool Open();
void Close();
static bool IsCtrl(int c) { return (c < 0x20); }
static bool IsAscii(int c) { return ((0x20 <= c) && (c < 0x7f)); }
static bool IsKana(int c) { return ((0xa0 <= c) && (c < 0xe0)); }
static bool IsHira(int c) {
if ((0x86 <= c) && (c < 0xa0))
return true;
if ((0xe0 <= c) && (c <= 0xff))
return true;
return false;
}
bool IsRange(int code) const;
int pattern_len {}; // 1文字分のバイト数
int offset {};
std::string filename {};
FILE *fp {};
int range_flag {}; // 出力範囲フラグ
};
// コンストラクタ
Font::Font(const std::string& filename_, int font_x, int font_y, int out_offset)
{
range_flag = R_ALL;
// 引数
filename = filename_;
pattern_len = HOWMANY(font_x, 8) * font_y;
offset = out_offset;
printf("generate %dx%d from %s\n", font_x, font_y, filename.c_str());
// ここでもうオープンする
Open();
}
// デストラクタ
Font::~Font()
{
Close();
}
// オープン
bool
Font::Open()
{
std::string pathname = ::fontdir + "/" + filename;
fp = fopen(pathname.c_str(), "r");
if (fp == NULL) {
printf("Cannot open: %s\n", pathname.c_str());
throw 0;
}
return true;
}
// クローズ
void
Font::Close()
{
if (fp) {
fclose(fp);
}
fp = NULL;
}
// 文字コードが今回出力を許可された範囲内か
bool
Font::IsRange(int code) const
{
if ((range_flag & R_CTRL) && IsCtrl(code)) {
return true;
}
if ((range_flag & R_ASCII) && IsAscii(code)) {
return true;
}
if ((range_flag & R_KANA) && IsKana(code)) {
return true;
}
if ((range_flag & R_HIRA) && IsHira(code)) {
return true;
}
if ((range_flag & R_80) && (code == 0x80)) {
return true;
}
if ((range_flag & R_81) && (code == 0x81)) {
return true;
}
if ((range_flag & R_82) && (code == 0x82)) {
return true;
}
return false;
}
//
// BDF 基底クラス
//
class BDF : public Font
{
using inherited = Font;
public:
BDF(const std::string& filename_, int, int, int);
virtual ~BDF() override;
void Convert();
protected:
bool ReadProperties();
bool ReadNextChar(font_t& data);
virtual int GetAddr(int code) const; // CGROM 収録アドレスを求める
static int Unicode2JIS(int unicode);
static int JIS2Addr(int code);
BBX fontbbx {}; // FONTBOUNDINGBOX。.w = 横サイズ、.h = 縦サイズ
int font_descent {};
};
// コンストラクタ
BDF::BDF(const std::string& filename_, int font_width, int font_height,
int out_offset)
: inherited(filename_, font_width, font_height, out_offset)
{
// ここでプロパティを読んでおく
ReadProperties();
}
// デストラクタ
BDF::~BDF()
{
}
// BDF のプロパティ (ヘッダみたいなもの) を読み込む
bool
BDF::ReadProperties()
{
char buf[100];
char *p;
int pixel_size; // 縦サイズ
int quad_width; // 横サイズ
pixel_size = -1;
quad_width = -1;
while (fgets(buf, sizeof(buf), fp)) {
if (strncmp(buf, "FONTBOUNDINGBOX ", 15) == 0) {
// フォントの縦横サイズ
fontbbx.w = strtol(buf + 15, &p, 10);
fontbbx.h = strtol(p, NULL, 10);
} else if (strncmp(buf, "PIXEL_SIZE ", 11) == 0) {
// フォントの縦サイズ
pixel_size = strtol(buf + 11, NULL, 10);
} else if (strncmp(buf, "QUAD_WIDTH ", 11) == 0) {
// フォントの横サイズ
quad_width = strtol(buf + 11, NULL, 10);
} else if (strncmp(buf, "FONT_DESCENT ", 13) == 0) {
font_descent = strtol(buf + 13, NULL, 10);
} else if (strncmp(buf, "ENDPROPERTIES", 13) == 0) {
break;
}
}
// PIXEL_SIZE, QUAD_WIDTH のほうを優先。
// TTF→BDF では FONTBOUNDINGBOX がうまく適切な値にならないため。
// 元から BDF なフォントでは大抵同じ値になってるはず。
if (pixel_size != -1) {
fontbbx.h = pixel_size;
}
if (quad_width != -1) {
fontbbx.w = quad_width;
}
return true;
}
// BDF から次の1文字を読み込んで data に格納する。
// 取り出せなければ false を返す。
bool
BDF::ReadNextChar(font_t& data)
{
char buf[100];
BBX bbx = {0};
bool in_bitmap;
int y = 0;
int dwidth = 0;
in_bitmap = false;
while (fgets(buf, sizeof(buf), fp)) {
if (strncmp(buf, "STARTCHAR ", 10) == 0) {
data.code = strtol(buf + 10, NULL, 16);
dwidth = 0;
memset(&data.pattern[0], 0, data.pattern.size());
} else if (strncmp(buf, "ENCODING ", 9) == 0) {
data.code = strtol(buf + 9, NULL, 10);
} else if (strncmp(buf, "DWIDTH ", 7) == 0) {
dwidth = strtol(buf + 7, NULL, 10);
if (dwidth > 24) {
printf("unsupported DWIDTH: %d\n", dwidth);
throw 0;
}
} else if (strncmp(buf, "BBX ", 4) == 0) {
char *p = buf + 4;
bbx.w = strtol(p, &p, 10);
bbx.h = strtol(p, &p, 10);
bbx.x = strtol(p, &p, 10);
bbx.y = strtol(p, &p, 10);
if (bbx.w > dwidth) {
warning("char %x: BBX.w(%d) > DWIDTH(%d)\n",
data.code, bbx.w, dwidth);
}
if (bbx.h > fontbbx.h) {
warning("char %x: BBX.h(%d) > FONTBBX.h(%d)\n",
data.code, bbx.h, fontbbx.h);
}
} else if (strncmp(buf, "BITMAP", 6) == 0) {
if (dwidth == 0) {
printf("BITMAP without DWIDTH!\n");
throw 0;
}
in_bitmap = true;
// BBX による縦方向の開始行を調整
y = fontbbx.h - font_descent; // 上から原点までの行数
y -= bbx.y; // 上から BBX 底辺までの行数
y -= bbx.h; // 上から BBX 上辺までの行数
y *= (dwidth + 7) / 8; // バイト数に
} else if (strncmp(buf, "ENDCHAR", 7) == 0) {
in_bitmap = false;
return true;
} else if (in_bitmap) {
if (y > pattern_len) {
printf("char %x: pattern %d > %d bytes\n",
data.code, y, pattern_len);
throw 0;
}
int len = strlen(buf) - 1; // '\n'分を引く
uint32 val = strtol(buf, NULL, 16);
// val を左詰めしてから
val <<= (4 - (len / 2)) * 8;
// BBX による横方向の位置調整
val >>= bbx.x;
// 1行を構成するバイト列を出力。等幅フォントを作っているので
// グリフごとの幅(DWIDTH)ではなく出力したいフォントの幅。
data.pattern[y++] = (val >> 24) & 0xff;
if (fontbbx.w > 8) {
data.pattern[y++] = (val >> 16) & 0xff;
}
if (fontbbx.w > 16) {
data.pattern[y++] = (val >> 8) & 0xff;
}
}
}
// EOF なので取り出す文字がない
return false;
}
// 変換メイン
void
BDF::Convert()
{
font_t data;
// フォントを1文字ずつ読み込んで
while (ReadNextChar(data)) {
// CGROM 収録アドレスを求めて書き出す
int addr = GetAddr(data.code);
if (addr >= 0) {
cgrom.Seek(offset + addr * pattern_len);
cgrom.Write(&data.pattern[0], pattern_len);
}
}
}
// CGROM 収録アドレスを得る
int
BDF::GetAddr(int code) const
{
// デフォルトはそのまま返す
return code;
}
// Unicode を JIS に変換する
/*static*/ int
BDF::Unicode2JIS(int unicode)
{
auto cd = iconv_open("iso-2022-jp", "unicode");
if (cd == (iconv_t)-1) {
printf("iconv_open failed\n");
return 0;
}
// ホストバイトオーダー4バイトのコードポイント
const char *src = (const char *)&unicode;
size_t srcleft = sizeof(unicode);
// JIS は前後のエスケープも含む
std::array<char, 16> dstbuf;
char *dst = dstbuf.data();
size_t dstlen = dstbuf.size();
ICONV(cd, &src, &srcleft, &dst, &dstlen);
iconv_close(cd);
int jiscode = ((unsigned int)dstbuf[3] << 8) + (unsigned int)dstbuf[4];
return jiscode;
}
// JIS コードから CGROM 収録コードを求めて返す
/*static*/ int
BDF::JIS2Addr(int code)
{
int jh, jl, addr;
// JIS コード 7425、7426 (区点84-05、84-06) の2文字は
// JIS X 0208:1990 で追加されたものなので、CGROM にはない。
if (code > 0x7424) {
return -1;
}
jh = (code >> 8);
jl = (code & 0xff);
// (Unicodeから変換した)コードが JIS の範囲外なら無視
if (jl < 0x21 || jl >= 0x7f) {
return -1;
}
if (jh < 0x30) {
addr = (jh - 0x21) * 94 + (jl - 0x21);
} else {
addr = (jh - 0x28) * 94 + (jl - 0x21);
}
return addr;
}
//
// BDF フォントから1バイトフォントを作るクラス。
// 文字コードは ASCII/ISO-8859 か JIS X 0201 など
// そのまま1バイトコードにマップできるもの。
//
class BDF_1byte : public BDF
{
using inherited = BDF;
public:
BDF_1byte(const std::string& filename_, int, int, int);
virtual ~BDF_1byte() override;
int GetAddr(int code) const override;
};
// コンストラクタ
BDF_1byte::BDF_1byte(const std::string& filename_, int font_x, int font_y,
int out_offset)
: inherited(filename_, font_x, font_y, out_offset)
{
}
// デストラクタ
BDF_1byte::~BDF_1byte()
{
}
// CGROM 収録アドレスを返す
int
BDF_1byte::GetAddr(int code) const
{
// 指定範囲に入っているかどうかだけ
if (IsRange(code)) {
return code;
}
return -1;
}
//
// BDF 日本語フォントから1バイトフォントを作るクラス。
// 文字コードは Unicode。
//
class BDF_1byte_unicode : public BDF_1byte
{
using inherited = BDF_1byte;
public:
BDF_1byte_unicode(const std::string& filename_, int, int, int);
virtual ~BDF_1byte_unicode() override;
int GetAddr(int code) const override;
protected:
static const wchar_t table_uni[];
};
// コンストラクタ
BDF_1byte_unicode::BDF_1byte_unicode(const std::string& filename_,
int font_x, int font_y, int out_offset)
: inherited(filename_, font_x, font_y, out_offset)
{
}
// デストラクタ
BDF_1byte_unicode::~BDF_1byte_unicode()
{
}
// CGROM 収録アドレスを返す
int
BDF_1byte_unicode::GetAddr(int code) const
{
for (int i = 0; i < 256; i++) {
if (code == table_uni[i]) {
return inherited::GetAddr(i);
}
}
return -1;
}
// Unicode を1バイト文字コードに変換するテーブル。
// 制御記号(0x01〜0x1b)、矢印(0x1c〜0x1f)、チルダ(0x81)、パイプ(0x82)は
// ここでは扱わない。
const wchar_t BDF_1byte_unicode::table_uni[] =
L" " // +00
L" →←↑↓" // +10
L" !”#$%&’()*+,−./" // +20
L"0123456789:;<=>?" // +30
L"@ABCDEFGHIJKLMNO" // +40
L"PQRSTUVWXYZ[¥]^_" // +50
L"`abcdefghijklmno" // +60
L"pqrstuvwxyz{|} ̄ " // +70
L"\ をぁぃぅぇぉゃゅょっ" // +80
L" あいうえおかきくけこさしすせそ" // +90
L" 。「」、・ヲァィゥェォャュョッ" // +a0
L"ーアイウエオカキクケコサシスセソ" // +b0
L"タチツテトナニヌネノハヒフヘホマ" // +c0
L"ミムメモヤユヨラリルレロワン゛゜" // +d0
L"たちつてとなにぬねのはひふへほま" // +e0
L"みむめもやゆよらりるれろわん "; // +f0
//
// BDF 日本語フォントから1バイトフォントを作るクラス。
// 文字コードは JIS X 0208 (の非漢字部分)。
//
class BDF_1byte_jis0208 : public BDF_1byte_unicode
{
using inherited = BDF_1byte_unicode;
public:
BDF_1byte_jis0208(const std::string& filename_, int, int, int);
virtual ~BDF_1byte_jis0208() override;
int GetAddr(int code) const override;
protected:
static std::array<int, 256> table_jis;
static bool table_jis_prepared;
};
// JIS コードを1バイト文字コードに変換するテーブル
std::array<int, 256> BDF_1byte_jis0208::table_jis;
// テーブルが用意できているか
bool BDF_1byte_jis0208::table_jis_prepared;
// コンストラクタ
BDF_1byte_jis0208::BDF_1byte_jis0208(const std::string& filename_,
int font_x, int font_y, int out_offset)
: inherited(filename_, font_x, font_y, out_offset)
{
// 最初に一度だけ table_uni から table_jis を用意しておく
if (!table_jis_prepared) {
for (int i = 0, end = table_jis.size(); i < end; i++) {
int unicode = table_uni[i];
table_jis[i] = Unicode2JIS(unicode);
}
table_jis_prepared = true;
}
}
// デストラクタ
BDF_1byte_jis0208::~BDF_1byte_jis0208()
{
}
// CGROM 収録アドレスを求める
int
BDF_1byte_jis0208::GetAddr(int code) const
{
for (int i = 0, end = table_jis.size(); i < end; i++) {
if (code == table_jis[i]) {
return i;
}
}
return -1;
}
//
// BDF フォントから漢字フォントを作るクラス。
// 文字コードは JIS X 0208。
//
class BDF_kanji_jis0208 : public BDF
{
using inherited = BDF;
public:
BDF_kanji_jis0208(const std::string& filename_, int, int, int);
virtual ~BDF_kanji_jis0208() override;
int GetAddr(int code) const override;
};
// コンストラクタ
BDF_kanji_jis0208::BDF_kanji_jis0208(const std::string& filename_,
int font_x, int font_y, int out_offset)
: inherited(filename_, font_x, font_y, out_offset)
{
}
// デストラクタ
BDF_kanji_jis0208::~BDF_kanji_jis0208()
{
}
// CGROM 収録コードを求めて返す
int
BDF_kanji_jis0208::GetAddr(int code) const
{
// JIS コードを変換するだけ
return JIS2Addr(code);
}
//
// アプリケーション
//
static void MakeCGROM(const std::string& outfile);
static void MakeMon(const std::string& outfile);
int
main(int ac, char *av[])
{
int c;
std::string outfile;
bool opt_m;
::fontdir = "./fonts";
outfile = "./cgrom.dat";
opt_m = false;
while ((c = getopt(ac, av, "f:ho:m")) != -1) {
switch (c) {
case 'f':
::fontdir = optarg;
break;
case 'o':
outfile = optarg;
break;
case 'm':
opt_m = true;
break;
case 'h':
default:
errx(1, "usage: [-f ./font] [-o ./cgrom.dat] [-m]");
}
}
if (opt_m) {
MakeMon(outfile);
} else {
MakeCGROM(outfile);
}
return 0;
}
// X680x0 CGROM を作成する
void
MakeCGROM(const std::string& outfile)
{
// 出力ファイルを空で作成
printf("output file: %s\n", outfile.c_str());
cgrom.Create(outfile);
cgrom.Seek(768 * 1024 - 1);
cgrom.Write("", 1);
try {
BDF_1byte_jis0208 r8("misaki_gothic-hikanji.bdf", 8, 8, 0x3a000);
r8.Convert();
BDF_1byte o8("nono-8x8.bdf", 8, 8, 0x3a000);
o8.Convert();
BDF_1byte_jis0208 r12("shnmk12min-hikanji.bdf", 12, 12, 0x3b800);
r12.Convert();
BDF_1byte o12x12("nono-12x12.bdf", 12, 12, 0x3b800);
o12x12.Convert();
BDF_1byte h8("milkjf_8x16r.bdf", 8, 16, 0x3a800);
h8.Convert();
BDF_1byte o8x16("nono-8x16.bdf", 8, 16, 0x3a800);
o8x16.Convert();
BDF_1byte h6("shnm6x12r.bdf", 6, 12, 0xbf400);
h6.SetRange(R_ASCII | R_KANA);
h6.Convert();
BDF_1byte o6("nono-6x12.bdf", 6, 12, 0xbf400);
o6.Convert();
BDF_1byte_unicode t6("ume-tmo3-6x12-hira.bdf", 6, 12, 0xbf400);
t6.SetRange(R_HIRA);
t6.Convert();
BDF_kanji_jis0208 k16("shnmk16min.bdf", 16, 16, 0);
k16.Convert();
BDF_1byte h12("hibiya-12x24.bdf", 12, 24, 0x3d000);
h12.SetRange(R_ASCII);
h12.Convert();
BDF_1byte o24("nono-12x24.bdf", 12, 24, 0x3d000);
o24.Convert();
BDF_kanji_jis0208 k24("hibiya-24x24.bdf", 24, 24, 0x40000);
k24.Convert();
BDF_1byte_unicode t12("ume-tmo3-12x24-kana.bdf", 12, 24, 0x3d000);
t12.SetRange(R_HIRA | R_KANA);
t12.Convert();
} catch (...) {
}
cgrom.Close();
}
// モニタウィンドウ用 12dot フォントデータ
void
MakeMon(const std::string& outfile)
{
// 出力ファイルを空で作成
printf("output file: %s\n", outfile.c_str());
cgrom.Create(outfile);
try {
BDF_kanji_jis0208 k12("knmzn12x.bdf", 12, 12, 0);
k12.Convert();
} catch (...) {
}
cgrom.Close();
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.