1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id: screenshot.c 2620 2007-08-01 05:17:09Z kevinkofler $ */
3 
4 /*  TiEmu - a TI emulator
5  *  Copyright (c) 2005, Julien Blache
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25 
26 #include <stdio.h>
27 
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 
30 #include <zlib.h>
31 
32 #include "screenshot.h"
33 #include "version.h"
34 #include "struct.h"
35 #include "../core/ti68k_int.h"
36 #include "intl.h"
37 
38 /*
39  * Utility function for the EPS and PDF output
40  */
write_compressed_a85_screen(FILE * fp,GdkPixbuf * pixbuf,GError ** error)41 static gboolean write_compressed_a85_screen(FILE *fp, GdkPixbuf *pixbuf, GError **error)
42 {
43 	guchar *ubuf, *cbuf;
44 	int cbuflen;
45 	int r, i, j;
46 	int h, w;
47 	int rlen;
48 	int ret;
49 	z_stream s;
50 	int flush;
51 	int outlen;
52 	int a85count;
53 	unsigned long a85tuple;
54 	guchar a85block[6];
55 
56 	ubuf = gdk_pixbuf_get_pixels(pixbuf);
57 	rlen = gdk_pixbuf_get_rowstride(pixbuf);
58 	w = gdk_pixbuf_get_width(pixbuf);
59 	h = gdk_pixbuf_get_height(pixbuf);
60 
61 	a85count = 0;
62 	a85tuple = 0;
63 	a85block[5] = '\0';
64 
65 	/* buffer length = length + 0.1 * length + 12 (mandatory) */
66 	cbuflen = outlen = rlen + rlen / 10 + 12;
67 	cbuf = g_malloc(cbuflen);
68 
69 	if (cbuf == NULL) {
70 		g_set_error(error, 0, 0, _("Couldn't allocate memory!"));
71 		return FALSE;
72 	}
73 
74 	s.zalloc = Z_NULL;
75 	s.zfree = Z_NULL;
76 	s.opaque = Z_NULL;
77 
78 	ret = deflateInit(&s, Z_DEFAULT_COMPRESSION);
79 
80 	if (ret != Z_OK) {
81 		g_set_error(error, 0, 0, _("zlib init error"));
82 		g_free(cbuf);
83 		return FALSE;
84 	}
85 	for (r = 0; r < h; r++) {
86 		s.avail_in = w * 3;
87 		s.next_in = ubuf;
88 		do {
89 			s.avail_out = outlen;
90 			s.next_out = cbuf;
91 
92 			flush = (r == (h - 1)) ? Z_FINISH : Z_NO_FLUSH;
93 
94 			ret = deflate(&s, flush);
95 
96 			if (ret == Z_STREAM_ERROR) {
97 				g_set_error(error, 0, 0, _("zlib deflate error"));
98 				g_free(cbuf);
99 				return FALSE;
100 			}
101 
102 			cbuflen = outlen - s.avail_out;
103 			/* ASCII85 (base 85) encoding */
104 			for (i = 0; i < cbuflen; i++) {
105 				switch (a85count) {
106 				case 0:
107 					a85tuple |= (cbuf[i] << 24);
108 					a85count++;
109 					break;
110 				case 1:
111 					a85tuple |= (cbuf[i] << 16);
112 					a85count++;
113 					break;
114 				case 2:
115 					a85tuple |= (cbuf[i] << 8);
116 					a85count++;
117 					break;
118 				case 3:
119 					a85tuple |= (cbuf[i] << 0);
120 
121 					if (a85tuple == 0) {
122 						a85block[0] = 'z';
123 						a85block[1] = '\0';
124 					}
125 					else {
126 						/* The ASCII chars must be written in reverse order,
127 						 * hence -> a85block[4-j]
128 						 */
129 						for (j = 0; j < 5; j++) {
130 							a85block[4-j] = (unsigned char)(a85tuple % 85 + '!');
131 							a85tuple /= 85;
132 						}
133 					}
134 					fprintf(fp, "%s", a85block);
135 
136 					a85count = 0;
137 					a85tuple = 0;
138 					break;
139 				default:
140 					break;
141 				}
142 
143 				if ((i > 0) && (i % 32 == 0)) {
144 					fprintf(fp, "\n");
145 				}
146 			}
147 		} while (s.avail_out == 0);
148 		ubuf += rlen;
149 	}
150 
151 	if (a85count > 0) {
152 		a85count++;
153 		for (j = 0; j <= a85count; j++) {
154 			a85block[j] = (unsigned char)(a85tuple % 85 + '!');
155 			a85tuple /= 85;
156 		}
157                 /* Reverse order */
158 		for (j--; j > 0; j--) {
159 			fprintf(fp, "%c", a85block[j]);
160 		}
161 	}
162 
163         /* ASCII85 EOD marker + newline*/
164 	fprintf(fp, "~>\n");
165 
166 	deflateEnd(&s);
167 
168 	g_free(cbuf);
169 
170 	return TRUE;
171 }
172 
173 
174 /*
175  * Write out an Encapsulated PostScript file.
176  */
tiemu_screen_write_eps(const gchar * filename,GdkPixbuf * pixbuf,GError ** error)177 gboolean tiemu_screen_write_eps(const gchar *filename, GdkPixbuf *pixbuf, GError **error)
178 {
179 	int h, w;
180 	FILE *fp;
181 	time_t t;
182 	gboolean ret;
183 	GError *err = NULL;
184 
185 	fp = fopen(filename, "wb");
186 	if (fp == NULL) {
187 		g_set_error(error, 0, 0, _("Couldn't open destination file for writing!"));
188 		return FALSE;
189 	}
190 
191 	h = gdk_pixbuf_get_height(pixbuf);
192 	w = gdk_pixbuf_get_width(pixbuf);
193 
194 	time(&t);
195 
196 	fprintf(fp, "%%!PS-Adobe-3.0 EPSF-3.0\n");
197 	fprintf(fp, "%%%%Creator: TiEmu %s / PostScript output Copyright (C) 2005 Julien BLACHE\n", TIEMU_VERSION);
198 	fprintf(fp, "%%%%Title: TiEmu %s screenshot\n",
199 		ti68k_calctype_to_string(tihw.calc_type));
200 	fprintf(fp, "%%%%CreationDate: %s", ctime(&t));
201 	fprintf(fp, "%%%%LanguageLevel: 3\n");
202 	fprintf(fp, "%%%%BoundingBox: 0 0 %d %d\n", w, h);
203 	fprintf(fp, "\n");
204 	fprintf(fp, "%d %d scale\n", w, h);
205 
206 	fprintf(fp, "%d %d 8 [%d 0 0 -%d 0 %d] currentfile /ASCII85Decode filter /FlateDecode filter false 3 colorimage\n", w, h, w, h, h);
207 	ret = write_compressed_a85_screen(fp, pixbuf, &err);
208 
209 	if (!ret) {
210 		g_propagate_error(error, err);
211 		fclose(fp);
212 		unlink(filename);
213 		return FALSE;
214 	}
215 
216 	fprintf(fp, "%%%%EOF\n");
217 	fclose(fp);
218 
219 	return TRUE;
220 }
221 
222 /*
223  * Write out a PDF file.
224  */
tiemu_screen_write_pdf(const gchar * filename,GdkPixbuf * pixbuf,GError ** error)225 gboolean tiemu_screen_write_pdf(const gchar *filename, GdkPixbuf *pixbuf, GError **error)
226 {
227 	int h, w;
228 	FILE *fp;
229 	long obj5, obj6, obj7, xref, slen, slenp;
230 	struct tm *t;
231 	time_t tt;
232 	gboolean ret;
233 	GError *err;
234 
235 	fp = fopen(filename, "wb");
236 	if (fp == NULL) {
237 		g_set_error(error, 0, 0, _("Couldn't open destination file for writing!"));
238 		return FALSE;
239 	}
240 
241 	h = gdk_pixbuf_get_height(pixbuf);
242 	w = gdk_pixbuf_get_width(pixbuf);
243 
244 	tt = time(NULL);
245 	t = gmtime(&tt);
246 
247 	fprintf(fp, "%%PDF-1.5\n");
248 	fprintf(fp, "\n");
249 	fprintf(fp, "1 0 obj\n");
250 	fprintf(fp, "   << /Type /Catalog\n");
251 	fprintf(fp, "      /Outlines 2 0 R\n");
252 	fprintf(fp, "      /Pages 3 0 R\n");
253 	fprintf(fp, "   >>\n");
254 	fprintf(fp, "endobj\n");
255 	fprintf(fp, "\n");
256 	fprintf(fp, "2 0 obj\n");
257 	fprintf(fp, "   << /Type /Outlines\n");
258 	fprintf(fp, "      /Count 0\n");
259 	fprintf(fp, "   >>\n");
260 	fprintf(fp, "endobj\n");
261 	fprintf(fp, "\n");
262 	fprintf(fp, "3 0 obj\n");
263 	fprintf(fp, "   << /Type /Pages\n");
264 	fprintf(fp, "      /Kids [4 0 R]\n");
265 	fprintf(fp, "      /Count 1\n");
266 	fprintf(fp, "   >>\n");
267 	fprintf(fp, "endobj\n");
268 	fprintf(fp, "\n");
269 	fprintf(fp, "4 0 obj\n");
270 	fprintf(fp, "    << /Type /Page\n");
271 	fprintf(fp, "       /Parent 3 0 R\n");
272 	fprintf(fp, "       /MediaBox [0 0 %d %d]\n", w, h);
273 	fprintf(fp, "       /Contents 5 0 R\n");
274 	fprintf(fp, "       /Resources << /ProcSet 6 0 R >>\n");
275 	fprintf(fp, "    >>\n");
276 	fprintf(fp, "endobj\n");
277 	fprintf(fp, "\n");
278 
279 	/* Offset of object 5, for xref */
280 	obj5 = ftell(fp);
281 
282 	fprintf(fp, "5 0 obj\n");
283 	fprintf(fp, "    << /Length          >>\n");
284 
285 	/* Position of the stream length, to be written later on */
286 	slenp = ftell(fp) - 12;
287 
288 	fprintf(fp, "stream\n");
289 
290 	/* Start of the stream data */
291 	slen = ftell(fp);
292 
293 	fprintf(fp, "q\n");
294 	fprintf(fp, "%d 0 0 %d 0 0 cm\n", w, h);
295 	fprintf(fp, "BI\n");
296 	fprintf(fp, "  /W %d\n", w);
297 	fprintf(fp, "  /H %d\n", h);
298 
299 	/* RGB, 8 bits per component, ASCIIHex encoding */
300 	fprintf(fp, "  /CS /RGB\n");
301 	fprintf(fp, "  /BPC 8\n");
302 	fprintf(fp, "  /F [/A85 /FlateDecode]\n");
303 	fprintf(fp, "ID\n");
304 
305 	ret = write_compressed_a85_screen(fp, pixbuf, &err);
306 
307 	if (!ret) {
308 		g_propagate_error(error, err);
309 		fclose(fp);
310 		unlink(filename);
311 		return FALSE;
312 	}
313 
314 	fprintf(fp, "EI\n");
315 	fprintf(fp, "Q\n");
316 
317 	/* Go back and write the length of the stream */
318 	slen = ftell(fp) - slen - 1;
319 	fseek(fp, slenp, SEEK_SET);
320 	fprintf(fp, "%lu", slen);
321 	fseek(fp, 0L, SEEK_END);
322 
323 	fprintf(fp, "endstream\n");
324 	fprintf(fp, "endobj\n");
325 	fprintf(fp, "\n");
326 
327 	/* Offset of object 6, for xref */
328 	obj6 = ftell(fp);
329 
330 	fprintf(fp, "6 0 obj\n");
331 	fprintf(fp, "    [/PDF]\n");
332 	fprintf(fp, "endobj\n");
333 	fprintf(fp, "\n");
334 
335 	/* Offset of object 7, for xref */
336 	obj7 = ftell(fp);
337 
338 	fprintf(fp, "7 0 obj\n");
339 	fprintf(fp, "   << /Title (TiEmu %s screenshot)\n",
340 		ti68k_calctype_to_string(tihw.calc_type));
341 	fprintf(fp, "      /Creator (TiEmu / PDF output Copyright (C) 2005 Julien BLACHE)\n");
342 	fprintf(fp, "      /Producer (TiEmu %s)\n", TIEMU_VERSION);
343 	fprintf(fp, "      /CreationDate (D:%04d%02d%02d%02d%02d%02d+00'00')\n",
344 		1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
345 	fprintf(fp, "   >>\n");
346 	fprintf(fp, "endobj\n");
347 	fprintf(fp, "\n");
348 
349 	/* Offset of xref, for startxref below */
350 	xref = ftell(fp);
351 
352 	fprintf(fp, "xref\n");
353 	fprintf(fp, "0 8\n");
354 	fprintf(fp, "0000000000 65535 f \n");
355 	fprintf(fp, "0000000010 00000 n \n");
356 	fprintf(fp, "0000000094 00000 n \n");
357 	fprintf(fp, "0000000153 00000 n \n");
358 	fprintf(fp, "0000000229 00000 n \n");
359 	fprintf(fp, "%010lu 00000 n \n", obj5);
360 	fprintf(fp, "%010lu 00000 n \n", obj6);
361 	fprintf(fp, "%010lu 00000 n \n", obj7);
362 	fprintf(fp, "\n");
363 	fprintf(fp, "trailer\n");
364 	fprintf(fp, "    << /Size 8\n");
365 	fprintf(fp, "       /Root 1 0 R\n");
366 	fprintf(fp, "       /Info 7 0 R\n");
367 	fprintf(fp, "    >>\n");
368 	fprintf(fp, "startxref\n");
369 	fprintf(fp, "%lu\n", xref);
370 	fprintf(fp, "%%%%EOF\n");
371 
372 	fclose(fp);
373 
374 	return TRUE;
375 }
376