1. Changed license copyright year
[apps/home/mobileprint.git] / mobileprint / previewgen / lib / previewgen.c
1 /*
2 *  Mobileprint
3 *
4 * Copyright 2012-2013  Samsung Electronics Co., Ltd
5
6 * Licensed under the Flora License, Version 1.1 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9
10 * http://floralicense.org/license/
11
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20 #include <libgen.h> //basename and dirname
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28
29 #include "pgen_debug.h"
30 #include "previewgen.h"
31
32 #define PDF_CONV_UTIL "/usr/bin/pdftoppm"
33 #define PDF_INFO_UTIL "/usr/bin/pdfinfo"
34 #define MAX_PATH_LEN            4096
35 #define IMG_MAX_SIZE            (1024 * 1024 * 2) /* 2MB */
36 #define PDF_INFO_STR_MAX_SIZE   511
37
38 #define PDF_INFO_TEMP_FNAME     "/tmp/mobileprint/pdfinfo.txt"
39
40 #define PDF_PX_MAX_VAL          999999
41 #define PDF_PX_VAL_MAX_STR_LEN  (sizeof("999999") - 1)
42
43
44 /* Just simple stub for further development
45    TODO: use pipe output, current implementation have high security risk
46 */
47 int call_pdftoppm(const char *path, const struct pdf_preview_settings *settings)
48 {
49         char conv_cmd[] = PDF_CONV_UTIL " "; //" -f 1 -l 1 ";
50         char output_cmd[] = " > " IMG_TEMP_FILE_NAME;
51         char *cmd_str;
52         char x_val_str[PDF_PX_VAL_MAX_STR_LEN + 1];
53         char y_val_str[PDF_PX_VAL_MAX_STR_LEN + 1];
54         char page_str[PDF_PAGE_MAX_VAL + 1];
55         int path_len;
56         int cmd_len;
57
58         PGEN_RETV_IF(path == NULL || settings == NULL, -1, "Invalid argument");
59         path_len = strnlen(path, MAX_PATH_LEN);
60         PGEN_RETV_IF(path_len <= 0 || path_len >= MAX_PATH_LEN, -1, "path_len is out of scope");
61
62         /* check for correct picture size */
63         if (settings->w >= PDF_PX_MAX_VAL
64                         || settings->w <= 0
65                         || settings->h >= PDF_PX_MAX_VAL
66                         || settings->h <= 0
67                         || settings->page < 0
68                         || settings->page >= PDF_PAGE_MAX_VAL) {
69                 PGEN_DEBUG("call_pdftoppm(): ERROR in settings;"
70                                    " w = %d, h = %d, page = %d",
71                                    settings->w, settings->h, settings->page);
72                 return -1;
73         }
74
75         x_val_str[0] = '\0';
76         sprintf(x_val_str, "%d", settings->w);
77         y_val_str[0] = '\0';
78         sprintf(y_val_str, "%d", settings->h);
79         page_str[0] = '\0';
80         sprintf(page_str, "%d", settings->page);
81
82
83         cmd_len = (sizeof(conv_cmd) - 1)
84                           + strlen("-f ") + strlen(page_str) + 1
85                           + strlen("-l ") + strlen(page_str) + 1
86                           + strlen("-scale-to-x ") + strlen(x_val_str) + 1
87                           + strlen("-scale-to-y ") + strlen(y_val_str) + 1
88                           + path_len + sizeof(output_cmd);
89         cmd_str = malloc(sizeof(char) * (cmd_len + 1));
90
91         PGEN_RETV_IF(cmd_str == NULL, -1, "cmd_str malloc failed");
92         memset(cmd_str, 0, sizeof(char) * (cmd_len + 1));
93         cmd_str[0] = '\0';
94
95         strcat(cmd_str, conv_cmd);
96         strcat(cmd_str, "-f ");
97         strcat(cmd_str, page_str);
98         strcat(cmd_str, " ");
99         strcat(cmd_str, "-l ");
100         strcat(cmd_str, page_str);
101         strcat(cmd_str, " ");
102         strcat(cmd_str, "-scale-to-x ");
103         strcat(cmd_str, x_val_str);
104         strcat(cmd_str, " ");
105         strcat(cmd_str, "-scale-to-y ");
106         strcat(cmd_str, y_val_str);
107         strcat(cmd_str, " ");
108         strcat(cmd_str, path);
109         strcat(cmd_str, output_cmd);
110
111         cmd_str[cmd_len] = '\0';
112
113         PGEN_DEBUG("call_pdftoppm(): cmd_str = %s", cmd_str);
114
115         if (system(cmd_str) < 0) {
116                 PGEN_DEBUG("Failed to call system");
117         }
118
119         PGEN_IF_FREE_MEM(cmd_str);
120
121         return 0;
122 }
123
124
125 int call_pdfinfo(const char *path, const char *field)
126 {
127         char *cmd_str;
128         int path_len;
129         int cmd_len;
130
131         PGEN_RETV_IF(path == NULL || field == NULL, -1, "Invalid argument");
132         path_len = strnlen(path, MAX_PATH_LEN);
133         PGEN_RETV_IF(path_len <= 0 || path_len >= MAX_PATH_LEN, -1, "path_len is out of scope");
134
135         cmd_len = sizeof(PDF_INFO_UTIL) + sizeof(" '") + path_len
136                           + sizeof("' | grep '") + strlen(field)
137                           + strlen("' > " PDF_INFO_TEMP_FNAME); // strlen(output_cmd);
138         cmd_str = malloc(sizeof(char) * (cmd_len + 1));
139
140         PGEN_RETV_IF(cmd_str == NULL, -1, "cmd_str malloc failed");
141         memset(cmd_str, 0, sizeof(char) * (cmd_len + 1));
142
143         cmd_str[0] = '\0';
144         strcat(cmd_str, PDF_INFO_UTIL);
145         strcat(cmd_str, " '");
146         strcat(cmd_str, path);
147         strcat(cmd_str, "' | grep '");
148         strcat(cmd_str, field);
149         strcat(cmd_str, "' > " PDF_INFO_TEMP_FNAME);
150         //strcat(cmd_str, output_cmd);
151         cmd_str[cmd_len] = '\0';
152
153         PGEN_DEBUG("call_pdfinfo(): cmd_str = %s", cmd_str);
154
155         if (system(cmd_str) < 0) {
156                 PGEN_DEBUG("Failed to call system");
157         }
158
159         PGEN_IF_FREE_MEM(cmd_str);
160
161         return 0;
162 }
163
164
165 /**
166  * @brief
167  * @param[in]   path    path to PDF file
168  */
169 int call_pdfinfo_pagesize(const char *path)
170 {
171         /* TODO: page number */
172         return call_pdfinfo(path, "Page size:");
173 }
174
175
176 int call_pdfinfo_pages(const char *path)
177 {
178         return call_pdfinfo(path, "Pages:");
179 }
180
181
182 int is_page_landscape(struct size_pts *s)
183 {
184         PGEN_RETV_IF(s == NULL, -1, "Invalid argument");
185
186         if (s->x > s->y) {
187                 return 1;
188         }
189
190         return 0;
191 }
192
193
194 /**
195  * @brief               get PDF file page size in pts
196  * @param[in]   path    PDF file path
197  * @param[out]  ow      width in pts
198  * @param[out]  oh      height in pts
199  */
200 int get_pdf_page_size(const char *path, struct size_pts *s)
201 {
202         int res;
203         char cmd_str[MAX_PATH_LEN] = {0,};
204
205         PGEN_RETV_IF(path == NULL || s == NULL, -1, "Invalid argument");
206
207         //NOTICE : Usage : pdfinfo [options] <PDF-file>, we use "pdfinfo <PDF-file>" to get page size
208         snprintf(cmd_str, MAX_PATH_LEN, "%s %s", PDF_INFO_UTIL, path);
209         PGEN_DEBUG("call_pdfinfo(): cmd_str = %s", cmd_str);
210
211         FILE *fp = NULL;
212         if ((fp=popen(cmd_str, "r"))==NULL) {
213                 PGEN_DEBUG("popen error(%s)",cmd_str);
214                 return -1;
215         }
216
217         char *str_buf           = NULL;
218         size_t len              = 0;
219         ssize_t read_len        = 0;
220         char temp[MAX_PATH_LEN] = {0,};
221
222         do {
223                 read_len = getline(&str_buf, &len, fp);
224                 if (strstr(str_buf, "Page size:") != NULL) {
225                         res = sscanf(str_buf, "Page size:\t%lf x %lf%s", &(s->x), &(s->y), temp);
226                         if (res != 3) {
227                                 PGEN_DEBUG("ERROR: get_pdf_page_size(): incorrect output\n");
228                                 return -1;
229                         }
230                         break;
231                 } else {
232                         continue;
233                 }
234         } while (read_len != -1);
235         PGEN_IF_FREE_MEM(str_buf);
236
237         PGEN_DEBUG("Page size : [%f %f]",s->x, s->y);
238
239         int ret = pclose(fp);
240         if (!WIFEXITED(ret) || (WEXITSTATUS(ret) != 0)) {
241                 PGEN_DEBUG("pclose error!");
242         }
243
244         return 0;
245 }
246
247 /**
248  *
249  *
250  *
251  */
252 int get_pdf_pages_count(const char *path)
253 {
254         PGEN_RETV_IF(path == NULL, -1, "Invalid argument");
255
256         int res                 = -1;
257         int val                 = -1;
258         int path_len    = 0;
259         char cmd_str[MAX_PATH_LEN] = {0,};
260
261         path_len = strnlen(path, MAX_PATH_LEN);
262         PGEN_RETV_IF(path_len <= 0 || path_len >= MAX_PATH_LEN, -1, "path_len is out of scope");
263
264         //NOTICE : Usage : pdfinfo [options] <PDF-file>, we use "pdfinfo <PDF-file>" to get pages
265         snprintf(cmd_str, MAX_PATH_LEN, "%s %s", PDF_INFO_UTIL, path);
266         PGEN_DEBUG("call_pdfinfo(): cmd_str = %s", cmd_str);
267
268         FILE *fp = NULL;
269         if ((fp=popen(cmd_str, "r"))==NULL) {
270                 PGEN_DEBUG("popen error(%s)",cmd_str);
271                 return -1;
272         }
273
274         char *str_buf           = NULL;
275         size_t len              = 0;
276         ssize_t read_len        = 0;
277
278         do {
279                 read_len = getline(&str_buf, &len, fp);
280                 if (strstr(str_buf, "Pages:") != NULL) {
281                         res = sscanf(str_buf, "Pages:\t%d", &val);
282                         if (res != 1) {
283                                 PGEN_DEBUG("ERROR: get_pdf_pages_count(): incorrect output\n");
284                                 return -1;
285                         }
286                         break;
287                 } else {
288                         continue;
289                 }
290         } while (read_len != -1);
291         PGEN_IF_FREE_MEM(str_buf);
292
293         PGEN_DEBUG("Pages : %d", val);
294
295         int ret = pclose(fp);
296         if (!WIFEXITED(ret) || (WEXITSTATUS(ret) != 0)) {
297                 PGEN_DEBUG("pclose error!");
298         }
299         return val;
300 }
301
302 /**
303  * @brief                       get PDF preview
304  * @param[in]   path            path to PDF to generate preview
305  * @param[in]   settings        currently just ignored
306  * @param[out]  out_img         pointer to return allocated buffer, user is
307  *                              responsible for it's memory deallocation
308  *                              (format: PPM)
309  * @param[out]  size            image data size
310  * @retval      0       Success
311  * @retval      <0      Error
312  */
313 int get_pdf_preview(const char *path,
314                                         const struct pdf_preview_settings *settings,
315                                         void **out_img, int *size)
316 {
317         int img_fd;
318         unsigned char *imgbuf;
319         PGEN_RETV_IF(size == NULL || settings == NULL || out_img == NULL , -1, "Invalid argument");
320
321         if (call_pdftoppm(path, settings) < 0) {
322                 PGEN_DEBUG("ERROR: get_pdf_preview(): call_pdftoppm() error");
323                 return -1;
324         }
325
326         /* just stub, insecure in case of multiple printing requests */
327         img_fd = open(IMG_TEMP_FILE_NAME, O_RDONLY);
328         PGEN_RETV_IF(img_fd == -1, -1, "Failed to open temporary file");
329
330         /* STUB: currently just with enough big memory area, no check for
331            higher memory requirements */
332         imgbuf = malloc(sizeof(char) * IMG_MAX_SIZE);
333         if (imgbuf == NULL) {
334                 close(img_fd);
335                 PGEN_DEBUG("malloc failed");
336                 return -1;
337         }
338         memset(imgbuf, 0, sizeof(char) * IMG_MAX_SIZE);
339         *size = read(img_fd, imgbuf, IMG_MAX_SIZE);
340         close(img_fd);
341
342         if (*size <= 0) {
343                 PGEN_IF_FREE_MEM(imgbuf);
344                 PGEN_DEBUG("ERROR: get_pdf_preview(): read error");
345                 return -1;
346         }
347
348         *out_img = imgbuf;
349
350         return 0;
351 }
352
353 #if 0
354 /* Check for file extension
355  * 0/1
356  */
357 int is_file_ext(const char *fname, int fname_len, const char *ext)
358 {
359         const char *ptr, *ext_ptr;
360         int ext_len;
361
362         PGEN_RETV_IF(fname == NULL || ext == NULL, 0, "Invalid argument");
363
364         ext_len = strlen(ext);
365         if (fname_len < ext_len + 1) {
366                 return 0;
367         }
368
369         ptr = fname + fname_len - ext_len - 1;
370         ext_ptr = ptr + 1;
371
372         if (*ptr == '.' && strcasecmp(ext_ptr, ext) == 0) {
373                 return 1;
374         }
375
376         return 0;
377 }
378
379 /**
380  * @brief               get file type from its name (extension-based)
381  * @param[in]   fname   file name (may be with path)
382  * @retval              file type
383  */
384 enum file_type get_file_type(const char *fname)
385 {
386         int fname_len;
387         const char *img_file_types[] = IMG_FILE_EXTS;
388         const char *pdf_ftype = PDF_FILE_EXT;
389         int i;
390
391         /* check for corectness */
392         PGEN_RETV_IF(fname == NULL, FILE_TYPE_INCORRECT, "Incorrect file type");
393
394         fname_len = strnlen(fname, MAX_PATH_LEN);
395         PGEN_RETV_IF(fname_len >= MAX_PATH_LEN, FILE_TYPE_INCORRECT, "Too long file name");
396
397         /* check for pdf extension */
398         if (is_file_ext(fname, fname_len, pdf_ftype)) {
399                 return FILE_TYPE_PDF;
400         }
401
402         /* check for image extensions */
403         for (i = 0; img_file_types[i] != NULL; i++) {
404                 if (is_file_ext(fname, fname_len, img_file_types[i])) {
405                         return FILE_TYPE_IMAGE;
406                 }
407         }
408
409         return FILE_TYPE_INCORRECT;
410 }
411
412 #else
413 /**
414  * @brief               get file type from its name (extension-based)
415  * @param[in]   fname   file name (may be with path)
416  * @retval              file type
417  */
418 enum file_type get_file_type(const char *fname)
419 {
420         PGEN_RETV_IF(fname == NULL, FILE_TYPE_INCORRECT, "fname is NULL");
421         char *fname_copy = strdup(fname);
422         PGEN_RETV_IF(fname_copy == NULL, FILE_TYPE_INCORRECT, "fname_copy is NULL");
423
424         char *bname = basename(fname_copy);
425         char *ext = strrchr(bname, '.');
426         PGEN_RETV_IF(ext == NULL, FILE_TYPE_INCORRECT, "ext is NULL");
427
428         const char *img_file_types[] = IMG_FILE_EXTS;
429         const char *pdf_ftype = PDF_FILE_EXT;
430         int ret = FILE_TYPE_INCORRECT;
431         int i = 0;
432
433         if (strcasestr(ext, pdf_ftype) != NULL) {
434                 ret = FILE_TYPE_PDF;
435         } else {
436                 for (i = 0; img_file_types[i] != NULL; i++) {
437                         if (strcasestr(ext, img_file_types[i]) != NULL) {
438                                 ret = FILE_TYPE_IMAGE;
439                                 break;
440                         }
441                 }
442         }
443
444         PGEN_IF_FREE_MEM(fname_copy);
445         return ret;
446 }
447 #endif