*/
/*
- * 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 <assert.h>
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;
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);
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);
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);
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->base_width - (conf->index_cols-1) * conf->index_gutter)
/ conf->index_cols;
- IGNORE(idx); /* FIXME */
-
/*
* First, set up some font structures.
*/
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) {
/* 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);
}
}
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);
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);
}
/*
}
/*
+ * 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.
*/
case word_HyperEnd:
case word_UpperXref:
case word_LowerXref:
+ case word_PageXref:
case word_XrefEnd:
case word_IndexRef:
return 0;
}
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;
frag->fe = fe;
frag->fontsize = size;
frag->text = dupstr(text);
+ frag->width = width;
}
/*
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.
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);
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;
}
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;
* 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;
* 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;
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)
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);
}
/*
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)
{