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