-
-
Save joserebelo/b9be41b7b88774f712e2f864fdd39878 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3 | |
# -*- coding: utf-8 -*- | |
# Simple python command line tool to pack / unpack the Mi Band 2 font firmware files | |
from PIL import Image | |
import os | |
import math | |
import binascii | |
import sys | |
# Unpack the Mi Band 2 font file | |
# Creates 1bpp bmp and txt file with the characters | |
def unpackFont(font_path): | |
print('Unpacking', font_path) | |
font_file = open(font_path, 'rb') | |
txt_file = open(font_path + ".txt", 'wb') | |
# header = 16 bytes | |
header = font_file.read(16) | |
# last 2 header bytes = number of bytes used for characters | |
# 2 bytes per character, utf-16le | |
num_bytes_characters = (header[15] << 8) + header[14] | |
num_characters = num_bytes_characters // 2 | |
# read the characters | |
for i in range (0, num_characters): | |
b = font_file.read(2) | |
txt_file.write(b) | |
# spritesheet dimensions (square) | |
dim = int(math.ceil(math.sqrt(num_characters))) | |
img = Image.new('1', (dim * 16, dim * 16), 0) | |
pixels = img.load() | |
for i in range (0, num_characters): | |
# each character is a 16x16 image, 1 bit per pixel, meaning 32 bytes | |
char_bytes = font_file.read(32) | |
row = i // dim | |
col = i % dim | |
x = 0 | |
y = 0 | |
# big endian | |
for byte in char_bytes: | |
bits = [(byte >> bit) & 1 for bit in range(8 - 1, -1, -1)] | |
for b in bits: | |
pixels[16 * col + x, 16 * row + y] = b | |
x += 1 | |
if x == 16: | |
x = 0 | |
y += 1 | |
img.save(font_path + '.bmp') | |
# Create a Mi Band 2 font file from a bmp and txt | |
def packFont(bmp_path, txt_path, font_path): | |
print('Packing', font_path) | |
font_file = open(font_path, 'wb') | |
bmp_file = open(bmp_path, 'rb') | |
txt_file = open(txt_path, 'rb') | |
characters = txt_file.read() | |
num_characters = len(characters) // 2 | |
# sort the characters, keep track of each index | |
chars_arr = str(characters, 'utf-16le') | |
chars_idx = range(num_characters) | |
chars = list(sorted(zip(chars_arr, chars_idx))) | |
# header, taken from Mili_pro.ft.en | |
header = bytearray(binascii.unhexlify('484D5A4B01FFFFFFFF00FFFFFFFFCA00')) | |
# header, taken from Mili_pro.ft | |
# header = bytearray(binascii.unhexlify('484D5A4B01FFFFFFFFFFFFFFFFFF7438')) | |
l = len(characters).to_bytes(2, byteorder='big') | |
header[14] = l[1] | |
header[15] = l[0] | |
font_file.write(header) | |
img = Image.open(bmp_path) | |
cols = img.size[0] // 16 | |
rows = img.size[1] // 16 | |
img_rgb = img.convert('RGB') | |
pixels = img_rgb.load() | |
for i in range (0, num_characters): | |
font_file.write(chars[i][0].encode('utf-16le')) | |
for i in range (0, num_characters): | |
idx = chars[i][1] | |
row = idx // cols | |
col = idx % cols | |
x = 0 | |
y = 0 | |
while y < 16: | |
b = 0 | |
for i in range(0, 8): | |
if pixels[col * 16 + x, row * 16 + y] != (0, 0, 0): | |
b = b | (1 << (7 - i)) | |
x += 1 | |
if x == 16: | |
x = 0 | |
y += 1 | |
font_file.write(b.to_bytes(1, 'big')) | |
if len(sys.argv) == 3 and sys.argv[1] == 'unpack': | |
unpackFont(sys.argv[2]) | |
elif len(sys.argv) == 5 and sys.argv[1] == 'pack': | |
packFont(sys.argv[2], sys.argv[3], sys.argv[4]) | |
else: | |
print('Usage:') | |
print(' python', sys.argv[0], 'unpack Mili_pro.ft.en') | |
print(' python', sys.argv[0], 'pack Mili_pro.ft.en.bmp Mili_pro.ft.en.txt out.ft.en') |
Hi, @joserebelo!
I want to add vietnamese character! Can I add to txt file and draw at the end of bmp file?
How Miband 2 knows which character in the ft file is displayed on the screen when it received vietnamese unicode text?
Thanks.
Hi, @chienkd,
if it is still important for you, I have created tool along the lines of bipfont from https://github.com/amazfitbip/tools, and successfully created a Czech font. It uses same input characters as fonts from Amazfit BIP, so if there is already a repo/font with vietnamese characters for that, it should be able to use it.
The font was uploaded successfully to Miband2 by gadgetbridge, but I was not able to test it in an application which would send characters directly and is known to work. Gadgetbridge's test call displayed utf-8 characters outside of standard 8-bit ASCII with an extra space after them, so I don't know if it is its issue or firmware issue. So I am not sure if I should publish my script to github or not. Most apps provide transliteration to 7-bit ASCII anyway or do not support text display at all.
Thank you very much, this script finally allowed me have Georgian characters on my mi band 2, though i struggled a bit, in the end i made it, again thank you very much.
Great work again @josebelo I will test again just to be sure that the packing is done correctly when characters are not in ascending format. :)