* 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
* 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
+ * * fixed text: `Contents', `Index', the colon-space and full
+ * stop in chapter title constructions
* * configurable location of contents?
* * certainly configurably _remove_ the contents, and possibly
* also the index
#include <assert.h>
#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
#include "halibut.h"
#include "paper.h"
typedef struct paper_conf_Tag paper_conf;
typedef struct paper_idx_Tag paper_idx;
+typedef struct {
+ font_data *fonts[NFONTS];
+ int font_size;
+} font_cfg;
+
struct paper_conf_Tag {
int paper_width;
int paper_height;
int right_margin;
int bottom_margin;
int indent_list_bullet;
+ int indent_list_after;
int indent_list;
int indent_quote;
int base_leading;
int chapter_underline_depth;
int chapter_underline_thickness;
int rule_thickness;
- int base_font_size;
+ font_cfg fbase, fcode, ftitle, fchapter, *fsect;
+ int nfsect;
int contents_indent_step;
int contents_margin;
int leader_separation;
int index_minsep;
int pagenum_fontsize;
int footer_distance;
+ wchar_t *lquote, *rquote, *bullet;
+ wchar_t *contents_text, *index_text;
/* These are derived from the above */
int base_width;
int page_height;
int index_colwidth;
- /* Fonts used in the configuration */
- font_data *tr, *ti, *hr, *hi, *cr, *co, *cb;
};
struct paper_idx_Tag {
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);
+ int w, int i1, int i2, paper_conf *conf);
static page_data *page_breaks(line_data *first, line_data *last,
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);
+ 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);
+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);
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;
+}
+
void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords,
indexdata *idx) {
paragraph *p;
line_data *firstline, *lastline, *firstcontline, *lastcontline;
page_data *pages;
font_list *fontlist;
- paper_conf *conf;
+ paper_conf *conf, ourconf;
int has_index;
int pagenum;
paragraph index_placeholder_para;
page_data *first_index_page;
- /*
- * FIXME: All these things ought to become configurable.
- */
- 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->contents_indent_step = 24 * 4096;
- conf->contents_margin = 84 * 4096;
- conf->leader_separation = 12 * 4096;
- 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;
- conf->page_height =
- conf->paper_height - conf->top_margin - conf->bottom_margin;
- conf->index_colwidth =
- (conf->base_width - (conf->index_cols-1) * conf->index_gutter)
- / conf->index_cols;
-
- /*
- * First, set up some font structures.
- */
- fontlist = mknew(font_list);
+ init_std_fonts();
+ fontlist = snew(font_list);
fontlist->head = fontlist->tail = NULL;
- 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");
+
+ ourconf = paper_configure(sourceform, fontlist);
+ conf = &ourconf;
/*
* Set up a data structure to collect page numbers for each
has_index = FALSE;
for (i = 0; (entry = index234(idx->entries, i)) != NULL; i++) {
- paper_idx *pi = mknew(paper_idx);
+ paper_idx *pi = snew(paper_idx);
has_index = TRUE;
*/
{
word *contents_title;
- contents_title = fake_word(L"Contents");
+ contents_title = fake_word(conf->contents_text);
firstcont = make_para_data(para_UnnumberedChapter, 0, 0, 0,
NULL, NULL, contents_title, conf);
if (has_index) {
pdata = make_para_data(para_Normal, 0, 0,
conf->contents_margin,
- NULL, NULL, fake_word(L"Index"), conf);
+ NULL, NULL,
+ fake_word(conf->index_text), conf);
pdata->next = NULL;
pdata->contents_entry = &index_placeholder_para;
lastcont->next = pdata;
}
if (has_index) {
- first_index_page = mknew(page_data);
+ first_index_page = snew(page_data);
first_index_page->next = first_index_page->prev = NULL;
first_index_page->first_line = NULL;
first_index_page->last_line = NULL;
/*
* Create a set of paragraphs for the index.
*/
- index_title = fake_word(L"Index");
+ index_title = fake_word(conf->index_text);
firstidx = make_para_data(para_UnnumberedChapter, 0, 0, 0,
NULL, NULL, index_title, conf);
int width;
width = conf->pagenum_fontsize *
- string_width(conf->tr, page->number, NULL);
+ string_width(conf->fbase.fonts[FONT_NORMAL], page->number,
+ NULL);
- render_string(page, conf->tr, conf->pagenum_fontsize,
+ render_string(page, conf->fbase.fonts[FONT_NORMAL],
+ 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.
*/
- doc = mknew(document);
+ doc = snew(document);
doc->fonts = fontlist;
doc->pages = pages;
doc->paper_width = conf->paper_width;
{
int osize = 20;
- doc->outline_elements = mknewa(outline_element, osize);
+ doc->outline_elements = snewn(osize, outline_element);
doc->n_outline_elements = 0;
/* First find the title. */
if (doc->n_outline_elements >= osize) {
osize += 20;
doc->outline_elements =
- resize(doc->outline_elements, osize);
+ sresize(doc->outline_elements, osize, outline_element);
}
doc->outline_elements[doc->n_outline_elements].level =
}
}
- sfree(conf);
-
return doc;
}
+static void setfont(para_data *p, font_cfg *f) {
+ int i;
+
+ for (i = 0; i < NFONTS; i++) {
+ p->fonts[i] = f->fonts[i];
+ p->sizes[i] = f->font_size;
+ }
+}
+
static para_data *make_para_data(int ptype, int paux, int indent, int rmargin,
word *pkwtext, word *pkwtext2, word *pwords,
paper_conf *conf)
int extra_indent, firstline_indent, aux_indent;
word *aux, *aux2;
- pdata = mknew(para_data);
+ pdata = snew(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;
+ setfont(pdata, &conf->ftitle);
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;
+ setfont(pdata, &conf->fchapter);
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);
+ setfont(pdata,
+ &conf->fsect[paux >= conf->nfsect ? conf->nfsect - 1 : paux]);
pdata->outline_level = 2 + paux;
break;
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;
+ setfont(pdata, &conf->fbase);
break;
}
int len;
aux = pkwtext2;
- len = paper_width_simple(pdata, pkwtext2);
+ len = paper_width_simple(pdata, pkwtext2, conf);
aux_indent = -len - conf->sect_num_left_space;
pdata->outline_title =
aux2 = fake_word(L": ");
aux_indent = 0;
- firstline_indent += paper_width_simple(pdata, aux);
- firstline_indent += paper_width_simple(pdata, aux2);
+ firstline_indent += paper_width_simple(pdata, aux, conf);
+ firstline_indent += paper_width_simple(pdata, aux2, conf);
pdata->outline_title =
prepare_outline_title(pkwtext, L": ", pwords);
case para_Bullet:
/*
- * Auxiliary text consisting of a bullet. (FIXME:
- * configurable bullet.)
+ * Auxiliary text consisting of a bullet.
*/
- aux = fake_word(L"\x2022");
+ aux = fake_word(conf->bullet);
aux_indent = indent + conf->indent_list_bullet;
break;
aux = pkwtext;
aux2 = fake_word(L" ");
aux_indent = indent;
- firstline_indent += paper_width_simple(pdata, aux);
- firstline_indent += paper_width_simple(pdata, aux2);
+ firstline_indent += paper_width_simple(pdata, aux, conf);
+ firstline_indent += paper_width_simple(pdata, aux2, conf);
break;
}
wrap_paragraph(pdata, pwords, conf->base_width - rmargin,
indent + firstline_indent,
- indent + extra_indent);
+ indent + extra_indent, conf);
pdata->first->aux_text = aux;
pdata->first->aux_text_2 = aux2;
font_encoding *fe;
int i;
- fe = mknew(font_encoding);
+ fe = snew(font_encoding);
fe->next = NULL;
if (font->list->tail)
return fe;
}
+int kern_cmp(void *a, void *b)
+{
+ kern_pair const *ka = a, *kb = b;
+
+ if (ka->left < kb->left)
+ return -1;
+ if (ka->left > kb->left)
+ return 1;
+ if (ka->right < kb->right)
+ return -1;
+ if (ka->right > kb->right)
+ return 1;
+ return 0;
+}
+
+/* This wouldn't be necessary if C had closures. */
+static font_info *glyph_cmp_fi;
+
+static int glyph_cmp(void const *a, void const *b)
+{
+ return strcmp(glyph_cmp_fi->glyphs[*(unsigned short *)a],
+ glyph_cmp_fi->glyphs[*(unsigned short *)b]);
+}
+
+/*
+ * Set up the glyphsbyname index for a font.
+ */
+void font_index_glyphs(font_info *fi) {
+ int i;
+
+ fi->glyphsbyname = snewn(fi->nglyphs, unsigned short);
+ for (i = 0; i < fi->nglyphs; i++)
+ fi->glyphsbyname[i] = i;
+ glyph_cmp_fi = fi;
+ qsort(fi->glyphsbyname, fi->nglyphs, sizeof(fi->glyphsbyname[0]),
+ glyph_cmp);
+}
+
+int find_glyph(font_info *fi, char const *name) {
+ int i, j, k, r;
+
+ i = -1;
+ j = fi->nglyphs;
+ while (j-i > 1) {
+ k = (i + j) / 2;
+ r = strcmp(fi->glyphs[fi->glyphsbyname[k]], name);
+ if (r == 0)
+ return fi->glyphsbyname[k];
+ else if (r > 0)
+ j = k;
+ else
+ i = k;
+ }
+ return -1;
+}
+
static font_data *make_std_font(font_list *fontlist, char const *name)
{
- const int *widths;
int nglyphs;
+ font_info const *fi;
font_data *f;
font_encoding *fe;
int i;
- widths = ps_std_font_widths(name);
- if (!widths)
- return NULL;
+ for (fe = fontlist->head; fe; fe = fe->next)
+ if (strcmp(fe->font->info->name, name) == 0)
+ return fe->font;
- for (nglyphs = 0; ps_std_glyphs[nglyphs] != NULL; nglyphs++);
+ for (fi = all_fonts; fi; fi = fi->next)
+ if (strcmp(fi->name, name) == 0) break;
+ if (!fi) return NULL;
- f = mknew(font_data);
+ f = snew(font_data);
f->list = fontlist;
- f->name = name;
- f->nglyphs = nglyphs;
- f->glyphs = ps_std_glyphs;
- f->widths = widths;
- f->subfont_map = mknewa(subfont_map_entry, nglyphs);
+ f->info = fi;
+ nglyphs = f->info->nglyphs;
+ f->subfont_map = snewn(nglyphs, subfont_map_entry);
/*
* Our first subfont will contain all of US-ASCII. This isn't
fe->free_pos = 0xA1; /* only the top half is free */
f->latest_subfont = fe;
- for (i = 0; i < (int)lenof(f->bmp); i++)
- f->bmp[i] = 0xFFFF;
-
for (i = 0; i < nglyphs; i++) {
wchar_t ucs;
- ucs = ps_glyph_to_unicode(f->glyphs[i]);
- assert(ucs != 0xFFFF);
- f->bmp[ucs] = i;
+ ucs = ps_glyph_to_unicode(f->info->glyphs[i]);
if (ucs >= 0x20 && ucs <= 0x7E) {
- fe->vector[ucs] = f->glyphs[i];
+ fe->vector[ucs] = f->info->glyphs[i];
fe->indices[ucs] = i;
fe->to_unicode[ucs] = ucs;
f->subfont_map[i].subfont = fe;
return f;
}
+/* NB: arguments are glyph numbers from font->bmp. */
+static int find_kern(font_data *font, int lindex, int rindex)
+{
+ kern_pair wantkp;
+ kern_pair const *kp;
+
+ if (lindex == 0xFFFF || rindex == 0xFFFF)
+ return 0;
+ wantkp.left = lindex;
+ wantkp.right = rindex;
+ kp = find234(font->info->kerns, &wantkp, NULL);
+ if (kp == NULL)
+ return 0;
+ return kp->kern;
+}
+
static int string_width(font_data *font, wchar_t const *string, int *errs)
{
int width = 0;
+ int index, oindex;
if (errs)
*errs = 0;
+ oindex = 0xFFFF;
for (; *string; string++) {
- int index;
+ index = (*string < 0 || *string > 0xFFFF ? 0xFFFF :
+ font->info->bmp[*string]);
- index = font->bmp[(unsigned short)*string];
if (index == 0xFFFF) {
if (errs)
*errs = 1;
} else {
- width += font->widths[index];
+ width += find_kern(font, oindex, index) +
+ font->info->widths[index];
}
+ oindex = index;
}
return width;
struct paper_width_ctx {
int minspacewidth;
para_data *pdata;
+ paper_conf *conf;
};
static int paper_width_list(void *vctx, word *text, word *end, int *nspaces) {
str = L" ";
} else /* if (type == word_Quote) */ {
if (word->aux == quote_Open)
- str = L"\x2018"; /* FIXME: configurability! */
+ str = ctx->conf->lquote;
else
- str = L"\x2019"; /* FIXME: configurability! */
+ str = ctx->conf->rquote;
}
width = string_width(ctx->pdata->fonts[findex], str, &errs);
return paper_width_internal(vctx, word, NULL);
}
-static int paper_width_simple(para_data *pdata, word *text)
+static int paper_width_simple(para_data *pdata, word *text, paper_conf *conf)
{
struct paper_width_ctx ctx;
ctx.minspacewidth =
(pdata->sizes[FONT_NORMAL] *
string_width(pdata->fonts[FONT_NORMAL], L" ", NULL));
+ ctx.conf = conf;
return paper_width_list(&ctx, text, NULL, NULL);
}
static void wrap_paragraph(para_data *pdata, word *words,
- int w, int i1, int i2)
+ int w, int i1, int i2, paper_conf *conf)
{
wrappedline *wrapping, *p;
int spacewidth;
for (i = 0; i < NFONTS; i++)
if (line_height < pdata->sizes[i])
line_height = pdata->sizes[i];
- line_height *= 4096;
+ line_height *= UNITS_PER_PT;
}
spacewidth = (pdata->sizes[FONT_NORMAL] *
* comes up, but I'll make a random guess anyway and set my
* space width to half the point size.
*/
- spacewidth = pdata->sizes[FONT_NORMAL] * 4096 / 2;
+ spacewidth = pdata->sizes[FONT_NORMAL] * UNITS_PER_PT / 2;
}
/*
*/
ctx.minspacewidth = spacewidth * 3 / 5;
ctx.pdata = pdata;
+ ctx.conf = conf;
wrapping = wrap_para(words, w - i1, w - i2, paper_width, &ctx, spacewidth);
word *wd;
int len, wid, spaces;
- ldata = mknew(line_data);
+ ldata = snew(line_data);
ldata->pdata = pdata;
ldata->first = p->begin;
*/
for (l = last; l; l = l->prev) {
- l->bestcost = mknewa(int, ncols+1);
- l->vshortfall = mknewa(int, ncols+1);
- l->text = mknewa(int, ncols+1);
- l->space = mknewa(int, ncols+1);
- l->page_last = mknewa(line_data *, ncols+1);
+ l->bestcost = snewn(ncols+1, int);
+ l->vshortfall = snewn(ncols+1, int);
+ l->text = snewn(ncols+1, int);
+ l->space = snewn(ncols+1, int);
+ l->page_last = snewn(ncols+1, line_data *);
for (n = 0; n <= ncols; n++) {
int minheight, text = 0, space = 0;
*/
if (m != last && m->next && !m->next->page_break)
{
- int x = this_height - minheight;
+ int x = (this_height - minheight) / FUNITS_PER_PT *
+ 4096.0;
int xf;
xf = x & 0xFF;
page_data *page;
int text, space, head;
- page = mknew(page_data);
+ page = snew(page_data);
page->next = NULL;
page->prev = pt;
if (pt)
text += l->line_height;
l->page = page;
- l->ypos = text + space + head +
- space * (float)page->first_line->vshortfall[n] /
- page->first_line->space[n];
+ l->ypos = text + space + head;
+ if (page->first_line->space[n]) {
+ l->ypos += space * (float)page->first_line->vshortfall[n] /
+ page->first_line->space[n];
+ }
if (l == page->last_line)
break;
static void add_rect_to_page(page_data *page, int x, int y, int w, int h)
{
- rect *r = mknew(rect);
+ rect *r = snew(rect);
r->next = NULL;
if (page->last_rect)
{
text_fragment *frag;
- frag = mknew(text_fragment);
+ frag = snew(text_fragment);
frag->next = NULL;
if (page->last_text)
int x, int y, wchar_t *str)
{
char *text;
- int textpos, textwid, glyph;
+ int textpos, textwid, kern, glyph, oglyph;
font_encoding *subfont = NULL, *sf;
- text = mknewa(char, 1 + ustrlen(str));
+ text = snewn(1 + ustrlen(str), char);
textpos = textwid = 0;
+ glyph = 0xFFFF;
while (*str) {
- glyph = font->bmp[*str];
+ oglyph = glyph;
+ glyph = (*str < 0 || *str > 0xFFFF ? 0xFFFF :
+ font->info->bmp[*str]);
if (glyph == 0xFFFF) {
str++;
font->subfont_map[glyph].subfont = font->latest_subfont;
font->subfont_map[glyph].position = c;
- font->latest_subfont->vector[c] = font->glyphs[glyph];
+ font->latest_subfont->vector[c] = font->info->glyphs[glyph];
font->latest_subfont->indices[c] = glyph;
font->latest_subfont->to_unicode[c] = *str;
sf = font->latest_subfont;
}
- if (!subfont || sf != subfont) {
+ kern = find_kern(font, oglyph, glyph) * fontsize;
+
+ if (!subfont || sf != subfont || kern) {
if (subfont) {
text[textpos] = '\0';
add_string_to_page(page, x, y, subfont, fontsize, text,
textwid);
- x += textwid;
+ x += textwid + kern;
} else {
assert(textpos == 0);
}
textpos = 0;
+ textwid = 0;
subfont = sf;
}
text[textpos++] = font->subfont_map[glyph].position;
- textwid += font->widths[glyph] * fontsize;
+ textwid += font->info->widths[glyph] * fontsize;
str++;
}
static int render_text(page_data *page, para_data *pdata, line_data *ldata,
int x, int y, word *text, word *text_end, xref **xr,
int shortfall, int nspaces, int *nspace,
- keywordlist *keywords, indexdata *idx)
+ keywordlist *keywords, indexdata *idx, paper_conf *conf)
{
while (text && text != text_end) {
int style, type, findex, errs;
}
}
if (dest.type != NONE) {
- *xr = mknew(xref);
+ *xr = snew(xref);
(*xr)->dest = dest; /* structure copy */
if (page->last_xref)
page->last_xref->next = *xr;
goto nextword;
} else /* if (type == word_Quote) */ {
if (text->aux == quote_Open)
- str = L"\x2018"; /* FIXME: configurability! */
+ str = conf->lquote;
else
- str = L"\x2019"; /* FIXME: configurability! */
+ str = conf->rquote;
}
(void) string_width(pdata->fonts[findex], str, &errs);
if (errs && text->alt)
x = render_text(page, pdata, ldata, x, y, text->alt, NULL,
- xr, shortfall, nspaces, nspace, keywords, idx);
+ xr, shortfall, nspaces, nspace, keywords, idx,
+ conf);
else
x = render_string(page, pdata->fonts[findex],
pdata->sizes[findex], x, y, str);
* 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, indexdata *idx)
+ xref_dest *dest, keywordlist *keywords, indexdata *idx,
+ paper_conf *conf)
{
int nspace;
xref *xr;
left_x + ldata->aux_left_indent,
top_y - ldata->ypos,
ldata->aux_text, NULL, &xr, 0, 0, &nspace,
- keywords, idx);
+ keywords, idx, conf);
if (ldata->aux_text_2)
render_text(ldata->page, ldata->pdata, ldata,
x, top_y - ldata->ypos,
ldata->aux_text_2, NULL, &xr, 0, 0, &nspace,
- keywords, idx);
+ keywords, idx, conf);
}
nspace = 0;
* previous line.
*/
if (dest->type != NONE) {
- xr = mknew(xref);
+ xr = snew(xref);
xr->next = NULL;
xr->dest = *dest; /* structure copy */
if (ldata->page->last_xref)
left_x + ldata->xpos + extra_indent,
top_y - ldata->ypos, ldata->first, ldata->end,
&xr, shortfall, spaces, &nspace,
- keywords, idx);
+ keywords, idx, conf);
}
if (xr) {
*/
if (pdata->contents_entry && ldata->page != cxref_page) {
cxref_page = ldata->page;
- cxref = mknew(xref);
+ cxref = snew(xref);
cxref->next = NULL;
cxref->dest.type = PAGE;
if (pdata->contents_entry == index_placeholder) {
last_x = render_line(ldata, conf->left_margin,
conf->paper_height - conf->top_margin,
- &dest, keywords, idx);
+ &dest, keywords, idx, conf);
if (ldata == pdata->last)
break;
}
}
w = fake_word(num);
- wid = paper_width_simple(pdata, w);
+ wid = paper_width_simple(pdata, w, conf);
sfree(w);
for (x = 0; x < conf->base_width; x += conf->leader_separation)
static para_data *code_paragraph(int indent, word *words, paper_conf *conf)
{
- para_data *pdata = mknew(para_data);
+ para_data *pdata = snew(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] = conf->cb;
- pdata->fonts[FONT_EMPH] = conf->co;
- pdata->fonts[FONT_CODE] = conf->cr;
- pdata->sizes[FONT_NORMAL] =
- pdata->sizes[FONT_EMPH] =
- pdata->sizes[FONT_CODE] = 12;
+ setfont(pdata, &conf->fcode);
pdata->first = pdata->last = NULL;
pdata->outline_level = -1;
* which has the same emphasis. Form it into a word
* structure.
*/
- w = mknew(word);
+ w = snew(word);
w->next = NULL;
w->alt = NULL;
w->type = (prev == 0 ? word_WeakCode :
prev == 1 ? word_Emph : word_Normal);
- w->text = mknewa(wchar_t, t-start+1);
+ w->text = snewn(t-start+1, wchar_t);
memcpy(w->text, start, (t-start) * sizeof(wchar_t));
w->text[t-start] = '\0';
w->breaks = FALSE;
prev = -1;
}
- ldata = mknew(line_data);
+ ldata = snew(line_data);
ldata->pdata = pdata;
ldata->first = lhead;
ldata->end = NULL;
- ldata->line_height = conf->base_font_size * 4096;
+ ldata->line_height = conf->fcode.font_size * UNITS_PER_PT;
ldata->xpos = indent;
static para_data *rule_paragraph(int indent, paper_conf *conf)
{
- para_data *pdata = mknew(para_data);
+ para_data *pdata = snew(para_data);
line_data *ldata;
- ldata = mknew(line_data);
+ ldata = snew(line_data);
ldata->pdata = pdata;
ldata->first = NULL;
static word *fake_word(wchar_t *text)
{
- word *ret = mknew(word);
+ word *ret = snew(word);
ret->next = NULL;
ret->alt = NULL;
ret->type = word_Normal;
static word *fake_space_word(void)
{
- word *ret = mknew(word);
+ word *ret = snew(word);
ret->next = NULL;
ret->alt = NULL;
ret->type = word_WhiteSpace;
static word *fake_page_ref(page_data *page)
{
- word *ret = mknew(word);
+ word *ret = snew(word);
ret->next = NULL;
ret->alt = NULL;
ret->type = word_PageXref;
static word *fake_end_ref(void)
{
- word *ret = mknew(word);
+ word *ret = snew(word);
ret->next = NULL;
ret->alt = NULL;
ret->type = word_XrefEnd;