forked from len0rd/rockbox
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:
parent
8b904e2bb4
commit
9e4bd41e41
3 changed files with 125 additions and 55 deletions
|
@ -41,6 +41,7 @@
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "pcmbuf.h"
|
#include "pcmbuf.h"
|
||||||
#include "errno.h"
|
#include "errno.h"
|
||||||
|
#include "diacritic.h"
|
||||||
|
|
||||||
#if CONFIG_CHARGING
|
#if CONFIG_CHARGING
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
@ -710,6 +711,10 @@ static const struct plugin_api rockbox_api = {
|
||||||
appsversion,
|
appsversion,
|
||||||
/* new stuff at the end, sort into place next time
|
/* new stuff at the end, sort into place next time
|
||||||
the API gets incompatible */
|
the API gets incompatible */
|
||||||
|
|
||||||
|
#ifdef HAVE_LCD_BITMAP
|
||||||
|
is_diacritic,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
int plugin_load(const char* plugin, const void* parameter)
|
int plugin_load(const char* plugin, const void* parameter)
|
||||||
|
|
|
@ -135,7 +135,7 @@ void* plugin_get_buffer(size_t *buffer_size);
|
||||||
#define PLUGIN_MAGIC 0x526F634B /* RocK */
|
#define PLUGIN_MAGIC 0x526F634B /* RocK */
|
||||||
|
|
||||||
/* increase this every time the api struct changes */
|
/* 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
|
/* update this to latest version if a change to the api struct breaks
|
||||||
backwards compatibility (and please take the opportunity to sort in any
|
backwards compatibility (and please take the opportunity to sort in any
|
||||||
|
@ -863,6 +863,10 @@ struct plugin_api {
|
||||||
const char *appsversion;
|
const char *appsversion;
|
||||||
/* new stuff at the end, sort into place next time
|
/* new stuff at the end, sort into place next time
|
||||||
the API gets incompatible */
|
the API gets incompatible */
|
||||||
|
|
||||||
|
#ifdef HAVE_LCD_BITMAP
|
||||||
|
int (*is_diacritic)(const unsigned short char_code, bool *is_rtl);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/* plugin header */
|
/* plugin header */
|
||||||
|
|
|
@ -636,11 +636,14 @@ static int bookmark_count;
|
||||||
static bool is_bom = false;
|
static bool is_bom = false;
|
||||||
|
|
||||||
/* calculate the width of a UCS character (zero width for diacritics) */
|
/* 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)
|
if (ch == 0)
|
||||||
ch = ' ';
|
ch = ' ';
|
||||||
|
|
||||||
|
if (rb->is_diacritic(ch, NULL))
|
||||||
|
return 0;
|
||||||
|
|
||||||
#ifdef HAVE_LCD_BITMAP
|
#ifdef HAVE_LCD_BITMAP
|
||||||
return rb->font_get_width(pf, ch);
|
return rb->font_get_width(pf, ch);
|
||||||
#else
|
#else
|
||||||
|
@ -730,6 +733,10 @@ static int col = 0;
|
||||||
|
|
||||||
static inline void advance_conters(unsigned short ch, int* k, int* width)
|
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);
|
*width += glyph_width(ch);
|
||||||
(*k)++;
|
(*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)
|
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++)
|
while(s <= size)
|
||||||
if (p[i] == 0)
|
{
|
||||||
return (unsigned char*) p+i;
|
if (*p == 0)
|
||||||
|
return (unsigned char*)p;
|
||||||
|
oldp = p;
|
||||||
|
p = get_ucs(p, &ch);
|
||||||
|
s += (p - oldp);
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -786,18 +800,21 @@ static unsigned char* find_last_space(const unsigned char* p, int size)
|
||||||
|
|
||||||
k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
|
k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
|
||||||
|
|
||||||
if (!BUFFER_OOB(&p[size]))
|
i = size;
|
||||||
for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
|
if (!BUFFER_OOB(&p[i]))
|
||||||
if (p[size] == line_break[j])
|
for (j=k; j < ((int) sizeof(line_break)) - 1; j++) {
|
||||||
return (unsigned char*) p+size;
|
if (p[i] == line_break[j])
|
||||||
|
return (unsigned char*) p+i;
|
||||||
|
}
|
||||||
|
|
||||||
for (i=size-1; i>=0; i--)
|
if (prefs.word_mode == WRAP) {
|
||||||
for (j=k; j < (int) sizeof(line_break); j++)
|
for (i=size-1; i>=0; i--) {
|
||||||
{
|
for (j=k; j < (int) sizeof(line_break) - 1; j++) {
|
||||||
if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
|
|
||||||
if (p[i] == line_break[j])
|
if (p[i] == line_break[j])
|
||||||
return (unsigned char*) p+i;
|
return (unsigned char*) p+i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -805,9 +822,9 @@ static unsigned char* find_last_space(const unsigned char* p, int size)
|
||||||
static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
|
static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
|
||||||
{
|
{
|
||||||
const unsigned char *next_line = NULL;
|
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;
|
bool first_chars;
|
||||||
unsigned char c;
|
unsigned short ch;
|
||||||
|
|
||||||
if (is_short != NULL)
|
if (is_short != NULL)
|
||||||
*is_short = true;
|
*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. */
|
or possibly set next_line at second hard return in a row. */
|
||||||
next_line = NULL;
|
next_line = NULL;
|
||||||
first_chars=true;
|
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))
|
if (BUFFER_OOB(cur_line+j))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (line_is_full(k, width)) {
|
if (line_is_full(k, width)) {
|
||||||
size = search_len = j;
|
size = search_len = j_prev;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = cur_line[j];
|
oldp = p = &cur_line[j];
|
||||||
switch (c) {
|
p = get_ucs(p, &ch);
|
||||||
|
j_next = j + (p - oldp);
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
case ' ':
|
case ' ':
|
||||||
if (prefs.line_mode == REFLOW) {
|
if (prefs.line_mode == REFLOW) {
|
||||||
if (newlines > 0) {
|
if (newlines > 0) {
|
||||||
|
@ -909,14 +935,18 @@ static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_sho
|
||||||
if (prefs.word_mode == WRAP) /* Find last space */
|
if (prefs.word_mode == WRAP) /* Find last space */
|
||||||
next_line = find_last_space(cur_line, size);
|
next_line = find_last_space(cur_line, size);
|
||||||
|
|
||||||
if (next_line == NULL)
|
if (next_line == NULL) {
|
||||||
next_line = crop_at_width(cur_line);
|
next_line = crop_at_width(cur_line);
|
||||||
else
|
}
|
||||||
if (prefs.word_mode == WRAP)
|
else {
|
||||||
for (i=0;
|
if (prefs.word_mode == WRAP) {
|
||||||
i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
|
for (i=0;i<WRAP_TRIM;i++) {
|
||||||
i++)
|
if (!(isspace(next_line[0]) && !BUFFER_OOB(next_line)))
|
||||||
|
break;
|
||||||
next_line++;
|
next_line++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prefs.line_mode == EXPAND)
|
if (prefs.line_mode == EXPAND)
|
||||||
|
@ -1230,18 +1260,26 @@ static void viewer_show_footer(void)
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
static void viewer_draw(int col)
|
||||||
{
|
{
|
||||||
int i, j, k, line_len, line_width, spaces, left_col=0;
|
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;
|
bool multiple_spacing, line_is_short;
|
||||||
unsigned short ch;
|
unsigned short ch;
|
||||||
unsigned char *str, *oldstr;
|
unsigned char *str, *oldstr;
|
||||||
unsigned char *line_begin;
|
unsigned char *line_begin;
|
||||||
unsigned char *line_end;
|
unsigned char *line_end;
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
unsigned char scratch_buffer[max_columns + 1];
|
int max_chars = max_columns * DIACRITIC_FACTOR;
|
||||||
unsigned char utf8_buffer[max_columns*4 + 1];
|
unsigned char scratch_buffer[max_chars + 1];
|
||||||
|
unsigned char utf8_buffer[max_chars * 4 + 1];
|
||||||
unsigned char *endptr;
|
unsigned char *endptr;
|
||||||
|
|
||||||
/* If col==-1 do all calculations but don't display */
|
/* If col==-1 do all calculations but don't display */
|
||||||
|
@ -1287,11 +1325,33 @@ static void viewer_draw(int col)
|
||||||
oldstr = str;
|
oldstr = str;
|
||||||
str = crop_at_width(str);
|
str = crop_at_width(str);
|
||||||
j++;
|
j++;
|
||||||
|
if (oldstr == str)
|
||||||
|
{
|
||||||
|
oldstr = line_end;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
/* width of un-displayed part of the line */
|
||||||
line_width = j*draw_columns;
|
line_width = j*draw_columns;
|
||||||
|
spaces_width = 0;
|
||||||
while (oldstr < line_end) {
|
while (oldstr < line_end) {
|
||||||
oldstr = get_ucs(oldstr, &ch);
|
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) {
|
if (prefs.line_mode == JOIN) {
|
||||||
|
@ -1303,7 +1363,7 @@ static void viewer_draw(int col)
|
||||||
line_len--;
|
line_len--;
|
||||||
}
|
}
|
||||||
for (j=k=spaces=0; j < line_len; j++) {
|
for (j=k=spaces=0; j < line_len; j++) {
|
||||||
if (k == max_columns)
|
if (k == max_chars)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
c = line_begin[j];
|
c = line_begin[j];
|
||||||
|
@ -1319,7 +1379,7 @@ static void viewer_draw(int col)
|
||||||
while (spaces) {
|
while (spaces) {
|
||||||
spaces--;
|
spaces--;
|
||||||
scratch_buffer[k++] = ' ';
|
scratch_buffer[k++] = ' ';
|
||||||
if (k == max_columns - 1)
|
if (k == max_chars - 1)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
scratch_buffer[k++] = c;
|
scratch_buffer[k++] = c;
|
||||||
|
@ -1388,7 +1448,7 @@ static void viewer_draw(int col)
|
||||||
|
|
||||||
multiple_spacing = false;
|
multiple_spacing = false;
|
||||||
for (j=k=spaces=0; j < line_len; j++) {
|
for (j=k=spaces=0; j < line_len; j++) {
|
||||||
if (k == max_columns)
|
if (k == max_chars)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
c = line_begin[j];
|
c = line_begin[j];
|
||||||
|
@ -1420,31 +1480,32 @@ static void viewer_draw(int col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
|
else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
|
||||||
if (col != -1)
|
if ((col != -1) && (line_width > col)) {
|
||||||
if (line_width > col) {
|
str = oldstr = line_begin;
|
||||||
str = oldstr = line_begin;
|
k = col;
|
||||||
k = col;
|
width = 0;
|
||||||
width = 0;
|
while( (width<draw_columns) && (oldstr<line_end) )
|
||||||
while( (width<draw_columns) && (oldstr<line_end) )
|
{
|
||||||
{
|
oldstr = get_ucs(oldstr, &ch);
|
||||||
oldstr = get_ucs(oldstr, &ch);
|
if (k > 0) {
|
||||||
if (k > 0) {
|
k -= glyph_width(ch);
|
||||||
k -= glyph_width(ch);
|
line_begin = oldstr;
|
||||||
line_begin = oldstr;
|
} else {
|
||||||
} else {
|
width += glyph_width(ch);
|
||||||
width += glyph_width(ch);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(prefs.view_mode==WIDE)
|
|
||||||
endptr = rb->iso_decode(line_begin, utf8_buffer,
|
|
||||||
prefs.encoding, oldstr-line_begin);
|
|
||||||
else
|
|
||||||
endptr = rb->iso_decode(line_begin, utf8_buffer,
|
|
||||||
prefs.encoding, line_end-line_begin);
|
|
||||||
*endptr = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(prefs.view_mode==WIDE)
|
||||||
|
endptr = rb->iso_decode(line_begin, utf8_buffer,
|
||||||
|
prefs.encoding, oldstr-line_begin);
|
||||||
|
else
|
||||||
|
endptr = rb->iso_decode(line_begin, utf8_buffer,
|
||||||
|
prefs.encoding, line_end-line_begin);
|
||||||
|
*endptr = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* display on screen the displayed part of the line */
|
||||||
if (col != -1 && line_width > col)
|
if (col != -1 && line_width > col)
|
||||||
{
|
{
|
||||||
int dpage = (cline+i <= display_lines)?cpage:cpage+1;
|
int dpage = (cline+i <= display_lines)?cpage:cpage+1;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue