/* Hey EMACS -*- linux-c -*- */ /* TiEmu - a TI emulator * Copyright (c) 2005, Julien Blache * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include "screenshot.h" #include "struct.h" #include "../core/ti68k_int.h" #include "intl.h" /* * Utility function for the EPS and PDF output */ static gboolean write_compressed_a85_screen(FILE *fp, GdkPixbuf *pixbuf, GError **error) { guchar *ubuf, *cbuf; int cbuflen; int r, i, j; int h, w; int rlen; int ret; z_stream s; int flush; int outlen; int a85count; unsigned long a85tuple; guchar a85block[6]; ubuf = gdk_pixbuf_get_pixels(pixbuf); rlen = gdk_pixbuf_get_rowstride(pixbuf); w = gdk_pixbuf_get_width(pixbuf); h = gdk_pixbuf_get_height(pixbuf); a85count = 0; a85tuple = 0; a85block[5] = '\0'; /* buffer length = length + 0.1 * length + 12 (mandatory) */ cbuflen = outlen = rlen + rlen / 10 + 12; cbuf = g_malloc(cbuflen); if (cbuf == NULL) { g_set_error(error, 0, 0, _("Couldn't allocate memory!")); return FALSE; } s.zalloc = Z_NULL; s.zfree = Z_NULL; s.opaque = Z_NULL; ret = deflateInit(&s, Z_DEFAULT_COMPRESSION); if (ret != Z_OK) { g_set_error(error, 0, 0, _("zlib init error")); g_free(cbuf); return FALSE; } for (r = 0; r < h; r++) { s.avail_in = w * 3; s.next_in = ubuf; do { s.avail_out = outlen; s.next_out = cbuf; flush = (r == (h - 1)) ? Z_FINISH : Z_NO_FLUSH; ret = deflate(&s, flush); if (ret == Z_STREAM_ERROR) { g_set_error(error, 0, 0, _("zlib deflate error")); g_free(cbuf); return FALSE; } cbuflen = outlen - s.avail_out; /* ASCII85 (base 85) encoding */ for (i = 0; i < cbuflen; i++) { switch (a85count) { case 0: a85tuple |= (cbuf[i] << 24); a85count++; break; case 1: a85tuple |= (cbuf[i] << 16); a85count++; break; case 2: a85tuple |= (cbuf[i] << 8); a85count++; break; case 3: a85tuple |= (cbuf[i] << 0); if (a85tuple == 0) { a85block[0] = 'z'; a85block[1] = '\0'; } else { /* The ASCII chars must be written in reverse order, * hence -> a85block[4-j] */ for (j = 0; j < 5; j++) { a85block[4-j] = (unsigned char)(a85tuple % 85 + '!'); a85tuple /= 85; } } fprintf(fp, "%s", a85block); a85count = 0; a85tuple = 0; break; default: break; } if ((i > 0) && (i % 32 == 0)) { fprintf(fp, "\n"); } } } while (s.avail_out == 0); ubuf += rlen; } if (a85count > 0) { a85count++; for (j = 0; j <= a85count; j++) { a85block[j] = (unsigned char)(a85tuple % 85 + '!'); a85tuple /= 85; } /* Reverse order */ for (j--; j > 0; j--) { fprintf(fp, "%c", a85block[j]); } } /* ASCII85 EOD marker + newline*/ fprintf(fp, "~>\n"); deflateEnd(&s); g_free(cbuf); return TRUE; } /* * Write out an Encapsulated PostScript file. */ gboolean tiemu_screen_write_eps(const gchar *filename, GdkPixbuf *pixbuf, GError **error) { int h, w; FILE *fp; time_t t; gboolean ret; GError *err = NULL; fp = fopen(filename, "wb"); if (fp == NULL) { g_set_error(error, 0, 0, _("Couldn't open destination file for writing!")); return FALSE; } h = gdk_pixbuf_get_height(pixbuf); w = gdk_pixbuf_get_width(pixbuf); time(&t); fprintf(fp, "%%!PS-Adobe-3.0 EPSF-3.0\n"); fprintf(fp, "%%%%Creator: TiEmu %s / PostScript output Copyright (C) 2005 Julien BLACHE\n", VERSION); fprintf(fp, "%%%%Title: TiEmu %s screenshot\n", ti68k_calctype_to_string(tihw.calc_type)); fprintf(fp, "%%%%CreationDate: %s", ctime(&t)); fprintf(fp, "%%%%LanguageLevel: 3\n"); fprintf(fp, "%%%%BoundingBox: 0 0 %d %d\n", w, h); fprintf(fp, "\n"); fprintf(fp, "%d %d scale\n", w, h); 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); ret = write_compressed_a85_screen(fp, pixbuf, &err); if (!ret) { g_propagate_error(error, err); fclose(fp); unlink(filename); return FALSE; } fprintf(fp, "%%%%EOF\n"); fclose(fp); return TRUE; } /* * Write out a PDF file. */ gboolean tiemu_screen_write_pdf(const gchar *filename, GdkPixbuf *pixbuf, GError **error) { int h, w; FILE *fp; long obj5, obj6, obj7, xref, slen, slenp; struct tm *t; time_t tt; gboolean ret; GError *err; fp = fopen(filename, "wb"); if (fp == NULL) { g_set_error(error, 0, 0, _("Couldn't open destination file for writing!")); return FALSE; } h = gdk_pixbuf_get_height(pixbuf); w = gdk_pixbuf_get_width(pixbuf); tt = time(NULL); t = gmtime(&tt); fprintf(fp, "%%PDF-1.5\n"); fprintf(fp, "\n"); fprintf(fp, "1 0 obj\n"); fprintf(fp, " << /Type /Catalog\n"); fprintf(fp, " /Outlines 2 0 R\n"); fprintf(fp, " /Pages 3 0 R\n"); fprintf(fp, " >>\n"); fprintf(fp, "endobj\n"); fprintf(fp, "\n"); fprintf(fp, "2 0 obj\n"); fprintf(fp, " << /Type /Outlines\n"); fprintf(fp, " /Count 0\n"); fprintf(fp, " >>\n"); fprintf(fp, "endobj\n"); fprintf(fp, "\n"); fprintf(fp, "3 0 obj\n"); fprintf(fp, " << /Type /Pages\n"); fprintf(fp, " /Kids [4 0 R]\n"); fprintf(fp, " /Count 1\n"); fprintf(fp, " >>\n"); fprintf(fp, "endobj\n"); fprintf(fp, "\n"); fprintf(fp, "4 0 obj\n"); fprintf(fp, " << /Type /Page\n"); fprintf(fp, " /Parent 3 0 R\n"); fprintf(fp, " /MediaBox [0 0 %d %d]\n", w, h); fprintf(fp, " /Contents 5 0 R\n"); fprintf(fp, " /Resources << /ProcSet 6 0 R >>\n"); fprintf(fp, " >>\n"); fprintf(fp, "endobj\n"); fprintf(fp, "\n"); /* Offset of object 5, for xref */ obj5 = ftell(fp); fprintf(fp, "5 0 obj\n"); fprintf(fp, " << /Length >>\n"); /* Position of the stream length, to be written later on */ slenp = ftell(fp) - 12; fprintf(fp, "stream\n"); /* Start of the stream data */ slen = ftell(fp); fprintf(fp, "q\n"); fprintf(fp, "%d 0 0 %d 0 0 cm\n", w, h); fprintf(fp, "BI\n"); fprintf(fp, " /W %d\n", w); fprintf(fp, " /H %d\n", h); /* RGB, 8 bits per component, ASCIIHex encoding */ fprintf(fp, " /CS /RGB\n"); fprintf(fp, " /BPC 8\n"); fprintf(fp, " /F [/A85 /FlateDecode]\n"); fprintf(fp, "ID\n"); ret = write_compressed_a85_screen(fp, pixbuf, &err); if (!ret) { g_propagate_error(error, err); fclose(fp); unlink(filename); return FALSE; } fprintf(fp, "EI\n"); fprintf(fp, "Q\n"); /* Go back and write the length of the stream */ slen = ftell(fp) - slen - 1; fseek(fp, slenp, SEEK_SET); fprintf(fp, "%lu", slen); fseek(fp, 0L, SEEK_END); fprintf(fp, "endstream\n"); fprintf(fp, "endobj\n"); fprintf(fp, "\n"); /* Offset of object 6, for xref */ obj6 = ftell(fp); fprintf(fp, "6 0 obj\n"); fprintf(fp, " [/PDF]\n"); fprintf(fp, "endobj\n"); fprintf(fp, "\n"); /* Offset of object 7, for xref */ obj7 = ftell(fp); fprintf(fp, "7 0 obj\n"); fprintf(fp, " << /Title (TiEmu %s screenshot)\n", ti68k_calctype_to_string(tihw.calc_type)); fprintf(fp, " /Creator (TiEmu / PDF output Copyright (C) 2005 Julien BLACHE)\n"); fprintf(fp, " /Producer (TiEmu %s)\n", VERSION); fprintf(fp, " /CreationDate (D:%04d%02d%02d%02d%02d%02d+00'00')\n", 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); fprintf(fp, " >>\n"); fprintf(fp, "endobj\n"); fprintf(fp, "\n"); /* Offset of xref, for startxref below */ xref = ftell(fp); fprintf(fp, "xref\n"); fprintf(fp, "0 8\n"); fprintf(fp, "0000000000 65535 f \n"); fprintf(fp, "0000000010 00000 n \n"); fprintf(fp, "0000000094 00000 n \n"); fprintf(fp, "0000000153 00000 n \n"); fprintf(fp, "0000000229 00000 n \n"); fprintf(fp, "%010lu 00000 n \n", obj5); fprintf(fp, "%010lu 00000 n \n", obj6); fprintf(fp, "%010lu 00000 n \n", obj7); fprintf(fp, "\n"); fprintf(fp, "trailer\n"); fprintf(fp, " << /Size 8\n"); fprintf(fp, " /Root 1 0 R\n"); fprintf(fp, " /Info 7 0 R\n"); fprintf(fp, " >>\n"); fprintf(fp, "startxref\n"); fprintf(fp, "%lu\n", xref); fprintf(fp, "%%%%EOF\n"); fclose(fp); return TRUE; }