diff --git a/apps/lang/english.lang b/apps/lang/english.lang index f8c0c33e19..c50a1269d4 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -3238,3 +3238,9 @@ desc: in browse_id3 eng: "" voice "" new: + +id: LANG_BIDI_SUPPORT +desc: in settings_menu, option to enable reversal of hebrew/arabic text +eng: "BiDi Hebrew/Arabic" +voice "" +new: diff --git a/apps/settings.c b/apps/settings.c index d37e02692c..63e7a3b9d9 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -40,6 +40,7 @@ #include "power.h" #include "backlight.h" #include "powermgmt.h" +#include "bidi.h" #include "status.h" #include "atoi.h" #include "screens.h" @@ -265,10 +266,14 @@ static const struct bit_entry rtc_bits[] = {1, S_O(remote_flip_display), false, "remote flip display", off_on }, #endif +#ifdef HAVE_LCD_BITMAP /* move to LCD next time we bump version */ + {1, S_O(bidi_support), false, "bidi hebrew/arabic", off_on }, +#endif + /* new stuff to be added here */ /* If values are just added to the end, no need to bump the version. */ - /* Current sum of bits: 268 (worst case, but w/o remote lcd) */ + /* Current sum of bits: 277 (worst case, but w/o remote lcd) */ /* Sum of all bit sizes must not grow beyond 288! */ }; @@ -820,6 +825,7 @@ void settings_apply(void) #endif #ifdef HAVE_LCD_BITMAP + set_bidi_support(global_settings.bidi_support); lcd_set_invert_display(global_settings.invert); lcd_set_flip(global_settings.flip_display); button_set_flip(global_settings.flip_display); diff --git a/apps/settings.h b/apps/settings.h index e0a61bf21d..ff121868cb 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -197,6 +197,7 @@ struct user_settings bool invert_cursor; /* invert the current file in dir browser and menu instead of using the default cursor */ bool flip_display; /* turn display (and button layout) by 180 degrees */ + bool bidi_support; /* reverse hebrew/arabic chars: 0=off, 1=on */ int poweroff; /* power off timer */ int backlight_timeout; /* backlight off timeout: 0-18 0=never, 1=always, diff --git a/apps/settings_menu.c b/apps/settings_menu.c index 50f139637c..54267e99c0 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -38,6 +38,7 @@ #include "fat.h" /* For dotfile settings */ #include "sleeptimer.h" #include "powermgmt.h" +#include "bidi.h" #include "rtc.h" #include "ata.h" #include "tree.h" @@ -278,6 +279,18 @@ static bool invert_cursor(void) NULL); } +/** + * Menu to reverse Hebrew and Arabic text according to BiDi algorythm + */ +static bool bidi_support(void) +{ + return set_bool_options( str(LANG_BIDI_SUPPORT), + &global_settings.bidi_support, + STR(LANG_SET_BOOL_YES), + STR(LANG_SET_BOOL_NO), + set_bidi_support); +} + /** * Menu to configure the battery display on status bar */ @@ -1421,6 +1434,7 @@ static bool lcd_settings_menu(void) { ID2P(LANG_INVERT), invert }, { ID2P(LANG_FLIP_DISPLAY), flip_display }, { ID2P(LANG_INVERT_CURSOR), invert_cursor }, + { ID2P(LANG_BIDI_SUPPORT), bidi_support }, #endif }; diff --git a/docs/CREDITS b/docs/CREDITS index 4c8b32fc16..2e7e064faf 100644 --- a/docs/CREDITS +++ b/docs/CREDITS @@ -129,3 +129,5 @@ Ryan Jackson Per Holmäng Frederic Devernay José M. Fandiño +Gadi Cohen +Naftali Goldstein diff --git a/firmware/SOURCES b/firmware/SOURCES index be471fb51b..84f54e56a3 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -49,6 +49,7 @@ drivers/lcd-player-charset.c drivers/lcd-player.c #endif #ifdef HAVE_LCD_BITMAP +bidi.c #if LCD_DEPTH == 2 drivers/lcd-h100.c #elif LCD_DEPTH == 1 diff --git a/firmware/bidi.c b/firmware/bidi.c new file mode 100644 index 0000000000..765d3dab24 --- /dev/null +++ b/firmware/bidi.c @@ -0,0 +1,184 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 by Gadi Cohen + * + * Largely based on php_hebrev by Zeev Suraski + * Heavily modified by Gadi Cohen aka Kinslayer + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include "file.h" +#include "lcd.h" + +#define _HEB_BUFFER_LENGTH MAX_PATH + LCD_WIDTH/2 + 3 + 2 + 2 +#define _HEB_BLOCK_TYPE_ENG 1 +#define _HEB_BLOCK_TYPE_HEB 0 +#define _HEB_ORIENTATION_LTR 1 +#define _HEB_ORIENTATION_RTL 0 + +#define ischar(c) (((((unsigned char) c)>=193) && (((unsigned char) c)<=250)) ? 1 : 0) +#define _isblank(c) (((((unsigned char) c)==' ' || ((unsigned char) c)=='\t')) ? 1 : 0) +#define _isnewline(c) (((((unsigned char) c)=='\n' || ((unsigned char) c)=='\r')) ? 1 : 0) +#define XOR(a,b) ((a||b) && !(a&&b)) + +bool bidi_support_enabled = false; + +unsigned char *bidi_l2v(const unsigned char *str, int orientation) +{ + static unsigned char buf_heb_str[_HEB_BUFFER_LENGTH]; + static unsigned char buf_broken_str[_HEB_BUFFER_LENGTH]; + const unsigned char *tmp; + unsigned char *heb_str, *target, *opposite_target, *broken_str; + int block_start, block_end, block_type, block_length, i; + int block_ended; + long max_chars=0; + int begin, end, char_count, orig_begin; + + if (!str || !*str) + return ""; + + tmp = str; + block_start=block_end=0; + block_ended=0; + + heb_str = buf_heb_str; + if (orientation) { + target = heb_str; + opposite_target = heb_str + strlen(str); + } else { + target = heb_str + strlen(str); + opposite_target = heb_str; + *target = 0; + target--; + } + + block_length=0; + if (ischar(*tmp)) + block_type = _HEB_BLOCK_TYPE_HEB; + else + block_type = _HEB_BLOCK_TYPE_ENG; + + do { + while((XOR(ischar((int)*(tmp+1)),block_type) + || _isblank((int)*(tmp+1)) || ispunct((int)*(tmp+1)) + || (int)*(tmp+1)=='\n') + && block_end<(int)strlen(str)-1) { + tmp++; + block_end++; + block_length++; + } + + if (block_type != orientation) { + while ((_isblank((int)*tmp) || ispunct((int)*tmp)) + && *tmp!='/' && *tmp!='-' && block_end>block_start) { + tmp--; + block_end--; + } + } + + for (i=block_start; i<=block_end; i++) { + *target = (block_type == orientation) ? *(str+i) : *(str+block_end-i+block_start); + if (block_type!=orientation) { + switch (*target) { + case '(': + *target = ')'; + break; + case ')': + *target = '('; + break; + default: + break; + } + } + target += orientation ? 1 : -1; + } + block_type = !block_type; + block_start=block_end+1; + } while(block_end<(int)strlen(str)-1); + + broken_str = buf_broken_str; + begin=end=strlen(str)-1; + target = broken_str; + + while (1) { + char_count=0; + while ((!max_chars || char_count0) { + char_count++; + begin--; + if (begin<=0 || _isnewline(heb_str[begin])) { + while(begin>0 && _isnewline(heb_str[begin-1])) { + begin--; + char_count++; + } + break; + } + } + if (char_count==max_chars) { /* try to avoid breaking words */ + int new_char_count = char_count; + int new_begin = begin; + + while (new_char_count>0) { + if (_isblank(heb_str[new_begin]) || + _isnewline(heb_str[new_begin])) { + break; + } + new_begin++; + new_char_count--; + } + if (new_char_count>0) { + char_count=new_char_count; + begin=new_begin; + } + } + orig_begin=begin; + + if (_isblank(heb_str[begin])) { + heb_str[begin]='\n'; + } + + /* skip leading newlines */ + while (begin<=end && _isnewline(heb_str[begin])) { + begin++; + } + + /* copy content */ + for (i=begin; i<=end; i++) { + *target = heb_str[i]; + target++; + } + + for (i=orig_begin; i<=end && _isnewline(heb_str[i]); i++) { + *target = heb_str[i]; + target++; + } + begin=orig_begin; + + if (begin<=0) { + *target = 0; + break; + } + begin--; + end=begin; + } + return broken_str; +} + +void set_bidi_support(bool setting) +{ + bidi_support_enabled = setting; +} diff --git a/firmware/drivers/lcd-h100.c b/firmware/drivers/lcd-h100.c index 5bdb08abd3..bfdceedc35 100644 --- a/firmware/drivers/lcd-h100.c +++ b/firmware/drivers/lcd-h100.c @@ -28,6 +28,7 @@ #include "debug.h" #include "system.h" #include "font.h" +#include "bidi.h" /*** definitions ***/ @@ -992,6 +993,9 @@ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) int ch; struct font* pf = font_get(curfont); + if (bidi_support_enabled) + str = bidi_l2v(str, 1); + while ((ch = *str++) != '\0' && x < LCD_WIDTH) { int width; diff --git a/firmware/drivers/lcd-recorder.c b/firmware/drivers/lcd-recorder.c index cdeb4f2959..53640ce649 100644 --- a/firmware/drivers/lcd-recorder.c +++ b/firmware/drivers/lcd-recorder.c @@ -28,6 +28,7 @@ #include "system.h" #include "font.h" #include "hwcompat.h" +#include "bidi.h" /*** definitions ***/ @@ -846,6 +847,9 @@ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) int ch; struct font* pf = font_get(curfont); + if (bidi_support_enabled) + str = bidi_l2v(str, 1); + while ((ch = *str++) != '\0' && x < LCD_WIDTH) { int width; diff --git a/firmware/export/bidi.h b/firmware/export/bidi.h new file mode 100644 index 0000000000..88e2eeb07e --- /dev/null +++ b/firmware/export/bidi.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 by Gadi Cohen + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef BIDI_H +extern unsigned char *bidi_l2v(const unsigned char *str, int orientation); +extern bool bidi_support_enabled; +extern void set_bidi_support(bool setting); +#endif