Initialize Tizen 2.3
[apps/home/mobileprint.git] / mobileprint / previewgen / lib / page_preview.c
1 /*
2 *  Mobileprint
3 *
4 * Copyright 2012  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 <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include <Ecore.h>
29 #include <Evas.h>
30 /*#include <Evas_Engine_Buffer.h>*/
31
32 #include <evas_render.h>
33 #include <image_scaler.h>
34 #include <paper_size.h>
35 #include <pgen_debug.h>
36 #include <preview_util.h>
37
38 #include <page_preview.h>
39
40 const int gl_is_image_downscale = 1;
41 const int gl_is_half_pdf_raster = 1;
42
43 /**
44  * @brief       Get PDF page content image
45  * @param[in]   canvas          canvas of new image object
46  * @param[in]   img_buf
47  * @param[in]   img_size
48  * @return      image object with page content image
49  */
50 Evas_Object *get_pdf_img(Evas *canvas,
51                                                  void *img_buf, int img_size)
52 {
53         PGEN_TRACE_BEGIN;
54         PGEN_RETV_IF(canvas == NULL || img_buf == NULL || img_size <= 0, NULL, "Invalid argument");
55
56         Evas_Object *img = NULL;
57         img = evas_object_image_filled_add(canvas);
58         evas_object_image_memfile_set(img, img_buf, img_size,"ppm", NULL);
59         PGEN_IF_FREE_MEM(img_buf); /* copy saved in image */
60
61         PGEN_TRACE_END;
62         return img;
63 }
64
65 /**
66  * @brief       Grayscales provided RGBA pixel
67  * @param[in]   rgba_val        pixel to grayscale
68  * @result      grayscaled pixel
69  */
70 inline int grayscale_rgba(int rgba_val)
71 {
72         /* just sumple formula for sRGB conversion, maybe gamma-correction
73            or color profile using is required */
74         unsigned char *bytes = (unsigned char *)&rgba_val;
75         double res = 0.21 * bytes[0] + 0.72 * bytes[1] + 0.07 * bytes[2];
76         int i;
77
78         for (i = 0; i < 3; ++i) {
79                 bytes[i] = (unsigned char)res;
80         }
81
82         return rgba_val;
83 }
84
85 /**
86  * @brief       Grayscales image
87  * @param[in]   img     image to grayscale
88  */
89 void grayscale_img(Evas_Object *img)
90 {
91         int h;
92         int w;
93         int i;
94         int *data;
95
96         PGEN_TRACE_BEGIN;
97         PGEN_RET_IF(img == NULL, "Invalid argument");
98
99         evas_object_image_size_get(img, &w, &h);
100         PGEN_RET_IF(w == 0 || h == 0 , "can't get (w, h)");
101         PGEN_DEBUG("grayscale_img(): size: (%d, %d)", w, h);
102
103         data = evas_object_image_data_get(img, EINA_TRUE);
104         PGEN_RET_IF(data == NULL, "can't get data");
105
106         for (i = 0; i < w * h; ++i) {
107                 data[i] = grayscale_rgba(data[i]);
108         }
109         evas_object_image_data_set(img, data);
110
111         PGEN_TRACE_END;
112 }
113
114 int set_canvas_background(Evas *canvas, const struct size_px *full_size)
115 {
116         Evas_Object *bg;
117
118         PGEN_RETV_IF(canvas == NULL || full_size == NULL, -1, "Invalid argument");
119
120         /* background */
121         bg = evas_object_rectangle_add(canvas);
122         //evas_object_color_set(bg, 242, 238, 233, 255);
123         //evas_object_color_set(bg, 255, 255, 255, 255);
124         evas_object_color_set(bg, 0, 0, 0, 255);
125         evas_object_move(bg, 0, 0);
126         evas_object_resize(bg, full_size->x, full_size->y);
127         evas_object_show(bg);
128
129         return 0;
130 }
131
132 /**
133  * @brief               Set blank preview page image on provided canvas
134  * @param[in-out]       canvas          Canvas to draw page image
135  * @param[in]           b_s             Full page image size (with shadow,
136  *                                      border)
137  * @param[in]           p_s             Page size (to put page image)
138  * @param[in]           border_size     Page border size
139  * @param[in]           off             Page image offset on canvas
140  * @result              Error code, 0 is OK, <0 - error
141  */
142 int set_preview_page(Evas *canvas,
143                                          const struct size_px *b_s, const struct size_px *p_s,
144                                          const struct size_px *border_size,
145                                          const struct size_px *off)
146 {
147         PGEN_TRACE_BEGIN;
148         PGEN_RETV_IF(canvas == NULL || b_s == NULL || p_s == NULL || off == NULL, -1 , "Invalid argument");
149
150         Evas_Object *bg;
151         int shadow_offset_x;
152         int shadow_offset_y;
153
154         shadow_offset_x = (b_s->x - p_s->x);
155         shadow_offset_y = (b_s->y - p_s->y);
156
157         bg = evas_object_rectangle_add(canvas);
158         evas_object_color_set(bg, 50, 50, 50, 255); /* shadow color */
159         evas_object_move(bg, shadow_offset_x + off->x - border_size->x * 2,
160                                          shadow_offset_y + off->y - border_size->y * 2);
161         evas_object_resize(bg, p_s->x + border_size->x,
162                                            p_s->y + border_size->y);
163         evas_object_show(bg);
164
165         bg = evas_object_rectangle_add(canvas);
166         evas_object_color_set(bg, 0, 0, 0, 255); /* border color */
167         evas_object_move(bg, off->x, off->y);
168         evas_object_resize(bg, p_s->x + border_size->x * 2,
169                                            p_s->y + border_size->y * 2);
170         evas_object_show(bg);
171
172         bg = evas_object_rectangle_add(canvas);
173         evas_object_color_set(bg, 255, 255, 255, 255); /* page color */
174         evas_object_move(bg, border_size->x + off->x, border_size->y + off->y);
175         evas_object_resize(bg, p_s->x, p_s->y);
176         evas_object_show(bg);
177
178         PGEN_TRACE_END;
179         return 0;
180 }
181
182 int load_pdf_page_img(const char *fname, int page,
183                                           const struct preview_page_px *settings_px,
184                                           void **out_img, int *size)
185 {
186         struct pdf_preview_settings pdf_settings;
187         PGEN_RETV_IF(fname == NULL || page <= 0  || settings_px == NULL || out_img == NULL || size == NULL
188                                 , -1 , "Invalid argument");
189
190         /* put page image */
191         pdf_settings.w = settings_px->area_size.x;
192         pdf_settings.h = settings_px->area_size.y;
193         if (gl_is_half_pdf_raster) {
194                 /* half-rasterization hack is used to increase speed */
195                 pdf_settings.w = pdf_settings.w / 2;
196                 pdf_settings.h = pdf_settings.h / 2;
197         }
198         pdf_settings.page = page;
199
200         return get_pdf_preview(fname, &pdf_settings, out_img, size);
201 }
202
203 /**
204  *
205  *
206  *
207  */
208 int set_pdf_preview_page_image(Evas *canvas,
209                                                            void *img_buf, int img_size,
210                                                            const struct size_px *off,
211                                                            const struct preview_page_px *settings_px)
212 {
213         PGEN_RETV_IF(canvas == NULL || img_buf == NULL || img_size <=0 || off == NULL || settings_px == NULL
214                                 , -1 , "Invalid argument");
215
216         Evas_Object *img;
217         struct size_px border_size = {1, 1};
218
219         if (set_preview_page(canvas, &(settings_px->full_size),
220                                                  &(settings_px->paper_size), &border_size, off) < 0) {
221                 PGEN_DEBUG("ERROR in set_preview_page()");
222                 return -1;
223         }
224
225         img = get_pdf_img(canvas, img_buf, img_size);
226         PGEN_RETV_IF(img == NULL, -1 , "img is NULL");
227
228         if (settings_px->is_rotate90
229                         && rotate90_image(img) < 0) {
230                 PGEN_DEBUG("ERROR in rotate90_image()");
231                 return -1;
232         }
233         evas_object_resize(img, settings_px->area_size.x,
234                                            settings_px->area_size.y);
235         if (settings_px->is_grayscale) {
236                 PGEN_DEBUG("Grayscaling image");
237                 grayscale_img(img);
238         }
239
240         evas_object_move(img, settings_px->area_offset.x + off->x,
241                                          settings_px->area_offset.y + off->y);
242         evas_object_show(img);
243
244         return 0;
245 }
246
247
248 /**
249  * @brief       Writes preview page images to file
250  * @param[in]   pdf_fname       input PDF file name
251  * @param[in]   page_num        page number
252  * @param[in]   settings_pts    preview generation settings
253  * @param[in]   out_pic_fname   output image file name (PPM)
254  * @return      -1              error
255  *              0               OK
256  */
257 int save_pdf_preview_page_image(const char *pdf_fname, int page_num,
258                                                                 const struct preview_page_req *settings_pts,
259                                                                 const char *out_pic_fname)
260 {
261         Evas *canvas;
262         struct preview_page_px settings_px;
263         struct size_px off = {0, 0};
264         void *img_buf;
265         int img_size;
266
267         PGEN_TRACE_BEGIN;
268
269         if (get_preview_page_settings(settings_pts, &settings_px) < 0) {
270                 PGEN_DEBUG("ERROR: get_pdf_preview_page_image():"
271                                    " get_preview_page_settings()");
272                 PGEN_TRACE_END;
273                 return -1;
274         }
275
276         canvas = create_canvas(&(settings_px.full_size));
277         PGEN_RETV_IF(canvas == NULL, -1, "canvas is NULL");
278
279         /* till we save page images in PPM this is required */
280         if (set_canvas_background(canvas, &(settings_px.full_size)) < 0) {
281                 PGEN_DEBUG("ERROR in set_canvas_background()");
282                 destroy_canvas(canvas);
283                 PGEN_TRACE_END;
284                 return -1;
285         }
286
287         if (load_pdf_page_img(pdf_fname, page_num, &settings_px,
288                                                   &img_buf, &img_size) < 0) {
289                 destroy_canvas(canvas);
290                 PGEN_TRACE_END;
291                 return -1;
292         }
293
294         PGEN_RETV_IF(img_buf == NULL, -1, "img_buf is NULL");
295         if (set_pdf_preview_page_image(canvas, img_buf, img_size,
296                                                                    &off, &settings_px) < 0) {
297                 PGEN_DEBUG("ERROR in set_pdf_preview_page_image()");
298                 destroy_canvas(canvas);
299                 PGEN_TRACE_END;
300                 return -1;
301         }
302
303         draw_scene(canvas);
304         save_scene(canvas, out_pic_fname);
305         destroy_canvas(canvas);
306
307         PGEN_TRACE_END;
308
309         return 0;
310 }
311
312
313 int save_empty_preview_page_image(const struct preview_page_req *settings_pts,
314                                 const char *out_pic_fname)
315 {
316         Evas *canvas;
317         struct preview_page_px settings_px;
318         struct size_px off = {0, 0};
319         struct size_px border_size = {1, 1};
320
321         PGEN_TRACE_BEGIN;
322
323         if (get_preview_page_settings(settings_pts, &settings_px) < 0) {
324                 PGEN_DEBUG("ERROR: get_pdf_preview_page_image():"
325                                    " get_preview_page_settings()");
326                 PGEN_TRACE_END;
327                 return -1;
328         }
329
330         canvas = create_canvas(&(settings_px.full_size));
331         PGEN_RETV_IF(canvas == NULL, -1, "canvas is NULL");
332
333         /* till we save page images in PPM this is required */
334         if (set_canvas_background(canvas, &(settings_px.full_size)) < 0) {
335                 PGEN_DEBUG("ERROR in set_canvas_background()");
336                 destroy_canvas(canvas);
337                 PGEN_TRACE_END;
338                 return -1;
339         }
340
341         if (set_preview_page(canvas, &(settings_px.full_size),
342                          &(settings_px.paper_size), &border_size, &off) < 0) {
343                 PGEN_DEBUG("ERROR in set_preview_page()");
344                 destroy_canvas(canvas);
345                 PGEN_TRACE_END;
346                 return -1;
347         }
348
349         draw_scene(canvas);
350         save_scene(canvas, out_pic_fname);
351         destroy_canvas(canvas);
352
353         PGEN_TRACE_END;
354
355         return 0;
356 }
357
358
359 /* fix pdftopdf page rotation problem by manual scaling option */
360 int fix_pdf_page_scale(const char *fname,
361                                            const struct size_pts *req_size,
362                                            struct pdfgen_settings *settings)
363 {
364         PGEN_TRACE_BEGIN;
365         PGEN_RETV_IF(NULL == fname || NULL == req_size || NULL == settings, -1, "Invalid argument");
366
367         struct size_pts input_pdf_pts = {0.0, };
368
369         int result = get_pdf_page_size(fname, &input_pdf_pts);
370         PGEN_RETV_IF(result < 0, -1, "can't get pdf page size");
371
372         /* check rotation */
373         if ((PAGE_ORIENTATION_LANDSCAPE == settings->orientation
374                         && input_pdf_pts.x < input_pdf_pts.y)
375                         || (PAGE_ORIENTATION_PORTRAIT == settings->orientation
376                                 && input_pdf_pts.x > input_pdf_pts.y)) {
377                 double tmp = input_pdf_pts.x;
378                 input_pdf_pts.x = input_pdf_pts.y;
379                 input_pdf_pts.y = tmp;
380
381                 /*when page is rotated we need to fix its size
382                   if source page is small and desired is big, then enlarge
383                   if source is big and desired is small, diminish*/
384                 int zoomHorizontal =
385                         (int)(100 * req_size->x / input_pdf_pts.x);
386                 int zoomVertical =
387                         (int)(100 * req_size->y / input_pdf_pts.y);
388
389                 /* minimal zoom is needed from vertical/horizontal
390                    - for the whole picture fitness */
391                 settings->scale.zoom = (zoomHorizontal < zoomVertical)
392                                                            ? zoomHorizontal : zoomVertical;
393                 settings->scale.type = SCALE_RELATIVE;
394         }
395         settings->scale.w = 0;
396         settings->scale.h = 0;
397
398         PGEN_DEBUG("page zoom fix calculated");
399         PGEN_DEBUG("input_pdf_size (x,y) = (%lf, %lf)",
400                            input_pdf_pts.x, input_pdf_pts.y);
401         PGEN_DEBUG("req_size (x,y) = (%lf, %lf)",
402                            req_size->x, req_size->y);
403
404         PGEN_TRACE_END;
405         return 0;
406 }
407
408
409 /**
410  * @brief       Generates preview images for all PDF pages in temporary files
411  *              (/tmp/mobileprint_xxxx.ppm)
412  * @param[in]   fname                   PDF file name
413  * @param[in]   paper_size              paper size in pts
414  * @param[in]   available_size_px       available size for images in px
415  * @param[in]   is_grayscale            is image must be grascaled
416  * @return      -1                      error
417  *              > 0                     generated pages count
418  */
419 int generate_pdf_preview_pages(const char *fname,
420                 const struct size_pts *paper_size,
421                 const struct size_px *available_size_px,
422                 int is_grayscale)
423 {
424         int pages_count;
425         int cur_page;
426         struct size_pts page_size = {0.0, 0.0};
427
428         struct size_px shadow_size_px = {10, 10};
429         struct preview_page_req settings_req;
430         /*struct preview_settings_px settings_px;*/
431         char out_fname[sizeof("/tmp/mobileprint_xxxx.ppm ")];
432
433         PGEN_TRACE_BEGIN;
434
435         /*PGEN_DEBUG("available_size: (%d, %d)",
436                         available_size_px->x, available_size_px->y);*/
437
438
439         if (get_pdf_page_size(fname, &page_size) < 0) {
440                 PGEN_DEBUG("ERROR in get_pdf_page_size()\n");
441                 return -1;
442         }
443         pages_count = get_pdf_pages_count(fname);
444         if (pages_count <= 0) {
445                 PGEN_DEBUG("ERROR: pages_count = %d (<=0)\n", pages_count);
446                 return -1;
447         }
448
449         settings_req.paper_size = *paper_size;
450         settings_req.available_size_px = *available_size_px;
451         settings_req.shadow_offset = shadow_size_px;
452         settings_req.is_rotate90 = 0;
453         settings_req.is_grayscale = is_grayscale;
454
455         /*if (get_preview_settings(&settings_req, &settings_px) < 0) {
456                 PGEN_DEBUG("ERROR in get_preview_settings()");
457                 PGEN_TRACE_END;
458                 return -1;
459         }*/
460
461         for (cur_page = 0; cur_page < pages_count; ++cur_page) {
462                 snprintf(out_fname, sizeof(out_fname), "/tmp/mobileprint_%04d.ppm", cur_page + 1);
463                 save_pdf_preview_page_image(fname,
464                                 cur_page + 1, &settings_req,
465                                 out_fname);
466         }
467
468         PGEN_TRACE_END;
469         return pages_count;
470 }
471
472
473 /**
474  * @brief      Process PDF preview generation with CUPS filters to PDF with
475  *             specific options
476  * @param[in]  fname                   input PDF file name
477  * @param[in]  printer_ppd_fname       PPD file name for current active printer
478  * @param[in]  paper_size              paper size with CUPS name
479  * @param[in]  orientation             requested printing orientation
480  * @param[in]  n_up                    number-up
481  * @param[in]  scale                   scaling of image
482  * @param[in]  is_grayscale            image must be grascaled
483  * @return     -1                      error
484  *             0                       success
485  */
486 int pdf2pdf_preview_pages(const char *fname, const char *out_pdf_fname,
487                                                   const char *printer_ppd_fname,
488                                                   const struct paper_size_pts *paper_size,
489                                                   enum page_orientation orientation, int n_up,
490                                                   const struct page_scale *scale, int is_grayscale)
491 {
492         struct pdfgen_settings pg_settings;
493
494         PGEN_TRACE_BEGIN;
495         PGEN_RETV_IF(!fname || !scale, -1, "Invalid argument");
496         PGEN_DEBUG("processing file name: %s", fname);
497
498         /* generate result pdf */
499         pg_settings.n_up = n_up;
500         pg_settings.paper_name = paper_size->name;
501         pg_settings.scale = *scale;
502         pg_settings.orientation = orientation;
503         pg_settings.ppd_filename = (char *)printer_ppd_fname;
504
505         int result = fix_pdf_page_scale(fname, &(paper_size->s), &pg_settings);
506         PGEN_RETV_IF(result < 0, -1, "ERROR in fix_pdf_page_scale()");
507
508         PGEN_DEBUG("pg_settings.paper_name = %s", pg_settings.paper_name);
509         PGEN_DEBUG("pg_settings.orientation = %d",
510                            (int)pg_settings.orientation);
511         PGEN_DEBUG("pg_settings.scale.zoom = %d", pg_settings.scale.zoom);
512         PGEN_DEBUG("pg_settings.scale.type = %d",
513                            (int)pg_settings.scale.type);
514
515         result = call_pdftopdf(fname, out_pdf_fname, &pg_settings);
516         PGEN_RETV_IF(result < 0, -1, "ERROR in call_pdftopdf()");
517
518         PGEN_TRACE_END;
519         return 0;
520 }
521
522
523 /**
524  * @brief       Process PDF preview generation with CUPS filters into temporary
525  *              pages image files (/tmp/mobileprint_xxxx.ppm)
526  * @param[in]   fname                   input PDF file name
527  * @param[in]   paper_size              paper size with CUPS name
528  * @param[in]   available_size_px       available size for image in px
529  * @param[in]   orientation             requested printing orientation
530  * @param[in]   n_up                    number-up
531  * @param[in]   scale
532  * @param[in]   is_grayscale            image must be grascaled
533  * @return      -1                      error
534  *              > 0                     generated pages count
535  */
536 int get_pdf_preview_pages(const char *fname, const char *out_pdf_fname,
537                 const char *printer_ppd_fname,
538                 const struct paper_size_pts *paper_size,
539                 const struct size_px *available_size_px,
540                 enum page_orientation orientation,
541                 int n_up, const struct page_scale *scale, int is_grayscale)
542 {
543         int res;
544         PGEN_TRACE_BEGIN;
545         pdf2pdf_preview_pages(fname, out_pdf_fname, printer_ppd_fname,
546                         paper_size, orientation,
547                         n_up, scale, is_grayscale);
548
549         res = generate_pdf_preview_pages(TEMP_PDFTOPDF_FNAME,
550                         &(paper_size->s), available_size_px,
551                         is_grayscale);
552         PGEN_TRACE_END;
553         return res;
554 }
555
556
557 int process_image_downscale(const char *fname, const char *out_fname,
558                                                         const struct size_px *available_size_px)
559 {
560         struct size_px new_size;
561         PGEN_RETV_IF(fname == NULL || out_fname == NULL || available_size_px == NULL, -1, "Invalid argument");
562         new_size.x = available_size_px->x / 2;
563         new_size.y = available_size_px->y / 2;
564         downscale_image(fname, out_fname, &new_size);
565         return 0;
566 }
567
568 int img2pdf_preview_pages(const char *fname, const char *out_pdf_fname,
569                                                   const char *printer_ppd_fname,
570                                                   const struct paper_size_pts *paper_size,
571                                                   const struct size_px *available_size_px,
572                                                   enum page_orientation orientation, int n_up,
573                                                   const struct page_scale *scale, int is_grayscale)
574 {
575         PGEN_TRACE_BEGIN;
576         PGEN_RETV_IF(!fname || !out_pdf_fname || !printer_ppd_fname ||
577                                 !paper_size || !available_size_px || !scale,
578                                 -1, "Invalid argument");
579
580         struct pdfgen_settings pg_settings;
581
582         /* generate result pdf */
583
584         /* TODO: use pdfunite for multiple files, currently only first file */
585         pg_settings.n_up = n_up;
586         pg_settings.paper_name = paper_size->name;
587         pg_settings.scale = *scale;
588         pg_settings.orientation = orientation;
589         pg_settings.ppd_filename = (char *)printer_ppd_fname;
590
591         int result = call_imagetopdf(fname, out_pdf_fname, &pg_settings);
592         PGEN_RETV_IF(result < 0, -1, "ERROR in call_imagetopdf()");
593
594         PGEN_TRACE_END;
595         return 0;
596 }
597
598
599 int img_files2pdf_preview_pages(char **const fnames,
600                 int files_count,
601                 const char *out_pdf_fname,
602                 const char *printer_ppd_fname,
603                 const struct paper_size_pts *paper_size,
604                 const struct size_px *available_size_px,
605                 enum page_orientation orientation, int n_up,
606                 const struct page_scale *scale, int is_grayscale)
607 {
608         int i;
609         struct pdfgen_settings pg_settings;
610         int out_fname_size = sizeof(PREVIEW_TEMP_DIR "/mobileprint_xxxx.pdf ");
611         int res;
612         char **out_fnames;
613         char *cur_fname;
614         int is_err = 0;
615         PGEN_TRACE_BEGIN;
616
617         PGEN_RETV_IF(files_count <= 0 || files_count > 9999,
618                         -1, "Incorrect files_count");
619
620         pg_settings.n_up = 1; /* TODO: n_up */
621         pg_settings.paper_name = paper_size->name;
622         pg_settings.scale = *scale;
623         pg_settings.orientation = orientation;
624         pg_settings.ppd_filename = (char *)printer_ppd_fname;
625
626         /* generate images pdf files and their files list */
627         out_fnames = (char **)malloc(sizeof(char*) * (files_count + 1));
628         PGEN_RETV_IF(NULL == out_fnames, -1, "Out of memory");
629         memset(out_fnames, 0, sizeof(char*) * (files_count + 1));
630         for (i = 0; i < files_count; ++i) {
631                 cur_fname = (char*)malloc(
632                                 sizeof(char) * (out_fname_size + 1));
633                 if (NULL == cur_fname) {
634                         is_err = 1;
635                         break;
636                 }
637                 out_fnames[i] = cur_fname;
638                 cur_fname[out_fname_size] = '\0';
639                 snprintf(cur_fname, out_fname_size,
640                                 PREVIEW_TEMP_DIR "/mobileprint_%04d.pdf",
641                                 i + 1);
642                 res = call_imagetopdf(fnames[i], cur_fname, &pg_settings);
643                 PGEN_DEBUG("res = %d, i = %d", res, i);
644         }
645
646         /* connect images pdf files into one file */
647         if (!is_err)
648                 res = call_pdfunite(out_fnames, out_pdf_fname);
649
650         /* free allocated memory */
651         for (i = 0; i < files_count; ++i)
652                 PGEN_IF_FREE_MEM(out_fnames[i]);
653         PGEN_IF_FREE_MEM(out_fnames);
654         if (is_err)
655                 PGEN_RETV_IF(1, -1, "Out of memory");
656
657
658         PGEN_TRACE_END;
659         return 0;
660 }
661