+ int page_height, int ncols, int headspace);
+static int render_string(page_data *page, font_data *font, int fontsize,
+ int x, int y, wchar_t *str);
+static int render_line(line_data *ldata, int left_x, int top_y,
+ xref_dest *dest, keywordlist *keywords, indexdata *idx,
+ paper_conf *conf);
+static void render_para(para_data *pdata, paper_conf *conf,
+ keywordlist *keywords, indexdata *idx,
+ paragraph *index_placeholder, page_data *index_page);
+static int string_width(font_data *font, wchar_t const *string, int *errs);
+static int paper_width_simple(para_data *pdata, word *text, paper_conf *conf);
+static para_data *code_paragraph(int indent, word *words, paper_conf *conf);
+static para_data *rule_paragraph(int indent, paper_conf *conf);
+static void add_rect_to_page(page_data *page, int x, int y, int w, int h);
+static para_data *make_para_data(int ptype, int paux, int indent, int rmargin,
+ word *pkwtext, word *pkwtext2, word *pwords,
+ paper_conf *conf);
+static void standard_line_spacing(para_data *pdata, paper_conf *conf);
+static wchar_t *prepare_outline_title(word *first, wchar_t *separator,
+ word *second);
+static word *fake_word(wchar_t *text);
+static word *fake_space_word(void);
+static word *fake_page_ref(page_data *page);
+static word *fake_end_ref(void);
+static word *prepare_contents_title(word *first, wchar_t *separator,
+ word *second);
+static void fold_into_page(page_data *dest, page_data *src, int right_shift);
+
+static int fonts_ok(wchar_t *string, ...)
+{
+ font_data *font;
+ va_list ap;
+ int ret = TRUE;
+
+ va_start(ap, string);
+ while ( (font = va_arg(ap, font_data *)) != NULL) {
+ int errs;
+ (void) string_width(font, string, &errs);
+ if (errs) {
+ ret = FALSE;
+ break;
+ }
+ }
+ va_end(ap);
+
+ return ret;
+}
+
+static void paper_cfg_fonts(font_data **fonts, font_list *fontlist,
+ wchar_t *wp, filepos *fpos) {
+ font_data *f;
+ char *fn;
+ int i;
+
+ for (i = 0; i < NFONTS && *wp; i++, wp = uadv(wp)) {
+ fn = utoa_dup(wp, CS_ASCII);
+ f = make_std_font(fontlist, fn);
+ if (f)
+ fonts[i] = f;
+ else
+ /* FIXME: proper error */
+ error(err_nofont, fpos, wp);
+ }
+}
+
+static paper_conf paper_configure(paragraph *source, font_list *fontlist) {
+ paragraph *p;
+ paper_conf ret;
+
+ /*
+ * Defaults.
+ */
+ ret.paper_width = 595 * UNITS_PER_PT;
+ ret.paper_height = 842 * UNITS_PER_PT;
+ ret.left_margin = 72 * UNITS_PER_PT;
+ ret.top_margin = 72 * UNITS_PER_PT;
+ ret.right_margin = 72 * UNITS_PER_PT;
+ ret.bottom_margin = 108 * UNITS_PER_PT;
+ ret.indent_list_bullet = 6 * UNITS_PER_PT;
+ ret.indent_list_after = 18 * UNITS_PER_PT;
+ ret.indent_quote = 18 * UNITS_PER_PT;
+ ret.base_leading = UNITS_PER_PT;
+ ret.base_para_spacing = 10 * UNITS_PER_PT;
+ ret.chapter_top_space = 72 * UNITS_PER_PT;
+ ret.sect_num_left_space = 12 * UNITS_PER_PT;
+ ret.chapter_underline_depth = 14 * UNITS_PER_PT;
+ ret.chapter_underline_thickness = 3 * UNITS_PER_PT;
+ ret.rule_thickness = 1 * UNITS_PER_PT;
+ ret.fbase.font_size = 12;
+ ret.fbase.fonts[FONT_NORMAL] = make_std_font(fontlist, "Times-Roman");
+ ret.fbase.fonts[FONT_EMPH] = make_std_font(fontlist, "Times-Italic");
+ ret.fbase.fonts[FONT_CODE] = make_std_font(fontlist, "Courier");
+ ret.fcode.font_size = 12;
+ ret.fcode.fonts[FONT_NORMAL] = make_std_font(fontlist, "Courier-Bold");
+ ret.fcode.fonts[FONT_EMPH] = make_std_font(fontlist, "Courier-Oblique");
+ ret.fcode.fonts[FONT_CODE] = make_std_font(fontlist, "Courier");
+ ret.ftitle.font_size = 24;
+ ret.ftitle.fonts[FONT_NORMAL] = make_std_font(fontlist, "Helvetica-Bold");
+ ret.ftitle.fonts[FONT_EMPH] =
+ make_std_font(fontlist, "Helvetica-BoldOblique");
+ ret.ftitle.fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
+ ret.fchapter.font_size = 20;
+ ret.fchapter.fonts[FONT_NORMAL]= make_std_font(fontlist, "Helvetica-Bold");
+ ret.fchapter.fonts[FONT_EMPH] =
+ make_std_font(fontlist, "Helvetica-BoldOblique");
+ ret.fchapter.fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
+ ret.nfsect = 3;
+ ret.fsect = snewn(ret.nfsect, font_cfg);
+ ret.fsect[0].font_size = 16;
+ ret.fsect[0].fonts[FONT_NORMAL]= make_std_font(fontlist, "Helvetica-Bold");
+ ret.fsect[0].fonts[FONT_EMPH] =
+ make_std_font(fontlist, "Helvetica-BoldOblique");
+ ret.fsect[0].fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
+ ret.fsect[1].font_size = 14;
+ ret.fsect[1].fonts[FONT_NORMAL]= make_std_font(fontlist, "Helvetica-Bold");
+ ret.fsect[1].fonts[FONT_EMPH] =
+ make_std_font(fontlist, "Helvetica-BoldOblique");
+ ret.fsect[1].fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
+ ret.fsect[2].font_size = 13;
+ ret.fsect[2].fonts[FONT_NORMAL]= make_std_font(fontlist, "Helvetica-Bold");
+ ret.fsect[2].fonts[FONT_EMPH] =
+ make_std_font(fontlist, "Helvetica-BoldOblique");
+ ret.fsect[2].fonts[FONT_CODE] = make_std_font(fontlist, "Courier-Bold");
+ ret.contents_indent_step = 24 * UNITS_PER_PT;
+ ret.contents_margin = 84 * UNITS_PER_PT;
+ ret.leader_separation = 12 * UNITS_PER_PT;
+ ret.index_gutter = 36 * UNITS_PER_PT;
+ ret.index_cols = 2;
+ ret.index_minsep = 18 * UNITS_PER_PT;
+ ret.pagenum_fontsize = 12;
+ ret.footer_distance = 32 * UNITS_PER_PT;
+ ret.lquote = L"\x2018\0\x2019\0'\0'\0\0";
+ ret.rquote = uadv(ret.lquote);
+ ret.bullet = L"\x2022\0-\0\0";
+ ret.contents_text = L"Contents";
+ ret.index_text = L"Index";
+
+ /*
+ * Two-pass configuration so that we can pick up global config
+ * (e.g. `quotes') before having it overridden by specific
+ * config (`paper-quotes'), irrespective of the order in which
+ * they occur.
+ */
+ for (p = source; p; p = p->next) {
+ if (p->type == para_Config) {
+ if (!ustricmp(p->keyword, L"quotes")) {
+ if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) {
+ ret.lquote = uadv(p->keyword);
+ ret.rquote = uadv(ret.lquote);
+ }
+ }
+ }
+ }
+
+ for (p = source; p; p = p->next) {
+ p->private_data = NULL;
+ if (p->type == para_Config) {
+ if (!ustricmp(p->keyword, L"paper-quotes")) {
+ if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) {
+ ret.lquote = uadv(p->keyword);
+ ret.rquote = uadv(ret.lquote);
+ }
+ } else if (!ustricmp(p->keyword, L"contents")) {
+ ret.contents_text = uadv(p->keyword);
+ } else if (!ustricmp(p->keyword, L"index")) {
+ ret.index_text = uadv(p->keyword);
+ } else if (!ustricmp(p->keyword, L"paper-bullet")) {
+ ret.bullet = uadv(p->keyword);
+ } else if (!ustricmp(p->keyword, L"paper-page-width")) {
+ ret.paper_width =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-page-height")) {
+ ret.paper_height =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-left-margin")) {
+ ret.left_margin =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-top-margin")) {
+ ret.top_margin =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-right-margin")) {
+ ret.right_margin =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-bottom-margin")) {
+ ret.bottom_margin =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-list-indent")) {
+ ret.indent_list_bullet =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-listitem-indent")) {
+ ret.indent_list =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-quote-indent")) {
+ ret.indent_quote =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-base-leading")) {
+ ret.base_leading =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-base-para-spacing")) {
+ ret.base_para_spacing =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-chapter-top-space")) {
+ ret.chapter_top_space =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-sect-num-left-space")) {
+ ret.sect_num_left_space =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-chapter-underline-depth")) {
+ ret.chapter_underline_depth =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-chapter-underline-thickness")) {
+ ret.chapter_underline_thickness =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-rule-thickness")) {
+ ret.rule_thickness =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-contents-indent-step")) {
+ ret.contents_indent_step =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-contents-margin")) {
+ ret.contents_margin =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-leader-separation")) {
+ ret.leader_separation =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-index-gutter")) {
+ ret.index_gutter =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-index-minsep")) {
+ ret.index_minsep =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-footer-distance")) {
+ ret.footer_distance =
+ (int) 0.5 + FUNITS_PER_PT * utof(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-base-font-size")) {
+ ret.fbase.font_size = utoi(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-index-columns")) {
+ ret.index_cols = utoi(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-pagenum-font-size")) {
+ ret.pagenum_fontsize = utoi(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-base-fonts")) {
+ paper_cfg_fonts(ret.fbase.fonts, fontlist, uadv(p->keyword),
+ &p->fpos);
+ } else if (!ustricmp(p->keyword, L"paper-code-font-size")) {
+ ret.fcode.font_size = utoi(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-code-fonts")) {
+ paper_cfg_fonts(ret.fcode.fonts, fontlist, uadv(p->keyword),
+ &p->fpos);
+ } else if (!ustricmp(p->keyword, L"paper-title-font-size")) {
+ ret.ftitle.font_size = utoi(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-title-fonts")) {
+ paper_cfg_fonts(ret.ftitle.fonts, fontlist, uadv(p->keyword),
+ &p->fpos);
+ } else if (!ustricmp(p->keyword, L"paper-chapter-font-size")) {
+ ret.fchapter.font_size = utoi(uadv(p->keyword));
+ } else if (!ustricmp(p->keyword, L"paper-chapter-fonts")) {
+ paper_cfg_fonts(ret.fchapter.fonts, fontlist, uadv(p->keyword),
+ &p->fpos);
+ } else if (!ustricmp(p->keyword, L"paper-section-font-size")) {
+ wchar_t *q = uadv(p->keyword);
+ int n = 0;
+ if (uisdigit(*q)) {
+ n = utoi(q);
+ q = uadv(q);
+ }
+ if (n >= ret.nfsect) {
+ int i;
+ ret.fsect = sresize(ret.fsect, n+1, font_cfg);
+ for (i = ret.nfsect; i <= n; i++)
+ ret.fsect[i] = ret.fsect[ret.nfsect-1];
+ ret.nfsect = n+1;
+ }
+ ret.fsect[n].font_size = utoi(q);
+ } else if (!ustricmp(p->keyword, L"paper-section-fonts")) {
+ wchar_t *q = uadv(p->keyword);
+ int n = 0;
+ if (uisdigit(*q)) {
+ n = utoi(q);
+ q = uadv(q);
+ }
+ if (n >= ret.nfsect) {
+ int i;
+ ret.fsect = sresize(ret.fsect, n+1, font_cfg);
+ for (i = ret.nfsect; i <= n; i++)
+ ret.fsect[i] = ret.fsect[ret.nfsect-1];
+ ret.nfsect = n+1;
+ }
+ paper_cfg_fonts(ret.fsect[n].fonts, fontlist, q, &p->fpos);
+ }
+ }
+ }
+
+ /*
+ * Set up the derived fields in the conf structure.
+ */
+
+ ret.base_width =
+ ret.paper_width - ret.left_margin - ret.right_margin;
+ ret.page_height =
+ ret.paper_height - ret.top_margin - ret.bottom_margin;
+ ret.indent_list = ret.indent_list_bullet + ret.indent_list_after;
+ ret.index_colwidth =
+ (ret.base_width - (ret.index_cols-1) * ret.index_gutter)
+ / ret.index_cols;
+
+ /*
+ * Now process fallbacks on quote characters and bullets. We
+ * use string_width() to determine whether all of the relevant
+ * fonts contain the same character, and fall back whenever we
+ * find a character which not all of them support.
+ */
+
+ /* Quote characters need not be supported in the fixed code fonts,
+ * but must be in the title and body fonts. */
+ while (*uadv(ret.rquote) && *uadv(uadv(ret.rquote))) {
+ int n;
+ if (fonts_ok(ret.lquote,
+ ret.fbase.fonts[FONT_NORMAL],
+ ret.fbase.fonts[FONT_EMPH],
+ ret.ftitle.fonts[FONT_NORMAL],
+ ret.ftitle.fonts[FONT_EMPH],
+ ret.fchapter.fonts[FONT_NORMAL],
+ ret.fchapter.fonts[FONT_EMPH], NULL) &&
+ fonts_ok(ret.rquote,
+ ret.fbase.fonts[FONT_NORMAL],
+ ret.fbase.fonts[FONT_EMPH],
+ ret.ftitle.fonts[FONT_NORMAL],
+ ret.ftitle.fonts[FONT_EMPH],
+ ret.fchapter.fonts[FONT_NORMAL],
+ ret.fchapter.fonts[FONT_EMPH], NULL)) {
+ for (n = 0; n < ret.nfsect; n++)
+ if (!fonts_ok(ret.lquote,
+ ret.fsect[n].fonts[FONT_NORMAL],
+ ret.fsect[n].fonts[FONT_EMPH], NULL) ||
+ !fonts_ok(ret.rquote,
+ ret.fsect[n].fonts[FONT_NORMAL],
+ ret.fsect[n].fonts[FONT_EMPH], NULL))
+ break;
+ if (n == ret.nfsect)
+ break;
+ }
+ ret.lquote = uadv(ret.rquote);
+ ret.rquote = uadv(ret.lquote);
+ }
+
+ /* The bullet character only needs to be supported in the normal body
+ * font (not even in italics). */
+ while (*ret.bullet && *uadv(ret.bullet) &&
+ !fonts_ok(ret.bullet, ret.fbase.fonts[FONT_NORMAL], NULL))
+ ret.bullet = uadv(ret.bullet);
+
+ return ret;
+}