-
-
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') |
@dpeddi thanks, updated :)
Good job.
Why do I get this error though:
python mi2font.py unpack Mili_pro.ft
('Unpacking', 'Mili_pro.ft')
Traceback (most recent call last):
File "mi2font.py", line 102, in
unpackFont(sys.argv[2])
File "mi2font.py", line 23, in unpackFont
num_bytes_characters = (header[15] << 8) + header[14]
TypeError: unsupported operand type(s) for <<: 'str' and 'int'
@lazarosfs can't reproduce the problem here, are you using Python 3?
I tried both 2.7 and 3. it only happens on one pc. Can't figure it out.
May I ask, in Mili_pro.ft.txt after the letters follow all binary data.
Do I have to add my characters at the end or replace some japanese for example to pack correcly?
Thankx again for the great work.
As long as the order in the txt and the bmp files is the same, there should be no problem. You can either replace characters in both files or add new characters at the end, it should work :)
I haven't tried altering the chinese font file, you might need to uncomment line 71 to use the header from Mili_pro.ft instead of Mili_pro.ft.en
Fixed the problem with the inverted colors.
tomi@tomi /media/sf_github/tools-master/miband $ python miband.py unpack Mili_pro.ft.en
('Unpacking', 'Mili_pro.ft.en')
Traceback (most recent call last):
File "miband.py", line 108, in
unpackFont(sys.argv[2])
File "miband.py", line 29, in unpackFont
num_bytes_characters = (header[15] << 8) + header[14]
TypeError: unsupported operand type(s) for <<: 'str' and 'int'
@tomurbanowicz you need Python 3, doesn't seem to work on Python 2.
Updated the script to ensure the font file is packed with the characters in order.
Thanks to @lazarosfs :)
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. :)
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.
$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 8.9 (jessie)
Release: 8.9
Codename: jessie
img.width img.height properties aren't available.
[code]
--- a/mi2font.py
+++ b/mi2font.py
@@ -1,4 +1,5 @@
-#!/usr/bin/python
+#!/usr/bin/python3
+# -- coding: utf-8 --
@@ -83,8 +84,8 @@ def packFont(bmp_path, txt_path, font_path):
font_file.write(characters)
pixels = img.load()
[/code]