// u8g2 Persian Reshaper
// Copyright (C) 2019 Ramin Sangesari
// https://github.com/idreamsi/u8g2-persian-reshaper
//#include <Arduino.h>
#include <U8g2lib.h>
#include "pixia.h"
#include "idreams.h"
U8G2_SSD1306_128X64_ALT0_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // same as the NONAME variant, but may solve the "every 2nd line skipped" problem
//U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 12, /* dc=*/ 4, /* reset=*/ 6); // Arduboy (Production, Kickstarter Edition)
//U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* reset=*/ 8);
//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
//U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 21, /* data=*/ 22, /* reset=*/ U8X8_PIN_NONE); // ESP32 Thing, pure SW emulated I2C
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17);
//----------------------------------------------------------------------------
//#define DEBUG
#define LCDWidth u8g2.getDisplayWidth()
#define ALINE_CENTER(t) ((LCDWidth - (u8g2.getUTF8Width(t))) / 2)
#define ALINE_RIGHT(t) (LCDWidth - u8g2.getUTF8Width(t))
#define ALINE_LEFT 0
#define N_DISTINCT_CHARACTERS 62
#define IS_UNICODE(c) (((c) & 0xc0) == 0xc0)
#define VERSION 1
//----------------------------------------------------------------------------
typedef struct prGlyph {
int AsciiCode;
char* codeGlyph;
char* isoGlyph;
char* iniGlyph;
char* midGlyph;
char* endGlyph;
};
const prGlyph prForms[] PROGMEM = {
// Ascii Code, Code, Isolated, Initial, Medial, Final
{193, "\u0621", "\uFE80", "\uFE80", "\uFE80", "\uFE80" }, //1 HAMZA ء [*]
{194, "\u0622", "\uFE81", "\uFE81", "\uFE82", "\uFE82" }, //2 ALEF_MADDA آ [*]
{195, "\u0623", "\uFE83", "\uFE83", "\uFE84", "\uFE84" }, //3 ALEF_HAMZA_ABOVE أ [*]
{196, "\u0624", "\uFE85", "\uFE85", "\uFE86", "\uFE86" }, //4 WAW_HAMZA ؤ [*]
{197, "\u0625", "\uFE87", "\uFE87", "\uFE88", "\uFE88" }, //5 ALEF_HAMZA_BELOW إ [*]
{198, "\u0626", "\uFE89", "\uFE8B", "\uFE8C", "\uFE8A" }, //6 YEH_HAMZA ئ [*]
{199, "\u0627", "\uFE8D", "\uFE8D", "\uFE8E", "\uFE8E" }, //7 ALEF ا [*]
{200, "\u0628", "\uFE8F", "\uFE91", "\uFE92", "\uFE90" }, //8 BEH ب
{555, "\u0629", "\uFE93", "\uFE93", "\uFE94", "\uFE94" }, //9 TEH_MARBUTA ة [*]
{202, "\u062A", "\uFE95", "\uFE97", "\uFE98", "\uFE96" }, //10 TEH ت
{203, "\u062B", "\uFE99", "\uFE9B", "\uFE9C", "\uFE9A" }, //11 THEH ث
{204, "\u062C", "\uFE9D", "\uFE9F", "\uFEA0", "\uFE9E" }, //12 JEEM ج
{205, "\u062D", "\uFEA1", "\uFEA3", "\uFEA4", "\uFEA2" }, //13 HAH ح
{206, "\u062E", "\uFEA5", "\uFEA7", "\uFEA8", "\uFEA6" }, //14 KHAH خ
{207, "\u062F", "\uFEA9", "\uFEA9", "\uFEAA", "\uFEAA" }, //15 DAL د [*]
{208, "\u0630", "\uFEAB", "\uFEAB", "\uFEAC", "\uFEAC" }, //16 THAL ذ [*]
{209, "\u0631", "\uFEAD", "\uFEAD", "\uFEAE", "\uFEAE" }, //17 REH ر [*]
{210, "\u0632", "\uFEAF", "\uFEAF", "\uFEB0", "\uFEB0" }, //18 ZAIN ز [*]
{184, "\u0698", "\uFB8A", "\uFB8A", "\uFB8B", "\uFB8B" }, //19 ZHEH ژ [*]
{211, "\u0633", "\uFEB1", "\uFEB3", "\uFEB4", "\uFEB2" }, //20 SEEN
{212, "\u0634", "\uFEB5", "\uFEB7", "\uFEB8", "\uFEB6" }, //21 SHEEN
{213, "\u0635", "\uFEB9", "\uFEBB", "\uFEBC", "\uFEBA" }, //22 SAD ص
{214, "\u0636", "\uFEBD", "\uFEBF", "\uFEC0", "\uFEBE" }, //23 DAD ض
{215, "\u0637", "\uFEC1", "\uFEC3", "\uFEC4", "\uFEC2" }, //24 TAH ط
{216, "\u0638", "\uFEC5", "\uFEC7", "\uFEC8", "\uFEC6" }, //25 ZAH ظ
{217, "\u0639", "\uFEC9", "\uFECB", "\uFECC", "\uFECA" }, //26 AIN ع
{218, "\u063A", "\uFECD", "\uFECF", "\uFED0", "\uFECE" }, //27 GHAIN غ
{160, "\u0640", "\u0640", "\u0640", "\u0640", "\u0640" }, //28 TATWEEL ـ
{161, "\u0641", "\uFED1", "\uFED3", "\uFED4", "\uFED2" }, //29 FEH ف
{162, "\u0642", "\uFED5", "\uFED7", "\uFED8", "\uFED6" }, //30 QAF ق
{163, "\u0643", "\uFED9", "\uFEDB", "\uFEDC", "\uFEDA" }, //31 KAF Arabic ك
{164, "\u0644", "\uFEDD", "\uFEDF", "\uFEE0", "\uFEDE" }, //32 LAM ل
{165, "\u0645", "\uFEE1", "\uFEE3", "\uFEE4", "\uFEE2" }, //33 MEEM م
{228, "\u0646", "\uFEE5", "\uFEE7", "\uFEE8", "\uFEE6" }, //34 NOON ن
{167, "\u0647", "\uFEE9", "\uFEEB", "\uFEEC", "\uFEEA" }, //35 HEH ه
{168, "\u0648", "\uFEED", "\uFEED", "\uFEEE", "\uFEEE" }, //36 WAW و [*]
{169, "\u0649", "\uFEEF", "\uFEEF", "\uFEF0", "\uFEF0" }, //37 ALEF_MAKSURA [*]
{170, "\u064A", "\uFEF1", "\uFEF3", "\uFEF4", "\uFEF2" }, //38 YEH Arabic ي
{172, "\u06CC", "\uFBFC", "\uFBFE", "\uFBFF", "\uFBFD" }, //39 YEH Farsi ی
{141, "\u0686", "\uFB7A", "\uFB7C", "\uFB7D", "\uFB7B" }, //40 CHEH چ
{222, "\u067E", "\uFB56", "\uFB58", "\uFB59", "\uFB57" }, //41 Peh پ
{144, "\u06AF", "\uFB92", "\uFB94", "\uFB95", "\uFB93" }, //42 Gaf گ
{201, "\u06A9", "\uFB8E", "\uFB90", "\uFB91", "\uFB8F" }, //43 Kaf ک
{32, "\u0020", "\u0020", "\u0020", "\u0020", "\u0020" }, //44 Space
{44, "\u060C", "\u060C", "\u060C", "\u060C", "\u060C" }, //45 Kama
{20, "\u200C", "\u200C", "\u200C", "\u200C","\u200C" }, //46 half-space
{58, "\u003A", "\u003A", "\u003A", "\u003A", "\u003A" }, //47 :
{187, "\u061B", "\u061B", "\u061B", "\u061B", "\u061B" }, //48 ؛
{46, "\u002E", "\u002E", "\u002E", "\u002E", "\u002E" }, //49 .
{191, "\u061F", "\u061F", "\u061F", "\u061F", "\u061F" }, //50 ؟
{48, "\u06F0", "\u06F0", "\u06F0", "\u06F0", "\u06F0" }, //51 0
{49, "\u06F1", "\u06F1", "\u06F1", "\u06F1", "\u06F1" }, //52 1
{50, "\u06F2", "\u06F2", "\u06F2", "\u06F2", "\u06F2" }, //53 2
{51, "\u06F3", "\u06F3", "\u06F3", "\u06F3", "\u06F3" }, //54 3
{52, "\u06F4", "\u06F4", "\u06F4", "\u06F4", "\u06F4" }, //55 4
{53, "\u06F5", "\u06F5", "\u06F5", "\u06F5", "\u06F5" }, //56 5
{54, "\u06F6", "\u06F6", "\u06F6", "\u06F6", "\u06F6" }, //57 6
{55, "\u06F7", "\u06F7", "\u06F7", "\u06F7", "\u06F7" }, //58 7
{56, "\u06F8", "\u06F8", "\u06F8", "\u06F8", "\u06F8" }, //59 8
{57, "\u06F9", "\u06F9", "\u06F9", "\u06F9", "\u06F9" }, //60 9
{41, "\u0028", "\u0028", "\u0028", "\u0028", "\u0028" }, //61 (
{40, "\u0029", "\u0029", "\u0029", "\u0029", "\u0029" } //62 )
};
//----------------------------------------------------------------------------
bool isFromTheSet1(unsigned char ch){
const unsigned char theSet1[18] = {
32, '\0', 199, 194, 207, 208, 209, 210,
184, 168, 191, 40, 41, 46, 33, 44, 58, 248};
int i = 0;
while (i < 18)
{
if(ch == theSet1[i])
return true;
++i;
}
return false;
}
//----------------------------------------------------------------------------
bool isFromTheSet2(unsigned char ch){
const unsigned char theSet1[10] = {
32, '\0', 191, 40, 41, 46, 33, 44,
58, 248 };
int i = 0;
while (i < 10)
{
if(ch == theSet1[i])
return true;
++i;
}
return false;
}
//----------------------------------------------------------------------------
int FindGlyph(unsigned char chFind){
for (int i = 0; i < N_DISTINCT_CHARACTERS; i++) {
if (pgm_read_word(&(prForms[i].AsciiCode)) == chFind) {
return i;
break;
}
}
return -1;
}
//----------------------------------------------------------------------------
String prReshaper(char *Text){
String prBuffer = "";
int stat = 0;
unsigned char pLetter = ' '; //Previous word
unsigned char letter; //Letter
unsigned char nLetter; //Next word
unsigned char temp;
while(temp = *Text++){
//is Number ?
if (temp >= '0' && temp <= '9') {
//d = temp - '0';
letter = temp;
}
else if(temp >= 128){
letter = *Text++;
letter += 32;
temp += 32;
if(letter == 207){
if(temp == 218 || temp == 250){
letter = 144; //گ
}
}
else if(letter == 166)
{
if(temp == 218 || temp == 250){ //چ
letter = 141;
}
else
{
letter = 228; //ن
}
}
}
else
{
letter = temp;
}
//
if(letter == 172)
{
if(temp == 248 || temp == 32)
{
letter = 44;
}
}
temp = *Text++;
if(temp >= 128)
{
nLetter = *Text++;
nLetter += 32;
temp += 32;
if(nLetter == 207)
{
if(temp == 218 || temp == 250)
{
nLetter = 144; //گ
}
}
else if(nLetter == 166)
{
if(temp == 218 || temp == 250)
{ //چ
nLetter = 141;
}
else
{
nLetter = 228; //ن
}
}
*Text--;
*Text--;
}
else
{
nLetter = temp;
*Text--;
}
//
if(nLetter == 172)
{
if(temp == 248 || temp == 32)
{
nLetter = 44;
}
}
int isunk = 0;
/*
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).
*/
if (isFromTheSet1(pLetter))
if (isFromTheSet2(nLetter))
stat = 0; //Isolated
else
stat = 1; //Initial
else
if (isFromTheSet2(nLetter))
stat = 2; //Final
else
stat = 3; //Medial
int number = FindGlyph(letter);
#ifdef DEBUG
Serial.print("Letter code: ");
Serial.println(letter);
Serial.print("Number is: ");
Serial.println(number);
Serial.print("Pos: ");
Serial.println(stat);
Serial.println("--------------");
#endif
switch (stat){
case 0: //Isolated
//Serial.println("Isolated");
prBuffer += (char*)((prForms[number].isoGlyph));
//Serial.println("END Isolated");
break;
case 1: //Initial
//Serial.println("Initial");
prBuffer += (char*)(prForms[number].iniGlyph);
//Serial.println("END Initial");
break;
case 2: //Final
//Serial.println("Final");
prBuffer += (char*)(prForms[number].endGlyph);
//Serial.println("END Final");
break;
case 3: //Medial
//Serial.println("Medial");
prBuffer += (char*)(prForms[number].midGlyph);
//Serial.println("END Medial");
break;
default:
isunk = 1;
break;
}
if(isunk == 0)
pLetter = letter;
}
//utf8rev(prBuffer.c_str());
utf8rev((char *)prBuffer.c_str());
//Serial.println("END");
return prBuffer;
}
//----------------------------------------------------------------------------
// https://stackoverflow.com/questions/199260/how-do-i-reverse-a-utf-8-string-in-place
void utf8rev(char *str){
/* this assumes that str is valid UTF-8 */
char *scanl, *scanr, *scanr2, c;
/* first reverse the string */
for (scanl = str, scanr = str + strlen(str); scanl < scanr;)
c = *scanl, *scanl++= *--scanr, *scanr= c;
/* then scan all bytes and reverse each multibyte character */
for (scanl = scanr = str; c = *scanr++;) {
if ( (c & 0x80) == 0) // ASCII char
scanl = scanr;
else if ( (c & 0xc0) == 0xc0 ) { // start of multibyte
scanr2 = scanr;
switch (scanr - scanl) {
case 4: c = *scanl, *scanl++= *--scanr, *scanr = c; // fallthrough
case 3: // fallthrough
case 2: c = *scanl, *scanl++= *--scanr, *scanr = c;
}
scanr = scanl = scanr2;
}
}
}
//----------------------------------------------------------------------------
//UTF-8 strlen function
int strlen_utf8(char *s) {
int i = 0, j = 0;
while (s[i]) {
if ((s[i] & 0xc0) != 0x80) j++;
i++;
}
return j;
}
//----------------------------------------------------------------------------
void setup(void) {
Serial.begin(9600);
u8g2.begin();
}
//----------------------------------------------------------------------------
void loop(void){
String msg1, msg2;
char txt[] = "پرشین ری شیپر";
char num[] = "نسخه 1";
//utf8rev(num); //For correct representation, the numbers must be reversed.
msg1 = prReshaper(txt);
msg2 = prReshaper(num);
//msg2 = String (utf8rev(prReshaper(num).c_str())); //For correct representation, the numbers must be reversed.
u8g2.enableUTF8Print();
//Set font
//u8g2.setFont(u8g2_font_samim_12_t_all); //Number view in both modes. (English & Persian)
//u8g2.setFont(u8g2_font_iranian_sans_16_t_all); //Number view in both modes. (English & Persian)
//u8g2.setFont(u8g2_font_ganj_nameh_sans16_t_all); //No number view.
//u8g2.setFont(u8g2_font_samim_fd_16_t_all); //By default, this font displays numbers in Persian.
u8g2.firstPage();
do {
//u8g2.setFont(u8g2_font_q_pixia);
u8g2.setFont(idreams_17);
u8g2.setCursor(ALINE_CENTER(msg1.c_str()), 25);
u8g2.print(msg1);
u8g2.setFont(u8g2_font_iranian_sans_10_t_all);
u8g2.setCursor(ALINE_CENTER(msg2.c_str()), 45);
u8g2.print(msg2);
} while(u8g2.nextPage());
delay(10000);
}