
在給網友答題時突然間想到的,要把點陣字庫拿出來研究研究的,適逢國慶佳節,剛好用它來列印國慶節祝福語,以表達我對偉大祖國生日的致敬!
??一段老代碼
以下是一段上世紀90年代比較流行的C代碼,從字庫檔案里讀取字模然后還原成點陣輸出:
#include <stdio.h>
#include <conio.h>
const unsigned char bit[8]={128,64,32,16,8,4,2,1};
unsigned char buffer[32];
unsigned long offset;
unsigned int q,w;
int x,y,qw;
void display(char *hz) {
FILE *hzk;
qw = *((int *)hz);
q = (qw&0x00FF)-0xA1;
w = ((qw>>8)&0x00FF)-0xA1;
offset = q*0x5E + w;
offset *= 32;
if ((hzk = fopen("HZK16","rb"))==NULL) {
printf("Can not open file HZK16!\n");
return;
}
fseek(hzk,offset,SEEK_SET);
fread(buffer,1,32,hzk);
fclose(hzk);
for (y=0;y<16;y++) {
printf(" ");
for (x=0;x<16;x++) {
if (buffer[y*2+x/8] & bit[x%8]) {
printf("%s","*");
} else {
printf(" ");
}
}
printf("\n");
}
printf("\n");
}
int main() {
display("國"); display("慶"); display("節");
getch();
}
以上代碼在 Dev-C++ 5.11(TDM-GCC 4.9.2 64-bit) 上通過編譯,效果如下:
??點陣字庫原理
點陣字庫是比較古老的技術,上世紀90年代初個人PC剛剛出現,中文大多都是用點陣字庫來記錄的,讀出漢字字模后用于顯示和列印,有12點陣、16點陣、24點陣以及32點陣等,1992年時,我見到了當時學校唯一一臺486的康柏電腦,安裝的是Win3.1中文版,也是我第一次見到有圖形界面的作業系統,后來很多年后才由現在廣泛使用的矢量字庫逐漸替代點陣字庫,不過現在還有銀行等單位在用針式列印機列印憑證、報表等,里面固化的應該還是點陣字庫,
以下原理性表述來源于網路:
HZK16字庫是符合GB2312標準的16×16點陣字庫,HZK16的GB2312-80支持的漢字有6763個,符號682個,其中一級漢字有 3755個,按聲序排列,二級漢字有3008個,按偏旁部首排列,HZK16字庫里的16×16漢字一共需要256個點來顯示,也就是說需要32個位元組才能達到顯示一個普通漢字的目的,
一個GB2312漢字是由兩個位元組編碼的,范圍為0xA1A1~0xFEFE,A1-A9為符號區,B0-F7為漢字區,每一個區有94個字符(注意:這只是編碼的許可范圍,不一定都有字型對應,比如符號區就有很多編碼空白區域),
下面以漢字"我"為例,介紹如何在HZK16檔案中找到它對應的32個位元組的字模資料,前面說到一個漢字占兩個位元組,這兩個中前一個位元組為該漢字的區號,后一個位元組為該字的位號,其中,每個區記錄94個漢字,位號為該字在該區中的位置,所以要找到"我"在hzk16庫中的位置就必須得到它的區碼和位碼,
區碼: 漢字的第一個位元組-0xA0
位碼: 漢字的第二個位元組-0xA0漢字編碼是從0xA0區開始的, 所以檔案最前面就是從0xA0區開始, 要算出相對區碼,這樣就可以得到漢字在HZK16中的絕對偏移位置: offset=(94*(區碼-1)+(位碼-1))*32
注解:
區碼減1是因為陣列是以0為開始而區號位號是以1為開始的
(94*(區號-1)+位號-1)是一個漢字字模占用的位元組數
最后乘以32是因為一個漢字要用到32個位元組來存盤,
注:在上面C代碼中所用到的及以下python代碼中將會用到的,就是上述文字中所講到的字體檔案“HZK16”,可以到網上搜索“UCDOS HZK16下載”來獲得,
??改寫C代碼
實作用python顯示點陣字庫
def hzk_offset(hz):
q,w = list(bytes(hz,encoding='gbk'))
return ((q-161)*94+w-161)*32
def hz_buffer(hzstr):
buffers = []
f = open('d:\\hzk16','rb')
for z in hzstr:
f.seek(hzk_offset(z),0)
buffers.append(f.read(32))
f.close()
return buffers
def hz_print(hzstr):
buffers = hz_buffer(hzstr)
bit = [2**i for i in range(7,-1,-1)]
for buffer in buffers:
for y in range(16):
for x in range(16):
if buffer[y*2+x//8]&bit[x%8]:
print(".",end='\n' if x==15 else '')
else:
print(' ',end='\n' if x==15 else '')
print()
if __name__ == '__main__':
hz_print('國慶快樂')
上述代碼運行的效果為下圖左一,中間和右邊的效果只要把 hz_print() 函式中第一句print()輸出字串分別改為"★"和“***”,第二句print()輸出字串分別改2個和3個空格,

??改進python代碼
把文字豎排輸出改進成橫排輸出,重點是重組字模陣列的順序:

代碼如下:
def hzk_offset(hz):
q,w = list(bytes(hz,encoding='gbk'))
return ((q-161)*94+w-161)*32
def hz_buffer(hzstr):
buffers = []
f = open('d:\\hzk16','rb')
for z in hzstr:
f.seek(hzk_offset(z),0)
buffers.append(list(f.read(32)))
f.close()
return buffers
def hz_print(hzstr):
global tmp
buffers = hz_buffer(hzstr)
bit = [2**i for i in range(7,-1,-1)]
tmp = ['']*16
for n,buffer in enumerate(buffers):
for y in range(16):
for x in range(16):
if buffer[y*2+x//8]&bit[x%8]:
tmp[y] += '*'
else:
tmp[y] += ' '
for t in tmp: print(t)
if __name__ == '__main__':
print()
hz_print('祝福偉大祖國')
print()
hz_print('更加繁榮昌盛')
print()
hz_print('我愛你,中國!')
print()
由于控制臺的輸出的文字間隔比較大,所以顯得有點粗壯,如果把這些點陣資訊還原到圖形界面中去,就可以縮小間隔顯示成很小的字;甚至可以讀取32點陣字庫,這樣文字會顯得更圓潤飽滿,

源代碼如下:
import pygame,sys
from pygame import *
WIDTH,HEIGHT = 640,480
WHITE = (255,255,255)
BLACK = (0,0,0)
RED = (255,0,0)
def hzk_offset(hz):
q,w = list(bytes(hz,encoding='gbk'))
return ((q-161)*94+w-161)*32
def hz_buffer(hzstr):
buffers = []
f = open('d:\\hzk16','rb')
for z in hzstr:
f.seek(hzk_offset(z),0)
buffers.append(list(f.read(32)))
f.close()
return buffers
def hz_matrix(hzstr,BOLD=0):
global tmp
buffers = hz_buffer(hzstr)
bit = [2**i for i in range(7,-1,-1)]
tmp = ['']*16
if BOLD==0:
str = '1','0'
else:
str = '11','00'
for n,buffer in enumerate(buffers):
for y in range(16):
for x in range(16):
if buffer[y*2+x//8]&bit[x%8]:
tmp[y] += str[0]
else:
tmp[y] += str[1]
return tmp
def draw_hz(hzstr,x,y,d=1,color=BLACK,BOLD=0):
matrix = hz_matrix(hzstr,BOLD)
for j,mat in enumerate(matrix):
for i,dot in enumerate(list(mat)):
if dot=='1':
pos_on_screen, radius = (x+i*d, y+j*d), d
draw.circle(screen, color, pos_on_screen, radius)
if __name__ == '__main__':
pygame.init()
screen = display.set_mode((WIDTH,HEIGHT),0,32)
display.set_caption("祝大家國慶節快樂!")
screen.fill(WHITE)
timer = pygame.time.Clock()
draw_hz('祝福偉大祖國', 80,30)
draw_hz('更加繁榮昌盛', 80,50)
draw_hz('我愛你,中國!',80,70)
draw_hz('祝福偉大祖國', 80,120,2,RED)
draw_hz('更加繁榮昌盛', 80,160,2,RED)
draw_hz('我愛你,中國!',80,200,2,RED)
draw_hz('祝福偉大祖國', 80,280,2,RED,1)
draw_hz('更加繁榮昌盛', 80,330,2,RED,1)
draw_hz('我愛你,中國!',80,380,2,RED,1)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
timer.tick(60)
display.update()

熱烈慶祝中華人民共和國成立72周年!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/305695.html
標籤:python
上一篇:Python資料分析筆記(上)
