2 * Paper printing pre-backend for Halibut.
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.
13 * TODO in future work:
15 * - linearised PDF, perhaps?
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.
22 * - rather than the ugly aux_text mechanism for rendering chapter
23 * titles, we could actually build the correct word list and
26 * - get vertical font metrics and use them to position the PDF
27 * xref boxes more pleasantly
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
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
43 * * double-sided document switch?
44 * + means you have two header/footer formats which
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
52 * - ability to use Type 1 fonts without AFM files
53 * * we need to parse the font to extract its metrics
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
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
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.
78 typedef struct paper_conf_Tag paper_conf
;
79 typedef struct paper_idx_Tag paper_idx
;
82 font_data
*fonts
[NFONTS
];
86 struct paper_conf_Tag
{
93 int indent_list_bullet
;
94 int indent_list_after
;
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
;
104 font_cfg fbase
, fcode
, ftitle
, fchapter
, *fsect
;
106 int contents_indent_step
;
108 int leader_separation
;
112 int pagenum_fontsize
;
114 wchar_t *lquote
, *rquote
, *bullet
;
115 wchar_t *contents_text
, *index_text
;
116 /* These are derived from the above */
122 struct paper_idx_Tag
{
124 * Word list giving the page numbers on which this index entry
125 * appears. Also the last word in the list, for ease of
131 * The last page added to the list (so we can ensure we don't
138 word_PageXref
= word_NotWordType
+ 1
141 /* Flags for render_string() */
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
,
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
,
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
,
166 static void standard_line_spacing(para_data
*pdata
, paper_conf
*conf
);
167 static wchar_t *prepare_outline_title(word
*first
, wchar_t *separator
,
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
,
175 static void fold_into_page(page_data
*dest
, page_data
*src
, int right_shift
);
177 static int fonts_ok(wchar_t *string
, ...)
183 va_start(ap
, string
);
184 while ( (font
= va_arg(ap
, font_data
*)) != NULL
) {
186 (void) string_width(font
, string
, &errs
, 0);
197 static void paper_cfg_fonts(font_data
**fonts
, font_list
*fontlist
,
198 wchar_t *wp
, filepos
*fpos
) {
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
);
209 /* FIXME: proper error */
210 error(err_nofont
, fpos
, wp
);
214 static paper_conf
paper_configure(paragraph
*source
, font_list
*fontlist
) {
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");
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
;
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";
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
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
);
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
);
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")) {
319 (int) 0.5 + FUNITS_PER_PT
* utof(uadv(p
->keyword
));
320 } else if (!ustricmp(p
->keyword
, L
"paper-page-height")) {
322 (int) 0.5 + FUNITS_PER_PT
* utof(uadv(p
->keyword
));
323 } else if (!ustricmp(p
->keyword
, L
"paper-left-margin")) {
325 (int) 0.5 + FUNITS_PER_PT
* utof(uadv(p
->keyword
));
326 } else if (!ustricmp(p
->keyword
, L
"paper-top-margin")) {
328 (int) 0.5 + FUNITS_PER_PT
* utof(uadv(p
->keyword
));
329 } else if (!ustricmp(p
->keyword
, L
"paper-right-margin")) {
331 (int) 0.5 + FUNITS_PER_PT
* utof(uadv(p
->keyword
));
332 } else if (!ustricmp(p
->keyword
, L
"paper-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")) {
340 (int) 0.5 + FUNITS_PER_PT
* utof(uadv(p
->keyword
));
341 } else if (!ustricmp(p
->keyword
, L
"paper-quote-indent")) {
343 (int) 0.5 + FUNITS_PER_PT
* utof(uadv(p
->keyword
));
344 } else if (!ustricmp(p
->keyword
, L
"paper-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")) {
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")) {
376 (int) 0.5 + FUNITS_PER_PT
* utof(uadv(p
->keyword
));
377 } else if (!ustricmp(p
->keyword
, L
"paper-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
),
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
),
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
),
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
),
407 } else if (!ustricmp(p
->keyword
, L
"paper-section-font-size")) {
408 wchar_t *q
= uadv(p
->keyword
);
414 if (n
>= ret
.nfsect
) {
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];
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
);
429 if (n
>= ret
.nfsect
) {
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];
436 paper_cfg_fonts(ret
.fsect
[n
].fonts
, fontlist
, q
, &p
->fpos
);
442 * Set up the derived fields in the conf structure.
446 ret
.paper_width
- ret
.left_margin
- ret
.right_margin
;
448 ret
.paper_height
- ret
.top_margin
- ret
.bottom_margin
;
449 ret
.indent_list
= ret
.indent_list_bullet
+ ret
.indent_list_after
;
451 (ret
.base_width
- (ret
.index_cols
-1) * ret
.index_gutter
)
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.
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
))) {
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
) &&
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
))
490 ret
.lquote
= uadv(ret
.rquote
);
491 ret
.rquote
= uadv(ret
.lquote
);
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
);
503 void *paper_pre_backend(paragraph
*sourceform
, keywordlist
*keywords
,
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
;
513 paper_conf
*conf
, ourconf
;
516 paragraph index_placeholder_para
;
517 page_data
*first_index_page
;
520 fontlist
= snew(font_list
);
521 fontlist
->head
= fontlist
->tail
= NULL
;
523 ourconf
= paper_configure(sourceform
, fontlist
);
527 * Set up a data structure to collect page numbers for each
536 for (i
= 0; (entry
= index234(idx
->entries
, i
)) != NULL
; i
++) {
537 paper_idx
*pi
= snew(paper_idx
);
541 pi
->words
= pi
->lastword
= NULL
;
544 entry
->backend_data
= pi
;
549 * Format the contents entry for each heading.
552 word
*contents_title
;
553 contents_title
= fake_word(conf
->contents_text
);
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
) {
568 case para_UnnumberedChapter
:
574 words
= prepare_contents_title(p
->kwtext
, L
": ", p
->words
);
577 case para_UnnumberedChapter
:
578 words
= prepare_contents_title(NULL
, NULL
, p
->words
);
583 words
= prepare_contents_title(p
->kwtext2
, L
" ", p
->words
);
584 indent
= (p
->aux
+ 1) * conf
->contents_indent_step
;
587 pdata
= make_para_data(para_Normal
, p
->aux
, indent
,
588 conf
->contents_margin
,
589 NULL
, NULL
, words
, conf
);
591 pdata
->contents_entry
= p
;
592 lastcont
->next
= pdata
;
596 * Link all contents line structures together into
601 lastcontline
->next
= pdata
->first
;
602 pdata
->first
->prev
= lastcontline
;
604 firstcontline
= pdata
->first
;
605 pdata
->first
->prev
= NULL
;
607 lastcontline
= pdata
->last
;
608 lastcontline
->next
= NULL
;
616 * And one extra one, for the index.
619 pdata
= make_para_data(para_Normal
, 0, 0,
620 conf
->contents_margin
,
622 fake_word(conf
->index_text
), conf
);
624 pdata
->contents_entry
= &index_placeholder_para
;
625 lastcont
->next
= pdata
;
630 lastcontline
->next
= pdata
->first
;
631 pdata
->first
->prev
= lastcontline
;
633 firstcontline
= pdata
->first
;
634 pdata
->first
->prev
= NULL
;
636 lastcontline
= pdata
->last
;
637 lastcontline
->next
= NULL
;
643 * Do the main paragraph formatting.
646 used_contents
= FALSE
;
647 firstline
= lastline
= NULL
;
648 for (p
= sourceform
; p
; p
= p
->next
) {
649 p
->private_data
= NULL
;
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.
660 case para_NotParaType
:
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.
672 indent
+= conf
->indent_list
; break;
674 indent
-= conf
->indent_list
; assert(indent
>= 0); break;
676 indent
+= conf
->indent_quote
; break;
678 indent
-= conf
->indent_quote
; assert(indent
>= 0); break;
681 * This paragraph type is special. Process it
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;
694 * This paragraph is also special.
697 pdata
= rule_paragraph(indent
, conf
);
698 p
->private_data
= pdata
;
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.
709 case para_UnnumberedChapter
:
713 case para_BiblioCited
:
715 case para_NumberedList
:
716 case para_DescribedThing
:
717 case para_Description
:
720 pdata
= make_para_data(p
->type
, p
->aux
, indent
, 0,
721 p
->kwtext
, p
->kwtext2
, p
->words
, conf
);
723 p
->private_data
= pdata
;
728 if (p
->private_data
) {
729 pdata
= (para_data
*)p
->private_data
;
732 * If this is the first non-title heading, we link the
733 * contents section in before it.
735 if (!used_contents
&& pdata
->outline_level
> 0) {
736 used_contents
= TRUE
;
738 lastpara
->next
= firstcont
;
740 firstpara
= firstcont
;
742 assert(lastpara
->next
== NULL
);
745 lastline
->next
= firstcontline
;
746 firstcontline
->prev
= lastline
;
748 firstline
= firstcontline
;
749 firstcontline
->prev
= NULL
;
751 assert(lastcontline
!= NULL
);
752 lastline
= lastcontline
;
753 lastline
->next
= NULL
;
757 * Link all line structures together into a big list.
761 lastline
->next
= pdata
->first
;
762 pdata
->first
->prev
= lastline
;
764 firstline
= pdata
->first
;
765 pdata
->first
->prev
= NULL
;
767 lastline
= pdata
->last
;
768 lastline
->next
= NULL
;
772 * Link all paragraph structures together similarly.
776 lastpara
->next
= pdata
;
784 * Now we have an enormous linked list of every line of text in
785 * the document. Break it up into pages.
787 pages
= page_breaks(firstline
, lastline
, conf
->page_height
, 0, 0);
798 for (page
= pages
; page
; page
= page
->next
) {
799 sprintf(buf
, "%d", ++pagenum
);
800 page
->number
= ufroma_dup(buf
, CS_ASCII
);
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
;
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
);
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.
823 for (pdata
= firstpara
; pdata
; pdata
= pdata
->next
)
824 render_para(pdata
, conf
, keywords
, idx
,
825 &index_placeholder_para
, first_index_page
);
828 * Now we've laid out the main body pages, we should have
829 * acquired a full set of page numbers for the index.
835 para_data
*firstidx
, *lastidx
;
836 line_data
*firstidxline
, *lastidxline
, *ldata
;
837 page_data
*ipages
, *ipages2
, *page
;
840 * Create a set of paragraphs for the index.
842 index_title
= fake_word(conf
->index_text
);
844 firstidx
= make_para_data(para_UnnumberedChapter
, 0, 0, 0,
845 NULL
, NULL
, index_title
, conf
);
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
;
857 text
= make_para_data(para_Normal
, 0, 0,
858 conf
->base_width
- conf
->index_colwidth
,
859 NULL
, NULL
, entry
->text
, conf
);
861 pages
= make_para_data(para_Normal
, 0, 0,
862 conf
->base_width
- conf
->index_colwidth
,
863 NULL
, NULL
, pi
->words
, conf
);
865 text
->justification
= LEFT
;
866 pages
->justification
= RIGHT
;
867 text
->last
->space_after
= pages
->first
->space_before
=
868 conf
->base_leading
/ 2;
870 pages
->last
->space_after
= text
->first
->space_before
=
874 assert(pages
->first
);
879 * If feasible, fold the two halves of the index entry
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;
888 lastidx
->next
= text
;
894 * Link all index line structures together into
897 text
->last
->next
= pages
->first
;
898 pages
->first
->prev
= text
->last
;
900 lastidxline
->next
= text
->first
;
901 text
->first
->prev
= lastidxline
;
903 lastidxline
= pages
->last
;
906 * Breaking an index entry anywhere is so bad that I
907 * think I'm going to forbid it totally.
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;
917 * Now break the index into pages.
919 ipages
= page_breaks(firstidxline
, firstidxline
, conf
->page_height
,
921 ipages2
= page_breaks(firstidxline
->next
, lastidxline
,
924 firstidxline
->space_before
+
925 firstidxline
->line_height
+
926 firstidxline
->space_after
);
929 * This will have put each _column_ of the index on a
930 * separate page, which isn't what we want. Fold the pages
937 for (i
= 1; i
< conf
->index_cols
; i
++)
941 fold_into_page(page
, page
->next
,
942 i
* (conf
->index_colwidth
+
943 conf
->index_gutter
));
945 page
->next
= page
->next
->next
;
947 page
->next
->prev
= page
;
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
;
957 ipages
->next
->prev
= ipages
;
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
;
964 ipages
= first_index_page
;
967 * Number the index pages, except the already-numbered
970 for (page
= ipages
->next
; page
; page
= page
->next
) {
972 sprintf(buf
, "%d", ++pagenum
);
973 page
->number
= ufroma_dup(buf
, CS_ASCII
);
977 * Render the index pages.
979 for (pdata
= firstidx
; pdata
; pdata
= pdata
->next
)
980 render_para(pdata
, conf
, keywords
, idx
,
981 &index_placeholder_para
, first_index_page
);
984 * Link the index page list on to the end of the main page
990 for (page
= pages
; page
->next
; page
= page
->next
);
995 * Same with the paragraph list, which will cause the index
996 * to be mentioned in the document outline.
999 firstpara
= firstidx
;
1001 lastpara
->next
= firstidx
;
1006 * Draw the headers and footers.
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.
1015 for (page
= pages
; page
; page
= page
->next
) {
1018 width
= conf
->pagenum_fontsize
*
1019 string_width(conf
->fbase
.fonts
[FONT_NORMAL
], page
->number
,
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
,
1031 * Start putting together the overall document structure we're
1034 doc
= snew(document
);
1035 doc
->fonts
= fontlist
;
1037 doc
->paper_width
= conf
->paper_width
;
1038 doc
->paper_height
= conf
->paper_height
;
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
1049 doc
->outline_elements
= snewn(osize
, outline_element
);
1050 doc
->n_outline_elements
= 0;
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
++;
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
) {
1067 doc
->outline_elements
=
1068 sresize(doc
->outline_elements
, osize
, outline_element
);
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
++;
1082 static void setfont(para_data
*p
, font_cfg
*f
) {
1085 for (i
= 0; i
< NFONTS
; i
++) {
1086 p
->fonts
[i
] = f
->fonts
[i
];
1087 p
->sizes
[i
] = f
->font_size
;
1091 static para_data
*make_para_data(int ptype
, int paux
, int indent
, int rmargin
,
1092 word
*pkwtext
, word
*pkwtext2
, word
*pwords
,
1097 int extra_indent
, firstline_indent
, aux_indent
;
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;
1109 * Choose fonts for this paragraph.
1113 setfont(pdata
, &conf
->ftitle
);
1114 pdata
->outline_level
= 0;
1119 case para_UnnumberedChapter
:
1120 setfont(pdata
, &conf
->fchapter
);
1121 pdata
->outline_level
= 1;
1127 &conf
->fsect
[paux
>= conf
->nfsect ? conf
->nfsect
- 1 : paux
]);
1128 pdata
->outline_level
= 2 + paux
;
1132 case para_BiblioCited
:
1134 case para_NumberedList
:
1135 case para_DescribedThing
:
1136 case para_Description
:
1137 case para_Copyright
:
1138 setfont(pdata
, &conf
->fbase
);
1143 * Also select an indentation level depending on the
1144 * paragraph type (list paragraphs other than
1145 * para_DescribedThing need extra indent).
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.)
1151 if (ptype
== para_Bullet
||
1152 ptype
== para_NumberedList
||
1153 ptype
== para_Description
) {
1154 extra_indent
= firstline_indent
= conf
->indent_list
;
1156 extra_indent
= firstline_indent
= 0;
1160 * Find the auxiliary text for this paragraph.
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
1181 if (ptype
== para_Heading
|| ptype
== para_Subsect
) {
1185 len
= paper_width_simple(pdata
, pkwtext2
, conf
);
1186 aux_indent
= -len
- conf
->sect_num_left_space
;
1188 pdata
->outline_title
=
1189 prepare_outline_title(pkwtext2
, L
" ", pwords
);
1192 aux2
= fake_word(L
": ");
1195 firstline_indent
+= paper_width_simple(pdata
, aux
, conf
);
1196 firstline_indent
+= paper_width_simple(pdata
, aux2
, conf
);
1198 pdata
->outline_title
=
1199 prepare_outline_title(pkwtext
, L
": ", pwords
);
1205 * Auxiliary text consisting of a bullet.
1207 aux
= fake_word(conf
->bullet
);
1208 aux_indent
= indent
+ conf
->indent_list_bullet
;
1211 case para_NumberedList
:
1213 * Auxiliary text consisting of the number followed
1214 * by a (FIXME: configurable) full stop.
1217 aux2
= fake_word(L
".");
1218 aux_indent
= indent
+ conf
->indent_list_bullet
;
1221 case para_BiblioCited
:
1223 * Auxiliary text consisting of the bibliography
1224 * reference text, and a trailing space.
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
);
1234 if (pdata
->outline_level
>= 0 && !pdata
->outline_title
) {
1235 pdata
->outline_title
=
1236 prepare_outline_title(NULL
, NULL
, pwords
);
1239 wrap_paragraph(pdata
, pwords
, conf
->base_width
- rmargin
,
1240 indent
+ firstline_indent
,
1241 indent
+ extra_indent
, conf
);
1243 pdata
->first
->aux_text
= aux
;
1244 pdata
->first
->aux_text_2
= aux2
;
1245 pdata
->first
->aux_left_indent
= aux_indent
;
1248 * Line breaking penalties.
1255 case para_UnnumberedChapter
:
1257 * Fixed and large penalty for breaking straight
1258 * after a heading; corresponding bonus for
1259 * breaking straight before.
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;
1267 case para_DescribedThing
:
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
1275 pdata
->first
->penalty_before
= -200000;
1276 pdata
->last
->penalty_after
= 200000;
1281 * Most paragraph types: widow/orphan control by
1282 * discouraging breaking one line from the end of
1285 if (pdata
->first
!= pdata
->last
) {
1286 pdata
->first
->penalty_after
= 100000;
1287 pdata
->last
->penalty_before
= 100000;
1292 standard_line_spacing(pdata
, conf
);
1295 * Some kinds of section heading require a page break before
1296 * them and an underline after.
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
;
1313 static void standard_line_spacing(para_data
*pdata
, paper_conf
*conf
)
1318 * Set the line spacing for each line in this paragraph.
1320 for (ldata
= pdata
->first
; ldata
; ldata
= ldata
->next
) {
1321 if (ldata
== pdata
->first
)
1322 ldata
->space_before
= conf
->base_para_spacing
/ 2;
1324 ldata
->space_before
= conf
->base_leading
/ 2;
1325 if (ldata
== pdata
->last
)
1326 ldata
->space_after
= conf
->base_para_spacing
/ 2;
1328 ldata
->space_after
= conf
->base_leading
/ 2;
1329 ldata
->page_break
= FALSE
;
1333 static font_encoding
*new_font_encoding(font_data
*font
)
1338 fe
= snew(font_encoding
);
1341 if (font
->list
->tail
)
1342 font
->list
->tail
->next
= fe
;
1344 font
->list
->head
= fe
;
1345 font
->list
->tail
= fe
;
1348 fe
->free_pos
= 0x21;
1350 for (i
= 0; i
< 256; i
++) {
1351 fe
->vector
[i
] = NOGLYPH
;
1352 fe
->to_unicode
[i
] = 0xFFFF;
1358 static subfont_map_entry
*encode_glyph_at(glyph g
, wchar_t u
,
1359 font_encoding
*fe
, int pos
)
1361 subfont_map_entry
*sme
= snew(subfont_map_entry
);
1364 sme
->position
= pos
;
1365 fe
->vector
[pos
] = g
;
1366 fe
->to_unicode
[pos
] = u
;
1367 add234(fe
->font
->subfont_map
, sme
);
1371 static int new_sfmap_cmp(void *a
, void *b
)
1373 glyph ga
= *(glyph
*)a
;
1374 subfont_map_entry
*sb
= b
;
1375 glyph gb
= sb
->subfont
->vector
[sb
->position
];
1377 if (ga
< gb
) return -1;
1378 if (ga
> gb
) return 1;
1382 static subfont_map_entry
*encode_glyph(glyph g
, wchar_t u
, font_data
*font
)
1384 subfont_map_entry
*sme
;
1387 sme
= find234(font
->subfont_map
, &g
, new_sfmap_cmp
);
1388 if (sme
) return sme
;
1391 * This character is not yet in a subfont. Assign one.
1393 if (font
->latest_subfont
->free_pos
>= 0x100)
1394 font
->latest_subfont
= new_font_encoding(font
);
1396 c
= font
->latest_subfont
->free_pos
++;
1397 if (font
->latest_subfont
->free_pos
== 0x7F)
1398 font
->latest_subfont
->free_pos
= 0xA1;
1400 return encode_glyph_at(g
, u
, font
->latest_subfont
, c
);
1403 static int sfmap_cmp(void *a
, void *b
)
1405 subfont_map_entry
*sa
= a
, *sb
= b
;
1406 glyph ga
= sa
->subfont
->vector
[sa
->position
];
1407 glyph gb
= sb
->subfont
->vector
[sb
->position
];
1409 if (ga
< gb
) return -1;
1410 if (ga
> gb
) return 1;
1414 int width_cmp(void *a
, void *b
)
1416 glyph_width
const *wa
= a
, *wb
= b
;
1418 if (wa
->glyph
< wb
->glyph
)
1420 if (wa
->glyph
> wb
->glyph
)
1425 int kern_cmp(void *a
, void *b
)
1427 kern_pair
const *ka
= a
, *kb
= b
;
1429 if (ka
->left
< kb
->left
)
1431 if (ka
->left
> kb
->left
)
1433 if (ka
->right
< kb
->right
)
1435 if (ka
->right
> kb
->right
)
1440 int lig_cmp(void *a
, void *b
)
1442 ligature
const *la
= a
, *lb
= b
;
1444 if (la
->left
< lb
->left
)
1446 if (la
->left
> lb
->left
)
1448 if (la
->right
< lb
->right
)
1450 if (la
->right
> lb
->right
)
1455 static int utoglyph(font_info
const *fi
, wchar_t u
) {
1456 return (u
< 0 || u
> 0xFFFF ? NOGLYPH
: fi
->bmp
[u
]);
1459 void listfonts(void) {
1460 font_info
const *fi
;
1463 for (fi
= all_fonts
; fi
; fi
= fi
->next
)
1464 printf("%s\n", fi
->name
);
1467 static font_data
*make_std_font(font_list
*fontlist
, char const *name
)
1469 font_info
const *fi
;
1474 for (fe
= fontlist
->head
; fe
; fe
= fe
->next
)
1475 if (strcmp(fe
->font
->info
->name
, name
) == 0)
1478 for (fi
= all_fonts
; fi
; fi
= fi
->next
)
1479 if (strcmp(fi
->name
, name
) == 0) break;
1480 if (!fi
) return NULL
;
1482 f
= snew(font_data
);
1486 f
->subfont_map
= newtree234(sfmap_cmp
);
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.
1495 fe
= new_font_encoding(f
);
1496 fe
->free_pos
= 0xA1; /* only the top half is free */
1497 f
->latest_subfont
= fe
;
1499 for (i
= 0x20; i
<= 0x7E; i
++) {
1500 glyph g
= utoglyph(fi
, i
);
1502 encode_glyph_at(g
, i
, fe
, i
);
1508 /* NB: arguments are glyph numbers from font->bmp. */
1509 int find_width(font_data
*font
, glyph index
)
1512 glyph_width
const *w
;
1514 wantw
.glyph
= index
;
1515 w
= find234(font
->info
->widths
, &wantw
, NULL
);
1520 static int find_kern(font_data
*font
, int lindex
, int rindex
)
1523 kern_pair
const *kp
;
1525 if (lindex
== NOGLYPH
|| rindex
== NOGLYPH
)
1527 wantkp
.left
= lindex
;
1528 wantkp
.right
= rindex
;
1529 kp
= find234(font
->info
->kerns
, &wantkp
, NULL
);
1535 static int find_lig(font_data
*font
, int lindex
, int rindex
)
1538 ligature
const *lig
;
1540 if (lindex
== NOGLYPH
|| rindex
== NOGLYPH
)
1542 wantlig
.left
= lindex
;
1543 wantlig
.right
= rindex
;
1544 lig
= find234(font
->info
->ligs
, &wantlig
, NULL
);
1550 static int string_width(font_data
*font
, wchar_t const *string
, int *errs
,
1554 int nindex
, index
, oindex
, lindex
;
1560 index
= utoglyph(font
->info
, *string
);
1561 for (; *string
; string
++) {
1562 nindex
= utoglyph(font
->info
, string
[1]);
1564 if (index
== NOGLYPH
) {
1568 if (!(flags
& RS_NOLIG
) &&
1569 (lindex
= find_lig(font
, index
, nindex
)) != NOGLYPH
) {
1573 width
+= find_kern(font
, oindex
, index
) + find_width(font
, index
);
1582 static int paper_width_internal(void *vctx
, word
*word
, int *nspaces
);
1584 struct paper_width_ctx
{
1590 static int paper_width_list(void *vctx
, word
*text
, word
*end
, int *nspaces
) {
1592 while (text
&& text
!= end
) {
1593 w
+= paper_width_internal(vctx
, text
, nspaces
);
1599 static int paper_width_internal(void *vctx
, word
*word
, int *nspaces
)
1601 struct paper_width_ctx
*ctx
= (struct paper_width_ctx
*)vctx
;
1602 int style
, type
, findex
, width
, errs
;
1606 switch (word
->type
) {
1607 case word_HyperLink
:
1609 case word_UpperXref
:
1610 case word_LowerXref
:
1617 style
= towordstyle(word
->type
);
1618 type
= removeattr(word
->type
);
1620 findex
= (style
== word_Normal ? FONT_NORMAL
:
1621 style
== word_Emph ? FONT_EMPH
:
1624 if (style
== word_Code
|| style
== word_WeakCode
) flags
|= RS_NOLIG
;
1626 if (type
== word_Normal
) {
1628 } else if (type
== word_WhiteSpace
) {
1629 if (findex
!= FONT_CODE
) {
1632 return ctx
->minspacewidth
;
1635 } else /* if (type == word_Quote) */ {
1636 if (word
->aux
== quote_Open
)
1637 str
= ctx
->conf
->lquote
;
1639 str
= ctx
->conf
->rquote
;
1642 width
= string_width(ctx
->pdata
->fonts
[findex
], str
, &errs
, flags
);
1644 if (errs
&& word
->alt
)
1645 return paper_width_list(vctx
, word
->alt
, NULL
, nspaces
);
1647 return ctx
->pdata
->sizes
[findex
] * width
;
1650 static int paper_width(void *vctx
, word
*word
)
1652 return paper_width_internal(vctx
, word
, NULL
);
1655 static int paper_width_simple(para_data
*pdata
, word
*text
, paper_conf
*conf
)
1657 struct paper_width_ctx ctx
;
1661 (pdata
->sizes
[FONT_NORMAL
] *
1662 string_width(pdata
->fonts
[FONT_NORMAL
], L
" ", NULL
, 0));
1665 return paper_width_list(&ctx
, text
, NULL
, NULL
);
1668 static void wrap_paragraph(para_data
*pdata
, word
*words
,
1669 int w
, int i1
, int i2
, paper_conf
*conf
)
1671 wrappedline
*wrapping
, *p
;
1673 struct paper_width_ctx ctx
;
1677 * We're going to need to store the line height in every line
1678 * structure we generate.
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
;
1689 spacewidth
= (pdata
->sizes
[FONT_NORMAL
] *
1690 string_width(pdata
->fonts
[FONT_NORMAL
], L
" ", NULL
, 0));
1691 if (spacewidth
== 0) {
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.
1697 spacewidth
= pdata
->sizes
[FONT_NORMAL
] * UNITS_PER_PT
/ 2;
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.
1704 ctx
.minspacewidth
= spacewidth
* 3 / 5;
1708 wrapping
= wrap_para(words
, w
- i1
, w
- i2
, paper_width
, &ctx
, spacewidth
);
1711 * Having done the wrapping, we now concoct a set of line_data
1714 pdata
->first
= pdata
->last
= NULL
;
1716 for (p
= wrapping
; p
; p
= p
->next
) {
1719 int len
, wid
, spaces
;
1721 ldata
= snew(line_data
);
1723 ldata
->pdata
= pdata
;
1724 ldata
->first
= p
->begin
;
1725 ldata
->end
= p
->end
;
1726 ldata
->line_height
= line_height
;
1728 ldata
->xpos
= (p
== wrapping ? i1
: i2
);
1731 pdata
->last
->next
= ldata
;
1732 ldata
->prev
= pdata
->last
;
1734 pdata
->first
= ldata
;
1738 pdata
->last
= ldata
;
1741 len
= paper_width_list(&ctx
, ldata
->first
, ldata
->end
, &spaces
);
1742 wid
= (p
== wrapping ? w
- i1
: w
- i2
);
1745 ldata
->hshortfall
= wid
- len
;
1746 ldata
->nspaces
= spaces
;
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.
1753 ldata
->hshortfall
+= ctx
.minspacewidth
* spaces
;
1754 ldata
->hshortfall
-= spacewidth
* spaces
;
1755 ldata
->real_shortfall
= ldata
->hshortfall
;
1757 * Special case: on the last line of a paragraph, we
1758 * never stretch spaces.
1760 if (ldata
->hshortfall
> 0 && !p
->next
)
1761 ldata
->hshortfall
= 0;
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;
1771 static page_data
*page_breaks(line_data
*first
, line_data
*last
,
1772 int page_height
, int ncols
, int headspace
)
1776 int n
, n1
, this_height
;
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.
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.
1795 * Since my line_data structures are only used for this
1796 * purpose, I might as well just store the algorithm data
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
*);
1807 for (n
= 0; n
<= ncols
; n
++) {
1808 int minheight
, text
= 0, space
= 0;
1811 n1
= (n
< ncols ? n
+1 : ncols
);
1813 this_height
= page_height
- headspace
;
1815 this_height
= page_height
;
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 */
1823 if (m
->prev
->space_after
> 0)
1824 space
+= m
->prev
->space_after
;
1826 text
+= m
->prev
->space_after
;
1828 if (m
!= l
|| m
->page_break
) {
1829 if (m
->space_before
> 0)
1830 space
+= m
->space_before
;
1832 text
+= m
->space_before
;
1834 text
+= m
->line_height
;
1835 minheight
= text
+ space
;
1837 if (m
!= l
&& minheight
> this_height
)
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.
1846 if (m
->space_after
>= 0) {
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.
1855 if (m
!= last
&& m
->next
&& !m
->next
->page_break
)
1857 int x
= (this_height
- minheight
) / FUNITS_PER_PT
*
1865 cost
+= (x
* xf
) >> 8;
1869 if (m
!= last
&& m
->next
&& !m
->next
->page_break
) {
1870 cost
+= m
->penalty_after
;
1871 cost
+= m
->next
->penalty_before
;
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
) {
1878 * This is the best option yet for this
1881 l
->bestcost
[n
] = cost
;
1882 if (m
!= last
&& m
->next
&& !m
->next
->page_break
)
1883 l
->vshortfall
[n
] = this_height
- minheight
;
1885 l
->vshortfall
[n
] = 0;
1887 l
->space
[n
] = space
;
1888 l
->page_last
[n
] = m
;
1899 * Now go through the line list forwards and assemble the
1908 int text
, space
, head
;
1910 page
= snew(page_data
);
1919 page
->first_line
= l
;
1920 page
->last_line
= l
->page_last
[n
];
1922 page
->first_text
= page
->last_text
= NULL
;
1923 page
->first_xref
= page
->last_xref
= NULL
;
1924 page
->first_rect
= page
->last_rect
= NULL
;
1927 * Now assign a y-coordinate to each line on the page.
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
;
1936 text
+= l
->prev
->space_after
;
1938 if (l
!= page
->first_line
|| l
->page_break
) {
1939 if (l
->space_before
> 0)
1940 space
+= l
->space_before
;
1942 text
+= l
->space_before
;
1944 text
+= l
->line_height
;
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
];
1953 if (l
== page
->last_line
)
1957 l
= page
->last_line
;
1962 n
= (n
< ncols ? n
+1 : ncols
);
1968 static void add_rect_to_page(page_data
*page
, int x
, int y
, int w
, int h
)
1970 rect
*r
= snew(rect
);
1973 if (page
->last_rect
)
1974 page
->last_rect
->next
= r
;
1976 page
->first_rect
= r
;
1977 page
->last_rect
= r
;
1985 static void add_string_to_page(page_data
*page
, int x
, int y
,
1986 font_encoding
*fe
, int size
, char *text
,
1989 text_fragment
*frag
;
1991 frag
= snew(text_fragment
);
1994 if (page
->last_text
)
1995 page
->last_text
->next
= frag
;
1997 page
->first_text
= frag
;
1998 page
->last_text
= frag
;
2003 frag
->fontsize
= size
;
2004 frag
->text
= dupstr(text
);
2005 frag
->width
= width
;
2009 * Returns the updated x coordinate.
2011 static int render_string(page_data
*page
, font_data
*font
, int fontsize
,
2012 int x
, int y
, wchar_t *str
, unsigned flags
)
2015 int textpos
, textwid
, kern
, nglyph
, glyph
, oglyph
, lig
;
2016 font_encoding
*subfont
= NULL
, *sf
;
2017 subfont_map_entry
*sme
;
2019 text
= snewn(1 + ustrlen(str
), char);
2020 textpos
= textwid
= 0;
2023 nglyph
= utoglyph(font
->info
, *str
);
2027 nglyph
= utoglyph(font
->info
, str
[1]);
2029 if (glyph
== NOGLYPH
) {
2031 continue; /* nothing more we can do here */
2034 if (!(flags
& RS_NOLIG
) &&
2035 (lig
= find_lig(font
, glyph
, nglyph
)) != NOGLYPH
) {
2042 * Find which subfont this character is going in.
2044 sme
= encode_glyph(glyph
, *str
, font
);
2047 kern
= find_kern(font
, oglyph
, glyph
) * fontsize
;
2049 if (!subfont
|| sf
!= subfont
|| kern
) {
2051 text
[textpos
] = '\0';
2052 add_string_to_page(page
, x
, y
, subfont
, fontsize
, text
,
2054 x
+= textwid
+ kern
;
2056 assert(textpos
== 0);
2063 text
[textpos
++] = sme
->position
;
2064 textwid
+= find_width(font
, glyph
) * fontsize
;
2070 text
[textpos
] = '\0';
2071 add_string_to_page(page
, x
, y
, subfont
, fontsize
, text
, textwid
);
2079 * Returns the updated x coordinate.
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
)
2086 while (text
&& text
!= text_end
) {
2087 int style
, type
, findex
, errs
;
2092 switch (text
->type
) {
2094 * Start a cross-reference.
2096 case word_HyperLink
:
2097 case word_UpperXref
:
2098 case word_LowerXref
:
2101 if (text
->type
== word_HyperLink
) {
2103 dest
.url
= utoa_dup(text
->text
, CS_ASCII
);
2105 } else if (text
->type
== word_PageXref
) {
2108 dest
.page
= (page_data
*)text
->private_data
;
2110 keyword
*kwl
= kw_lookup(keywords
, text
->text
);
2114 assert(kwl
->para
->private_data
);
2115 pdata
= (para_data
*) kwl
->para
->private_data
;
2117 dest
.page
= pdata
->first
->page
;
2121 * Shouldn't happen, but *shrug*
2128 if (dest
.type
!= NONE
) {
2130 (*xr
)->dest
= dest
; /* structure copy */
2131 if (page
->last_xref
)
2132 page
->last_xref
->next
= *xr
;
2134 page
->first_xref
= *xr
;
2135 page
->last_xref
= *xr
;
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.
2145 (*xr
)->lx
= (*xr
)->rx
= x
;
2147 (*xr
)->ty
= y
+ ldata
->line_height
;
2152 * Finish extending a cross-reference box.
2160 * Add the current page number to the list of pages
2161 * referenced by an index entry.
2165 * We don't create index references in contents entries.
2167 if (!pdata
->contents_entry
) {
2171 tag
= index_findtag(idx
, text
->text
);
2175 for (i
= 0; i
< tag
->nrefs
; i
++) {
2176 indexentry
*entry
= tag
->refs
[i
];
2177 paper_idx
*pi
= (paper_idx
*)entry
->backend_data
;
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.
2184 if (pi
->lastpage
!= page
) {
2188 pi
->lastword
= pi
->lastword
->next
=
2190 pi
->lastword
= pi
->lastword
->next
=
2192 wp
= &pi
->lastword
->next
;
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
=
2204 pi
->lastpage
= page
;
2210 style
= towordstyle(text
->type
);
2211 type
= removeattr(text
->type
);
2213 findex
= (style
== word_Normal ? FONT_NORMAL
:
2214 style
== word_Emph ? FONT_EMPH
:
2217 if (style
== word_Code
|| style
== word_WeakCode
) flags
|= RS_NOLIG
;
2218 flags
|= pdata
->extraflags
;
2220 if (type
== word_Normal
) {
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
;
2231 } else /* if (type == word_Quote) */ {
2232 if (text
->aux
== quote_Open
)
2238 (void) string_width(pdata
->fonts
[findex
], str
, &errs
, flags
);
2240 if (errs
&& text
->alt
)
2241 x
= render_text(page
, pdata
, ldata
, x
, y
, text
->alt
, NULL
,
2242 xr
, shortfall
, nspaces
, nspace
, keywords
, idx
,
2245 x
= render_string(page
, pdata
->fonts
[findex
],
2246 pdata
->sizes
[findex
], x
, y
, str
, flags
);
2259 * Returns the last x position used on the line.
2261 static int render_line(line_data
*ldata
, int left_x
, int top_y
,
2262 xref_dest
*dest
, keywordlist
*keywords
, indexdata
*idx
,
2269 if (ldata
->aux_text
) {
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
);
2288 * There might be a cross-reference carried over from a
2291 if (dest
->type
!= NONE
) {
2294 xr
->dest
= *dest
; /* structure copy */
2295 if (ldata
->page
->last_xref
)
2296 ldata
->page
->last_xref
->next
= xr
;
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
;
2307 int extra_indent
, shortfall
, spaces
;
2308 int just
= ldata
->pdata
->justification
;
2311 * All forms of justification become JUST when we have
2312 * to squeeze the paragraph.
2314 if (ldata
->hshortfall
< 0)
2319 shortfall
= ldata
->hshortfall
;
2320 spaces
= ldata
->nspaces
;
2324 shortfall
= spaces
= extra_indent
= 0;
2327 shortfall
= spaces
= 0;
2328 extra_indent
= ldata
->real_shortfall
;
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
);
2341 * There's a cross-reference continued on to the next line.
2351 static void render_para(para_data
*pdata
, paper_conf
*conf
,
2352 keywordlist
*keywords
, indexdata
*idx
,
2353 paragraph
*index_placeholder
, page_data
*index_page
)
2357 page_data
*cxref_page
;
2366 for (ldata
= pdata
->first
; ldata
; ldata
= ldata
->next
) {
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.)
2372 if (pdata
->contents_entry
&& ldata
->page
!= cxref_page
) {
2373 cxref_page
= ldata
->page
;
2376 cxref
->dest
.type
= PAGE
;
2377 if (pdata
->contents_entry
== index_placeholder
) {
2378 cxref
->dest
.page
= index_page
;
2380 assert(pdata
->contents_entry
->private_data
);
2381 target
= (para_data
*)pdata
->contents_entry
->private_data
;
2382 cxref
->dest
.page
= target
->first
->page
;
2384 cxref
->dest
.url
= NULL
;
2385 if (ldata
->page
->last_xref
)
2386 ldata
->page
->last_xref
->next
= cxref
;
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
;
2395 if (pdata
->contents_entry
) {
2396 assert(cxref
!= NULL
);
2397 cxref
->by
= conf
->paper_height
- conf
->top_margin
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
)
2409 * If this is a contents entry, add leaders and a page
2412 if (pdata
->contents_entry
) {
2418 if (pdata
->contents_entry
== index_placeholder
) {
2419 num
= index_page
->number
;
2421 assert(pdata
->contents_entry
->private_data
);
2422 target
= (para_data
*)pdata
->contents_entry
->private_data
;
2423 num
= target
->first
->page
->number
;
2427 wid
= paper_width_simple(pdata
, w
, conf
);
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);
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);
2449 * Render any rectangle (chapter title underline or rule)
2450 * that goes with this paragraph.
2452 switch (pdata
->rect_type
) {
2453 case RECT_CHAPTER_UNDERLINE
:
2454 add_rect_to_page(pdata
->last
->page
,
2456 (conf
->paper_height
- conf
->top_margin
-
2458 conf
->chapter_underline_depth
),
2460 conf
->chapter_underline_thickness
);
2463 add_rect_to_page(pdata
->first
->page
,
2464 conf
->left_margin
+ pdata
->first
->xpos
,
2465 (conf
->paper_height
- conf
->top_margin
-
2467 pdata
->last
->line_height
),
2468 conf
->base_width
- pdata
->first
->xpos
,
2469 pdata
->last
->line_height
);
2471 default: /* placate gcc */
2476 static para_data
*code_paragraph(int indent
, word
*words
, paper_conf
*conf
)
2478 para_data
*pdata
= snew(para_data
);
2481 * For code paragraphs, I'm going to hack grievously and
2482 * pretend the three normal fonts are the three code paragraph
2485 setfont(pdata
, &conf
->fcode
);
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
;
2494 for (; words
; words
= words
->next
) {
2495 wchar_t *t
, *e
, *start
;
2496 word
*lhead
= NULL
, *ltail
= NULL
, *w
;
2498 int prev
= -1, curr
;
2501 if (words
->next
&& words
->next
->type
== word_Emph
) {
2502 e
= words
->next
->text
;
2503 words
= words
->next
;
2513 else if (*e
== L
'i')
2515 else if (*e
== L
'b')
2532 * We've isolated a maximal subsequence of the line
2533 * which has the same emphasis. Form it into a word
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';
2556 ldata
= snew(line_data
);
2558 ldata
->pdata
= pdata
;
2559 ldata
->first
= lhead
;
2561 ldata
->line_height
= conf
->fcode
.font_size
* UNITS_PER_PT
;
2563 ldata
->xpos
= indent
;
2566 pdata
->last
->next
= ldata
;
2567 ldata
->prev
= pdata
->last
;
2569 pdata
->first
= ldata
;
2573 pdata
->last
= ldata
;
2575 ldata
->hshortfall
= 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;
2584 standard_line_spacing(pdata
, conf
);
2589 static para_data
*rule_paragraph(int indent
, paper_conf
*conf
)
2591 para_data
*pdata
= snew(para_data
);
2594 ldata
= snew(line_data
);
2596 ldata
->pdata
= pdata
;
2597 ldata
->first
= NULL
;
2599 ldata
->line_height
= conf
->rule_thickness
;
2601 ldata
->xpos
= indent
;
2606 ldata
->hshortfall
= 0;
2608 ldata
->aux_text
= NULL
;
2609 ldata
->aux_text_2
= NULL
;
2610 ldata
->aux_left_indent
= 0;
2613 * Better to break after a rule than before it
2615 ldata
->penalty_after
+= 100000;
2616 ldata
->penalty_before
+= -100000;
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;
2625 standard_line_spacing(pdata
, conf
);
2631 * Plain-text-like formatting for outline titles.
2633 static void paper_rdaddw(rdstring
*rs
, word
*text
) {
2634 for (; text
; text
= text
->next
) switch (text
->type
) {
2635 case word_HyperLink
:
2637 case word_UpperXref
:
2638 case word_LowerXref
:
2647 case word_WhiteSpace
:
2648 case word_EmphSpace
:
2649 case word_CodeSpace
:
2650 case word_WkCodeSpace
:
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
) {
2669 } else if (removeattr(text
->type
) == word_Quote
) {
2670 rdadd(rs
, L
'\''); /* fixme: configurability */
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 */
2684 static wchar_t *prepare_outline_title(word
*first
, wchar_t *separator
,
2687 rdstring rs
= {0, 0, NULL
};
2690 paper_rdaddw(&rs
, first
);
2692 rdadds(&rs
, separator
);
2694 paper_rdaddw(&rs
, second
);
2699 static word
*fake_word(wchar_t *text
)
2701 word
*ret
= snew(word
);
2704 ret
->type
= word_Normal
;
2705 ret
->text
= ustrdup(text
);
2706 ret
->breaks
= FALSE
;
2711 static word
*fake_space_word(void)
2713 word
*ret
= snew(word
);
2716 ret
->type
= word_WhiteSpace
;
2723 static word
*fake_page_ref(page_data
*page
)
2725 word
*ret
= snew(word
);
2728 ret
->type
= word_PageXref
;
2730 ret
->breaks
= FALSE
;
2732 ret
->private_data
= page
;
2736 static word
*fake_end_ref(void)
2738 word
*ret
= snew(word
);
2741 ret
->type
= word_XrefEnd
;
2743 ret
->breaks
= FALSE
;
2748 static word
*prepare_contents_title(word
*first
, wchar_t *separator
,
2757 w
= dup_word_list(first
);
2765 w
= fake_word(separator
);
2771 *wptr
= dup_word_list(second
);
2777 static void fold_into_page(page_data
*dest
, page_data
*src
, int right_shift
)
2781 if (!src
->first_line
)
2784 if (dest
->last_line
) {
2785 dest
->last_line
->next
= src
->first_line
;
2786 src
->first_line
->prev
= dest
->last_line
;
2788 dest
->last_line
= src
->last_line
;
2790 for (ldata
= src
->first_line
; ldata
; ldata
= ldata
->next
) {
2792 ldata
->xpos
+= right_shift
;
2794 if (ldata
== src
->last_line
)