漢字フォントの表示と拡張
2023.10.16
YouTube でも紹介しています。画像をクリックすると再生できます。
今回は漢字フォントを柔軟に活用するための仕組みを考えてみました。
美咲フォントをROM化して TFT LCD に表示する方法を扱った記事はネット上に多々存在しますが、
ここではオリジナルフォントへの拡張も踏まえ、フォントを簡単に扱えるようにしています。
まずは、漢字フォントのベースとして縦横16ドットの東雲フォントを利用します。
●東雲フォント

東雲フォント(0.9.6):[public domain]
東雲フォントは、パブリックドメイン(public domain)で提供されています。
パブリックドメインとは、知的財産権が発生していない状態または消滅した状態にあり、自由に利用可能な素材です。
東雲フォントはBDF(Bitmap Distribution Format)形式のテキストファイルで提供されています。
BDF形式
BDFについて
ダウンロードした shinonome-0.9.11p1.tar.bz2 を解凍して、展開された shnmk16min.bdf をみてみます。
STARTFONT 2.1
FONT -Shinonome-Mincho-Medium-R-Normal--16-150-75-75-C-160-JISX0208.1990-0
SIZE 16 75 75
FONTBOUNDINGBOX 16 16 0 -2 ← 文字サイズ、オフセット
STARTPROPERTIES 20
FONT_ASCENT 14
FONT_DESCENT 2
DEFAULT_CHAR 8481
COPYRIGHT "Public Domain"
FONTNAME_REGISTRY ""
FOUNDRY "Shinonome" ← フォント名
FAMILY_NAME "Mincho" ← 書体名
WEIGHT_NAME "Medium"
SLANT "R"
SETWIDTH_NAME "Normal"
ADD_STYLE_NAME ""
PIXEL_SIZE 16
POINT_SIZE 150
RESOLUTION_X 75
RESOLUTION_Y 75
SPACING "C"
AVERAGE_WIDTH 160
CHARSET_REGISTRY "JISX0208.1983"
CHARSET_ENCODING "0"
_XMBDFED_INFO "Edited with xmbdfed 4.4."
ENDPROPERTIES
CHARS 6879 ← 収録漢字数
~
STARTCHAR 3c53 ← 漢字のJISコード
ENCODING 15443
SWIDTH 960 0
DWIDTH 16 0
BBX 16 16 0 -2
| BITMAP ← 16進形式データをビット列に展開すると下記のようになります |
| 0810 | → | □□□□■□□□□□□■□□□□ |
| 0810 | → | □□□□■□□□□□□■□□□□ |
| 0a10 | → | □□□□■□■□□□□■□□□□ |
| 7214 | → | □■■■□□■□□□□■□■□□ |
| 1452 | → | □□□■□■□□□■□■□□■□ |
| 0852 | → | □□□□■□□□□■□■□□■□ |
| 0a51 | → | □□□□■□■□□■□■□□□■ |
| 1295 | → | □□□■□□■□■□□■□■□■ |
| 7f14 | → | □■■■■■■■□□□■□■□□ |
| 0934 | → | □□□□■□□■□□■■□■□□ |
| 2808 | → | □□■□■□□□□□□□■□□□ |
| 2a08 | → | □□■□■□■□□□□□■□□□ |
| 2910 | → | □□■□■□□■□□□■□□□□ |
| 2920 | → | □□■□■□□■□□■□□□□□ |
| 48c0 | → | □■□□■□□□■■□□□□□□ |
| 0b00 | → | □□□□■□■■□□□□□□□□ |
| ENDCHAR |
~
ENDFONT
STARTCHAR ~ ENDCHAR の部分は収録漢字数分繰り返されます。
JIS X 0208は、日本語表記、地名、人名などで用いられる6,879図形文字を含む、主として情報交換用の2バイト符号化文字集合を規定する日本産業規格 (JIS) です。
●Unicode対応 JIS X 0208 文字コード表
ここで別に
JIS X 0208 JIS非漢字 文字コード表 を用意します。
区 点 JIS SJIS EUC UTF-8 UTF-16 字
01 01 2121 8140 A1A1 E38080 3000
01 02 2122 8141 A1A2 E38081 3001 、
01 03 2123 8142 A1A3 E38082 3002 。
01 04 2124 8143 A1A4 EFBC8C FF0C ,
01 05 2125 8144 A1A5 EFBC8E FF0E .
01 06 2126 8145 A1A6 E383BB 30FB ・
01 07 2127 8146 A1A7 EFBC9A FF1A :
01 08 2128 8147 A1A8 EFBC9B FF1B ;
01 09 2129 8148 A1A9 EFBC9F FF1F ?
01 10 212A 8149 A1AA EFBC81 FF01 !
~~~~~~~~~~~
JISコードをキーとして、東雲BDFファイルと文字コード表をマージしてヘッダーファイルを作成すると下記のようになります。
【shnmk16min.h】
typedef struct {
prog_uint16_t jis;
prog_uint16_t sjis;
prog_uint16_t euc;
prog_uint32_t utf8;
prog_uint16_t utf16;
prog_uchar bitmap[32];
} BDF_FORMAT;
int font_num = 6965;
#define FONT_WIDTH 16
#define FONT_HEIGHT 16
const BDF_FORMAT shnmk16min[6965] PROGMEM = {
~~~~~~~~~~~
{0x3C51,0x8ECF,0xBCD1,0xE785AE,0x716E,{0x00,0x82,~,0x41,0x22}}, // 煮
{0x3C52,0x8ED0,0xBCD2,0xE7A4BE,0x793E,{0x08,0x10,~,0x08,0x00}}, // 社
{0x3C53,0x8ED1,0xBCD3,0xE7B497,0x7D17,{0x08,0x10,~,0x0b,0x00}}, // 紗
{0x3C54,0x8ED2,0xBCD4,0xE88085,0x8005,{0x00,0x80,~,0x02,0x08}}, // 者
{0x3C55,0x8ED3,0xBCD5,0xE8AC9D,0x8B1D,{0x00,0x42,~,0x24,0x22}}, // 謝
~~~~~~~~~~~
};
Download
shnmk16min.zip (解凍して使用してください)
構造体の要素をみるとわかるように、フォントのビットマップ情報は
JIS, SJIS, EUC, UTF-8, UTF-16コードで検索可能になっています。
構造体データの prog_uchar bitmap[32] のところに BDFファイルのBITMAP情報が格納されます。
例えば16進2バイト、16行のデータ
BITMAP
0810
~
0b00
は下記のように変換してセットしています。
{0x08,0x10,~,0x0b,0x00}
後述していますが、このフォント情報はSPI FLASHに格納されるため、FLASH領域を実装したESP32マイコンボードが必要になります。
同様に半角のASCII文字には、shnm8x16a.bdf を用いています。
STARTCHAR 65 ← ASCIIコード(16進)
ENCODING 101
SWIDTH 480 0
DWIDTH 8 0
BBX 8 16 0 -2
| BITMAP |
| 00 | → | □□□□□□□□ |
| 00 | → | □□□□□□□□ |
| 00 | → | □□□□□□□□ |
| 00 | → | □□□□□□□□ |
| 00 | → | □□□□□□□□ |
| 38 | → | □□■■■□□□ |
| 44 | → | □■□□□■□□ |
| 82 | → | ■□□□□□■□ |
| 82 | → | ■□□□□□■□ |
| fe | → | ■■■■■■■□ |
| 80 | → | ■□□□□□□□ |
| 82 | → | ■□□□□□■□ |
| 44 | → | □■□□□■□□ |
| 38 | → | □□■■■□□□ |
| 00 | → | □□□□□□□□ |
| 00 | → | □□□□□□□□ |
| ENDCHAR |
このBDFファイルは1バイトのASCIIコードなので、文字コード表とのマージは必要なく、
ASCIIコードとそれに続くビットマップ列になります。
【shnm8x16a.h】
typedef struct {
prog_uint8_t ascii;
prog_uchar bitmap[16];
} BDF_ASCII_FORMAT;
int font_num = 95;
#define FONT_WIDTH 8
#define FONT_HEIGHT 16
const BDF_ASCII_FORMAT shnm8x16a[95] PROGMEM = {
{0x20,{0x00,0x00,0x00,0x00,~,0x00,0x00,0x00,0x00}},
{0x21,{0x00,0x18,0x18,0x18,~,0x00,0x10,0x10,0x00}},
{0x22,{0x6c,0x24,0x24,0x48,~,0x00,0x00,0x00,0x00}},
{0x23,{0x00,0x12,0x12,0x12,~,0x48,0x48,0x48,0x00}},
{0x24,{0x10,0x38,0x54,0x92,~,0x54,0x38,0x10,0x10}},
{0x25,{0x01,0x61,0x92,0x92,~,0x49,0x49,0x86,0x80}},
{0x26,{0x00,0x38,0x44,0x44,~,0x4a,0x31,0x00,0x00}},
~~~~~~~~~~~
Download
shnm8x16a.zip (解凍して使用してください)
テスト用のシステム構成は下記のようになります。
●Seeeduino XIAO Expansion board
検証環境には、4MB FLASHを実装しているAdafruit QT Py ESP32-S2とSSD1306 OLED DISPLAYを使用しています。
ESP32-S2 と OLED DISPLAY はI2C接続です。ここでは配線の手間を省いてXIAO Expansion boardを利用しました。
Adafruit QT Py + XIAO Expansion board
Seeeduino XIAO Expansion board
●Adafruit QT Py ESP32-S2 WiFi Dev Board with STEMMA QT
・ESP32-S2 240MHz
・4 MB Flash & 2 MB PSRAM
・2.4 GHz Wi-Fi (SoC)
・Two I2C ports
・Hardware UART
・Hardware SPI
・Hardware I2S on any pins
・3.3V regulator with 600mA peak output
Adafruit QT Py ESP32-S2
●システム要件
〇漢字コードには、UTF-8を使用する。
〇漢字表示の際には、shnmk16min.h からビットマップ情報を取得する。
〇ASCII文字表示の際には、shnm8x16a.h からビットマップ情報を取得する。
〇フォントは、1 pixelずつ描画する。
〇半角・全角が混在する文字列で行末の漢字が半分欠けてしまう場合には、改行して表示する。
(128x64ドットの OLED DISPLAY を使った場合)

●開発環境
ソースコードのビルドには、PlatformIOを使用しています。
Arduino開発環境構築 PlatformIO
●ソースコード解説(抜粋)
Download
drawFonts.zip (解凍して使用してください)
フォント情報の取得
FLASH領域に保存してあるフォント情報から該当するUTF-8コードを検索します。
UTF-8最大4バイトのコードまで取得できるように、
pgm_read_dword_near()関数を使用します。
続いて、コードに紐づいているフォントのビットマップ情報を取得します。
for (int i = 0; i < font_num; i++) {
utf8code = pgm_read_dword_near(&shnmk16min[i].utf8);
if (utf8code == utf8) {
pt = (unsigned char *)shnmk16min[i].bitmap;
for (int pos = 0; pos < 32; pos++, bitmap++, pt++) {
*bitmap = pgm_read_byte_near(pt);
}
break;
}
}
ESP32-S2マイコンボードではストレスなく検索を完了しています。
改行処理
ASCIIコードが混在する文字列を表示する際、文字数のみの判断で改行すると不具合が発生します。
ASCIIコードには横8ドット、漢字には16ドット幅のフォントを用いているので、ドット数を考慮して改行処理を行います。
while(*str) {
switch(*str) {
case 0x0a:
case 0x0d:
pos_y += FONT_HEIGHT;
str++;
break;
default:
if ((*str & 0x80) == 0x00) { // 1byte文字
if ((pos_x + FONT_ASCII_WIDTH) > SCREEN_WIDTH) {
pos_x = 0;
pos_y += FONT_HEIGHT;
}
getFontAscii(bitmap, *str);
drawAscii(bitmap, pos_x, pos_y);
pos_x += (FONT_WIDTH / 2);
str++;
} else {
if ((*str & 0xE0) == 0xC0) { numByte=2; // 2byte文字
} else if ((*str & 0xF0) == 0xE0) { numByte=3; // 3byte文字
} else if ((*str & 0xF8) == 0xF0) { numByte=4; // 4byte文字
}
if ((pos_x + FONT_WIDTH) > SCREEN_WIDTH) {
pos_x = 0;
pos_y += FONT_HEIGHT;
}
for (code = *str, str++, i = 1; i < numByte; i++, str++) {
code = (code << 8) + *str;
};
getFont(bitmap, code);
drawKanji(bitmap, pos_x, pos_y);
pos_x += FONT_WIDTH;
}
break;
}
}
フォント表示
フォント表示は1ドット単位で行います。ビットに1が立っている箇所のみを書き出します。
下記は漢字の場合です。
for(int y = 0; y < 16; y++) {
for(int x = 0; x < 8; x++) {
if (*bitmap & (0x80>>x))
ssd1306.drawPixel(pos_x + x, pos_y + y, SSD1306_WHITE);
}
bitmap++;
for(int x = 0; x < 8; x++) {
if (*bitmap & (0x80>>x))
ssd1306.drawPixel(pos_x + x + 8, pos_y + y, SSD1306_WHITE);
}
bitmap++;
}
●テスト

●オリジナルフォントの作成
再度、東雲フォントBDFファイルと文字コード表をマージして作ったファイルをみてみましょう。
const BDF_FORMAT shnmk16min[6965] PROGMEM = {
~~~~~~~~~~~
{0x3C51,0x8ECF,0xBCD1,0xE785AE,0x716E,{0x00,0x82,~,0x41,0x22}}, // 煮
{0x3C52,0x8ED0,0xBCD2,0xE7A4BE,0x793E,{0x08,0x10,~,0x08,0x00}}, // 社
{0x3C53,0x8ED1,0xBCD3,0xE7B497,0x7D17,{0x08,0x10,~,0x0b,0x00}}, // 紗
{0x3C54,0x8ED2,0xBCD4,0xE88085,0x8005,{0x00,0x80,~,0x02,0x08}}, // 者
{0x3C55,0x8ED3,0xBCD5,0xE8AC9D,0x8B1D,{0x00,0x42,~,0x24,0x22}}, // 謝
~~~~~~~~~~~
};
コメント部分には漢字を表記しているので、テキストエディタなどでファイルを開いて、
漢字検索をすれば、該当するビットマップ情報を得ることができます。
{0x3C53,0x8ED1,0xBCD3,0xE7B497,0x7D17,{
| 0x08,0x10, | → | □□□□■□□□□□□■□□□□ |
| 0x08,0x10, | → | □□□□■□□□□□□■□□□□ |
| 0x0a,0x10, | → | □□□□■□■□□□□■□□□□ |
| 0x72,0x14, | → | □■■■□□■□□□□■□■□□ |
| 0x14,0x52, | → | □□□■□■□□□■□■□□■□ |
| 0x08,0x52, | → | □□□□■□□□□■□■□□■□ |
| 0x0a,0x51, | → | □□□□■□■□□■□■□□□■ |
| 0x12,0x95, | → | □□□■□□■□■□□■□■□■ |
| 0x7f,0x14, | → | □■■■■■■■□□□■□■□□ |
| 0x09,0x34, | → | □□□□■□□■□□■■□■□□ |
| 0x28,0x08, | → | □□■□■□□□□□□□■□□□ |
| 0x2a,0x08, | → | □□■□■□■□□□□□■□□□ |
| 0x29,0x10, | → | □□■□■□□■□□□■□□□□ |
| 0x29,0x20, | → | □□■□■□□■□□■□□□□□ |
| 0x48,0xc0, | → | □■□□■□□□■■□□□□□□ |
| 0x0b,0x00 | → | □□□□■□■■□□□□□□□□ |
}}, // 紗
このビットマップ配列を編集することでオリジナルフォントに書き換えることができます。
【参考文献】
【連載】よくわかる!組み込みフォント 第3回「ビットマップフォントを知ろう。」 | FONT SWITCH PROJECT
ESP32で 漢字を表示するプログラムを作ってみた(ESP32、SSD1306)
U8g2 Font List
|
Raspberry Pi(ラズベリー パイ)は、ARMプロセッサを搭載したシングルボードコンピュータ。イギリスのラズベリーパイ財団によって開発されている。
たいていのことは100日あれば、うまくいく。長田英知著
「時間がなくて、なかなか自分のやりたいことができない」
「一念発起して何かを始めても、いつも三日坊主で終わってしまう」
「色んなことを先延ばしにしたまま、時間だけが過ぎていく」
そこで本書では、そんな著者が独自に開発した、
まったく新しい目標達成メソッド「100日デザイン」について、
その知識と技術を、余すところなくご紹介します。
まんがで納得ナポレオン・ヒル 思考は現実化する
OLとして雑務をこなす日々に飽き足らず、科学者だった父が残した薬品を商品化すべく、起業を決意した内山麻由(27)。彼女はセミナーで知り合った謎の女性からサポートを得ながら、彼女と二人三脚でナポレオン・ヒルの成功哲学を実践し、さまざまな問題を乗り越えていく。
ヒル博士の<ゴールデンルール>に従い、仕事に、恋に全力疾走する彼女の、成功への物語。
今日は人生最悪で最高の日 1秒で世界を変えるたったひとつの方法 ひすいこたろう著
偉人の伝記を読むと、最悪な日は、不幸な日ではなく、新しい自分が始まる日であることがわかります。最悪な出来事は、自分の人生が、想像を超えて面白くなる兆しなのです。偉人伝を読むことで、このときの不幸があったおかげで、未来にこういう幸せがくるのかと、人生を俯瞰する視線が立ち上がるのです。
ご飯は私を裏切らない heisoku著
辛い現実から目を背けて食べるご飯は、いつも美味しく幸せを届けてくれる。
29歳、中卒、恋人いない歴イコール年齢。バイト以外の職歴もなく、短期バイトを転々とする日々。ぐるぐると思索に耽るけど、ご飯を食べると幸せになれる。奇才の新鋭・heisokuが贈るリアル労働グルメ物語!
【最新版Gemini 3に対応!】できるGemini (できるシリーズ)
Geminiを「最強の知的生産パートナー」として使いこなすための、実践的なノウハウを凝縮した一冊です。
基本的な操作方法から、具体的なビジネスシーンでの活用、日々の業務を自動化するGoogle Workspaceとの連携、さらには自分だけのオリジナルAIを作成する方法まで余すところなく解説します。
Rustプログラミング完全ガイド 他言語との比較で違いが分かる!
Rustの各手法や考え方を幅広く解説!
500以上のサンプルを掲載。実行結果も確認。
全24章の包括的なチュートリアル。
ポチらせる文章術
販売サイト・ネット広告・メルマガ・ブログ・ホームページ・SNS…
全WEB媒体で効果バツグン!
カリスマコピーライターが教える「見てもらう」「買ってもらう」「共感してもらう」すべてに効くネット文章術
小型で便利な Type-C アダプター USB C オス - USB3.1 オスアダプター
Type-C端子のマイコンボードをこのアダプタを介して直接Raspberry Piに挿すことができます。ケーブルなしで便利なツールです。
Divoom Ditoo Pro ワイヤレススピーカー
15W高音質重低音/青軸キーボード/Bluetooth5.3/ピクセルアート 専用アプリ/USB接続/microSDカード
電源供給USBケーブル スリム 【5本セット】
USB電源ケーブル 5V DC電源供給ケーブル スリム 【5本セット】 電源供給 バッテリー 修理 自作 DIY 電子工作 (100cm)
|