From: simon Date: Wed, 14 Apr 2004 12:17:44 +0000 (+0000) Subject: Restructuring to remove the requirement for a printed paragraph to X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/halibut/commitdiff_plain/be76d5970137415a78d56d844610d32668e65c9a Restructuring to remove the requirement for a printed paragraph to correspond exactly to a source paragraph. Should allow me to create multiple printed paragraphs from the same source paragraph (i.e. a contents entry for each heading in addition to the heading itself), and invent entirely new printed paragraphs of my own (e.g. for index entries). git-svn-id: svn://svn.tartarus.org/sgt/halibut@4068 cda61777-01e9-0310-a592-d414129be87e --- diff --git a/bk_paper.c b/bk_paper.c index 6b6980f..1d4696c 100644 --- a/bk_paper.c +++ b/bk_paper.c @@ -42,6 +42,33 @@ #include "halibut.h" #include "paper.h" +typedef struct paper_conf_Tag paper_conf; + +struct paper_conf_Tag { + int paper_width; + int paper_height; + int left_margin; + int top_margin; + int right_margin; + int bottom_margin; + int indent_list_bullet; + int indent_list; + int indent_quote; + int base_leading; + int base_para_spacing; + int chapter_top_space; + int sect_num_left_space; + int chapter_underline_depth; + int chapter_underline_thickness; + int rule_thickness; + int base_font_size; + /* These are derived from the above */ + int base_width; + int page_height; + /* Fonts used in the configuration */ + font_data *tr, *ti, *hr, *hi, *cr, *co, *cb; +}; + static font_data *make_std_font(font_list *fontlist, char const *name); static void wrap_paragraph(para_data *pdata, word *words, int w, int i1, int i2); @@ -50,46 +77,53 @@ static page_data *page_breaks(line_data *first, line_data *last, static void 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 void code_paragraph(para_data *pdata, - font_data *fn, font_data *fi, font_data *fb, - int font_size, int indent, word *words); -static void rule_paragraph(para_data *pdata, int indent, int height); +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, + 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); void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, indexdata *idx) { paragraph *p; document *doc; - int indent, extra_indent, firstline_indent, aux_indent; - para_data *pdata; + int indent; + para_data *pdata, *firstpara = NULL, *lastpara = NULL; line_data *ldata, *firstline, *lastline; - font_data *tr, *ti, *hr, *hi, *cr, *co, *cb; page_data *pages; font_list *fontlist; - word *aux, *aux2; + paper_conf *conf; /* * FIXME: All these things ought to become configurable. */ - int paper_width = 595 * 4096; - int paper_height = 841 * 4096; - int left_margin = 72 * 4096; - int top_margin = 72 * 4096; - int right_margin = 72 * 4096; - int bottom_margin = 108 * 4096; - int indent_list_bullet = 6 * 4096; - int indent_list = 24 * 4096; - int indent_quote = 18 * 4096; - int base_leading = 4096; - int base_para_spacing = 10 * 4096; - int chapter_top_space = 72 * 4096; - int sect_num_left_space = 12 * 4096; - int chapter_underline_depth = 14 * 4096; - int chapter_underline_thickness = 3 * 4096; - int rule_thickness = 1 * 4096; - - int base_width = paper_width - left_margin - right_margin; - int page_height = paper_height - top_margin - bottom_margin; + conf = mknew(paper_conf); + conf->paper_width = 595 * 4096; + conf->paper_height = 841 * 4096; + conf->left_margin = 72 * 4096; + conf->top_margin = 72 * 4096; + conf->right_margin = 72 * 4096; + conf->bottom_margin = 108 * 4096; + conf->indent_list_bullet = 6 * 4096; + conf->indent_list = 24 * 4096; + conf->indent_quote = 18 * 4096; + conf->base_leading = 4096; + conf->base_para_spacing = 10 * 4096; + conf->chapter_top_space = 72 * 4096; + conf->sect_num_left_space = 12 * 4096; + conf->chapter_underline_depth = 14 * 4096; + conf->chapter_underline_thickness = 3 * 4096; + conf->rule_thickness = 1 * 4096; + conf->base_font_size = 12; + + conf->base_width = + conf->paper_width - conf->left_margin - conf->right_margin; + conf->page_height = + conf->paper_height - conf->top_margin - conf->bottom_margin; IGNORE(idx); /* FIXME */ @@ -98,13 +132,13 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, */ fontlist = mknew(font_list); fontlist->head = fontlist->tail = NULL; - tr = make_std_font(fontlist, "Times-Roman"); - ti = make_std_font(fontlist, "Times-Italic"); - hr = make_std_font(fontlist, "Helvetica-Bold"); - hi = make_std_font(fontlist, "Helvetica-BoldOblique"); - cr = make_std_font(fontlist, "Courier"); - co = make_std_font(fontlist, "Courier-Oblique"); - cb = make_std_font(fontlist, "Courier-Bold"); + conf->tr = make_std_font(fontlist, "Times-Roman"); + conf->ti = make_std_font(fontlist, "Times-Italic"); + conf->hr = make_std_font(fontlist, "Helvetica-Bold"); + conf->hi = make_std_font(fontlist, "Helvetica-BoldOblique"); + conf->cr = make_std_font(fontlist, "Courier"); + conf->co = make_std_font(fontlist, "Courier-Oblique"); + conf->cb = make_std_font(fontlist, "Courier-Bold"); /* * Go through and break up each paragraph into lines. @@ -135,21 +169,20 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, * rest of the paragraphs, so we need to pay attention. */ case para_LcontPush: - indent += indent_list; break; + indent += conf->indent_list; break; case para_LcontPop: - indent -= indent_list; assert(indent >= 0); break; + indent -= conf->indent_list; assert(indent >= 0); break; case para_QuotePush: - indent += indent_quote; break; + indent += conf->indent_quote; break; case para_QuotePop: - indent -= indent_quote; assert(indent >= 0); break; + indent -= conf->indent_quote; assert(indent >= 0); break; /* * This paragraph type is special. Process it * specially. */ case para_Code: - pdata = mknew(para_data); - code_paragraph(pdata, cr, co, cb, 12, indent, p->words); + pdata = code_paragraph(indent, p->words, conf); p->private_data = pdata; if (pdata->first != pdata->last) { pdata->first->penalty_after += 100000; @@ -161,8 +194,7 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, * This paragraph is also special. */ case para_Rule: - pdata = mknew(para_data); - rule_paragraph(pdata, indent, rule_thickness); + pdata = rule_paragraph(indent, conf); p->private_data = pdata; break; @@ -185,228 +217,11 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, case para_Description: case para_Copyright: case para_Title: - pdata = mknew(para_data); - - /* - * Choose fonts for this paragraph. - * - * FIXME: All of this ought to be completely - * user-configurable. - */ - switch (p->type) { - case para_Title: - pdata->fonts[FONT_NORMAL] = hr; - pdata->sizes[FONT_NORMAL] = 24; - pdata->fonts[FONT_EMPH] = hi; - pdata->sizes[FONT_EMPH] = 24; - pdata->fonts[FONT_CODE] = cb; - pdata->sizes[FONT_CODE] = 24; - break; - - case para_Chapter: - case para_Appendix: - case para_UnnumberedChapter: - pdata->fonts[FONT_NORMAL] = hr; - pdata->sizes[FONT_NORMAL] = 20; - pdata->fonts[FONT_EMPH] = hi; - pdata->sizes[FONT_EMPH] = 20; - pdata->fonts[FONT_CODE] = cb; - pdata->sizes[FONT_CODE] = 20; - break; - - case para_Heading: - case para_Subsect: - pdata->fonts[FONT_NORMAL] = hr; - pdata->fonts[FONT_EMPH] = hi; - pdata->fonts[FONT_CODE] = cb; - pdata->sizes[FONT_NORMAL] = - pdata->sizes[FONT_EMPH] = - pdata->sizes[FONT_CODE] = - (p->aux == 0 ? 16 : p->aux == 1 ? 14 : 13); - break; - - case para_Normal: - case para_BiblioCited: - case para_Bullet: - case para_NumberedList: - case para_DescribedThing: - case para_Description: - case para_Copyright: - pdata->fonts[FONT_NORMAL] = tr; - pdata->sizes[FONT_NORMAL] = 12; - pdata->fonts[FONT_EMPH] = ti; - pdata->sizes[FONT_EMPH] = 12; - pdata->fonts[FONT_CODE] = cr; - pdata->sizes[FONT_CODE] = 12; - break; - } - - /* - * Also select an indentation level depending on the - * paragraph type (list paragraphs other than - * para_DescribedThing need extra indent). - * - * (FIXME: Perhaps at some point we might even arrange - * for the user to be able to request indented first - * lines in paragraphs.) - */ - if (p->type == para_Bullet || - p->type == para_NumberedList || - p->type == para_Description) { - extra_indent = firstline_indent = indent_list; - } else { - extra_indent = firstline_indent = 0; - } - - /* - * Find the auxiliary text for this paragraph. - */ - aux = aux2 = NULL; - aux_indent = 0; - - switch (p->type) { - case para_Chapter: - case para_Appendix: - case para_Heading: - case para_Subsect: - /* - * For some heading styles (FIXME: be able to - * configure which), the auxiliary text contains - * the chapter number and is arranged to be - * right-aligned a few points left of the primary - * margin. For other styles, the auxiliary text is - * the full chapter _name_ and takes up space - * within the (wrapped) chapter title, meaning that - * we must move the first line indent over to make - * space for it. - */ - if (p->type == para_Heading || p->type == para_Subsect) { - int len; - - aux = p->kwtext2; - len = paper_width_simple(pdata, p->kwtext2); - aux_indent = -len - sect_num_left_space; - } else { - aux = p->kwtext; - aux2 = mknew(word); - aux2->next = NULL; - aux2->alt = NULL; - aux2->type = word_Normal; - aux2->text = ustrdup(L": "); - aux2->breaks = FALSE; - aux2->aux = 0; - aux_indent = 0; - - firstline_indent += paper_width_simple(pdata, aux); - firstline_indent += paper_width_simple(pdata, aux2); - } - break; - - case para_Bullet: - /* - * 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_indent = indent + indent_list_bullet; - break; - - case para_NumberedList: - /* - * Auxiliary text consisting of the number followed - * by a (FIXME: configurable) full stop. - */ - aux = p->kwtext; - aux2 = mknew(word); - aux2->next = NULL; - aux2->alt = NULL; - aux2->type = word_Normal; - aux2->text = ustrdup(L"."); - aux2->breaks = FALSE; - aux2->aux = 0; - aux_indent = indent + indent_list_bullet; - break; - - case para_BiblioCited: - /* - * Auxiliary text consisting of the bibliography - * reference text, and a trailing space. - */ - aux = p->kwtext; - aux2 = mknew(word); - aux2->next = NULL; - aux2->alt = NULL; - aux2->type = word_Normal; - aux2->text = ustrdup(L" "); - aux2->breaks = FALSE; - aux2->aux = 0; - aux_indent = indent; - firstline_indent += paper_width_simple(pdata, aux); - firstline_indent += paper_width_simple(pdata, aux2); - break; - } - - wrap_paragraph(pdata, p->words, base_width, - indent + firstline_indent, - indent + extra_indent); + pdata = make_para_data(p->type, p->aux, indent, + p->kwtext, p->kwtext2, p->words, conf); p->private_data = pdata; - pdata->first->aux_text = aux; - pdata->first->aux_text_2 = aux2; - pdata->first->aux_left_indent = aux_indent; - - /* - * Line breaking penalties. - */ - switch (p->type) { - case para_Chapter: - case para_Appendix: - case para_Heading: - case para_Subsect: - case para_UnnumberedChapter: - /* - * Fixed and large penalty for breaking straight - * after a heading; corresponding bonus for - * breaking straight before. - */ - pdata->first->penalty_before = -500000; - pdata->last->penalty_after = 500000; - for (ldata = pdata->first; ldata; ldata = ldata->next) - ldata->penalty_after = 500000; - break; - - case para_DescribedThing: - /* - * This is treated a bit like a small heading: - * there's a penalty for breaking after it (i.e. - * between it and its description), and a bonus for - * breaking before it (actually _between_ list - * items). - */ - pdata->first->penalty_before = -200000; - pdata->last->penalty_after = 200000; - break; - - default: - /* - * Most paragraph types: widow/orphan control by - * discouraging breaking one line from the end of - * any paragraph. - */ - if (pdata->first != pdata->last) { - pdata->first->penalty_after = 100000; - pdata->last->penalty_before = 100000; - } - break; - } - break; } @@ -414,35 +229,6 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, pdata = (para_data *)p->private_data; /* - * Set the line spacing for each line in this paragraph. - */ - for (ldata = pdata->first; ldata; ldata = ldata->next) { - if (ldata == pdata->first) - ldata->space_before = base_para_spacing / 2; - else - ldata->space_before = base_leading / 2; - if (ldata == pdata->last) - ldata->space_after = base_para_spacing / 2; - else - ldata->space_after = base_leading / 2; - ldata->page_break = FALSE; - } - - /* - * Some kinds of section heading do require a page - * break before them. - */ - if (p->type == para_Title || - p->type == para_Chapter || - p->type == para_Appendix || - p->type == para_UnnumberedChapter) { - pdata->first->page_break = TRUE; - pdata->first->space_before = chapter_top_space; - pdata->last->space_after += - chapter_underline_depth + chapter_underline_thickness; - } - - /* * Link all line structures together into a big list. */ if (pdata->first) { @@ -455,6 +241,16 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, } lastline = pdata->last; } + + /* + * Link all paragraph structures together similarly. + */ + pdata->next = NULL; + if (lastpara) + lastpara->next = pdata; + else + firstpara = pdata; + lastpara = pdata; } } @@ -462,52 +258,49 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, * Now we have an enormous linked list of every line of text in * the document. Break it up into pages. */ - pages = page_breaks(firstline, lastline, page_height); + pages = page_breaks(firstline, lastline, conf->page_height); /* * 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 (p = sourceform; p; p = p->next) { - pdata = (para_data *)p->private_data; - - if (pdata) { - xref_dest dest; - dest.type = NONE; - for (ldata = pdata->first; ldata; ldata = ldata->next) { - render_line(ldata, left_margin, paper_height - top_margin, - &dest, keywords); - if (ldata == pdata->last) - break; - } - - /* - * Some section headings (FIXME: should be configurable - * which) want to be underlined. - */ - if (p->type == para_Chapter || p->type == para_Appendix || - p->type == para_UnnumberedChapter || p->type == para_Title) { - add_rect_to_page(pdata->last->page, - left_margin, - (paper_height - top_margin - - pdata->last->ypos - chapter_underline_depth), - base_width, - chapter_underline_thickness); - } + for (pdata = firstpara; pdata; pdata = pdata->next) { + 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); + if (ldata == pdata->last) + break; + } - /* - * Rule paragraphs need to contain an actual rule! - */ - if (p->type == para_Rule) { - add_rect_to_page(pdata->first->page, - left_margin + pdata->first->xpos, - (paper_height - top_margin - - pdata->last->ypos - - pdata->last->line_height), - base_width - pdata->first->xpos, - pdata->last->line_height); - } + /* + * Render any rectangle (chapter title underline or rule) + * that goes with this paragraph. + */ + switch (pdata->rect_type) { + case RECT_CHAPTER_UNDERLINE: + add_rect_to_page(pdata->last->page, + conf->left_margin, + (conf->paper_height - conf->top_margin - + pdata->last->ypos - + conf->chapter_underline_depth), + conf->base_width, + conf->chapter_underline_thickness); + break; + case RECT_RULE: + add_rect_to_page(pdata->first->page, + conf->left_margin + pdata->first->xpos, + (conf->paper_height - conf->top_margin - + pdata->last->ypos - + pdata->last->line_height), + conf->base_width - pdata->first->xpos, + pdata->last->line_height); + break; + default: /* placate gcc */ + break; } } @@ -518,8 +311,8 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, doc = mknew(document); doc->fonts = fontlist; doc->pages = pages; - doc->paper_width = paper_width; - doc->paper_height = paper_height; + doc->paper_width = conf->paper_width; + doc->paper_height = conf->paper_height; /* * Collect the section heading paragraphs into a document @@ -534,49 +327,324 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, doc->n_outline_elements = 0; /* First find the title. */ - for (p = sourceform; p; p = p->next) { - switch (p->type) { - case para_Title: + for (pdata = firstpara; pdata; pdata = pdata->next) { + if (pdata->outline_level == 0) { doc->outline_elements[0].level = 0; - doc->outline_elements[0].para = p; + doc->outline_elements[0].pdata = pdata; doc->n_outline_elements++; break; } } /* Then collect the rest. */ - for (p = sourceform; p; p = p->next) { - switch (p->type) { - case para_Chapter: - case para_UnnumberedChapter: - case para_Appendix: - case para_Heading: - case para_Subsect: - + for (pdata = firstpara; pdata; pdata = pdata->next) { + if (pdata->outline_level > 0) { if (doc->n_outline_elements >= osize) { osize += 20; doc->outline_elements = resize(doc->outline_elements, osize); } - if (p->type == para_Heading) { - doc->outline_elements[doc->n_outline_elements].level = 2; - } else if (p->type == para_Subsect) { - doc->outline_elements[doc->n_outline_elements].level = - 3 + p->aux; - } else - doc->outline_elements[doc->n_outline_elements].level = 1; - - doc->outline_elements[doc->n_outline_elements].para = p; + doc->outline_elements[doc->n_outline_elements].level = + pdata->outline_level; + doc->outline_elements[doc->n_outline_elements].pdata = pdata; doc->n_outline_elements++; - break; } } } + sfree(conf); + return doc; } +static para_data *make_para_data(int ptype, int paux, int indent, + word *pkwtext, word *pkwtext2, word *pwords, + paper_conf *conf) +{ + para_data *pdata; + line_data *ldata; + int extra_indent, firstline_indent, aux_indent; + word *aux, *aux2; + + pdata = mknew(para_data); + pdata->outline_level = -1; + pdata->outline_title = NULL; + pdata->rect_type = RECT_NONE; + + /* + * Choose fonts for this paragraph. + * + * FIXME: All of this ought to be completely + * user-configurable. + */ + switch (ptype) { + case para_Title: + pdata->fonts[FONT_NORMAL] = conf->hr; + pdata->sizes[FONT_NORMAL] = 24; + pdata->fonts[FONT_EMPH] = conf->hi; + pdata->sizes[FONT_EMPH] = 24; + pdata->fonts[FONT_CODE] = conf->cb; + pdata->sizes[FONT_CODE] = 24; + pdata->outline_level = 0; + break; + + case para_Chapter: + case para_Appendix: + case para_UnnumberedChapter: + pdata->fonts[FONT_NORMAL] = conf->hr; + pdata->sizes[FONT_NORMAL] = 20; + pdata->fonts[FONT_EMPH] = conf->hi; + pdata->sizes[FONT_EMPH] = 20; + pdata->fonts[FONT_CODE] = conf->cb; + pdata->sizes[FONT_CODE] = 20; + pdata->outline_level = 1; + break; + + case para_Heading: + case para_Subsect: + pdata->fonts[FONT_NORMAL] = conf->hr; + pdata->fonts[FONT_EMPH] = conf->hi; + pdata->fonts[FONT_CODE] = conf->cb; + pdata->sizes[FONT_NORMAL] = + pdata->sizes[FONT_EMPH] = + pdata->sizes[FONT_CODE] = + (paux == 0 ? 16 : paux == 1 ? 14 : 13); + pdata->outline_level = 2 + paux; + break; + + case para_Normal: + case para_BiblioCited: + case para_Bullet: + case para_NumberedList: + case para_DescribedThing: + case para_Description: + case para_Copyright: + pdata->fonts[FONT_NORMAL] = conf->tr; + pdata->sizes[FONT_NORMAL] = 12; + pdata->fonts[FONT_EMPH] = conf->ti; + pdata->sizes[FONT_EMPH] = 12; + pdata->fonts[FONT_CODE] = conf->cr; + pdata->sizes[FONT_CODE] = 12; + break; + } + + /* + * Also select an indentation level depending on the + * paragraph type (list paragraphs other than + * para_DescribedThing need extra indent). + * + * (FIXME: Perhaps at some point we might even arrange + * for the user to be able to request indented first + * lines in paragraphs.) + */ + if (ptype == para_Bullet || + ptype == para_NumberedList || + ptype == para_Description) { + extra_indent = firstline_indent = conf->indent_list; + } else { + extra_indent = firstline_indent = 0; + } + + /* + * Find the auxiliary text for this paragraph. + */ + aux = aux2 = NULL; + aux_indent = 0; + + switch (ptype) { + case para_Chapter: + case para_Appendix: + case para_Heading: + case para_Subsect: + /* + * For some heading styles (FIXME: be able to + * configure which), the auxiliary text contains + * the chapter number and is arranged to be + * right-aligned a few points left of the primary + * margin. For other styles, the auxiliary text is + * the full chapter _name_ and takes up space + * within the (wrapped) chapter title, meaning that + * we must move the first line indent over to make + * space for it. + */ + if (ptype == para_Heading || ptype == para_Subsect) { + int len; + + aux = pkwtext2; + len = paper_width_simple(pdata, pkwtext2); + aux_indent = -len - conf->sect_num_left_space; + + pdata->outline_title = + 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; + aux_indent = 0; + + firstline_indent += paper_width_simple(pdata, aux); + firstline_indent += paper_width_simple(pdata, aux2); + + pdata->outline_title = + prepare_outline_title(pkwtext, L": ", pwords); + } + break; + + case para_Bullet: + /* + * 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_indent = indent + conf->indent_list_bullet; + break; + + case para_NumberedList: + /* + * Auxiliary text consisting of the number followed + * 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; + aux_indent = indent + conf->indent_list_bullet; + break; + + case para_BiblioCited: + /* + * Auxiliary text consisting of the bibliography + * 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; + aux_indent = indent; + firstline_indent += paper_width_simple(pdata, aux); + firstline_indent += paper_width_simple(pdata, aux2); + break; + } + + if (pdata->outline_level >= 0 && !pdata->outline_title) { + pdata->outline_title = + prepare_outline_title(NULL, NULL, pwords); + } + + wrap_paragraph(pdata, pwords, conf->base_width, + indent + firstline_indent, + indent + extra_indent); + + pdata->first->aux_text = aux; + pdata->first->aux_text_2 = aux2; + pdata->first->aux_left_indent = aux_indent; + + /* + * Line breaking penalties. + */ + switch (ptype) { + case para_Chapter: + case para_Appendix: + case para_Heading: + case para_Subsect: + case para_UnnumberedChapter: + /* + * Fixed and large penalty for breaking straight + * after a heading; corresponding bonus for + * breaking straight before. + */ + pdata->first->penalty_before = -500000; + pdata->last->penalty_after = 500000; + for (ldata = pdata->first; ldata; ldata = ldata->next) + ldata->penalty_after = 500000; + break; + + case para_DescribedThing: + /* + * This is treated a bit like a small heading: + * there's a penalty for breaking after it (i.e. + * between it and its description), and a bonus for + * breaking before it (actually _between_ list + * items). + */ + pdata->first->penalty_before = -200000; + pdata->last->penalty_after = 200000; + break; + + default: + /* + * Most paragraph types: widow/orphan control by + * discouraging breaking one line from the end of + * any paragraph. + */ + if (pdata->first != pdata->last) { + pdata->first->penalty_after = 100000; + pdata->last->penalty_before = 100000; + } + break; + } + + standard_line_spacing(pdata, conf); + + /* + * Some kinds of section heading require a page break before + * them and an underline after. + */ + if (ptype == para_Title || + ptype == para_Chapter || + ptype == para_Appendix || + ptype == para_UnnumberedChapter) { + pdata->first->page_break = TRUE; + pdata->first->space_before = conf->chapter_top_space; + pdata->last->space_after += + (conf->chapter_underline_depth + + conf->chapter_underline_thickness); + pdata->rect_type = RECT_CHAPTER_UNDERLINE; + } + + return pdata; +} + +static void standard_line_spacing(para_data *pdata, paper_conf *conf) +{ + line_data *ldata; + + /* + * Set the line spacing for each line in this paragraph. + */ + for (ldata = pdata->first; ldata; ldata = ldata->next) { + if (ldata == pdata->first) + ldata->space_before = conf->base_para_spacing / 2; + else + ldata->space_before = conf->base_leading / 2; + if (ldata == pdata->last) + ldata->space_after = conf->base_para_spacing / 2; + else + ldata->space_after = conf->base_leading / 2; + ldata->page_break = FALSE; + } +} + static font_encoding *new_font_encoding(font_data *font) { font_encoding *fe; @@ -1295,23 +1363,25 @@ static void render_line(line_data *ldata, int left_x, int top_y, } } -static void code_paragraph(para_data *pdata, - font_data *fn, font_data *fi, font_data *fb, - int font_size, int indent, word *words) +static para_data *code_paragraph(int indent, word *words, paper_conf *conf) { + para_data *pdata = mknew(para_data); + /* * For code paragraphs, I'm going to hack grievously and * pretend the three normal fonts are the three code paragraph * fonts. */ - pdata->fonts[FONT_NORMAL] = fb; - pdata->fonts[FONT_EMPH] = fi; - pdata->fonts[FONT_CODE] = fn; + pdata->fonts[FONT_NORMAL] = conf->cb; + pdata->fonts[FONT_EMPH] = conf->co; + pdata->fonts[FONT_CODE] = conf->cb; pdata->sizes[FONT_NORMAL] = pdata->sizes[FONT_EMPH] = - pdata->sizes[FONT_CODE] = font_size; + pdata->sizes[FONT_CODE] = 12; pdata->first = pdata->last = NULL; + pdata->outline_level = -1; + pdata->rect_type = RECT_NONE; for (; words; words = words->next) { wchar_t *t, *e, *start; @@ -1380,7 +1450,7 @@ static void code_paragraph(para_data *pdata, ldata->pdata = pdata; ldata->first = lhead; ldata->end = NULL; - ldata->line_height = font_size * 4096; + ldata->line_height = conf->base_font_size * 4096; ldata->xpos = indent; @@ -1402,10 +1472,15 @@ static void code_paragraph(para_data *pdata, /* General opprobrium for breaking in a code paragraph. */ ldata->penalty_before = ldata->penalty_after = 50000; } + + standard_line_spacing(pdata, conf); + + return pdata; } -static void rule_paragraph(para_data *pdata, int indent, int height) +static para_data *rule_paragraph(int indent, paper_conf *conf) { + para_data *pdata = mknew(para_data); line_data *ldata; ldata = mknew(line_data); @@ -1413,7 +1488,7 @@ static void rule_paragraph(para_data *pdata, int indent, int height) ldata->pdata = pdata; ldata->first = NULL; ldata->end = NULL; - ldata->line_height = height; + ldata->line_height = conf->rule_thickness; ldata->xpos = indent; @@ -1433,4 +1508,79 @@ static void rule_paragraph(para_data *pdata, int indent, int height) ldata->penalty_before += -100000; pdata->first = pdata->last = ldata; + pdata->outline_level = -1; + pdata->rect_type = RECT_RULE; + + standard_line_spacing(pdata, conf); + + return pdata; +} + +/* + * Plain-text-like formatting for outline titles. + */ +static void paper_rdaddw(rdstring *rs, word *text) { + for (; text; text = text->next) switch (text->type) { + case word_HyperLink: + case word_HyperEnd: + case word_UpperXref: + case word_LowerXref: + case word_XrefEnd: + case word_IndexRef: + break; + + case word_Normal: + case word_Emph: + case word_Code: + case word_WeakCode: + case word_WhiteSpace: + case word_EmphSpace: + case word_CodeSpace: + case word_WkCodeSpace: + case word_Quote: + case word_EmphQuote: + case word_CodeQuote: + case word_WkCodeQuote: + assert(text->type != word_CodeQuote && + text->type != word_WkCodeQuote); + if (towordstyle(text->type) == word_Emph && + (attraux(text->aux) == attr_First || + attraux(text->aux) == attr_Only)) + rdadd(rs, L'_'); /* FIXME: configurability */ + else if (towordstyle(text->type) == word_Code && + (attraux(text->aux) == attr_First || + attraux(text->aux) == attr_Only)) + rdadd(rs, L'\''); /* FIXME: configurability */ + if (removeattr(text->type) == word_Normal) { + rdadds(rs, text->text); + } else if (removeattr(text->type) == word_WhiteSpace) { + rdadd(rs, L' '); + } else if (removeattr(text->type) == word_Quote) { + rdadd(rs, L'\''); /* fixme: configurability */ + } + if (towordstyle(text->type) == word_Emph && + (attraux(text->aux) == attr_Last || + attraux(text->aux) == attr_Only)) + rdadd(rs, L'_'); /* FIXME: configurability */ + else if (towordstyle(text->type) == word_Code && + (attraux(text->aux) == attr_Last || + attraux(text->aux) == attr_Only)) + rdadd(rs, L'\''); /* FIXME: configurability */ + break; + } +} + +static wchar_t *prepare_outline_title(word *first, wchar_t *separator, + word *second) +{ + rdstring rs = {0, 0, NULL}; + + if (first) + paper_rdaddw(&rs, first); + if (separator) + rdadds(&rs, separator); + if (second) + paper_rdaddw(&rs, second); + + return rs.text; } diff --git a/bk_pdf.c b/bk_pdf.c index fd1424a..b68598c 100644 --- a/bk_pdf.c +++ b/bk_pdf.c @@ -579,78 +579,19 @@ static int pdf_convert(wchar_t *s, char **result) { return ok; } -static void pdf_rdaddwc(rdstringc *rs, word *text) { - char *c; - - for (; text; text = text->next) switch (text->type) { - case word_HyperLink: - case word_HyperEnd: - case word_UpperXref: - case word_LowerXref: - case word_XrefEnd: - case word_IndexRef: - break; - - case word_Normal: - case word_Emph: - case word_Code: - case word_WeakCode: - case word_WhiteSpace: - case word_EmphSpace: - case word_CodeSpace: - case word_WkCodeSpace: - case word_Quote: - case word_EmphQuote: - case word_CodeQuote: - case word_WkCodeQuote: - assert(text->type != word_CodeQuote && - text->type != word_WkCodeQuote); - if (towordstyle(text->type) == word_Emph && - (attraux(text->aux) == attr_First || - attraux(text->aux) == attr_Only)) - rdaddc(rs, '_'); /* FIXME: configurability */ - else if (towordstyle(text->type) == word_Code && - (attraux(text->aux) == attr_First || - attraux(text->aux) == attr_Only)) - rdaddc(rs, '\''); /* FIXME: configurability */ - if (removeattr(text->type) == word_Normal) { - if (pdf_convert(text->text, &c)) - rdaddsc(rs, c); - else - pdf_rdaddwc(rs, text->alt); - sfree(c); - } else if (removeattr(text->type) == word_WhiteSpace) { - rdaddc(rs, ' '); - } else if (removeattr(text->type) == word_Quote) { - rdaddc(rs, '\''); /* FIXME: configurability */ - } - if (towordstyle(text->type) == word_Emph && - (attraux(text->aux) == attr_Last || - attraux(text->aux) == attr_Only)) - rdaddc(rs, '_'); /* FIXME: configurability */ - else if (towordstyle(text->type) == word_Code && - (attraux(text->aux) == attr_Last || - attraux(text->aux) == attr_Only)) - rdaddc(rs, '\''); /* FIXME: configurability */ - break; - } -} - static int make_outline(object *parent, outline_element *items, int n, int open) { int level, totalcount = 0; outline_element *itemp; object *curr, *prev = NULL, *first = NULL, *last = NULL; - para_data *pdata; assert(n > 0); level = items->level; while (n > 0) { - rdstringc rs = {0, 0, NULL}; - char *p; + char *title, *p; /* * Here we expect to be sitting on an item at the given @@ -659,21 +600,14 @@ static int make_outline(object *parent, outline_element *items, int n, */ assert(items->level == level); - if (level == 1 && items->para->kwtext) { - pdf_rdaddwc(&rs, items->para->kwtext); - rdaddsc(&rs, ": "); - } else if (level > 1 && items->para->kwtext2) { - pdf_rdaddwc(&rs, items->para->kwtext2); - rdaddsc(&rs, " "); - } - pdf_rdaddwc(&rs, items->para->words); + pdf_convert(items->pdata->outline_title, &title); totalcount++; curr = new_object(parent->list); if (!first) first = curr; last = curr; objtext(curr, "<<\n/Title ("); - for (p = rs.text; p < rs.text+rs.pos; p++) { + for (p = title; *p; p++) { char c[2]; if (*p == '\\' || *p == '(' || *p == ')') objtext(curr, "\\"); @@ -684,8 +618,7 @@ static int make_outline(object *parent, outline_element *items, int n, objtext(curr, ")\n/Parent "); objref(curr, parent); objtext(curr, "\n/Dest ["); - pdata = (para_data *)items->para->private_data; - objref(curr, (object *)pdata->first->page->spare); + objref(curr, (object *)items->pdata->first->page->spare); objtext(curr, " /XYZ null null null]\n"); if (prev) { objtext(curr, "/Prev "); diff --git a/paper.h b/paper.h index 7c46571..160bff5 100644 --- a/paper.h +++ b/paper.h @@ -123,6 +123,7 @@ enum { */ struct para_data_Tag { + para_data *next; /* * Data about the fonts used in this paragraph. Indices are the * FONT_* constants defined above. @@ -140,6 +141,18 @@ struct para_data_Tag { */ line_data *first; /* first line in paragraph */ line_data *last; /* last line in paragraph */ + /* + * Some paragraphs have associated graphics; currently this is + * nothing more complex than a single black rectangle. + */ + enum { + RECT_NONE, RECT_CHAPTER_UNDERLINE, RECT_RULE + } rect_type; + /* + * For constructing the page outline. + */ + int outline_level; /* 0=title 1=C 2=H 3=S 4=S2... */ + wchar_t *outline_title; }; struct line_data_Tag { @@ -269,7 +282,7 @@ struct rect_Tag { struct outline_element_Tag { int level; /* 0=title 1=C 2=H 3=S 4=S2... */ - paragraph *para; + para_data *pdata; }; /*