/* Copyright (C) 2001-2002 Kenichi Suto * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "defs.h" #include "global.h" #include "xml.h" #include "link.h" #include "select.h" #include "eb.h" #define IMAGE_TYPE_JPEG 1 #define IMAGE_TYPE_COLOR_BMP 2 #define IMAGE_TYPE_MONO_BMP 3 #define IMAGE_TYPE_GRAY_BMP 4 #ifndef HAVE_WCTYPE_H static gint iswalpha(GdkWChar c) { GdkWChar wc_buff[2]; char *p; wc_buff[0] = c; wc_buff[1] = 0x00000000; p = gdk_wcstombs(wc_buff); return(isalpha(*p)); } #endif static void print_color(GdkGC *gc){ GdkGCValues gcv; gdk_gc_get_values(gc, &gcv); // g_print("gcv.foreground = %x\n",gcv.foreground); g_print("gcv.foreground.pixel = %04x\n",(guint)gcv.foreground.pixel); g_print("gcv.foreground.red = %04x\n",gcv.foreground.red); g_print("gcv.foreground.green = %04x\n",gcv.foreground.green); g_print("gcv.foreground.blue = %04x\n",gcv.foreground.blue); // g_print("gcv.background = %x\n",gcv.background); g_print("gcv.background.pixel = %04x\n",(guint)gcv.background.pixel); g_print("gcv.background.red = %04x\n",gcv.background.red); g_print("gcv.background.green = %04x\n",gcv.background.green); g_print("gcv.background.blue = %04x\n",gcv.background.blue); } gint calculate_gaiji_size(gint height){ gint size=16; /* if(height <= 20) size = 16; else if(height <= 27) size = 24; else if(height <= 39) size = 30; else size = 48; */ if(height < 24) size = 16; else if(height < 30) size = 24; else if(height < 48) size = 30; else size = 48; return(size); } void load_xbm(BOOK_INFO *binfo, gchar *name, GdkGC *gc, GdkPixmap **pixmap, GdkBitmap **bitmap, gint *w, gint *h){ GtkStyle *style; gint x, y, width, height, depth; gint w_width, w_height; guchar *data = NULL; GdkGCValues gcv; GList **gaiji_cache; GList *gaiji_item; GAIJI_CACHE *gaiji_p; gint found=0; gint char_no; gint size; g_assert(gc != NULL); g_assert(pixmap != NULL); g_assert(bitmap != NULL); // g_print("load_xbm %s\n", name); gdk_gc_get_values(gc, &gcv); #ifdef OLD_GAIJI char_no = strtol(&name[1], NULL, 16); found = 0; if(name[0] == 'h'){ gaiji_p = binfo->gaiji_narrow; } else { gaiji_p = binfo->gaiji_wide; } while(gaiji_p != NULL){ if(gaiji_p->code == char_no){ found = 1; break; } gaiji_p = gaiji_p->next; } #else char_no = strtol(&name[1], NULL, 16); found = 0; // size = calculate_gaiji_size(font_normal->ascent+font_normal->descent); size = calculate_gaiji_size(font_height); size = check_gaiji_size(binfo, size); if(name[0] == 'h'){ switch(size){ case 16: gaiji_cache = &(binfo->gaiji_narrow16); break; case 24: gaiji_cache = &(binfo->gaiji_narrow24); break; case 30: gaiji_cache = &(binfo->gaiji_narrow30); break; case 48: gaiji_cache = &(binfo->gaiji_narrow48); break; } } else { switch(size){ case 16: gaiji_cache = &(binfo->gaiji_wide16); break; case 24: gaiji_cache = &(binfo->gaiji_wide24); break; case 30: gaiji_cache = &(binfo->gaiji_wide30); break; case 48: gaiji_cache = &(binfo->gaiji_wide48); break; } } gaiji_item = g_list_first(*gaiji_cache); while(gaiji_item != NULL){ gaiji_p = gaiji_item->data; if(gaiji_p->code == char_no){ found = 1; break; } gaiji_item = g_list_next(gaiji_item); } #endif if(found){ *pixmap = NULL; data = gaiji_p->data; width = gaiji_p->width; height = gaiji_p->height; style = gtk_widget_get_style(window); gdk_window_get_geometry (window->window, &x, &y, &w_width, &w_height, &depth); *pixmap = gdk_pixmap_create_from_data (window->window, data, width, height, depth, &gcv.foreground, &gcv.background); *bitmap = NULL; *bitmap = gdk_bitmap_create_from_data (window->window, data, width, height); } else { *pixmap = NULL; // XReadBitmapFileData(filename, &width, &height, &data, &x_hot, &y_hot); data = read_gaiji_as_bitmap(binfo, name, size, &width, &height); if(data == NULL){ fprintf(stderr, "failed to read gaiji : %s\n", name); return; } style = gtk_widget_get_style(window); gdk_window_get_geometry (window->window, &x, &y, &w_width, &w_height, &depth); *pixmap = gdk_pixmap_create_from_data (window->window, data, width, height, depth, &gcv.foreground, &gcv.background); *bitmap = NULL; *bitmap = gdk_bitmap_create_from_data (window->window, data, width, height); gaiji_p = (GAIJI_CACHE *)calloc(sizeof(GAIJI_CACHE), 1); if(gaiji_p == NULL){ fprintf(stderr, "No memory\n"); exit(1); } gaiji_p->code = char_no; gaiji_p->data = data; gaiji_p->width = width; gaiji_p->height = height; *gaiji_cache = g_list_append(*gaiji_cache, gaiji_p); } *w = width; *h = height; return; } static void draw_string(CANVAS *canvas, DRAW_TEXT *text, LINK *link) { GdkWChar *wc_buff; GdkWChar *wc_p; GdkWChar wc; GdkWChar *word_p; GdkFont *font; gint length; gint width; gint word_w; gint wc_buff_size; gint i; gint x,y; gint save_x, save_y; char *char_p; g_assert(canvas != NULL); g_assert(text != NULL); #if 0 { gchar *t; t = g_strndup(text->text, text->length); g_print("draw_string %s\n", t); free(t); } #endif x = canvas->x; y = canvas->y; if((link != NULL) && (link->type & LINK_TYPE_EMPHASIS)){ font = font_bold; } else if((link != NULL) && (link->type & LINK_TYPE_ITALIC)){ font = font_italic; } else if((link != NULL) && (link->type & LINK_TYPE_SUBSCRIPT)){ font = font_superscript; } else if((link != NULL) && (link->type & LINK_TYPE_SUPERSCRIPT)){ font = font_superscript; } else { font = font_normal; } wc_buff_size = text->length * sizeof(GdkWChar)+1; wc_buff = malloc(wc_buff_size); length = gdk_mbstowcs(wc_buff, text->text, wc_buff_size - 1); wc_p = wc_buff; if((link != NULL) && (link->type & LINK_TYPE_CENTER)){ gint w=0; for(i=0;iwidth - h_border * 2)){ x = h_border + ((canvas->width - h_border * 2) - w) / 2; } wc_p = wc_buff; } for(i=0;iindent * font_width; // x = h_border; y = y + canvas->line_height + v_space; canvas->line_height = font_height; wc_p ++; continue; } // キャンバスのサイズを超えるようなら改行する width = gdk_text_width_wc(font, wc_p, 1); if((x + width + h_border) > canvas->width) { x = h_border + canvas->indent * font_width; // x = h_border; y = y + canvas->line_height + v_space; canvas->line_height = font_height; } // 英単語の途中で改行されないようにする char_p = (char *)wc_p; if(isalpha(*char_p)){ word_p = wc_p; word_w = 0; while(1){ char_p = (char *)word_p; if(!isalpha(*char_p)) break; word_w += gdk_text_width_wc(font, word_p, 1); word_p ++; } if((x + word_w + h_border) > canvas->width) { // x = h_border; x = h_border + canvas->indent * font_width; y = y + canvas->line_height + v_space; } } if(canvas->pixmap != NULL){ if((link != NULL) && (link->type & LINK_TYPE_SUPERSCRIPT)){ gdk_draw_text_wc(canvas->pixmap, font, canvas->gc, x, y-6, wc_p, 1); } else { gdk_draw_text_wc(canvas->pixmap, font, canvas->gc, x, y, wc_p, 1); } wc = *wc_p; if(canvas->content_area) add_location_char(canvas->content_area, wc, x, y - font_ascent, width+h_space, font_height, font, link); if((link != NULL) && (canvas->content_area != NULL) && ((link->type & LINK_TYPE_JUMP) || (link->type & LINK_TYPE_WAVE) || (link->type & LINK_TYPE_MPEG))){ save_x = x; save_y = y - font_ascent; x = x + width + h_space; link->start_x = save_x; link->start_y = save_y; link->end_x = x - 1; link->end_y = y + font_descent; set_link(canvas->content_area, link); } else { x = x + width + h_space; } } else { x = x + width + h_space; } wc_p ++; } canvas->x = x; canvas->y = y; } static void draw_gaiji(CANVAS *canvas, BOOK_INFO *binfo, LINK *link, gchar *code) { gint width; gint height; GdkPixmap *gaiji_pixmap = NULL; GdkBitmap *mask; gint x, y, yy; gint save_x, save_y; g_assert(canvas != NULL); g_assert(binfo != NULL); g_assert(code != NULL); x = canvas->x; y = canvas->y; /* if(code[0] == 'h'){ width = font_height / 2; } else { width = font_height; } */ load_xbm(binfo, code, canvas->gc, &gaiji_pixmap, &mask, &width, &height); if((x + width + h_border) > canvas->width){ x = h_border + canvas->indent * font_width; // x = h_border; y = y + canvas->line_height + v_space; // canvas->line_height = font_height; canvas->line_height = height; } /* if(height > font_ascent) yy = y - font_ascent; else */ yy = y + font_descent - height; if(canvas->pixmap != NULL){ gdk_draw_pixmap(canvas->pixmap, canvas->gc, gaiji_pixmap, 0,0, x, //y - font_ascent, yy, width, height); if(gaiji_pixmap != NULL) gdk_pixmap_unref(gaiji_pixmap); if(mask != NULL) gdk_bitmap_unref(mask); if(canvas->content_area) add_location_gaiji(canvas->content_area, code, x, yy, // y - font_ascent, // x, y - height + 2, // width, font_height, link); width, height, link); if((link != NULL) && ((link->type & LINK_TYPE_JUMP) || (link->type & LINK_TYPE_WAVE))){ save_x = x; // save_y = y - font_ascent; save_y = yy; x = x + width + h_space; // set_link(save_x, save_y, x-1, y, page, offset); link->start_x = save_x; link->start_y = save_y; link->end_x = x - 1; link->end_y = save_y + height; if(canvas->content_area != NULL) set_link(canvas->content_area, link); } else { x = x + width + h_space; } } else { x = x + width + h_space; } canvas->x = x; canvas->y = y; } // imlibがキャッシュしているようなので、ファイル名を変える。 // キャッシュは有限? でないとリークする。 static int image_count=0; static void draw_graphic(CANVAS *canvas, BOOK_INFO *binfo, gint type, gint page, gint offset, gint width, gint height) { char filename[512]; GdkPixmap *image=NULL; GdkBitmap *mask=NULL; GdkWindowPrivate *private; gint x, y; gint l_width, l_height; EB_Error_Code error_code=EB_SUCCESS; g_assert(canvas != NULL); g_assert(binfo != NULL); x = canvas->x; y = canvas->y; // いったんファイルに落とす sprintf(filename, "%s/%d-%d.img", tmp_dir, getpid(), image_count); image_count++; switch(type){ case IMAGE_TYPE_COLOR_BMP: case IMAGE_TYPE_JPEG: error_code = ebook_output_color(binfo, filename, page, offset); break; case IMAGE_TYPE_MONO_BMP: error_code = ebook_output_mono(binfo, filename, page, offset, width, height); break; case IMAGE_TYPE_GRAY_BMP: error_code = ebook_output_gray(binfo, filename, page, offset, width, height); break; } if(error_code != EB_SUCCESS){ return; } gdk_imlib_load_file_to_pixmap(filename, &image, &mask); g_assert(image != NULL); // g_assert(mask != NULL); gdk_pixmap_ref(image); // 改行したほうが見やすい? x = h_border; y = y + canvas->line_height + v_space; private = (GdkWindowPrivate *)image; l_width = private->width; l_height = private->height; if(canvas->pixmap != NULL){ gdk_draw_pixmap(canvas->pixmap, canvas->gc, image, 0,0, x, (y - font_height), l_width, l_height); } gdk_imlib_free_pixmap(image); if((image != NULL) && (private->ref_count > 0)) gdk_pixmap_unref(image); // gdk_bitmap_unref(mask); // unlink(filename); // x = h_border; // x = h_border + canvas->indent * font_width; x = x + l_width; // y = y + l_height; if(canvas->line_height < l_height) canvas->line_height = l_height; if(x > canvas->max_width) canvas->max_width = x; canvas->x = x; canvas->y = y; return; } void draw_content(CANVAS *canvas, DRAW_TEXT *text, BOOK_INFO *binfo, LINK *link){ gchar *p; gchar start_tag[512]; gchar end_tag[512]; gchar tag_name[512]; gchar attr[512]; gchar code[16]; gchar body[65536]; gchar *content; gint content_length; gint body_length; gint l_page=0, l_offset=0, l_size=0; gint l_width, l_height; gint l_indent; LINK l_link; DRAW_TEXT l_text; GdkColor color_save; GdkGCValues gcv; g_assert(canvas != NULL); g_assert(text != NULL); g_assert(text->text != NULL); l_link.type=0; l_link.page=0; l_link.offset=0; l_link.size=0; body_length = 0; p = text->text; if(text->length >= 65536){ g_print("Text too long. Truncated to 65535 bytes. (Original %d bytes)\n", text->length); text->length = 65535; text->text[65535] = '\0'; } #if 0 { gchar *tmp; tmp = g_strndup(text->text, text->length); g_print("text(length = %d) = \n%s\n\n", text->length, tmp); free(tmp); } #endif while((p - text->text) < text->length){ if(*p == '<'){ if(body_length != 0){ l_text.text = body; l_text.length = body_length; draw_string(canvas, &l_text, link); body_length = 0; } get_start_tag(p, start_tag); get_tag_name(start_tag, tag_name); if((strcmp(tag_name, "reference") == 0) || (strcmp(tag_name, "candidate") == 0)){ get_end_tag(p, tag_name, end_tag); get_attr(end_tag, "page", attr); l_page = strtol(attr, NULL, 16); get_attr(end_tag, "offset", attr); l_offset = strtol(attr, NULL, 16); get_content(p, tag_name, &content, &content_length); if(link) { l_link = *link; l_link.type = LINK_TYPE_JUMP | link->type; } else { l_link.type = LINK_TYPE_JUMP; } l_link.page = l_page; l_link.offset = l_offset; if(canvas->pixmap != NULL){ gdk_gc_get_values(canvas->gc, &gcv); color_save = gcv.foreground; gdk_gc_set_foreground(canvas->gc, &colors[COLOR_BLUE]); } l_text.text = content; l_text.length = content_length; draw_content(canvas, &l_text, binfo, &l_link); if(canvas->pixmap != NULL){ gdk_gc_set_foreground(canvas->gc, &color_save); } skip_end_tag(&p, tag_name); } else if(strcmp(tag_name, "keyword") == 0){ get_content(p, tag_name, &content, &content_length); if(link) { l_link = *link; l_link.type = LINK_TYPE_KEYWORD | link->type; } else { l_link.type = LINK_TYPE_KEYWORD; } if(canvas->pixmap != NULL){ gdk_gc_get_values(canvas->gc, &gcv); color_save = gcv.foreground; gdk_gc_set_foreground(canvas->gc, &colors[COLOR_BROWN]); } l_text.text = content; l_text.length = content_length; draw_content(canvas, &l_text, binfo, &l_link); if(canvas->pixmap != NULL){ gdk_gc_set_foreground(canvas->gc, &color_save); } skip_end_tag(&p, tag_name); } else if(strcmp(tag_name, "modification") == 0){ get_content(p, tag_name, &content, &content_length); get_attr(start_tag, "method", attr); if(link) { l_link = *link; if(attr[0] == '3') l_link.type = LINK_TYPE_EMPHASIS | link->type; else l_link.type = LINK_TYPE_ITALIC | link->type; } else { if(attr[0] == '3') l_link.type = LINK_TYPE_EMPHASIS; else l_link.type = LINK_TYPE_ITALIC; } l_text.text = content; l_text.length = content_length; draw_content(canvas, &l_text, binfo, &l_link); skip_end_tag(&p, tag_name); } else if(strcmp(tag_name, "gaiji") == 0){ get_attr(start_tag, "code", code); draw_gaiji(canvas, binfo, link, code); skip_start_tag(&p, tag_name); } else if(strcmp(tag_name, "indent") == 0){ get_attr(start_tag, "position", attr); l_indent = strtol(attr, NULL, 10); // インデントの引数は位置? // 改行したばかり、または、文字が重ならないなら // 指定位置にセット if((canvas->x == h_border + canvas->indent * font_width) || (canvas->x < h_border + l_indent * font_width)) canvas->x = h_border + l_indent * font_width; canvas->indent = l_indent; skip_start_tag(&p, tag_name); } else if(strcmp(tag_name, "emphasis") == 0){ get_content(p, tag_name, &content, &content_length); l_text = *text; l_text.text = content; l_text.length = content_length; if(link) { l_link = *link; l_link.type = LINK_TYPE_EMPHASIS | link->type; } else { l_link.type = LINK_TYPE_EMPHASIS; } draw_content(canvas, &l_text, binfo, &l_link); skip_end_tag(&p, tag_name); } else if(strcmp(tag_name, "subscript") == 0){ get_content(p, tag_name, &content, &content_length); l_text = *text; l_text.text = content; l_text.length = content_length; if(link) { l_link = *link; l_link.type = LINK_TYPE_SUBSCRIPT | link->type; } else { l_link.type = LINK_TYPE_SUBSCRIPT; } draw_content(canvas, &l_text, binfo, &l_link); skip_end_tag(&p, tag_name); } else if(strcmp(tag_name, "superscript") == 0){ get_content(p, tag_name, &content, &content_length); l_text = *text; l_text.text = content; l_text.length = content_length; if(link) { l_link = *link; l_link.type = LINK_TYPE_SUPERSCRIPT | link->type; } else { l_link.type = LINK_TYPE_SUPERSCRIPT; } draw_content(canvas, &l_text, binfo, &l_link); skip_end_tag(&p, tag_name); } else if(strcmp(tag_name, "center") == 0){ get_content(p, tag_name, &content, &content_length); l_text = *text; l_text.text = content; l_text.length = content_length; if(link) { l_link = *link; l_link.type = LINK_TYPE_CENTER | link->type; } else { l_link.type = LINK_TYPE_CENTER; } draw_content(canvas, &l_text, binfo, &l_link); skip_end_tag(&p, tag_name); } else if(strcmp(tag_name, "nonewline") == 0){ skip_start_tag(&p, tag_name); } else if(strcmp(tag_name, "/nonewline") == 0){ skip_start_tag(&p, tag_name); } else if(strcmp(tag_name, "narrow") == 0){ skip_start_tag(&p, tag_name); } else if(strcmp(tag_name, "/narrow") == 0){ skip_start_tag(&p, tag_name); } else if(strcmp(tag_name, "jpeg") == 0){ get_attr(start_tag, "page", attr); l_page = strtol(attr, NULL, 16); get_attr(p, "offset", attr); l_offset = strtol(attr, NULL, 16); draw_graphic(canvas, binfo, IMAGE_TYPE_JPEG, l_page, l_offset, 0, 0); skip_start_tag(&p, tag_name); } else if(strcmp(tag_name, "bmp") == 0){ get_attr(start_tag, "page", attr); l_page = strtol(attr, NULL, 16); get_attr(p, "offset", attr); l_offset = strtol(attr, NULL, 16); draw_graphic(canvas, binfo, IMAGE_TYPE_COLOR_BMP, l_page, l_offset, 0, 0); skip_start_tag(&p, tag_name); } else if(strcmp(tag_name, "mono") == 0){ get_attr(start_tag, "width", attr); l_width = strtol(attr, NULL, 10); get_attr(start_tag, "height", attr); l_height = strtol(attr, NULL, 10); get_end_tag(p, tag_name, end_tag); get_attr(end_tag, "page", attr); l_page = strtol(attr, NULL, 16); get_attr(end_tag, "offset", attr); l_offset = strtol(attr, NULL, 16); draw_graphic(canvas, binfo, IMAGE_TYPE_MONO_BMP, l_page, l_offset, l_width, l_height); skip_end_tag(&p, tag_name); } else if(strcmp(tag_name, "wave") == 0){ get_end_tag(p, tag_name, end_tag); get_attr(end_tag, "page", attr); l_page = strtol(attr, NULL, 16); get_attr(end_tag, "offset", attr); l_offset = strtol(attr, NULL, 16); get_attr(end_tag, "size", attr); l_size = strtol(attr, NULL, 10); get_content(p, tag_name, &content, &content_length); if(link) { l_link = *link; l_link.type = LINK_TYPE_WAVE | link->type; } else { l_link.type = LINK_TYPE_WAVE; } l_link.page = l_page; l_link.offset = l_offset; l_link.size = l_size; if(canvas->pixmap != NULL){ gdk_gc_get_values(canvas->gc, &gcv); color_save = gcv.foreground; gdk_gc_set_foreground(canvas->gc, &colors[COLOR_GREEN]); } l_text.text = content; l_text.length = content_length; draw_content(canvas, &l_text, binfo, &l_link); if(canvas->pixmap != NULL){ gdk_gc_set_foreground(canvas->gc, &color_save); } skip_end_tag(&p, tag_name); } else if(strcmp(tag_name, "mpeg") == 0){ get_attr(start_tag, "filename", attr); // get_content(p, tag_name, &content, &content_length); if(link) { l_link = *link; l_link.type = LINK_TYPE_MPEG | link->type; } else { l_link.type = LINK_TYPE_MPEG; } l_link.page = l_page; l_link.offset = l_offset; l_link.size = l_size; sprintf(l_link.filename, "%s", attr); if(canvas->pixmap != NULL){ gdk_gc_get_values(canvas->gc, &gcv); color_save = gcv.foreground; gdk_gc_set_foreground(canvas->gc, &colors[COLOR_GREEN]); } l_text.text = _(" [Movie] "); l_text.length = strlen(l_text.text); draw_content(canvas, &l_text, binfo, &l_link); if(canvas->pixmap != NULL){ gdk_gc_set_foreground(canvas->gc, &color_save); } skip_start_tag(&p, tag_name); } else { // fprintf(stderr, "warning: unknown tag <%s> found. Treated as plain text\n", tag_name); body[body_length] = *p; body_length ++; body[body_length] = '\0'; p++; } } else { body[body_length] = *p; body_length ++; body[body_length] = '\0'; p++; } } if(body_length != 0){ l_text.text = body; l_text.length = body_length; draw_string(canvas, &l_text, link); } }