2 * PostScript backend for Halibut
9 static void ps_comment(FILE *fp
, char const *leader
, word
*words
);
10 static void ps_string_len(FILE *fp
, char const *str
, int len
);
11 static void ps_string(FILE *fp
, char const *str
);
13 paragraph
*ps_config_filename(char *filename
)
15 return cmdline_cfg_simple("ps-filename", filename
, NULL
);
18 void ps_backend(paragraph
*sourceform
, keywordlist
*keywords
,
19 indexdata
*idx
, void *vdoc
) {
20 document
*doc
= (document
*)vdoc
;
35 filename
= dupstr("output.ps");
36 for (p
= sourceform
; p
; p
= p
->next
) {
37 if (p
->type
== para_Config
) {
38 if (!ustricmp(p
->keyword
, L
"ps-filename")) {
40 filename
= dupstr(adv(p
->origkeyword
));
45 fp
= fopen(filename
, "w");
47 error(err_cantopenw
, filename
);
51 fprintf(fp
, "%%!PS-Adobe-3.0\n");
52 fprintf(fp
, "%%%%Creator: Halibut, %s\n", version
);
53 fprintf(fp
, "%%%%DocumentData: Clean7Bit\n");
54 fprintf(fp
, "%%%%LanguageLevel: 1\n");
55 for (pageno
= 0, page
= doc
->pages
; page
; page
= page
->next
)
57 fprintf(fp
, "%%%%Pages: %d\n", pageno
);
58 for (p
= sourceform
; p
; p
= p
->next
)
59 if (p
->type
== para_Title
)
60 ps_comment(fp
, "%%Title: ", p
->words
);
61 fprintf(fp
, "%%%%DocumentNeededResources:\n");
62 for (fe
= doc
->fonts
->head
; fe
; fe
= fe
->next
)
63 /* XXX This may request the same font multiple times. */
64 if (!fe
->font
->info
->fp
)
65 fprintf(fp
, "%%%%+ font %s\n", fe
->font
->info
->name
);
66 fprintf(fp
, "%%%%DocumentSuppliedResources: procset Halibut 0 2\n");
67 for (fe
= doc
->fonts
->head
; fe
; fe
= fe
->next
)
68 /* XXX This may request the same font multiple times. */
69 if (fe
->font
->info
->fp
)
70 fprintf(fp
, "%%%%+ font %s\n", fe
->font
->info
->name
);
71 fprintf(fp
, "%%%%EndComments\n");
73 fprintf(fp
, "%%%%BeginProlog\n");
74 fprintf(fp
, "%%%%BeginResource: procset Halibut 0 2\n");
76 * Supply a prologue function which allows a reasonably
77 * compressed representation of the text on the pages.
79 * "t" expects two arguments: a y-coordinate, and then an array.
80 * Elements of the array are processed sequentially as follows:
82 * - a number is treated as an x-coordinate
83 * - an array is treated as a (font, size) pair
86 * "r" takes four arguments, and behaves like "rectfill".
89 "/tdict 4 dict dup begin\n"
90 " /arraytype {aload pop scalefont setfont} bind def\n"
91 " /realtype {1 index moveto} bind def\n"
92 " /integertype /realtype load def\n"
93 " /stringtype {show} bind def\n"
95 "/t { tdict begin {dup type exec} forall end pop } bind def\n"
96 "/r { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
97 " neg 0 rlineto closepath fill } bind def\n");
101 * "p" generates a named destination referencing this page.
102 * "x" generates a link to a named destination.
103 * "u" generates a link to a URI.
105 * They all do nothing if pdfmark is undefined.
108 "/pdfmark where { pop\n"
109 " /p { [ /Dest 3 -1 roll /View [ /XYZ null null null ]\n"
110 " /DEST pdfmark } bind def\n"
111 " /x { [ /Dest 3 -1 roll /Rect 5 -1 roll /Border [0 0 0]\n"
112 " /Subtype /Link /ANN pdfmark } bind def\n"
113 " /u { 2 dict dup /Subtype /URI put dup /URI 4 -1 roll put\n"
114 " [ /Action 3 -1 roll /Rect 5 -1 roll /Border [0 0 0]\n"
115 " /Subtype /Link /ANN pdfmark } bind def\n"
117 " /p { pop } bind def\n"
118 " /x { pop pop } bind def\n"
122 fprintf(fp
, "%%%%EndResource\n");
123 fprintf(fp
, "%%%%EndProlog\n");
125 fprintf(fp
, "%%%%BeginSetup\n");
128 * Assign a destination name to each page for pdfmark purposes.
131 for (page
= doc
->pages
; page
; page
= page
->next
) {
134 buf
= snewn(12, char);
135 sprintf(buf
, "/p%d", pageno
);
140 * This is as good a place as any to put version IDs.
142 for (p
= sourceform
; p
; p
= p
->next
)
143 if (p
->type
== para_VersionID
)
144 ps_comment(fp
, "% ", p
->words
);
147 * Request the correct page size. We might want to bracket this
148 * with "%%BeginFeature: *PageSize A4" or similar, and "%%EndFeature",
149 * but that would require us to have a way of getting the name of
150 * the page size given its dimensions.
152 fprintf(fp
, "/setpagedevice where {\n");
153 fprintf(fp
, " pop 2 dict dup /PageSize [%g %g] put setpagedevice\n",
154 doc
->paper_width
/ FUNITS_PER_PT
,
155 doc
->paper_height
/ FUNITS_PER_PT
);
156 fprintf(fp
, "} if\n");
158 /* Outline etc, only if pdfmark is supported */
159 fprintf(fp
, "/pdfmark where { pop %% if\n");
160 fprintf(fp
, " [/PageMode/UseOutlines/DOCVIEW pdfmark\n");
161 noe
= doc
->n_outline_elements
;
162 for (oe
= doc
->outline_elements
; noe
; oe
++, noe
--) {
164 int titlelen
, count
, i
;
166 title
= pdf_outline_convert(oe
->pdata
->outline_title
, &titlelen
);
169 for (i
= 1; i
< noe
&& oe
[i
].level
> oe
->level
; i
++)
170 if (oe
[i
].level
== oe
->level
+ 1)
172 if (oe
->level
> 0) count
= -count
;
174 fprintf(fp
, " [/Title");
175 ps_string_len(fp
, title
, titlelen
);
177 fprintf(fp
, "/Dest%s/Count %d/OUT pdfmark\n",
178 (char *)oe
->pdata
->first
->page
->spare
, count
);
180 fprintf(fp
, "} if\n");
182 for (fe
= doc
->fonts
->head
; fe
; fe
= fe
->next
) {
183 /* XXX This may request the same font multiple times. */
184 if (fe
->font
->info
->fp
) {
187 fprintf(fp
, "%%%%BeginResource: font %s\n", fe
->font
->info
->name
);
188 rewind(fe
->font
->info
->fp
);
190 len
= fread(buf
, 1, sizeof(buf
), fe
->font
->info
->fp
);
191 fwrite(buf
, 1, len
, fp
);
192 } while (len
== sizeof(buf
));
193 fprintf(fp
, "%%%%EndResource\n");
195 fprintf(fp
, "%%%%IncludeResource: font %s\n",
196 fe
->font
->info
->name
);
201 * Re-encode the fonts.
204 for (fe
= doc
->fonts
->head
; fe
; fe
= fe
->next
) {
208 sprintf(fname
, "f%d", font_index
++);
209 fe
->name
= dupstr(fname
);
211 fprintf(fp
, "/%s findfont dup length dict begin\n",
212 fe
->font
->info
->name
);
213 fprintf(fp
, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
214 fprintf(fp
, "/Encoding [\n");
215 for (i
= 0; i
< 256; i
++)
216 fprintf(fp
, "/%s%c", fe
->vector
[i
] ? fe
->vector
[i
] : ".notdef",
217 i
% 4 == 3 ?
'\n' : ' ');
218 fprintf(fp
, "] def\n");
219 fprintf(fp
, "currentdict end\n");
220 fprintf(fp
, "/fontname-%s exch definefont /%s exch def\n\n",
223 fprintf(fp
, "%%%%EndSetup\n");
226 * Output the text and graphics.
229 for (page
= doc
->pages
; page
; page
= page
->next
) {
230 text_fragment
*frag
, *frag_end
;
237 fprintf(fp
, "%%%%Page: %d %d\n", pageno
, pageno
);
238 fprintf(fp
, "save %s p\n", (char *)page
->spare
);
240 for (xr
= page
->first_xref
; xr
; xr
= xr
->next
) {
241 fprintf(fp
, "[%g %g %g %g]",
242 xr
->lx
/FUNITS_PER_PT
, xr
->by
/FUNITS_PER_PT
,
243 xr
->rx
/FUNITS_PER_PT
, xr
->ty
/FUNITS_PER_PT
);
244 if (xr
->dest
.type
== PAGE
) {
245 fprintf(fp
, "%s x\n", (char *)xr
->dest
.page
->spare
);
247 ps_string(fp
, xr
->dest
.url
);
252 for (r
= page
->first_rect
; r
; r
= r
->next
) {
253 fprintf(fp
, "%g %g %g %g r\n",
254 r
->x
/ FUNITS_PER_PT
, r
->y
/ FUNITS_PER_PT
,
255 r
->w
/ FUNITS_PER_PT
, r
->h
/ FUNITS_PER_PT
);
258 frag
= page
->first_text
;
263 * Collect all the adjacent text fragments with the
266 for (frag_end
= frag
;
267 frag_end
&& frag_end
->y
== frag
->y
;
268 frag_end
= frag_end
->next
);
270 fprintf(fp
, "%g[", frag
->y
/ FUNITS_PER_PT
);
272 while (frag
&& frag
!= frag_end
) {
274 if (frag
->fe
!= fe
|| frag
->fontsize
!= fs
)
275 fprintf(fp
, "[%s %d]", frag
->fe
->name
, frag
->fontsize
);
279 fprintf(fp
, "%g", frag
->x
/FUNITS_PER_PT
);
280 ps_string(fp
, frag
->text
);
288 fprintf(fp
, "restore showpage\n");
291 fprintf(fp
, "%%%%EOF\n");
298 static void ps_comment(FILE *fp
, char const *leader
, word
*words
)
300 fprintf(fp
, "%s", leader
);
302 for (; words
; words
= words
->next
) {
306 switch (words
->type
) {
316 type
= removeattr(words
->type
);
320 text
= utoa_dup(words
->text
, CS_ASCII
);
322 case word_WhiteSpace
:
337 static void ps_string_len(FILE *fp
, char const *str
, int len
) {
341 for (c
= str
; c
< str
+len
; c
++) {
342 if (*c
< ' ' || *c
> '~')
344 else if (*c
== '(' || *c
== ')' || *c
== '\\')
351 for (c
= str
; c
< str
+len
; c
++) {
352 fprintf(fp
, "%02X", 0xFF & (int)*c
);
357 for (c
= str
; c
< str
+len
; c
++) {
358 if (*c
< ' ' || *c
> '~') {
359 fprintf(fp
, "\\%03o", 0xFF & (int)*c
);
361 if (*c
== '(' || *c
== ')' || *c
== '\\')
370 static void ps_string(FILE *fp
, char const *str
) {
371 ps_string_len(fp
, str
, strlen(str
));