Tweak to Tye 42 font generation -- construct the CharStrings dictionary
[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 static font_data *make_std_font(font_list *fontlist, char const *name)
1460 {
1461 font_info const *fi;
1462 font_data *f;
1463 font_encoding *fe;
1464 int i;
1465
1466 for (fe = fontlist->head; fe; fe = fe->next)
1467 if (strcmp(fe->font->info->name, name) == 0)
1468 return fe->font;
1469
1470 for (fi = all_fonts; fi; fi = fi->next)
1471 if (strcmp(fi->name, name) == 0) break;
1472 if (!fi) return NULL;
1473
1474 f = snew(font_data);
1475
1476 f->list = fontlist;
1477 f->info = fi;
1478 f->subfont_map = newtree234(sfmap_cmp);
1479
1480 /*
1481 * Our first subfont will contain all of US-ASCII. This isn't
1482 * really necessary - we could just create custom subfonts
1483 * precisely as the whim of render_string dictated - but
1484 * instinct suggests that it might be nice to have the text in
1485 * the output files look _marginally_ recognisable.
1486 */
1487 fe = new_font_encoding(f);
1488 fe->free_pos = 0xA1; /* only the top half is free */
1489 f->latest_subfont = fe;
1490
1491 for (i = 0x20; i <= 0x7E; i++) {
1492 glyph g = utoglyph(fi, i);
1493 if (g != NOGLYPH)
1494 encode_glyph_at(g, i, fe, i);
1495 }
1496
1497 return f;
1498 }
1499
1500 /* NB: arguments are glyph numbers from font->bmp. */
1501 int find_width(font_data *font, glyph index)
1502 {
1503 glyph_width wantw;
1504 glyph_width const *w;
1505
1506 wantw.glyph = index;
1507 w = find234(font->info->widths, &wantw, NULL);
1508 if (!w) return 0;
1509 return w->width;
1510 }
1511
1512 static int find_kern(font_data *font, int lindex, int rindex)
1513 {
1514 kern_pair wantkp;
1515 kern_pair const *kp;
1516
1517 if (lindex == NOGLYPH || rindex == NOGLYPH)
1518 return 0;
1519 wantkp.left = lindex;
1520 wantkp.right = rindex;
1521 kp = find234(font->info->kerns, &wantkp, NULL);
1522 if (kp == NULL)
1523 return 0;
1524 return kp->kern;
1525 }
1526
1527 static int find_lig(font_data *font, int lindex, int rindex)
1528 {
1529 ligature wantlig;
1530 ligature const *lig;
1531
1532 if (lindex == NOGLYPH || rindex == NOGLYPH)
1533 return NOGLYPH;
1534 wantlig.left = lindex;
1535 wantlig.right = rindex;
1536 lig = find234(font->info->ligs, &wantlig, NULL);
1537 if (lig == NULL)
1538 return NOGLYPH;
1539 return lig->lig;
1540 }
1541
1542 static int string_width(font_data *font, wchar_t const *string, int *errs,
1543 unsigned flags)
1544 {
1545 int width = 0;
1546 int nindex, index, oindex, lindex;
1547
1548 if (errs)
1549 *errs = 0;
1550
1551 oindex = NOGLYPH;
1552 index = utoglyph(font->info, *string);
1553 for (; *string; string++) {
1554 nindex = utoglyph(font->info, string[1]);
1555
1556 if (index == NOGLYPH) {
1557 if (errs)
1558 *errs = 1;
1559 } else {
1560 if (!(flags & RS_NOLIG) &&
1561 (lindex = find_lig(font, index, nindex)) != NOGLYPH) {
1562 index = lindex;
1563 continue;
1564 }
1565 width += find_kern(font, oindex, index) + find_width(font, index);
1566 }
1567 oindex = index;
1568 index = nindex;
1569 }
1570
1571 return width;
1572 }
1573
1574 static int paper_width_internal(void *vctx, word *word, int *nspaces);
1575
1576 struct paper_width_ctx {
1577 int minspacewidth;
1578 para_data *pdata;
1579 paper_conf *conf;
1580 };
1581
1582 static int paper_width_list(void *vctx, word *text, word *end, int *nspaces) {
1583 int w = 0;
1584 while (text && text != end) {
1585 w += paper_width_internal(vctx, text, nspaces);
1586 text = text->next;
1587 }
1588 return w;
1589 }
1590
1591 static int paper_width_internal(void *vctx, word *word, int *nspaces)
1592 {
1593 struct paper_width_ctx *ctx = (struct paper_width_ctx *)vctx;
1594 int style, type, findex, width, errs;
1595 wchar_t *str;
1596 unsigned flags = 0;
1597
1598 switch (word->type) {
1599 case word_HyperLink:
1600 case word_HyperEnd:
1601 case word_UpperXref:
1602 case word_LowerXref:
1603 case word_PageXref:
1604 case word_XrefEnd:
1605 case word_IndexRef:
1606 return 0;
1607 }
1608
1609 style = towordstyle(word->type);
1610 type = removeattr(word->type);
1611
1612 findex = (style == word_Normal ? FONT_NORMAL :
1613 style == word_Emph ? FONT_EMPH :
1614 FONT_CODE);
1615
1616 if (style == word_Code || style == word_WeakCode) flags |= RS_NOLIG;
1617
1618 if (type == word_Normal) {
1619 str = word->text;
1620 } else if (type == word_WhiteSpace) {
1621 if (findex != FONT_CODE) {
1622 if (nspaces)
1623 (*nspaces)++;
1624 return ctx->minspacewidth;
1625 } else
1626 str = L" ";
1627 } else /* if (type == word_Quote) */ {
1628 if (word->aux == quote_Open)
1629 str = ctx->conf->lquote;
1630 else
1631 str = ctx->conf->rquote;
1632 }
1633
1634 width = string_width(ctx->pdata->fonts[findex], str, &errs, flags);
1635
1636 if (errs && word->alt)
1637 return paper_width_list(vctx, word->alt, NULL, nspaces);
1638 else
1639 return ctx->pdata->sizes[findex] * width;
1640 }
1641
1642 static int paper_width(void *vctx, word *word)
1643 {
1644 return paper_width_internal(vctx, word, NULL);
1645 }
1646
1647 static int paper_width_simple(para_data *pdata, word *text, paper_conf *conf)
1648 {
1649 struct paper_width_ctx ctx;
1650
1651 ctx.pdata = pdata;
1652 ctx.minspacewidth =
1653 (pdata->sizes[FONT_NORMAL] *
1654 string_width(pdata->fonts[FONT_NORMAL], L" ", NULL, 0));
1655 ctx.conf = conf;
1656
1657 return paper_width_list(&ctx, text, NULL, NULL);
1658 }
1659
1660 static void wrap_paragraph(para_data *pdata, word *words,
1661 int w, int i1, int i2, paper_conf *conf)
1662 {
1663 wrappedline *wrapping, *p;
1664 int spacewidth;
1665 struct paper_width_ctx ctx;
1666 int line_height;
1667
1668 /*
1669 * We're going to need to store the line height in every line
1670 * structure we generate.
1671 */
1672 {
1673 int i;
1674 line_height = 0;
1675 for (i = 0; i < NFONTS; i++)
1676 if (line_height < pdata->sizes[i])
1677 line_height = pdata->sizes[i];
1678 line_height *= UNITS_PER_PT;
1679 }
1680
1681 spacewidth = (pdata->sizes[FONT_NORMAL] *
1682 string_width(pdata->fonts[FONT_NORMAL], L" ", NULL, 0));
1683 if (spacewidth == 0) {
1684 /*
1685 * A font without a space?! Disturbing. I hope this never
1686 * comes up, but I'll make a random guess anyway and set my
1687 * space width to half the point size.
1688 */
1689 spacewidth = pdata->sizes[FONT_NORMAL] * UNITS_PER_PT / 2;
1690 }
1691
1692 /*
1693 * I'm going to set the _minimum_ space width to 3/5 of the
1694 * standard one, and use the standard one as the optimum.
1695 */
1696 ctx.minspacewidth = spacewidth * 3 / 5;
1697 ctx.pdata = pdata;
1698 ctx.conf = conf;
1699
1700 wrapping = wrap_para(words, w - i1, w - i2, paper_width, &ctx, spacewidth);
1701
1702 /*
1703 * Having done the wrapping, we now concoct a set of line_data
1704 * structures.
1705 */
1706 pdata->first = pdata->last = NULL;
1707
1708 for (p = wrapping; p; p = p->next) {
1709 line_data *ldata;
1710 word *wd;
1711 int len, wid, spaces;
1712
1713 ldata = snew(line_data);
1714
1715 ldata->pdata = pdata;
1716 ldata->first = p->begin;
1717 ldata->end = p->end;
1718 ldata->line_height = line_height;
1719
1720 ldata->xpos = (p == wrapping ? i1 : i2);
1721
1722 if (pdata->last) {
1723 pdata->last->next = ldata;
1724 ldata->prev = pdata->last;
1725 } else {
1726 pdata->first = ldata;
1727 ldata->prev = NULL;
1728 }
1729 ldata->next = NULL;
1730 pdata->last = ldata;
1731
1732 spaces = 0;
1733 len = paper_width_list(&ctx, ldata->first, ldata->end, &spaces);
1734 wid = (p == wrapping ? w - i1 : w - i2);
1735 wd = ldata->first;
1736
1737 ldata->hshortfall = wid - len;
1738 ldata->nspaces = spaces;
1739 /*
1740 * This tells us how much the space width needs to
1741 * change from _min_spacewidth. But we want to store
1742 * its difference from the _natural_ space width, to
1743 * make the text rendering easier.
1744 */
1745 ldata->hshortfall += ctx.minspacewidth * spaces;
1746 ldata->hshortfall -= spacewidth * spaces;
1747 ldata->real_shortfall = ldata->hshortfall;
1748 /*
1749 * Special case: on the last line of a paragraph, we
1750 * never stretch spaces.
1751 */
1752 if (ldata->hshortfall > 0 && !p->next)
1753 ldata->hshortfall = 0;
1754
1755 ldata->aux_text = NULL;
1756 ldata->aux_text_2 = NULL;
1757 ldata->aux_left_indent = 0;
1758 ldata->penalty_before = ldata->penalty_after = 0;
1759 }
1760
1761 }
1762
1763 static page_data *page_breaks(line_data *first, line_data *last,
1764 int page_height, int ncols, int headspace)
1765 {
1766 line_data *l, *m;
1767 page_data *ph, *pt;
1768 int n, n1, this_height;
1769
1770 /*
1771 * Page breaking is done by a close analogue of the optimal
1772 * paragraph wrapping algorithm used by wrap_para(). We work
1773 * backwards from the end of the document line by line; for
1774 * each line, we contemplate every possible number of lines we
1775 * could put on a page starting with that line, determine a
1776 * cost function for each one, add it to the pre-computed cost
1777 * function for optimally page-breaking everything after that
1778 * page, and pick the best option.
1779 *
1780 * This is made slightly more complex by the fact that we have
1781 * a multi-column index with a heading at the top of the
1782 * _first_ page, meaning that the first _ncols_ pages must have
1783 * a different length. Hence, we must do the wrapping ncols+1
1784 * times over, hypothetically trying to put every subsequence
1785 * on every possible page.
1786 *
1787 * Since my line_data structures are only used for this
1788 * purpose, I might as well just store the algorithm data
1789 * directly in them.
1790 */
1791
1792 for (l = last; l; l = l->prev) {
1793 l->bestcost = snewn(ncols+1, int);
1794 l->vshortfall = snewn(ncols+1, int);
1795 l->text = snewn(ncols+1, int);
1796 l->space = snewn(ncols+1, int);
1797 l->page_last = snewn(ncols+1, line_data *);
1798
1799 for (n = 0; n <= ncols; n++) {
1800 int minheight, text = 0, space = 0;
1801 int cost;
1802
1803 n1 = (n < ncols ? n+1 : ncols);
1804 if (n < ncols)
1805 this_height = page_height - headspace;
1806 else
1807 this_height = page_height;
1808
1809 l->bestcost[n] = -1;
1810 for (m = l; m; m = m->next) {
1811 if (m != l && m->page_break)
1812 break; /* we've gone as far as we can */
1813
1814 if (m != l) {
1815 if (m->prev->space_after > 0)
1816 space += m->prev->space_after;
1817 else
1818 text += m->prev->space_after;
1819 }
1820 if (m != l || m->page_break) {
1821 if (m->space_before > 0)
1822 space += m->space_before;
1823 else
1824 text += m->space_before;
1825 }
1826 text += m->line_height;
1827 minheight = text + space;
1828
1829 if (m != l && minheight > this_height)
1830 break;
1831
1832 /*
1833 * If the space after this paragraph is _negative_
1834 * (which means the next line is folded on to this
1835 * one, which happens in the index), we absolutely
1836 * cannot break here.
1837 */
1838 if (m->space_after >= 0) {
1839
1840 /*
1841 * Compute the cost of this arrangement, as the
1842 * square of the amount of wasted space on the
1843 * page. Exception: if this is the last page
1844 * before a mandatory break or the document
1845 * end, we don't penalise a large blank area.
1846 */
1847 if (m != last && m->next && !m->next->page_break)
1848 {
1849 int x = (this_height - minheight) / FUNITS_PER_PT *
1850 4096.0;
1851 int xf;
1852
1853 xf = x & 0xFF;
1854 x >>= 8;
1855
1856 cost = x*x;
1857 cost += (x * xf) >> 8;
1858 } else
1859 cost = 0;
1860
1861 if (m != last && m->next && !m->next->page_break) {
1862 cost += m->penalty_after;
1863 cost += m->next->penalty_before;
1864 }
1865
1866 if (m != last && m->next && !m->next->page_break)
1867 cost += m->next->bestcost[n1];
1868 if (l->bestcost[n] == -1 || l->bestcost[n] > cost) {
1869 /*
1870 * This is the best option yet for this
1871 * starting point.
1872 */
1873 l->bestcost[n] = cost;
1874 if (m != last && m->next && !m->next->page_break)
1875 l->vshortfall[n] = this_height - minheight;
1876 else
1877 l->vshortfall[n] = 0;
1878 l->text[n] = text;
1879 l->space[n] = space;
1880 l->page_last[n] = m;
1881 }
1882 }
1883
1884 if (m == last)
1885 break;
1886 }
1887 }
1888 }
1889
1890 /*
1891 * Now go through the line list forwards and assemble the
1892 * actual pages.
1893 */
1894 ph = pt = NULL;
1895
1896 l = first;
1897 n = 0;
1898 while (l) {
1899 page_data *page;
1900 int text, space, head;
1901
1902 page = snew(page_data);
1903 page->next = NULL;
1904 page->prev = pt;
1905 if (pt)
1906 pt->next = page;
1907 else
1908 ph = page;
1909 pt = page;
1910
1911 page->first_line = l;
1912 page->last_line = l->page_last[n];
1913
1914 page->first_text = page->last_text = NULL;
1915 page->first_xref = page->last_xref = NULL;
1916 page->first_rect = page->last_rect = NULL;
1917
1918 /*
1919 * Now assign a y-coordinate to each line on the page.
1920 */
1921 text = space = 0;
1922 head = (n < ncols ? headspace : 0);
1923 for (l = page->first_line; l; l = l->next) {
1924 if (l != page->first_line) {
1925 if (l->prev->space_after > 0)
1926 space += l->prev->space_after;
1927 else
1928 text += l->prev->space_after;
1929 }
1930 if (l != page->first_line || l->page_break) {
1931 if (l->space_before > 0)
1932 space += l->space_before;
1933 else
1934 text += l->space_before;
1935 }
1936 text += l->line_height;
1937
1938 l->page = page;
1939 l->ypos = text + space + head;
1940 if (page->first_line->space[n]) {
1941 l->ypos += space * (float)page->first_line->vshortfall[n] /
1942 page->first_line->space[n];
1943 }
1944
1945 if (l == page->last_line)
1946 break;
1947 }
1948
1949 l = page->last_line;
1950 if (l == last)
1951 break;
1952 l = l->next;
1953
1954 n = (n < ncols ? n+1 : ncols);
1955 }
1956
1957 return ph;
1958 }
1959
1960 static void add_rect_to_page(page_data *page, int x, int y, int w, int h)
1961 {
1962 rect *r = snew(rect);
1963
1964 r->next = NULL;
1965 if (page->last_rect)
1966 page->last_rect->next = r;
1967 else
1968 page->first_rect = r;
1969 page->last_rect = r;
1970
1971 r->x = x;
1972 r->y = y;
1973 r->w = w;
1974 r->h = h;
1975 }
1976
1977 static void add_string_to_page(page_data *page, int x, int y,
1978 font_encoding *fe, int size, char *text,
1979 int width)
1980 {
1981 text_fragment *frag;
1982
1983 frag = snew(text_fragment);
1984 frag->next = NULL;
1985
1986 if (page->last_text)
1987 page->last_text->next = frag;
1988 else
1989 page->first_text = frag;
1990 page->last_text = frag;
1991
1992 frag->x = x;
1993 frag->y = y;
1994 frag->fe = fe;
1995 frag->fontsize = size;
1996 frag->text = dupstr(text);
1997 frag->width = width;
1998 }
1999
2000 /*
2001 * Returns the updated x coordinate.
2002 */
2003 static int render_string(page_data *page, font_data *font, int fontsize,
2004 int x, int y, wchar_t *str, unsigned flags)
2005 {
2006 char *text;
2007 int textpos, textwid, kern, nglyph, glyph, oglyph, lig;
2008 font_encoding *subfont = NULL, *sf;
2009 subfont_map_entry *sme;
2010
2011 text = snewn(1 + ustrlen(str), char);
2012 textpos = textwid = 0;
2013
2014 glyph = NOGLYPH;
2015 nglyph = utoglyph(font->info, *str);
2016 while (*str) {
2017 oglyph = glyph;
2018 glyph = nglyph;
2019 nglyph = utoglyph(font->info, str[1]);
2020
2021 if (glyph == NOGLYPH) {
2022 str++;
2023 continue; /* nothing more we can do here */
2024 }
2025
2026 if (!(flags & RS_NOLIG) &&
2027 (lig = find_lig(font, glyph, nglyph)) != NOGLYPH) {
2028 nglyph = lig;
2029 str++;
2030 continue;
2031 }
2032
2033 /*
2034 * Find which subfont this character is going in.
2035 */
2036 sme = encode_glyph(glyph, *str, font);
2037 sf = sme->subfont;
2038
2039 kern = find_kern(font, oglyph, glyph) * fontsize;
2040
2041 if (!subfont || sf != subfont || kern) {
2042 if (subfont) {
2043 text[textpos] = '\0';
2044 add_string_to_page(page, x, y, subfont, fontsize, text,
2045 textwid);
2046 x += textwid + kern;
2047 } else {
2048 assert(textpos == 0);
2049 }
2050 textpos = 0;
2051 textwid = 0;
2052 subfont = sf;
2053 }
2054
2055 text[textpos++] = sme->position;
2056 textwid += find_width(font, glyph) * fontsize;
2057
2058 str++;
2059 }
2060
2061 if (textpos > 0) {
2062 text[textpos] = '\0';
2063 add_string_to_page(page, x, y, subfont, fontsize, text, textwid);
2064 x += textwid;
2065 }
2066
2067 return x;
2068 }
2069
2070 /*
2071 * Returns the updated x coordinate.
2072 */
2073 static int render_text(page_data *page, para_data *pdata, line_data *ldata,
2074 int x, int y, word *text, word *text_end, xref **xr,
2075 int shortfall, int nspaces, int *nspace,
2076 keywordlist *keywords, indexdata *idx, paper_conf *conf)
2077 {
2078 while (text && text != text_end) {
2079 int style, type, findex, errs;
2080 wchar_t *str;
2081 xref_dest dest;
2082 unsigned flags = 0;
2083
2084 switch (text->type) {
2085 /*
2086 * Start a cross-reference.
2087 */
2088 case word_HyperLink:
2089 case word_UpperXref:
2090 case word_LowerXref:
2091 case word_PageXref:
2092
2093 if (text->type == word_HyperLink) {
2094 dest.type = URL;
2095 dest.url = utoa_dup(text->text, CS_ASCII);
2096 dest.page = NULL;
2097 } else if (text->type == word_PageXref) {
2098 dest.type = PAGE;
2099 dest.url = NULL;
2100 dest.page = (page_data *)text->private_data;
2101 } else {
2102 keyword *kwl = kw_lookup(keywords, text->text);
2103 para_data *pdata;
2104
2105 if (kwl) {
2106 assert(kwl->para->private_data);
2107 pdata = (para_data *) kwl->para->private_data;
2108 dest.type = PAGE;
2109 dest.page = pdata->first->page;
2110 dest.url = NULL;
2111 } else {
2112 /*
2113 * Shouldn't happen, but *shrug*
2114 */
2115 dest.type = NONE;
2116 dest.page = NULL;
2117 dest.url = NULL;
2118 }
2119 }
2120 if (dest.type != NONE) {
2121 *xr = snew(xref);
2122 (*xr)->dest = dest; /* structure copy */
2123 if (page->last_xref)
2124 page->last_xref->next = *xr;
2125 else
2126 page->first_xref = *xr;
2127 page->last_xref = *xr;
2128 (*xr)->next = NULL;
2129
2130 /*
2131 * FIXME: Ideally we should have, and use, some
2132 * vertical font metric information here so that
2133 * our cross-ref rectangle can take account of
2134 * descenders and the font's cap height. This will
2135 * do for the moment, but it isn't ideal.
2136 */
2137 (*xr)->lx = (*xr)->rx = x;
2138 (*xr)->by = y;
2139 (*xr)->ty = y + ldata->line_height;
2140 }
2141 goto nextword;
2142
2143 /*
2144 * Finish extending a cross-reference box.
2145 */
2146 case word_HyperEnd:
2147 case word_XrefEnd:
2148 *xr = NULL;
2149 goto nextword;
2150
2151 /*
2152 * Add the current page number to the list of pages
2153 * referenced by an index entry.
2154 */
2155 case word_IndexRef:
2156 /*
2157 * We don't create index references in contents entries.
2158 */
2159 if (!pdata->contents_entry) {
2160 indextag *tag;
2161 int i;
2162
2163 tag = index_findtag(idx, text->text);
2164 if (!tag)
2165 goto nextword;
2166
2167 for (i = 0; i < tag->nrefs; i++) {
2168 indexentry *entry = tag->refs[i];
2169 paper_idx *pi = (paper_idx *)entry->backend_data;
2170
2171 /*
2172 * If the same index term is indexed twice
2173 * within the same section, we only want to
2174 * mention it once in the index.
2175 */
2176 if (pi->lastpage != page) {
2177 word **wp;
2178
2179 if (pi->lastword) {
2180 pi->lastword = pi->lastword->next =
2181 fake_word(L",");
2182 pi->lastword = pi->lastword->next =
2183 fake_space_word();
2184 wp = &pi->lastword->next;
2185 } else
2186 wp = &pi->words;
2187
2188 pi->lastword = *wp =
2189 fake_page_ref(page);
2190 pi->lastword = pi->lastword->next =
2191 fake_word(page->number);
2192 pi->lastword = pi->lastword->next =
2193 fake_end_ref();
2194 }
2195
2196 pi->lastpage = page;
2197 }
2198 }
2199 goto nextword;
2200 }
2201
2202 style = towordstyle(text->type);
2203 type = removeattr(text->type);
2204
2205 findex = (style == word_Normal ? FONT_NORMAL :
2206 style == word_Emph ? FONT_EMPH :
2207 FONT_CODE);
2208
2209 if (style == word_Code || style == word_WeakCode) flags |= RS_NOLIG;
2210 flags |= pdata->extraflags;
2211
2212 if (type == word_Normal) {
2213 str = text->text;
2214 } else if (type == word_WhiteSpace) {
2215 x += pdata->sizes[findex] *
2216 string_width(pdata->fonts[findex], L" ", NULL, 0);
2217 if (nspaces && findex != FONT_CODE) {
2218 x += (*nspace+1) * shortfall / nspaces;
2219 x -= *nspace * shortfall / nspaces;
2220 (*nspace)++;
2221 }
2222 goto nextword;
2223 } else /* if (type == word_Quote) */ {
2224 if (text->aux == quote_Open)
2225 str = conf->lquote;
2226 else
2227 str = conf->rquote;
2228 }
2229
2230 (void) string_width(pdata->fonts[findex], str, &errs, flags);
2231
2232 if (errs && text->alt)
2233 x = render_text(page, pdata, ldata, x, y, text->alt, NULL,
2234 xr, shortfall, nspaces, nspace, keywords, idx,
2235 conf);
2236 else
2237 x = render_string(page, pdata->fonts[findex],
2238 pdata->sizes[findex], x, y, str, flags);
2239
2240 if (*xr)
2241 (*xr)->rx = x;
2242
2243 nextword:
2244 text = text->next;
2245 }
2246
2247 return x;
2248 }
2249
2250 /*
2251 * Returns the last x position used on the line.
2252 */
2253 static int render_line(line_data *ldata, int left_x, int top_y,
2254 xref_dest *dest, keywordlist *keywords, indexdata *idx,
2255 paper_conf *conf)
2256 {
2257 int nspace;
2258 xref *xr;
2259 int ret = 0;
2260
2261 if (ldata->aux_text) {
2262 int x;
2263 xr = NULL;
2264 nspace = 0;
2265 x = render_text(ldata->page, ldata->pdata, ldata,
2266 left_x + ldata->aux_left_indent,
2267 top_y - ldata->ypos,
2268 ldata->aux_text, NULL, &xr, 0, 0, &nspace,
2269 keywords, idx, conf);
2270 if (ldata->aux_text_2)
2271 render_text(ldata->page, ldata->pdata, ldata,
2272 x, top_y - ldata->ypos,
2273 ldata->aux_text_2, NULL, &xr, 0, 0, &nspace,
2274 keywords, idx, conf);
2275 }
2276 nspace = 0;
2277
2278 if (ldata->first) {
2279 /*
2280 * There might be a cross-reference carried over from a
2281 * previous line.
2282 */
2283 if (dest->type != NONE) {
2284 xr = snew(xref);
2285 xr->next = NULL;
2286 xr->dest = *dest; /* structure copy */
2287 if (ldata->page->last_xref)
2288 ldata->page->last_xref->next = xr;
2289 else
2290 ldata->page->first_xref = xr;
2291 ldata->page->last_xref = xr;
2292 xr->lx = xr->rx = left_x + ldata->xpos;
2293 xr->by = top_y - ldata->ypos;
2294 xr->ty = top_y - ldata->ypos + ldata->line_height;
2295 } else
2296 xr = NULL;
2297
2298 {
2299 int extra_indent, shortfall, spaces;
2300 int just = ldata->pdata->justification;
2301
2302 /*
2303 * All forms of justification become JUST when we have
2304 * to squeeze the paragraph.
2305 */
2306 if (ldata->hshortfall < 0)
2307 just = JUST;
2308
2309 switch (just) {
2310 case JUST:
2311 shortfall = ldata->hshortfall;
2312 spaces = ldata->nspaces;
2313 extra_indent = 0;
2314 break;
2315 case LEFT:
2316 shortfall = spaces = extra_indent = 0;
2317 break;
2318 case RIGHT:
2319 shortfall = spaces = 0;
2320 extra_indent = ldata->real_shortfall;
2321 break;
2322 }
2323
2324 ret = render_text(ldata->page, ldata->pdata, ldata,
2325 left_x + ldata->xpos + extra_indent,
2326 top_y - ldata->ypos, ldata->first, ldata->end,
2327 &xr, shortfall, spaces, &nspace,
2328 keywords, idx, conf);
2329 }
2330
2331 if (xr) {
2332 /*
2333 * There's a cross-reference continued on to the next line.
2334 */
2335 *dest = xr->dest;
2336 } else
2337 dest->type = NONE;
2338 }
2339
2340 return ret;
2341 }
2342
2343 static void render_para(para_data *pdata, paper_conf *conf,
2344 keywordlist *keywords, indexdata *idx,
2345 paragraph *index_placeholder, page_data *index_page)
2346 {
2347 int last_x;
2348 xref *cxref;
2349 page_data *cxref_page;
2350 xref_dest dest;
2351 para_data *target;
2352 line_data *ldata;
2353
2354 dest.type = NONE;
2355 cxref = NULL;
2356 cxref_page = NULL;
2357
2358 for (ldata = pdata->first; ldata; ldata = ldata->next) {
2359 /*
2360 * If this is a contents entry, we expect to have a single
2361 * enormous cross-reference rectangle covering the whole
2362 * thing. (Unless, of course, it spans multiple pages.)
2363 */
2364 if (pdata->contents_entry && ldata->page != cxref_page) {
2365 cxref_page = ldata->page;
2366 cxref = snew(xref);
2367 cxref->next = NULL;
2368 cxref->dest.type = PAGE;
2369 if (pdata->contents_entry == index_placeholder) {
2370 cxref->dest.page = index_page;
2371 } else {
2372 assert(pdata->contents_entry->private_data);
2373 target = (para_data *)pdata->contents_entry->private_data;
2374 cxref->dest.page = target->first->page;
2375 }
2376 cxref->dest.url = NULL;
2377 if (ldata->page->last_xref)
2378 ldata->page->last_xref->next = cxref;
2379 else
2380 ldata->page->first_xref = cxref;
2381 ldata->page->last_xref = cxref;
2382 cxref->lx = conf->left_margin;
2383 cxref->rx = conf->paper_width - conf->right_margin;
2384 cxref->ty = conf->paper_height - conf->top_margin
2385 - ldata->ypos + ldata->line_height;
2386 }
2387 if (pdata->contents_entry) {
2388 assert(cxref != NULL);
2389 cxref->by = conf->paper_height - conf->top_margin
2390 - ldata->ypos;
2391 }
2392
2393 last_x = render_line(ldata, conf->left_margin,
2394 conf->paper_height - conf->top_margin,
2395 &dest, keywords, idx, conf);
2396 if (ldata == pdata->last)
2397 break;
2398 }
2399
2400 /*
2401 * If this is a contents entry, add leaders and a page
2402 * number.
2403 */
2404 if (pdata->contents_entry) {
2405 word *w;
2406 wchar_t *num;
2407 int wid;
2408 int x;
2409
2410 if (pdata->contents_entry == index_placeholder) {
2411 num = index_page->number;
2412 } else {
2413 assert(pdata->contents_entry->private_data);
2414 target = (para_data *)pdata->contents_entry->private_data;
2415 num = target->first->page->number;
2416 }
2417
2418 w = fake_word(num);
2419 wid = paper_width_simple(pdata, w, conf);
2420 sfree(w);
2421
2422 for (x = 0; x < conf->base_width; x += conf->leader_separation)
2423 if (x - conf->leader_separation > last_x - conf->left_margin &&
2424 x + conf->leader_separation < conf->base_width - wid)
2425 render_string(pdata->last->page,
2426 pdata->fonts[FONT_NORMAL],
2427 pdata->sizes[FONT_NORMAL],
2428 conf->left_margin + x,
2429 (conf->paper_height - conf->top_margin -
2430 pdata->last->ypos), L".", 0);
2431
2432 render_string(pdata->last->page,
2433 pdata->fonts[FONT_NORMAL],
2434 pdata->sizes[FONT_NORMAL],
2435 conf->paper_width - conf->right_margin - wid,
2436 (conf->paper_height - conf->top_margin -
2437 pdata->last->ypos), num, 0);
2438 }
2439
2440 /*
2441 * Render any rectangle (chapter title underline or rule)
2442 * that goes with this paragraph.
2443 */
2444 switch (pdata->rect_type) {
2445 case RECT_CHAPTER_UNDERLINE:
2446 add_rect_to_page(pdata->last->page,
2447 conf->left_margin,
2448 (conf->paper_height - conf->top_margin -
2449 pdata->last->ypos -
2450 conf->chapter_underline_depth),
2451 conf->base_width,
2452 conf->chapter_underline_thickness);
2453 break;
2454 case RECT_RULE:
2455 add_rect_to_page(pdata->first->page,
2456 conf->left_margin + pdata->first->xpos,
2457 (conf->paper_height - conf->top_margin -
2458 pdata->last->ypos -
2459 pdata->last->line_height),
2460 conf->base_width - pdata->first->xpos,
2461 pdata->last->line_height);
2462 break;
2463 default: /* placate gcc */
2464 break;
2465 }
2466 }
2467
2468 static para_data *code_paragraph(int indent, word *words, paper_conf *conf)
2469 {
2470 para_data *pdata = snew(para_data);
2471
2472 /*
2473 * For code paragraphs, I'm going to hack grievously and
2474 * pretend the three normal fonts are the three code paragraph
2475 * fonts.
2476 */
2477 setfont(pdata, &conf->fcode);
2478
2479 pdata->first = pdata->last = NULL;
2480 pdata->outline_level = -1;
2481 pdata->rect_type = RECT_NONE;
2482 pdata->contents_entry = NULL;
2483 pdata->justification = LEFT;
2484 pdata->extraflags = RS_NOLIG;
2485
2486 for (; words; words = words->next) {
2487 wchar_t *t, *e, *start;
2488 word *lhead = NULL, *ltail = NULL, *w;
2489 line_data *ldata;
2490 int prev = -1, curr;
2491
2492 t = words->text;
2493 if (words->next && words->next->type == word_Emph) {
2494 e = words->next->text;
2495 words = words->next;
2496 } else
2497 e = NULL;
2498
2499 start = t;
2500
2501 while (*start) {
2502 while (*t) {
2503 if (!e || !*e)
2504 curr = 0;
2505 else if (*e == L'i')
2506 curr = 1;
2507 else if (*e == L'b')
2508 curr = 2;
2509 else
2510 curr = 0;
2511
2512 if (prev < 0)
2513 prev = curr;
2514
2515 if (curr != prev)
2516 break;
2517
2518 t++;
2519 if (e && *e)
2520 e++;
2521 }
2522
2523 /*
2524 * We've isolated a maximal subsequence of the line
2525 * which has the same emphasis. Form it into a word
2526 * structure.
2527 */
2528 w = snew(word);
2529 w->next = NULL;
2530 w->alt = NULL;
2531 w->type = (prev == 0 ? word_WeakCode :
2532 prev == 1 ? word_Emph : word_Normal);
2533 w->text = snewn(t-start+1, wchar_t);
2534 memcpy(w->text, start, (t-start) * sizeof(wchar_t));
2535 w->text[t-start] = '\0';
2536 w->breaks = FALSE;
2537
2538 if (ltail)
2539 ltail->next = w;
2540 else
2541 lhead = w;
2542 ltail = w;
2543
2544 start = t;
2545 prev = -1;
2546 }
2547
2548 ldata = snew(line_data);
2549
2550 ldata->pdata = pdata;
2551 ldata->first = lhead;
2552 ldata->end = NULL;
2553 ldata->line_height = conf->fcode.font_size * UNITS_PER_PT;
2554
2555 ldata->xpos = indent;
2556
2557 if (pdata->last) {
2558 pdata->last->next = ldata;
2559 ldata->prev = pdata->last;
2560 } else {
2561 pdata->first = ldata;
2562 ldata->prev = NULL;
2563 }
2564 ldata->next = NULL;
2565 pdata->last = ldata;
2566
2567 ldata->hshortfall = 0;
2568 ldata->nspaces = 0;
2569 ldata->aux_text = NULL;
2570 ldata->aux_text_2 = NULL;
2571 ldata->aux_left_indent = 0;
2572 /* General opprobrium for breaking in a code paragraph. */
2573 ldata->penalty_before = ldata->penalty_after = 50000;
2574 }
2575
2576 standard_line_spacing(pdata, conf);
2577
2578 return pdata;
2579 }
2580
2581 static para_data *rule_paragraph(int indent, paper_conf *conf)
2582 {
2583 para_data *pdata = snew(para_data);
2584 line_data *ldata;
2585
2586 ldata = snew(line_data);
2587
2588 ldata->pdata = pdata;
2589 ldata->first = NULL;
2590 ldata->end = NULL;
2591 ldata->line_height = conf->rule_thickness;
2592
2593 ldata->xpos = indent;
2594
2595 ldata->prev = NULL;
2596 ldata->next = NULL;
2597
2598 ldata->hshortfall = 0;
2599 ldata->nspaces = 0;
2600 ldata->aux_text = NULL;
2601 ldata->aux_text_2 = NULL;
2602 ldata->aux_left_indent = 0;
2603
2604 /*
2605 * Better to break after a rule than before it
2606 */
2607 ldata->penalty_after += 100000;
2608 ldata->penalty_before += -100000;
2609
2610 pdata->first = pdata->last = ldata;
2611 pdata->outline_level = -1;
2612 pdata->rect_type = RECT_RULE;
2613 pdata->contents_entry = NULL;
2614 pdata->justification = LEFT;
2615 pdata->extraflags = 0;
2616
2617 standard_line_spacing(pdata, conf);
2618
2619 return pdata;
2620 }
2621
2622 /*
2623 * Plain-text-like formatting for outline titles.
2624 */
2625 static void paper_rdaddw(rdstring *rs, word *text) {
2626 for (; text; text = text->next) switch (text->type) {
2627 case word_HyperLink:
2628 case word_HyperEnd:
2629 case word_UpperXref:
2630 case word_LowerXref:
2631 case word_XrefEnd:
2632 case word_IndexRef:
2633 break;
2634
2635 case word_Normal:
2636 case word_Emph:
2637 case word_Code:
2638 case word_WeakCode:
2639 case word_WhiteSpace:
2640 case word_EmphSpace:
2641 case word_CodeSpace:
2642 case word_WkCodeSpace:
2643 case word_Quote:
2644 case word_EmphQuote:
2645 case word_CodeQuote:
2646 case word_WkCodeQuote:
2647 assert(text->type != word_CodeQuote &&
2648 text->type != word_WkCodeQuote);
2649 if (towordstyle(text->type) == word_Emph &&
2650 (attraux(text->aux) == attr_First ||
2651 attraux(text->aux) == attr_Only))
2652 rdadd(rs, L'_'); /* FIXME: configurability */
2653 else if (towordstyle(text->type) == word_Code &&
2654 (attraux(text->aux) == attr_First ||
2655 attraux(text->aux) == attr_Only))
2656 rdadd(rs, L'\''); /* FIXME: configurability */
2657 if (removeattr(text->type) == word_Normal) {
2658 rdadds(rs, text->text);
2659 } else if (removeattr(text->type) == word_WhiteSpace) {
2660 rdadd(rs, L' ');
2661 } else if (removeattr(text->type) == word_Quote) {
2662 rdadd(rs, L'\''); /* fixme: configurability */
2663 }
2664 if (towordstyle(text->type) == word_Emph &&
2665 (attraux(text->aux) == attr_Last ||
2666 attraux(text->aux) == attr_Only))
2667 rdadd(rs, L'_'); /* FIXME: configurability */
2668 else if (towordstyle(text->type) == word_Code &&
2669 (attraux(text->aux) == attr_Last ||
2670 attraux(text->aux) == attr_Only))
2671 rdadd(rs, L'\''); /* FIXME: configurability */
2672 break;
2673 }
2674 }
2675
2676 static wchar_t *prepare_outline_title(word *first, wchar_t *separator,
2677 word *second)
2678 {
2679 rdstring rs = {0, 0, NULL};
2680
2681 if (first)
2682 paper_rdaddw(&rs, first);
2683 if (separator)
2684 rdadds(&rs, separator);
2685 if (second)
2686 paper_rdaddw(&rs, second);
2687
2688 return rs.text;
2689 }
2690
2691 static word *fake_word(wchar_t *text)
2692 {
2693 word *ret = snew(word);
2694 ret->next = NULL;
2695 ret->alt = NULL;
2696 ret->type = word_Normal;
2697 ret->text = ustrdup(text);
2698 ret->breaks = FALSE;
2699 ret->aux = 0;
2700 return ret;
2701 }
2702
2703 static word *fake_space_word(void)
2704 {
2705 word *ret = snew(word);
2706 ret->next = NULL;
2707 ret->alt = NULL;
2708 ret->type = word_WhiteSpace;
2709 ret->text = NULL;
2710 ret->breaks = TRUE;
2711 ret->aux = 0;
2712 return ret;
2713 }
2714
2715 static word *fake_page_ref(page_data *page)
2716 {
2717 word *ret = snew(word);
2718 ret->next = NULL;
2719 ret->alt = NULL;
2720 ret->type = word_PageXref;
2721 ret->text = NULL;
2722 ret->breaks = FALSE;
2723 ret->aux = 0;
2724 ret->private_data = page;
2725 return ret;
2726 }
2727
2728 static word *fake_end_ref(void)
2729 {
2730 word *ret = snew(word);
2731 ret->next = NULL;
2732 ret->alt = NULL;
2733 ret->type = word_XrefEnd;
2734 ret->text = NULL;
2735 ret->breaks = FALSE;
2736 ret->aux = 0;
2737 return ret;
2738 }
2739
2740 static word *prepare_contents_title(word *first, wchar_t *separator,
2741 word *second)
2742 {
2743 word *ret;
2744 word **wptr, *w;
2745
2746 wptr = &ret;
2747
2748 if (first) {
2749 w = dup_word_list(first);
2750 *wptr = w;
2751 while (w->next)
2752 w = w->next;
2753 wptr = &w->next;
2754 }
2755
2756 if (separator) {
2757 w = fake_word(separator);
2758 *wptr = w;
2759 wptr = &w->next;
2760 }
2761
2762 if (second) {
2763 *wptr = dup_word_list(second);
2764 }
2765
2766 return ret;
2767 }
2768
2769 static void fold_into_page(page_data *dest, page_data *src, int right_shift)
2770 {
2771 line_data *ldata;
2772
2773 if (!src->first_line)
2774 return;
2775
2776 if (dest->last_line) {
2777 dest->last_line->next = src->first_line;
2778 src->first_line->prev = dest->last_line;
2779 }
2780 dest->last_line = src->last_line;
2781
2782 for (ldata = src->first_line; ldata; ldata = ldata->next) {
2783 ldata->page = dest;
2784 ldata->xpos += right_shift;
2785
2786 if (ldata == src->last_line)
2787 break;
2788 }
2789 }