Kurs Arduino – Problemy z polonizacją GFX
Ostatnio (UR028) stworzyliśmy i spolonizowaliśmy font GFX, jednak przy próbie wyświetlenia napisu wystąpił błąd, a w istocie dwa pokrewne błędy. Spróbujemy poznać przyczyny i znaleźć sposoby rozwiązania problemu.
Nasze polskie litery powinny mieć kody powyżej zakresu ASCII, czyli 128…145 (0x80…0x91). A na razie tak nie jest. Już w opisie wygenerowanego fontu jest informacja, że obejmuje on tylko kody 32…126 (0x20…0x7E).
Pierwszy problem: przy realizacji większego fontu w pliku FontEdW20.h) zapomnieliśmy o kodzie numer 127 (0x7F), który uwzględniliśmy w pierwszym, mniejszym foncie FontEdW.h), żeby zachować ciągłość. To nasze przeoczenie i na pewno trzeba dodać kod 127.
Do tablicy FontEdW20Glyphs[] możemy skopiować znak spacji – kod 32 (0x20), choćby dlatego, że w kodzie podstawowym ASCII na pozycji 127 miała to być spacja nierozdzielająca (NBSP). Do tablicy FontEdW20Bitmap[] też moglibyśmy skopiować opis spacji z samego początku tej tablicy, ale akurat w przypadku spacji możemy go… pominąć.
O oto drugi problem: w naszym foncie brak symbolu tyldy (~), która powinna mieć kod 126 (0x7F)! Ostatnim znakiem jest klamra zamykająca (}), która ma numer 125. Problem tkwi więc przede wszystkim w dolnej tablicy FontEdW20Glyphs[], bo tam nie ma w ogóle pozycji dla kodu 126, który powinien wyświetlać znak tyldy (~). Nie ma też graficznego opisu wyglądu tyldy w tablicy FontEdW20Glyphs[]. To nie jest nasze przeoczenie, tylko niedoróbka przy konwersji czcionki na stronie:
http://oleddisplay.squix.ch/,
jak pokazuje to szkic 1.
Szkic 1:
// Created by http://oleddisplay.squix.ch/ Consider a donation //In case of problems make sure that you are using the font file with the correct version! const uint8_t Open_Sans_Condensed_Bold_20Bitmaps[] PROGMEM = { // Bitmap Data: 0x00, // ' ' 0xEE,0xEE,0xEE,0xEE,0x60,0x0E,0xEE, // '!' 0xEE,0xEE,0xEE,0xC6,0x44, // '"' (...) 0x7C,0x7C,0x1C,0x1C,0x18,0x38,0x30,0x70,0x70,0xFE,0xFE, // 'z' 0x0E,0x1E,0x18,0x18,0x18,0x18,0x18,0xF8,0xE0,0xF8,0x18,0x18,0x18,0x18,0x18,0x1E,0x0E,//'{' 0xDB,0x6D,0xB6,0xDB,0x6D,0xB6,0xDB,0x60, // '|' 0xE0,0xF0,0x30,0x30,0x30,0x30,0x30,0x3E,0x0E,0x3E,0x30,0x30,0x30,0x30,0x30,0xF0,0xE0 //'}' }; const GFXglyph Open_Sans_Condensed_Bold_20Glyphs[] PROGMEM = { // bitmapOffset, width, height, xAdvance, xOffset, yOffset { 0, 1, 1, 6, 0, 0 }, // ' ' { 1, 4, 14, 6, 1, -14 }, // '!' { 8, 8, 5, 10, 1, -14 }, // '"' (...) { 1286, 8, 11, 8, 0, -11 }, // 'z' { 1297, 8, 17, 8, 0, -14 }, // '{' { 1314, 3, 20, 11, 4, -15 }, // '|' { 1322, 8, 17, 8, 0, -14 } // '}' - to jest kod nr 125 (0x7D) }; const GFXfont Open_Sans_Condensed_Bold_20 PROGMEM = { (uint8_t *)Open_Sans_Condensed_Bold_20Bitmaps, GFXglyph *)Open_Sans_Condensed_Bold_20Glyphs,0x20,0x7E,28};
Aby przywrócić porządek, trzeba więc dodać do naszego fontu brakujące dwa kody o numerach 126 i 127.
A konkretnie co należy dodać?
Zastanówmy się: kodu 127 nie będziemy nigdy wykorzystywać, bo mamy zwykłą spację pod numerem 32. Znak tyldy (~) jest wykorzystywany niezmiernie rzadko i też możemy uznać, że nie będzie nam potrzebny.
Jeśli nie będziemy ich używać, to wystarczy jedynie wstawić do tablicy FontEdW20Glyphs[] dwie pozycje o wymaganej strukturze i o dowolnej zawartości. Tak – o dowolnej zawartości. Przykładowo:
{ 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0},
albo na przykład:
{ 9999, 99, 99, 99, 99, 99 }, { 9999, 99, 99, 99, 99, 99 },
Do tablicy FontEdW20Bitmap[] niczego nie musimy dodawać, bo program nigdy nie będzie sięgał do nieużywanych kodów.
A jeżeli chodzi o odnalezienie omawianego właśnie błędu, to spostrzegawczy Czytelnicy zapewne szybko wykryli go na podstawie rysunku 6 z poprzedniego odcinka – widać tam wyraźnie, że pierwsza dodana przez nas litera (A) wylądowała na miejscu tyldy o kodzie 0x7E (126), a nie jako kod 128.
W ostatniej wersji pliku FontEdW20.h tylko w tablicy FontEdW20Glyphs[] dodajmy dwa puste miejsca (nie zapominając o przecinkach). Przy okazji zobaczymy, że skrypt ze strony https://tchapi.github.io/Adafruit-GFX-Font-Customiser/ połączył wszystkie bajty tablicy FontEdW20Glyphs[] w jedną nieczytelną całość i usunął stamtąd komentarze, a za to dodał własne komentarze w tablicy FontEdW20Glyphs[] opisujące znaki, a ponadto śmiesznie poustawiał tam przecinki na początku linii. Tu też wyraźnie widać, że w linii opisującej znak tyldy wylądowała literka Ą.
Jeżeli dodamy dwie „puste” linijki według szkicu 2 (nie zapominając o przecinkach!) i zmienimy odstęp między liniami tekstu z 28 na 22 piksele, to po wczytaniu pliku FontEdW20.h na stronie https://tchapi.github.io/Adafruit-GFX-Font-Customiser/ zobaczymy obraz jak na rysunku 1.
Szkic 2:
const uint8_t FontEdW20Bitmaps[] PROGMEM = { 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0x60, 0x0E, 0xEE, 0xEE, 0xEE, 0xEE, 0xC6, 0x44, 0x0D, 0x80, 0x98, 0x19, 0x81, 0x98, 0x7F, 0xE7, 0xFE, 0x1B, 0x01, (...) 0x1C, 0x18, 0x38, 0x30, 0x70, 0x70, 0x60, 0xFE, 0xFE, 0x08, 0xFE, 0x7C, 0x5C, 0x1C, 0x38, 0x30, 0x70, 0x70, 0x60, 0xFE, 0xFE } const GFXglyph FontEdW20Glyphs[] PROGMEM = { { 0, 1, 1, 6, 0, 0 } // ' ' ,{ 1, 4, 14, 6, 1, -14 } // '!' ,{ 8, 8, 5, 10, 1, -14 } // '"' (...) ,{ 1286, 8, 11, 8, 0, -11 } // 'z' ,{ 1297, 8, 17, 8, 0, -14 } // '{' ,{ 1314, 3, 20, 11, 4, -15 } // '|' ,{ 1322, 8, 17, 8, 0, -14 } // '}' , { 0, 0, 0, 0, 0, 0 } //dodany pusty znak 126=0x7E , { 0, 0, 0, 0, 0, 0 } //dodany pusty znak 126=0x7F ,{ 1339, 12, 15, 12, 0, -14 } // ,{ 1362, 9, 15, 10, 1, -15 } // tu mamy ,{ 1380, 8, 15, 10, 1, -14 } // dodane przez nas ,{ 1395, 8, 14, 9, 1, -14 } // 18 polskich liter ,{ 1409, 12, 15, 14, 1, -15 } // ,{ 1432, 11, 15, 13, 1, -15 } // ,{ 1454, 8, 15, 9, 1, -15 } // ,{ 1469, 10, 15, 10, 0, -15 } // ,{ 1489, 10, 15, 10, 0, -15 } // ,{ 1509, 8, 12, 10, 1, -11 } // ,{ 1521, 7, 12, 8, 1, -12 } // ,{ 1532, 8, 12, 10, 1, -11 } // ,{ 1544, 5, 15, 6, 1, -15 } // ,{ 1554, 9, 12, 11, 1, -12 } // ,{ 1568, 9, 12, 11, 1, -12 } // ,{ 1582, 7, 12, 8, 1, -12 } // ,{ 1593, 8, 12, 8, 0, -12 } // ,{ 1605, 8, 12, 8, 0, -12 } // }; const GFXfont FontEdW20 PROGMEM = { (uint8_t *)FontEdW20Bitmaps,(GFXglyph *)FontEdW20Glyphs,0x20, 0x91, 22};
Natomiast po ponownym skompilowaniu szkicu A2603.ino (lub identycznego A2701.ino), na ekranie zobaczymy prawidłowy napis, jak pokazuje fotografia 2.
SUKCES! Możemy uznać, że opanowaliśmy polonizowanie fontów GFX!
W zasadzie tak, ale dociekliwi Czytelnicy dopatrzą się, że na rysunku 1 nie widać kształtu symbolu klamry zamykającej – kod 125=0x7D. Będą się też zastanawiać, jak można dodać obrazek pokazujący wygląd tyldy o kodzie 126=0x7E i jak zapewnić prawidłowe działanie spacji z kodu 127. To zadanie domowe dla chętnych, a ja podpowiem, że w skrypcie na stronie tchapi.github.io można dodać brakującą bitmapę, klikając Rows+, Cols+. A spacja jest w pewnym sensie „znakiem pustym” i nie musi mieć definiującej ją bitmapy. Wystarczająca jest informacja, o ile pikseli kod 32 (i kod 127) ma przesunąć kursor w poziomie. A ta informacja jest zawarta w czwartym polu (xAdvance) tablicy FontEdWGlyphs[].
Inne możliwości
Przedstawiony sposób polonizowania fontów GFX nie jest jedyny i zapewne też nie jest najlepszy. Czytelnicy mający większe doświadczenie oraz najbardziej dociekliwi mogą wypróbować inne metody. Ważne, żeby rozumieć, na czym polega przeróbka i jaki jest jej cel. W każdym razie my musimy do istniejącego fontu dodać 18 polskich liter.
W trudniejszej sytuacji są na przykład Rosjanie i wszyscy inni posługującymi się niełacińskimi alfabetami. Wtedy wymagana jest poważniejsza przeróbka fontu. Warto wiedzieć, że na stronie https://forum.arduino.cc/index.php?topic=447956.0
w skrócie: https://bit.ly/37gmweV
udostępniony jest zrealizowany przez rosyjskich programistów edytor – konwerter fontów dla różnych bibliotek Arduino, w tym także dla Adafruit GFX. W pierwszym poście w tym wątku jest wiele odnośników, którymi nie trzeba się zajmować. Należy tylko pobrać zazipowany plik binFontsTool-0.2.7.xlsm, jak pokazuje czerwona strzałka na rysunku 3. Po rozpakowaniu otrzymujemy plik .xlsm i od razu warto zrobić kilka jego kopii, bo po użyciu może nastąpić zmiana w jego ustawieniach.
Rozpakowany plik .xlsm w zasadzie można otworzyć w różnych programach. Jednak u mnie próba otwarcia za pomocą LibreOffice, który jest moim głównym pakietem do pisania, skończyła się fiaskiem, przede wszystkim z powodu makr, które są domyślnie blokowane. Otworzyłem go w Excelu.
Aby konwerter zadziałał, po otwarciu arkusza w Excelu, pomimo ostrzeżenia o niebezpieczeństwie, najpierw trzeba włączyć skrypty, jak pokazuje czerwona strzałka na rysunku 4. Potem klikając „Open C file”, jak pokazuje niebieska strzałka, trzeba wczytać plik fontu GFX.
Ja wstępne testy przeprowadziłem wcześniej z użyciem starego pliku FontEdW.h o wielkości 10 punktów, jak pokazuje rysunek 5.
Po otwarciu pliku fontu, z lewej strony ekranu zobaczymy wygląd naszych znaków. Na rysunku 6 czerwonym kolorem dopisałem, jakie numery mają poszczególne znaki „na pograniczu” kodów oryginalnych i dodanych.
Przyznaję, że nie doprowadziłem do satysfakcjonującego spolonizowania fontu za pomocą tego narzędzia. Zresztą jest ono w pierwszej kolejności przeznaczone dla alfabetu rosyjskiego. Niemniej warto o nim wiedzieć, bowiem narzędzie to ma bardzo duże możliwości, mnóstwo opcji i na pewno może służyć choćby tylko do podglądania zawartości i wyglądu różnych fontów bitmapowych, nie tylko GFX. Jeżeli ktoś z Czytelników z powodzeniem wykorzysta je do polonizacji fontów GFX, niech napisze do redakcji (edw@elportal.pl).
Kończymy temat fontów Arduino. W praktyce zapewne potrzebne będą Wam spolonizowane fonty o różnej wielkości i kroju, dlatego mocno zachęcam wszystkich Czytelników, żeby samodzielnie przeprowadzili przeróbkę choćby jednego pliku fontu GFX. Tyle odcinków kursu było opublikowanych w czasopiśmie EdW. Na życzenie Czytelników do wybranych wątków tego cyklu możemy wrócić na łamach czasopisma Zrozumieć Elektronikę. Możliwości kontaktu podane są na stronie Zapytaj, odpowiedz.
Piotr Górecki