Hello Oliver,
I would like to include the function of printing Arabic-script text (e.g. Persian, Urdu, etc.) to your library.
I've written some code, which converts the individual characters (which look like ش م ع) into their joined forms (which look like شمع). This means that usual typed Arabic (which isn't joined on its own, it is joined during the display process by the operating system) can be input without the need for prior conversion.
Sorry I'm not a very good programmer so I had to use Strings (which take up lots of memory space). You could replace this with char* if you can find a way to use the equivalent of .replace().
Lastly, can you modify the print() function so that it automatically places diacritics above or below (on) the letter e.g. ´ a = á should be done during display, but it is not done automatically nor is it included in Unicode as a joined form.
Here is my code (if you can find a way to place the functions together that would be more convenient):
const unsigned long joining[23][5] = { //use UTF-8 codes
//original, isolated, end, beginning, middle
//hookAfter, hookBefore: FF, FT, TF, TT
{0xD8A8,0xEFBA8F,0xEFBA90,0xEFBA91,0xEFBA92}, //ba'
{0xD8AA,0xEFBA95,0xEFBA96,0xEFBA97,0xEFBA98}, //ta'
{0xD8AB,0xEFBA99,0xEFBA9A,0xEFBA9B,0xEFBA9C}, //tha'
{0xD8AC,0xEFBA9D,0xEFBA9E,0xEFBA9F,0xEFBAA0}, //jim
{0xD8AD,0xEFBAA1,0xEFBAA2,0xEFBAA3,0xEFBAA4}, //hard ḥa'
{0xD8AE,0xEFBAA5,0xEFBAA6,0xEFBAA7,0xEFBAA8}, //kha'
{0xD8B3,0xEFBAB1,0xEFBAB2,0xEFBAB3,0xEFBAB4}, //sin
{0xD8B4,0xEFBAB5,0xEFBAB6,0xEFBAB7,0xEFBAB8}, //shin
{0xD8B5,0xEFBAB9,0xEFBABA,0xEFBABB,0xEFBABC}, //sad
{0xD8B6,0xEFBABD,0xEFBABE,0xEFBABF,0xEFBB80}, //dhad
{0xD8B7,0xEFBB81,0xEFBB82,0xEFBB83,0xEFBB84}, //hard ṭa'
{0xD8B8,0xEFBB85,0xEFBB86,0xEFBB87,0xEFBB88}, //hard dha'
{0xD8B9,0xEFBB89,0xEFBB8A,0xEFBB8B,0xEFBB8C}, //'ayn
{0xD8BA,0xEFBB8D,0xEFBB8E,0xEFBB8F,0xEFBB90}, //ghayn
{0xD981,0xEFBB91,0xEFBB92,0xEFBB93,0xEFBB94}, //fa'
{0xD982,0xEFBB95,0xEFBB96,0xEFBB97,0xEFBB98}, //qaf
{0xD983,0xEFBB99,0xEFBB9A,0xEFBB9B,0xEFBB9C}, //kaf
{0xD984,0xEFBB9D,0xEFBB9E,0xEFBB9F,0xEFBBA0}, //lam
{0xD985,0xEFBBA1,0xEFBBA2,0xEFBBA3,0xEFBBA4}, //mim
{0xD986,0xEFBBA5,0xEFBBA6,0xEFBBA7,0xEFBBA8}, //nun
{0xD987,0xEFBBA9,0xEFBBAA,0xEFBBAB,0xEFBBAC}, //ha'
{0xD98A,0xEFBBB1,0xEFBBB2,0xEFBBB3,0xEFBBB4}, //ya'
{0xD8AC,0xEFBA89,0xEFBA8A,0xEFBA8B,0xEFBA8C} //ya' with hamza above
};
const unsigned long separating[14][3] = { //use UTF-8 codes
//original, isolated and final
{0xD8A7,0xEFBA8D,0xEFBA8E}, //'alif
{0xD8AF,0xEFBAA9,0xEFBAAA}, //dal
{0xD8B0,0xEFBAAB,0xEFBAAC}, //dhal
{0xD8B1,0xEFBAAD,0xEFBAAE}, //ra'
{0xD8B2,0xEFBAAF,0xEFBAB0}, //zay
{0xD988,0xEFBBAD,0xEFBBAE}, //waw
{0xD8A2,0xEFBA81,0xEFBA82}, //'alif maddah
{0xD8A9,0xEFBA93,0xEFBA94}, //ta' marbuṭah
{0xD989,0xEFBBAF,0xEFBBB0}, //'alif maksurah
{0xD9B1,0xEFAD90,0xEFAD91}, //'alif waslah
{0xD8A1,0xEFBA80,0xEFBA80}, //hamza
{0xD8A3,0xEFBA83,0xEFBA84}, //'alif with hamza above
{0xD8A4,0xEFBA85,0xEFBA86}, //waw hamza
{0xD8A5,0xEFBA87,0xEFBA88} //'alif with hamza below
};
const unsigned long diacritics[22] { //use utf8 codes
// 0xD890, //sallallahou alayhe wassallam
// 0xD891, //alayhe assallam
// 0xD892, //rahmatullah alayhe
// 0xD893, //radhi allahou anhu
0xD894, //takhallus
0xD895, //small high tah
0xD896, //'alif lam ya'
0xD897, //small high zay
0xD898, //fatha
0xD899, //dhamma
0xD900, //kasra
0xD98B, //fathatan
0xD98C, //dhammatan
0xD98D, //kasratan
0xD98E, //fatha
0xD98F, //dhamma
0xD990, //kasra
0xD991, //shaddah
0xD992, //sukun
0xD993, //maddah above
0xD994, //hamza above
0xD995 //hamza below
};
const int joiningSize = 22; //change this when expanding joining table to include
//length of joining
const int separatingSize = 9;
const int diacriticSize = 22;
String arabicToPresentationForms(String text) {
int p = 0; //starting position of text
String arabicBuffer;
unsigned long letter = 0; //the letter at p
unsigned long previous = 0; //the letter before p
unsigned long next = 0; //the letter after p
bool hookBefore; //whether or not to join before
bool hookAfter; //whether or not to join after
String diacriticsTemp; //no more than 2 diacritics at one time + 1 null char
int dTemp = 0;
text.replace(",", "،"); //replace English comma with Arabic comma
text.replace("ـ", ""); //remove tatweel/kashida
//text.replace("
int tempp = p;
while (p < text.length()/*/sizeof(char))*/) { //while p is not at the end of the string
//clear diacriticsTemp
diacriticsTemp = "";
//new version to ignore diacritics
if (text[p] >= 0b11000000) { //if 2 bytes
letter = 256*text[p] + text[p+1];
p+=2;
}
else {
letter = text[p];
p++;
}
tempp = p;
//find out if to attach hook before
//look in array to see if a joining character is there
do {
Serial.print("p before previous: "); Serial.println(p);
Serial.print(" previous: "); Serial.println(previous, HEX);
//if (p <= 2) {
// previous = 0; //if p is at beginning of string
//}
if (text[p-4] >= 0b11000000) { //if 2 bytes
previous = 256*text[p-4] + text[p-3];
p -= 2;
}
else { //if 1 byte
previous = text[p-4];
p--;
}
} while (isDiacritic(previous));
Serial.print("p after previous: "); Serial.println(p);
Serial.print(" previous: "); Serial.println(previous, HEX);
p = tempp; //reset to normal
Serial.print("p after previous2: "); Serial.println(p);
do {
Serial.print("p before next: "); Serial.println(p);
Serial.print(" next: "); Serial.println(next, HEX);
//if (p+2 == text.length()) { //if p is at the end of the string
// next = 0;
//}
if (text[p] >= 0b11000000) { //if 2 bytes
next = 256*text[p] + text[p+1];
p += 2;
}
else { //if 1 byte
next = text[p];
p++;
}
} while (isDiacritic(next));
Serial.print("p after next: "); Serial.println(p);
Serial.print(" next: "); Serial.println(next, HEX);
p = tempp; //reset to normal
Serial.print("p after next2: "); Serial.println(p);
if (foundJoining(previous)) {
hookBefore = true;
}
else {
hookBefore = false;
}
if ((p < text.length()) and (foundSeparating(next) or foundJoining(next))) {
hookAfter = true;
}
else {
hookAfter = false;
}
//if this character is joining
bool isJoining = false;
bool isSeparating = false;
int number;
unsigned long modified;
int index = 0;
////Serial.print("X index (non) = "); Serial.println((2*(int)hookAfter)+(int)(hookBefore)+1);
////Serial.print("X index (sep) = "); Serial.println((int)(hookBefore)+1);
////Serial.print("Y index (non) = "); Serial.print(findJoining(letter)); Serial.print(" with a value of "); Serial.println(joining[findJoining(letter)][(2*(int)hookAfter)+(int)(hookBefore)+1], HEX);
////Serial.print("Y index (sep) = "); Serial.print(findSeparating(letter)); Serial.print(" with a value of "); Serial.println(separating[findSeparating(letter)][(int)(hookBefore)+1], HEX);
if (foundJoining(letter) == true) {
number = findJoining(letter);
if (hookAfter) {
index += 2;
}
if (hookBefore) {
index += 1;
}
index++;
//Serial.println((2*(int)hookAfter)+(int)(hookBefore)+1);
arabicBuffer += (String)((char)(joining[number][index] >> 16));
arabicBuffer += (String)((char)(joining[number][index] >> 8));
arabicBuffer += (String)((char)(joining[number][index]));
arabicBuffer += diacriticsTemp;
}
else if (foundSeparating(letter) == true) {
number = findSeparating(letter);
if (hookBefore) {
index += 1;
}
index++;
//Serial.println((int)(hookBefore)+1);
arabicBuffer += (String)((char)(separating[number][index] >> 16));
arabicBuffer += (String)((char)(separating[number][index] >> 8));
arabicBuffer += (String)((char)(separating[number][index]));
arabicBuffer += diacriticsTemp;
}
else {
number = letter;
//if (letter >= 65536) {
// arabicBuffer += (String)((char)(letter >> 16));
//}
arabicBuffer += (String)((char)(letter >> 8));
arabicBuffer += (String)((char)letter);
//arabicBuffer += diacriticsTemp;
}
Serial.print("P is now: "); Serial.println(p);
Serial.print("Previous = "); Serial.println(previous, HEX);
Serial.print("Letter = "); Serial.println(letter, HEX);
Serial.print("Next = "); Serial.println(next, HEX);
Serial.print("Buffer = "); Serial.println(arabicBuffer);
}
arabicBuffer.replace("ﻠ""ﺄ", "ﻸ");
arabicBuffer.replace("ﻟ""ﺄ", "ﻷ");
arabicBuffer.replace("ﻠ""ﺎ", "ﻼ");
arabicBuffer.replace("ﻟ""ﺎ", "ﻻ");
arabicBuffer.replace("ﻠ""ﺈ", "ﻺ");
arabicBuffer.replace("ﻟ""ﺈ", "ﻹ");
arabicBuffer.replace("ﻠ""ﺂ", "ﻶ");
arabicBuffer.replace("ﻟ""ﺂ", "ﻵ");
return arabicBuffer;
}
bool foundJoining(unsigned long toFind) {
for (int x = 0; x < joiningSize; x++) {
if (joining[x][0] == toFind) {
return true;
break;
}
}
return false;
}
bool foundSeparating(unsigned long toFind) {
for (int x = 0; x < separatingSize; x++) {
if (separating[x][0] == toFind) {
return true;
break;
}
}
return false;
}
int findJoining(unsigned long toFind) {
for (int x = 0; x < joiningSize; x++) {
if (joining[x][0] == toFind) {
return x;
break;
}
}
return -1;
}
int findSeparating(unsigned long toFind) {
for (int x = 0; x < separatingSize; x++) {
if (separating[x][0] == toFind) {
return x;
break;
}
}
return -1;
}
bool isDiacritic(unsigned long toCheck) {
if (toCheck == 0) {
return false;
}
else {
for (int i = 0; i < diacriticSize; i++) {
if (toCheck == diacritics[i]) {
return true;
break;
}
}
return false;
}
}
Lastly, if you want to use the function with other languages e.g. Persian then perhaps someone who knows it can add more characters.
Many thanks.
Can you describe more clearly what kind of transformation you do?
If i take latin (and my own language), then I see the following types:
1) Compression/Overlay. Two chars are printed more tight together, like "A" and "W". If combinded they become more close or: "f" and "f", if combinde they become closer: ff
unicode1, unicode2 --> kerning correction
2) Two characters become a new character: so to say: f and f may become a new unicode (indeed the is a unicode for ff). Same is true for diacritics, where " and a may become ä.
unicode1, unicode2 --> unicode3
What are other types in your program? Can we have a list of transformations which are required?
In languages like Arabic, the letter is modified depending on where it should go in a word.
Usually (incorrectly), when you print to the display ordinary Arabic text, each character is printed individually and is not joined together, whereas a computer would join it automatically on the screen but not modifying the actual data of each character.
What my code does is that it modifies each character of the string and it changes it so that it is joined (what a computer would show if you were to type a word). This uses the Unicode block Arabic Presentation Forms-B, whereas the input string uses the block Arabic.
This is different from kerning because kerning only changes the distance between the characters - the code changes the appearance as well.
The transformations are expressed as the UTF-8 values at the top of the code. You can see them at https://en.wikipedia.org/wiki/Arabic_script_in_Unicode.
The appearance changes like in the picture.

