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