diff options
Diffstat (limited to 'src/plugins/thumbnailgtk_extractor.c')
-rw-r--r-- | src/plugins/thumbnailgtk_extractor.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/plugins/thumbnailgtk_extractor.c b/src/plugins/thumbnailgtk_extractor.c new file mode 100644 index 0000000..63ac359 --- /dev/null +++ b/src/plugins/thumbnailgtk_extractor.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | This file is part of libextractor. | ||
3 | (C) 2005, 2009, 2012 Vidyut Samanta and Christian Grothoff | ||
4 | |||
5 | libextractor is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | libextractor is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with libextractor; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | /** | ||
21 | * @file plugins/thumbnailgtk_extractor.c | ||
22 | * @author Christian Grothoff | ||
23 | * @brief this extractor produces a binary (!) encoded | ||
24 | * thumbnail of images (using gdk pixbuf). The bottom | ||
25 | * of the file includes a decoder method that can be used | ||
26 | * to reproduce the 128x128 PNG thumbnails. We use | ||
27 | * libmagic to test if the input data is actually an | ||
28 | * image before trying to give it to gtk. | ||
29 | */ | ||
30 | #include "platform.h" | ||
31 | #include "extractor.h" | ||
32 | #include <magic.h> | ||
33 | #include <glib.h> | ||
34 | #include <gdk-pixbuf/gdk-pixbuf.h> | ||
35 | |||
36 | /** | ||
37 | * Target size for the thumbnails (width and height). | ||
38 | */ | ||
39 | #define THUMBSIZE 128 | ||
40 | |||
41 | /** | ||
42 | * Maximum image size supported (to avoid unreasonable | ||
43 | * allocations) | ||
44 | */ | ||
45 | #define MAX_IMAGE_SIZE (32 * 1024 * 1024) | ||
46 | |||
47 | |||
48 | /** | ||
49 | * Global handle to MAGIC data. | ||
50 | */ | ||
51 | static magic_t magic; | ||
52 | |||
53 | |||
54 | /** | ||
55 | * Main method for the thumbnailer plugin. | ||
56 | * | ||
57 | * @param ec extraction context | ||
58 | */ | ||
59 | void | ||
60 | EXTRACTOR_thumbnailgtk_extract_method (struct EXTRACTOR_ExtractContext *ec) | ||
61 | { | ||
62 | GdkPixbufLoader *loader; | ||
63 | GdkPixbuf *in; | ||
64 | GdkPixbuf *out; | ||
65 | size_t length; | ||
66 | char *thumb; | ||
67 | unsigned long width; | ||
68 | unsigned long height; | ||
69 | char format[64]; | ||
70 | void *data; | ||
71 | uint64_t size; | ||
72 | size_t off; | ||
73 | ssize_t iret; | ||
74 | void *buf; | ||
75 | const char *mime; | ||
76 | |||
77 | iret = ec->read (ec->cls, | ||
78 | &data, | ||
79 | 16 * 1024); | ||
80 | if (-1 == iret) | ||
81 | return; | ||
82 | if (NULL == (mime = magic_buffer (magic, data, iret))) | ||
83 | return; | ||
84 | if (0 != strncmp (mime, | ||
85 | "image/", | ||
86 | strlen ("image/"))) | ||
87 | return; /* not an image */ | ||
88 | |||
89 | /* read entire image into memory */ | ||
90 | size = ec->get_size (ec->cls); | ||
91 | if (UINT64_MAX == size) | ||
92 | size = MAX_IMAGE_SIZE; /* unknown size, cap at max */ | ||
93 | if (size > MAX_IMAGE_SIZE) | ||
94 | return; /* FAR too big to be an image */ | ||
95 | if (NULL == (buf = malloc (size))) | ||
96 | return; /* too big to fit into memory on this system */ | ||
97 | |||
98 | /* start with data already read */ | ||
99 | memcpy (buf, data, iret); | ||
100 | off = iret; | ||
101 | while (off < size) | ||
102 | { | ||
103 | iret = ec->read (ec->cls, &data, size - off); | ||
104 | if (iret <= 0) | ||
105 | { | ||
106 | /* io error */ | ||
107 | free (buf); | ||
108 | return; | ||
109 | } | ||
110 | memcpy (buf + off, data, iret); | ||
111 | off += iret; | ||
112 | } | ||
113 | |||
114 | loader = gdk_pixbuf_loader_new (); | ||
115 | gdk_pixbuf_loader_write (loader, | ||
116 | buf, | ||
117 | size, NULL); | ||
118 | free (buf); | ||
119 | in = gdk_pixbuf_loader_get_pixbuf (loader); | ||
120 | gdk_pixbuf_loader_close (loader, NULL); | ||
121 | if (NULL == in) | ||
122 | { | ||
123 | g_object_unref (loader); | ||
124 | return; | ||
125 | } | ||
126 | g_object_ref (in); | ||
127 | g_object_unref (loader); | ||
128 | height = gdk_pixbuf_get_height (in); | ||
129 | width = gdk_pixbuf_get_width (in); | ||
130 | snprintf (format, | ||
131 | sizeof (format), | ||
132 | "%ux%u", | ||
133 | (unsigned int) width, | ||
134 | (unsigned int) height); | ||
135 | if (0 != ec->proc (ec->cls, | ||
136 | "thumbnailgtk", | ||
137 | EXTRACTOR_METATYPE_IMAGE_DIMENSIONS, | ||
138 | EXTRACTOR_METAFORMAT_UTF8, | ||
139 | "text/plain", | ||
140 | format, | ||
141 | strlen (format) + 1)) | ||
142 | { | ||
143 | g_object_unref (in); | ||
144 | return; | ||
145 | } | ||
146 | if ((height <= THUMBSIZE) && (width <= THUMBSIZE)) | ||
147 | { | ||
148 | g_object_unref (in); | ||
149 | return; | ||
150 | } | ||
151 | if (height > THUMBSIZE) | ||
152 | { | ||
153 | width = width * THUMBSIZE / height; | ||
154 | height = THUMBSIZE; | ||
155 | } | ||
156 | if (width > THUMBSIZE) | ||
157 | { | ||
158 | height = height * THUMBSIZE / width; | ||
159 | width = THUMBSIZE; | ||
160 | } | ||
161 | if ( (0 == height) || (0 == width) ) | ||
162 | { | ||
163 | g_object_unref (in); | ||
164 | return; | ||
165 | } | ||
166 | out = gdk_pixbuf_scale_simple (in, width, height, | ||
167 | GDK_INTERP_BILINEAR); | ||
168 | g_object_unref (in); | ||
169 | thumb = NULL; | ||
170 | length = 0; | ||
171 | if (NULL == out) | ||
172 | return; | ||
173 | if (! gdk_pixbuf_save_to_buffer (out, &thumb, | ||
174 | &length, | ||
175 | "png", NULL, | ||
176 | "compression", "9", | ||
177 | NULL)) | ||
178 | { | ||
179 | g_object_unref (out); | ||
180 | return; | ||
181 | } | ||
182 | g_object_unref (out); | ||
183 | if (NULL == thumb) | ||
184 | return; | ||
185 | ec->proc (ec->cls, | ||
186 | "thumbnailgtk", | ||
187 | EXTRACTOR_METATYPE_THUMBNAIL, | ||
188 | EXTRACTOR_METAFORMAT_BINARY, | ||
189 | "image/png", | ||
190 | thumb, length); | ||
191 | free (thumb); | ||
192 | } | ||
193 | |||
194 | |||
195 | /** | ||
196 | * This plugin sometimes is installed under the alias 'thumbnail'. | ||
197 | * So we need to provide a second entry method. | ||
198 | * | ||
199 | * @param ec extraction context | ||
200 | */ | ||
201 | void | ||
202 | EXTRACTOR_thumbnail_extract_method (struct EXTRACTOR_ExtractContext *ec) | ||
203 | { | ||
204 | EXTRACTOR_thumbnailgtk_extract_method (ec); | ||
205 | } | ||
206 | |||
207 | |||
208 | /** | ||
209 | * Initialize glib and load magic file. | ||
210 | */ | ||
211 | void __attribute__ ((constructor)) | ||
212 | ole_gobject_init () | ||
213 | { | ||
214 | g_type_init (); | ||
215 | magic = magic_open (MAGIC_MIME_TYPE); | ||
216 | if (0 != magic_load (magic, NULL)) | ||
217 | { | ||
218 | /* FIXME: how to deal with errors? */ | ||
219 | } | ||
220 | } | ||
221 | |||
222 | |||
223 | /** | ||
224 | * Destructor for the library, cleans up. | ||
225 | */ | ||
226 | void __attribute__ ((destructor)) | ||
227 | mime_ltdl_fini () | ||
228 | { | ||
229 | if (NULL != magic) | ||
230 | { | ||
231 | magic_close (magic); | ||
232 | magic = NULL; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | /* end of thumbnailgtk_extractor.c */ | ||