You can see that the character changes shape depending on the position it takes:
Final = at the end of the word
Medial = at the middle of the word
Initial = at the beginning of the word
Isolated = the character alone (not part of a word).
The only time that kerning is needed is in order to overlay diacritics (like the image below, the diacritics are the small characters at the top of the letter).

There are also some ligatures in the same way that f + f + i = ffi, ل + ا = لا.

By the way, Arabic text is written in right-to-left whereas numbers are written in the same way as English (left-to-right). Sorry if that complicates things.
Thanks.
Hi,
Thanks @RobotGB
I've already created a library that's easy to use.
https://github.com/idreamsi/arduino-persian-reshaper
@idreamsi
Nice, but it looks at least as complicated as the code from @RobotGB
But actually I think about a more generic solution and i wanted to know the specification for your code.
I mean, I just see the code, but what is the purpose of your code. How would you describe what you do in the code.
@olikraus
Unfortunately, Persian, Arabic, and ... are very complex and their implementation is time consuming.
You need to be familiar with these languages.
Also, there is a bug in your library and I can not use u8g2.drawPixel(x, y)
Function written by me, requires drawPixel(x, y) but my attempt to convert to your library was unsuccessful.
Even the following code has no output!
#include <Arduino.h>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 12, /* dc=*/ 4, /* reset=*/ 6); // Arduboy (Production, Kickstarter Edition)
void setup(void) {
u8g2.begin();
u8g2.firstPage();
u8g2.setColorIndex(1);
u8g2.drawPixel(100,10);
u8g2.nextPage();
}
What my (and I'm assuming @idreamsi's too) code does is that it converts each letter into its joined form (which takes up a different Unicode code point).
You can imagine it like writing joined up handwriting - each letter joins to the previous and next character differently depending on the position in the word (if the letter is alone, at the beginning, at the middle or at the end).
If the character is at the beginning of the word, it is not joined before, but if at the middle, it is joined before and after. Likewise, if the character is at the end, it is joined before and not after.
Of course, it is more complex than this, because different letters behave differently, some might not join after etc.
A simplified process would be:
Read each character, the one before it and the one after it, then increment to the next set of characters (ignore diacritics when finding the characters because they do not serve in attaching each character, they go above and are not part of the text).
I've named these as _'previous'_, _'letter'_ and _'next'_ in my code.
Look into the table to find if the previous letter is a joining character or a separating character (using functions _foundJoining_ and _foundSeparating_). If so, replace the current character _letter_ so that it is joined correctly based on whether it is joined before and after (_hookBefore_ and hookAfter).
Then add the current modified letter to the end of the buffer, which will be returned when the variable is called.
Repeat for the next character until the end of the String is reached.
Also @idreamsi, the setPixel code works on my display (SSD1306) but you will need to change setPixel(x, y, color) in your code to
u8g2.setDrawColor(color); //white is 1 and black is 0
u8g2.drawPixel(x, y);
Function written by me, requires drawPixel(x, y) but my attempt to convert to your library was unsuccessful.
In your example, the loop itself is missing, so it can not work. As an alternative: Use the clear buffer/send buffer approach with the F constructor
You need to be familiar with these languages.
Well, yes infact I see that his will be too much for u8g2 project.
Thanks a lot for all your explainations.
Actually what I look for is something like this: A procedure, which gets unicode glyphs as input and outputs a new list of unicode glyphs.
In between the arabic/persian transformation happens.
In order to add this to u8g2 it should be some plain c code without side effects.
Anyhow, my knowledge is indeed not deep enough... so I keep this issue here so that other people can benefit from your solutions.
Also, after passing the String through the function arabicToPresentationForms, I've created some code to test the display results. Sorry, it's a hodgepodge at best, and it only works with constant width fonts.
#include <SPI.h>
#include <Wire.h>
#include <U8x8lib.h>
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // All Boards without Reset of the Display
const int joiningSize PROGMEM = 22; //change this when expanding joining table to include
//length of joining
const int separatingSize PROGMEM = 14;
const int diacriticSize PROGMEM = 18;
int x = 118;
const unsigned long joining[][5] PROGMEM = { //use UTF-8 codes
//original, isolated, end, beginning, middle
//hookAfter, hookBefore: FF, FT, TF, TT
{0xD8A8, 0xEFBA8F, 0xEFBA90, 0xEFBA91, 0xEFBA92}, //ba'
{0xD8AA, 0xEFBA95, 0xEFBA96, 0xEFBA97, 0xEFBA98}, //ta'
{0xD8AB, 0xEFBA99, 0xEFBA9A, 0xEFBA9B, 0xEFBA9C}, //tha'
{0xD8AC, 0xEFBA9D, 0xEFBA9E, 0xEFBA9F, 0xEFBAA0}, //jim
{0xD8AD, 0xEFBAA1, 0xEFBAA2, 0xEFBAA3, 0xEFBAA4}, //hard ḥa'
{0xD8AE, 0xEFBAA5, 0xEFBAA6, 0xEFBAA7, 0xEFBAA8}, //kha'
{0xD8B3, 0xEFBAB1, 0xEFBAB2, 0xEFBAB3, 0xEFBAB4}, //sin
{0xD8B4, 0xEFBAB5, 0xEFBAB6, 0xEFBAB7, 0xEFBAB8}, //shin
{0xD8B5, 0xEFBAB9, 0xEFBABA, 0xEFBABB, 0xEFBABC}, //sad
{0xD8B6, 0xEFBABD, 0xEFBABE, 0xEFBABF, 0xEFBB80}, //dhad
{0xD8B7, 0xEFBB81, 0xEFBB82, 0xEFBB83, 0xEFBB84}, //hard ṭa'
{0xD8B8, 0xEFBB85, 0xEFBB86, 0xEFBB87, 0xEFBB88}, //hard dha'
{0xD8B9, 0xEFBB89, 0xEFBB8A, 0xEFBB8B, 0xEFBB8C}, //'ayn
{0xD8BA, 0xEFBB8D, 0xEFBB8E, 0xEFBB8F, 0xEFBB90}, //ghayn
{0xD981, 0xEFBB91, 0xEFBB92, 0xEFBB93, 0xEFBB94}, //fa'
{0xD982, 0xEFBB95, 0xEFBB96, 0xEFBB97, 0xEFBB98}, //qaf
{0xD983, 0xEFBB99, 0xEFBB9A, 0xEFBB9B, 0xEFBB9C}, //kaf
{0xD984, 0xEFBB9D, 0xEFBB9E, 0xEFBB9F, 0xEFBBA0}, //lam
{0xD985, 0xEFBBA1, 0xEFBBA2, 0xEFBBA3, 0xEFBBA4}, //mim
{0xD986, 0xEFBBA5, 0xEFBBA6, 0xEFBBA7, 0xEFBBA8}, //nun
{0xD987, 0xEFBBA9, 0xEFBBAA, 0xEFBBAB, 0xEFBBAC}, //ha'
{0xD98A, 0xEFBBB1, 0xEFBBB2, 0xEFBBB3, 0xEFBBB4}, //ya'
{0xD8AC, 0xEFBA89, 0xEFBA8A, 0xEFBA8B, 0xEFBA8C} //ya' with hamza above
};
const unsigned long separating[][3] PROGMEM = { //use UTF-8 codes
//original, isolated and final
{0xD8A7, 0xEFBA8D, 0xEFBA8E}, //'alif
{0xD8AF, 0xEFBAA9, 0xEFBAAA}, //dal
{0xD8B0, 0xEFBAAB, 0xEFBAAC}, //dhal
{0xD8B1, 0xEFBAAD, 0xEFBAAE}, //ra'
{0xD8B2, 0xEFBAAF, 0xEFBAB0}, //zay
{0xD988, 0xEFBBAD, 0xEFBBAE}, //waw
{0xD8A2, 0xEFBA81, 0xEFBA82}, //'alif maddah
{0xD8A9, 0xEFBA93, 0xEFBA94}, //ta' marbuṭah
{0xD989, 0xEFBBAF, 0xEFBBB0}, //'alif maksurah
{0xD9B1, 0xEFAD90, 0xEFAD91}, //'alif waslah
{0xD8A1, 0xEFBA80, 0xEFBA80}, //hamza
{0xD8A3, 0xEFBA83, 0xEFBA84}, //'alif with hamza above
{0xD8A4, 0xEFBA85, 0xEFBA86}, //waw hamza
{0xD8A5, 0xEFBA87, 0xEFBA88} //'alif with hamza below
};
const unsigned long diacritics[] PROGMEM { //use utf8 codes
// 0xD890, //sallallahou alayhe wassallam
// 0xD891, //alayhe assallam
// 0xD892, //rahmatullah alayhe
// 0xD893, //radhi allahou anhu
0xD894, //takhallus
0xD895, //small high tah
0xD896, //'alif lam ya'
0xD897, //small high zay
0xD898, //fatha
0xD899, //dhamma
0xD900, //kasra
0xD98B, //fathatan
0xD98C, //dhammatan
0xD98D, //kasratan
0xD98E, //fatha
0xD98F, //dhamma
0xD990, //kasra
0xD991, //shaddah
0xD992, //sukun
0xD993, //maddah above
0xD994, //hamza above
0xD995 //hamza below
};
String arabicToPresentationForms(String text) {
unsigned int p = 0; //starting position of text
String arabicBuffer;
unsigned long letter = 0; //the letter at p
unsigned long previous = 0; //the letter before p
unsigned long next = 0; //the letter after p
bool hookBefore; //whether or not to join before
bool hookAfter; //whether or not to join after
//String diacriticsTemp; //no more than 2 diacritics at one time + 1 null char
//int dTemp = 0;
text.replace(",", "،"); //replace English comma with Arabic comma
text.replace("ـ", ""); //remove tatweel/kashida
//text.replace("
int tempp = p;
while (p < text.length()) { //while p is not at the end of the string
//clear diacriticsTemp
//diacriticsTemp;
//new version to ignore diacritics
if (text[p] >= 0b11000000) { //if 2 bytes
letter = 256 * text[p] + text[p + 1];
p += 2;
}
else {
letter = text[p];
p++;
}
tempp = p;
//find out if to attach hook before
//look in array to see if a joining character is there
do {
//Serial.print("p before previous: "); Serial.println(p);
//Serial.print(" previous: "); Serial.println(previous, HEX);
//if (p <= 2) {
// previous = 0; //if p is at beginning of string
//}
if (text[p - 4] >= 0b11000000) { //if 2 bytes
previous = 256 * text[p - 4] + text[p - 3];
p -= 2;
}
else { //if 1 byte
previous = text[p - 4];
p--;
}
} while (isDiacritic(previous));
//Serial.print("p after previous: "); Serial.println(p);
//Serial.print(" previous: "); Serial.println(previous, HEX);
p = tempp; //reset to normal
//Serial.print("p after previous2: "); Serial.println(p);
do {
//Serial.print("p before next: "); Serial.println(p);
//Serial.print(" next: "); Serial.println(next, HEX);
//if (p+2 == text.length()) { //if p is at the end of the string
// next = 0;
//}
if (text[p] >= 0b11000000) { //if 2 bytes
next = 256 * text[p] + text[p + 1];
p += 2;
}
else { //if 1 byte
next = text[p];
p++;
}
} while (isDiacritic(next));
//Serial.print("p after next: "); Serial.println(p);
//Serial.print(" next: "); Serial.println(next, HEX);
p = tempp; //reset to normal
//Serial.print("p after next2: "); Serial.println(p);
if (foundJoining(previous)) {
hookBefore = true;
}
else {
hookBefore = false;
}
if ((p < text.length()) and (foundSeparating(next) or foundJoining(next))) {
hookAfter = true;
}
else {
hookAfter = false;
}
//if this character is joining
bool isJoining = false;
bool isSeparating = false;
int number;
unsigned long modified;
int index = 0;
////Serial.print("X index (non) = "); Serial.println((2*(int)hookAfter)+(int)(hookBefore)+1);
////Serial.print("X index (sep) = "); Serial.println((int)(hookBefore)+1);
////Serial.print("Y index (non) = "); Serial.print(findJoining(letter)); Serial.print(" with a value of "); Serial.println(joining[findJoining(letter)][(2*(int)hookAfter)+(int)(hookBefore)+1], HEX);
////Serial.print("Y index (sep) = "); Serial.print(findSeparating(letter)); Serial.print(" with a value of "); Serial.println(separating[findSeparating(letter)][(int)(hookBefore)+1], HEX);
if (foundJoining(letter) == true) {
number = findJoining(letter);
if (hookAfter) {
index += 2;
}
if (hookBefore) {
index += 1;
}
index++;
//Serial.println((2*(int)hookAfter)+(int)(hookBefore)+1);
arabicBuffer += (String)((char)(joining[number][index] >> 16));
arabicBuffer += (String)((char)(joining[number][index] >> 8));
arabicBuffer += (String)((char)(joining[number][index]));
//arabicBuffer += diacriticsTemp;
}
else if (foundSeparating(letter) == true) {
number = findSeparating(letter);
if (hookBefore) {
index += 1;
}
index++;
//Serial.println((int)(hookBefore)+1);
arabicBuffer += (String)((char)(separating[number][index] >> 16));
arabicBuffer += (String)((char)(separating[number][index] >> 8));
arabicBuffer += (String)((char)(separating[number][index]));
//arabicBuffer += diacriticsTemp;
}
else {
number = letter;
//if (letter >= 65536) {
// arabicBuffer += (String)((char)(letter >> 16));
//}
arabicBuffer += (String)((char)(letter >> 8));
arabicBuffer += (String)((char)letter);
//arabicBuffer += diacriticsTemp;
}
Serial.print(F("P is now: ")); Serial.println(p);
Serial.print(F("Previous = ")); Serial.println(previous, HEX);
Serial.print(F("Letter = ")); Serial.println(letter, HEX);
Serial.print(F("Next = ")); Serial.println(next, HEX);
Serial.print(F("Buffer = ")); Serial.println(arabicBuffer);
}
arabicBuffer.replace("ﻠ""ﺄ", "ﻸ");
arabicBuffer.replace("ﻟ""ﺄ", "ﻷ");
arabicBuffer.replace("ﻠ""ﺎ", "ﻼ");
arabicBuffer.replace("ﻟ""ﺎ", "ﻻ");
arabicBuffer.replace("ﻠ""ﺈ", "ﻺ");
arabicBuffer.replace("ﻟ""ﺈ", "ﻹ");
arabicBuffer.replace("ﻠ""ﺂ", "ﻶ");
arabicBuffer.replace("ﻟ""ﺂ", "ﻵ");
return arabicBuffer;
}
bool foundJoining(unsigned long toFind) {
for (int x = 0; x < joiningSize; x++) {
if (joining[x][0] == toFind) {
return true;
break;
}
}
return false;
}
bool foundSeparating(unsigned long toFind) {
for (int x = 0; x < separatingSize; x++) {
if (separating[x][0] == toFind) {
return true;
break;
}
}
return false;
}
int findJoining(unsigned long toFind) {
for (int x = 0; x < joiningSize; x++) {
if (joining[x][0] == toFind) {
return x;
break;
}
}
return -1;
}
int findSeparating(unsigned long toFind) {
for (int x = 0; x < separatingSize; x++) {
if (separating[x][0] == toFind) {
return x;
break;
}
}
return -1;
}
bool isDiacritic(unsigned long toCheck) {
if (toCheck == 0) {
return false;
}
else {
for (int i = 0; i < diacriticSize; i++) {
if (toCheck == diacritics[i]) {
return true;
break;
}
}
return false;
}
}
String msg = "ﺍﻟﺸُﻮﻛُﻮﻻَﺗَﺔ أﻭ ﺍﻟﺸُﻜُﻮﻻَﺗَﺔ أﻭ ﺍﻟﺸِﻴﻜُﻮﻻَﺗَﺔ أﻭ ﺍﻟﺸﻮﻛﻮﻻ أﻭ ﺍﻟﺸﻜﻼﻁ"; //this is already prepared
//String msg = "Hello world! This is a test string.";
unsigned long int current = 0;
const int width = 10;
const int y = 20;
void setup(void) {
Serial.begin(19200);
while (!Serial) {
;
}
Serial.println("Hello World!");
//Serial.println(msg);
//msg = arabicToPresentationForms(msg);
//Serial.println(msg);
//u8g2.begin();
u8g2.begin();
for (int n = 0; n < 169; n++) {
Serial.print("Char: 0x");
if ((unsigned int)msg[n] >= 0xFFFFFF00) {
msg[n] = (unsigned int)(msg[n]) - (unsigned int)0xFFFFFF00;
}
else {
msg[n] = (unsigned int)(msg[n]);
}
Serial.print((uint8_t)msg[n], HEX); Serial.print(", 0b"); Serial.println((uint8_t)msg[n], BIN);
}
u8g2.enableUTF8Print();
u8g2.setDrawColor(1);
u8g2.setFont(u8g2_font_10x20_t_arabic);
Serial.println("Done");
u8g2.clearBuffer();
for (int i = 0; i < 169; i+=0) {
if (((uint8_t)(msg[i]))>>6 == 0b11) {
current = msg[i];
do {
current = current<<8;
current += ((uint8_t)(msg[i+1]));
i++;
} while (((uint8_t)(msg[i+1]))>>6 == 0b10);
i++;
}
else {
current = msg[i];
i++;
}
if (current >= 0xFFFF0000) {
current -= 0xFFFF0000;
}
else if (current >= 0xFFEF0000) {
current -= 0xFF000000;
}
/*u8g2.setCursor(x, 20);
if (isDiacritic(current)) {
u8g2.print((String)((uint8_t)(current>>8)) + (String)((uint8_t)(current)));
}
else if (current >= 0xEF) {
u8g2.print((String)((uint8_t)(current>>16)) + (String)((uint8_t)(current>>8)) + (String)((uint8_t)(current)));
x-=10;
}
else {
u8g2.print((String)((uint8_t)(current)));
x-=10;
}
*/
//i--;
Serial.println(current, HEX);
if (current >= 0xFFFF) {
u8g2.setCursor(x, y);
u8g2.print(String((char)(current>>16)) + String((char)(current>>8)) + String((char)(current)));
x-=width;
}
else if (current == 0x20) {
x-= (2*width);
}
else if (isDiacritic(current)) {
u8g2.setCursor(x, y);
u8g2.print(String((char)(current>>8)) + String((char)(current)));
x-=width;
}
else if (!isDiacritic(current) and (current >= 0xFF)) {
u8g2.setCursor(x+width, y);
u8g2.print(String((char)(current>>8)) + String((char)(current)));
}
else {
u8g2.setCursor(x, y);
u8g2.print(String((char)(current)));
x-=width;
}
}
u8g2.sendBuffer();
}
void loop(void) {
}
The only code that would complete the process is reversing all Latin characters. Because the code is designed to handle right-to-left text, this means that Latin text will also be displayed in reverse e.g. Hello becomes olleH when displayed with this code.
Also, apparently, 0xEF as a char in String becomes 0xFFFFFFEF on boards like the Arduino Uno and Mega but not on boards like the MKR Zero. Can anyone find a workaround?
Thanks.
Most helpful comment
Also, after passing the String through the function arabicToPresentationForms, I've created some code to test the display results. Sorry, it's a hodgepodge at best, and it only works with constant width fonts.
I might add a function which finds the width of the text afterwards, and definitely clean this up.
(the code that I updated is at the bottom, and this includes the function from before)