import struct import freetype import os # Font file and output file FONT_FILE = "/Users/jeremygan/Desktop/python_dev/epaper2/websocket_server/GB2312.ttf" OUTPUT_FILE = "/Users/jeremygan/Desktop/python_dev/epaper2/GB2312-16.bin" # Font size (16x16) FONT_SIZE = 16 def create_gb2312_font(): # Load the face try: face = freetype.Face(FONT_FILE) except Exception as e: print(f"Error loading font: {e}") return # Set char size face.set_pixel_sizes(FONT_SIZE, FONT_SIZE) print(f"Generating GB2312 font file: {OUTPUT_FILE}") with open(OUTPUT_FILE, 'wb') as f: # Iterate through GB2312 code points # Area: 0xA1 - 0xFE (161 - 254) -> 94 areas # Index: 0xA1 - 0xFE (161 - 254) -> 94 chars per area count = 0 total_chars = 94 * 94 # Buffer for empty char (32 bytes of 0x00) empty_char = b'\x00' * 32 for area in range(0xA1, 0xFF): for index in range(0xA1, 0xFF): # Construct GB2312 code gb_code = bytes([area, index]) try: # Decode to unicode character char = gb_code.decode('gb2312') # Load glyph face.load_char(char, freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGET_MONO) bitmap = face.glyph.bitmap # Convert bitmap to 32 bytes (16x16 / 8) # The bitmap.buffer is a flat list of bytes. # For mono rendering, each byte is 0 or 255? No, it's packed? # FT_LOAD_TARGET_MONO packs 8 pixels into 1 byte. # We need to ensure it's 16x16. # Center the glyph in 16x16 box. glyph_width = bitmap.width glyph_rows = bitmap.rows glyph_pitch = bitmap.pitch # Create a 16x16 buffer (32 bytes) char_buffer = bytearray(32) # Calculate offsets to center x_off = (FONT_SIZE - glyph_width) // 2 # Vertical alignment is tricky. Let's use bearing Y or just center based on rows. # A better way is using face.glyph.bitmap_top # But for fixed height font generation, usually we just center or align baseline. # Let's try simple centering for now. y_off = (FONT_SIZE - glyph_rows) // 2 # Adjust y_off if it's too high/low? # Let's align to baseline approximately. # Usually baseline is at 12-13px for 16px font. # face.size.ascender might help but let's stick to bitmap center for simplicity first. # Copy bitmap to buffer src_buf = bitmap.buffer for row in range(glyph_rows): # Target row dst_row = row + y_off if dst_row < 0 or dst_row >= FONT_SIZE: continue # Source row bytes # pitch is bytes per row src_start = row * glyph_pitch # We need to copy bits. # This is getting complicated because FreeType mono bitmap format # might not match our target format exactly (MSB/LSB). # Let's iterate pixels. for col in range(glyph_width): dst_col = col + x_off if dst_col < 0 or dst_col >= FONT_SIZE: continue # Get pixel from src byte_idx = src_start + (col >> 3) bit_idx = 7 - (col & 7) pixel = (src_buf[byte_idx] >> bit_idx) & 1 if pixel: # Set pixel in dst # format: row by row, 2 bytes per row. # row 0: byte 0, byte 1 # byte 0: bits 0-7 (left to right) -> wait, usually MSB is left. dst_byte_idx = dst_row * 2 + (dst_col >> 3) dst_bit_idx = 7 - (dst_col & 7) char_buffer[dst_byte_idx] |= (1 << dst_bit_idx) f.write(char_buffer) count += 1 except Exception: # Character not found or decode error f.write(empty_char) # Progress if count % 1000 == 0: print(f"Processed {count} characters...") print(f"Done! Generated {OUTPUT_FILE} with size {os.path.getsize(OUTPUT_FILE)} bytes.") if __name__ == "__main__": create_gb2312_font()