Text viewer: Fix RTL languages and diacritic characters support

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25515 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Tomer Shalev 2010-04-07 17:22:16 +00:00
parent 8b904e2bb4
commit 9e4bd41e41
3 changed files with 125 additions and 55 deletions

View file

@ -41,6 +41,7 @@
#include "storage.h"
#include "pcmbuf.h"
#include "errno.h"
#include "diacritic.h"
#if CONFIG_CHARGING
#include "power.h"
@ -710,6 +711,10 @@ static const struct plugin_api rockbox_api = {
appsversion,
/* new stuff at the end, sort into place next time
the API gets incompatible */
#ifdef HAVE_LCD_BITMAP
is_diacritic,
#endif
};
int plugin_load(const char* plugin, const void* parameter)

View file

@ -135,7 +135,7 @@ void* plugin_get_buffer(size_t *buffer_size);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
#define PLUGIN_API_VERSION 182
#define PLUGIN_API_VERSION 183
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
@ -863,6 +863,10 @@ struct plugin_api {
const char *appsversion;
/* new stuff at the end, sort into place next time
the API gets incompatible */
#ifdef HAVE_LCD_BITMAP
int (*is_diacritic)(const unsigned short char_code, bool *is_rtl);
#endif
};
/* plugin header */

View file

@ -636,11 +636,14 @@ static int bookmark_count;
static bool is_bom = false;
/* calculate the width of a UCS character (zero width for diacritics) */
static int glyph_width(int ch)
static int glyph_width(unsigned short ch)
{
if (ch == 0)
ch = ' ';
if (rb->is_diacritic(ch, NULL))
return 0;
#ifdef HAVE_LCD_BITMAP
return rb->font_get_width(pf, ch);
#else
@ -730,6 +733,10 @@ static int col = 0;
static inline void advance_conters(unsigned short ch, int* k, int* width)
{
/* diacritics do not count */
if (rb->is_diacritic(ch, NULL))
return;
*width += glyph_width(ch);
(*k)++;
}
@ -760,11 +767,18 @@ static unsigned char* crop_at_width(const unsigned char* p)
static unsigned char* find_first_feed(const unsigned char* p, int size)
{
int i;
int s = 0;
unsigned short ch;
const unsigned char *oldp = p;
for (i=0; i < size; i++)
if (p[i] == 0)
return (unsigned char*) p+i;
while(s <= size)
{
if (*p == 0)
return (unsigned char*)p;
oldp = p;
p = get_ucs(p, &ch);
s += (p - oldp);
}
return NULL;
}
@ -786,28 +800,31 @@ static unsigned char* find_last_space(const unsigned char* p, int size)
k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
if (!BUFFER_OOB(&p[size]))
for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
if (p[size] == line_break[j])
return (unsigned char*) p+size;
for (i=size-1; i>=0; i--)
for (j=k; j < (int) sizeof(line_break); j++)
{
if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
i = size;
if (!BUFFER_OOB(&p[i]))
for (j=k; j < ((int) sizeof(line_break)) - 1; j++) {
if (p[i] == line_break[j])
return (unsigned char*) p+i;
}
if (prefs.word_mode == WRAP) {
for (i=size-1; i>=0; i--) {
for (j=k; j < (int) sizeof(line_break) - 1; j++) {
if (p[i] == line_break[j])
return (unsigned char*) p+i;
}
}
}
return NULL;
}
static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
{
const unsigned char *next_line = NULL;
int size, i, j, k, width, search_len, spaces, newlines;
int size, i, j, j_next, j_prev, k, width, search_len, spaces, newlines;
bool first_chars;
unsigned char c;
unsigned short ch;
if (is_short != NULL)
*is_short = true;
@ -829,16 +846,25 @@ static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_sho
or possibly set next_line at second hard return in a row. */
next_line = NULL;
first_chars=true;
for (j=k=width=spaces=newlines=0; ; j++) {
j_next=j=k=width=spaces=newlines=0;
while (1) {
const unsigned char *p, *oldp;
j_prev = j;
j = j_next;
if (BUFFER_OOB(cur_line+j))
return NULL;
if (line_is_full(k, width)) {
size = search_len = j;
size = search_len = j_prev;
break;
}
c = cur_line[j];
switch (c) {
oldp = p = &cur_line[j];
p = get_ucs(p, &ch);
j_next = j + (p - oldp);
switch (ch) {
case ' ':
if (prefs.line_mode == REFLOW) {
if (newlines > 0) {
@ -909,15 +935,19 @@ static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_sho
if (prefs.word_mode == WRAP) /* Find last space */
next_line = find_last_space(cur_line, size);
if (next_line == NULL)
if (next_line == NULL) {
next_line = crop_at_width(cur_line);
else
if (prefs.word_mode == WRAP)
for (i=0;
i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
i++)
}
else {
if (prefs.word_mode == WRAP) {
for (i=0;i<WRAP_TRIM;i++) {
if (!(isspace(next_line[0]) && !BUFFER_OOB(next_line)))
break;
next_line++;
}
}
}
}
if (prefs.line_mode == EXPAND)
if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
@ -1230,18 +1260,26 @@ static void viewer_show_footer(void)
}
#endif
/* We draw a diacritic char over a non-diacritic one. Therefore, such chars are
* not considered to occupy space, therefore buffers might have more than
* max_columns characters. The DIACRITIC_FACTOR is the max ratio between all
* characters and non-diacritic characters in the buffer
*/
#define DIACRITIC_FACTOR 2
static void viewer_draw(int col)
{
int i, j, k, line_len, line_width, spaces, left_col=0;
int width, extra_spaces, indent_spaces, spaces_per_word;
int width, extra_spaces, indent_spaces, spaces_per_word, spaces_width;
bool multiple_spacing, line_is_short;
unsigned short ch;
unsigned char *str, *oldstr;
unsigned char *line_begin;
unsigned char *line_end;
unsigned char c;
unsigned char scratch_buffer[max_columns + 1];
unsigned char utf8_buffer[max_columns*4 + 1];
int max_chars = max_columns * DIACRITIC_FACTOR;
unsigned char scratch_buffer[max_chars + 1];
unsigned char utf8_buffer[max_chars * 4 + 1];
unsigned char *endptr;
/* If col==-1 do all calculations but don't display */
@ -1287,11 +1325,33 @@ static void viewer_draw(int col)
oldstr = str;
str = crop_at_width(str);
j++;
if (oldstr == str)
{
oldstr = line_end;
break;
}
}
/* width of un-displayed part of the line */
line_width = j*draw_columns;
spaces_width = 0;
while (oldstr < line_end) {
oldstr = get_ucs(oldstr, &ch);
line_width += glyph_width(ch);
/* add width of displayed part of the line */
if (ch)
{
int dw = glyph_width(ch);
/* avoid counting spaces at the end of the line */
if (ch == ' ')
{
spaces_width += dw;
}
else
{
line_width += dw + spaces_width;
spaces_width = 0;
}
}
}
if (prefs.line_mode == JOIN) {
@ -1303,7 +1363,7 @@ static void viewer_draw(int col)
line_len--;
}
for (j=k=spaces=0; j < line_len; j++) {
if (k == max_columns)
if (k == max_chars)
break;
c = line_begin[j];
@ -1319,7 +1379,7 @@ static void viewer_draw(int col)
while (spaces) {
spaces--;
scratch_buffer[k++] = ' ';
if (k == max_columns - 1)
if (k == max_chars - 1)
break;
}
scratch_buffer[k++] = c;
@ -1388,7 +1448,7 @@ static void viewer_draw(int col)
multiple_spacing = false;
for (j=k=spaces=0; j < line_len; j++) {
if (k == max_columns)
if (k == max_chars)
break;
c = line_begin[j];
@ -1420,8 +1480,7 @@ static void viewer_draw(int col)
}
}
else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
if (col != -1)
if (line_width > col) {
if ((col != -1) && (line_width > col)) {
str = oldstr = line_begin;
k = col;
width = 0;
@ -1445,6 +1504,8 @@ static void viewer_draw(int col)
*endptr = 0;
}
}
/* display on screen the displayed part of the line */
if (col != -1 && line_width > col)
{
int dpage = (cline+i <= display_lines)?cpage:cpage+1;