/* * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors * Copyright (C) 2007-2022 Kim Woelders * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies of the Software, its documentation and marketing & publicity * materials, and acknowledgment shall be given in the documentation, materials * and software packages that this Software was used. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "config.h" #include #include #include "dox.h" #define DEFAULT_LINKCOLOR_R 30 #define DEFAULT_LINKCOLOR_G 50 #define DEFAULT_LINKCOLOR_B 160 typedef enum _type { IMG, BR, FONT, P, TEXT, PAGE } Type; typedef struct _img { char *src; char *src2; char *src3; int x, y; char *link; int w, h; } Img_; typedef struct _font { char *face; int r, g, b; } Font_; typedef struct _p { float align; } P_; typedef struct _object { Type type; void *object; } Object; typedef struct _page { char *name; int count; Object *obj; int columns; int padding; int linkr, linkg, linkb; char *background; } Page; static int num_pages = 0; static Page *page = NULL; static char *fdat_ptr = NULL; static int fdat_size = 0; static char *fdat = NULL; static int fdgetc(void) { int val; if (fdat_ptr >= (fdat + fdat_size)) return EOF; val = (int)(*fdat_ptr); fdat_ptr++; return val; } static void fdjump(int count) { fdat_ptr += count; if (fdat_ptr < fdat) fdat_ptr = fdat; if (fdat_ptr >= (fdat + fdat_size)) fdat_ptr = (fdat + fdat_size) - 1; } static void AddPage(Object *obj) { num_pages++; page = EREALLOC(Page, page, num_pages); page[num_pages - 1].name = NULL; page[num_pages - 1].count = 0; page[num_pages - 1].obj = NULL; page[num_pages - 1].columns = 1; page[num_pages - 1].background = NULL; page[num_pages - 1].padding = 2; page[num_pages - 1].linkr = DEFAULT_LINKCOLOR_R; page[num_pages - 1].linkg = DEFAULT_LINKCOLOR_G; page[num_pages - 1].linkb = DEFAULT_LINKCOLOR_B; if ((obj) && (obj->type == PAGE) && (obj->object)) { Page *pg; pg = (Page *) (obj->object); if (pg->name) page[num_pages - 1].name = pg->name; page[num_pages - 1].columns = pg->columns; page[num_pages - 1].padding = pg->padding; page[num_pages - 1].linkr = pg->linkr; page[num_pages - 1].linkg = pg->linkg; page[num_pages - 1].linkb = pg->linkb; if (pg->background) page[num_pages - 1].background = pg->background; } } static void AddObject(Object *obj) { page[num_pages - 1].count++; page[num_pages - 1].obj = EREALLOC(Object, page[num_pages - 1].obj, page[num_pages - 1].count); page[num_pages - 1].obj[page[num_pages - 1].count - 1].type = obj->type; page[num_pages - 1].obj[page[num_pages - 1].count - 1].object = obj->object; } static void BuildObj(Object *obj, char *var, char *param) { static Page *pg = NULL; static P_ *p = NULL; static Font_ *fn = NULL; static Img_ *img = NULL; switch (obj->type) { case IMG: img = (Img_ *) obj->object; if (!img) { img = EMALLOC(Img_, 1); obj->object = img; img->src = NULL; img->src2 = NULL; img->src3 = NULL; img->x = 0; img->y = 0; img->link = NULL; img->w = 0; img->h = 0; } if (!strcmp(var, "x")) img->x = atoi(param); else if (!strcmp(var, "y")) img->y = atoi(param); else if (!strcmp(var, "src")) img->src = strdup(param); else if (!strcmp(var, "src2")) img->src2 = strdup(param); else if (!strcmp(var, "src3")) img->src3 = strdup(param); else if (!strcmp(var, "href")) img->link = strdup(param); break; case BR: break; case FONT: fn = (Font_ *) obj->object; if (!fn) { fn = EMALLOC(Font_, 1); obj->object = fn; fn->face = NULL; fn->r = 0; fn->g = 0; fn->b = 0; } if (!strcmp(var, "face")) fn->face = strdup(param); else if (!strcmp(var, "color")) { char hex[3] = "00"; if (param[0] == '#') { hex[0] = param[1]; hex[1] = param[2]; sscanf(hex, "%x", &(fn->r)); hex[0] = param[3]; hex[1] = param[4]; sscanf(hex, "%x", &(fn->g)); hex[0] = param[5]; hex[1] = param[6]; sscanf(hex, "%x", &(fn->b)); } } break; case P: p = (P_ *) obj->object; if (!p) { p = EMALLOC(P_, 1); obj->object = p; p->align = 0; } if (!strcmp(var, "align")) { if ((strlen(param) > 0) && (param[strlen(param) - 1] == '%')) param[strlen(param) - 1] = 0; p->align = atof(param); } break; case TEXT: break; case PAGE: pg = (Page *) obj->object; if (!pg) { pg = EMALLOC(Page, 1); obj->object = pg; pg->columns = 1; pg->padding = 1; pg->name = NULL; pg->background = NULL; pg->linkr = DEFAULT_LINKCOLOR_R; pg->linkg = DEFAULT_LINKCOLOR_G; pg->linkb = DEFAULT_LINKCOLOR_B; } if (!strcmp(var, "columns")) pg->columns = atoi(param); else if (!strcmp(var, "padding")) pg->padding = atoi(param); else if (!strcmp(var, "name")) pg->name = strdup(param); else if (!strcmp(var, "background")) pg->background = strdup(param); else if (!strcmp(var, "linkcolor")) { char hex[3] = "00"; if (param[0] == '#') { hex[0] = param[1]; hex[1] = param[2]; sscanf(hex, "%x", &(pg->linkr)); hex[0] = param[3]; hex[1] = param[4]; sscanf(hex, "%x", &(pg->linkg)); hex[0] = param[5]; hex[1] = param[6]; sscanf(hex, "%x", &(pg->linkb)); } } break; default: break; } } static int GetNextTag(Object *obj) { char s[65536]; int i = 0, wd = 0; int val; char intag = 0; char havobj = 0; for (;;) { val = fdgetc(); if (val == EOF) return 0; if (intag) { if (val == '>') intag = 0; s[i++] = (char)val; if (s[i - 1] == '\n') s[i - 1] = ' '; if (s[i - 1] == '>') s[i - 1] = ' '; if (s[i - 1] == ' ') { if (i == 1) i = 0; else { s[i - 1] = 0; if (!havobj) { if (wd == 0) { if (!strcmp(s, "page")) obj->type = PAGE; else if (!strcmp(s, "img")) obj->type = IMG; else if (!strcmp(s, "br")) obj->type = BR; else if (!strcmp(s, "font")) obj->type = FONT; else if (!strcmp(s, "p")) obj->type = P; havobj = 1; } i = 0; } else { char w1[1024]; char w2[1024]; int j = 0; w1[0] = 0; w2[0] = 0; while ((s[j]) && (s[j] != '=')) { w1[j] = s[j]; j++; } w1[j] = 0; if (j < (int)strlen(s)) strcpy(w2, &(s[j + 1])); BuildObj(obj, w1, w2); i = 0; } wd++; } } if (!intag) return 1; } if (val == '<') intag = 1; } return 1; } static char * GetTextUntilTag(void) { char s[65536]; int i = 0; int val; for (;;) { val = fdgetc(); if (val == EOF) { s[i] = 0; if (strlen(s) < 1) return NULL; return strdup(s); } s[i++] = (char)val; if (s[i - 1] == '\n') s[i - 1] = ' '; if ((i == 1) && (s[0] == ' ')) i--; else if (s[i - 1] == '<') { s[i - 1] = 0; fdjump(-1); if (strlen(s) < 1) return NULL; return strdup(s); } if ((i > 2) && (s[i - 2] == ' ') && (s[i - 1] == ' ')) i--; if (i > 65530) return NULL; } return NULL; } void GetObjects(FILE *f) { static char have_font = 0; static char in_para = 0; Object obj; char *txt; char buf[4096]; int count; fdat = NULL; fdat_size = 0; while ((count = fread(buf, 1, 4096, f)) > 0) { if (!fdat) fdat = EMALLOC(char, count); else fdat = EREALLOC(char, fdat, fdat_size + count); memcpy(fdat + fdat_size, buf, count); fdat_size += count; } fdat_ptr = fdat; if (page) { int i; for (i = 0; i < num_pages; i++) { int j; Efree(page[i].name); Efree(page[i].background); for (j = 0; j < page[i].count; j++) { switch (page[i].obj[j].type) { case IMG: Efree(((Img_ *) page[i].obj[j].object)->src); Efree(((Img_ *) page[i].obj[j].object)->src2); Efree(((Img_ *) page[i].obj[j].object)->src3); Efree(((Img_ *) page[i].obj[j].object)->link); break; case BR: break; case FONT: Efree(((Font_ *) page[i].obj[j].object)->face); break; case P: break; case TEXT: break; case PAGE: break; } Efree(page[i].obj[j].object); } Efree(page[i].obj); } Efree(page); num_pages = 0; page = NULL; have_font = 0; in_para = 0; } obj.type = P; obj.object = NULL; for (;;) { if ((have_font) && (in_para)) { txt = GetTextUntilTag(); if (txt) { obj.type = TEXT; free(obj.object); obj.object = (void *)txt; } else { if (!GetNextTag(&obj)) break; } } else { if (!GetNextTag(&obj)) break; } if (obj.type == PAGE) { in_para = 0; have_font = 0; AddPage(&obj); } else if (page) AddObject(&obj); if (obj.type == IMG) in_para = 0; if (obj.type == P) in_para = 1; if (obj.type == FONT) have_font = 1; obj.object = NULL; } Efree(fdat); } int FixPage(int p) { if (p < 0) return 0; if (p >= num_pages) return num_pages - 1; return p; } int GetPage(char *name) { int i; for (i = 0; i < num_pages; i++) { if ((page[i].name) && (!strcmp(name, page[i].name))) return i; } return -1; } void GetLinkColors(int page_num, int *r, int *g, int *b) { if (page_num < 0) { *r = DEFAULT_LINKCOLOR_R; *g = DEFAULT_LINKCOLOR_G; *b = DEFAULT_LINKCOLOR_B; } else { *r = page[page_num].linkr; *g = page[page_num].linkg; *b = page[page_num].linkb; } } static void CalcOffset(Page *pg, int col_w, int x, int y, int th, int *pxspace, int *poff) { int xspace, off; int sx, sy, ssx, ssy; Img_ *img; int j; off = 0; sx = x + off; sy = y; ssx = sx + col_w - 1; ssy = sy + th - 1; for (j = 0; j < pg->count; j++) { if (pg->obj[j].type != IMG) continue; img = (Img_ *) pg->obj[j].object; if ((img->w > 0) && (img->h > 0)) { int ix, iy, iix, iiy; ix = img->x; iy = img->y; iix = img->x + img->w - 1; iiy = img->y + img->h - 1; if ((iy <= ssy) && (iiy >= sy)) { if ((ix >= sx) && (ix <= ssx)) { if ((iix >= sx) && (iix <= ssx)) { if (((ix + iix) / 2) > ((sx + ssx) / 2)) ssx = ix - 1; else sx = iix + 1; } else { ssx = ix - 1; } } else if ((iix >= sx) && (iix <= ssx)) { sx = iix + 1; } } } } off = sx - x; xspace = (ssx - sx) + 1; if (xspace < 0) xspace = 0; *pxspace = xspace; *poff = off; } static void next_y(int w __UNUSED__, int h, int col_w, int *px, int *py, int *pcol, const Page *pg, const TextState *ts, const char *txt __UNUSED__) { int x = *px; int y = *py; int col = *pcol; int y_max; y += ts->height; /* y_max = h - (pg->padding + ts->height); */ /* Allow descent to enter padding area */ y_max = h - (pg->padding + ts->xfontset_ascent); if (y >= y_max) { col = (col + 1) % pg->columns; if (col > 0) { /* Goto top of next col */ y = pg->padding; x += col * col_w + pg->padding; } else { /* Goto bottom of first col (hack) */ x = pg->padding; y = y_max; } *px = x; *pcol = col; } *py = y; } Link * RenderPage(Window win, int page_num, int w, int h) { Link *ll = NULL; Page *pg; TextState ts; int i, col_w, col; int x, y; int justification = 0; int firstp = 1; Imlib_Image im; int wastext = 0; memset(&ts, 0, sizeof(ts)); pg = &(page[page_num]); x = pg->padding; y = pg->padding; col = 0; col_w = ((w - (pg->padding * (pg->columns + 1))) / pg->columns); if (pg->background) { im = ImageLoadDoc(pg->background); if (im) { imlib_context_set_image(im); imlib_context_set_drawable(win); imlib_render_image_on_drawable_at_size(0, 0, w, h); imlib_free_image(); } } for (i = 0; i < pg->count; i++) { char s[32768], ss[32768], wd[4096], *txt; Img_ *img; Font_ *fn; P_ *p; int wc, eol, eot; int islink = 0, lx, lw; switch (pg->obj[i].type) { default: break; case IMG: img = (Img_ *) pg->obj[i].object; if (img->src) { im = ImageLoadDoc(img->src); if (im) { imlib_context_set_image(im); img->w = imlib_image_get_width(); img->h = imlib_image_get_height(); imlib_context_set_drawable(win); imlib_render_image_on_drawable_at_size(img->x, img->y, img->w, img->h); imlib_free_image(); } if (img->link) { Link *l; l = EMALLOC(Link, 1); l->name = strdup(img->link); l->x = img->x; l->y = img->y; l->w = img->w; l->h = img->h; l->next = ll; ll = l; } } break; case BR: if (!wastext) next_y(w, h, col_w, &x, &y, &col, pg, &ts, "
"); wastext = 0; break; case FONT: fn = (Font_ *) pg->obj[i].object; ts.fontname = NULL; ESetColor(&ts.fg_col, 0, 0, 0); ESetColor(&ts.bg_col, 0, 0, 0); ts.effect = 0; if (ts.efont) Efont_free(ts.efont); ts.efont = NULL; if (ts.xfontset) XFreeFontSet(disp, ts.xfontset); ts.xfontset = NULL; ts.xfontset_ascent = 0; ts.height = 0; ts.fontname = fn->face; ESetColor(&ts.fg_col, fn->r, fn->g, fn->b); TextStateLoadFont(&ts); break; case P: p = (P_ *) pg->obj[i].object; if (p) justification = (int)((p->align / 100) * 1024); else justification = 0; if (!firstp) next_y(w, h, col_w, &x, &y, &col, pg, &ts, "

"); else firstp = 0; break; case TEXT: txt = (char *)pg->obj[i].object; wc = 1; ss[0] = 0; s[0] = 0; eol = 0; eot = 0; for (;;) { char *txt_disp; int tw, th, xspace; int off, j; char link_txt[1024]; char link_link[1024]; int spaceflag, oldwc = 0, linkwc; wd[0] = 0; spaceflag = 1; #ifdef USE_WORD_MB if (MB_CUR_MAX > 1) /* If multibyte locale,... */ word_mb(txt, wc, wd, &spaceflag); else #endif word(txt, wc, wd); if (!wd[0]) eol = 1; wc++; eot++; strcpy(ss, s); if ((eot != 1) && spaceflag) strcat(s, " "); linkwc = -1; if (wd[0] == '_') { link_txt[0] = '\0'; link_link[0] = '\0'; islink = 1; oldwc = wc; TextSize(&ts, s, &lx, &th); } if (islink == 1) { if (eol || ((wd[0] != '_') && spaceflag)) /* if NO link tag, ... */ { link_txt[0] = '\0'; link_link[0] = '\0'; islink = 0; wc = oldwc; #ifdef USE_WORD_MB spaceflag = 1; if (MB_CUR_MAX > 1) word_mb(txt, wc - 1, wd, &spaceflag); else #endif word(txt, wc - 1, wd); } else { int k, linkflg; j = 0; linkflg = 0; if (wd[0] == '_') { j++; linkflg++; } k = strlen(link_txt); for (; wd[j] != '(' && wd[j] != '\0'; j++, k++) { if (wd[j] == '_') link_txt[k] = ' '; else link_txt[k] = wd[j]; if (linkflg) wd[j - 1] = link_txt[k]; else wd[j] = link_txt[k]; } link_txt[k] = '\0'; if (linkflg) wd[j - 1] = '\0'; if (wd[j] == '(') { wd[j++] = '\0'; strcpy(link_link, wd + j); k = strlen(link_link) - 1; if (k <= 0) continue; for (j = k; j > 0 && link_link[j] != ')'; j--) ; link_link[j] = '\0'; strcpy(wd, link_txt); if (j < k) strcat(wd, link_link + j + 1); islink = 2; linkwc = wc; } else continue; } } strcat(s, wd); CalcOffset(pg, col_w, x, y, ts.height, &xspace, &off); TextSize(&ts, s, &tw, &th); txt_disp = ss; if (eot == 1) txt_disp = s; if (((tw > xspace) || (eol)) && (strlen(txt_disp) > 0)) { if (txt_disp[strlen(txt_disp) - 1] == ' ') txt_disp[strlen(txt_disp) - 1] = 0; if ((eot == 1) && (tw > xspace)) { char p1[4096]; int point = 0, cnt = 0, ii, len; while (txt_disp[(point + cnt)]) { len = mblen(txt_disp + point + cnt, MB_CUR_MAX); if (len < 0) { cnt++; continue; } else for (ii = 0; ii < len; ii++, cnt++) p1[cnt] = txt_disp[point + cnt]; p1[cnt] = 0; TextSize(&ts, p1, &tw, &th); if ((tw > xspace) || (!txt_disp[(point + cnt)])) { if (txt_disp[(point + cnt)]) { point = point + cnt - len; p1[cnt - len] = 0; cnt = 0; } else { point = point + cnt; p1[cnt] = 0; cnt = 0; } wastext = 1; TextDraw(&ts, win, p1, x + off, y, xspace, 99999, justification); next_y(w, h, col_w, &x, &y, &col, pg, &ts, p1); CalcOffset(pg, col_w, x, y, ts.height, &xspace, &off); } } } else { if ((tw > xspace) && (eot != 1)) wc--; wastext = 1; TextDraw(&ts, win, txt_disp, x + off, y, xspace, 99999, justification); if (islink > 1 && linkwc > wc) { islink = 0; link_link[0] = '\0'; link_txt[0] = '\0'; wc = oldwc - 1; } if (islink > 1) { int rr, gg, bb; int extra; GC gc; XGCValues gcv; gc = XCreateGC(disp, win, 0, &gcv); EGetColor(&ts.fg_col, &rr, &gg, &bb); ESetColor(&ts.fg_col, pg->linkr, pg->linkg, pg->linkb); EAllocColor(&ts.fg_col); XSetForeground(disp, gc, ts.fg_col.pixel); TextSize(&ts, txt_disp, &tw, &th); extra = ((xspace - tw) * justification) >> 10; TextDraw(&ts, win, link_txt, x + off + lx + extra, y, 99999, 99999, 0); TextSize(&ts, link_txt, &lw, &th); XDrawLine(disp, win, gc, x + off + lx + extra, y + ts.xfontset_ascent, x + off + lx + lw + extra, y + ts.xfontset_ascent); ESetColor(&ts.fg_col, rr, gg, bb); islink = 0; XFreeGC(disp, gc); { Link *l; l = EMALLOC(Link, 1); l->name = strdup(link_link); l->x = x + off + lx + extra; l->y = y; l->w = lw; l->h = ts.height; l->next = ll; ll = l; } link_link[0] = '\0'; link_txt[0] = '\0'; } next_y(w, h, col_w, &x, &y, &col, pg, &ts, txt_disp); } eot = 0; s[0] = 0; } if (eol) break; } break; } } if (ts.efont) Efont_free(ts.efont); if (ts.xfontset) XFreeFontSet(disp, ts.xfontset); return ll; }