1 /*
2 * FIG : Facility for Interactive Generation of figures
3 * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4 * Parts Copyright (c) 1989-2015 by Brian V. Smith
5 * Parts Copyright (c) 1991 by Paul King
6 * Parts Copyright (c) 2016-2020 by Thomas Loimer
7 *
8 * Any party obtaining a copy of these files is granted, free of charge, a
9 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10 * nonexclusive right and license to deal in this software and documentation
11 * files (the "Software"), including without limitation the rights to use,
12 * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
13 * the Software, and to permit persons who receive copies from any such
14 * party to do so, with the only requirement being that the above copyright
15 * and this permission notice remain intact.
16 *
17 */
18
19 /*
20 * Import eps files.
21 * Copyright (c) 1992 by Brian Boyter
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <ctype.h>
29 #include <math.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #ifdef I18N
35 #include <locale.h>
36 #endif
37 #include <X11/Xlib.h>
38
39 #include "resources.h"
40 #include "object.h"
41 #include "f_picobj.h"
42 #include "w_msgpanel.h"
43 #include "w_setup.h"
44 #include "w_util.h"
45 #include "xfig_math.h"
46
47 /* u_ghostscript.c */
48 extern int gs_mediabox(char *file, int *llx, int *lly, int *urx, int *ury);
49 extern int gs_bitmap(char *file, F_pic *pic, int llx, int lly,
50 int urx, int ury);
51
52 static void lower(char *buf);
53 static int hex(char c);
54
55
56 /*
57 * Scan a pdf-file for a /MediaBox specification.
58 * Return 0 on success, -1 on failure.
59 */
60 static int
scan_mediabox(char * name,int * llx,int * lly,int * urx,int * ury)61 scan_mediabox(char *name, int *llx, int *lly, int *urx, int *ury)
62 {
63 /*
64 * The line length of pdfs should not exceed 256 characters. However, in
65 * pdfs, the line end character might be a carriage return, while
66 * fgets() reads lines ended by newlines.
67 */
68 char buf[512];
69 char *s;
70 int ret = -1; /* prime with failure */
71 double lx, ly, ux, uy;
72 FILE *file;
73
74 if ((file = fopen(name, "rb")) == NULL)
75 return -1;
76
77 while (fgets(buf, sizeof buf, file) != NULL) {
78 if ((s = strstr(buf, "/MediaBox"))) {
79 s = strchr(s, '[');
80 if (s && sscanf(s + 1, "%lf %lf %lf %lf",
81 &lx, &ly, &ux, &uy) == 4) {
82 *llx = (int)floor(lx);
83 *lly = (int)floor(ly);
84 *urx = (int)ceil(ux);
85 *ury = (int)ceil(uy);
86 ret = 0;
87 }
88 /* do not search for a second occurrence of /MediaBox */
89 break;
90 }
91 }
92
93 fclose(file);
94 return ret;
95 }
96
97
98 /*
99 * Check the bounding box, and provide a fallback for invalid values.
100 */
101 static void
correct_boundingbox(int * llx,int * lly,int * urx,int * ury,const char * box)102 correct_boundingbox(int *llx, int *lly, int *urx, int *ury, const char *box) {
103
104 if (*urx - *llx > 0 && *ury - *lly > 0)
105 return;
106
107 /* bad bounding box */
108 *llx = *lly = 0;
109 *urx = paper_sizes[appres.papersize].width * 72 / PIX_PER_INCH;
110 *ury = paper_sizes[appres.papersize].height * 72 / PIX_PER_INCH;
111 file_msg("Bad %s, assuming %s size", box,
112 paper_sizes[appres.papersize].sname);
113 app_flush();
114 }
115
116
117 /*
118 * Read a pdf file. The filename "name" refers to an uncompressed file.
119 * Return gs_bitmap().
120 */
121 int
read_pdf(F_pic * pic,struct xfig_stream * restrict pic_stream)122 read_pdf(F_pic *pic, struct xfig_stream *restrict pic_stream)
123 {
124 char *savelocale;
125 /* prime with an invalid bounding box */
126 int llx = 0, lly = 0, urx = 0, ury = 0;
127
128 if (uncompressed_content(pic_stream))
129 return FileInvalid;
130
131 /*
132 * Find the /MediaBox. First, do a simple text-scan for "/MediaBox",
133 * failing that, call ghostscript. Both scan_mediabox() and
134 * gs_mediabox() need the C or POSIX locale. Do not reset the locale to
135 * the environment, because read_pdf might be called from readfp_fig(),
136 * which temporarily sets and needs the C locale.
137 */
138 #ifdef I18N
139 savelocale = setlocale(LC_NUMERIC, NULL);
140 if (strcmp(savelocale, "C") && strcmp(savelocale, "POSIX"))
141 setlocale(LC_NUMERIC, "C");
142 else
143 savelocale = "";
144 #endif
145 if (scan_mediabox(pic_stream->content, &llx, &lly, &urx, &ury))
146 gs_mediabox(pic_stream->content, &llx, &lly, &urx, &ury);
147 #ifdef I18N
148 if (*savelocale)
149 setlocale(LC_NUMERIC, savelocale);
150 #endif
151
152 /* provide A4 or Letter bounding box, if reading /MediaBox fails */
153 correct_boundingbox(&llx, &lly, &urx, &ury, "/MediaBox");
154
155 /* set picture properties */
156 pic->hw_ratio = (float) (ury - lly) / (float) (urx - llx);
157 pic->pic_cache->subtype = T_PIC_PDF;
158 pic->pic_cache->size_x = round((urx - llx) * PIC_FACTOR);
159 pic->pic_cache->size_y = round((ury - lly) * PIC_FACTOR);
160 /* make 2-entry colormap here if we use monochrome */
161 pic->pic_cache->cmap[0].red = pic->pic_cache->cmap[0].green =
162 pic->pic_cache->cmap[0].blue = 0;
163 pic->pic_cache->cmap[1].red = pic->pic_cache->cmap[1].green =
164 pic->pic_cache->cmap[1].blue = 255;
165 pic->pic_cache->numcols = 0;
166
167 /* create the bitmap */
168 if (gs_bitmap(pic_stream->content, pic, llx, lly, urx, ury))
169 return FileInvalid;
170 else
171 return PicSuccess;
172 }
173
174
175 /*
176 * Read an EPS file.
177 * Return codes: PicSuccess (1): success
178 * FileInvalid (-2): invalid file
179 */
180 int
read_eps(F_pic * pic,struct xfig_stream * restrict pic_stream)181 read_eps(F_pic *pic, struct xfig_stream *restrict pic_stream)
182 {
183 bool bitmapz;
184 bool flag;
185 int llx, lly, urx, ury;
186 int nested;
187 char *cp;
188 unsigned char *mp;
189 unsigned int hexnib;
190 unsigned char *last;
191 char buf[300];
192 size_t nbitmap;
193
194 if (!rewind_stream(pic_stream))
195 return FileInvalid;
196
197 /* invalid bounding box */
198 llx = lly = urx = ury = 0;
199
200 /* scan for the bounding box */
201 nested = 0;
202 while (fgets(buf, sizeof buf, pic_stream->fp) != NULL) {
203 if (!nested && !strncmp(buf, "%%BoundingBox:", 14)) {
204 /* make sure doesn't say (atend) */
205 if (!strstr(buf, "(atend)")) {
206 float rllx, rlly, rurx, rury;
207
208 if (sscanf(strchr(buf, ':') + 1, "%f %f %f %f",
209 &rllx,&rlly,&rurx,&rury) < 4) {
210 file_msg("Bad EPS file: %s",
211 /* name might be a
212 temporary file */
213 pic->pic_cache->file);
214 return FileInvalid;
215 }
216 llx = floor(rllx);
217 lly = floor(rlly);
218 urx = ceil(rurx);
219 ury = ceil(rury);
220 if (appres.DEBUG)
221 fputs("Found EPS Bounding Box\n",
222 stderr);
223 break;
224 }
225 } else if (!strncmp(buf, "%%Begin", 7)) {
226 ++nested;
227 } else if (nested && !strncmp(buf, "%%End", 5)) {
228 --nested;
229 }
230 }
231
232 /* if bounding box is invalid, provide fallback values */
233 correct_boundingbox(&llx, &lly, &urx, &ury, "EPS bounding box");
234
235 /* set picture properties */
236 pic->hw_ratio = (float) (ury - lly) / (float) (urx - llx);
237 pic->pic_cache->subtype = T_PIC_EPS;
238 pic->pic_cache->size_x = round((urx - llx) * PIC_FACTOR);
239 pic->pic_cache->size_y = round((ury - lly) * PIC_FACTOR);
240 /* make 2-entry colormap here if we use monochrome */
241 pic->pic_cache->cmap[0].red = pic->pic_cache->cmap[0].green =
242 pic->pic_cache->cmap[0].blue = 0;
243 pic->pic_cache->cmap[1].red = pic->pic_cache->cmap[1].green =
244 pic->pic_cache->cmap[1].blue = 255;
245 pic->pic_cache->numcols = 0;
246
247
248 /* look for a preview bitmap */
249 bitmapz = False;
250 while (fgets(buf, sizeof buf, pic_stream->fp) != NULL) {
251 lower(buf);
252 if (!strncmp(buf, "%%beginpreview", 14)) {
253 sscanf(buf, "%%%%beginpreview: %d %d %*d",
254 &pic->pic_cache->bit_size.x,
255 &pic->pic_cache->bit_size.y);
256 bitmapz = true;
257 if (appres.DEBUG)
258 fputs("Found preview bitmap.\n", stderr);
259 break;
260 }
261 }
262
263 /* use ghostscript, if a preview bitmap does not exist */
264 if (!bitmapz && !uncompressed_content(pic_stream) && !gs_bitmap(
265 pic_stream->content, pic, llx, lly, urx, ury)) {
266 return PicSuccess;
267 }
268
269 if (!bitmapz) {
270 file_msg("EPS object read OK, but no preview bitmap "
271 "found/generated");
272 return PicSuccess;
273 }
274
275 /* bitmapz == true */
276 if (pic->pic_cache->bit_size.x <= 0 || pic->pic_cache->bit_size.y <=0) {
277 file_msg("Strange bounding-box/bitmap-size error, no bitmap "
278 "found/generated");
279 return FileInvalid;
280 }
281
282 /* read the preview bitmap */
283 nbitmap = (pic->pic_cache->bit_size.x + 7) / 8 *
284 pic->pic_cache->bit_size.y;
285 pic->pic_cache->bitmap = malloc(nbitmap);
286 if (pic->pic_cache->bitmap == NULL) {
287 file_msg("Could not allocate %zd bytes of memory for "
288 "EPS bitmap", nbitmap);
289 return PicSuccess;
290 }
291
292 if (appres.DEBUG)
293 fputs("Reading preview bitmap in EPS file.\n", stderr);
294
295 mp = pic->pic_cache->bitmap;
296 memset(mp, 0, nbitmap);
297 last = pic->pic_cache->bitmap + nbitmap;
298 flag = true;
299 while (fgets(buf, sizeof buf, pic_stream->fp) != NULL && mp < last) {
300 lower(buf);
301 if (!strncmp(buf, "%%endpreview", 12) ||
302 !strncmp(buf, "%%endimage", 10))
303 break;
304 cp = buf;
305 if (*cp != '%')
306 break;
307 cp++;
308 while (*cp != '\0') {
309 if (isxdigit(*cp)) {
310 hexnib = hex(*cp);
311 if (flag) {
312 flag = false;
313 *mp = hexnib << 4;
314 } else {
315 flag = true;
316 *mp = *mp + hexnib;
317 mp++;
318 if (mp >= last)
319 break;
320 }
321 }
322 cp++;
323 }
324 }
325 return PicSuccess;
326 }
327
328
329 static int
hex(char c)330 hex(char c)
331 {
332 if (isdigit(c))
333 return c - 48;
334 else
335 return c - 87;
336 }
337
338 static void
lower(char * buf)339 lower(char *buf)
340 {
341 while (*buf) {
342 if (isupper(*buf))
343 *buf = (char)tolower(*buf);
344 ++buf;
345 }
346 }
347