+ indexdata *idx, void *vdoc) {
+ document *doc = (document *)vdoc;
+ int font_index;
+ font_encoding *fe;
+ page_data *page;
+ int pageno;
+ FILE *fp;
+ char *filename;
+ paragraph *p;
+ outline_element *oe;
+ int noe;
+ int cc; /* Character count on current line */
+
+ IGNORE(keywords);
+ IGNORE(idx);
+
+ filename = dupstr("output.ps");
+ for (p = sourceform; p; p = p->next) {
+ if (p->type == para_Config) {
+ if (!ustricmp(p->keyword, L"ps-filename")) {
+ sfree(filename);
+ filename = dupstr(adv(p->origkeyword));
+ }
+ }
+ }
+
+ fp = fopen(filename, "w");
+ if (!fp) {
+ error(err_cantopenw, filename);
+ return;
+ }
+
+ fprintf(fp, "%%!PS-Adobe-3.0\n");
+ fprintf(fp, "%%%%Creator: Halibut, %s\n", version);
+ fprintf(fp, "%%%%DocumentData: Clean7Bit\n");
+ fprintf(fp, "%%%%LanguageLevel: 1\n");
+ for (pageno = 0, page = doc->pages; page; page = page->next)
+ pageno++;
+ fprintf(fp, "%%%%Pages: %d\n", pageno);
+ for (p = sourceform; p; p = p->next)
+ if (p->type == para_Title)
+ ps_comment(fp, "%%Title: ", p->words);
+ fprintf(fp, "%%%%DocumentNeededResources:\n");
+ for (fe = doc->fonts->head; fe; fe = fe->next)
+ /* XXX This may request the same font multiple times. */
+ if (!fe->font->info->fontfile)
+ fprintf(fp, "%%%%+ font %s\n", fe->font->info->name);
+ fprintf(fp, "%%%%DocumentSuppliedResources: procset Halibut 0 3\n");
+ for (fe = doc->fonts->head; fe; fe = fe->next)
+ /* XXX This may request the same font multiple times. */
+ if (fe->font->info->fontfile)
+ fprintf(fp, "%%%%+ font %s\n", fe->font->info->name);
+ fprintf(fp, "%%%%EndComments\n");
+
+ fprintf(fp, "%%%%BeginProlog\n");
+ fprintf(fp, "%%%%BeginResource: procset Halibut 0 3\n");
+ /*
+ * Supply a prologue function which allows a reasonably
+ * compressed representation of the text on the pages.
+ *
+ * "t" expects two arguments: a y-coordinate, and then an array.
+ * Elements of the array are processed sequentially as follows:
+ *
+ * - a number is treated as an x-coordinate
+ * - an array is treated as a (font, size) pair
+ * - a string is shown
+ *
+ * "r" takes four arguments, and behaves like "rectfill".
+ */
+ fprintf(fp,
+ "/tdict 4 dict dup begin\n"
+ " /arraytype {aload pop scalefont setfont} bind def\n"
+ " /realtype {1 index moveto} bind def\n"
+ " /integertype /realtype load def\n"
+ " /stringtype {show} bind def\n"
+ "end def\n"
+ "/t { tdict begin {dup type exec} forall end pop } bind def\n"
+ "/r { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
+ " neg 0 rlineto closepath fill } bind def\n");
+ /*
+ * pdfmark wrappers
+ *
+ * "p" generates a named destination referencing this page.
+ * "x" generates a link to a named destination.
+ * "u" generates a link to a URI.
+ * "o" generates an outline entry.
+ * "m" generates a general pdfmark.
+ *
+ * They all do nothing if pdfmark is undefined.
+ */
+ fprintf(fp,
+ "/pdfmark where { pop\n"
+ " /p { [ /Dest 3 -1 roll /View [ /XYZ null null null ]\n"
+ " /DEST pdfmark } bind def\n"
+ " /x { [ /Dest 3 -1 roll /Rect 5 -1 roll /Border [0 0 0]\n"
+ " /Subtype /Link /ANN pdfmark } bind def\n"
+ " /u { 2 dict dup /Subtype /URI put dup /URI 4 -1 roll put\n"
+ " [ /Action 3 -1 roll /Rect 5 -1 roll /Border [0 0 0]\n"
+ " /Subtype /Link /ANN pdfmark } bind def\n"
+ " /o { [ /Count 3 -1 roll /Dest 5 -1 roll /Title 7 -1 roll\n"
+ " /OUT pdfmark } bind def\n"
+ " /m /pdfmark load def\n"
+ "}\n");
+ fprintf(fp, "{\n"
+ " /p { pop } bind def\n"
+ " /x { pop pop } bind def\n"
+ " /u /x load def\n"
+ " /o { pop pop pop } bind def\n"
+ " /m /cleartomark load def\n"
+ "} ifelse\n");
+
+ fprintf(fp, "%%%%EndResource\n");
+ fprintf(fp, "%%%%EndProlog\n");
+
+ fprintf(fp, "%%%%BeginSetup\n");
+
+ /*
+ * Assign a destination name to each page for pdfmark purposes.
+ */
+ pageno = 0;
+ for (page = doc->pages; page; page = page->next) {
+ char *buf;
+ pageno++;
+ buf = snewn(12, char);
+ sprintf(buf, "/p%d", pageno);
+ page->spare = buf;
+ }
+
+ /*
+ * This is as good a place as any to put version IDs.
+ */
+ for (p = sourceform; p; p = p->next)
+ if (p->type == para_VersionID)
+ ps_comment(fp, "% ", p->words);
+
+ cc = 0;
+ /*
+ * Request the correct page size. We might want to bracket this
+ * with "%%BeginFeature: *PageSize A4" or similar, and "%%EndFeature",
+ * but that would require us to have a way of getting the name of
+ * the page size given its dimensions.
+ */
+ ps_token(fp, &cc, "/setpagedevice where {\n");
+ ps_token(fp, &cc, " pop 2 dict dup /PageSize [%g %g] put setpagedevice\n",
+ doc->paper_width / FUNITS_PER_PT,
+ doc->paper_height / FUNITS_PER_PT);
+ ps_token(fp, &cc, "} if\n");
+
+ ps_token(fp, &cc, "[/PageMode/UseOutlines/DOCVIEW m\n");
+ noe = doc->n_outline_elements;
+ for (oe = doc->outline_elements; noe; oe++, noe--) {
+ char *title;
+ int titlelen, count, i;
+
+ title = pdf_outline_convert(oe->pdata->outline_title, &titlelen);
+ if (oe->level == 0) {
+ ps_token(fp, &cc, "[/Title");
+ ps_string_len(fp, &cc, title, titlelen);
+ ps_token(fp, &cc, "/DOCINFO m\n");
+ }
+
+ count = 0;
+ for (i = 1; i < noe && oe[i].level > oe->level; i++)
+ if (oe[i].level == oe->level + 1)
+ count++;
+ if (oe->level > 0) count = -count;
+
+ ps_string_len(fp, &cc, title, titlelen);
+ sfree(title);
+ ps_token(fp, &cc, "%s %d o\n",
+ (char *)oe->pdata->first->page->spare, count);
+ }
+
+ for (fe = doc->fonts->head; fe; fe = fe->next) {
+ /* XXX This may request the same font multiple times. */
+ if (fe->font->info->fontfile) {
+ fprintf(fp, "%%%%BeginResource: font %s\n", fe->font->info->name);
+ if (fe->font->info->filetype == TYPE1)
+ pf_writeps(fe->font->info, fp);
+ else
+ sfnt_writeps(fe->font->info, fp);
+ fprintf(fp, "%%%%EndResource\n");
+ } else {
+ fprintf(fp, "%%%%IncludeResource: font %s\n",
+ fe->font->info->name);
+ }
+ }
+