1 /****************************************************************************
2     Copyright (C) 1987-2015 by Jeffery P. Hansen
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18     Last edit by hansen on Sun Feb 22 18:12:11 2009
19 ****************************************************************************/
20 /*
21 
22 This module implements a subset of html for use in tkgate comments.
23 
24 The implemented elements are:
25   <value-of name="name">			Special variable value
26   <a href="url"></a>				Hyperlink
27   <a name="tag">				Hyperlink tag
28   <b></b>					Bold
29   <i></i>					Italic
30   <h3></h3>					Heading 3
31   <h2></h2>					Heading 2
32   <h1></h1>					Heading 1
33   <small></small>				Small font
34   <big></big>					Big font
35   <tt></tt>					Fixed-width font
36   <pre></pre>					Preformated
37   <br>						Break
38   <p>						Paragraph
39   <font size=n color=color face=face></font>	Set font characteristics
40   <hr>						Horizontal rule
41   &??;						Special character
42 
43 Special pre-processed tags:
44   <tutorial-navigation>                         Generates html for navigation bar in tutorials
45 
46 */
47 #include <stdio.h>
48 #include <ctype.h>
49 #include <sys/time.h>
50 #include <stdarg.h>
51 
52 #include "tkgate.h"
53 #include "print.h"
54 
55 //
56 // Testing only
57 //
58 //#include <X11/Xatom.h>
59 
60 /*
61  * Step size for extending the array of html tag options.
62  */
63 #define HTML_OSTEP 8
64 
65 void expandSpecialDirs(char *file);
66 
67 typedef void HtmlHandlerFunc(Html *h,HtmlTag *tag);
68 
69 /*
70  * Private class for specifying functions to handle html tags
71  */
72 typedef struct {
73   char			*tag;		/* Tag */
74   HtmlHandlerFunc	*func;	        /* Handling function for tag */
75 } HtmlHandler;
76 
77 typedef struct {
78   char			*spec;		/* Special character specifier */
79   char			*text;		/* Raw text for replacement */
80   char			*pretag;	/* Html code to preceed text */
81   char			*posttag;	/* Html code to follow text */
82 } HtmlSpecialSpec;
83 
84 void Html_handle_basic(Html *h, HtmlTag *tag);
85 void Html_handle_valueOf(Html *h, HtmlTag *tag);
86 void Html_handle_a(Html *h, HtmlTag *tag);
87 void Html_handle_heading(Html *h, HtmlTag *tag);
88 void Html_handle_img(Html *h, HtmlTag *tag);
89 
90 /*
91  * Table of html tags that are recognized by tkgate
92  */
93 HtmlHandler htmlHandlers[] = {
94   {"a",		Html_handle_a },
95   {"b",		Html_handle_basic },
96   {"tt",	Html_handle_basic },
97   {"i",		Html_handle_basic },
98   {"big",	Html_handle_basic },
99   {"small",	Html_handle_basic },
100   {"font",	Html_handle_basic },
101   {"h1",	Html_handle_heading },
102   {"h2",	Html_handle_heading },
103   {"h3",	Html_handle_heading },
104   {"value-of",	Html_handle_valueOf },
105   {"img",	Html_handle_img },
106   {0, 0}
107 };
108 
109 /*
110  * Table of html special characters that are recognized by tkgate
111  */
112 HtmlSpecialSpec htmlSpecialSpecs[] = {
113   {"&nbsp;",	" ",	0,	0},
114   {"&gt;",	">",	0,	0},
115   {"&lt;",	"<",	0,	0},
116   {"&amp;",	"&",	0,	0},
117 
118 
119   {"&Alpha;",	"A",	"<font face=symbol>",	"</font>"},
120   {"&Beta;",	"B",	"<font face=symbol>",	"</font>"},
121   {"&Gamma;",	"G",	"<font face=symbol>",	"</font>"},
122   {"&Delta;",	"D",	"<font face=symbol>",	"</font>"},
123   {"&Epsilon;",	"E",	"<font face=symbol>",	"</font>"},
124   {"&Zeta;",	"Z",	"<font face=symbol>",	"</font>"},
125   {"&Eta;",	"H",	"<font face=symbol>",	"</font>"},
126   {"&Theta;",	"Q",	"<font face=symbol>",	"</font>"},
127   {"&Iota;",	"I",	"<font face=symbol>",	"</font>"},
128   {"&Kappa;",	"K",	"<font face=symbol>",	"</font>"},
129   {"&Lambda;",	"L",	"<font face=symbol>",	"</font>"},
130   {"&Mu;",	"M",	"<font face=symbol>",	"</font>"},
131   {"&Nu;",	"N",	"<font face=symbol>",	"</font>"},
132   {"&Xi;",	"X",	"<font face=symbol>",	"</font>"},
133   {"&Omicron;",	"O",	"<font face=symbol>",	"</font>"},
134   {"&Pi;",	"P",	"<font face=symbol>",	"</font>"},
135   {"&Rho;",	"R",	"<font face=symbol>",	"</font>"},
136   {"&Sigma;",	"S",	"<font face=symbol>",	"</font>"},
137   {"&Tau;",	"T",	"<font face=symbol>",	"</font>"},
138   {"&Upsilon;",	"U",	"<font face=symbol>",	"</font>"},
139   {"&Phi;",	"F",	"<font face=symbol>",	"</font>"},
140   {"&Chi;",	"C",	"<font face=symbol>",	"</font>"},
141   {"&Psi;",	"Y",	"<font face=symbol>",	"</font>"},
142   {"&Omega;",	"W",	"<font face=symbol>",	"</font>"},
143   {"&alpha;",	"a",	"<font face=symbol>",	"</font>"},
144   {"&beta;",	"b",	"<font face=symbol>",	"</font>"},
145   {"&gamma;",	"g",	"<font face=symbol>",	"</font>"},
146   {"&delta;",	"d",	"<font face=symbol>",	"</font>"},
147   {"&epsilon;",	"e",	"<font face=symbol>",	"</font>"},
148   {"&zeta;",	"z",	"<font face=symbol>",	"</font>"},
149   {"&eta;",	"h",	"<font face=symbol>",	"</font>"},
150   {"&theta;",	"q",	"<font face=symbol>",	"</font>"},
151   {"&iota;",	"i",	"<font face=symbol>",	"</font>"},
152   {"&kappa;",	"k",	"<font face=symbol>",	"</font>"},
153   {"&lambda;",	"l",	"<font face=symbol>",	"</font>"},
154   {"&mu;",	"m",	"<font face=symbol>",	"</font>"},
155   {"&nu;",	"n",	"<font face=symbol>",	"</font>"},
156   {"&xi;",	"x",	"<font face=symbol>",	"</font>"},
157   {"&omicron;",	"o",	"<font face=symbol>",	"</font>"},
158   {"&pi;",	"p",	"<font face=symbol>",	"</font>"},
159   {"&rho;",	"r",	"<font face=symbol>",	"</font>"},
160   {"&sigmaf;",	"V",	"<font face=symbol>",	"</font>"},
161   {"&sigma;",	"s",	"<font face=symbol>",	"</font>"},
162   {"&tau;",	"t",	"<font face=symbol>",	"</font>"},
163   {"&upsilon;",	"u",	"<font face=symbol>",	"</font>"},
164   {"&phi;",	"f",	"<font face=symbol>",	"</font>"},
165   {"&chi;",	"c",	"<font face=symbol>",	"</font>"},
166   {"&psi;",	"y",	"<font face=symbol>",	"</font>"},
167   {"&omega;",	"w",	"<font face=symbol>",	"</font>"},
168   {"&thetasym;","J",	"<font face=symbol>",	"</font>"},
169   {"&upsih;",	"\241",	"<font face=symbol>",	"</font>"},
170   {"&piv;",	"v",	"<font face=symbol>",	"</font>"},
171 
172   {"&bull;",	"\267",	"<font face=symbol>",	"</font>"},
173   {"&hellip;",	"\274",	"<font face=symbol>",	"</font>"},
174   {"&prime;",	"\242",	"<font face=symbol>",	"</font>"},
175   {"&Prime;",	"\262",	"<font face=symbol>",	"</font>"},
176   {"&oline;",	"\140",	"<font face=symbol>",	"</font>"},
177   {"&frasl;",	"\244",	"<font face=symbol>",	"</font>"},
178   {"&weierp;",	"\303",	"<font face=symbol>",	"</font>"},
179   {"&image;",	"\301",	"<font face=symbol>",	"</font>"},
180   {"&real;",	"\302",	"<font face=symbol>",	"</font>"},
181   {"&trade;",	"\324",	"<font face=symbol>",	"</font>"},
182   {"&alefsym;",	"\300",	"<font face=symbol>",	"</font>"},
183   {"&larr;",	"\254",	"<font face=symbol>",	"</font>"},
184   {"&uarr;",	"\255",	"<font face=symbol>",	"</font>"},
185   {"&rarr;",	"\256",	"<font face=symbol>",	"</font>"},
186   {"&darr;",	"\257",	"<font face=symbol>",	"</font>"},
187   {"&harr;",	"\253",	"<font face=symbol>",	"</font>"},
188   {"&crarr;",	"\277",	"<font face=symbol>",	"</font>"},
189   {"&lArr;",	"\334",	"<font face=symbol>",	"</font>"},
190   {"&uArr;",	"\335",	"<font face=symbol>",	"</font>"},
191   {"&rArr;",	"\336",	"<font face=symbol>",	"</font>"},
192   {"&dArr;",	"\337",	"<font face=symbol>",	"</font>"},
193   {"&hArr;",	"\333",	"<font face=symbol>",	"</font>"},
194   {"&forall;",	"\042",	"<font face=symbol>",	"</font>"},
195   {"&part;",	"\266",	"<font face=symbol>",	"</font>"},
196   {"&exist;",	"\044",	"<font face=symbol>",	"</font>"},
197   {"&empty;",	"\306",	"<font face=symbol>",	"</font>"},
198   {"&nabla;",	"\321",	"<font face=symbol>",	"</font>"},
199   {"&isin;",	"\316",	"<font face=symbol>",	"</font>"},
200   {"&notin;",	"\317",	"<font face=symbol>",	"</font>"},
201   {"&ni;",	"\267",	"<font face=symbol>",	"</font>"},	/* NOT CORRECT */
202   {"&prod;",	"\325",	"<font face=symbol>",	"</font>"},
203   {"&sum;",	"\345",	"<font face=symbol>",	"</font>"},
204   {"&minus;",	"-",	"<font face=symbol>",	"</font>"},
205   {"&lowast;",	"*",	"<font face=symbol>",	"</font>"},
206   {"&radic;",	"\326",	"<font face=symbol>",	"</font>"},
207   {"&prop;",	"\265",	"<font face=symbol>",	"</font>"},
208   {"&infin;",	"\245",	"<font face=symbol>",	"</font>"},
209   {"&ang;",	"\320",	"<font face=symbol>",	"</font>"},
210   {"&and;",	"\331",	"<font face=symbol>",	"</font>"},
211   {"&or;",	"\332",	"<font face=symbol>",	"</font>"},
212   {"&cap;",	"\307",	"<font face=symbol>",	"</font>"},
213   {"&cup;",	"\310",	"<font face=symbol>",	"</font>"},
214   {"&int;",	"\362",	"<font face=symbol>",	"</font>"},
215   {"&there4;",	"\134",	"<font face=symbol>",	"</font>"},
216   {"&sim;",	"\176",	"<font face=symbol>",	"</font>"},
217   {"&cong;",	"\100",	"<font face=symbol>",	"</font>"},
218   {"&asymp;",	"\273",	"<font face=symbol>",	"</font>"},
219   {"&ne;",	"\271",	"<font face=symbol>",	"</font>"},
220   {"&equiv;",	"\272",	"<font face=symbol>",	"</font>"},
221   {"&le;",	"\243",	"<font face=symbol>",	"</font>"},
222   {"&ge;",	"\263",	"<font face=symbol>",	"</font>"},
223   {"&sub;",	"\314",	"<font face=symbol>",	"</font>"},
224   {"&sup;",	"\311",	"<font face=symbol>",	"</font>"},
225   {"&nsub;",	"\313",	"<font face=symbol>",	"</font>"},
226   {"&sube;",	"\315",	"<font face=symbol>",	"</font>"},
227   {"&supe;",	"\312",	"<font face=symbol>",	"</font>"},
228   {"&oplus;",	"\305",	"<font face=symbol>",	"</font>"},
229   {"&otimes;",	"\304",	"<font face=symbol>",	"</font>"},
230   {"&perp;",	"\267",	"<font face=symbol>",	"</font>"},	/* NOT CORRECT */
231   {"&sdot;",	"\327",	"<font face=symbol>",	"</font>"},
232   {"&lceil;",	"\351",	"<font face=symbol>",	"</font>"},
233   {"&rceil;",	"\371",	"<font face=symbol>",	"</font>"},
234   {"&lfloor;",	"\353",	"<font face=symbol>",	"</font>"},
235   {"&rfloor;",	"\373",	"<font face=symbol>",	"</font>"},
236   {"&lang;",	"\341",	"<font face=symbol>",	"</font>"},
237   {"&rang;",	"\361",	"<font face=symbol>",	"</font>"},
238   {"&loz;",	"\340",	"<font face=symbol>",	"</font>"},
239   {"&spades;",	"\252",	"<font face=symbol>",	"</font>"},
240   {"&clubs;",	"\247",	"<font face=symbol>",	"</font>"},
241   {"&hearts;",	"\251",	"<font face=symbol>",	"</font>"},
242   {"&diams;",	"\250",	"<font face=symbol>",	"</font>"},
243 
244   {0,0,0,0}
245 };
246 
247 #define DATA_STEP_SIZE			512
248 
249 static HtmlContext default_context = {
250   .hc_font = { .gateFont = {FF_HELVETICA, FP_ROMAN, FS_NORMAL} },
251   .hc_pixelColor.xColor.pixel = 0,
252   .hc_link = 0,
253   .hc_tag = 0,
254   .hc_preformat = 1,
255   .hc_next = 0
256 };
257 
Html_getEncoder(Html * h)258 static Encoder *Html_getEncoder(Html *h)
259 {
260   if (h->h_target == TD_X11)
261     return Circuit_getDisplayEncoder(TkGate.circuit);
262   else
263     return Circuit_getPSEncoder(TkGate.circuit);
264 }
265 
HtmlFont_isEqual(const HtmlFont * a,const HtmlFont * b)266 int HtmlFont_isEqual(const HtmlFont *a,const HtmlFont *b)
267 {
268   if (memcmp(a, b, sizeof(HtmlFont)) == 0)
269     return (1);
270   else
271     return (0);
272 }
273 
HtmlFont_updatePoints(HtmlFont * font)274 void HtmlFont_updatePoints(HtmlFont *font)
275 {
276   if (font->gateFont.family == FF_KANJI)
277     font->points = getKanjiFontSize(font->gateFont.size);
278   else
279     font->points = getFontSize(font->gateFont.size);
280 }
281 
HtmlFont_init(HtmlFont * font,GateFont gateFont,fontsize_t points)282 HtmlFont *HtmlFont_init(HtmlFont *font,GateFont gateFont,fontsize_t points)
283 {
284   int i;
285 
286   ob_touch(font);
287 
288   memcpy(&font->gateFont, &gateFont, sizeof (GateFont));
289   font->points = points;
290 
291   if (gateFont.family != FF_KANJI) {
292     for (i = 0;i < FS_MAX;i++)
293       if (getFontSize(i) >= points) break;
294   } else {
295     for (i = 0;i < FS_MAX;i++)
296       if (getKanjiFontSize(i) >= points) break;
297   }
298   if (i >= FS_MAX) i = FS_MAX - 1;
299   font->gateFont.size = i;
300 
301   return font;
302 }
303 
new_HtmlTag()304 HtmlTag *new_HtmlTag()
305 {
306   HtmlTag *ht = OM_MALLOC(HtmlTag);
307 
308   memset(ht, 0, sizeof (HtmlTag));
309 
310   return (ht);
311 }
312 
delete_HtmlTag(HtmlTag * ht)313 void delete_HtmlTag(HtmlTag *ht)
314 {
315   if (ht->ht_name) ob_free(ht->ht_name);
316   if (ht->ht_options) {
317     int i;
318 
319     for (i = 0;i < ht->ht_numOptions;i++) {
320       ob_free(ht->ht_options[i].hto_label);
321       ob_free(ht->ht_options[i].hto_value);
322     }
323 
324     ob_free(ht->ht_options);
325   }
326   ob_free(ht);
327 }
328 
329 /*****************************************************************************
330  * Break down an options string into tags and values.  Return the
331  * number of tags parsed, or -1 if there was an error.
332  *****************************************************************************/
Html_parseTag(const char * tag)333 HtmlTag *Html_parseTag(const char *tag)
334 {
335   char name[STRMAX];
336   char options[STRMAX];
337   char ptag[STRMAX],pvalue[STRMAX];
338   char *p;
339   char c;
340   HtmlTag *ht = 0;
341 
342   if (sscanf(tag,"< %[^ \t\n>] %[^>] %c",name,options,&c) == 3 && c == '>') {
343     /* Tag with options */
344   } else if (sscanf(tag,"< %[^ \t\n>] %c",name,&c) == 2 && c == '>') {
345     /* Tag with no options */
346     *options = 0;
347   } else {
348     /* Ignore badly formatted tag */
349     return 0;
350   }
351 
352   ht = new_HtmlTag();
353   ht->ht_name = ob_strdup(name);
354 
355   p = options;
356   while (*p && isspace(*p)) p++;
357   while (*p) {
358 
359     if (sscanf(p,"%[^=] = \"%[^\"]",ptag,pvalue) == 2) {
360       /* ok - advance past option */
361       while (*p && *p != '=') p++;
362       while (*p && *p != '"') p++;
363       if (*p) p++;
364       while (*p && *p != '"') p++;
365       if (*p) p++;
366     } else if (sscanf(p,"%[^=] = %s",ptag,pvalue) == 2) {
367       /* ok - advance past option */
368       while (*p && *p != '=') p++;
369       if (*p) p++;
370       while (*p && isspace(*p)) p++;
371       while (*p && !isspace(*p)) p++;
372     } else {
373       while (*p && isspace(*p)) p++;		/* Ignore unparsable tag */
374       while (*p && !isspace(*p)) p++;
375       continue;
376     }
377 
378     if ((ht->ht_numOptions % HTML_OSTEP) == 0) {
379       if (ht->ht_numOptions == 0)
380 	ht->ht_options = (HtmlTagOpt*) ob_malloc(sizeof(HtmlTagOpt)*HTML_OSTEP,"HtmlTagOpt");
381       else
382 	ht->ht_options = (HtmlTagOpt*) ob_realloc(ht->ht_options,sizeof(HtmlTagOpt)*(ht->ht_numOptions + HTML_OSTEP));
383     }
384 
385     ht->ht_options[ht->ht_numOptions].hto_label = ob_strdup(ptag);
386     ht->ht_options[ht->ht_numOptions].hto_value = ob_strdup(pvalue);
387     ht->ht_numOptions++;
388 
389     while (*p && isspace(*p)) p++;
390   }
391 
392   return ht;
393 }
394 
HtmlContext_stringWidth(HtmlContext * hc,const char * text,int len)395 static int HtmlContext_stringWidth(HtmlContext *hc,const char *text,int len)
396 {
397   switch (hc->hc_html->h_target) {
398   case TD_X11 :
399     if (hc->hc_is16bit)
400       return XTextWidth16(hc->hc_xFont,(XChar2b*)text,len/2);
401     else
402       return GatePainterContext_textWidth(TkGate.commentContext,
403 	  hc->hc_font.gateFont, text, len);
404   case TD_PRINT :
405     return PSStringWidth(&hc->hc_font,text,len);
406   }
407   return 0;
408 }
409 
HtmlFont_print(HtmlFont * hf,FILE * f)410 void HtmlFont_print(HtmlFont *hf,FILE *f)
411 {
412   char name[STRMAX];
413   getFontName(name,hf->gateFont.family,hf->gateFont.prop,hf->gateFont.size,1);
414   fprintf(f,"%s",name);
415 }
416 
HtmlContext_activateFont(HtmlContext * hc)417 static void HtmlContext_activateFont(HtmlContext *hc)
418 {
419   ob_touch(hc);
420 
421   switch (hc->hc_html->h_target) {
422   case TD_X11 : {
423 #ifdef DEBUG
424     printf("%p: activate-x ",hc);HtmlFont_print(&hc->hc_font,stdout);printf("\n");
425 #endif
426 
427     hc->hc_xFont = GetXFont(hc->hc_font.gateFont, TkGate.circuit->zoom_factor);
428     hc->hc_is16bit = (hc->hc_font.gateFont.family == FF_KANJI);
429 
430     if (hc->hc_is16bit)
431       hc->hc_spaceWidth = XTextWidth16(hc->hc_xFont, (XChar2b*)"  ", 1);
432     else
433       hc->hc_spaceWidth = GatePainterContext_textWidth(
434 	  TkGate.commentContext, hc->hc_font.gateFont, " ", 1);
435 
436     hc->hc_fontMetrics = GatePainterContext_fontMetrics(
437       TkGate.commentContext, &hc->hc_font.gateFont);
438   }
439     break;
440   case TD_PRINT :
441 #ifdef DEBUG
442     printf("activate-ps ");HtmlFont_print(&hc->hc_font,stdout);printf("\n");
443 #endif
444     hc->hc_xFont = 0;
445     hc->hc_is16bit = (hc->hc_font.gateFont.family == FF_KANJI);
446 
447     if (hc->hc_is16bit)
448       hc->hc_spaceWidth = PSStringWidth(&hc->hc_font,"  ",2);
449     else
450       hc->hc_spaceWidth = PSStringWidth(&hc->hc_font," ",1);
451 
452     hc->hc_fontMetrics.ascent = (int)(hc->hc_font.points*0.2);
453     hc->hc_fontMetrics.descent = (int)(hc->hc_font.points*1.2) -
454       hc->hc_fontMetrics.descent;
455     break;
456   }
457 }
458 
new_HtmlContext(HtmlContext * base,Html * html)459 static HtmlContext *new_HtmlContext(HtmlContext *base,Html *html)
460 {
461   HtmlContext *hc = OM_MALLOC(HtmlContext);
462 
463   if (base)
464     *hc = *base;
465   else {
466     *hc = default_context;
467     hc->hc_pixelColor = TkGate.comment_color;
468   }
469 
470   hc->hc_html = html;
471 
472   if (hc->hc_link) hc->hc_link = ob_strdup(hc->hc_link);
473   if (hc->hc_tag)  hc->hc_tag = ob_strdup(hc->hc_tag);
474   hc->hc_next = 0;
475 
476   HtmlFont_updatePoints(&hc->hc_font);
477   HtmlContext_activateFont(hc);
478 
479   return hc;
480 }
481 
delete_HtmlContext(HtmlContext * hc)482 static void delete_HtmlContext(HtmlContext *hc)
483 {
484   if (hc->hc_link) ob_free(hc->hc_link);
485   if (hc->hc_tag) ob_free(hc->hc_tag);
486 }
487 
new_HtmlUnit(const char * text,int len,HtmlContext * hc)488 static HtmlUnit *new_HtmlUnit(const char *text,int len,HtmlContext *hc)
489 {
490   HtmlUnit *hu = OM_MALLOC(HtmlUnit);
491 
492   hu->hu_type = HU_TEXT;
493   hu->hu_text = ob_malloc(len+1,"char*");
494   strncpy(hu->hu_text,text,len);
495   hu->hu_text[len] =0;
496   hu->hu_x = hu->hu_y = 0;
497   hu->hu_context = hc;
498   hu->hu_image = 0;
499 
500   return hu;
501 }
502 
new_HtmlUnit_T(int htype,HtmlContext * hc)503 static HtmlUnit *new_HtmlUnit_T(int htype,HtmlContext *hc)
504 {
505   HtmlUnit *hu = OM_MALLOC(HtmlUnit);
506 
507   hu->hu_type = htype;
508   hu->hu_text =0;
509   hu->hu_x = hu->hu_y = 0;
510   hu->hu_width = 0;
511   hu->hu_context = hc;
512   hu->hu_image = 0;
513 
514   return hu;
515 }
516 
delete_HtmlUnit(HtmlUnit * hu)517 static void delete_HtmlUnit(HtmlUnit *hu)
518 {
519   if (hu->hu_image)
520     Tk_FreeImage(hu->hu_image);
521 
522   if (hu->hu_text)
523     ob_free(hu->hu_text);
524   ob_free(hu);
525 }
526 
new_Html(TargetDev_e target)527 Html *new_Html(TargetDev_e target)
528 {
529   Html *h = OM_MALLOC(Html);
530 
531   h->h_reqWidth = 100;
532   h->h_width = h->h_reqWidth;
533   h->h_target = target;
534   h->h_height = 0;
535   h->h_dataLen = 0;
536   h->h_data = 0;
537   h->h_head = 0;
538   h->h_tail = 0;
539   h->h_context = new_HtmlContext(0,h);
540   h->h_contextPool = 0;
541   h->h_zoom = TkGate.circuit->zoom_factor;
542   h->h_locale = TkGate.circuit->c_locale;
543 
544   return h;
545 }
546 
delete_Html(Html * h)547 void delete_Html(Html *h)
548 {
549   HtmlUnit *u,*next_u;
550   HtmlContext *hc,*next_hc;
551 
552   if (h->h_data) ob_free(h->h_data);
553 
554   for (u = h->h_head;u;u = next_u) {
555     next_u = u->hu_next;
556     delete_HtmlUnit(u);
557   }
558 
559   for (hc = h->h_context;hc;hc = next_hc) {
560     next_hc = hc->hc_next;
561     delete_HtmlContext(hc);
562   }
563   for (hc = h->h_contextPool;hc;hc = next_hc) {
564     next_hc = hc->hc_next;
565     delete_HtmlContext(hc);
566   }
567 
568   ob_free(h);
569 }
570 
Html_makeTutorialNavigationLine_bymodule(char * line)571 const char *Html_makeTutorialNavigationLine_bymodule(char *line)
572 {
573   char *p = line;
574   GModuleDef *M = TkGate.circuit->es->env;
575   int cur_pnum,max_pnum,i;
576   int spaces = 45;
577 
578   for (max_pnum = 0;;) {
579     char name[STRMAX];
580 
581     sprintf(name,"PAGE%d",max_pnum+1);
582     if (!env_findModule(name)) break;
583     max_pnum++;
584   }
585 
586   if (max_pnum > 1) {
587     if (sscanf(M->m_name,"PAGE%d",&cur_pnum) != 1 || cur_pnum < 1 || cur_pnum > max_pnum)
588       return "[no-controls]";
589 
590     if (cur_pnum == 1) {
591 
592       p += sprintf(p,"<font color=gray>&lt;%s</font>   ",msgLookup("tutorial.prev"));
593     } else {
594       p += sprintf(p,"<a href=\"#/PAGE%d\">&lt;%s</a>   ",cur_pnum-1,msgLookup("tutorial.prev"));
595     }
596 
597     for (i = 1;i <= max_pnum;i++) {
598       if (i == cur_pnum)
599 	p += sprintf(p," <b size=5>%d</b>",i);
600       else
601 	p += sprintf(p," <a href=\"#/PAGE%d\">%d</b>",i,i);
602 
603       if (i < 10)
604 	spaces -= 2;
605       else
606 	spaces -= 3;
607     }
608 
609     if (cur_pnum == max_pnum)
610       p += sprintf(p,"    <font color=gray>%s></font>",msgLookup("tutorial.next"));
611     else
612       p += sprintf(p,"    <a href=\"#/PAGE%d\">%s></a>",cur_pnum+1,msgLookup("tutorial.next"));
613 
614     for (i = 0;i < spaces;i++)
615       p += sprintf(p," ");
616 
617     p += sprintf(p,"   ");
618   }
619 
620   p += sprintf(p,"<a href=\"index.v\">&lang;%s&rang;</a>     <a href=\"%s#/PAGE%d\">&lang;%s&rang;</a>",
621 	       msgLookup("tutorial.chapter"),
622 	       CurrentFile_path(TkGate.circuit->currentFile),cur_pnum,
623 	       msgLookup("tutorial.reload"));
624 
625   return line;
626 }
627 
Html_makeTutorialNavigationLine_byfile(char * line)628 const char *Html_makeTutorialNavigationLine_byfile(char *line)
629 {
630   char *p = line;
631   int cur_pnum,max_pnum,i;
632   int spaces = 45;
633   const char *curDirName = CurrentFile_getDir(TkGate.circuit->currentFile);
634   const char *curFileName = CurrentFile_getBase(TkGate.circuit->currentFile);
635   char baseName[STRMAX], extension[STRMAX], path[STRMAX];
636 
637   if (sscanf(curFileName,"%[^0123456789]%d%s",baseName,&cur_pnum,extension) != 3 || strcmp(extension,".v") != 0)
638     return "[no-controls - invalid file name]";
639 
640 #if 1
641   sprintf(path,"%s/%s",curDirName,baseName);
642 #endif
643 
644   for (max_pnum = 0;;) {
645     char name[STRMAX];
646     struct stat sb;
647 
648     sprintf(name,"%s%d.v",path,max_pnum+1);
649 
650     if (stat(name, &sb) != 0) break;
651 
652     max_pnum++;
653   }
654 
655   if (max_pnum > 1) {
656     if (cur_pnum == 1) {
657       p += sprintf(p,"<font color=gray>&lt;%s</font>   ",msgLookup("tutorial.prev"));
658     }  else
659       p += sprintf(p,"<a href=\"%s%d.v\">&lt;%s</a>   ",baseName,cur_pnum-1,msgLookup("tutorial.prev"));
660 
661     for (i = 1;i <= max_pnum;i++) {
662       if (i == cur_pnum)
663 	p += sprintf(p," <b size=5>%d</b>",i);
664       else
665 	p += sprintf(p," <a href=\"%s%d.v\">%d</b>",baseName,i,i);
666 
667       if (i < 10)
668 	spaces -= 2;
669       else
670 	spaces -= 3;
671     }
672 
673     if (cur_pnum == max_pnum)
674       p += sprintf(p,"    <font color=gray>%s></font>",msgLookup("tutorial.next"));
675    else
676       p += sprintf(p,"    <a href=\"%s%d.v\">%s></a>",baseName,cur_pnum+1,msgLookup("tutorial.next"));
677 
678     for (i = 0;i < spaces;i++)
679       p += sprintf(p," ");
680 
681     p += sprintf(p,"   ");
682   }
683 
684   p += sprintf(p,"<a href=\"index.v\">&lang;%s&rang;</a>     <a href=\"%s%d.v\">&lang;%s&rang;</a>",
685 	       msgLookup("tutorial.chapter"),
686 	       baseName,cur_pnum,
687 	       msgLookup("tutorial.reload"));
688 
689   return line;
690 }
691 
692 /*****************************************************************************
693  *
694  * Generate the HTML resulting from the <tutorial-navigation> tag.
695  *
696  *****************************************************************************/
Html_makeTutorialNavigationLine(Html * h,HtmlTag * navtag)697 const char *Html_makeTutorialNavigationLine(Html *h, HtmlTag *navtag)
698 {
699   Encoder *encoder = Html_getEncoder(h);
700   static char line[STRMAX];
701   char buf[STRMAX];
702   int byfile = 0;
703   int i;
704 
705   //  *buf = 0;
706 
707   for (i = 0;i < navtag->ht_numOptions;i++) {
708     if (strcasecmp(navtag->ht_options[i].hto_label, "byfile") == 0)
709       byfile = istruevalue(navtag->ht_options[i].hto_value);
710   }
711 
712   if (byfile)
713     Html_makeTutorialNavigationLine_byfile(buf);
714   else
715     Html_makeTutorialNavigationLine_bymodule(buf);
716 
717   recodeText(encoder, line, STRMAX, buf);
718 
719   return line;
720 }
721 
722 /******************************************************************************
723  *
724  * Add a line of data to the html object
725  *
726  *****************************************************************************/
Html_addLine(Html * h,const char * line)727 void Html_addLine(Html *h,const char *line)
728 {
729   char buf[STRMAX];
730   /* Current # of allocated bytes */
731   int curLen = ((h->h_dataLen+DATA_STEP_SIZE-1)/DATA_STEP_SIZE)*DATA_STEP_SIZE;
732   int l;
733   Encoder *encoder = Html_getEncoder(h);
734 
735   ob_touch(h);
736 
737   recodeText(encoder, buf, STRMAX, line);
738   line = buf;
739 
740   //
741   // This is a kludge to automatically create the tutorial navigation buttons
742   //
743   if (strncasecmp(line,"<tutorial-navigation",20) == 0) {
744     HtmlTag *navtag = Html_parseTag(line);
745 
746     line = Html_makeTutorialNavigationLine(h,navtag);
747   }
748 
749   l = strlen(line);
750 
751 
752   /*
753    * Check to see if we need to increase length of data buffer.  Be sure to
754    * make space for the newline and the null character.
755    */
756   if (h->h_dataLen + l + 2 > curLen) {
757     int newLen = ((h->h_dataLen + l + 2 + DATA_STEP_SIZE-1)/DATA_STEP_SIZE)*DATA_STEP_SIZE;
758 
759     if (h->h_data) {
760         h->h_data = ob_realloc(h->h_data,newLen);
761      } else
762        h->h_data = ob_malloc(newLen,"char*");
763   }
764 
765   /*
766    * Append the data plus a newline.
767    */
768   strcpy(h->h_data + h->h_dataLen,line);
769   h->h_dataLen += l;
770   h->h_data[h->h_dataLen++] = '\n';
771   h->h_data[h->h_dataLen] = 0;
772 }
773 
774 /******************************************************************************
775  *
776  * Delete all html units from an html object.
777  *
778  *****************************************************************************/
Html_flushUnits(Html * h)779 static void Html_flushUnits(Html *h)
780 {
781   HtmlUnit *hu,*next_hu;
782 
783   ob_touch(h);
784 
785   for (hu = h->h_head;hu;hu = next_hu) {
786     next_hu = hu->hu_next;
787     delete_HtmlUnit(hu);
788   }
789   h->h_head = h->h_tail = 0;
790 }
791 
Html_addUnit(Html * h,HtmlUnit * hu)792 static void Html_addUnit(Html *h,HtmlUnit *hu)
793 {
794   ob_touch(h);
795 
796   if (h->h_tail) {
797     h->h_tail->hu_next = hu;
798     hu->hu_prev = h->h_tail;
799     h->h_tail = hu;
800     hu->hu_next = 0;
801   } else {
802     h->h_head = h->h_tail = hu;
803     hu->hu_prev = 0;
804     hu->hu_next = 0;
805   }
806 }
807 
Html_pushContext(Html * h,HtmlContext * hc)808 static void Html_pushContext(Html *h,HtmlContext *hc)
809 {
810   ob_touch(hc);
811   ob_touch(h);
812 
813   hc->hc_next = h->h_context;
814   h->h_context = hc;
815 }
816 
Html_popContext(Html * h)817 static void Html_popContext(Html *h)
818 {
819   HtmlContext *hc = h->h_context;
820 
821   if (!hc->hc_next) return;		/* Don't pop last context */
822 
823   ob_touch(hc);
824   ob_touch(h);
825 
826   h->h_context = h->h_context->hc_next;
827 
828   hc->hc_next = h->h_contextPool;
829   h->h_contextPool = hc;
830 }
831 
on_image_changed(ClientData data,int x,int y,int width,int height,int image_width,int image_height)832 static void on_image_changed(ClientData data,
833                              int x, int y,
834                              int width, int height,
835                              int image_width, int image_height)
836 {
837   /* This space intentionally left blank. */
838 }
839 
840 /*****************************************************************************
841  *
842  * Handle a <img> element.
843  *
844  *****************************************************************************/
Html_handle_img(Html * h,HtmlTag * tag)845 void Html_handle_img(Html *h, HtmlTag *tag)
846 {
847   HtmlContext *hc = new_HtmlContext(h->h_context,h);
848   HtmlUnit *hu = new_HtmlUnit_T(HU_IMAGE,hc);
849   int width = 16, height = 16;
850   const char *gifFile = "blk_copy.gif";
851   int i;
852 
853   hc->hc_pixelColor = notAColor;
854 
855   ob_touch(hu);
856 
857   for (i = 0;i < tag->ht_numOptions;i++) {
858     if (strcasecmp(tag->ht_options[i].hto_label, "src") == 0) {
859       char buf[STRMAX];
860 
861       strcpy(buf,tag->ht_options[i].hto_value);
862       expandSpecialDirs(buf);
863       gifFile = ob_strdup(buf);
864     } else if (strcasecmp(tag->ht_options[i].hto_label, "bgcolor") == 0) {
865       ob_touch(hc);
866       hc->hc_pixelColor = GatePainter_getColor(TkGate.painterW,
867 	  tag->ht_options[i].hto_value);
868     }
869   }
870 
871   Html_pushContext(h,hc);
872   Html_popContext(h);
873   Html_addUnit(h,hu);
874 
875   ob_touch(hu);
876   ob_touch(hc);
877 
878   DoTcl("gifI %s",gifFile);
879   hu->hu_image = Tk_GetImage(TkGate.tcl, Tk_MainWindow(TkGate.tcl), Tcl_GetStringResult(TkGate.tcl), on_image_changed, 0);
880   if (hu->hu_image)
881     Tk_SizeOfImage(hu->hu_image, &width, &height);
882 
883   hu->hu_text = 0;
884   hu->hu_width = width;
885 
886   hc->hc_fontMetrics.descent = h->h_context->hc_fontMetrics.descent;
887   hc->hc_fontMetrics.ascent = height - hc->hc_fontMetrics.descent;
888 }
889 
890 /*****************************************************************************
891  *
892  * Handle a <value-of> element.
893  *
894  *****************************************************************************/
Html_handle_valueOf(Html * h,HtmlTag * tag)895 void Html_handle_valueOf(Html *h, HtmlTag *tag)
896 {
897   extern char *release_date;
898   char *name = 0;
899   const char *text = 0;
900   int i;
901 
902   for (i = 0;i < tag->ht_numOptions;i++)
903     if (strcasecmp(tag->ht_options[i].hto_label,"name") == 0)
904       name = tag->ht_options[i].hto_value;
905 
906   if (!name) return;
907 
908   if (strcasecmp(name,"tkgate-version") == 0) {
909     text = TKGATE_FULL_VERSION;
910   } else if (strcasecmp(name,"tkgate-homepage") == 0) {
911     text = PACKAGE_URL;
912   } else if (strcasecmp(name,"tkgate-mailcontact") == 0) {
913     text = PACKAGE_BUGREPORT;
914   } else if (strcasecmp(name,"tkgate-copyright") == 0) {
915     text = TKGATE_COPYRIGHT;
916   } else if (strcasecmp(name,"tkgate-release-date") == 0) {
917     text = release_date;
918   } else if (strcasecmp(name,"pancake-recipe") == 0) {
919     extern const char *recipe_list[];
920     text = recipe_list[0];
921   }
922 
923   if (text)
924     Html_addUnit(h,new_HtmlUnit(text,strlen(text),h->h_context));
925 }
926 
927 /*****************************************************************************
928  *
929  * Handle font modifiers in a tag
930  *
931  *****************************************************************************/
HtmlContext_handle_modifiers(HtmlContext * hc,HtmlTag * tag)932 void HtmlContext_handle_modifiers(HtmlContext *hc,HtmlTag *tag)
933 {
934   int i;
935 
936   for (i = 0;i < tag->ht_numOptions;i++) {
937     const char *label = tag->ht_options[i].hto_label;
938     const char *value = tag->ht_options[i].hto_value;
939 
940     if (strcasecmp(label,"face") == 0) {
941       fontfamily_t j;
942 
943       ob_touch(hc);
944 
945       for (j = 0;j < FF_MAX;j++)
946 	if (strcasecmp(getFontFamilyName(j),value) == 0)
947 	  hc->hc_font.gateFont.family = j;
948     } else if (strcasecmp(label,"size") == 0) {
949       /*
950        * Scan font size.  We decrement the scanned size to convert from the
951        * html specification to our internal size code.
952        */
953       if (sscanf(value,"%d",(int*)&hc->hc_font.gateFont.size) == 1)
954 	--hc->hc_font.gateFont.size;
955       HtmlFont_updatePoints(&hc->hc_font);
956     } else if (strcasecmp(label,"color") == 0) {
957       ob_touch(hc);
958       if (hc->hc_html->h_target == TD_X11) {
959 	hc->hc_pixelColor = GatePainter_getColor(TkGate.painterW, value);
960       }
961       else
962 	hc->hc_pixelColor = GatePainter_getColor(TkGate.painterW, "balack");
963     }
964   }
965 }
966 
967 
968 /*****************************************************************************
969  *
970  * Handler for basic open/close html tags which modify the context.
971  *
972  *****************************************************************************/
Html_handle_basic(Html * h,HtmlTag * tag)973 void Html_handle_basic(Html *h, HtmlTag *tag)
974 {
975   HtmlContext *hc = new_HtmlContext(h->h_context,h);
976 
977   ob_touch(hc);
978   if (strcasecmp(tag->ht_name,"b") == 0) {
979     hc->hc_font.gateFont.prop |= FP_BOLD;
980     HtmlContext_handle_modifiers(hc,tag);
981   } else if (strcasecmp(tag->ht_name,"i") == 0) {
982     hc->hc_font.gateFont.prop |= FP_ITALIC;
983     HtmlContext_handle_modifiers(hc,tag);
984   } else if (strcasecmp(tag->ht_name,"tt") == 0) {
985     hc->hc_font.gateFont.family = FF_COURIER;
986     HtmlContext_handle_modifiers(hc,tag);
987   } else if (strcasecmp(tag->ht_name,"big") == 0) {
988     if (hc->hc_font.gateFont.size + 1  < FS_MAX)
989       ++hc->hc_font.gateFont.size;
990     HtmlFont_updatePoints(&hc->hc_font);
991     HtmlContext_handle_modifiers(hc,tag);
992   } else if (strcasecmp(tag->ht_name,"small") == 0) {
993     if (hc->hc_font.gateFont.size - 1  >= 0)
994       --hc->hc_font.gateFont.size;
995     HtmlFont_updatePoints(&hc->hc_font);
996     HtmlContext_handle_modifiers(hc,tag);
997   } else if (strcasecmp(tag->ht_name,"font") == 0) {
998     HtmlContext_handle_modifiers(hc,tag);
999   }
1000 
1001   HtmlContext_activateFont(hc);
1002   Html_pushContext(h,hc);
1003 }
1004 
1005 /*****************************************************************************
1006  *
1007  * Handler for heading tags
1008  *
1009  *****************************************************************************/
Html_handle_heading(Html * h,HtmlTag * tag)1010 void Html_handle_heading(Html *h, HtmlTag *tag)
1011 {
1012   HtmlContext *hc = new_HtmlContext(h->h_context,h);
1013 
1014   Html_addUnit(h,new_HtmlUnit_T(HU_BREAK,h->h_context));
1015 
1016   ob_touch(hc);
1017 
1018   hc->hc_font.gateFont.prop |= FP_BOLD;
1019 
1020   if (strcasecmp(tag->ht_name,"h1") == 0) {
1021     hc->hc_font.gateFont.size = FS_XHUGE;
1022   } else if (strcasecmp(tag->ht_name,"h2") == 0) {
1023     hc->hc_font.gateFont.size = FS_HUGE;
1024   } else if (strcasecmp(tag->ht_name,"h3") == 0) {
1025     hc->hc_font.gateFont.size = FS_LARGE;
1026   }
1027 
1028   HtmlFont_updatePoints(&hc->hc_font);
1029 
1030   HtmlContext_handle_modifiers(hc,tag);
1031 
1032   HtmlContext_activateFont(hc);
1033   Html_pushContext(h,hc);
1034 }
1035 
Html_handle_a(Html * h,HtmlTag * ht)1036 void Html_handle_a(Html *h, HtmlTag *ht)
1037 {
1038   HtmlContext *hc = new_HtmlContext(h->h_context,h);
1039   int i;
1040 
1041   ob_touch(hc);
1042   hc->hc_pixelColor = TkGate.hyperlink_color;
1043   HtmlContext_activateFont(hc);
1044 
1045   for (i = 0;i < ht->ht_numOptions;i++) {
1046     if (strcasecmp(ht->ht_options[i].hto_label, "href") == 0) {
1047       hc->hc_link = ob_strdup(ht->ht_options[i].hto_value);
1048     } else if (strcasecmp(ht->ht_options[i].hto_label, "name") == 0) {
1049       hc->hc_tag = ob_strdup(ht->ht_options[i].hto_value);
1050     }
1051   }
1052 
1053   Html_pushContext(h,hc);
1054 }
1055 
1056 /*****************************************************************************
1057  *
1058  * Process the html tag that starts at 'tag' and is 'len' characters long.
1059  *
1060  *****************************************************************************/
Html_processTag(Html * h,const char * tag,int len)1061 static void Html_processTag(Html *h, const char *tag,int len)
1062 {
1063   HtmlTag *ht;
1064   int i;
1065 
1066   ht = Html_parseTag(tag);
1067   if (!ht) return;
1068 
1069 
1070   if (*ht->ht_name == '/') {
1071     Html_popContext(h);
1072     return;
1073   }
1074 
1075   for (i = 0;htmlHandlers[i].tag;i++)
1076     if (strcasecmp(ht->ht_name,htmlHandlers[i].tag) == 0)
1077       (*htmlHandlers[i].func)(h,ht);
1078 }
1079 
1080 /*****************************************************************************
1081  *
1082  * Process character entity references.
1083  *
1084  *****************************************************************************/
Html_processSpecialChar(Html * h,char * spec,int len)1085 static void Html_processSpecialChar(Html *h,char *spec,int len)
1086 {
1087   char *text = 0;
1088   char *pretag = 0;
1089   char *posttag = 0;
1090   int i;
1091 
1092   for (i = 0;htmlSpecialSpecs[i].spec;i++) {
1093     if (strncmp(spec,htmlSpecialSpecs[i].spec,len) == 0) {
1094       text = htmlSpecialSpecs[i].text;
1095       pretag = htmlSpecialSpecs[i].pretag;
1096       posttag = htmlSpecialSpecs[i].posttag;
1097     }
1098   }
1099 
1100   if (text) {
1101     if (pretag) Html_processTag(h,pretag,strlen(pretag));
1102     Html_addUnit(h,new_HtmlUnit(text,strlen(text),h->h_context));
1103     if (posttag) Html_processTag(h,posttag,strlen(posttag));
1104   } else
1105     Html_addUnit(h,new_HtmlUnit(spec,len,h->h_context));
1106 }
1107 
1108 
1109 /******************************************************************************
1110  *
1111  * Parition an html object into pieces.  Each piece is one of:
1112  *   * An html tag
1113  *   * An html special character sequence
1114  *   * A new line
1115  *   * A text string
1116  *
1117  *****************************************************************************/
Html_partition(Html * h,char * data)1118 void Html_partition(Html *h,char *data)
1119 {
1120   char *p,*q;
1121   Encoder *encoder = Html_getEncoder(h);
1122   int isJapanese = isJapaneseDisplay(encoder);
1123   int i;
1124   HtmlUnit *hu;
1125   HtmlContext *hc;
1126 
1127   Html_flushUnits(h);
1128 
1129   if (!data) return;				/* No data */
1130 
1131   p = q = data;
1132   while (*p) {
1133     if (*p == '<') {
1134       /******************************************************************
1135        *
1136        * This is an html tag.  Include everything up to end of tag.
1137        *
1138        ******************************************************************/
1139       q = strchr(p,'>');
1140       if (!q) q = p + strlen(p);
1141 
1142       Html_processTag(h,p,q-p+1);
1143       if (!*q) break;
1144       p = q+1;
1145     } else if (*p == '&') {
1146       /******************************************************************
1147        *
1148        * This is special character.  Parse until ';' and convert to the
1149        * specified character.
1150        *
1151        ******************************************************************/
1152       q = strchr(p,';');
1153       if (!q) q = p + strlen(p);
1154 
1155       Html_processSpecialChar(h,p,q-p+1);
1156       if (!*q) break;
1157       p = q+1;
1158     } else if (*p == '\n') {
1159       /******************************************************************
1160        *
1161        * This is newline.  Insert a newline unit if we are in preformat
1162        * mode, otherwise just insert a space.
1163        *
1164        ******************************************************************/
1165       if (h->h_context->hc_preformat) {
1166 	hu = new_HtmlUnit_T(HU_NEWLINE,h->h_context);
1167 	Html_addUnit(h,hu);
1168       } else
1169 	Html_addUnit(h,new_HtmlUnit(" ",1,h->h_context));
1170       p++;
1171     } else if ((*p & 0x80) && isJapanese) {
1172       /******************************************************************
1173        *
1174        * This is Kanji text.  Scan until we find a non-8-bit character.
1175        *
1176        ******************************************************************/
1177 
1178       for (q = p;*q;q++)
1179         if (!(*q & 0x80)) break;
1180 
1181       hc = new_HtmlContext(h->h_context,h);
1182       hc->hc_font.gateFont.family = FF_KANJI;
1183       HtmlFont_updatePoints(&hc->hc_font);
1184       HtmlContext_activateFont(hc);
1185       Html_pushContext(h,hc);
1186       Html_popContext(h);
1187       hu = new_HtmlUnit(p,q-p,hc);
1188       Html_addUnit(h,hu);
1189       for (i = 0;hu->hu_text[i];i++) hu->hu_text[i] &= 0x7f;
1190 
1191       if (!*q) break;
1192       p = q;
1193     } else {
1194       /******************************************************************
1195        *
1196        * This is a regular non-kanji text string
1197        *
1198        ******************************************************************/
1199 
1200       for (q = p;*q;q++)
1201 	    if (strchr("<&\n",*q) != 0 || ((*q & 0x80) && isJapanese))
1202 	      break;
1203       hu = new_HtmlUnit(p,q-p,h->h_context);
1204       Html_addUnit(h,hu);
1205 
1206       if (!*q) break;
1207       p = q;
1208     }
1209   }
1210 }
1211 
1212 /******************************************************************************
1213  *
1214  * Format an html object.
1215  *
1216  * Parameters:
1217  *     h		Html object to be formatted.
1218  *
1219  *****************************************************************************/
Html_format(Html * h)1220 void Html_format(Html *h)
1221 {
1222   HtmlUnit *hu,*hu2;
1223   HtmlUnit *linestart_hu;
1224   int max_ascent = 0;
1225   int max_descent = 0;
1226   int max_width = 0;
1227   int x = 0, y = 0;					/* Current text position */
1228   char *p;
1229 
1230   ob_touch(h);
1231   Html_partition(h,h->h_data);
1232 
1233   h->h_isVisible = 0;
1234 
1235   linestart_hu = h->h_head;				/* This is the text unit for the current line */
1236   for (hu = h->h_head;hu;hu = hu->hu_next) {
1237     HtmlContext *hc = hu->hu_context;			/* Get context of this unit */
1238 
1239     /*
1240      * Update line height metrics
1241      */
1242     if (HtmlContext_fontAscent(hc) > max_ascent)
1243       max_ascent = HtmlContext_fontAscent(hc);
1244     if (HtmlContext_fontDescent(hc) > max_descent)
1245       max_descent = HtmlContext_fontDescent(hc);
1246 
1247     switch (hu->hu_type) {
1248     case HU_BREAK :
1249       if (x == 0) break;	/* Do a newline only if we are not already on a newline */
1250       /* fall through */
1251     case HU_NEWLINE :
1252       y += max_ascent;
1253       for (hu2 = linestart_hu;hu2 != hu;hu2 = hu2->hu_next) {
1254 	ob_touch(hu2);
1255 	hu2->hu_y = y;
1256       }
1257       y += max_descent;
1258       x = 0;
1259 
1260       /*
1261        * Reset metrics and advance linestart_hu to start of next line.
1262        */
1263       max_ascent = 0;
1264       max_descent = 0;
1265       linestart_hu = hu->hu_next;
1266       break;
1267     case HU_TEXT :
1268       ob_touch(hu);
1269 
1270       hu->hu_x = x;
1271 
1272       hu->hu_width = HtmlContext_stringWidth(hc,hu->hu_text,strlen(hu->hu_text));
1273 
1274       x += hu->hu_width;
1275       if (x > max_width) max_width = x;
1276 
1277       if (!h->h_isVisible) {
1278 	for (p = hu->hu_text;*p;p++)
1279 	  if (!isspace(*p)) {
1280 	    h->h_isVisible = 1;
1281 	  }
1282       }
1283       break;
1284     case HU_IMAGE :
1285       hu->hu_x = x;
1286       x += hu->hu_width;
1287       if (x > max_width) max_width = x;
1288       h->h_isVisible = 1;
1289       break;
1290     default:
1291       /* For cases like HU_RULE */
1292       break;
1293     }
1294   }
1295 
1296   h->h_width = max_width;
1297   h->h_height = y + max_descent;
1298 }
1299 
Html_psPrint(Html * h,GPrint * P,int x,int y)1300 void Html_psPrint(Html *h,GPrint *P,int x,int y)
1301 {
1302   HtmlUnit *hu;
1303   HtmlContext *last_hc = 0;
1304   /** @TODO to remove */
1305   /* Encoder *encoder = Circuit_getPSEncoder(TkGate.circuit); */
1306   /* char text[STRMAX]; */
1307 
1308   for (hu = h->h_head;hu;hu = hu->hu_next) {
1309     HtmlContext *hc = hu->hu_context;			/* Get context of this unit */
1310 
1311     /*
1312      * Update properties only if there was a change.
1313      */
1314     if (hc != last_hc) {
1315       /** @TODO to remove */
1316       /* Tkg_changeColor(gc, GXxor, hc->hc_pixel); */
1317       last_hc = hc;
1318     }
1319 
1320     switch (hu->hu_type) {
1321     case HU_TEXT :
1322       PSDrawText(P,&hc->hc_font,hu->hu_x + x,hu->hu_y + y,hu->hu_text,AtBaseline|AtLeft);
1323       break;
1324     case HU_IMAGE :
1325       break;
1326     default :
1327       break;
1328     }
1329   }
1330 }
1331 
1332 /*****************************************************************************
1333  *
1334  * Draw a block of html text.
1335  *
1336  *****************************************************************************/
Html_draw(Html * h,int x,int y)1337 void Html_draw(Html *h,int x,int y)
1338 {
1339   GC gc = GatePainterContext_gc(TkGate.commentContext);
1340   GC igc = TkGate.imageGC;
1341   HtmlUnit *hu;
1342   HtmlContext *last_hc = 0;
1343 
1344   GatePainterContext_print(TkGate.commentContext, stdout);
1345 
1346   x = ctow_x(x)*TkGate.circuit->zoom_factor;
1347   y = ctow_y(y)*TkGate.circuit->zoom_factor;
1348 
1349 #ifdef DEBUG
1350   Locale_print(h->h_locale, stdout);
1351 #endif
1352 
1353   for (hu = h->h_head;hu;hu = hu->hu_next) {
1354     HtmlContext *hc = hu->hu_context;			/* Get context of this unit */
1355 #ifdef DEBUG
1356   HtmlContext_print(hc,stdout);
1357 #endif
1358     switch (hu->hu_type) {
1359     case HU_TEXT :
1360       /*
1361        * Update properties only if there was a change.
1362        */
1363       if (hc != last_hc) {
1364 	GatePainterContext_setFont(TkGate.commentContext, hc->hc_font.gateFont);
1365 
1366 	if (!GateColor_equals(&hc->hc_pixelColor, &notAColor)) {
1367           Tkg_changeColor(gc, GXxor, hc->hc_pixelColor.xColor.pixel);
1368 	  GatePainterContext_setColor(TkGate.commentContext, hc->hc_pixelColor);
1369 	}
1370         last_hc = hc;
1371       }
1372 
1373       if (hc->hc_font.gateFont.family == FF_KANJI) {
1374 	    XDrawString16( TkGate.D,
1375                        TkGate.W,
1376                        gc,
1377                        hu->hu_x + x,hu->hu_y + y,
1378                        (XChar2b*)hu->hu_text,
1379                        strlen(hu->hu_text)/2 );
1380       }/* else if (strcmp(h->h_locale->l_encDisplay, "utf-8") == 0) {
1381 	      XCreateFontSet
1382 	      Xutf8DrawString(TkGate.D, TkGate.W,gc, XFontSet
1383                        hu->hu_x + x,hu->hu_y + y,
1384                        (XChar2b*)hu->hu_text,
1385                        strlen(hu->hu_text)/2 );
1386       } */else {
1387 	  GatePainter_drawString_new( TkGate.painterW,
1388 	                          TkGate.commentContext,
1389 	                          hu->hu_x + x, hu->hu_y + y,
1390 		                  hu->hu_text,
1391 		                  strlen(hu->hu_text) );
1392       }
1393 
1394       break;
1395     case HU_IMAGE :
1396       {
1397 	int width = hu->hu_width;
1398 	int height = HtmlContext_fontAscent(hc) + HtmlContext_fontDescent(hc);
1399 	int base_x = hu->hu_x + x;
1400 	int base_y = hu->hu_y + y - HtmlContext_fontAscent(hc);
1401 
1402 	if (!GateColor_equals(&hc->hc_pixelColor, &notAColor)) {
1403 	  XSetForeground(TkGate.D,igc,hc->hc_pixelColor.xColor.pixel);
1404 	  ZFillRectangle(TkGate.D,TkGate.W,igc, base_x,base_y, width, height);
1405 	} else
1406 	  XSetForeground(TkGate.D,igc,XWhitePixelOfScreen(TkGate.S));
1407 	Tk_RedrawImage(hu->hu_image, 0, 0, width, height, TkGate.W, base_x, base_y);
1408       }
1409       break;
1410     default :
1411       break;
1412     }
1413   }
1414 }
1415 
Html_isHit(Html * h,int x,int y)1416 int Html_isHit(Html *h,int x,int y)
1417 {
1418   HtmlUnit *hu;
1419 
1420   for (hu = h->h_head;hu;hu = hu->hu_next) {
1421     HtmlContext *hc = hu->hu_context;			/* Get context of this unit */
1422 
1423     if ((x >= hu->hu_x) &&
1424         (x <= (hu->hu_x + hu->hu_width)) &&
1425         (y <= (hu->hu_y + HtmlContext_fontDescent(hc))) &&
1426         (y >= (hu->hu_y - HtmlContext_fontAscent(hc))))
1427       return 1;
1428   }
1429 
1430   return 0;
1431 }
1432 
Html_getLink(Html * h,int x,int y)1433 const char *Html_getLink(Html *h,int x,int y)
1434 {
1435   HtmlUnit *hu;
1436 
1437   for (hu = h->h_head;hu;hu = hu->hu_next) {
1438     HtmlContext *hc = hu->hu_context;		/* Get context of this unit */
1439 
1440     if (!hc->hc_link) continue;
1441 
1442     if (hu->hu_type == HU_IMAGE) {
1443       int width = hu->hu_width;
1444       int height = HtmlContext_fontAscent(hc) + HtmlContext_fontDescent(hc);
1445       int base_x = hu->hu_x;
1446       int base_y = hu->hu_y - HtmlContext_fontAscent(hc);
1447 
1448       if (x >= base_x && x <= (base_x + width)
1449 	  && y >= base_y && y <= (base_y + height)) {
1450 
1451 	return hc->hc_link;
1452       }
1453     } else {
1454       if (x >= hu->hu_x && x <= (hu->hu_x + hu->hu_width)
1455 	  && y <= (hu->hu_y + HtmlContext_fontDescent(hc)) && y >=
1456 	  (hu->hu_y - HtmlContext_fontAscent(hc))) {
1457 
1458 	return hc->hc_link;
1459       }
1460     }
1461   }
1462 
1463   return 0;
1464 }
1465 
HtmlContext_print(const HtmlContext * context,FILE * fp)1466 void HtmlContext_print(const HtmlContext * context, FILE * fp)
1467 {
1468   fputs("Html context:    ", fp);
1469 
1470   fprintf(fp, "\tassociated hyperlink:%s\n", context->hc_link);
1471   fprintf(fp, "\tassociated tag:      %s\n", context->hc_tag);
1472   fprintf(fp, "\tpreformat:           %d\n", context->hc_preformat);
1473   fprintf(fp, "\tis 16 bit:           %d\n", context->hc_is16bit);
1474   fprintf(fp, "\tspace width:         %d\n", context->hc_spaceWidth);
1475 }
1476