You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have spent the last week, trying to display arabic text on the screen
Suggested Solution
I would like you to either add a font that comes by default for arabic, just like for chinese, korean, etc.
OR
Add documentation on how to use an arabic font.
Additional Context
I gave up on ChatGPT after it was just making things up. then after searching around everywhere I found this solution.
Download some arabic tft font, from anywhere, example Amiri from google fonts
Download bdfconv and run: bdfconv -v -f 1 -m "0-127,1536-1791,65136-65279" amiri.bdf -o amiri_u8g2.c -n amiri_u8g2 -d amiri.bdf
This includes the basic english letters, isolated arabic letters, and arabic letters when connected to other letters.
The arabic letters will be all isolated when you simply do for example: lcd.print("السلام عليكم\n"); so you will need to create a function or use a library which gives tells lgfx the specific unicode of each character to print.
I'll edit say how when I have step 4 done.
Edit:
This is the way I'm currently displaying arabic text with all the logic to make words connected. (I could have used I library to connect letters but I chose not to)
NOTE: This is code for the ESP32, it can be made to work with arduino with a few changes. (hint: chatgpt can do it)
#include"esp_log.h"
#include"freertos/FreeRTOS.h"
#include"freertos/task.h"
#include"esp_system.h"
#include"esp_freertos_hooks.h"
#include"lgfx/v1/lgfx_fonts.hpp"
#include<cstdint>
#defineLGFX_AUTODETECT
#defineLGFX_USE_V1
#include<LovyanGFX.hpp>
#include<amiri_u8g2.c>
LGFX lcd;
lgfx::U8g2font amiri_font (amiri_u8g2);
structArabicWord {
std::u16string word;
uint16_t wordWidth = 0;
std::vector<uint8_t> letterWidths;
};
structArabicMapping {
uint16_t base, initial, medial, final;
};
static ArabicMapping arabic_map[] = {
{0x0621, 0xFE80, 0xFE80, 0xFE80}, // Hamza
{0x0622, 0xFE81, 0xFE82, 0xFE82}, // Alef Madda
{0x0623, 0xFE83, 0xFE84, 0xFE84}, // Alef Hamza Above
{0x0624, 0xFE85, 0xFE86, 0xFE86}, // Waw Hamza Above
{0x0625, 0xFE87, 0xFE88, 0xFE88}, // Alef Hamza Below
{0x0626, 0xFE8B, 0xFE8C, 0xFE8A}, // Yeh Hamza
{0x0627, 0xFE8D, 0xFE8E, 0xFE8E}, // Alef
{0x0628, 0xFE91, 0xFE92, 0xFE90}, // Ba
{0x0629, 0xFE93, 0xFE94, 0xFE94}, // Teh Marbuta
{0x062A, 0xFE97, 0xFE98, 0xFE96}, // Ta
{0x062B, 0xFE9B, 0xFE9C, 0xFE9A}, // Tha
{0x062C, 0xFE9F, 0xFEA0, 0xFE9E}, // Jeem
{0x062D, 0xFEA3, 0xFEA4, 0xFEA2}, // Hah
{0x062E, 0xFEA7, 0xFEA8, 0xFEA6}, // Khah
{0x062F, 0xFEA9, 0xFEAA, 0xFEAA}, // Dal
{0x0630, 0xFEAB, 0xFEAC, 0xFEAC}, // Thal
{0x0631, 0xFEAD, 0xFEAE, 0xFEAE}, // Ra
{0x0632, 0xFEAF, 0xFEB0, 0xFEB0}, // Zain
{0x0633, 0xFEB3, 0xFEB4, 0xFEB2}, // Seen
{0x0634, 0xFEB7, 0xFEB8, 0xFEB6}, // Sheen
{0x0635, 0xFEBB, 0xFEBC, 0xFEBA}, // Sad
{0x0636, 0xFEBF, 0xFEC0, 0xFEBE}, // Dad
{0x0637, 0xFEC3, 0xFEC4, 0xFEC2}, // Tah
{0x0638, 0xFEC7, 0xFEC8, 0xFEC6}, // Zah
{0x0639, 0xFECB, 0xFECC, 0xFECA}, // Ain
{0x063A, 0xFECF, 0xFED0, 0xFECE}, // Ghain
{0x0641, 0xFED3, 0xFED4, 0xFED2}, // Fa
{0x0642, 0xFED7, 0xFED8, 0xFED6}, // Qaf
{0x0643, 0xFEDB, 0xFEDC, 0xFEDA}, // Kaf
{0x0644, 0xFEDF, 0xFEE0, 0xFEDE}, // Lam
{0x0645, 0xFEE3, 0xFEE4, 0xFEE2}, // Meem
{0x0646, 0xFEE7, 0xFEE8, 0xFEE6}, // Noon
{0x0647, 0xFEEB, 0xFEEC, 0xFEEA}, // Ha
{0x0648, 0xFEED, 0xFEEE, 0xFEEE}, // Waw
{0x0649, 0xFEEF, 0xFEE0, 0xFEF0}, // Alef Layina
{0x064A, 0xFEF3, 0xFEF4, 0xFEF2}, // Yeh
{0xFEF5, 0xFEF5, 0xFEF6, 0xFEF5}, // Lam & Alef Hamza Madda
{0xFEF7, 0xFEF7, 0xFEF8, 0xFEF7}, // Lam & Alef Hamza Above
{0xFEF9, 0xFEF9, 0xFEFA, 0xFEF9}, // Lam & Alef Hamza Below
{0xFEFB, 0xFEFB, 0xFEFC, 0xFEFB}, // Lam & Alef
};
staticboolisLetter(uint16_t character) {
for (auto &entry : arabic_map)
if (entry.base == character)
returntrue;
returnfalse;
}
staticconst ArabicMapping* getArabicMapping(uint16_t character) {
for (auto &entry : arabic_map)
if (entry.base == character)
return &entry;
returnnullptr;
}
static std::vector<ArabicWord> shapeArabicText(const std::u16string &input) {
lgfx::FontMetrics ch_metric;
std::vector<ArabicWord> text;
text.emplace_back();
bool connectPrevious = false;
bool connectNext = false;
for (uint16_t i = 0; i < input.size(); i++) {
uint16_t ch = input[i];
// If new wordif (ch == u'') {
if (!text.back().word.empty()) text.emplace_back();
text.back().word.push_back(u'');
text.back().letterWidths.push_back(lcd.textWidth(""));
text.back().wordWidth = text.back().letterWidths[0];
connectPrevious = false;
text.emplace_back();
} else {
// All just for Lam & Alefif ((ch == u'ا' || ch == u'أ' || ch == u'إ' || ch == u'آ') && !text.back().word.empty()) {
uint8_t word_size = text.back().word.size();
bool lam_found = false;
uint8_t j = 1;
while (1) {
if (j == word_size) { break; }
ESP_LOGI("H", "HI1");
if (input[i - j] == u'ل') {
ESP_LOGI("H", "HI2");
text.back().wordWidth -= text.back().letterWidths.back();
text.back().word.erase(word_size - j);
text.back().letterWidths.erase(text.back().letterWidths.begin() + (word_size - j));
switch (ch) {
case u'ا': ch = 0xFEFB; break;
case u'أ': ch = 0xFEF7; break;
case u'إ': ch = 0xFEF9; break;
case u'آ': ch = 0xFEF5; break;
}
lam_found = true;
}
elseif (isLetter(input[i - j])) {
ESP_LOGI("H", "HI3, %x", input[i-j]);
if (lam_found) {
ESP_LOGI("H", "HI4");
switch (input[i - j]) {
case u'أ': case u'إ': case u'آ': case u'ا': case u'و': case u'ز': case u'ر': case u'ذ': case u'د':
connectPrevious = false;
}
}
break;
}
j++;
}
}
// Get all possible version of the letter. (If returned null, then ch is not an arabic letter)auto mapping = getArabicMapping(ch);
if (!mapping) {
text.back().word.push_back(ch);
amiri_font.updateFontMetric(&ch_metric, ch);
text.back().wordWidth += ch_metric.x_advance;
text.back().letterWidths.push_back(ch_metric.x_advance);
continue;
}
// Find next letter in worduint16_t j = i + 1;
while (1) {
if (j > input.size() || input[j] == '') { connectNext = false; break; }
elseif (isLetter(input[j])) { connectNext = true; break; }
j++;
}
// Set correct letter versionuint16_t shapedCh;
if (!connectPrevious && !connectNext) shapedCh = mapping->base;
elseif (connectPrevious && connectNext) shapedCh = mapping->medial;
elseif (connectPrevious && !connectNext) shapedCh = mapping->final;
else shapedCh = mapping->initial;
// Set letter and it's width
text.back().word.push_back(shapedCh);
amiri_font.updateFontMetric(&ch_metric, shapedCh);
text.back().wordWidth += ch_metric.x_advance;
text.back().letterWidths.push_back(ch_metric.x_advance);
// Set that the next letter can connect to this letter
connectPrevious = true;
switch (ch) {
case u'أ': case u'إ': case u'آ': case u'ا': case u'و': case u'ز': case u'ر': case u'ذ': case u'د':
connectPrevious = false;
}
// Same as above, specific to Lam & Alefswitch (shapedCh) {
case0xFEF5: case0xFEF6: case0xFEF7: case0xFEF8: case0xFEF9: case0xFEFA: case0xFEFB: case0xFEFC:
connectPrevious = false;
}
}
}
return text;
}
voidloop(void*)
{
lcd.init();
lcd.setTextColor(TFT_WHITE);
lcd.setRotation(1); // Make screen landscape
lcd.clear();
lcd.setFont(&amiri_font);
lcd.setCursor(20, 30);
uint16_t scr_width = lcd.width();
std::u16string text = u"الْحَمْدُ لِلَّهِ رَبِّ الْعَالمينَ، الرَّحْمَٰنِ الرَّحِيمِ، مَلِكِ يَوْمِ الدِّينِ,,,, - السلام عليكم";
std::vector<ArabicWord> shapedText = shapeArabicText(text);
// Figure out the line widths (only to centre lines horizontally)
std::vector<uint16_t> lineWidths;
lineWidths.emplace_back();
uint8_t margin = 10;
for (auto word : shapedText) {
if (lineWidths.back() + word.wordWidth > scr_width - margin) lineWidths.push_back(margin);
lineWidths.back() += word.wordWidth;
}
// Draw lettersuint8_t j = 0;
uint16_t y = 60;
int16_t x = scr_width - margin - ((scr_width - lineWidths[j]) / 2);
for (auto word : shapedText) {
if (x - word.wordWidth < margin) {
j++;
x = scr_width - margin - ((scr_width - lineWidths[j]) / 2);
y += 60;
}
for (uint16_t i=0; i < word.word.size(); i++) {
lcd.drawChar(word.word[i], x - word.letterWidths[i], y);
x -= word.letterWidths[i];
}
}
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
extern"C"voidapp_main(void) {
xTaskCreate(loop, "app", 8192, NULL, 1, NULL);
}
Thanks in advance.
The text was updated successfully, but these errors were encountered:
given that LVGL supports bidirectional text and can work on top of LovyanGFX, it's unlikely to happen in LGFX core, but this is totally worth an addition in the examples section
feel free to submit a pull request on the develop branch
I have spent the last week, trying to display arabic text on the screen
Suggested Solution
I would like you to either add a font that comes by default for arabic, just like for chinese, korean, etc.
OR
Add documentation on how to use an arabic font.
Additional Context
I gave up on ChatGPT after it was just making things up. then after searching around everywhere I found this solution.
otf2bdf -r 72 -p 32 Amiri.ttf -o amiri.bdf
bdfconv -v -f 1 -m "0-127,1536-1791,65136-65279" amiri.bdf -o amiri_u8g2.c -n amiri_u8g2 -d amiri.bdf
This includes the basic english letters, isolated arabic letters, and arabic letters when connected to other letters.
lcd.print("السلام عليكم\n");
so you will need to create a function or use a library which gives tells lgfx the specific unicode of each character to print.I'll edit say how when I have step 4 done.
Edit:
This is the way I'm currently displaying arabic text with all the logic to make words connected. (I could have used I library to connect letters but I chose not to)
NOTE: This is code for the ESP32, it can be made to work with arduino with a few changes. (hint: chatgpt can do it)
Thanks in advance.
The text was updated successfully, but these errors were encountered: