Character-set-isation and configurability in the WinHelp backend.
[sgt/halibut] / bk_whlp.c
1 /*
2 * Windows Help backend for Halibut
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8 #include <assert.h>
9
10 #include "halibut.h"
11 #include "winhelp.h"
12
13 struct bk_whlp_state {
14 WHLP h;
15 indexdata *idx;
16 keywordlist *keywords;
17 WHLP_TOPIC curr_topic;
18 int charset;
19 charset_state cstate;
20 FILE *cntfp;
21 int cnt_last_level, cnt_workaround;
22 };
23
24 typedef struct {
25 int charset;
26 wchar_t *bullet, *lquote, *rquote, *titlepage, *sectsuffix, *listsuffix;
27 char *filename;
28 } whlpconf;
29
30 /*
31 * Indexes of fonts in our standard font descriptor set.
32 */
33 enum {
34 FONT_NORMAL,
35 FONT_EMPH,
36 FONT_CODE,
37 FONT_ITAL_CODE,
38 FONT_BOLD_CODE,
39 FONT_TITLE,
40 FONT_TITLE_EMPH,
41 FONT_TITLE_CODE,
42 FONT_RULE
43 };
44
45 static void whlp_rdaddwc(rdstringc *rs, word *text, whlpconf *conf,
46 charset_state *state);
47 static void whlp_rdadds(rdstringc *rs, const wchar_t *text, whlpconf *conf,
48 charset_state *state);
49 static void whlp_mkparagraph(struct bk_whlp_state *state,
50 int font, word *text, int subsidiary,
51 whlpconf *conf);
52 static void whlp_navmenu(struct bk_whlp_state *state, paragraph *p,
53 whlpconf *conf);
54 static void whlp_contents_write(struct bk_whlp_state *state,
55 int level, char *text, WHLP_TOPIC topic);
56 static void whlp_wtext(struct bk_whlp_state *state, const wchar_t *text);
57
58 paragraph *whlp_config_filename(char *filename)
59 {
60 return cmdline_cfg_simple("winhelp-filename", filename, NULL);
61 }
62
63 static whlpconf whlp_configure(paragraph *source) {
64 paragraph *p;
65 whlpconf ret;
66
67 /*
68 * Defaults.
69 */
70 ret.charset = CS_CP1252;
71 ret.bullet = L"\x2022\0-\0\0";
72 ret.lquote = L"\x2018\0\x2019\0\"\0\"\0\0";
73 ret.rquote = uadv(ret.lquote);
74 ret.filename = dupstr("output.hlp");
75 ret.titlepage = L"Title page";
76 ret.sectsuffix = L": ";
77 ret.listsuffix = L".";
78
79 /*
80 * Two-pass configuration so that we can pick up global config
81 * (e.g. `quotes') before having it overridden by specific
82 * config (`win-quotes'), irrespective of the order in which
83 * they occur.
84 */
85 for (p = source; p; p = p->next) {
86 if (p->type == para_Config) {
87 if (!ustricmp(p->keyword, L"quotes")) {
88 if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) {
89 ret.lquote = uadv(p->keyword);
90 ret.rquote = uadv(ret.lquote);
91 }
92 }
93 }
94 }
95
96 for (p = source; p; p = p->next) {
97 p->private_data = NULL;
98 if (p->type == para_Config) {
99 /*
100 * In principle we should support a `winhelp-charset'
101 * here. We don't, because my WinHelp output code
102 * doesn't know how to change character set. Once I
103 * find out, I'll support it.
104 */
105 if (p->parent && !ustricmp(p->keyword, L"winhelp-topic")) {
106 /* Store the topic name in the private_data field of the
107 * containing section. */
108 p->parent->private_data = uadv(p->keyword);
109 } else if (!ustricmp(p->keyword, L"winhelp-filename")) {
110 sfree(ret.filename);
111 ret.filename = dupstr(adv(p->origkeyword));
112 } else if (!ustricmp(p->keyword, L"winhelp-bullet")) {
113 ret.bullet = uadv(p->keyword);
114 } else if (!ustricmp(p->keyword, L"winhelp-section-suffix")) {
115 ret.sectsuffix = uadv(p->keyword);
116 } else if (!ustricmp(p->keyword, L"winhelp-list-suffix")) {
117 ret.listsuffix = uadv(p->keyword);
118 } else if (!ustricmp(p->keyword, L"winhelp-contents-titlepage")) {
119 ret.titlepage = uadv(p->keyword);
120 } else if (!ustricmp(p->keyword, L"winhelp-quotes")) {
121 if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) {
122 ret.lquote = uadv(p->keyword);
123 ret.rquote = uadv(ret.lquote);
124 }
125 }
126 }
127 }
128
129 /*
130 * Now process fallbacks on quote characters and bullets.
131 */
132 while (*uadv(ret.rquote) && *uadv(uadv(ret.rquote)) &&
133 (!cvt_ok(ret.charset, ret.lquote) ||
134 !cvt_ok(ret.charset, ret.rquote))) {
135 ret.lquote = uadv(ret.rquote);
136 ret.rquote = uadv(ret.lquote);
137 }
138
139 while (*ret.bullet && *uadv(ret.bullet) &&
140 !cvt_ok(ret.charset, ret.bullet))
141 ret.bullet = uadv(ret.bullet);
142
143 return ret;
144 }
145
146 void whlp_backend(paragraph *sourceform, keywordlist *keywords,
147 indexdata *idx, void *unused) {
148 WHLP h;
149 char *cntname;
150 paragraph *p, *lastsect;
151 struct bk_whlp_state state;
152 WHLP_TOPIC contents_topic;
153 int i;
154 int nesting;
155 indexentry *ie;
156 int done_contents_topic = FALSE;
157 whlpconf conf;
158
159 IGNORE(unused);
160
161 h = state.h = whlp_new();
162 state.keywords = keywords;
163 state.idx = idx;
164
165 whlp_start_macro(h, "CB(\"btn_about\",\"&About\",\"About()\")");
166 whlp_start_macro(h, "CB(\"btn_up\",\"&Up\",\"Contents()\")");
167 whlp_start_macro(h, "BrowseButtons()");
168
169 whlp_create_font(h, "Times New Roman", WHLP_FONTFAM_SERIF, 24,
170 0, 0, 0, 0);
171 whlp_create_font(h, "Times New Roman", WHLP_FONTFAM_SERIF, 24,
172 WHLP_FONT_ITALIC, 0, 0, 0);
173 whlp_create_font(h, "Courier New", WHLP_FONTFAM_FIXED, 24,
174 0, 0, 0, 0);
175 whlp_create_font(h, "Courier New", WHLP_FONTFAM_FIXED, 24,
176 WHLP_FONT_ITALIC, 0, 0, 0);
177 whlp_create_font(h, "Courier New", WHLP_FONTFAM_FIXED, 24,
178 WHLP_FONT_BOLD, 0, 0, 0);
179 whlp_create_font(h, "Arial", WHLP_FONTFAM_SERIF, 30,
180 WHLP_FONT_BOLD, 0, 0, 0);
181 whlp_create_font(h, "Arial", WHLP_FONTFAM_SERIF, 30,
182 WHLP_FONT_BOLD|WHLP_FONT_ITALIC, 0, 0, 0);
183 whlp_create_font(h, "Courier New", WHLP_FONTFAM_FIXED, 30,
184 WHLP_FONT_BOLD, 0, 0, 0);
185 whlp_create_font(h, "Courier New", WHLP_FONTFAM_SANS, 18,
186 WHLP_FONT_STRIKEOUT, 0, 0, 0);
187
188 conf = whlp_configure(sourceform);
189
190 state.charset = conf.charset;
191
192 /*
193 * Ensure the output file name has a .hlp extension. This is
194 * required since we must create the .cnt file in parallel with
195 * it.
196 */
197 {
198 int len = strlen(conf.filename);
199 if (len < 4 || conf.filename[len-4] != '.' ||
200 tolower(conf.filename[len-3] != 'h') ||
201 tolower(conf.filename[len-2] != 'l') ||
202 tolower(conf.filename[len-1] != 'p')) {
203 char *newf;
204 newf = mknewa(char, len + 5);
205 sprintf(newf, "%s.hlp", conf.filename);
206 sfree(conf.filename);
207 conf.filename = newf;
208 len = strlen(newf);
209 }
210 cntname = mknewa(char, len+1);
211 sprintf(cntname, "%.*s.cnt", len-4, conf.filename);
212 }
213
214 state.cntfp = fopen(cntname, "wb");
215 state.cnt_last_level = -1; state.cnt_workaround = 0;
216
217 /*
218 * Loop over the source form registering WHLP_TOPICs for
219 * everything.
220 */
221
222 contents_topic = whlp_register_topic(h, "Top", NULL);
223 whlp_primary_topic(h, contents_topic);
224 for (p = sourceform; p; p = p->next) {
225 if (p->type == para_Chapter ||
226 p->type == para_Appendix ||
227 p->type == para_UnnumberedChapter ||
228 p->type == para_Heading ||
229 p->type == para_Subsect) {
230
231 rdstringc rs = { 0, 0, NULL };
232 char *errstr;
233
234 whlp_rdadds(&rs, (wchar_t *)p->private_data, &conf, NULL);
235
236 p->private_data = whlp_register_topic(h, rs.text, &errstr);
237 if (!p->private_data) {
238 p->private_data = whlp_register_topic(h, NULL, NULL);
239 error(err_winhelp_ctxclash, &p->fpos, rs.text, errstr);
240 }
241 sfree(rs.text);
242 }
243 }
244
245 /*
246 * Loop over the index entries, preparing final text forms for
247 * each one.
248 */
249 for (i = 0; (ie = index234(idx->entries, i)) != NULL; i++) {
250 rdstringc rs = {0, 0, NULL};
251 whlp_rdaddwc(&rs, ie->text, &conf, NULL);
252 ie->backend_data = rs.text;
253 }
254
255 whlp_prepare(h);
256
257 /* ------------------------------------------------------------------
258 * Begin the contents page.
259 */
260
261 whlp_begin_topic(h, contents_topic, "Contents", "DB(\"btn_up\")", NULL);
262
263 /*
264 * The manual title goes in the non-scroll region, and also
265 * goes into the system title slot.
266 */
267 {
268 rdstringc rs = {0, 0, NULL};
269 for (p = sourceform; p; p = p->next) {
270 if (p->type == para_Title) {
271 whlp_begin_para(h, WHLP_PARA_NONSCROLL);
272 state.cstate = charset_init_state;
273 whlp_mkparagraph(&state, FONT_TITLE, p->words, FALSE, &conf);
274 whlp_wtext(&state, NULL);
275 whlp_end_para(h);
276 whlp_rdaddwc(&rs, p->words, &conf, NULL);
277 }
278 }
279 if (rs.text) {
280 whlp_title(h, rs.text);
281 fprintf(state.cntfp, ":Title %s\r\n", rs.text);
282 sfree(rs.text);
283 }
284 {
285 rdstringc rs2 = {0,0,NULL};
286 whlp_rdadds(&rs2, conf.titlepage, &conf, NULL);
287 whlp_contents_write(&state, 1, rs2.text, contents_topic);
288 sfree(rs2.text);
289 }
290 }
291
292 /*
293 * Put the copyright into the system section.
294 */
295 {
296 rdstringc rs = {0, 0, NULL};
297 for (p = sourceform; p; p = p->next) {
298 if (p->type == para_Copyright)
299 whlp_rdaddwc(&rs, p->words, &conf, NULL);
300 }
301 if (rs.text) {
302 whlp_copyright(h, rs.text);
303 sfree(rs.text);
304 }
305 }
306
307 lastsect = NULL;
308
309 /* ------------------------------------------------------------------
310 * Now we've done the contents page, we're ready to go through
311 * and do the main manual text. Ooh.
312 */
313 nesting = 0;
314 for (p = sourceform; p; p = p->next) switch (p->type) {
315 /*
316 * Things we ignore because we've already processed them or
317 * aren't going to touch them in this pass.
318 */
319 case para_IM:
320 case para_BR:
321 case para_Biblio: /* only touch BiblioCited */
322 case para_VersionID:
323 case para_NoCite:
324 case para_Title:
325 break;
326
327 case para_LcontPush:
328 case para_QuotePush:
329 nesting++;
330 break;
331 case para_LcontPop:
332 case para_QuotePop:
333 assert(nesting > 0);
334 nesting--;
335 break;
336
337 /*
338 * Chapter and section titles: start a new Help topic.
339 */
340 case para_Chapter:
341 case para_Appendix:
342 case para_UnnumberedChapter:
343 case para_Heading:
344 case para_Subsect:
345
346 if (!done_contents_topic) {
347 paragraph *p;
348
349 /*
350 * If this is the first section title we've seen, then
351 * we're currently still in the contents topic. We
352 * should therefore finish up the contents page by
353 * writing a nav menu.
354 */
355 for (p = sourceform; p; p = p->next) {
356 if (p->type == para_Chapter ||
357 p->type == para_Appendix ||
358 p->type == para_UnnumberedChapter)
359 whlp_navmenu(&state, p, &conf);
360 }
361
362 state.curr_topic = contents_topic;
363
364 done_contents_topic = TRUE;
365 }
366
367 if (lastsect && lastsect->child) {
368 paragraph *q;
369 /*
370 * Do a navigation menu for the previous section we
371 * were in.
372 */
373 for (q = lastsect->child; q; q = q->sibling)
374 whlp_navmenu(&state, q, &conf);
375 }
376 {
377 rdstringc rs = {0, 0, NULL};
378 WHLP_TOPIC new_topic, parent_topic;
379 char *macro, *topicid;
380 charset_state cstate = CHARSET_INIT_STATE;
381
382 new_topic = p->private_data;
383 whlp_browse_link(h, state.curr_topic, new_topic);
384 state.curr_topic = new_topic;
385
386 if (p->kwtext) {
387 whlp_rdaddwc(&rs, p->kwtext, &conf, &cstate);
388 whlp_rdadds(&rs, conf.sectsuffix, &conf, &cstate);
389 }
390 whlp_rdaddwc(&rs, p->words, &conf, &cstate);
391 whlp_rdadds(&rs, NULL, &conf, &cstate);
392
393 if (p->parent == NULL)
394 parent_topic = contents_topic;
395 else
396 parent_topic = (WHLP_TOPIC)p->parent->private_data;
397 topicid = whlp_topic_id(parent_topic);
398 macro = smalloc(100+strlen(topicid));
399 sprintf(macro,
400 "CBB(\"btn_up\",\"JI(`',`%s')\");EB(\"btn_up\")",
401 topicid);
402 whlp_begin_topic(h, new_topic,
403 rs.text ? rs.text : "",
404 macro, NULL);
405 sfree(macro);
406
407 {
408 /*
409 * Output the .cnt entry.
410 *
411 * WinHelp has a bug involving having an internal
412 * node followed by a leaf at the same level: the
413 * leaf is output at the wrong level. We can mostly
414 * work around this by modifying the leaf level
415 * itself (see whlp_contents_write), but this
416 * doesn't work for top-level sections since we
417 * can't turn a level-1 leaf into a level-0 one. So
418 * for top-level leaf sections (Bibliography
419 * springs to mind), we output an internal node
420 * containing only the leaf for that section.
421 */
422 int i;
423 paragraph *q;
424
425 /* Count up the level. */
426 i = 1;
427 for (q = p; q->parent; q = q->parent) i++;
428
429 if (p->child || !p->parent) {
430 /*
431 * If p has children then it needs to be a
432 * folder; if it has no parent then it needs to
433 * be a folder to work around the bug.
434 */
435 whlp_contents_write(&state, i, rs.text, NULL);
436 i++;
437 }
438 whlp_contents_write(&state, i, rs.text, new_topic);
439 }
440
441 sfree(rs.text);
442
443 whlp_begin_para(h, WHLP_PARA_NONSCROLL);
444 state.cstate = charset_init_state;
445 if (p->kwtext) {
446 whlp_mkparagraph(&state, FONT_TITLE, p->kwtext, FALSE, &conf);
447 whlp_set_font(h, FONT_TITLE);
448 whlp_wtext(&state, conf.sectsuffix);
449 }
450 whlp_mkparagraph(&state, FONT_TITLE, p->words, FALSE, &conf);
451 whlp_wtext(&state, NULL);
452 whlp_end_para(h);
453
454 lastsect = p;
455 }
456 break;
457
458 case para_Rule:
459 whlp_para_attr(h, WHLP_PARA_SPACEBELOW, 12);
460 whlp_para_attr(h, WHLP_PARA_ALIGNMENT, WHLP_ALIGN_CENTRE);
461 whlp_begin_para(h, WHLP_PARA_SCROLL);
462 whlp_set_font(h, FONT_RULE);
463 state.cstate = charset_init_state;
464 #define TEN L"\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"
465 #define TWENTY TEN TEN
466 #define FORTY TWENTY TWENTY
467 #define EIGHTY FORTY FORTY
468 state.cstate = charset_init_state;
469 whlp_wtext(&state, EIGHTY);
470 whlp_wtext(&state, NULL);
471 #undef TEN
472 #undef TWENTY
473 #undef FORTY
474 #undef EIGHTY
475 whlp_end_para(h);
476 break;
477
478 case para_Normal:
479 case para_Copyright:
480 case para_DescribedThing:
481 case para_Description:
482 case para_BiblioCited:
483 case para_Bullet:
484 case para_NumberedList:
485 whlp_para_attr(h, WHLP_PARA_SPACEBELOW, 12);
486 if (p->type == para_Bullet || p->type == para_NumberedList) {
487 whlp_para_attr(h, WHLP_PARA_LEFTINDENT, 72*nesting + 72);
488 whlp_para_attr(h, WHLP_PARA_FIRSTLINEINDENT, -36);
489 whlp_set_tabstop(h, 72, WHLP_ALIGN_LEFT);
490 whlp_begin_para(h, WHLP_PARA_SCROLL);
491 whlp_set_font(h, FONT_NORMAL);
492 state.cstate = charset_init_state;
493 if (p->type == para_Bullet) {
494 whlp_wtext(&state, conf.bullet);
495 } else {
496 whlp_mkparagraph(&state, FONT_NORMAL, p->kwtext, FALSE, &conf);
497 whlp_wtext(&state, conf.listsuffix);
498 }
499 whlp_wtext(&state, NULL);
500 whlp_tab(h);
501 } else {
502 whlp_para_attr(h, WHLP_PARA_LEFTINDENT,
503 72*nesting + (p->type==para_Description ? 72 : 0));
504 whlp_begin_para(h, WHLP_PARA_SCROLL);
505 }
506
507 state.cstate = charset_init_state;
508
509 if (p->type == para_BiblioCited) {
510 whlp_mkparagraph(&state, FONT_NORMAL, p->kwtext, FALSE, &conf);
511 whlp_wtext(&state, L" ");
512 }
513
514 whlp_mkparagraph(&state, FONT_NORMAL, p->words, FALSE, &conf);
515 whlp_wtext(&state, NULL);
516 whlp_end_para(h);
517 break;
518
519 case para_Code:
520 /*
521 * In a code paragraph, each individual word is a line. For
522 * Help files, we will have to output this as a set of
523 * paragraphs, all but the last of which don't set
524 * SPACEBELOW.
525 */
526 {
527 word *w;
528 wchar_t *t, *e, *tmp;
529
530 for (w = p->words; w; w = w->next) if (w->type == word_WeakCode) {
531 t = w->text;
532 if (w->next && w->next->type == word_Emph) {
533 w = w->next;
534 e = w->text;
535 } else
536 e = NULL;
537
538 if (!w->next)
539 whlp_para_attr(h, WHLP_PARA_SPACEBELOW, 12);
540
541 whlp_para_attr(h, WHLP_PARA_LEFTINDENT, 72*nesting);
542 whlp_begin_para(h, WHLP_PARA_SCROLL);
543 state.cstate = charset_init_state;
544 while (e && *e && *t) {
545 int n;
546 int ec = *e;
547
548 for (n = 0; t[n] && e[n] && e[n] == ec; n++);
549 if (ec == 'i')
550 whlp_set_font(h, FONT_ITAL_CODE);
551 else if (ec == 'b')
552 whlp_set_font(h, FONT_BOLD_CODE);
553 else
554 whlp_set_font(h, FONT_CODE);
555 tmp = mknewa(wchar_t, n+1);
556 ustrncpy(tmp, t, n);
557 tmp[n] = L'\0';
558 whlp_wtext(&state, tmp);
559 whlp_wtext(&state, NULL);
560 state.cstate = charset_init_state;
561 sfree(tmp);
562 t += n;
563 e += n;
564 }
565 whlp_set_font(h, FONT_CODE);
566 whlp_wtext(&state, t);
567 whlp_wtext(&state, NULL);
568 whlp_end_para(h);
569 }
570 }
571 break;
572 }
573
574 fclose(state.cntfp);
575 whlp_close(h, conf.filename);
576
577 /*
578 * Loop over the index entries, cleaning up our final text
579 * forms.
580 */
581 for (i = 0; (ie = index234(idx->entries, i)) != NULL; i++) {
582 sfree(ie->backend_data);
583 }
584
585 sfree(conf.filename);
586 sfree(cntname);
587 }
588
589 static void whlp_contents_write(struct bk_whlp_state *state,
590 int level, char *text, WHLP_TOPIC topic) {
591 /*
592 * Horrifying bug in WinHelp. When dropping a section level or
593 * more without using a folder-type entry, WinHelp accidentally
594 * adds one to the section level. So we correct for that here.
595 */
596 if (state->cnt_last_level > level && topic)
597 state->cnt_workaround = -1;
598 else if (!topic)
599 state->cnt_workaround = 0;
600 state->cnt_last_level = level;
601
602 fprintf(state->cntfp, "%d ", level + state->cnt_workaround);
603 while (*text) {
604 if (*text == '=')
605 fputc('\\', state->cntfp);
606 fputc(*text, state->cntfp);
607 text++;
608 }
609 if (topic)
610 fprintf(state->cntfp, "=%s", whlp_topic_id(topic));
611 fputc('\n', state->cntfp);
612 }
613
614 static void whlp_navmenu(struct bk_whlp_state *state, paragraph *p,
615 whlpconf *conf) {
616 whlp_begin_para(state->h, WHLP_PARA_SCROLL);
617 whlp_start_hyperlink(state->h, (WHLP_TOPIC)p->private_data);
618 state->cstate = charset_init_state;
619 if (p->kwtext) {
620 whlp_mkparagraph(state, FONT_NORMAL, p->kwtext, TRUE, conf);
621 whlp_set_font(state->h, FONT_NORMAL);
622 whlp_wtext(state, conf->sectsuffix);
623 }
624 whlp_mkparagraph(state, FONT_NORMAL, p->words, TRUE, conf);
625 whlp_wtext(state, NULL);
626 whlp_end_hyperlink(state->h);
627 whlp_end_para(state->h);
628
629 }
630
631 static void whlp_mkparagraph(struct bk_whlp_state *state,
632 int font, word *text, int subsidiary,
633 whlpconf *conf) {
634 keyword *kwl;
635 int deffont = font;
636 int currfont = -1;
637 int newfont;
638 paragraph *xref_target = NULL;
639
640 for (; text; text = text->next) switch (text->type) {
641 case word_HyperLink:
642 case word_HyperEnd:
643 break;
644
645 case word_IndexRef:
646 if (subsidiary) break; /* disabled in subsidiary bits */
647 {
648 indextag *tag = index_findtag(state->idx, text->text);
649 int i;
650 if (!tag)
651 break;
652 for (i = 0; i < tag->nrefs; i++)
653 whlp_index_term(state->h, tag->refs[i]->backend_data,
654 state->curr_topic);
655 }
656 break;
657
658 case word_UpperXref:
659 case word_LowerXref:
660 if (subsidiary) break; /* disabled in subsidiary bits */
661 kwl = kw_lookup(state->keywords, text->text);
662 assert(xref_target == NULL);
663 if (kwl) {
664 if (kwl->para->type == para_NumberedList) {
665 break; /* don't xref to numbered list items */
666 } else if (kwl->para->type == para_BiblioCited) {
667 /*
668 * An xref to a bibliography item jumps to the section
669 * containing it.
670 */
671 if (kwl->para->parent)
672 xref_target = kwl->para->parent;
673 else
674 break;
675 } else {
676 xref_target = kwl->para;
677 }
678 whlp_start_hyperlink(state->h,
679 (WHLP_TOPIC)xref_target->private_data);
680 }
681 break;
682
683 case word_XrefEnd:
684 if (subsidiary) break; /* disabled in subsidiary bits */
685 if (xref_target)
686 whlp_end_hyperlink(state->h);
687 xref_target = NULL;
688 break;
689
690 case word_Normal:
691 case word_Emph:
692 case word_Code:
693 case word_WeakCode:
694 case word_WhiteSpace:
695 case word_EmphSpace:
696 case word_CodeSpace:
697 case word_WkCodeSpace:
698 case word_Quote:
699 case word_EmphQuote:
700 case word_CodeQuote:
701 case word_WkCodeQuote:
702 if (towordstyle(text->type) == word_Emph)
703 newfont = deffont + FONT_EMPH;
704 else if (towordstyle(text->type) == word_Code ||
705 towordstyle(text->type) == word_WeakCode)
706 newfont = deffont + FONT_CODE;
707 else
708 newfont = deffont;
709 if (newfont != currfont) {
710 currfont = newfont;
711 whlp_set_font(state->h, newfont);
712 }
713 if (removeattr(text->type) == word_Normal) {
714 if (cvt_ok(conf->charset, text->text) || !text->alt)
715 whlp_wtext(state, text->text);
716 else
717 whlp_mkparagraph(state, deffont, text->alt, FALSE, conf);
718 } else if (removeattr(text->type) == word_WhiteSpace) {
719 whlp_wtext(state, L" ");
720 } else if (removeattr(text->type) == word_Quote) {
721 whlp_wtext(state,
722 quoteaux(text->aux) == quote_Open ?
723 conf->lquote : conf->rquote);
724 }
725 break;
726 }
727 }
728
729 static void whlp_rdaddwc(rdstringc *rs, word *text, whlpconf *conf,
730 charset_state *state) {
731 charset_state ourstate = CHARSET_INIT_STATE;
732
733 if (!state)
734 state = &ourstate;
735
736 for (; text; text = text->next) switch (text->type) {
737 case word_HyperLink:
738 case word_HyperEnd:
739 case word_UpperXref:
740 case word_LowerXref:
741 case word_XrefEnd:
742 case word_IndexRef:
743 break;
744
745 case word_Normal:
746 case word_Emph:
747 case word_Code:
748 case word_WeakCode:
749 case word_WhiteSpace:
750 case word_EmphSpace:
751 case word_CodeSpace:
752 case word_WkCodeSpace:
753 case word_Quote:
754 case word_EmphQuote:
755 case word_CodeQuote:
756 case word_WkCodeQuote:
757 assert(text->type != word_CodeQuote &&
758 text->type != word_WkCodeQuote);
759 if (removeattr(text->type) == word_Normal) {
760 if (cvt_ok(conf->charset, text->text) || !text->alt)
761 whlp_rdadds(rs, text->text, conf, state);
762 else
763 whlp_rdaddwc(rs, text->alt, conf, state);
764 } else if (removeattr(text->type) == word_WhiteSpace) {
765 whlp_rdadds(rs, L" ", conf, state);
766 } else if (removeattr(text->type) == word_Quote) {
767 whlp_rdadds(rs, quoteaux(text->aux) == quote_Open ?
768 conf->lquote : conf->rquote, conf, state);
769 }
770 break;
771 }
772
773 if (state == &ourstate)
774 whlp_rdadds(rs, NULL, conf, state);
775 }
776
777 static void whlp_rdadds(rdstringc *rs, const wchar_t *text, whlpconf *conf,
778 charset_state *state)
779 {
780 charset_state ourstate = CHARSET_INIT_STATE;
781 int textlen = text ? ustrlen(text) : 0;
782 char outbuf[256];
783 int ret;
784
785 if (!state)
786 state = &ourstate;
787
788 while (textlen > 0 &&
789 (ret = charset_from_unicode(&text, &textlen, outbuf,
790 lenof(outbuf)-1,
791 conf->charset, state, NULL)) > 0) {
792 outbuf[ret] = '\0';
793 rdaddsc(rs, outbuf);
794 }
795
796 if (text == NULL || state == &ourstate) {
797 if ((ret = charset_from_unicode(NULL, 0, outbuf, lenof(outbuf)-1,
798 conf->charset, state, NULL)) > 0) {
799 outbuf[ret] = '\0';
800 rdaddsc(rs, outbuf);
801 }
802 }
803 }
804
805 static void whlp_wtext(struct bk_whlp_state *state, const wchar_t *text)
806 {
807 int textlen = text ? ustrlen(text) : 0;
808 char outbuf[256];
809 int ret;
810
811 while (textlen > 0 &&
812 (ret = charset_from_unicode(&text, &textlen, outbuf,
813 lenof(outbuf)-1,
814 state->charset, &state->cstate,
815 NULL)) > 0) {
816 outbuf[ret] = '\0';
817 whlp_text(state->h, outbuf);
818 }
819
820 if (text == NULL) {
821 if ((ret = charset_from_unicode(NULL, 0, outbuf, lenof(outbuf)-1,
822 state->charset, &state->cstate,
823 NULL)) > 0) {
824 outbuf[ret] = '\0';
825 whlp_text(state->h, outbuf);
826 }
827 }
828 }