+
+/*
+ * In text on the page, PDF uses the PostScript font model, which
+ * means that glyphs are identified by PS strings and hence font
+ * encoding can be managed independently of the supplied encoding
+ * of the font. However, in the document outline, the PDF spec
+ * encodes in either PDFDocEncoding (a custom superset of
+ * ISO-8859-1) or UTF-16BE.
+ */
+char *pdf_outline_convert(wchar_t *s, int *len) {
+ char *ret;
+
+ ret = utoa_careful_dup(s, CS_PDF);
+
+ /*
+ * Very silly special case: if the returned string begins with
+ * FE FF, then the PDF reader will mistake it for a UTF-16BE
+ * string. So in this case we give up on PDFDocEncoding and
+ * encode it in UTF-16 straight away.
+ */
+ if (ret && ret[0] == '\xFE' && ret[1] == '\xFF') {
+ sfree(ret);
+ ret = NULL;
+ }
+
+ if (!ret) {
+ ret = utoa_dup_len(s, CS_UTF16BE, len);
+ } else {
+ *len = strlen(ret);
+ }
+
+ return ret;
+}
+
+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;
+
+ assert(n > 0);
+
+ level = items->level;
+
+ while (n > 0) {
+ char *title;
+ int titlelen;
+
+ /*
+ * Here we expect to be sitting on an item at the given
+ * level. So we start by constructing an outline entry for
+ * that item.
+ */
+ assert(items->level == level);
+
+ title = pdf_outline_convert(items->pdata->outline_title, &titlelen);
+
+ totalcount++;
+ curr = new_object(parent->list);
+ if (!first) first = curr;
+ last = curr;
+ objtext(curr, "<<\n/Title ");
+ pdf_string_len(objtext, curr, title, titlelen);
+ sfree(title);
+ objtext(curr, "\n/Parent ");
+ objref(curr, parent);
+ objtext(curr, "\n/Dest");
+ objdest(curr, items->pdata->first->page);
+ objtext(curr, "\n");
+ if (prev) {
+ objtext(curr, "/Prev ");
+ objref(curr, prev);
+ objtext(curr, "\n");
+
+ objtext(prev, "/Next ");
+ objref(prev, curr);
+ objtext(prev, "\n>>\n");
+ }
+ prev = curr;
+
+ items++, n--;
+ for (itemp = items; itemp < items+n && itemp->level > level;
+ itemp++);
+
+ if (itemp > items) {
+ char buf[80];
+ int count = make_outline(curr, items, itemp - items, FALSE);
+ if (!open)
+ count = -count;
+ else
+ totalcount += count;
+ sprintf(buf, "/Count %d\n", count);
+ objtext(curr, buf);
+ }
+
+ n -= itemp - items;
+ items = itemp;
+ }
+ objtext(prev, ">>\n");
+
+ assert(first && last);
+ objtext(parent, "/First ");
+ objref(parent, first);
+ objtext(parent, "\n/Last ");
+ objref(parent, last);
+ objtext(parent, "\n");
+
+ return totalcount;
+}
+
+static int pdf_versionid(FILE *fp, word *words)
+{
+ int ret;
+
+ ret = fprintf(fp, "%% ");
+
+ for (; words; words = words->next) {
+ char *text;
+ int type;
+
+ switch (words->type) {
+ case word_HyperLink:
+ case word_HyperEnd:
+ case word_UpperXref:
+ case word_LowerXref:
+ case word_XrefEnd:
+ case word_IndexRef:
+ continue;
+ }
+
+ type = removeattr(words->type);
+
+ switch (type) {
+ case word_Normal:
+ text = utoa_dup(words->text, CS_ASCII);
+ break;
+ case word_WhiteSpace:
+ text = dupstr(" ");
+ break;
+ case word_Quote:
+ text = dupstr("'");
+ break;
+ }
+
+ fputs(text, fp);
+ ret += strlen(text);
+ sfree(text);
+ }
+
+ ret += fprintf(fp, "\n");
+
+ return ret;
+}
+
+static void pdf_string_len(void (*add)(object *, char const *),
+ object *o, char const *str, int len)
+{
+ char const *p;
+
+ add(o, "(");
+ for (p = str; len > 0; p++, len--) {
+ char c[10];
+ if (*p < ' ' || *p > '~') {
+ sprintf(c, "\\%03o", 0xFF & (int)*p);
+ } else {
+ int n = 0;
+ if (*p == '\\' || *p == '(' || *p == ')')
+ c[n++] = '\\';
+ c[n++] = *p;
+ c[n] = '\0';
+ }
+ add(o, c);
+ }
+ add(o, ")");
+}
+
+static void pdf_string(void (*add)(object *, char const *),
+ object *o, char const *str)
+{
+ pdf_string_len(add, o, str, strlen(str));
+}