8951366e1f803a4819a04b4653f7ee3765b3c4b7
2 * PostScript backend for Halibut
9 static void ps_comment(FILE *fp
, char const *leader
, word
*words
);
11 paragraph
*ps_config_filename(char *filename
)
13 return cmdline_cfg_simple("ps-filename", filename
, NULL
);
16 void ps_backend(paragraph
*sourceform
, keywordlist
*keywords
,
17 indexdata
*idx
, void *vdoc
) {
18 document
*doc
= (document
*)vdoc
;
30 filename
= dupstr("output.ps");
31 for (p
= sourceform
; p
; p
= p
->next
) {
32 if (p
->type
== para_Config
) {
33 if (!ustricmp(p
->keyword
, L
"ps-filename")) {
35 filename
= dupstr(adv(p
->origkeyword
));
40 fp
= fopen(filename
, "w");
42 error(err_cantopenw
, filename
);
46 fprintf(fp
, "%%!PS-Adobe-3.0\n");
47 fprintf(fp
, "%%%%Creator: Halibut, %s\n", version
);
48 fprintf(fp
, "%%%%DocumentData: Clean8Bit\n");
49 fprintf(fp
, "%%%%LanguageLevel: 1\n");
50 for (pageno
= 0, page
= doc
->pages
; page
; page
= page
->next
)
52 fprintf(fp
, "%%%%Pages: %d\n", pageno
);
53 for (p
= sourceform
; p
; p
= p
->next
)
54 if (p
->type
== para_Title
)
55 ps_comment(fp
, "%%Title: ", p
->words
);
56 fprintf(fp
, "%%%%DocumentNeededResources:\n");
57 for (fe
= doc
->fonts
->head
; fe
; fe
= fe
->next
)
58 /* XXX This may request the same font multiple times. */
59 if (!fe
->font
->info
->fp
)
60 fprintf(fp
, "%%%%+ font %s\n", fe
->font
->info
->name
);
61 fprintf(fp
, "%%%%DocumentSuppliedResources: procset Halibut 0 1\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
, "%%%%EndComments\n");
68 fprintf(fp
, "%%%%BeginProlog\n");
69 fprintf(fp
, "%%%%BeginResource: procset Halibut 0 1\n");
71 * Supply a prologue function which allows a reasonably
72 * compressed representation of the text on the pages.
74 * "t" expects two arguments: a y-coordinate, and then an array.
75 * Elements of the array are processed sequentially as follows:
77 * - a number is treated as an x-coordinate
78 * - an array is treated as a (font, size) pair
81 * "r" takes four arguments, and behaves like "rectfill".
84 "/tdict 4 dict dup begin\n"
85 " /arraytype {aload pop scalefont setfont} bind def\n"
86 " /realtype {1 index moveto} bind def\n"
87 " /integertype /realtype load def\n"
88 " /stringtype {show} bind def\n"
90 "/t { tdict begin {dup type exec} forall end pop } bind def\n"
91 "/r { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
92 " neg 0 rlineto closepath fill } bind def\n");
94 fprintf(fp
, "%%%%EndResource\n");
95 fprintf(fp
, "%%%%EndProlog\n");
97 fprintf(fp
, "%%%%BeginSetup\n");
100 * This is as good a place as any to put version IDs.
102 for (p
= sourceform
; p
; p
= p
->next
)
103 if (p
->type
== para_VersionID
)
104 ps_comment(fp
, "% ", p
->words
);
107 * Request the correct page size. We might want to bracket this
108 * with "%%BeginFeature: *PageSize A4" or similar, and "%%EndFeature",
109 * but that would require us to have a way of getting the name of
110 * the page size given its dimensions.
112 fprintf(fp
, "/setpagedevice where {\n");
113 fprintf(fp
, " pop 2 dict dup /PageSize [%g %g] put setpagedevice\n",
114 doc
->paper_width
/ FUNITS_PER_PT
,
115 doc
->paper_height
/ FUNITS_PER_PT
);
116 fprintf(fp
, "} if\n");
118 for (fe
= doc
->fonts
->head
; fe
; fe
= fe
->next
) {
119 /* XXX This may request the same font multiple times. */
120 if (fe
->font
->info
->fp
) {
123 fprintf(fp
, "%%%%BeginResource: font %s\n", fe
->font
->info
->name
);
124 rewind(fe
->font
->info
->fp
);
126 len
= fread(buf
, 1, sizeof(buf
), fe
->font
->info
->fp
);
127 fwrite(buf
, 1, len
, fp
);
128 } while (len
== sizeof(buf
));
129 fprintf(fp
, "%%%%EndResource\n");
131 fprintf(fp
, "%%%%IncludeResource: font %s\n",
132 fe
->font
->info
->name
);
137 * Re-encode the fonts.
140 for (fe
= doc
->fonts
->head
; fe
; fe
= fe
->next
) {
144 sprintf(fname
, "f%d", font_index
++);
145 fe
->name
= dupstr(fname
);
147 fprintf(fp
, "/%s findfont dup length dict begin\n",
148 fe
->font
->info
->name
);
149 fprintf(fp
, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
150 fprintf(fp
, "/Encoding [\n");
151 for (i
= 0; i
< 256; i
++)
152 fprintf(fp
, "/%s%c", fe
->vector
[i
] ? fe
->vector
[i
] : ".notdef",
153 i
% 4 == 3 ?
'\n' : ' ');
154 fprintf(fp
, "] def\n");
155 fprintf(fp
, "currentdict end\n");
156 fprintf(fp
, "/fontname-%s exch definefont /%s exch def\n\n",
159 fprintf(fp
, "%%%%EndSetup\n");
162 * Output the text and graphics.
165 for (page
= doc
->pages
; page
; page
= page
->next
) {
166 text_fragment
*frag
, *frag_end
;
172 fprintf(fp
, "%%%%Page: %d %d\n", pageno
, pageno
);
173 fprintf(fp
, "save\n");
178 * I used this diagnostic briefly to ensure that
179 * cross-reference rectangles were being put where they
182 for (xr
= page
->first_xref
; xr
; xr
= xr
->next
) {
183 fprintf(fp
, "gsave 0.7 setgray %g %g moveto",
184 xr
->lx
/FUNITS_PER_PT
, xr
->ty
/FUNITS_PER_PT
);
185 fprintf(fp
, " %g %g lineto %g %g lineto",
186 xr
->lx
/FUNITS_PER_PT
, xr
->by
/FUNITS_PER_PT
,
187 xr
->rx
/FUNITS_PER_PT
, xr
->by
/FUNITS_PER_PT
);
188 fprintf(fp
, " %g %g lineto closepath fill grestore\n",
189 xr
->rx
/FUNITS_PER_PT
, xr
->ty
/FUNITS_PER_PT
);
194 for (r
= page
->first_rect
; r
; r
= r
->next
) {
195 fprintf(fp
, "%g %g %g %g r\n",
196 r
->x
/ FUNITS_PER_PT
, r
->y
/ FUNITS_PER_PT
,
197 r
->w
/ FUNITS_PER_PT
, r
->h
/ FUNITS_PER_PT
);
200 frag
= page
->first_text
;
207 * Collect all the adjacent text fragments with the
210 for (frag_end
= frag
;
211 frag_end
&& frag_end
->y
== frag
->y
;
212 frag_end
= frag_end
->next
);
214 fprintf(fp
, "%g[", frag
->y
/ FUNITS_PER_PT
);
216 while (frag
&& frag
!= frag_end
) {
218 if (frag
->fe
!= fe
|| frag
->fontsize
!= fs
)
219 fprintf(fp
, "[%s %d]", frag
->fe
->name
, frag
->fontsize
);
223 fprintf(fp
, "%g(", frag
->x
/FUNITS_PER_PT
);
224 for (c
= frag
->text
; *c
; c
++) {
225 if (*c
== '(' || *c
== ')' || *c
== '\\')
237 fprintf(fp
, "restore showpage\n");
240 fprintf(fp
, "%%%%EOF\n");
247 static void ps_comment(FILE *fp
, char const *leader
, word
*words
)
249 fprintf(fp
, "%s", leader
);
251 for (; words
; words
= words
->next
) {
255 switch (words
->type
) {
265 type
= removeattr(words
->type
);
269 text
= utoa_dup(words
->text
, CS_ASCII
);
271 case word_WhiteSpace
: