updated on Thu Jan 26 00:18:00 UTC 2012
[aur-mirror.git] / chm-thumbnailer / chm-thumbnailer.c
blob4f61846c3cdeda4aa1b2f99671d2deca9ebc6033
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
5 #include <chm_lib.h>
6 #include <gdk-pixbuf/gdk-pixbuf.h>
7 #include <libxml/HTMLparser.h>
8 #include <libxml/SAX2.h>
9 #include <libxml/uri.h>
10 #include <libxml/xmlstring.h>
12 static inline unsigned int get_word(const unsigned char *buffer)
14 unsigned int ret = 0;
16 ret |= buffer[0] << 0;
17 ret |= buffer[1] << 8;
19 return ret;
22 static int get_content(struct chmFile *file, const char *path,
23 unsigned char **buffer)
25 struct chmUnitInfo ui;
27 if (chm_resolve_object(file, path, &ui) == CHM_RESOLVE_FAILURE)
28 return 0;
29 if ((*buffer = malloc(ui.length)) == NULL)
30 return 0;
31 if (chm_retrieve_object(file, &ui, *buffer, 0, ui.length) !=
32 (long long)ui.length)
34 free(*buffer);
35 return 0;
38 return ui.length;
41 static char *get_home_path(struct chmFile *file)
43 unsigned char *buffer;
44 unsigned int current, type, len, total;
45 char *path = NULL;
47 if ((total = get_content(file, "/#SYSTEM", &buffer)) == 0)
48 return NULL;
49 for (current = 4; current < total; current += 4 + len)
51 type = get_word(buffer + current);
52 len = get_word(buffer + current + 2);
53 if (type == 2)
55 if (current + 4 + len <= total && (path = malloc(len + 1)))
57 path[0] = '/';
58 memcpy(path + 1, buffer + current + 4, len);
60 break;
64 free(buffer);
65 return path;
68 GdkPixbuf *get_image_from_data(unsigned char *buffer, int length)
70 GInputStream *input;
71 GdkPixbuf *image;
73 input = g_memory_input_stream_new_from_data(buffer, length, NULL);
74 image = gdk_pixbuf_new_from_stream(input, NULL, NULL);
75 g_input_stream_close(input, NULL, NULL);
77 return image;
80 static inline int round_div(const int x, const int y)
82 return (x + y / 2) / y;
85 GdkPixbuf *scale_image(GdkPixbuf *original, int size)
87 int width, height;
88 GdkInterpType type;
90 width = gdk_pixbuf_get_width(original);
91 height = gdk_pixbuf_get_height(original);
92 if (width < height)
94 width = round_div(width * size, height);
95 type = height <= size ? GDK_INTERP_TILES : GDK_INTERP_HYPER;
96 height = size;
98 else
100 height = round_div(height * size, width);
101 type = width <= size ? GDK_INTERP_TILES : GDK_INTERP_HYPER;
102 width = size;
105 return gdk_pixbuf_scale_simple(original, width, height, type);
108 static const double lambda = 0.993;
110 struct inner_file
112 struct chmFile *file;
113 xmlChar *path;
114 int size;
115 double w;
116 const xmlChar *current;
119 static int get_content_length(struct chmFile *file, const char *path)
121 struct chmUnitInfo ui;
123 if (chm_resolve_object(file, path, &ui) == CHM_RESOLVE_FAILURE)
124 return 0;
126 return ui.length;
129 static void startelem(struct inner_file *fp, const xmlChar *name,
130 const xmlChar **atts)
132 int size;
133 xmlChar *uri = NULL;
135 if (!xmlStrcasecmp(name, (const xmlChar *)"img"))
137 while (*atts)
139 if (!xmlStrcasecmp(*atts, (const xmlChar *)"src"))
141 uri = xmlBuildURI(atts[1], fp->current);
142 size = get_content_length(fp->file, (const char *)uri) *
143 fp->w;
144 fp->w *= lambda;
145 if (size > fp->size)
147 free(fp->path);
148 fp->path = uri;
149 fp->size = size;
151 else
152 free(uri);
153 break;
155 atts += 2;
160 static char *parse_home(struct chmFile *file, unsigned char *buffer,
161 const char *current)
163 struct inner_file fp = { .file = file, .path = NULL, .size = 0, .w = 1.0,
164 .current = xmlURIEscapeStr((const xmlChar *)current, NULL) };
165 htmlSAXHandler sax = { .startElement = (startElementSAXFunc)startelem };
167 htmlSAXParseDoc(buffer, NULL, &sax, &fp);
169 free((void *)fp.current);
170 return (char *)fp.path;
173 int main(const int argc, const char *argv[])
175 struct chmFile *file;
176 char *homepath, *imagepath = NULL;
177 unsigned char *buffer;
178 int buffer_length, size;
179 GdkPixbuf *image, *output;
181 if (argc != 4 || (size = atoi(argv[3])) <= 0)
182 exit(EXIT_FAILURE);
184 g_type_init();
185 if ((file = chm_open(argv[1])) == NULL)
186 exit(EXIT_FAILURE);
187 if ((homepath = get_home_path(file)) == NULL)
188 exit(EXIT_FAILURE);
189 if ((buffer_length = get_content(file, homepath, &buffer)) == 0)
190 exit(EXIT_FAILURE);
191 if ((image = get_image_from_data(buffer, buffer_length)) == NULL)
193 if ((imagepath = parse_home(file, buffer, homepath)) == NULL)
194 exit(EXIT_FAILURE);
195 free(buffer);
196 if ((buffer_length = get_content(file, imagepath, &buffer)) == 0)
197 exit(EXIT_FAILURE);
198 if ((image = get_image_from_data(buffer, buffer_length)) == NULL)
199 exit(EXIT_FAILURE);
202 output = scale_image(image, size);
203 gdk_pixbuf_save(output, argv[2], "png", NULL, "compression", "9", NULL);
205 chm_close(file);
206 g_object_unref(image);
207 g_object_unref(output);
208 free(buffer);
209 free(homepath);
210 free(imagepath);
212 return EXIT_SUCCESS;