Add an error check for correct formatting in Deflate uncompressed
[sgt/halibut] / bk_paper.c
1 /*
2 * Paper printing pre-backend for Halibut.
3 *
4 * This module does all the processing common to both PostScript
5 * and PDF output: selecting fonts, line wrapping and page breaking
6 * in accordance with font metrics, laying out the contents and
7 * index pages, generally doing all the page layout. After this,
8 * bk_ps.c and bk_pdf.c should only need to do linear translations
9 * into their literal output format.
10 */
11
12 /*
13 * TODO in future work:
14 *
15 * - linearised PDF, perhaps?
16 *
17 * - I'm uncertain of whether I need to include a ToUnicode CMap
18 * in each of my font definitions in PDF. Currently things (by
19 * which I mean cut and paste out of acroread) seem to be
20 * working fairly happily without it, but I don't know.
21 *
22 * - rather than the ugly aux_text mechanism for rendering chapter
23 * titles, we could actually build the correct word list and
24 * wrap it as a whole.
25 *
26 * - get vertical font metrics and use them to position the PDF
27 * xref boxes more pleasantly
28 *
29 * - configurability
30 * * page header and footer should be configurable; we should
31 * be able to shift the page number elsewhere, and add other
32 * things such as the current chapter/section title and fixed
33 * text
34 * * remove the fixed mapping from heading levels to heading
35 * styles; offer a menu of styles from which the user can
36 * choose at every heading level
37 * * first-line indent in paragraphs
38 * * fixed text: `Contents', `Index', the colon-space and full
39 * stop in chapter title constructions
40 * * configurable location of contents?
41 * * certainly configurably _remove_ the contents, and possibly
42 * also the index
43 * * double-sided document switch?
44 * + means you have two header/footer formats which
45 * alternate
46 * + and means that mandatory page breaks before chapter
47 * titles should include a blank page if necessary to
48 * start the next section to a right-hand page
49 *
50 * - title pages
51 *
52 * - ability to use Type 1 fonts without AFM files
53 * * we need to parse the font to extract its metrics
54 *
55 * - character substitution for better typography?
56 * * use real ellipsis rather than ...
57 * * a hyphen in a word by itself might prefer to be an en-dash
58 * * (Americans might even want a convenient way to use an
59 * em-dash)
60 * * DON'T DO ANY OF THE ABOVE WITHIN \c OR \cw!
61 * * substituting `minus' for `hyphen' in the standard encoding
62 * is probably preferable in Courier, though certainly not in
63 * the main text font
64 * * if I do do this lot, I'm rather inclined to at least try
65 * to think up a configurable way to do it so that Americans
66 * can do em-dash tricks without my intervention and other
67 * people can do other odd things too.
68 */
69
70 #include <assert.h>
71 #include <stdio.h>
72 #include <stdarg.h>
73 #include <stdlib.h>
74
75 #include "halibut.h"
76 #include "paper.h"
77
78 typedef struct paper_conf_Tag paper_conf;
79 typedef struct paper_idx_Tag paper_idx;
80
81 typedef struct {
82 font_data *fonts[NFONTS];
83 int font_size;
84 } font_cfg;
85
86 struct paper_conf_Tag {
87 int paper_width;
88 int paper_height;
89 int left_margin;
90 int top_margin;
91 int right_margin;
92 int bottom_margin;
93 int indent_list_bullet;
94 int indent_list_after;
95 int indent_list;
96 int indent_quote;
97 int base_leading;
98 int base_para_spacing;
99 int chapter_top_space;
100 int sect_num_left_space;
101 int chapter_underline_depth;
102 int chapter_underline_thickness;
103 int rule_thickness;
104 font_cfg fbase, fcode, ftitle, fchapter, *fsect;
105 int nfsect;
106 int contents_indent_step;
107 int contents_margin;
108 int leader_separation;
109 int index_gutter;
110 int index_cols;
111 int index_minsep;
112 int pagenum_fontsize;
113 int footer_distance;
114 wchar_t *lquote, *rquote, *bullet;
115 wchar_t *contents_text, *index_text;
116 /* These are derived from the above */
117 int base_width;
118 int page_height;
119 int index_colwidth;
120 };
121
122 struct paper_idx_Tag {
123 /*
124 * Word list giving the page numbers on which this index entry
125 * appears. Also the last word in the list, for ease of
126 * construction.
127 */
128 word *words;
129 word *lastword;
130 /*
131 * The last page added to the list (so we can ensure we don't
132 * add one twice).
133 */
134 page_data *lastpage;
135 };
136
137 enum {
138 word_PageXref = word_NotWordType + 1
139 };
140
141 /* Flags for render_string() */
142 #define RS_NOLIG 1
143
144 static font_data *make_std_font(font_list *fontlist, char const *name);
145 static void wrap_paragraph(para_data *pdata, word *words,
146 int w, int i1, int i2, paper_conf *conf);
147 static page_data *page_breaks(line_data *first, line_data *last,
148 int page_height, int ncols, int headspace);
149 static int render_string(page_data *page, font_data *font, int fontsize,
150 int x, int y, wchar_t *str, unsigned flags);
151 static int render_line(line_data *ldata, int left_x, int top_y,
152 xref_dest *dest, keywordlist *keywords, indexdata *idx,
153 paper_conf *conf);
154 static void render_para(para_data *pdata, paper_conf *conf,
155 keywordlist *keywords, indexdata *idx,
156 paragraph *index_placeholder, page_data *index_page);
157 static int string_width(font_data *font, wchar_t const *string, int *errs,
158 unsigned flags);
159 static int paper_width_simple(para_data *pdata, word *text, paper_conf *conf);
160 static para_data *code_paragraph(int indent, word *words, paper_conf *conf);
161 static para_data *rule_paragraph(int indent, paper_conf *conf);
162 static void add_rect_to_page(page_data *page, int x, int y, int w, int h);
163 static para_data *make_para_data(int ptype, int paux, int indent, int rmargin,
164 word *pkwtext, word *pkwtext2, word *pwords,
165 paper_conf *conf);
166 static void standard_line_spacing(para_data *pdata, paper_conf *conf);
167 static wchar_t *prepare_outline_title(word *first, wchar_t *separator,
168 word *second);
169 static word *fake_word(wchar_t *text);
170 static word *fake_space_word(void);
171 static word *fake_page_ref(page_data *page);
172 static word *fake_end_ref(void);
173 static word *prepare_contents_title(word *first, wchar_t *separator,
174 word *second);
175 static void fold_into_page(page_data *dest, page_data *src, int right_shift);
176
177 static int fonts_ok(wchar_t *string, ...)
178 {
179 font_data *font;
180 va_list ap;
181 int ret = TRUE;
182
183 va_start(ap, string);
184 while ( (font = va_arg(ap, font_data *)) != NULL) {
185 int errs;
186 (void) string_width(font, string, &errs, 0);
187 if (errs) {
188 ret = FALSE;
189 break;
190 }
191 }
192 va_end(ap);
193
194 return ret;
195 }
196
197 static void paper_cfg_fonts(font_data **fonts, font_list *fontlist,
198 wchar_t *wp, filepos *fpos) {
199 font_data *f;
200 char *fn;
201 int i;
202
203 for (i = 0; i < NFONTS && *wp; i++, wp = uadv(wp)) {
204 fn = utoa_dup(wp, CS_ASCII);
205 f = make_std_font(fontlist, fn);
206 if (f)
207 fonts[i] = f;
208 else
209 /* FIXME: proper error */
210 error(err_nofont, fpos, wp);
211 }
212 }
213
214 static paper_conf paper_configure(paragraph *source, font_list *fontlist) {
215 paragraph *p;
216 paper_conf ret;
217
218 /*
219 * Defaults.
220 */
221 ret.paper_width = 595 * UNITS_PER_PT;
222 ret.paper_height = 842 * UNITS_PER_PT;
223 ret.left_margin = 72 * UNITS_PER_PT;
224 ret.top_margin = 72 * UNITS_PER_PT;
225 ret.right_margin = 72 * UNITS_PER_PT;
226 ret.bottom_margin = 108 * UNITS_PER_PT;
227 ret.indent_list_bullet = 6 * UNITS_PER_PT;
228 ret.indent_list_after = 18 * UNITS_PER_PT;
229 ret.indent_quote = 18 * UNITS_PER_PT;
230 ret.base_leading = UNITS_PER_PT;
231 ret.base_para_spacing = 10 * UNITS_PER_PT;
232 ret.chapter_top_space = 72 * UNITS_PER_PT;
233 ret.sect_num_left_space = 12 * UNITS_PER_PT;
234 ret.chapter_underline_depth = 14 * UNITS_PER_PT;
235 ret.chapter_underline_thickness = 3 * UNITS_PER_PT;
236 ret.rule_thickness = 1 * UNITS_PER_PT;
237 ret.fbase.font_size = 12;
238 ret.fbase.fonts[FONT_NORMAL] = make_std_font(fontlist, "Times-Roman");
239 ret.fbase.fonts[FONT_EMPH] = make_std_font(fontlist, "Times-Italic");
240 ret.fbase.fonts[FONT_CODE] = make_std_font(fontlist, "Courier");
241 ret.fcode.font_size = 12;
242 ret.fcode.fonts[FONT_NORMAL] = make_std_font(fontlist, "Courier-Bold");
243 ret.fcode.fonts[FONT_EMPH] = make_std_font(fontlist, "Courier-Oblique");
244 ret.fcode.fonts[FONT_CODE] = make_std_font(fontlist, "Courier");
245 ret.ftitle.font_size = 24;
246 ret.ftitle.fonts[FONT_NORMAL] = make_std_font(fontlist, "Helvetica-Bold");
247 ret.ftitle.fonts[FONT_EMPH] =
248 make_std_font(fontlist, "Helvetica-BoldOblique");
249 ret.ftitle.fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
250 ret.fchapter.font_size = 20;
251 ret.fchapter.fonts[FONT_NORMAL]= make_std_font(fontlist, "Helvetica-Bold");
252 ret.fchapter.fonts[FONT_EMPH] =
253 make_std_font(fontlist, "Helvetica-BoldOblique");
254 ret.fchapter.fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
255 ret.nfsect = 3;
256 ret.fsect = snewn(ret.nfsect, font_cfg);
257 ret.fsect[0].font_size = 16;
258 ret.fsect[0].fonts[FONT_NORMAL]= make_std_font(fontlist, "Helvetica-Bold");
259 ret.fsect[0].fonts[FONT_EMPH] =
260 make_std_font(fontlist, "Helvetica-BoldOblique");
261 ret.fsect[0].fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
262 ret.fsect[1].font_size = 14;
263 ret.fsect[1].fonts[FONT_NORMAL]= make_std_font(fontlist, "Helvetica-Bold");
264 ret.fsect[1].fonts[FONT_EMPH] =
265 make_std_font(fontlist, "Helvetica-BoldOblique");
266 ret.fsect[1].fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
267 ret.fsect[2].font_size = 13;
268 ret.fsect[2].fonts[FONT_NORMAL]= make_std_font(fontlist, "Helvetica-Bold");
269 ret.fsect[2].fonts[FONT_EMPH] =
270 make_std_font(fontlist, "Helvetica-BoldOblique");
271 ret.fsect[2].fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
272 ret.contents_indent_step = 24 * UNITS_PER_PT;
273 ret.contents_margin = 84 * UNITS_PER_PT;
274 ret.leader_separation = 12 * UNITS_PER_PT;
275 ret.index_gutter = 36 * UNITS_PER_PT;
276 ret.index_cols = 2;
277 ret.index_minsep = 18 * UNITS_PER_PT;
278 ret.pagenum_fontsize = 12;
279 ret.footer_distance = 32 * UNITS_PER_PT;
280 ret.lquote = L"\x2018\0\x2019\0'\0'\0\0";
281 ret.rquote = uadv(ret.lquote);
282 ret.bullet = L"\x2022\0-\0\0";
283 ret.contents_text = L"Contents";
284 ret.index_text = L"Index";
285
286 /*
287 * Two-pass configuration so that we can pick up global config
288 * (e.g. `quotes') before having it overridden by specific
289 * config (`paper-quotes'), irrespective of the order in which
290 * they occur.
291 */
292 for (p = source; p; p = p->next) {
293 if (p->type == para_Config) {
294 if (!ustricmp(p->keyword, L"quotes")) {
295 if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) {
296 ret.lquote = uadv(p->keyword);
297 ret.rquote = uadv(ret.lquote);
298 }
299 }
300 }
301 }
302
303 for (p = source; p; p = p->next) {
304 p->private_data = NULL;
305 if (p->type == para_Config) {
306 if (!ustricmp(p->keyword, L"paper-quotes")) {
307 if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) {
308 ret.lquote = uadv(p->keyword);
309 ret.rquote = uadv(ret.lquote);
310 }
311 } else if (!ustricmp(p->keyword, L"contents")) {
312 ret.contents_text = uadv(p->keyword);
313 } else if (!ustricmp(p->keyword, L"index")) {
314 ret.index_text = uadv(p->keyword);
315 } else if (!ustricmp(p->keyword, L"paper-bullet")) {
316 ret.bullet = uadv(p->keyword);
317 } else if (!ustricmp(p->keyword, L"paper-page-width")) {
318 ret.paper_width =
319 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
320 } else if (!ustricmp(p->keyword, L"paper-page-height")) {
321 ret.paper_height =
322 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
323 } else if (!ustricmp(p->keyword, L"paper-left-margin")) {
324 ret.left_margin =
325 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
326 } else if (!ustricmp(p->keyword, L"paper-top-margin")) {
327 ret.top_margin =
328 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
329 } else if (!ustricmp(p->keyword, L"paper-right-margin")) {
330 ret.right_margin =
331 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
332 } else if (!ustricmp(p->keyword, L"paper-bottom-margin")) {
333 ret.bottom_margin =
334 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
335 } else if (!ustricmp(p->keyword, L"paper-list-indent")) {
336 ret.indent_list_bullet =
337 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
338 } else if (!ustricmp(p->keyword, L"paper-listitem-indent")) {
339 ret.indent_list =
340 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
341 } else if (!ustricmp(p->keyword, L"paper-quote-indent")) {
342 ret.indent_quote =
343 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
344 } else if (!ustricmp(p->keyword, L"paper-base-leading")) {
345 ret.base_leading =
346 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
347 } else if (!ustricmp(p->keyword, L"paper-base-para-spacing")) {
348 ret.base_para_spacing =
349 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
350 } else if (!ustricmp(p->keyword, L"paper-chapter-top-space")) {
351 ret.chapter_top_space =
352 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
353 } else if (!ustricmp(p->keyword, L"paper-sect-num-left-space")) {
354 ret.sect_num_left_space =
355 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
356 } else if (!ustricmp(p->keyword, L"paper-chapter-underline-depth")) {
357 ret.chapter_underline_depth =
358 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
359 } else if (!ustricmp(p->keyword, L"paper-chapter-underline-thickness")) {
360 ret.chapter_underline_thickness =
361 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
362 } else if (!ustricmp(p->keyword, L"paper-rule-thickness")) {
363 ret.rule_thickness =
364 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
365 } else if (!ustricmp(p->keyword, L"paper-contents-indent-step")) {
366 ret.contents_indent_step =
367 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
368 } else if (!ustricmp(p->keyword, L"paper-contents-margin")) {
369 ret.contents_margin =
370 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
371 } else if (!ustricmp(p->keyword, L"paper-leader-separation")) {
372 ret.leader_separation =
373 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
374 } else if (!ustricmp(p->keyword, L"paper-index-gutter")) {
375 ret.index_gutter =
376 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
377 } else if (!ustricmp(p->keyword, L"paper-index-minsep")) {
378 ret.index_minsep =
379 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
380 } else if (!ustricmp(p->keyword, L"paper-footer-distance")) {
381 ret.footer_distance =
382 (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
383 } else if (!ustricmp(p->keyword, L"paper-base-font-size")) {
384 ret.fbase.font_size = utoi(uadv(p->keyword));
385 } else if (!ustricmp(p->keyword, L"paper-index-columns")) {
386 ret.index_cols = utoi(uadv(p->keyword));
387 } else if (!ustricmp(p->keyword, L"paper-pagenum-font-size")) {
388 ret.pagenum_fontsize = utoi(uadv(p->keyword));
389 } else if (!ustricmp(p->keyword, L"paper-base-fonts")) {
390 paper_cfg_fonts(ret.fbase.fonts, fontlist, uadv(p->keyword),
391 &p->fpos);
392 } else if (!ustricmp(p->keyword, L"paper-code-font-size")) {
393 ret.fcode.font_size = utoi(uadv(p->keyword));
394 } else if (!ustricmp(p->keyword, L"paper-code-fonts")) {
395 paper_cfg_fonts(ret.fcode.fonts, fontlist, uadv(p->keyword),
396 &p->fpos);
397 } else if (!ustricmp(p->keyword, L"paper-title-font-size")) {
398 ret.ftitle.font_size = utoi(uadv(p->keyword));
399 } else if (!ustricmp(p->keyword, L"paper-title-fonts")) {
400 paper_cfg_fonts(ret.ftitle.fonts, fontlist, uadv(p->keyword),
401 &p->fpos);
402 } else if (!ustricmp(p->keyword, L"paper-chapter-font-size")) {
403 ret.fchapter.font_size = utoi(uadv(p->keyword));
404 } else if (!ustricmp(p->keyword, L"paper-chapter-fonts")) {
405 paper_cfg_fonts(ret.fchapter.fonts, fontlist, uadv(p->keyword),
406 &p->fpos);
407 } else if (!ustricmp(p->keyword, L"paper-section-font-size")) {
408 wchar_t *q = uadv(p->keyword);
409 int n = 0;
410 if (uisdigit(*q)) {
411 n = utoi(q);
412 q = uadv(q);
413 }
414 if (n >= ret.nfsect) {
415 int i;
416 ret.fsect = sresize(ret.fsect, n+1, font_cfg);
417 for (i = ret.nfsect; i <= n; i++)
418 ret.fsect[i] = ret.fsect[ret.nfsect-1];
419 ret.nfsect = n+1;
420 }
421 ret.fsect[n].font_size = utoi(q);
422 } else if (!ustricmp(p->keyword, L"paper-section-fonts")) {
423 wchar_t *q = uadv(p->keyword);
424 int n = 0;
425 if (uisdigit(*q)) {
426 n = utoi(q);
427 q = uadv(q);
428 }
429 if (n >= ret.nfsect) {
430 int i;
431 ret.fsect = sresize(ret.fsect, n+1, font_cfg);
432 for (i = ret.nfsect; i <= n; i++)
433 ret.fsect[i] = ret.fsect[ret.nfsect-1];
434 ret.nfsect = n+1;
435 }
436 paper_cfg_fonts(ret.fsect[n].fonts, fontlist, q, &p->fpos);
437 }
438 }
439 }
440
441 /*
442 * Set up the derived fields in the conf structure.
443 */
444
445 ret.base_width =
446 ret.paper_width - ret.left_margin - ret.right_margin;
447 ret.page_height =
448 ret.paper_height - ret.top_margin - ret.bottom_margin;
449 ret.indent_list = ret.indent_list_bullet + ret.indent_list_after;
450 ret.index_colwidth =
451 (ret.base_width - (ret.index_cols-1) * ret.index_gutter)
452 / ret.index_cols;
453
454 /*
455 * Now process fallbacks on quote characters and bullets. We
456 * use string_width() to determine whether all of the relevant
457 * fonts contain the same character, and fall back whenever we
458 * find a character which not all of them support.
459 */
460
461 /* Quote characters need not be supported in the fixed code fonts,
462 * but must be in the title and body fonts. */
463 while (*uadv(ret.rquote) && *uadv(uadv(ret.rquote))) {
464 int n;
465 if (fonts_ok(ret.lquote,
466 ret.fbase.fonts[FONT_NORMAL],
467 ret.fbase.fonts[FONT_EMPH],
468 ret.ftitle.fonts[FONT_NORMAL],
469 ret.ftitle.fonts[FONT_EMPH],
470 ret.fchapter.fonts[FONT_NORMAL],
471 ret.fchapter.fonts[FONT_EMPH], NULL) &&
472 fonts_ok(ret.rquote,
473 ret.fbase.fonts[FONT_NORMAL],
474 ret.fbase.fonts[FONT_EMPH],
475 ret.ftitle.fonts[FONT_NORMAL],
476 ret.ftitle.fonts[FONT_EMPH],
477 ret.fchapter.fonts[FONT_NORMAL],
478 ret.fchapter.fonts[FONT_EMPH], NULL)) {
479 for (n = 0; n < ret.nfsect; n++)
480 if (!fonts_ok(ret.lquote,
481 ret.fsect[n].fonts[FONT_NORMAL],
482 ret.fsect[n].fonts[FONT_EMPH], NULL) ||
483 !fonts_ok(ret.rquote,
484 ret.fsect[n].fonts[FONT_NORMAL],
485 ret.fsect[n].fonts[FONT_EMPH], NULL))
486 break;
487 if (n == ret.nfsect)
488 break;
489 }
490 ret.lquote = uadv(ret.rquote);
491 ret.rquote = uadv(ret.lquote);
492 }
493
494 /* The bullet character only needs to be supported in the normal body
495 * font (not even in italics). */
496 while (*ret.bullet && *uadv(ret.bullet) &&
497 !fonts_ok(ret.bullet, ret.fbase.fonts[FONT_NORMAL], NULL))
498 ret.bullet = uadv(ret.bullet);
499
500 return ret;
501 }
502
503 void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords,
504 indexdata *idx) {
505 paragraph *p;
506 document *doc;
507 int indent, used_contents;
508 para_data *pdata, *firstpara = NULL, *lastpara = NULL;
509 para_data *firstcont, *lastcont;
510 line_data *firstline, *lastline, *firstcontline, *lastcontline;
511 page_data *pages;
512 font_list *fontlist;
513 paper_conf *conf, ourconf;
514 int has_index;
515 int pagenum;
516 paragraph index_placeholder_para;
517 page_data *first_index_page;
518
519 init_std_fonts();
520 fontlist = snew(font_list);
521 fontlist->head = fontlist->tail = NULL;
522
523 ourconf = paper_configure(sourceform, fontlist);
524 conf = &ourconf;
525
526 /*
527 * Set up a data structure to collect page numbers for each
528 * index entry.
529 */
530 {
531 int i;
532 indexentry *entry;
533
534 has_index = FALSE;
535
536 for (i = 0; (entry = index234(idx->entries, i)) != NULL; i++) {
537 paper_idx *pi = snew(paper_idx);
538
539 has_index = TRUE;
540
541 pi->words = pi->lastword = NULL;
542 pi->lastpage = NULL;
543
544 entry->backend_data = pi;
545 }
546 }
547
548 /*
549 * Format the contents entry for each heading.
550 */
551 {
552 word *contents_title;
553 contents_title = fake_word(conf->contents_text);
554
555 firstcont = make_para_data(para_UnnumberedChapter, 0, 0, 0,
556 NULL, NULL, contents_title, conf);
557 lastcont = firstcont;
558 lastcont->next = NULL;
559 firstcontline = firstcont->first;
560 lastcontline = lastcont->last;
561 for (p = sourceform; p; p = p->next) {
562 word *words;
563 int indent;
564
565 switch (p->type) {
566 case para_Chapter:
567 case para_Appendix:
568 case para_UnnumberedChapter:
569 case para_Heading:
570 case para_Subsect:
571 switch (p->type) {
572 case para_Chapter:
573 case para_Appendix:
574 words = prepare_contents_title(p->kwtext, L": ", p->words);
575 indent = 0;
576 break;
577 case para_UnnumberedChapter:
578 words = prepare_contents_title(NULL, NULL, p->words);
579 indent = 0;
580 break;
581 case para_Heading:
582 case para_Subsect:
583 words = prepare_contents_title(p->kwtext2, L" ", p->words);
584 indent = (p->aux + 1) * conf->contents_indent_step;
585 break;
586 }
587 pdata = make_para_data(para_Normal, p->aux, indent,
588 conf->contents_margin,
589 NULL, NULL, words, conf);
590 pdata->next = NULL;
591 pdata->contents_entry = p;
592 lastcont->next = pdata;
593 lastcont = pdata;
594
595 /*
596 * Link all contents line structures together into
597 * a big list.
598 */
599 if (pdata->first) {
600 if (lastcontline) {
601 lastcontline->next = pdata->first;
602 pdata->first->prev = lastcontline;
603 } else {
604 firstcontline = pdata->first;
605 pdata->first->prev = NULL;
606 }
607 lastcontline = pdata->last;
608 lastcontline->next = NULL;
609 }
610
611 break;
612 }
613 }
614
615 /*
616 * And one extra one, for the index.
617 */
618 if (has_index) {
619 pdata = make_para_data(para_Normal, 0, 0,
620 conf->contents_margin,
621 NULL, NULL,
622 fake_word(conf->index_text), conf);
623 pdata->next = NULL;
624 pdata->contents_entry = &index_placeholder_para;
625 lastcont->next = pdata;
626 lastcont = pdata;
627
628 if (pdata->first) {
629 if (lastcontline) {
630 lastcontline->next = pdata->first;
631 pdata->first->prev = lastcontline;
632 } else {
633 firstcontline = pdata->first;
634 pdata->first->prev = NULL;
635 }
636 lastcontline = pdata->last;
637 lastcontline->next = NULL;
638 }
639 }
640 }
641
642 /*
643 * Do the main paragraph formatting.
644 */
645 indent = 0;
646 used_contents = FALSE;
647 firstline = lastline = NULL;
648 for (p = sourceform; p; p = p->next) {
649 p->private_data = NULL;
650
651 switch (p->type) {
652 /*
653 * These paragraph types are either invisible or don't
654 * define text in the normal sense. Either way, they
655 * don't require wrapping.
656 */
657 case para_IM:
658 case para_BR:
659 case para_Biblio:
660 case para_NotParaType:
661 case para_Config:
662 case para_VersionID:
663 case para_NoCite:
664 break;
665
666 /*
667 * These paragraph types don't require wrapping, but
668 * they do affect the line width to which we wrap the
669 * rest of the paragraphs, so we need to pay attention.
670 */
671 case para_LcontPush:
672 indent += conf->indent_list; break;
673 case para_LcontPop:
674 indent -= conf->indent_list; assert(indent >= 0); break;
675 case para_QuotePush:
676 indent += conf->indent_quote; break;
677 case para_QuotePop:
678 indent -= conf->indent_quote; assert(indent >= 0); break;
679
680 /*
681 * This paragraph type is special. Process it
682 * specially.
683 */
684 case para_Code:
685 pdata = code_paragraph(indent, p->words, conf);
686 p->private_data = pdata;
687 if (pdata->first != pdata->last) {
688 pdata->first->penalty_after += 100000;
689 pdata->last->penalty_before += 100000;
690 }
691 break;
692
693 /*
694 * This paragraph is also special.
695 */
696 case para_Rule:
697 pdata = rule_paragraph(indent, conf);
698 p->private_data = pdata;
699 break;
700
701 /*
702 * All of these paragraph types require wrapping in the
703 * ordinary way. So we must supply a set of fonts, a
704 * line width and auxiliary information (e.g. bullet
705 * text) for each one.
706 */
707 case para_Chapter:
708 case para_Appendix:
709 case para_UnnumberedChapter:
710 case para_Heading:
711 case para_Subsect:
712 case para_Normal:
713 case para_BiblioCited:
714 case para_Bullet:
715 case para_NumberedList:
716 case para_DescribedThing:
717 case para_Description:
718 case para_Copyright:
719 case para_Title:
720 pdata = make_para_data(p->type, p->aux, indent, 0,
721 p->kwtext, p->kwtext2, p->words, conf);
722
723 p->private_data = pdata;
724
725 break;
726 }
727
728 if (p->private_data) {
729 pdata = (para_data *)p->private_data;
730
731 /*
732 * If this is the first non-title heading, we link the
733 * contents section in before it.
734 */
735 if (!used_contents && pdata->outline_level > 0) {
736 used_contents = TRUE;
737 if (lastpara)
738 lastpara->next = firstcont;
739 else
740 firstpara = firstcont;
741 lastpara = lastcont;
742 assert(lastpara->next == NULL);
743
744 if (lastline) {
745 lastline->next = firstcontline;
746 firstcontline->prev = lastline;
747 } else {
748 firstline = firstcontline;
749 firstcontline->prev = NULL;
750 }
751 assert(lastcontline != NULL);
752 lastline = lastcontline;
753 lastline->next = NULL;
754 }
755
756 /*
757 * Link all line structures together into a big list.
758 */
759 if (pdata->first) {
760 if (lastline) {
761 lastline->next = pdata->first;
762 pdata->first->prev = lastline;
763 } else {
764 firstline = pdata->first;
765 pdata->first->prev = NULL;
766 }
767 lastline = pdata->last;
768 lastline->next = NULL;
769 }
770
771 /*
772 * Link all paragraph structures together similarly.
773 */
774 pdata->next = NULL;
775 if (lastpara)
776 lastpara->next = pdata;
777 else
778 firstpara = pdata;
779 lastpara = pdata;
780 }
781 }
782
783 /*
784 * Now we have an enormous linked list of every line of text in
785 * the document. Break it up into pages.
786 */
787 pages = page_breaks(firstline, lastline, conf->page_height, 0, 0);
788
789 /*
790 * Number the pages.
791 */
792 {
793 char buf[40];
794 page_data *page;
795
796 pagenum = 0;
797
798 for (page = pages; page; page = page->next) {
799 sprintf(buf, "%d", ++pagenum);
800 page->number = ufroma_dup(buf, CS_ASCII);
801 }
802
803 if (has_index) {
804 first_index_page = snew(page_data);
805 first_index_page->next = first_index_page->prev = NULL;
806 first_index_page->first_line = NULL;
807 first_index_page->last_line = NULL;
808 first_index_page->first_text = first_index_page->last_text = NULL;
809 first_index_page->first_xref = first_index_page->last_xref = NULL;
810 first_index_page->first_rect = first_index_page->last_rect = NULL;
811
812 /* And don't forget the as-yet-uncreated index. */
813 sprintf(buf, "%d", ++pagenum);
814 first_index_page->number = ufroma_dup(buf, CS_ASCII);
815 }
816 }
817
818 /*
819 * Now we're ready to actually lay out the pages. We do this by
820 * looping over _paragraphs_, since we may need to track cross-
821 * references between lines and even across pages.
822 */
823 for (pdata = firstpara; pdata; pdata = pdata->next)
824 render_para(pdata, conf, keywords, idx,
825 &index_placeholder_para, first_index_page);
826
827 /*
828 * Now we've laid out the main body pages, we should have
829 * acquired a full set of page numbers for the index.
830 */
831 if (has_index) {
832 int i;
833 indexentry *entry;
834 word *index_title;
835 para_data *firstidx, *lastidx;
836 line_data *firstidxline, *lastidxline, *ldata;
837 page_data *ipages, *ipages2, *page;
838
839 /*
840 * Create a set of paragraphs for the index.
841 */
842 index_title = fake_word(conf->index_text);
843
844 firstidx = make_para_data(para_UnnumberedChapter, 0, 0, 0,
845 NULL, NULL, index_title, conf);
846 lastidx = firstidx;
847 lastidx->next = NULL;
848 firstidxline = firstidx->first;
849 lastidxline = lastidx->last;
850 for (i = 0; (entry = index234(idx->entries, i)) != NULL; i++) {
851 paper_idx *pi = (paper_idx *)entry->backend_data;
852 para_data *text, *pages;
853
854 if (!pi->words)
855 continue;
856
857 text = make_para_data(para_Normal, 0, 0,
858 conf->base_width - conf->index_colwidth,
859 NULL, NULL, entry->text, conf);
860
861 pages = make_para_data(para_Normal, 0, 0,
862 conf->base_width - conf->index_colwidth,
863 NULL, NULL, pi->words, conf);
864
865 text->justification = LEFT;
866 pages->justification = RIGHT;
867 text->last->space_after = pages->first->space_before =
868 conf->base_leading / 2;
869
870 pages->last->space_after = text->first->space_before =
871 conf->base_leading;
872
873 assert(text->first);
874 assert(pages->first);
875 assert(lastidxline);
876 assert(lastidx);
877
878 /*
879 * If feasible, fold the two halves of the index entry
880 * together.
881 */
882 if (text->last->real_shortfall + pages->first->real_shortfall >
883 conf->index_colwidth + conf->index_minsep) {
884 text->last->space_after = -1;
885 pages->first->space_before = -pages->first->line_height+1;
886 }
887
888 lastidx->next = text;
889 text->next = pages;
890 pages->next = NULL;
891 lastidx = pages;
892
893 /*
894 * Link all index line structures together into
895 * a big list.
896 */
897 text->last->next = pages->first;
898 pages->first->prev = text->last;
899
900 lastidxline->next = text->first;
901 text->first->prev = lastidxline;
902
903 lastidxline = pages->last;
904
905 /*
906 * Breaking an index entry anywhere is so bad that I
907 * think I'm going to forbid it totally.
908 */
909 for (ldata = text->first; ldata && ldata->next;
910 ldata = ldata->next) {
911 ldata->next->space_before += ldata->space_after + 1;
912 ldata->space_after = -1;
913 }
914 }
915
916 /*
917 * Now break the index into pages.
918 */
919 ipages = page_breaks(firstidxline, firstidxline, conf->page_height,
920 0, 0);
921 ipages2 = page_breaks(firstidxline->next, lastidxline,
922 conf->page_height,
923 conf->index_cols,
924 firstidxline->space_before +
925 firstidxline->line_height +
926 firstidxline->space_after);
927
928 /*
929 * This will have put each _column_ of the index on a
930 * separate page, which isn't what we want. Fold the pages
931 * back together.
932 */
933 page = ipages2;
934 while (page) {
935 int i;
936
937 for (i = 1; i < conf->index_cols; i++)
938 if (page->next) {
939 page_data *tpage;
940
941 fold_into_page(page, page->next,
942 i * (conf->index_colwidth +
943 conf->index_gutter));
944 tpage = page->next;
945 page->next = page->next->next;
946 if (page->next)
947 page->next->prev = page;
948 sfree(tpage);
949 }
950
951 page = page->next;
952 }
953 /* Also fold the heading on to the same page as the index items. */
954 fold_into_page(ipages, ipages2, 0);
955 ipages->next = ipages2->next;
956 if (ipages->next)
957 ipages->next->prev = ipages;
958 sfree(ipages2);
959 fold_into_page(first_index_page, ipages, 0);
960 first_index_page->next = ipages->next;
961 if (first_index_page->next)
962 first_index_page->next->prev = first_index_page;
963 sfree(ipages);
964 ipages = first_index_page;
965
966 /*
967 * Number the index pages, except the already-numbered
968 * first one.
969 */
970 for (page = ipages->next; page; page = page->next) {
971 char buf[40];
972 sprintf(buf, "%d", ++pagenum);
973 page->number = ufroma_dup(buf, CS_ASCII);
974 }
975
976 /*
977 * Render the index pages.
978 */
979 for (pdata = firstidx; pdata; pdata = pdata->next)
980 render_para(pdata, conf, keywords, idx,
981 &index_placeholder_para, first_index_page);
982
983 /*
984 * Link the index page list on to the end of the main page
985 * list.
986 */
987 if (!pages)
988 pages = ipages;
989 else {
990 for (page = pages; page->next; page = page->next);
991 page->next = ipages;
992 }
993
994 /*
995 * Same with the paragraph list, which will cause the index
996 * to be mentioned in the document outline.
997 */
998 if (!firstpara)
999 firstpara = firstidx;
1000 else
1001 lastpara->next = firstidx;
1002 lastpara = lastidx;
1003 }
1004
1005 /*
1006 * Draw the headers and footers.
1007 *
1008 * FIXME: this should be fully configurable, but for the moment
1009 * I'm just going to put in page numbers in the centre of a
1010 * footer and leave it at that.
1011 */
1012 {
1013 page_data *page;
1014
1015 for (page = pages; page; page = page->next) {
1016 int width;
1017
1018 width = conf->pagenum_fontsize *
1019 string_width(conf->fbase.fonts[FONT_NORMAL], page->number,
1020 NULL, 0);
1021
1022 render_string(page, conf->fbase.fonts[FONT_NORMAL],
1023 conf->pagenum_fontsize,
1024 conf->left_margin + (conf->base_width - width)/2,
1025 conf->bottom_margin - conf->footer_distance,
1026 page->number, 0);
1027 }
1028 }
1029
1030 /*
1031 * Start putting together the overall document structure we're
1032 * going to return.
1033 */
1034 doc = snew(document);
1035 doc->fonts = fontlist;
1036 doc->pages = pages;
1037 doc->paper_width = conf->paper_width;
1038 doc->paper_height = conf->paper_height;
1039
1040 /*
1041 * Collect the section heading paragraphs into a document
1042 * outline. This is slightly fiddly because the Title paragraph
1043 * isn't required to be at the start, although all the others
1044 * must be in order.
1045 */
1046 {
1047 int osize = 20;
1048
1049 doc->outline_elements = snewn(osize, outline_element);
1050 doc->n_outline_elements = 0;
1051
1052 /* First find the title. */
1053 for (pdata = firstpara; pdata; pdata = pdata->next) {
1054 if (pdata->outline_level == 0) {
1055 doc->outline_elements[0].level = 0;
1056 doc->outline_elements[0].pdata = pdata;
1057 doc->n_outline_elements++;
1058 break;
1059 }
1060 }
1061
1062 /* Then collect the rest. */
1063 for (pdata = firstpara; pdata; pdata = pdata->next) {
1064 if (pdata->outline_level > 0) {
1065 if (doc->n_outline_elements >= osize) {
1066 osize += 20;
1067 doc->outline_elements =
1068 sresize(doc->outline_elements, osize, outline_element);
1069 }
1070
1071 doc->outline_elements[doc->n_outline_elements].level =
1072 pdata->outline_level;
1073 doc->outline_elements[doc->n_outline_elements].pdata = pdata;
1074 doc->n_outline_elements++;
1075 }
1076 }
1077 }
1078
1079 return doc;
1080 }
1081
1082 static void setfont(para_data *p, font_cfg *f) {
1083 int i;
1084
1085 for (i = 0; i < NFONTS; i++) {
1086 p->fonts[i] = f->fonts[i];
1087 p->sizes[i] = f->font_size;
1088 }
1089 }
1090
1091 static para_data *make_para_data(int ptype, int paux, int indent, int rmargin,
1092 word *pkwtext, word *pkwtext2, word *pwords,
1093 paper_conf *conf)
1094 {
1095 para_data *pdata;
1096 line_data *ldata;
1097 int extra_indent, firstline_indent, aux_indent;
1098 word *aux, *aux2;
1099
1100 pdata = snew(para_data);
1101 pdata->outline_level = -1;
1102 pdata->outline_title = NULL;
1103 pdata->rect_type = RECT_NONE;
1104 pdata->contents_entry = NULL;
1105 pdata->justification = JUST;
1106 pdata->extraflags = 0;
1107
1108 /*
1109 * Choose fonts for this paragraph.
1110 */
1111 switch (ptype) {
1112 case para_Title:
1113 setfont(pdata, &conf->ftitle);
1114 pdata->outline_level = 0;
1115 break;
1116
1117 case para_Chapter:
1118 case para_Appendix:
1119 case para_UnnumberedChapter:
1120 setfont(pdata, &conf->fchapter);
1121 pdata->outline_level = 1;
1122 break;
1123
1124 case para_Heading:
1125 case para_Subsect:
1126 setfont(pdata,
1127 &conf->fsect[paux >= conf->nfsect ? conf->nfsect - 1 : paux]);
1128 pdata->outline_level = 2 + paux;
1129 break;
1130
1131 case para_Normal:
1132 case para_BiblioCited:
1133 case para_Bullet:
1134 case para_NumberedList:
1135 case para_DescribedThing:
1136 case para_Description:
1137 case para_Copyright:
1138 setfont(pdata, &conf->fbase);
1139 break;
1140 }
1141
1142 /*
1143 * Also select an indentation level depending on the
1144 * paragraph type (list paragraphs other than
1145 * para_DescribedThing need extra indent).
1146 *
1147 * (FIXME: Perhaps at some point we might even arrange
1148 * for the user to be able to request indented first
1149 * lines in paragraphs.)
1150 */
1151 if (ptype == para_Bullet ||
1152 ptype == para_NumberedList ||
1153 ptype == para_Description) {
1154 extra_indent = firstline_indent = conf->indent_list;
1155 } else {
1156 extra_indent = firstline_indent = 0;
1157 }
1158
1159 /*
1160 * Find the auxiliary text for this paragraph.
1161 */
1162 aux = aux2 = NULL;
1163 aux_indent = 0;
1164
1165 switch (ptype) {
1166 case para_Chapter:
1167 case para_Appendix:
1168 case para_Heading:
1169 case para_Subsect:
1170 /*
1171 * For some heading styles (FIXME: be able to
1172 * configure which), the auxiliary text contains
1173 * the chapter number and is arranged to be
1174 * right-aligned a few points left of the primary
1175 * margin. For other styles, the auxiliary text is
1176 * the full chapter _name_ and takes up space
1177 * within the (wrapped) chapter title, meaning that
1178 * we must move the first line indent over to make
1179 * space for it.
1180 */
1181 if (ptype == para_Heading || ptype == para_Subsect) {
1182 int len;
1183
1184 aux = pkwtext2;
1185 len = paper_width_simple(pdata, pkwtext2, conf);
1186 aux_indent = -len - conf->sect_num_left_space;
1187
1188 pdata->outline_title =
1189 prepare_outline_title(pkwtext2, L" ", pwords);
1190 } else {
1191 aux = pkwtext;
1192 aux2 = fake_word(L": ");
1193 aux_indent = 0;
1194
1195 firstline_indent += paper_width_simple(pdata, aux, conf);
1196 firstline_indent += paper_width_simple(pdata, aux2, conf);
1197
1198 pdata->outline_title =
1199 prepare_outline_title(pkwtext, L": ", pwords);
1200 }
1201 break;
1202
1203 case para_Bullet:
1204 /*
1205 * Auxiliary text consisting of a bullet.
1206 */
1207 aux = fake_word(conf->bullet);
1208 aux_indent = indent + conf->indent_list_bullet;
1209 break;
1210
1211 case para_NumberedList:
1212 /*
1213 * Auxiliary text consisting of the number followed
1214 * by a (FIXME: configurable) full stop.
1215 */
1216 aux = pkwtext;
1217 aux2 = fake_word(L".");
1218 aux_indent = indent + conf->indent_list_bullet;
1219 break;
1220
1221 case para_BiblioCited:
1222 /*
1223 * Auxiliary text consisting of the bibliography
1224 * reference text, and a trailing space.
1225 */
1226 aux = pkwtext;
1227 aux2 = fake_word(L" ");
1228 aux_indent = indent;
1229 firstline_indent += paper_width_simple(pdata, aux, conf);
1230 firstline_indent += paper_width_simple(pdata, aux2, conf);
1231 break;
1232 }
1233
1234 if (pdata->outline_level >= 0 && !pdata->outline_title) {
1235 pdata->outline_title =
1236 prepare_outline_title(NULL, NULL, pwords);
1237 }
1238
1239 wrap_paragraph(pdata, pwords, conf->base_width - rmargin,
1240 indent + firstline_indent,
1241 indent + extra_indent, conf);
1242
1243 pdata->first->aux_text = aux;
1244 pdata->first->aux_text_2 = aux2;
1245 pdata->first->aux_left_indent = aux_indent;
1246
1247 /*
1248 * Line breaking penalties.
1249 */
1250 switch (ptype) {
1251 case para_Chapter:
1252 case para_Appendix:
1253 case para_Heading:
1254 case para_Subsect:
1255 case para_UnnumberedChapter:
1256 /*
1257 * Fixed and large penalty for breaking straight
1258 * after a heading; corresponding bonus for
1259 * breaking straight before.
1260 */
1261 pdata->first->penalty_before = -500000;
1262 pdata->last->penalty_after = 500000;
1263 for (ldata = pdata->first; ldata; ldata = ldata->next)
1264 ldata->penalty_after = 500000;
1265 break;
1266
1267 case para_DescribedThing:
1268 /*
1269 * This is treated a bit like a small heading:
1270 * there's a penalty for breaking after it (i.e.
1271 * between it and its description), and a bonus for
1272 * breaking before it (actually _between_ list
1273 * items).
1274 */
1275 pdata->first->penalty_before = -200000;
1276 pdata->last->penalty_after = 200000;
1277 break;
1278
1279 default:
1280 /*
1281 * Most paragraph types: widow/orphan control by
1282 * discouraging breaking one line from the end of
1283 * any paragraph.
1284 */
1285 if (pdata->first != pdata->last) {
1286 pdata->first->penalty_after = 100000;
1287 pdata->last->penalty_before = 100000;
1288 }
1289 break;
1290 }
1291
1292 standard_line_spacing(pdata, conf);
1293
1294 /*
1295 * Some kinds of section heading require a page break before
1296 * them and an underline after.
1297 */
1298 if (ptype == para_Title ||
1299 ptype == para_Chapter ||
1300 ptype == para_Appendix ||
1301 ptype == para_UnnumberedChapter) {
1302 pdata->first->page_break = TRUE;
1303 pdata->first->space_before = conf->chapter_top_space;
1304 pdata->last->space_after +=
1305 (conf->chapter_underline_depth +
1306 conf->chapter_underline_thickness);
1307 pdata->rect_type = RECT_CHAPTER_UNDERLINE;
1308 }
1309
1310 return pdata;
1311 }
1312
1313 static void standard_line_spacing(para_data *pdata, paper_conf *conf)
1314 {
1315 line_data *ldata;
1316
1317 /*
1318 * Set the line spacing for each line in this paragraph.
1319 */
1320 for (ldata = pdata->first; ldata; ldata = ldata->next) {
1321 if (ldata == pdata->first)
1322 ldata->space_before = conf->base_para_spacing / 2;
1323 else
1324 ldata->space_before = conf->base_leading / 2;
1325 if (ldata == pdata->last)
1326 ldata->space_after = conf->base_para_spacing / 2;
1327 else
1328 ldata->space_after = conf->base_leading / 2;
1329 ldata->page_break = FALSE;
1330 }
1331 }
1332
1333 static font_encoding *new_font_encoding(font_data *font)
1334 {
1335 font_encoding *fe;
1336 int i;
1337
1338 fe = snew(font_encoding);
1339 fe->next = NULL;
1340
1341 if (font->list->tail)
1342 font->list->tail->next = fe;
1343 else
1344 font->list->head = fe;
1345 font->list->tail = fe;
1346
1347 fe->font = font;
1348 fe->free_pos = 0x21;
1349
1350 for (i = 0; i < 256; i++) {
1351 fe->vector[i] = NOGLYPH;
1352 fe->to_unicode[i] = 0xFFFF;
1353 }
1354
1355 return fe;
1356 }
1357
1358 static subfont_map_entry *encode_glyph_at(glyph g, wchar_t u,
1359 font_encoding *fe, int pos)
1360 {
1361 subfont_map_entry *sme = snew(subfont_map_entry);
1362
1363 sme->subfont = fe;
1364 sme->position = pos;
1365 fe->vector[pos] = g;
1366 fe->to_unicode[pos] = u;
1367 add234(fe->font->subfont_map, sme);
1368 return sme;
1369 }
1370
1371 static int new_sfmap_cmp(void *a, void *b)
1372 {
1373 glyph ga = *(glyph *)a;
1374 subfont_map_entry *sb = b;
1375 glyph gb = sb->subfont->vector[sb->position];
1376
1377 if (ga < gb) return -1;
1378 if (ga > gb) return 1;
1379 return 0;
1380 }
1381
1382 static subfont_map_entry *encode_glyph(glyph g, wchar_t u, font_data *font)
1383 {
1384 subfont_map_entry *sme;
1385 int c;
1386
1387 sme = find234(font->subfont_map, &g, new_sfmap_cmp);
1388 if (sme) return sme;
1389
1390 /*
1391 * This character is not yet in a subfont. Assign one.
1392 */
1393 if (font->latest_subfont->free_pos >= 0x100)
1394 font->latest_subfont = new_font_encoding(font);
1395
1396 c = font->latest_subfont->free_pos++;
1397 if (font->latest_subfont->free_pos == 0x7F)
1398 font->latest_subfont->free_pos = 0xA1;
1399
1400 return encode_glyph_at(g, u, font->latest_subfont, c);
1401 }
1402
1403 static int sfmap_cmp(void *a, void *b)
1404 {
1405 subfont_map_entry *sa = a, *sb = b;
1406 glyph ga = sa->subfont->vector[sa->position];
1407 glyph gb = sb->subfont->vector[sb->position];
1408
1409 if (ga < gb) return -1;
1410 if (ga > gb) return 1;
1411 return 0;
1412 }
1413
1414 int width_cmp(void *a, void *b)
1415 {
1416 glyph_width const *wa = a, *wb = b;
1417
1418 if (wa->glyph < wb->glyph)
1419 return -1;
1420 if (wa->glyph > wb->glyph)
1421 return 1;
1422 return 0;
1423 }
1424
1425 int kern_cmp(void *a, void *b)
1426 {
1427 kern_pair const *ka = a, *kb = b;
1428
1429 if (ka->left < kb->left)
1430 return -1;
1431 if (ka->left > kb->left)
1432 return 1;
1433 if (ka->right < kb->right)
1434 return -1;
1435 if (ka->right > kb->right)
1436 return 1;
1437 return 0;
1438 }
1439
1440 int lig_cmp(void *a, void *b)
1441 {
1442 ligature const *la = a, *lb = b;
1443
1444 if (la->left < lb->left)
1445 return -1;
1446 if (la->left > lb->left)
1447 return 1;
1448 if (la->right < lb->right)
1449 return -1;
1450 if (la->right > lb->right)
1451 return 1;
1452 return 0;
1453 }
1454
1455 static int utoglyph(font_info const *fi, wchar_t u) {
1456 return (u < 0 || u > 0xFFFF ? NOGLYPH : fi->bmp[u]);
1457 }
1458
1459 void listfonts(void) {
1460 font_info const *fi;
1461
1462 init_std_fonts();
1463 for (fi = all_fonts; fi; fi = fi->next)
1464 printf("%s\n", fi->name);
1465 }
1466
1467 static font_data *make_std_font(font_list *fontlist, char const *name)
1468 {
1469 font_info const *fi;
1470 font_data *f;
1471 font_encoding *fe;
1472 int i;
1473
1474 for (fe = fontlist->head; fe; fe = fe->next)
1475 if (strcmp(fe->font->info->name, name) == 0)
1476 return fe->font;
1477
1478 for (fi = all_fonts; fi; fi = fi->next)
1479 if (strcmp(fi->name, name) == 0) break;
1480 if (!fi) return NULL;
1481
1482 f = snew(font_data);
1483
1484 f->list = fontlist;
1485 f->info = fi;
1486 f->subfont_map = newtree234(sfmap_cmp);
1487
1488 /*
1489 * Our first subfont will contain all of US-ASCII. This isn't
1490 * really necessary - we could just create custom subfonts
1491 * precisely as the whim of render_string dictated - but
1492 * instinct suggests that it might be nice to have the text in
1493 * the output files look _marginally_ recognisable.
1494 */
1495 fe = new_font_encoding(f);
1496 fe->free_pos = 0xA1; /* only the top half is free */
1497 f->latest_subfont = fe;
1498
1499 for (i = 0x20; i <= 0x7E; i++) {
1500 glyph g = utoglyph(fi, i);
1501 if (g != NOGLYPH)
1502 encode_glyph_at(g, i, fe, i);
1503 }
1504
1505 return f;
1506 }
1507
1508 /* NB: arguments are glyph numbers from font->bmp. */
1509 int find_width(font_data *font, glyph index)
1510 {
1511 glyph_width wantw;
1512 glyph_width const *w;
1513
1514 wantw.glyph = index;
1515 w = find234(font->info->widths, &wantw, NULL);
1516 if (!w) return 0;
1517 return w->width;
1518 }
1519
1520 static int find_kern(font_data *font, int lindex, int rindex)
1521 {
1522 kern_pair wantkp;
1523 kern_pair const *kp;
1524
1525 if (lindex == NOGLYPH || rindex == NOGLYPH)
1526 return 0;
1527 wantkp.left = lindex;
1528 wantkp.right = rindex;
1529 kp = find234(font->info->kerns, &wantkp, NULL);
1530 if (kp == NULL)
1531 return 0;
1532 return kp->kern;
1533 }
1534
1535 static int find_lig(font_data *font, int lindex, int rindex)
1536 {
1537 ligature wantlig;
1538 ligature const *lig;
1539
1540 if (lindex == NOGLYPH || rindex == NOGLYPH)
1541 return NOGLYPH;
1542 wantlig.left = lindex;
1543 wantlig.right = rindex;
1544 lig = find234(font->info->ligs, &wantlig, NULL);
1545 if (lig == NULL)
1546 return NOGLYPH;
1547 return lig->lig;
1548 }
1549
1550 static int string_width(font_data *font, wchar_t const *string, int *errs,
1551 unsigned flags)
1552 {
1553 int width = 0;
1554 int nindex, index, oindex, lindex;
1555
1556 if (errs)
1557 *errs = 0;
1558
1559 oindex = NOGLYPH;
1560 index = utoglyph(font->info, *string);
1561 for (; *string; string++) {
1562 nindex = utoglyph(font->info, string[1]);
1563
1564 if (index == NOGLYPH) {
1565 if (errs)
1566 *errs = 1;
1567 } else {
1568 if (!(flags & RS_NOLIG) &&
1569 (lindex = find_lig(font, index, nindex)) != NOGLYPH) {
1570 index = lindex;
1571 continue;
1572 }
1573 width += find_kern(font, oindex, index) + find_width(font, index);
1574 }
1575 oindex = index;
1576 index = nindex;
1577 }
1578
1579 return width;
1580 }
1581
1582 static int paper_width_internal(void *vctx, word *word, int *nspaces);
1583
1584 struct paper_width_ctx {
1585 int minspacewidth;
1586 para_data *pdata;
1587 paper_conf *conf;
1588 };
1589
1590 static int paper_width_list(void *vctx, word *text, word *end, int *nspaces) {
1591 int w = 0;
1592 while (text && text != end) {
1593 w += paper_width_internal(vctx, text, nspaces);
1594 text = text->next;
1595 }
1596 return w;
1597 }
1598
1599 static int paper_width_internal(void *vctx, word *word, int *nspaces)
1600 {
1601 struct paper_width_ctx *ctx = (struct paper_width_ctx *)vctx;
1602 int style, type, findex, width, errs;
1603 wchar_t *str;
1604 unsigned flags = 0;
1605
1606 switch (word->type) {
1607 case word_HyperLink:
1608 case word_HyperEnd:
1609 case word_UpperXref:
1610 case word_LowerXref:
1611 case word_PageXref:
1612 case word_XrefEnd:
1613 case word_IndexRef:
1614 return 0;
1615 }
1616
1617 style = towordstyle(word->type);
1618 type = removeattr(word->type);
1619
1620 findex = (style == word_Normal ? FONT_NORMAL :
1621 style == word_Emph ? FONT_EMPH :
1622 FONT_CODE);
1623
1624 if (style == word_Code || style == word_WeakCode) flags |= RS_NOLIG;
1625
1626 if (type == word_Normal) {
1627 str = word->text;
1628 } else if (type == word_WhiteSpace) {
1629 if (findex != FONT_CODE) {
1630 if (nspaces)
1631 (*nspaces)++;
1632 return ctx->minspacewidth;
1633 } else
1634 str = L" ";
1635 } else /* if (type == word_Quote) */ {
1636 if (word->aux == quote_Open)
1637 str = ctx->conf->lquote;
1638 else
1639 str = ctx->conf->rquote;
1640 }
1641
1642 width = string_width(ctx->pdata->fonts[findex], str, &errs, flags);
1643
1644 if (errs && word->alt)
1645 return paper_width_list(vctx, word->alt, NULL, nspaces);
1646 else
1647 return ctx->pdata->sizes[findex] * width;
1648 }
1649
1650 static int paper_width(void *vctx, word *word)
1651 {
1652 return paper_width_internal(vctx, word, NULL);
1653 }
1654
1655 static int paper_width_simple(para_data *pdata, word *text, paper_conf *conf)
1656 {
1657 struct paper_width_ctx ctx;
1658
1659 ctx.pdata = pdata;
1660 ctx.minspacewidth =
1661 (pdata->sizes[FONT_NORMAL] *
1662 string_width(pdata->fonts[FONT_NORMAL], L" ", NULL, 0));
1663 ctx.conf = conf;
1664
1665 return paper_width_list(&ctx, text, NULL, NULL);
1666 }
1667
1668 static void wrap_paragraph(para_data *pdata, word *words,
1669 int w, int i1, int i2, paper_conf *conf)
1670 {
1671 wrappedline *wrapping, *p;
1672 int spacewidth;
1673 struct paper_width_ctx ctx;
1674 int line_height;
1675
1676 /*
1677 * We're going to need to store the line height in every line
1678 * structure we generate.
1679 */
1680 {
1681 int i;
1682 line_height = 0;
1683 for (i = 0; i < NFONTS; i++)
1684 if (line_height < pdata->sizes[i])
1685 line_height = pdata->sizes[i];
1686 line_height *= UNITS_PER_PT;
1687 }
1688
1689 spacewidth = (pdata->sizes[FONT_NORMAL] *
1690 string_width(pdata->fonts[FONT_NORMAL], L" ", NULL, 0));
1691 if (spacewidth == 0) {
1692 /*
1693 * A font without a space?! Disturbing. I hope this never
1694 * comes up, but I'll make a random guess anyway and set my
1695 * space width to half the point size.
1696 */
1697 spacewidth = pdata->sizes[FONT_NORMAL] * UNITS_PER_PT / 2;
1698 }
1699
1700 /*
1701 * I'm going to set the _minimum_ space width to 3/5 of the
1702 * standard one, and use the standard one as the optimum.
1703 */
1704 ctx.minspacewidth = spacewidth * 3 / 5;
1705 ctx.pdata = pdata;
1706 ctx.conf = conf;
1707
1708 wrapping = wrap_para(words, w - i1, w - i2, paper_width, &ctx, spacewidth);
1709
1710 /*
1711 * Having done the wrapping, we now concoct a set of line_data
1712 * structures.
1713 */
1714 pdata->first = pdata->last = NULL;
1715
1716 for (p = wrapping; p; p = p->next) {
1717 line_data *ldata;
1718 word *wd;
1719 int len, wid, spaces;
1720
1721 ldata = snew(line_data);
1722
1723 ldata->pdata = pdata;
1724 ldata->first = p->begin;
1725 ldata->end = p->end;
1726 ldata->line_height = line_height;
1727
1728 ldata->xpos = (p == wrapping ? i1 : i2);
1729
1730 if (pdata->last) {
1731 pdata->last->next = ldata;
1732 ldata->prev = pdata->last;
1733 } else {
1734 pdata->first = ldata;
1735 ldata->prev = NULL;
1736 }
1737 ldata->next = NULL;
1738 pdata->last = ldata;
1739
1740 spaces = 0;
1741 len = paper_width_list(&ctx, ldata->first, ldata->end, &spaces);
1742 wid = (p == wrapping ? w - i1 : w - i2);
1743 wd = ldata->first;
1744
1745 ldata->hshortfall = wid - len;
1746 ldata->nspaces = spaces;
1747 /*
1748 * This tells us how much the space width needs to
1749 * change from _min_spacewidth. But we want to store
1750 * its difference from the _natural_ space width, to
1751 * make the text rendering easier.
1752 */
1753 ldata->hshortfall += ctx.minspacewidth * spaces;
1754 ldata->hshortfall -= spacewidth * spaces;
1755 ldata->real_shortfall = ldata->hshortfall;
1756 /*
1757 * Special case: on the last line of a paragraph, we
1758 * never stretch spaces.
1759 */
1760 if (ldata->hshortfall > 0 && !p->next)
1761 ldata->hshortfall = 0;
1762
1763 ldata->aux_text = NULL;
1764 ldata->aux_text_2 = NULL;
1765 ldata->aux_left_indent = 0;
1766 ldata->penalty_before = ldata->penalty_after = 0;
1767 }
1768
1769 }
1770
1771 static page_data *page_breaks(line_data *first, line_data *last,
1772 int page_height, int ncols, int headspace)
1773 {
1774 line_data *l, *m;
1775 page_data *ph, *pt;
1776 int n, n1, this_height;
1777
1778 /*
1779 * Page breaking is done by a close analogue of the optimal
1780 * paragraph wrapping algorithm used by wrap_para(). We work
1781 * backwards from the end of the document line by line; for
1782 * each line, we contemplate every possible number of lines we
1783 * could put on a page starting with that line, determine a
1784 * cost function for each one, add it to the pre-computed cost
1785 * function for optimally page-breaking everything after that
1786 * page, and pick the best option.
1787 *
1788 * This is made slightly more complex by the fact that we have
1789 * a multi-column index with a heading at the top of the
1790 * _first_ page, meaning that the first _ncols_ pages must have
1791 * a different length. Hence, we must do the wrapping ncols+1
1792 * times over, hypothetically trying to put every subsequence
1793 * on every possible page.
1794 *
1795 * Since my line_data structures are only used for this
1796 * purpose, I might as well just store the algorithm data
1797 * directly in them.
1798 */
1799
1800 for (l = last; l; l = l->prev) {
1801 l->bestcost = snewn(ncols+1, int);
1802 l->vshortfall = snewn(ncols+1, int);
1803 l->text = snewn(ncols+1, int);
1804 l->space = snewn(ncols+1, int);
1805 l->page_last = snewn(ncols+1, line_data *);
1806
1807 for (n = 0; n <= ncols; n++) {
1808 int minheight, text = 0, space = 0;
1809 int cost;
1810
1811 n1 = (n < ncols ? n+1 : ncols);
1812 if (n < ncols)
1813 this_height = page_height - headspace;
1814 else
1815 this_height = page_height;
1816
1817 l->bestcost[n] = -1;
1818 for (m = l; m; m = m->next) {
1819 if (m != l && m->page_break)
1820 break; /* we've gone as far as we can */
1821
1822 if (m != l) {
1823 if (m->prev->space_after > 0)
1824 space += m->prev->space_after;
1825 else
1826 text += m->prev->space_after;
1827 }
1828 if (m != l || m->page_break) {
1829 if (m->space_before > 0)
1830 space += m->space_before;
1831 else
1832 text += m->space_before;
1833 }
1834 text += m->line_height;
1835 minheight = text + space;
1836
1837 if (m != l && minheight > this_height)
1838 break;
1839
1840 /*
1841 * If the space after this paragraph is _negative_
1842 * (which means the next line is folded on to this
1843 * one, which happens in the index), we absolutely
1844 * cannot break here.
1845 */
1846 if (m->space_after >= 0) {
1847
1848 /*
1849 * Compute the cost of this arrangement, as the
1850 * square of the amount of wasted space on the
1851 * page. Exception: if this is the last page
1852 * before a mandatory break or the document
1853 * end, we don't penalise a large blank area.
1854 */
1855 if (m != last && m->next && !m->next->page_break)
1856 {
1857 int x = (this_height - minheight) / FUNITS_PER_PT *
1858 4096.0;
1859 int xf;
1860
1861 xf = x & 0xFF;
1862 x >>= 8;
1863
1864 cost = x*x;
1865 cost += (x * xf) >> 8;
1866 } else
1867 cost = 0;
1868
1869 if (m != last && m->next && !m->next->page_break) {
1870 cost += m->penalty_after;
1871 cost += m->next->penalty_before;
1872 }
1873
1874 if (m != last && m->next && !m->next->page_break)
1875 cost += m->next->bestcost[n1];
1876 if (l->bestcost[n] == -1 || l->bestcost[n] > cost) {
1877 /*
1878 * This is the best option yet for this
1879 * starting point.
1880 */
1881 l->bestcost[n] = cost;
1882 if (m != last && m->next && !m->next->page_break)
1883 l->vshortfall[n] = this_height - minheight;
1884 else
1885 l->vshortfall[n] = 0;
1886 l->text[n] = text;
1887 l->space[n] = space;
1888 l->page_last[n] = m;
1889 }
1890 }
1891
1892 if (m == last)
1893 break;
1894 }
1895 }
1896 }
1897
1898 /*
1899 * Now go through the line list forwards and assemble the
1900 * actual pages.
1901 */
1902 ph = pt = NULL;
1903
1904 l = first;
1905 n = 0;
1906 while (l) {
1907 page_data *page;
1908 int text, space, head;
1909
1910 page = snew(page_data);
1911 page->next = NULL;
1912 page->prev = pt;
1913 if (pt)
1914 pt->next = page;
1915 else
1916 ph = page;
1917 pt = page;
1918
1919 page->first_line = l;
1920 page->last_line = l->page_last[n];
1921
1922 page->first_text = page->last_text = NULL;
1923 page->first_xref = page->last_xref = NULL;
1924 page->first_rect = page->last_rect = NULL;
1925
1926 /*
1927 * Now assign a y-coordinate to each line on the page.
1928 */
1929 text = space = 0;
1930 head = (n < ncols ? headspace : 0);
1931 for (l = page->first_line; l; l = l->next) {
1932 if (l != page->first_line) {
1933 if (l->prev->space_after > 0)
1934 space += l->prev->space_after;
1935 else
1936 text += l->prev->space_after;
1937 }
1938 if (l != page->first_line || l->page_break) {
1939 if (l->space_before > 0)
1940 space += l->space_before;
1941 else
1942 text += l->space_before;
1943 }
1944 text += l->line_height;
1945
1946 l->page = page;
1947 l->ypos = text + space + head;
1948 if (page->first_line->space[n]) {
1949 l->ypos += space * (float)page->first_line->vshortfall[n] /
1950 page->first_line->space[n];
1951 }
1952
1953 if (l == page->last_line)
1954 break;
1955 }
1956
1957 l = page->last_line;
1958 if (l == last)
1959 break;
1960 l = l->next;
1961
1962 n = (n < ncols ? n+1 : ncols);
1963 }
1964
1965 return ph;
1966 }
1967
1968 static void add_rect_to_page(page_data *page, int x, int y, int w, int h)
1969 {
1970 rect *r = snew(rect);
1971
1972 r->next = NULL;
1973 if (page->last_rect)
1974 page->last_rect->next = r;
1975 else
1976 page->first_rect = r;
1977 page->last_rect = r;
1978
1979 r->x = x;
1980 r->y = y;
1981 r->w = w;
1982 r->h = h;
1983 }
1984
1985 static void add_string_to_page(page_data *page, int x, int y,
1986 font_encoding *fe, int size, char *text,
1987 int width)
1988 {
1989 text_fragment *frag;
1990
1991 frag = snew(text_fragment);
1992 frag->next = NULL;
1993
1994 if (page->last_text)
1995 page->last_text->next = frag;
1996 else
1997 page->first_text = frag;
1998 page->last_text = frag;
1999
2000 frag->x = x;
2001 frag->y = y;
2002 frag->fe = fe;
2003 frag->fontsize = size;
2004 frag->text = dupstr(text);
2005 frag->width = width;
2006 }
2007
2008 /*
2009 * Returns the updated x coordinate.
2010 */
2011 static int render_string(page_data *page, font_data *font, int fontsize,
2012 int x, int y, wchar_t *str, unsigned flags)
2013 {
2014 char *text;
2015 int textpos, textwid, kern, nglyph, glyph, oglyph, lig;
2016 font_encoding *subfont = NULL, *sf;
2017 subfont_map_entry *sme;
2018
2019 text = snewn(1 + ustrlen(str), char);
2020 textpos = textwid = 0;
2021
2022 glyph = NOGLYPH;
2023 nglyph = utoglyph(font->info, *str);
2024 while (*str) {
2025 oglyph = glyph;
2026 glyph = nglyph;
2027 nglyph = utoglyph(font->info, str[1]);
2028
2029 if (glyph == NOGLYPH) {
2030 str++;
2031 continue; /* nothing more we can do here */
2032 }
2033
2034 if (!(flags & RS_NOLIG) &&
2035 (lig = find_lig(font, glyph, nglyph)) != NOGLYPH) {
2036 nglyph = lig;
2037 str++;
2038 continue;
2039 }
2040
2041 /*
2042 * Find which subfont this character is going in.
2043 */
2044 sme = encode_glyph(glyph, *str, font);
2045 sf = sme->subfont;
2046
2047 kern = find_kern(font, oglyph, glyph) * fontsize;
2048
2049 if (!subfont || sf != subfont || kern) {
2050 if (subfont) {
2051 text[textpos] = '\0';
2052 add_string_to_page(page, x, y, subfont, fontsize, text,
2053 textwid);
2054 x += textwid + kern;
2055 } else {
2056 assert(textpos == 0);
2057 }
2058 textpos = 0;
2059 textwid = 0;
2060 subfont = sf;
2061 }
2062
2063 text[textpos++] = sme->position;
2064 textwid += find_width(font, glyph) * fontsize;
2065
2066 str++;
2067 }
2068
2069 if (textpos > 0) {
2070 text[textpos] = '\0';
2071 add_string_to_page(page, x, y, subfont, fontsize, text, textwid);
2072 x += textwid;
2073 }
2074
2075 return x;
2076 }
2077
2078 /*
2079 * Returns the updated x coordinate.
2080 */
2081 static int render_text(page_data *page, para_data *pdata, line_data *ldata,
2082 int x, int y, word *text, word *text_end, xref **xr,
2083 int shortfall, int nspaces, int *nspace,
2084 keywordlist *keywords, indexdata *idx, paper_conf *conf)
2085 {
2086 while (text && text != text_end) {
2087 int style, type, findex, errs;
2088 wchar_t *str;
2089 xref_dest dest;
2090 unsigned flags = 0;
2091
2092 switch (text->type) {
2093 /*
2094 * Start a cross-reference.
2095 */
2096 case word_HyperLink:
2097 case word_UpperXref:
2098 case word_LowerXref:
2099 case word_PageXref:
2100
2101 if (text->type == word_HyperLink) {
2102 dest.type = URL;
2103 dest.url = utoa_dup(text->text, CS_ASCII);
2104 dest.page = NULL;
2105 } else if (text->type == word_PageXref) {
2106 dest.type = PAGE;
2107 dest.url = NULL;
2108 dest.page = (page_data *)text->private_data;
2109 } else {
2110 keyword *kwl = kw_lookup(keywords, text->text);
2111 para_data *pdata;
2112
2113 if (kwl) {
2114 assert(kwl->para->private_data);
2115 pdata = (para_data *) kwl->para->private_data;
2116 dest.type = PAGE;
2117 dest.page = pdata->first->page;
2118 dest.url = NULL;
2119 } else {
2120 /*
2121 * Shouldn't happen, but *shrug*
2122 */
2123 dest.type = NONE;
2124 dest.page = NULL;
2125 dest.url = NULL;
2126 }
2127 }
2128 if (dest.type != NONE) {
2129 *xr = snew(xref);
2130 (*xr)->dest = dest; /* structure copy */
2131 if (page->last_xref)
2132 page->last_xref->next = *xr;
2133 else
2134 page->first_xref = *xr;
2135 page->last_xref = *xr;
2136 (*xr)->next = NULL;
2137
2138 /*
2139 * FIXME: Ideally we should have, and use, some
2140 * vertical font metric information here so that
2141 * our cross-ref rectangle can take account of
2142 * descenders and the font's cap height. This will
2143 * do for the moment, but it isn't ideal.
2144 */
2145 (*xr)->lx = (*xr)->rx = x;
2146 (*xr)->by = y;
2147 (*xr)->ty = y + ldata->line_height;
2148 }
2149 goto nextword;
2150
2151 /*
2152 * Finish extending a cross-reference box.
2153 */
2154 case word_HyperEnd:
2155 case word_XrefEnd:
2156 *xr = NULL;
2157 goto nextword;
2158
2159 /*
2160 * Add the current page number to the list of pages
2161 * referenced by an index entry.
2162 */
2163 case word_IndexRef:
2164 /*
2165 * We don't create index references in contents entries.
2166 */
2167 if (!pdata->contents_entry) {
2168 indextag *tag;
2169 int i;
2170
2171 tag = index_findtag(idx, text->text);
2172 if (!tag)
2173 goto nextword;
2174
2175 for (i = 0; i < tag->nrefs; i++) {
2176 indexentry *entry = tag->refs[i];
2177 paper_idx *pi = (paper_idx *)entry->backend_data;
2178
2179 /*
2180 * If the same index term is indexed twice
2181 * within the same section, we only want to
2182 * mention it once in the index.
2183 */
2184 if (pi->lastpage != page) {
2185 word **wp;
2186
2187 if (pi->lastword) {
2188 pi->lastword = pi->lastword->next =
2189 fake_word(L",");
2190 pi->lastword = pi->lastword->next =
2191 fake_space_word();
2192 wp = &pi->lastword->next;
2193 } else
2194 wp = &pi->words;
2195
2196 pi->lastword = *wp =
2197 fake_page_ref(page);
2198 pi->lastword = pi->lastword->next =
2199 fake_word(page->number);
2200 pi->lastword = pi->lastword->next =
2201 fake_end_ref();
2202 }
2203
2204 pi->lastpage = page;
2205 }
2206 }
2207 goto nextword;
2208 }
2209
2210 style = towordstyle(text->type);
2211 type = removeattr(text->type);
2212
2213 findex = (style == word_Normal ? FONT_NORMAL :
2214 style == word_Emph ? FONT_EMPH :
2215 FONT_CODE);
2216
2217 if (style == word_Code || style == word_WeakCode) flags |= RS_NOLIG;
2218 flags |= pdata->extraflags;
2219
2220 if (type == word_Normal) {
2221 str = text->text;
2222 } else if (type == word_WhiteSpace) {
2223 x += pdata->sizes[findex] *
2224 string_width(pdata->fonts[findex], L" ", NULL, 0);
2225 if (nspaces && findex != FONT_CODE) {
2226 x += (*nspace+1) * shortfall / nspaces;
2227 x -= *nspace * shortfall / nspaces;
2228 (*nspace)++;
2229 }
2230 goto nextword;
2231 } else /* if (type == word_Quote) */ {
2232 if (text->aux == quote_Open)
2233 str = conf->lquote;
2234 else
2235 str = conf->rquote;
2236 }
2237
2238 (void) string_width(pdata->fonts[findex], str, &errs, flags);
2239
2240 if (errs && text->alt)
2241 x = render_text(page, pdata, ldata, x, y, text->alt, NULL,
2242 xr, shortfall, nspaces, nspace, keywords, idx,
2243 conf);
2244 else
2245 x = render_string(page, pdata->fonts[findex],
2246 pdata->sizes[findex], x, y, str, flags);
2247
2248 if (*xr)
2249 (*xr)->rx = x;
2250
2251 nextword:
2252 text = text->next;
2253 }
2254
2255 return x;
2256 }
2257
2258 /*
2259 * Returns the last x position used on the line.
2260 */
2261 static int render_line(line_data *ldata, int left_x, int top_y,
2262 xref_dest *dest, keywordlist *keywords, indexdata *idx,
2263 paper_conf *conf)
2264 {
2265 int nspace;
2266 xref *xr;
2267 int ret = 0;
2268
2269 if (ldata->aux_text) {
2270 int x;
2271 xr = NULL;
2272 nspace = 0;
2273 x = render_text(ldata->page, ldata->pdata, ldata,
2274 left_x + ldata->aux_left_indent,
2275 top_y - ldata->ypos,
2276 ldata->aux_text, NULL, &xr, 0, 0, &nspace,
2277 keywords, idx, conf);
2278 if (ldata->aux_text_2)
2279 render_text(ldata->page, ldata->pdata, ldata,
2280 x, top_y - ldata->ypos,
2281 ldata->aux_text_2, NULL, &xr, 0, 0, &nspace,
2282 keywords, idx, conf);
2283 }
2284 nspace = 0;
2285
2286 if (ldata->first) {
2287 /*
2288 * There might be a cross-reference carried over from a
2289 * previous line.
2290 */
2291 if (dest->type != NONE) {
2292 xr = snew(xref);
2293 xr->next = NULL;
2294 xr->dest = *dest; /* structure copy */
2295 if (ldata->page->last_xref)
2296 ldata->page->last_xref->next = xr;
2297 else
2298 ldata->page->first_xref = xr;
2299 ldata->page->last_xref = xr;
2300 xr->lx = xr->rx = left_x + ldata->xpos;
2301 xr->by = top_y - ldata->ypos;
2302 xr->ty = top_y - ldata->ypos + ldata->line_height;
2303 } else
2304 xr = NULL;
2305
2306 {
2307 int extra_indent, shortfall, spaces;
2308 int just = ldata->pdata->justification;
2309
2310 /*
2311 * All forms of justification become JUST when we have
2312 * to squeeze the paragraph.
2313 */
2314 if (ldata->hshortfall < 0)
2315 just = JUST;
2316
2317 switch (just) {
2318 case JUST:
2319 shortfall = ldata->hshortfall;
2320 spaces = ldata->nspaces;
2321 extra_indent = 0;
2322 break;
2323 case LEFT:
2324 shortfall = spaces = extra_indent = 0;
2325 break;
2326 case RIGHT:
2327 shortfall = spaces = 0;
2328 extra_indent = ldata->real_shortfall;
2329 break;
2330 }
2331
2332 ret = render_text(ldata->page, ldata->pdata, ldata,
2333 left_x + ldata->xpos + extra_indent,
2334 top_y - ldata->ypos, ldata->first, ldata->end,
2335 &xr, shortfall, spaces, &nspace,
2336 keywords, idx, conf);
2337 }
2338
2339 if (xr) {
2340 /*
2341 * There's a cross-reference continued on to the next line.
2342 */
2343 *dest = xr->dest;
2344 } else
2345 dest->type = NONE;
2346 }
2347
2348 return ret;
2349 }
2350
2351 static void render_para(para_data *pdata, paper_conf *conf,
2352 keywordlist *keywords, indexdata *idx,
2353 paragraph *index_placeholder, page_data *index_page)
2354 {
2355 int last_x;
2356 xref *cxref;
2357 page_data *cxref_page;
2358 xref_dest dest;
2359 para_data *target;
2360 line_data *ldata;
2361
2362 dest.type = NONE;
2363 cxref = NULL;
2364 cxref_page = NULL;
2365
2366 for (ldata = pdata->first; ldata; ldata = ldata->next) {
2367 /*
2368 * If this is a contents entry, we expect to have a single
2369 * enormous cross-reference rectangle covering the whole
2370 * thing. (Unless, of course, it spans multiple pages.)
2371 */
2372 if (pdata->contents_entry && ldata->page != cxref_page) {
2373 cxref_page = ldata->page;
2374 cxref = snew(xref);
2375 cxref->next = NULL;
2376 cxref->dest.type = PAGE;
2377 if (pdata->contents_entry == index_placeholder) {
2378 cxref->dest.page = index_page;
2379 } else {
2380 assert(pdata->contents_entry->private_data);
2381 target = (para_data *)pdata->contents_entry->private_data;
2382 cxref->dest.page = target->first->page;
2383 }
2384 cxref->dest.url = NULL;
2385 if (ldata->page->last_xref)
2386 ldata->page->last_xref->next = cxref;
2387 else
2388 ldata->page->first_xref = cxref;
2389 ldata->page->last_xref = cxref;
2390 cxref->lx = conf->left_margin;
2391 cxref->rx = conf->paper_width - conf->right_margin;
2392 cxref->ty = conf->paper_height - conf->top_margin
2393 - ldata->ypos + ldata->line_height;
2394 }
2395 if (pdata->contents_entry) {
2396 assert(cxref != NULL);
2397 cxref->by = conf->paper_height - conf->top_margin
2398 - ldata->ypos;
2399 }
2400
2401 last_x = render_line(ldata, conf->left_margin,
2402 conf->paper_height - conf->top_margin,
2403 &dest, keywords, idx, conf);
2404 if (ldata == pdata->last)
2405 break;
2406 }
2407
2408 /*
2409 * If this is a contents entry, add leaders and a page
2410 * number.
2411 */
2412 if (pdata->contents_entry) {
2413 word *w;
2414 wchar_t *num;
2415 int wid;
2416 int x;
2417
2418 if (pdata->contents_entry == index_placeholder) {
2419 num = index_page->number;
2420 } else {
2421 assert(pdata->contents_entry->private_data);
2422 target = (para_data *)pdata->contents_entry->private_data;
2423 num = target->first->page->number;
2424 }
2425
2426 w = fake_word(num);
2427 wid = paper_width_simple(pdata, w, conf);
2428 sfree(w);
2429
2430 for (x = 0; x < conf->base_width; x += conf->leader_separation)
2431 if (x - conf->leader_separation > last_x - conf->left_margin &&
2432 x + conf->leader_separation < conf->base_width - wid)
2433 render_string(pdata->last->page,
2434 pdata->fonts[FONT_NORMAL],
2435 pdata->sizes[FONT_NORMAL],
2436 conf->left_margin + x,
2437 (conf->paper_height - conf->top_margin -
2438 pdata->last->ypos), L".", 0);
2439
2440 render_string(pdata->last->page,
2441 pdata->fonts[FONT_NORMAL],
2442 pdata->sizes[FONT_NORMAL],
2443 conf->paper_width - conf->right_margin - wid,
2444 (conf->paper_height - conf->top_margin -
2445 pdata->last->ypos), num, 0);
2446 }
2447
2448 /*
2449 * Render any rectangle (chapter title underline or rule)
2450 * that goes with this paragraph.
2451 */
2452 switch (pdata->rect_type) {
2453 case RECT_CHAPTER_UNDERLINE:
2454 add_rect_to_page(pdata->last->page,
2455 conf->left_margin,
2456 (conf->paper_height - conf->top_margin -
2457 pdata->last->ypos -
2458 conf->chapter_underline_depth),
2459 conf->base_width,
2460 conf->chapter_underline_thickness);
2461 break;
2462 case RECT_RULE:
2463 add_rect_to_page(pdata->first->page,
2464 conf->left_margin + pdata->first->xpos,
2465 (conf->paper_height - conf->top_margin -
2466 pdata->last->ypos -
2467 pdata->last->line_height),
2468 conf->base_width - pdata->first->xpos,
2469 pdata->last->line_height);
2470 break;
2471 default: /* placate gcc */
2472 break;
2473 }
2474 }
2475
2476 static para_data *code_paragraph(int indent, word *words, paper_conf *conf)
2477 {
2478 para_data *pdata = snew(para_data);
2479
2480 /*
2481 * For code paragraphs, I'm going to hack grievously and
2482 * pretend the three normal fonts are the three code paragraph
2483 * fonts.
2484 */
2485 setfont(pdata, &conf->fcode);
2486
2487 pdata->first = pdata->last = NULL;
2488 pdata->outline_level = -1;
2489 pdata->rect_type = RECT_NONE;
2490 pdata->contents_entry = NULL;
2491 pdata->justification = LEFT;
2492 pdata->extraflags = RS_NOLIG;
2493
2494 for (; words; words = words->next) {
2495 wchar_t *t, *e, *start;
2496 word *lhead = NULL, *ltail = NULL, *w;
2497 line_data *ldata;
2498 int prev = -1, curr;
2499
2500 t = words->text;
2501 if (words->next && words->next->type == word_Emph) {
2502 e = words->next->text;
2503 words = words->next;
2504 } else
2505 e = NULL;
2506
2507 start = t;
2508
2509 while (*start) {
2510 while (*t) {
2511 if (!e || !*e)
2512 curr = 0;
2513 else if (*e == L'i')
2514 curr = 1;
2515 else if (*e == L'b')
2516 curr = 2;
2517 else
2518 curr = 0;
2519
2520 if (prev < 0)
2521 prev = curr;
2522
2523 if (curr != prev)
2524 break;
2525
2526 t++;
2527 if (e && *e)
2528 e++;
2529 }
2530
2531 /*
2532 * We've isolated a maximal subsequence of the line
2533 * which has the same emphasis. Form it into a word
2534 * structure.
2535 */
2536 w = snew(word);
2537 w->next = NULL;
2538 w->alt = NULL;
2539 w->type = (prev == 0 ? word_WeakCode :
2540 prev == 1 ? word_Emph : word_Normal);
2541 w->text = snewn(t-start+1, wchar_t);
2542 memcpy(w->text, start, (t-start) * sizeof(wchar_t));
2543 w->text[t-start] = '\0';
2544 w->breaks = FALSE;
2545
2546 if (ltail)
2547 ltail->next = w;
2548 else
2549 lhead = w;
2550 ltail = w;
2551
2552 start = t;
2553 prev = -1;
2554 }
2555
2556 ldata = snew(line_data);
2557
2558 ldata->pdata = pdata;
2559 ldata->first = lhead;
2560 ldata->end = NULL;
2561 ldata->line_height = conf->fcode.font_size * UNITS_PER_PT;
2562
2563 ldata->xpos = indent;
2564
2565 if (pdata->last) {
2566 pdata->last->next = ldata;
2567 ldata->prev = pdata->last;
2568 } else {
2569 pdata->first = ldata;
2570 ldata->prev = NULL;
2571 }
2572 ldata->next = NULL;
2573 pdata->last = ldata;
2574
2575 ldata->hshortfall = 0;
2576 ldata->nspaces = 0;
2577 ldata->aux_text = NULL;
2578 ldata->aux_text_2 = NULL;
2579 ldata->aux_left_indent = 0;
2580 /* General opprobrium for breaking in a code paragraph. */
2581 ldata->penalty_before = ldata->penalty_after = 50000;
2582 }
2583
2584 standard_line_spacing(pdata, conf);
2585
2586 return pdata;
2587 }
2588
2589 static para_data *rule_paragraph(int indent, paper_conf *conf)
2590 {
2591 para_data *pdata = snew(para_data);
2592 line_data *ldata;
2593
2594 ldata = snew(line_data);
2595
2596 ldata->pdata = pdata;
2597 ldata->first = NULL;
2598 ldata->end = NULL;
2599 ldata->line_height = conf->rule_thickness;
2600
2601 ldata->xpos = indent;
2602
2603 ldata->prev = NULL;
2604 ldata->next = NULL;
2605
2606 ldata->hshortfall = 0;
2607 ldata->nspaces = 0;
2608 ldata->aux_text = NULL;
2609 ldata->aux_text_2 = NULL;
2610 ldata->aux_left_indent = 0;
2611
2612 /*
2613 * Better to break after a rule than before it
2614 */
2615 ldata->penalty_after += 100000;
2616 ldata->penalty_before += -100000;
2617
2618 pdata->first = pdata->last = ldata;
2619 pdata->outline_level = -1;
2620 pdata->rect_type = RECT_RULE;
2621 pdata->contents_entry = NULL;
2622 pdata->justification = LEFT;
2623 pdata->extraflags = 0;
2624
2625 standard_line_spacing(pdata, conf);
2626
2627 return pdata;
2628 }
2629
2630 /*
2631 * Plain-text-like formatting for outline titles.
2632 */
2633 static void paper_rdaddw(rdstring *rs, word *text) {
2634 for (; text; text = text->next) switch (text->type) {
2635 case word_HyperLink:
2636 case word_HyperEnd:
2637 case word_UpperXref:
2638 case word_LowerXref:
2639 case word_XrefEnd:
2640 case word_IndexRef:
2641 break;
2642
2643 case word_Normal:
2644 case word_Emph:
2645 case word_Code:
2646 case word_WeakCode:
2647 case word_WhiteSpace:
2648 case word_EmphSpace:
2649 case word_CodeSpace:
2650 case word_WkCodeSpace:
2651 case word_Quote:
2652 case word_EmphQuote:
2653 case word_CodeQuote:
2654 case word_WkCodeQuote:
2655 assert(text->type != word_CodeQuote &&
2656 text->type != word_WkCodeQuote);
2657 if (towordstyle(text->type) == word_Emph &&
2658 (attraux(text->aux) == attr_First ||
2659 attraux(text->aux) == attr_Only))
2660 rdadd(rs, L'_'); /* FIXME: configurability */
2661 else if (towordstyle(text->type) == word_Code &&
2662 (attraux(text->aux) == attr_First ||
2663 attraux(text->aux) == attr_Only))
2664 rdadd(rs, L'\''); /* FIXME: configurability */
2665 if (removeattr(text->type) == word_Normal) {
2666 rdadds(rs, text->text);
2667 } else if (removeattr(text->type) == word_WhiteSpace) {
2668 rdadd(rs, L' ');
2669 } else if (removeattr(text->type) == word_Quote) {
2670 rdadd(rs, L'\''); /* fixme: configurability */
2671 }
2672 if (towordstyle(text->type) == word_Emph &&
2673 (attraux(text->aux) == attr_Last ||
2674 attraux(text->aux) == attr_Only))
2675 rdadd(rs, L'_'); /* FIXME: configurability */
2676 else if (towordstyle(text->type) == word_Code &&
2677 (attraux(text->aux) == attr_Last ||
2678 attraux(text->aux) == attr_Only))
2679 rdadd(rs, L'\''); /* FIXME: configurability */
2680 break;
2681 }
2682 }
2683
2684 static wchar_t *prepare_outline_title(word *first, wchar_t *separator,
2685 word *second)
2686 {
2687 rdstring rs = {0, 0, NULL};
2688
2689 if (first)
2690 paper_rdaddw(&rs, first);
2691 if (separator)
2692 rdadds(&rs, separator);
2693 if (second)
2694 paper_rdaddw(&rs, second);
2695
2696 return rs.text;
2697 }
2698
2699 static word *fake_word(wchar_t *text)
2700 {
2701 word *ret = snew(word);
2702 ret->next = NULL;
2703 ret->alt = NULL;
2704 ret->type = word_Normal;
2705 ret->text = ustrdup(text);
2706 ret->breaks = FALSE;
2707 ret->aux = 0;
2708 return ret;
2709 }
2710
2711 static word *fake_space_word(void)
2712 {
2713 word *ret = snew(word);
2714 ret->next = NULL;
2715 ret->alt = NULL;
2716 ret->type = word_WhiteSpace;
2717 ret->text = NULL;
2718 ret->breaks = TRUE;
2719 ret->aux = 0;
2720 return ret;
2721 }
2722
2723 static word *fake_page_ref(page_data *page)
2724 {
2725 word *ret = snew(word);
2726 ret->next = NULL;
2727 ret->alt = NULL;
2728 ret->type = word_PageXref;
2729 ret->text = NULL;
2730 ret->breaks = FALSE;
2731 ret->aux = 0;
2732 ret->private_data = page;
2733 return ret;
2734 }
2735
2736 static word *fake_end_ref(void)
2737 {
2738 word *ret = snew(word);
2739 ret->next = NULL;
2740 ret->alt = NULL;
2741 ret->type = word_XrefEnd;
2742 ret->text = NULL;
2743 ret->breaks = FALSE;
2744 ret->aux = 0;
2745 return ret;
2746 }
2747
2748 static word *prepare_contents_title(word *first, wchar_t *separator,
2749 word *second)
2750 {
2751 word *ret;
2752 word **wptr, *w;
2753
2754 wptr = &ret;
2755
2756 if (first) {
2757 w = dup_word_list(first);
2758 *wptr = w;
2759 while (w->next)
2760 w = w->next;
2761 wptr = &w->next;
2762 }
2763
2764 if (separator) {
2765 w = fake_word(separator);
2766 *wptr = w;
2767 wptr = &w->next;
2768 }
2769
2770 if (second) {
2771 *wptr = dup_word_list(second);
2772 }
2773
2774 return ret;
2775 }
2776
2777 static void fold_into_page(page_data *dest, page_data *src, int right_shift)
2778 {
2779 line_data *ldata;
2780
2781 if (!src->first_line)
2782 return;
2783
2784 if (dest->last_line) {
2785 dest->last_line->next = src->first_line;
2786 src->first_line->prev = dest->last_line;
2787 }
2788 dest->last_line = src->last_line;
2789
2790 for (ldata = src->first_line; ldata; ldata = ldata->next) {
2791 ldata->page = dest;
2792 ldata->xpos += right_shift;
2793
2794 if (ldata == src->last_line)
2795 break;
2796 }
2797 }