Merge pull request #884 from Ralim/extend-font-encoding
Extend font encoding
This commit is contained in:
@@ -233,6 +233,55 @@ def getCJKGlyph(sym):
|
||||
return s
|
||||
|
||||
|
||||
def getCharsFromFontIndex(index: int) -> str:
|
||||
'''
|
||||
Converts the font table index into its corresponding string escape
|
||||
sequence(s).
|
||||
'''
|
||||
|
||||
# We want to be able to use more than 254 symbols (excluding \x00 null
|
||||
# terminator and \x01 new-line) in the font table but without making all
|
||||
# the chars take 2 bytes. To do this, we use \xF1 to \xFF as lead bytes
|
||||
# to designate double-byte chars, and leave the remaining as single-byte
|
||||
# chars.
|
||||
#
|
||||
# For the sake of sanity, \x00 always means the end of string, so we skip
|
||||
# \xF1\x00 and others in the mapping.
|
||||
#
|
||||
# Mapping example:
|
||||
#
|
||||
# 0x02 => 2
|
||||
# 0x03 => 3
|
||||
# ...
|
||||
# 0xEF => 239
|
||||
# 0xF0 => 240
|
||||
# 0xF1 0x01 => 1 * 0xFF - 15 + 1 = 241
|
||||
# 0xF1 0x02 => 1 * 0xFF - 15 + 2 = 242
|
||||
# ...
|
||||
# 0xF1 0xFF => 1 * 0xFF - 15 + 255 = 495
|
||||
# 0xF2 0x01 => 2 * 0xFF - 15 + 1 = 496
|
||||
# ...
|
||||
# 0xF2 0xFF => 2 * 0xFF - 15 + 255 = 750
|
||||
# 0xF3 0x01 => 3 * 0xFF - 15 + 1 = 751
|
||||
# ...
|
||||
# 0xFF 0xFF => 15 * 0xFF - 15 + 255 = 4065
|
||||
|
||||
assert index >= 0
|
||||
page = int((index + 14) / 0xFF)
|
||||
assert page <= 0x0F
|
||||
if page == 0:
|
||||
return "\\x%0.2X" % index
|
||||
else:
|
||||
# Into extended range
|
||||
# Leader is 0xFz where z is the page number
|
||||
# Following char is the remainder
|
||||
leader = page + 0xF0
|
||||
value = ((index + 14) % 0xFF) + 1
|
||||
assert leader <= 0xFF
|
||||
assert value <= 0xFF
|
||||
return "\\x%0.2X\\x%0.2X" % (leader, value)
|
||||
|
||||
|
||||
def getFontMapAndTable(textList):
|
||||
# the text list is sorted
|
||||
# allocate out these in their order as number codes
|
||||
@@ -242,19 +291,20 @@ def getFontMapAndTable(textList):
|
||||
forcedFirstSymbols = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||
# enforce numbers are first
|
||||
for sym in forcedFirstSymbols:
|
||||
symbolMap[sym] = "\\x%0.2X" % index
|
||||
symbolMap[sym] = getCharsFromFontIndex(index)
|
||||
index = index + 1
|
||||
totalSymbolCount = len(set(textList) | set(forcedFirstSymbols))
|
||||
# \x00 is for NULL termination and \x01 is for newline, so the maximum
|
||||
# number of symbols allowed with 8 bits is `256 - 2`.
|
||||
if totalSymbolCount > (256 - 2):
|
||||
# number of symbols allowed is as follow (see also the comments in
|
||||
# `getCharsFromFontIndex`):
|
||||
if totalSymbolCount > (0x10 * 0xFF - 15) - 2:
|
||||
log(f"Error, too many used symbols for this version (total {totalSymbolCount})")
|
||||
sys.exit(1)
|
||||
log("Generating fonts for {} symbols".format(totalSymbolCount))
|
||||
|
||||
for sym in textList:
|
||||
if sym not in symbolMap:
|
||||
symbolMap[sym] = "\\x%0.2X" % index
|
||||
symbolMap[sym] = getCharsFromFontIndex(index)
|
||||
index = index + 1
|
||||
# Get the font table
|
||||
fontTableStrings = []
|
||||
@@ -316,6 +366,7 @@ def convStr(symbolConversionTable, text):
|
||||
for c in text.replace("\\r", "").replace("\\n", "\n"):
|
||||
if c not in symbolConversionTable:
|
||||
log("Missing font definition for {}".format(c))
|
||||
sys.exit(1)
|
||||
else:
|
||||
outputString = outputString + symbolConversionTable[c]
|
||||
return outputString
|
||||
|
||||
21
Translations/make_translation_test.py
Normal file
21
Translations/make_translation_test.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import unittest
|
||||
|
||||
|
||||
class TestMakeTranslation(unittest.TestCase):
|
||||
def test_getCharsFromFontIndex(self):
|
||||
from make_translation import getCharsFromFontIndex
|
||||
self.assertEqual(getCharsFromFontIndex(2), "\\x02")
|
||||
self.assertEqual(getCharsFromFontIndex(239), "\\xEF")
|
||||
self.assertEqual(getCharsFromFontIndex(240), "\\xF0")
|
||||
self.assertEqual(getCharsFromFontIndex(241), "\\xF1\\x01")
|
||||
self.assertEqual(getCharsFromFontIndex(495), "\\xF1\\xFF")
|
||||
self.assertEqual(getCharsFromFontIndex(496), "\\xF2\\x01")
|
||||
self.assertEqual(getCharsFromFontIndex(750), "\\xF2\\xFF")
|
||||
self.assertEqual(getCharsFromFontIndex(751), "\\xF3\\x01")
|
||||
self.assertEqual(getCharsFromFontIndex(0x10 * 0xFF - 15), "\\xFF\\xFF")
|
||||
with self.assertRaises(AssertionError):
|
||||
getCharsFromFontIndex(0x10 * 0xFF - 14)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -117,15 +117,16 @@ void OLED::setFramebuffer(uint8_t *buffer) {
|
||||
* UTF font handling is done using the two input chars.
|
||||
* Precursor is the command char that is used to select the table.
|
||||
*/
|
||||
void OLED::drawChar(char c) {
|
||||
if (c == '\x01' && cursor_y == 0) { // 0x01 is used as new line char
|
||||
void OLED::drawChar(const uint16_t charCode) {
|
||||
if (charCode == '\x01' && cursor_y == 0) { // 0x01 is used as new line char
|
||||
cursor_x = 0;
|
||||
cursor_y = 8;
|
||||
return;
|
||||
} else if (c == 0) {
|
||||
} else if (charCode <= 0x01) {
|
||||
return;
|
||||
}
|
||||
uint16_t index = static_cast<unsigned char>(c) - 2; // First index is \x02
|
||||
// First index is \x02
|
||||
uint16_t index = charCode - 2;
|
||||
uint8_t *charPointer;
|
||||
charPointer = ((uint8_t *)currentFont) + ((fontWidth * (fontHeight / 8)) * index);
|
||||
drawArea(cursor_x, cursor_y, fontWidth, fontHeight, charPointer);
|
||||
@@ -234,10 +235,21 @@ void OLED::setRotation(bool leftHanded) {
|
||||
}
|
||||
|
||||
// print a string to the current cursor location
|
||||
void OLED::print(const char *str) {
|
||||
while (str[0]) {
|
||||
drawChar(str[0]);
|
||||
str++;
|
||||
void OLED::print(const char *const str) {
|
||||
const uint8_t *next = reinterpret_cast<const uint8_t *>(str);
|
||||
while (next[0]) {
|
||||
uint16_t index;
|
||||
if (next[0] <= 0xF0) {
|
||||
index = next[0];
|
||||
next++;
|
||||
} else {
|
||||
if (!next[1]) {
|
||||
return;
|
||||
}
|
||||
index = (next[0] - 0xF0) * 0xFF - 15 + next[1];
|
||||
next += 2;
|
||||
}
|
||||
drawChar(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
static void useSecondaryFramebuffer(bool useSecondary);
|
||||
|
||||
private:
|
||||
static void drawChar(char c); // Draw a character to a specific location
|
||||
static void drawChar(const uint16_t charCode); // Draw a character to the current cursor location
|
||||
static void setFramebuffer(uint8_t *buffer);
|
||||
static const uint8_t *currentFont; // Pointer to the current font used for rendering to the buffer
|
||||
static uint8_t * firstStripPtr; // Pointers to the strips to allow for buffer having extra content
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "Translation.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "main.hpp"
|
||||
#include "string.h"
|
||||
|
||||
void gui_Menu(const menuitem *menu);
|
||||
|
||||
@@ -274,8 +273,33 @@ static void printShortDescription(uint32_t shortDescIndex, uint16_t cursorCharPo
|
||||
OLED::setCursor(OLED::getCursorX() - 2, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of chars in the string excluding the null terminator.
|
||||
* This is a custom version of `strlen` which takes into account our custom
|
||||
* double-byte char encoding.
|
||||
* @param str The input string.
|
||||
* @return The length of the string.
|
||||
*/
|
||||
static uint16_t str_display_len(const char *const str) {
|
||||
const uint8_t *next = reinterpret_cast<const uint8_t *>(str);
|
||||
uint16_t count = 0;
|
||||
while (next[0]) {
|
||||
if (next[0] <= 0xF0) {
|
||||
count++;
|
||||
next++;
|
||||
} else {
|
||||
if (!next[1]) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
next += 2;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int userConfirmation(const char *message) {
|
||||
uint16_t messageWidth = FONT_12_WIDTH * (strlen(message) + 7);
|
||||
uint16_t messageWidth = FONT_12_WIDTH * (str_display_len(message) + 7);
|
||||
uint32_t messageStart = xTaskGetTickCount();
|
||||
|
||||
OLED::setFont(0);
|
||||
@@ -1144,7 +1168,7 @@ void gui_Menu(const menuitem *menu) {
|
||||
if (descriptionStart == 0)
|
||||
descriptionStart = xTaskGetTickCount();
|
||||
// lower the value - higher the speed
|
||||
int16_t descriptionWidth = FONT_12_WIDTH * (strlen(menu[currentScreen].description) + 7);
|
||||
int16_t descriptionWidth = FONT_12_WIDTH * (str_display_len(menu[currentScreen].description) + 7);
|
||||
int16_t descriptionOffset = ((xTaskGetTickCount() - descriptionStart) / (systemSettings.descriptionScrollSpeed == 1 ? (TICKS_100MS / 10) : (TICKS_100MS / 5)));
|
||||
descriptionOffset %= descriptionWidth; // Roll around at the end
|
||||
if (lastOffset != descriptionOffset) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
TRANSLATION_DIR="../Translations"
|
||||
TRANSLATION_SCRIPT="make_translation.py"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user