From 2bfd1b7641285ff4110e9405888457f54b5cdbb2 Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 14 Apr 2004 13:14:12 +0000 Subject: [PATCH] Support for a contents section. git-svn-id: svn://svn.tartarus.org/sgt/halibut@4069 cda61777-01e9-0310-a592-d414129be87e --- bk_paper.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- paper.h | 8 ++ 2 files changed, 239 insertions(+), 45 deletions(-) diff --git a/bk_paper.c b/bk_paper.c index 1d4696c..5d500ff 100644 --- a/bk_paper.c +++ b/bk_paper.c @@ -62,6 +62,9 @@ struct paper_conf_Tag { int chapter_underline_thickness; int rule_thickness; int base_font_size; + int contents_indent_step; + int contents_margin; + int leader_separation; /* These are derived from the above */ int base_width; int page_height; @@ -74,26 +77,32 @@ static void wrap_paragraph(para_data *pdata, word *words, int w, int i1, int i2); static page_data *page_breaks(line_data *first, line_data *last, int page_height); -static void render_line(line_data *ldata, int left_x, int top_y, - xref_dest *dest, keywordlist *keywords); +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); static int paper_width_simple(para_data *pdata, word *text); 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, +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 *prepare_contents_title(word *first, wchar_t *separator, + word *second); void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, indexdata *idx) { paragraph *p; document *doc; - int indent; + int indent, used_contents; para_data *pdata, *firstpara = NULL, *lastpara = NULL; - line_data *ldata, *firstline, *lastline; + para_data *firstcont, *lastcont; + line_data *ldata, *firstline, *lastline, *firstcontline, *lastcontline; page_data *pages; font_list *fontlist; paper_conf *conf; @@ -119,6 +128,9 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, conf->chapter_underline_thickness = 3 * 4096; conf->rule_thickness = 1 * 4096; conf->base_font_size = 12; + conf->contents_indent_step = 24 * 4096; + conf->contents_margin = 84 * 4096; + conf->leader_separation = 12 * 4096; conf->base_width = conf->paper_width - conf->left_margin - conf->right_margin; @@ -141,9 +153,78 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, conf->cb = make_std_font(fontlist, "Courier-Bold"); /* - * Go through and break up each paragraph into lines. + * Format the contents entry for each heading. + */ + { + word *contents_title; + contents_title = fake_word(L"Contents"); + + firstcont = make_para_data(para_UnnumberedChapter, 0, 0, 0, + NULL, NULL, contents_title, conf); + lastcont = firstcont; + lastcont->next = NULL; + firstcontline = firstcont->first; + lastcontline = lastcont->last; + for (p = sourceform; p; p = p->next) { + word *words; + int indent; + + switch (p->type) { + case para_Chapter: + case para_Appendix: + case para_UnnumberedChapter: + case para_Heading: + case para_Subsect: + switch (p->type) { + case para_Chapter: + case para_Appendix: + words = prepare_contents_title(p->kwtext, L": ", p->words); + indent = 0; + break; + case para_UnnumberedChapter: + words = prepare_contents_title(NULL, NULL, p->words); + indent = 0; + break; + case para_Heading: + case para_Subsect: + words = prepare_contents_title(p->kwtext2, L" ", p->words); + indent = (p->aux + 1) * conf->contents_indent_step; + break; + } + pdata = make_para_data(para_Normal, p->aux, indent, + conf->contents_margin, + NULL, NULL, words, conf); + pdata->next = NULL; + pdata->contents_entry = p; + lastcont->next = pdata; + lastcont = pdata; + + /* + * Link all contents line structures together into + * a big list. + */ + if (pdata->first) { + if (lastcontline) { + lastcontline->next = pdata->first; + pdata->first->prev = lastcontline; + } else { + firstcontline = pdata->first; + pdata->first->prev = NULL; + } + lastcontline = pdata->last; + lastcontline->next = NULL; + } + + break; + } + } + } + + /* + * Do the main paragraph formatting. */ indent = 0; + used_contents = FALSE; firstline = lastline = NULL; for (p = sourceform; p; p = p->next) { p->private_data = NULL; @@ -217,7 +298,7 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, case para_Description: case para_Copyright: case para_Title: - pdata = make_para_data(p->type, p->aux, indent, + pdata = make_para_data(p->type, p->aux, indent, 0, p->kwtext, p->kwtext2, p->words, conf); p->private_data = pdata; @@ -229,6 +310,31 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, pdata = (para_data *)p->private_data; /* + * If this is the first non-title heading, we link the + * contents section in before it. + */ + if (!used_contents && pdata->outline_level > 0) { + used_contents = TRUE; + if (lastpara) + lastpara->next = firstcont; + else + firstpara = firstcont; + lastpara = lastcont; + assert(lastpara->next == NULL); + + if (lastline) { + lastline->next = firstcontline; + firstcontline->prev = lastline; + } else { + firstline = firstcontline; + firstcontline->prev = NULL; + } + assert(lastcontline != NULL); + lastline = lastcontline; + lastline->next = NULL; + } + + /* * Link all line structures together into a big list. */ if (pdata->first) { @@ -240,6 +346,7 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, pdata->first->prev = NULL; } lastline = pdata->last; + lastline->next = NULL; } /* @@ -261,22 +368,73 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, pages = page_breaks(firstline, lastline, conf->page_height); /* + * Number the pages. + */ + { + page_data *page; + int num = 0; + for (page = pages; page; page = page->next) { + char buf[40]; + sprintf(buf, "%d", ++num); + page->number = ufroma_dup(buf); + } + } + + /* * Now we're ready to actually lay out the pages. We do this by * looping over _paragraphs_, since we may need to track cross- * references between lines and even across pages. */ for (pdata = firstpara; pdata; pdata = pdata->next) { + int last_x; xref_dest dest; dest.type = NONE; for (ldata = pdata->first; ldata; ldata = ldata->next) { - render_line(ldata, conf->left_margin, - conf->paper_height - conf->top_margin, - &dest, keywords); + last_x = render_line(ldata, conf->left_margin, + conf->paper_height - conf->top_margin, + &dest, keywords); if (ldata == pdata->last) break; } /* + * If this is a contents entry, add leaders and a page + * number. + */ + if (pdata->contents_entry) { + word *w; + para_data *target; + wchar_t *num; + int wid; + int x; + + assert(pdata->contents_entry->private_data); + target = (para_data *)pdata->contents_entry->private_data; + num = target->first->page->number; + + w = fake_word(num); + wid = paper_width_simple(pdata, w); + sfree(w); + + render_string(pdata->last->page, + pdata->fonts[FONT_NORMAL], + pdata->sizes[FONT_NORMAL], + conf->paper_width - conf->right_margin - wid, + (conf->paper_height - conf->top_margin - + pdata->last->ypos), num); + + for (x = 0; x < conf->base_width; x += conf->leader_separation) + if (x - conf->leader_separation > last_x - conf->left_margin && + x + conf->leader_separation < conf->base_width - wid) + render_string(pdata->last->page, + pdata->fonts[FONT_NORMAL], + pdata->sizes[FONT_NORMAL], + conf->left_margin + x, + (conf->paper_height - conf->top_margin - + pdata->last->ypos), L"."); + } + + /* * Render any rectangle (chapter title underline or rule) * that goes with this paragraph. */ @@ -358,7 +516,7 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, return doc; } -static para_data *make_para_data(int ptype, int paux, int indent, +static para_data *make_para_data(int ptype, int paux, int indent, int rmargin, word *pkwtext, word *pkwtext2, word *pwords, paper_conf *conf) { @@ -371,6 +529,7 @@ static para_data *make_para_data(int ptype, int paux, int indent, pdata->outline_level = -1; pdata->outline_title = NULL; pdata->rect_type = RECT_NONE; + pdata->contents_entry = NULL; /* * Choose fonts for this paragraph. @@ -479,13 +638,7 @@ static para_data *make_para_data(int ptype, int paux, int indent, prepare_outline_title(pkwtext2, L" ", pwords); } else { aux = pkwtext; - aux2 = mknew(word); - aux2->next = NULL; - aux2->alt = NULL; - aux2->type = word_Normal; - aux2->text = ustrdup(L": "); - aux2->breaks = FALSE; - aux2->aux = 0; + aux2 = fake_word(L": "); aux_indent = 0; firstline_indent += paper_width_simple(pdata, aux); @@ -501,13 +654,7 @@ static para_data *make_para_data(int ptype, int paux, int indent, * Auxiliary text consisting of a bullet. (FIXME: * configurable bullet.) */ - aux = mknew(word); - aux->next = NULL; - aux->alt = NULL; - aux->type = word_Normal; - aux->text = ustrdup(L"\x2022"); - aux->breaks = FALSE; - aux->aux = 0; + aux = fake_word(L"\x2022"); aux_indent = indent + conf->indent_list_bullet; break; @@ -517,13 +664,7 @@ static para_data *make_para_data(int ptype, int paux, int indent, * by a (FIXME: configurable) full stop. */ aux = pkwtext; - aux2 = mknew(word); - aux2->next = NULL; - aux2->alt = NULL; - aux2->type = word_Normal; - aux2->text = ustrdup(L"."); - aux2->breaks = FALSE; - aux2->aux = 0; + aux2 = fake_word(L"."); aux_indent = indent + conf->indent_list_bullet; break; @@ -533,13 +674,7 @@ static para_data *make_para_data(int ptype, int paux, int indent, * reference text, and a trailing space. */ aux = pkwtext; - aux2 = mknew(word); - aux2->next = NULL; - aux2->alt = NULL; - aux2->type = word_Normal; - aux2->text = ustrdup(L" "); - aux2->breaks = FALSE; - aux2->aux = 0; + aux2 = fake_word(L" "); aux_indent = indent; firstline_indent += paper_width_simple(pdata, aux); firstline_indent += paper_width_simple(pdata, aux2); @@ -551,7 +686,7 @@ static para_data *make_para_data(int ptype, int paux, int indent, prepare_outline_title(NULL, NULL, pwords); } - wrap_paragraph(pdata, pwords, conf->base_width, + wrap_paragraph(pdata, pwords, conf->base_width - rmargin, indent + firstline_indent, indent + extra_indent); @@ -1308,11 +1443,15 @@ static int render_text(page_data *page, para_data *pdata, line_data *ldata, return x; } -static void render_line(line_data *ldata, int left_x, int top_y, - xref_dest *dest, keywordlist *keywords) +/* + * Returns the last x position used on the line. + */ +static int render_line(line_data *ldata, int left_x, int top_y, + xref_dest *dest, keywordlist *keywords) { int nspace; xref *xr; + int ret = 0; if (ldata->aux_text) { int x; @@ -1349,9 +1488,11 @@ static void render_line(line_data *ldata, int left_x, int top_y, } else xr = NULL; - render_text(ldata->page, ldata->pdata, ldata, left_x + ldata->xpos, - top_y - ldata->ypos, ldata->first, ldata->end, &xr, - ldata->hshortfall, ldata->nspaces, &nspace, keywords); + ret = render_text(ldata->page, ldata->pdata, ldata, + left_x + ldata->xpos, + top_y - ldata->ypos, ldata->first, ldata->end, &xr, + ldata->hshortfall, ldata->nspaces, &nspace, + keywords); if (xr) { /* @@ -1361,6 +1502,8 @@ static void render_line(line_data *ldata, int left_x, int top_y, } else dest->type = NONE; } + + return ret; } static para_data *code_paragraph(int indent, word *words, paper_conf *conf) @@ -1382,6 +1525,7 @@ static para_data *code_paragraph(int indent, word *words, paper_conf *conf) pdata->first = pdata->last = NULL; pdata->outline_level = -1; pdata->rect_type = RECT_NONE; + pdata->contents_entry = NULL; for (; words; words = words->next) { wchar_t *t, *e, *start; @@ -1510,6 +1654,7 @@ static para_data *rule_paragraph(int indent, paper_conf *conf) pdata->first = pdata->last = ldata; pdata->outline_level = -1; pdata->rect_type = RECT_RULE; + pdata->contents_entry = NULL; standard_line_spacing(pdata, conf); @@ -1584,3 +1729,44 @@ static wchar_t *prepare_outline_title(word *first, wchar_t *separator, return rs.text; } + +static word *fake_word(wchar_t *text) +{ + word *ret = mknew(word); + ret->next = NULL; + ret->alt = NULL; + ret->type = word_Normal; + ret->text = ustrdup(text); + ret->breaks = FALSE; + ret->aux = 0; + return ret; +} + +static word *prepare_contents_title(word *first, wchar_t *separator, + word *second) +{ + word *ret; + word **wptr, *w; + + wptr = &ret; + + if (first) { + w = dup_word_list(first); + *wptr = w; + while (w->next) + w = w->next; + wptr = &w->next; + } + + if (separator) { + w = fake_word(separator); + *wptr = w; + wptr = &w->next; + } + + if (second) { + *wptr = dup_word_list(second); + } + + return ret; +} diff --git a/paper.h b/paper.h index 160bff5..fe2e795 100644 --- a/paper.h +++ b/paper.h @@ -153,6 +153,10 @@ struct para_data_Tag { */ int outline_level; /* 0=title 1=C 2=H 3=S 4=S2... */ wchar_t *outline_title; + /* + * For adding the page number of a contents entry afterwards. + */ + paragraph *contents_entry; }; struct line_data_Tag { @@ -250,6 +254,10 @@ struct page_data_Tag { rect *first_rect; rect *last_rect; /* + * The page number, as a string. + */ + wchar_t *number; + /* * This spare pointer field is for use by the client backends. */ void *spare; -- 2.11.0