1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 Gilles Roux
12 * 2010 Yoshihisa Uchida
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
25 #include "tv_preferences.h"
26 #include "tv_text_processor.h"
35 /* the max characters of each blocks */
36 #ifdef HAVE_LCD_BITMAP
37 #define TV_MAX_CHARS_PER_BLOCK (LCD_WIDTH / 2 + 1)
39 #define TV_MAX_CHARS_PER_BLOCK (LCD_WIDTH + 1)
42 #define TV_MAX_BLOCKS 5
45 * number of spaces to indent first paragraph
46 * (this value uses the line mode is REFLOW only)
48 #define TV_INDENT_SPACES 2
50 static const struct tv_preferences
*prefs
;
51 static enum tv_text_type text_type
= TV_TEXT_UNKNOWN
;
53 static const unsigned char *end_ptr
;
55 static unsigned short *ucsbuf
[TV_MAX_BLOCKS
];
56 static unsigned char *utf8buf
;
57 static unsigned char *outbuf
;
59 static int block_count
;
60 static int block_width
;
62 /* if this value is true, then tv_create_line_text returns a blank line. */
63 static bool expand_extra_line
= false;
65 /* when a line is divided, this value sets true. */
66 static bool is_break_line
= false;
68 static unsigned short break_chars
[] =
71 /* halfwidth characters */
72 '\t', '\n', 0x0b, 0x0c, ' ', '!', ',', '-', '.', ':', ';', '?', 0xb7,
73 /* fullwidth characters */
75 0x3000, /* fullwidth space */
76 0x3001, /* ideographic comma */
77 0x3002, /* ideographic full stop */
78 0x30fb, /* katakana middle dot */
79 0x30fc, /* katakana-hiragana prolonged sound mark */
80 0xff01, /* fullwidth exclamation mark */
81 0xff0c, /* fullwidth comma */
82 0xff0d, /* fullwidth hyphen-minus */
83 0xff0e, /* fullwidth full stop */
84 0xff1a, /* fullwidth colon */
85 0xff1b, /* fullwidth semicolon */
86 0xff1f, /* fullwidth question mark */
89 /* the characters which is not judged as space with isspace() */
90 static unsigned short extra_spaces
[] = { 0, 0x3000 };
92 static int tv_glyph_width(int ch
)
100 #ifdef HAVE_LCD_BITMAP
101 /* the width of the diacritics charcter is 0 */
102 if (rb
->is_diacritic(ch
, NULL
))
105 return rb
->font_get_width(prefs
->font
, ch
);
111 static unsigned char *tv_get_ucs(const unsigned char *str
, unsigned short *ch
)
114 unsigned char utf8_tmp
[3];
116 /* distinguish the text_type */
119 if (text_type
== TV_TEXT_WIN
|| text_type
== TV_TEXT_UNKNOWN
)
121 if (str
+ 1 < end_ptr
&& *(str
+1) == '\n')
123 if (text_type
== TV_TEXT_UNKNOWN
)
124 text_type
= TV_TEXT_WIN
;
127 return (unsigned char *)str
+ 2;
130 if (text_type
== TV_TEXT_UNKNOWN
)
131 text_type
= TV_TEXT_MAC
;
133 *ch
= (text_type
== TV_TEXT_MAC
)? '\n' : ' ';
134 return (unsigned char *)str
+ 1;
136 else if (*str
== '\n')
138 if (text_type
== TV_TEXT_UNKNOWN
)
139 text_type
= TV_TEXT_UNIX
;
141 *ch
= (text_type
== TV_TEXT_UNIX
)? '\n' : ' ';
142 return (unsigned char *)str
+ 1;
145 if (prefs
->encoding
== UTF_8
)
146 return (unsigned char*)rb
->utf8decode(str
, ch
);
148 #ifdef HAVE_LCD_BITMAP
149 if ((*str
>= 0x80) &&
150 ((prefs
->encoding
> SJIS
) ||
151 (prefs
->encoding
== SJIS
&& (*str
<= 0xa0 || *str
>= 0xe0))))
153 if (str
+ 1 >= end_ptr
)
157 return (unsigned char *)str
;
162 rb
->iso_decode(str
, utf8_tmp
, prefs
->encoding
, count
);
163 rb
->utf8decode(utf8_tmp
, ch
);
164 return (unsigned char *)str
+ count
;
167 static void tv_decode2utf8(const unsigned short *ucs
, int count
)
171 for (i
= 0; i
< count
; i
++)
172 outbuf
= rb
->utf8encode(ucs
[i
], outbuf
);
177 static bool tv_is_line_break_char(unsigned short ch
)
181 /* when the word mode is CHOP, all characters does not break line. */
182 if (prefs
->word_mode
== CHOP
)
185 for (i
= 0; i
< sizeof(break_chars
); i
++)
187 if (break_chars
[i
] == ch
)
193 static bool tv_isspace(unsigned short ch
)
197 if (ch
< 128 && isspace(ch
))
200 for (i
= 0; i
< sizeof(extra_spaces
); i
++)
202 if (extra_spaces
[i
] == ch
)
208 static bool tv_is_break_line_join_mode(const unsigned char *next_str
)
212 tv_get_ucs(next_str
, &ch
);
213 return tv_isspace(ch
);
216 static int tv_form_reflow_line(unsigned short *ucs
, int chars
)
218 unsigned short new_ucs
[TV_MAX_CHARS_PER_BLOCK
];
219 unsigned short *p
= new_ucs
;
224 int indent_chars
= 0;
225 int nonspace_chars
= 0;
226 int nonspace_width
= 0;
231 if (prefs
->alignment
== LEFT
)
233 while (chars
> 0 && ucs
[chars
-1] == ' ')
240 while (ucs
[indent_chars
] == ' ')
243 for (i
= indent_chars
; i
< chars
; i
++)
251 nonspace_width
+= tv_glyph_width(ch
);
258 expand_spaces
= (block_width
- nonspace_width
) / tv_glyph_width(' ') - indent_chars
;
259 if (indent_chars
+ nonspace_chars
+ expand_spaces
> TV_MAX_CHARS_PER_BLOCK
)
260 expand_spaces
= TV_MAX_CHARS_PER_BLOCK
- indent_chars
- nonspace_chars
;
262 words_spaces
= expand_spaces
/ spaces
;
263 remain_spaces
= expand_spaces
- words_spaces
* spaces
;
265 for (i
= 0; i
< indent_chars
; i
++)
268 for ( ; i
< chars
; i
++)
274 for (k
= ((remain_spaces
> 0)? 0 : 1); k
< words_spaces
; k
++)
281 rb
->memcpy(ucs
, new_ucs
, sizeof(unsigned short) * TV_MAX_CHARS_PER_BLOCK
);
282 return indent_chars
+ nonspace_chars
+ expand_spaces
;
285 static void tv_align_right(int *block_chars
)
287 unsigned short *cur_text
;
288 unsigned short *prev_text
;
290 int cur_block
= block_count
- 1;
300 while (cur_block
> 0)
302 cur_text
= ucsbuf
[cur_block
];
303 cur_chars
= block_chars
[cur_block
];
307 width
+= tv_glyph_width(cur_text
[idx
]);
309 width
= block_width
- width
;
310 prev_block
= cur_block
- 1;
313 prev_text
= ucsbuf
[prev_block
];
314 prev_chars
= block_chars
[prev_block
];
318 break_pos
= prev_chars
;
319 while (append_width
< width
&& idx
> 0)
321 ch
= prev_text
[--idx
];
322 if (tv_is_line_break_char(ch
))
325 break_width
= append_width
;
327 append_width
+= tv_glyph_width(ch
);
329 if (append_width
> width
)
335 break_width
= append_width
;
338 if (break_pos
< prev_chars
)
339 append_width
= break_width
;
341 * (1) when the first character of the cur_text concatenates
342 * the last character of the prev_text.
343 * (2) the length of ucsbuf[block] is short (< 0.75 * block width)
345 else if (((!tv_isspace(*cur_text
) && !tv_isspace(prev_text
[prev_chars
- 1])) ||
346 (4 * width
>= 3 * block_width
)))
351 if (break_pos
< prev_chars
)
353 rb
->memmove(cur_text
+ prev_chars
- break_pos
,
354 cur_text
, block_chars
[cur_block
] * sizeof(unsigned short));
355 rb
->memcpy(cur_text
, prev_text
+ break_pos
,
356 (prev_chars
- break_pos
) * sizeof(unsigned short));
358 block_chars
[prev_block
] = break_pos
;
359 block_chars
[cur_block
] += prev_chars
- break_pos
;
361 } while ((width
-= append_width
) > 0 && --prev_block
>= 0);
366 static int tv_parse_text(const unsigned char *src
, unsigned short *ucs
,
367 int *ucs_chars
, bool is_indent
)
369 const unsigned char *cur
= src
;
370 const unsigned char *next
= src
;
371 const unsigned char *line_break_ptr
= NULL
;
372 const unsigned char *line_end_ptr
= NULL
;
373 unsigned short ch
= 0;
374 unsigned short prev_ch
;
378 int line_break_width
= 0;
379 int line_end_chars
= 0;
381 bool is_space
= false;
388 line_end_chars
= chars
;
389 is_break_line
= true;
394 next
= tv_get_ucs(cur
, &ch
);
397 if (prefs
->line_mode
!= JOIN
|| tv_is_break_line_join_mode(next
))
400 line_end_chars
= chars
;
401 is_break_line
= false;
405 if (prefs
->word_mode
== CHOP
|| tv_isspace(prev_ch
))
409 * when the line mode is JOIN and the word mode is WRAP,
410 * the next character does not concatenate with the
411 * previous character.
415 else if ((is_space
= tv_isspace(ch
)) == true)
418 * when the line mode is REFLOW:
419 * (1) spacelike character convert to ' '
420 * (2) plural spaces are collected to one
422 if (prefs
->line_mode
== REFLOW
)
429 /* when the alignment is RIGHT, ignores indent spaces. */
430 if (prefs
->alignment
== RIGHT
&& is_indent
)
436 if (prefs
->line_mode
== REFLOW
&& is_indent
)
437 gw
= tv_glyph_width(ch
) * TV_INDENT_SPACES
;
439 gw
= tv_glyph_width(ch
);
442 if (width
> block_width
)
448 line_end_chars
= chars
;
450 is_break_line
= true;
454 if (prefs
->line_mode
== REFLOW
&& is_indent
)
456 for (i
= 1; i
< TV_INDENT_SPACES
; i
++)
461 if (tv_is_line_break_char(ch
))
463 line_break_ptr
= next
;
464 line_break_width
= width
;
465 line_end_chars
= chars
;
467 if (chars
>= TV_MAX_CHARS_PER_BLOCK
)
469 is_break_line
= true;
474 /* set the end position and character count */
475 if (line_end_ptr
== NULL
)
478 * when the last line break position is too short (line length < 0.75 * block width),
479 * the line is cut off at the position where it is closest to the displayed width.
481 if ((prefs
->line_mode
== REFLOW
&& line_break_ptr
== NULL
) ||
482 (4 * line_break_width
< 3 * block_width
))
485 line_end_chars
= chars
;
488 line_end_ptr
= line_break_ptr
;
491 *ucs_chars
= line_end_chars
;
492 return line_end_ptr
- src
;
495 int tv_create_formed_text(const unsigned char *src
, ssize_t bufsize
,
496 int block
, bool is_multi
, const unsigned char **dst
)
499 int chars
[block_count
];
507 for (i
= 0; i
< block_count
; i
++)
513 if (prefs
->line_mode
== EXPAND
&& (expand_extra_line
= !expand_extra_line
) == true)
516 end_ptr
= src
+ bufsize
;
518 tv_get_ucs(src
, &ch
);
519 is_indent
= (tv_isspace(ch
) && !is_break_line
);
521 for (i
= 0; i
< block_count
; i
++)
523 size
+= tv_parse_text(src
+ size
, ucsbuf
[i
], &chars
[i
], is_indent
);
532 if (prefs
->alignment
== RIGHT
)
533 tv_align_right(chars
);
535 for (i
= 0; i
< block_count
; i
++)
537 if (i
== block
|| (is_multi
&& i
== block
+ 1))
539 if (is_break_line
&& prefs
->line_mode
== REFLOW
)
540 chars
[i
] = tv_form_reflow_line(ucsbuf
[i
], chars
[i
]);
542 tv_decode2utf8(ucsbuf
[i
], chars
[i
]);
550 bool tv_init_text_processor(unsigned char *buf
, size_t bufsize
, size_t *used_size
)
554 *used_size
= TV_MAX_CHARS_PER_BLOCK
* (2 * 3 + TV_MAX_BLOCKS
* sizeof(unsigned short));
555 if (bufsize
< *used_size
)
558 prefs
= tv_get_preferences();
559 text_type
= TV_TEXT_UNKNOWN
;
560 expand_extra_line
= false;
561 is_break_line
= false;
563 ucsbuf
[0] = (unsigned short*)buf
;
564 for (i
= 1; i
< TV_MAX_BLOCKS
; i
++)
565 ucsbuf
[i
] = ucsbuf
[i
- 1] + TV_MAX_CHARS_PER_BLOCK
;
567 utf8buf
= buf
+ TV_MAX_CHARS_PER_BLOCK
* TV_MAX_BLOCKS
* sizeof(unsigned short);
572 void tv_set_creation_conditions(int blocks
, int width
)
574 block_count
= blocks
;