X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/halibut/blobdiff_plain/c65367738ae7b8bde8c52d01d82a19987bb4941d..93688997ac35ff239dad82a093d3313a4c46ac62:/bk_paper.c diff --git a/bk_paper.c b/bk_paper.c index 720f088..a9f6f24 100644 --- a/bk_paper.c +++ b/bk_paper.c @@ -10,27 +10,71 @@ */ /* - * To be done: - * - * - index - * - * - header/footer? Page numbers at least would be handy. Fully - * configurable footer can wait, though. - * - * That should bring us to the same level of functionality that - * original-Halibut had, and the same in PDF plus the obvious - * interactive navigation features. After that, in future work: + * TODO in future work: * * - linearised PDF, perhaps? * + * - we should use PDFDocEncoding or Unicode for outline strings, + * now that I actually know how to do them. Probably easiest if + * I do this _after_ bringing in libcharset, since I can simply + * supply PDFDocEncoding in there. + * * - I'm uncertain of whether I need to include a ToUnicode CMap * in each of my font definitions in PDF. Currently things (by * which I mean cut and paste out of acroread) seem to be * working fairly happily without it, but I don't know. * + * - rather than the ugly aux_text mechanism for rendering chapter + * titles, we could actually build the correct word list and + * wrap it as a whole. + * + * - get vertical font metrics and use them to position the PDF + * xref boxes more pleasantly + * * - configurability + * * all the measurements in `conf' should be configurable + * + notably paper size/shape + * * page header and footer should be configurable; we should + * be able to shift the page number elsewhere, and add other + * things such as the current chapter/section title and fixed + * text + * * remove the fixed mapping from heading levels to heading + * styles; offer a menu of styles from which the user can + * choose at every heading level + * * first-line indent in paragraphs + * * fixed text: `Contents', `Index', bullet, quotes, the + * colon-space and full stop in chapter title constructions + * * configurable location of contents? + * * certainly configurably _remove_ the contents, and possibly + * also the index + * * double-sided document switch? + * + means you have two header/footer formats which + * alternate + * + and means that mandatory page breaks before chapter + * titles should include a blank page if necessary to + * start the next section to a right-hand page * * - title pages + * + * - ability to import other Type 1 fonts + * * we need to parse the font to extract its metrics + * * then we pass the font bodily to both PS and PDF so it can + * be included in the output file + * + * - character substitution for better typography? + * * fi, fl, ffi, ffl ligatures + * * use real ellipsis rather than ... + * * a hyphen in a word by itself might prefer to be an en-dash + * * (Americans might even want a convenient way to use an + * em-dash) + * * DON'T DO ANY OF THE ABOVE WITHIN \c OR \cw! + * * substituting `minus' for `hyphen' in the standard encoding + * is probably preferable in Courier, though certainly not in + * the main text font + * * if I do do this lot, I'm rather inclined to at least try + * to think up a configurable way to do it so that Americans + * can do em-dash tricks without my intervention and other + * people can do other odd things too. */ #include @@ -66,6 +110,8 @@ struct paper_conf_Tag { int index_gutter; int index_cols; int index_minsep; + int pagenum_fontsize; + int footer_distance; /* These are derived from the above */ int base_width; int page_height; @@ -89,6 +135,10 @@ struct paper_idx_Tag { page_data *lastpage; }; +enum { + word_PageXref = word_NotWordType + 1 +}; + 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); @@ -101,6 +151,7 @@ static int render_line(line_data *ldata, int left_x, int top_y, 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); static para_data *code_paragraph(int indent, word *words, paper_conf *conf); static para_data *rule_paragraph(int indent, paper_conf *conf); @@ -113,6 +164,8 @@ 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); @@ -160,6 +213,8 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, conf->index_gutter = 36 * 4096; conf->index_cols = 2; conf->index_minsep = 18 * 4096; + conf->pagenum_fontsize = 12; + conf->footer_distance = 32 * 4096; conf->base_width = conf->paper_width - conf->left_margin - conf->right_margin; @@ -169,8 +224,6 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, (conf->base_width - (conf->index_cols-1) * conf->index_gutter) / conf->index_cols; - IGNORE(idx); /* FIXME */ - /* * First, set up some font structures. */ @@ -457,7 +510,7 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, for (page = pages; page; page = page->next) { sprintf(buf, "%d", ++pagenum); - page->number = ufroma_dup(buf); + page->number = ufroma_dup(buf, CS_ASCII); } if (has_index) { @@ -471,7 +524,7 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, /* And don't forget the as-yet-uncreated index. */ sprintf(buf, "%d", ++pagenum); - first_index_page->number = ufroma_dup(buf); + first_index_page->number = ufroma_dup(buf, CS_ASCII); } } @@ -511,6 +564,9 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, paper_idx *pi = (paper_idx *)entry->backend_data; para_data *text, *pages; + if (!pi->words) + continue; + text = make_para_data(para_Normal, 0, 0, conf->base_width - conf->index_colwidth, NULL, NULL, entry->text, conf); @@ -627,7 +683,7 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, for (page = ipages->next; page; page = page->next) { char buf[40]; sprintf(buf, "%d", ++pagenum); - page->number = ufroma_dup(buf); + page->number = ufroma_dup(buf, CS_ASCII); } /* @@ -660,6 +716,29 @@ void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords, } /* + * Draw the headers and footers. + * + * FIXME: this should be fully configurable, but for the moment + * I'm just going to put in page numbers in the centre of a + * footer and leave it at that. + */ + { + page_data *page; + + for (page = pages; page; page = page->next) { + int width; + + width = conf->pagenum_fontsize * + string_width(conf->tr, page->number, NULL); + + render_string(page, conf->tr, conf->pagenum_fontsize, + conf->left_margin + (conf->base_width - width)/2, + conf->bottom_margin - conf->footer_distance, + page->number); + } + } + + /* * Start putting together the overall document structure we're * going to return. */ @@ -1113,6 +1192,7 @@ static int paper_width_internal(void *vctx, word *word, int *nspaces) case word_HyperEnd: case word_UpperXref: case word_LowerXref: + case word_PageXref: case word_XrefEnd: case word_IndexRef: return 0; @@ -1480,7 +1560,8 @@ static void add_rect_to_page(page_data *page, int x, int y, int w, int h) } static void add_string_to_page(page_data *page, int x, int y, - font_encoding *fe, int size, char *text) + font_encoding *fe, int size, char *text, + int width) { text_fragment *frag; @@ -1498,6 +1579,7 @@ static void add_string_to_page(page_data *page, int x, int y, frag->fe = fe; frag->fontsize = size; frag->text = dupstr(text); + frag->width = width; } /* @@ -1516,8 +1598,10 @@ static int render_string(page_data *page, font_data *font, int fontsize, while (*str) { glyph = font->bmp[*str]; - if (glyph == 0xFFFF) + if (glyph == 0xFFFF) { + str++; continue; /* nothing more we can do here */ + } /* * Find which subfont this character is going in. @@ -1549,7 +1633,8 @@ static int render_string(page_data *page, font_data *font, int fontsize, if (!subfont || sf != subfont) { if (subfont) { text[textpos] = '\0'; - add_string_to_page(page, x, y, subfont, fontsize, text); + add_string_to_page(page, x, y, subfont, fontsize, text, + textwid); x += textwid; } else { assert(textpos == 0); @@ -1566,7 +1651,7 @@ static int render_string(page_data *page, font_data *font, int fontsize, if (textpos > 0) { text[textpos] = '\0'; - add_string_to_page(page, x, y, subfont, fontsize, text); + add_string_to_page(page, x, y, subfont, fontsize, text, textwid); x += textwid; } @@ -1593,11 +1678,16 @@ static int render_text(page_data *page, para_data *pdata, line_data *ldata, case word_HyperLink: case word_UpperXref: case word_LowerXref: + case word_PageXref: if (text->type == word_HyperLink) { dest.type = URL; - dest.url = utoa_dup(text->text); + dest.url = utoa_dup(text->text, CS_ASCII); dest.page = NULL; + } else if (text->type == word_PageXref) { + dest.type = PAGE; + dest.url = NULL; + dest.page = (page_data *)text->private_data; } else { keyword *kwl = kw_lookup(keywords, text->text); para_data *pdata; @@ -1653,7 +1743,10 @@ static int render_text(page_data *page, para_data *pdata, line_data *ldata, * referenced by an index entry. */ case word_IndexRef: - { + /* + * We don't create index references in contents entries. + */ + if (!pdata->contents_entry) { indextag *tag; int i; @@ -1671,17 +1764,23 @@ static int render_text(page_data *page, para_data *pdata, line_data *ldata, * mention it once in the index. */ if (pi->lastpage != page) { + word **wp; + if (pi->lastword) { pi->lastword = pi->lastword->next = fake_word(L","); pi->lastword = pi->lastword->next = fake_space_word(); - pi->lastword = pi->lastword->next = - fake_word(page->number); - } else { - pi->lastword = pi->words = - fake_word(page->number); - } + wp = &pi->lastword->next; + } else + wp = &pi->words; + + pi->lastword = *wp = + fake_page_ref(page); + pi->lastword = pi->lastword->next = + fake_word(page->number); + pi->lastword = pi->lastword->next = + fake_end_ref(); } pi->lastpage = page; @@ -1905,13 +2004,6 @@ static void render_para(para_data *pdata, paper_conf *conf, 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) @@ -1921,6 +2013,13 @@ static void render_para(para_data *pdata, paper_conf *conf, conf->left_margin + x, (conf->paper_height - conf->top_margin - pdata->last->ypos), L"."); + + 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); } /* @@ -2201,6 +2300,31 @@ static word *fake_space_word(void) return ret; } +static word *fake_page_ref(page_data *page) +{ + word *ret = mknew(word); + ret->next = NULL; + ret->alt = NULL; + ret->type = word_PageXref; + ret->text = NULL; + ret->breaks = FALSE; + ret->aux = 0; + ret->private_data = page; + return ret; +} + +static word *fake_end_ref(void) +{ + word *ret = mknew(word); + ret->next = NULL; + ret->alt = NULL; + ret->type = word_XrefEnd; + ret->text = NULL; + ret->breaks = FALSE; + ret->aux = 0; + return ret; +} + static word *prepare_contents_title(word *first, wchar_t *separator, word *second) {