4 * Copyright 2012 Samsung Electronics Co., Ltd
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
10 * http://floralicense.org/license/
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.
25 #include <sys/types.h>
30 /*#include <Evas_Engine_Buffer.h>*/
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"
38 #include "page_preview.h"
40 const int gl_is_image_downscale = 1;
41 const int gl_is_half_pdf_raster = 1;
44 * @brief Get PDF page content image
45 * @param[in] canvas canvas of new image object
48 * @return image object with page content image
50 Evas_Object *get_pdf_img(Evas *canvas,
51 void *img_buf, int img_size)
54 PGEN_RETV_IF(canvas == NULL || img_buf == NULL || img_size <= 0, NULL, "Invalid argument");
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 */
66 * @brief Grayscales provided RGBA pixel
67 * @param[in] rgba_val pixel to grayscale
68 * @result grayscaled pixel
70 inline int grayscale_rgba(int rgba_val)
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];
78 for (i = 0; i < 3; ++i) {
79 bytes[i] = (unsigned char)res;
86 * @brief Grayscales image
87 * @param[in] img image to grayscale
89 void grayscale_img(Evas_Object *img)
97 PGEN_RET_IF(img == NULL, "Invalid argument");
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);
103 data = evas_object_image_data_get(img, EINA_TRUE);
104 PGEN_RET_IF(data == NULL, "can't get data");
106 for (i = 0; i < w * h; ++i) {
107 data[i] = grayscale_rgba(data[i]);
109 evas_object_image_data_set(img, data);
114 int set_canvas_background(Evas *canvas, const struct size_px *full_size)
118 PGEN_RETV_IF(canvas == NULL || full_size == NULL, -1, "Invalid argument");
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_move(bg, 0, 0);
125 evas_object_resize(bg, full_size->x, full_size->y);
126 evas_object_show(bg);
135 int set_preview_page(Evas *canvas,
136 const struct size_px *b_s, const struct size_px *p_s,
137 const struct size_px *border_size,
138 const struct size_px *off)
141 PGEN_RETV_IF(canvas == NULL || b_s == NULL || p_s == NULL || off == NULL, -1 , "Invalid argument");
147 shadow_offset_x = (b_s->x - p_s->x);
148 shadow_offset_y = (b_s->y - p_s->y);
151 bg = evas_object_rectangle_add(canvas);
152 //evas_object_color_set(bg, 242, 238, 233, 255);
153 evas_object_color_set(bg, 255, 255, 255, 255);
154 evas_object_move(bg, off->x, off->y);
155 evas_object_resize(bg, b_s->x, b_s->y);
156 evas_object_show(bg);
158 bg = evas_object_rectangle_add(canvas);
159 evas_object_color_set(bg, 100, 100, 100, 255); /* shadow color */
160 evas_object_move(bg, shadow_offset_x + off->x - border_size->x * 2,
161 shadow_offset_y + off->y - border_size->y * 2);
162 evas_object_resize(bg, p_s->x + border_size->x,
163 p_s->y + border_size->y);
164 evas_object_show(bg);
166 bg = evas_object_rectangle_add(canvas);
167 evas_object_color_set(bg, 0, 0, 0, 255); /* border color */
168 evas_object_move(bg, off->x, off->y);
169 evas_object_resize(bg, p_s->x + border_size->x * 2,
170 p_s->y + border_size->y * 2);
171 evas_object_show(bg);
173 bg = evas_object_rectangle_add(canvas);
174 evas_object_color_set(bg, 255, 255, 255, 255); /* page color */
175 evas_object_move(bg, border_size->x + off->x, border_size->y + off->y);
176 evas_object_resize(bg, p_s->x, p_s->y);
177 evas_object_show(bg);
183 int load_pdf_page_img(const char *fname, int page,
184 const struct preview_page_px *settings_px,
185 void **out_img, int *size)
187 struct pdf_preview_settings pdf_settings;
188 PGEN_RETV_IF(fname == NULL || page <= 0 || settings_px == NULL || out_img == NULL || size == NULL
189 , -1 , "Invalid argument");
192 pdf_settings.w = settings_px->area_size.x;
193 pdf_settings.h = settings_px->area_size.y;
194 if (gl_is_half_pdf_raster) {
195 /* half-rasterization hack is used to increase speed */
196 pdf_settings.w = pdf_settings.w / 2;
197 pdf_settings.h = pdf_settings.h / 2;
199 pdf_settings.page = page;
201 return get_pdf_preview(fname, &pdf_settings, out_img, size);
209 int set_pdf_preview_page_image(Evas *canvas,
210 void *img_buf, int img_size,
211 const struct size_px *off,
212 const struct preview_page_px *settings_px)
214 PGEN_RETV_IF(canvas == NULL || img_buf == NULL || img_size <=0 || off == NULL || settings_px == NULL
215 , -1 , "Invalid argument");
218 struct size_px border_size = {1, 1};
220 if (set_preview_page(canvas, &(settings_px->full_size),
221 &(settings_px->paper_size), &border_size, off) < 0) {
222 PGEN_DEBUG("ERROR in set_preview_page()");
226 img = get_pdf_img(canvas, img_buf, img_size);
227 PGEN_RETV_IF(img == NULL, -1 , "img is NULL");
229 if (settings_px->is_rotate90
230 && rotate90_image(img) < 0) {
231 PGEN_DEBUG("ERROR in rotate90_image()");
234 evas_object_resize(img, settings_px->area_size.x,
235 settings_px->area_size.y);
236 if (settings_px->is_grayscale) {
237 PGEN_DEBUG("Grayscaling image");
241 evas_object_move(img, settings_px->area_offset.x + off->x,
242 settings_px->area_offset.y + off->y);
243 evas_object_show(img);
253 int set_pdf_preview_page_image_w_np(Evas *canvas,
254 void *img_buf, int img_size,
255 int page_num, int pages_count,
256 const struct preview_settings_px *settings_px)
258 struct size_px border_size = {1, 1};
259 struct size_px center_off = {0, 0};
260 struct size_px left_off = {0, 0};
261 struct size_px right_off = {0, 0};
264 PGEN_RETV_IF(canvas == NULL || img_buf == NULL || img_size <=0 || page_num <=0
265 || pages_count <=0 || settings_px == NULL, -1, "Invalid argument");
267 center_off.x = settings_px->center_off;
268 left_off.x = settings_px->left_off;
269 right_off.x = settings_px->right_off;
271 if (set_pdf_preview_page_image(canvas, img_buf, img_size,
272 ¢er_off, &(settings_px->page_px)) < 0) {
273 PGEN_DEBUG("ERROR in set_pdf_preview_page_image()");
278 /* put side (previous and next) pages images */
279 /* TODO: check for pages availability */
280 if (pages_count > 1) {
282 set_preview_page(canvas,
283 &(settings_px->page_px.full_size),
284 &(settings_px->page_px.paper_size),
285 &border_size, &left_off) < 0) {
286 PGEN_DEBUG("ERROR in set_preview_page()");
291 if (page_num < pages_count &&
292 set_preview_page(canvas,
293 &(settings_px->page_px.full_size),
294 &(settings_px->page_px.paper_size),
295 &border_size, &right_off) < 0) {
296 PGEN_DEBUG("ERROR in set_preview_page()");
308 * @brief Writes preview page images to file
309 * @param[in] pdf_fname input PDF file name
310 * @param[in] page_num page number
311 * @param[in] settings_pts preview generation settings
312 * @param[in] out_pic_fname output image file name (PPM)
316 int save_pdf_preview_page_image(const char *pdf_fname, int page_num,
317 const struct preview_page_req *settings_pts,
318 const char *out_pic_fname)
321 struct preview_page_px settings_px;
322 struct size_px off = {0, 0};
328 if (get_preview_page_settings(settings_pts, &settings_px) < 0) {
329 PGEN_DEBUG("ERROR: get_pdf_preview_page_image():"
330 " get_preview_page_settings()");
335 canvas = create_canvas(&(settings_px.full_size));
336 PGEN_RETV_IF(canvas == NULL, -1, "canvas is NULL");
338 if (load_pdf_page_img(pdf_fname, page_num, &settings_px,
339 &img_buf, &img_size) < 0) {
344 if (set_pdf_preview_page_image(canvas, img_buf, img_size,
345 &off, &settings_px) < 0) {
346 PGEN_DEBUG("ERROR in set_pdf_preview_page_image()");
347 destroy_canvas(canvas);
353 save_scene(canvas, out_pic_fname);
354 destroy_canvas(canvas);
362 int save_empty_preview_page_image(const struct preview_page_req *settings_pts,
363 const char *out_pic_fname)
366 struct preview_page_px settings_px;
367 struct size_px off = {0, 0};
368 struct size_px border_size = {1, 1};
372 if (get_preview_page_settings(settings_pts, &settings_px) < 0) {
373 PGEN_DEBUG("ERROR: get_pdf_preview_page_image():"
374 " get_preview_page_settings()");
379 canvas = create_canvas(&(settings_px.full_size));
380 PGEN_RETV_IF(canvas == NULL, -1, "canvas is NULL");
382 if (set_preview_page(canvas, &(settings_px.full_size),
383 &(settings_px.paper_size), &border_size, &off) < 0) {
384 PGEN_DEBUG("ERROR in set_preview_page()");
385 destroy_canvas(canvas);
391 save_scene(canvas, out_pic_fname);
392 destroy_canvas(canvas);
400 int save_pdf_preview_page_image_w_np(
401 void *img_buf, int img_size,
402 int page_num, int pages_count,
403 const struct preview_settings_px *settings_px,
404 const char *out_pic_fname)
408 canvas = create_canvas(&(settings_px->full_size));
409 PGEN_RETV_IF(canvas == NULL, -1, "canvas is NULL");
411 /* till we save page images in PPM this is required */
412 if (set_canvas_background(canvas, &(settings_px->full_size)) < 0) {
413 PGEN_DEBUG("ERROR in set_canvas_background()");
414 destroy_canvas(canvas);
419 if (set_pdf_preview_page_image_w_np(canvas, img_buf, img_size,
420 page_num, pages_count, settings_px) < 0) {
421 PGEN_DEBUG("ERROR in set_pdf_preview_page_image_w_np()");
422 destroy_canvas(canvas);
428 save_scene(canvas, out_pic_fname);
429 destroy_canvas(canvas);
438 int save_pdf_preview_page_image_w_np_fname(const char *pdf_fname,
439 int page_num, int pages_count,
440 const struct preview_settings_px *settings_px,
441 const char *out_pic_fname)
446 if (load_pdf_page_img(pdf_fname, page_num, &(settings_px->page_px),
447 &img_buf, &img_size) < 0) {
452 /* called function is responsible for img_buf allocation */
453 res = save_pdf_preview_page_image_w_np(img_buf, img_size,
454 page_num, pages_count, settings_px, out_pic_fname);
459 /* fix pdftopdf page rotation problem by manual scaling option */
460 int fix_pdf_page_scale(const char *fname,
461 const struct size_pts *req_size,
462 struct pdfgen_settings *settings)
465 PGEN_RETV_IF(NULL == fname || NULL == req_size || NULL == settings, -1, "Invalid argument");
467 struct size_pts input_pdf_pts = {0.0, };
469 int result = get_pdf_page_size(fname, &input_pdf_pts);
470 PGEN_RETV_IF(result < 0, -1, "can't get pdf page size");
473 if ((PAGE_ORIENTATION_LANDSCAPE == settings->orientation
474 && input_pdf_pts.x < input_pdf_pts.y)
475 || (PAGE_ORIENTATION_PORTRAIT == settings->orientation
476 && input_pdf_pts.x > input_pdf_pts.y)) {
477 double tmp = input_pdf_pts.x;
478 input_pdf_pts.x = input_pdf_pts.y;
479 input_pdf_pts.y = tmp;
481 /*when page is rotated we need to fix its size
482 if source page is small and desired is big, then enlarge
483 if source is big and desired is small, diminish*/
485 (int)(100 * req_size->x / input_pdf_pts.x);
487 (int)(100 * req_size->y / input_pdf_pts.y);
489 /* minimal zoom is needed from vertical/horizontal
490 - for the whole picture fitness */
491 settings->scale.zoom = (zoomHorizontal < zoomVertical)
492 ? zoomHorizontal : zoomVertical;
493 settings->scale.type = SCALE_RELATIVE;
495 settings->scale.w = 0;
496 settings->scale.h = 0;
498 PGEN_DEBUG("page zoom fix calculated");
499 PGEN_DEBUG("input_pdf_size (x,y) = (%lf, %lf)",
500 input_pdf_pts.x, input_pdf_pts.y);
501 PGEN_DEBUG("req_size (x,y) = (%lf, %lf)",
502 req_size->x, req_size->y);
510 * @brief Generates preview images for all PDF pages in temporary files
511 * (/tmp/mobileprint_xxxx.ppm)
512 * @param[in] fname PDF file name
513 * @param[in] paper_size paper size in pts
514 * @param[in] available_size_px available size for images in px
515 * @param[in] is_grayscale is image must be grascaled
517 * > 0 generated pages count
519 int generate_pdf_preview_pages(const char *fname,
520 const struct size_pts *paper_size,
521 const struct size_px *available_size_px,
526 struct size_pts page_size = {0.0, 0.0};
528 struct size_px shadow_size_px = {10, 10};
529 struct preview_page_req settings_req;
530 /*struct preview_settings_px settings_px;*/
531 char out_fname[sizeof("/tmp/mobileprint_xxxx.ppm ")];
535 /*PGEN_DEBUG("available_size: (%d, %d)",
536 available_size_px->x, available_size_px->y);*/
539 if (get_pdf_page_size(fname, &page_size) < 0) {
540 PGEN_DEBUG("ERROR in get_pdf_page_size()\n");
543 pages_count = get_pdf_pages_count(fname);
544 if (pages_count <= 0) {
545 PGEN_DEBUG("ERROR: pages_count = %d (<=0)\n", pages_count);
549 settings_req.paper_size = *paper_size;
550 settings_req.available_size_px = *available_size_px;
551 settings_req.shadow_offset = shadow_size_px;
552 settings_req.is_rotate90 = 0;
553 settings_req.is_grayscale = is_grayscale;
555 /*if (get_preview_settings(&settings_req, &settings_px) < 0) {
556 PGEN_DEBUG("ERROR in get_preview_settings()");
561 for (cur_page = 0; cur_page < pages_count; ++cur_page) {
562 sprintf(out_fname, "/tmp/mobileprint_%04d.ppm", cur_page + 1);
563 save_pdf_preview_page_image(fname,
564 cur_page + 1, &settings_req,
574 * @brief Process PDF preview generation with CUPS filters to PDF with
576 * @param[in] fname input PDF file name
577 * @param[in] printer_ppd_fname PPD file name for current active printer
578 * @param[in] paper_size paper size with CUPS name
579 * @param[in] orientation requested printing orientation
580 * @param[in] n_up number-up
581 * @param[in] scale scaling of image
582 * @param[in] is_grayscale image must be grascaled
586 int pdf2pdf_preview_pages(const char *fname, const char *out_pdf_fname,
587 const char *printer_ppd_fname,
588 const struct paper_size_pts *paper_size,
589 enum page_orientation orientation, int n_up,
590 const struct page_scale *scale, int is_grayscale)
592 struct pdfgen_settings pg_settings;
595 PGEN_RETV_IF(!fname || !scale, -1, "Invalid argument");
596 PGEN_DEBUG("processing file name: %s", fname);
598 /* generate result pdf */
599 pg_settings.n_up = n_up;
600 pg_settings.paper_name = paper_size->name;
601 pg_settings.scale = *scale;
602 pg_settings.orientation = orientation;
603 pg_settings.ppd_filename = (char *)printer_ppd_fname;
605 int result = fix_pdf_page_scale(fname, &(paper_size->s), &pg_settings);
606 PGEN_RETV_IF(result < 0, -1, "ERROR in fix_pdf_page_scale()");
608 PGEN_DEBUG("pg_settings.paper_name = %s", pg_settings.paper_name);
609 PGEN_DEBUG("pg_settings.orientation = %d",
610 (int)pg_settings.orientation);
611 PGEN_DEBUG("pg_settings.scale.zoom = %d", pg_settings.scale.zoom);
612 PGEN_DEBUG("pg_settings.scale.type = %d",
613 (int)pg_settings.scale.type);
615 result = call_pdftopdf(fname, out_pdf_fname, &pg_settings);
616 PGEN_RETV_IF(result < 0, -1, "ERROR in call_pdftopdf()");
624 * @brief Process PDF preview generation with CUPS filters into temporary
625 * pages image files (/tmp/mobileprint_xxxx.ppm)
626 * @param[in] fname input PDF file name
627 * @param[in] paper_size paper size with CUPS name
628 * @param[in] available_size_px available size for image in px
629 * @param[in] orientation requested printing orientation
630 * @param[in] n_up number-up
632 * @param[in] is_grayscale image must be grascaled
634 * > 0 generated pages count
636 int get_pdf_preview_pages(const char *fname, const char *out_pdf_fname,
637 const char *printer_ppd_fname,
638 const struct paper_size_pts *paper_size,
639 const struct size_px *available_size_px,
640 enum page_orientation orientation,
641 int n_up, const struct page_scale *scale, int is_grayscale)
645 pdf2pdf_preview_pages(fname, out_pdf_fname, printer_ppd_fname,
646 paper_size, orientation,
647 n_up, scale, is_grayscale);
649 res = generate_pdf_preview_pages(TEMP_PDFTOPDF_FNAME,
650 &(paper_size->s), available_size_px,
657 int process_image_downscale(const char *fname, const char *out_fname,
658 const struct size_px *available_size_px)
660 struct size_px new_size;
661 PGEN_RETV_IF(fname == NULL || out_fname == NULL || available_size_px == NULL, -1, "Invalid argument");
662 new_size.x = available_size_px->x / 2;
663 new_size.y = available_size_px->y / 2;
664 downscale_image(fname, out_fname, &new_size);
668 int img2pdf_preview_pages(const char *fname, const char *out_pdf_fname,
669 const char *printer_ppd_fname,
670 const struct paper_size_pts *paper_size,
671 const struct size_px *available_size_px,
672 enum page_orientation orientation, int n_up,
673 const struct page_scale *scale, int is_grayscale)
676 PGEN_RETV_IF(!fname || !out_pdf_fname || !printer_ppd_fname ||
677 !paper_size || !available_size_px || !scale,
678 -1, "Invalid argument");
680 struct pdfgen_settings pg_settings;
682 /* generate result pdf */
684 /* TODO: use pdfunite for multiple files, currently only first file */
685 pg_settings.n_up = n_up;
686 pg_settings.paper_name = paper_size->name;
687 pg_settings.scale = *scale;
688 pg_settings.orientation = orientation;
689 pg_settings.ppd_filename = (char *)printer_ppd_fname;
691 int result = call_imagetopdf(fname, out_pdf_fname, &pg_settings);
692 PGEN_RETV_IF(result < 0, -1, "ERROR in call_imagetopdf()");
699 int img_files2pdf_preview_pages(char **const fnames,
701 const char *out_pdf_fname,
702 const char *printer_ppd_fname,
703 const struct paper_size_pts *paper_size,
704 const struct size_px *available_size_px,
705 enum page_orientation orientation, int n_up,
706 const struct page_scale *scale, int is_grayscale)
709 struct pdfgen_settings pg_settings;
710 int out_fname_size = sizeof(PREVIEW_TEMP_DIR "/mobileprint_xxxx.pdf ");
717 PGEN_RETV_IF(files_count <= 0 || files_count > 9999,
718 -1, "Incorrect files_count");
720 pg_settings.n_up = 1; /* TODO: n_up */
721 pg_settings.paper_name = paper_size->name;
722 pg_settings.scale = *scale;
723 pg_settings.orientation = orientation;
724 pg_settings.ppd_filename = (char *)printer_ppd_fname;
726 /* generate images pdf files and their files list */
727 out_fnames = (char **)malloc(sizeof(char*) * (files_count + 1));
728 PGEN_RETV_IF(NULL == out_fnames, -1, "Out of memory");
729 memset(out_fnames, 0, sizeof(char*) * (files_count + 1));
730 for (i = 0; i < files_count; ++i) {
731 cur_fname = (char*)malloc(
732 sizeof(char) * (out_fname_size + 1));
733 if (NULL == cur_fname) {
737 out_fnames[i] = cur_fname;
738 cur_fname[out_fname_size] = '\0';
739 snprintf(cur_fname, out_fname_size,
740 PREVIEW_TEMP_DIR "/mobileprint_%04d.pdf",
742 res = call_imagetopdf(fnames[i], cur_fname, &pg_settings);
743 PGEN_DEBUG("res = %d, i = %d", res, i);
746 /* connect images pdf files into one file */
748 res = call_pdfunite(out_fnames, out_pdf_fname);
750 /* free allocated memory */
751 for (i = 0; i < files_count; ++i)
752 PGEN_IF_FREE_MEM(out_fnames[i]);
753 PGEN_IF_FREE_MEM(out_fnames);
755 PGEN_RETV_IF(1, -1, "Out of memory");