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_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);
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,
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
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)
148 PGEN_RETV_IF(canvas == NULL || b_s == NULL || p_s == NULL || off == NULL, -1 , "Invalid argument");
154 shadow_offset_x = (b_s->x - p_s->x);
155 shadow_offset_y = (b_s->y - p_s->y);
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);
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);
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);
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)
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");
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;
198 pdf_settings.page = page;
200 return get_pdf_preview(fname, &pdf_settings, out_img, size);
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)
213 PGEN_RETV_IF(canvas == NULL || img_buf == NULL || img_size <=0 || off == NULL || settings_px == NULL
214 , -1 , "Invalid argument");
217 struct size_px border_size = {1, 1};
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()");
225 img = get_pdf_img(canvas, img_buf, img_size);
226 PGEN_RETV_IF(img == NULL, -1 , "img is NULL");
228 if (settings_px->is_rotate90
229 && rotate90_image(img) < 0) {
230 PGEN_DEBUG("ERROR in rotate90_image()");
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");
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);
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)
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)
262 struct preview_page_px settings_px;
263 struct size_px off = {0, 0};
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()");
276 canvas = create_canvas(&(settings_px.full_size));
277 PGEN_RETV_IF(canvas == NULL, -1, "canvas is NULL");
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);
287 if (load_pdf_page_img(pdf_fname, page_num, &settings_px,
288 &img_buf, &img_size) < 0) {
289 destroy_canvas(canvas);
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);
304 save_scene(canvas, out_pic_fname);
305 destroy_canvas(canvas);
313 int save_empty_preview_page_image(const struct preview_page_req *settings_pts,
314 const char *out_pic_fname)
317 struct preview_page_px settings_px;
318 struct size_px off = {0, 0};
319 struct size_px border_size = {1, 1};
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()");
330 canvas = create_canvas(&(settings_px.full_size));
331 PGEN_RETV_IF(canvas == NULL, -1, "canvas is NULL");
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);
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);
350 save_scene(canvas, out_pic_fname);
351 destroy_canvas(canvas);
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)
365 PGEN_RETV_IF(NULL == fname || NULL == req_size || NULL == settings, -1, "Invalid argument");
367 struct size_pts input_pdf_pts = {0.0, };
369 int result = get_pdf_page_size(fname, &input_pdf_pts);
370 PGEN_RETV_IF(result < 0, -1, "can't get pdf page size");
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;
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*/
385 (int)(100 * req_size->x / input_pdf_pts.x);
387 (int)(100 * req_size->y / input_pdf_pts.y);
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;
395 settings->scale.w = 0;
396 settings->scale.h = 0;
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);
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
417 * > 0 generated pages count
419 int generate_pdf_preview_pages(const char *fname,
420 const struct size_pts *paper_size,
421 const struct size_px *available_size_px,
426 struct size_pts page_size = {0.0, 0.0};
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 ")];
435 /*PGEN_DEBUG("available_size: (%d, %d)",
436 available_size_px->x, available_size_px->y);*/
439 if (get_pdf_page_size(fname, &page_size) < 0) {
440 PGEN_DEBUG("ERROR in get_pdf_page_size()\n");
443 pages_count = get_pdf_pages_count(fname);
444 if (pages_count <= 0) {
445 PGEN_DEBUG("ERROR: pages_count = %d (<=0)\n", pages_count);
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;
455 /*if (get_preview_settings(&settings_req, &settings_px) < 0) {
456 PGEN_DEBUG("ERROR in get_preview_settings()");
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,
474 * @brief Process PDF preview generation with CUPS filters to PDF with
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
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)
492 struct pdfgen_settings pg_settings;
495 PGEN_RETV_IF(!fname || !scale, -1, "Invalid argument");
496 PGEN_DEBUG("processing file name: %s", fname);
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;
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()");
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);
515 result = call_pdftopdf(fname, out_pdf_fname, &pg_settings);
516 PGEN_RETV_IF(result < 0, -1, "ERROR in call_pdftopdf()");
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
532 * @param[in] is_grayscale image must be grascaled
534 * > 0 generated pages count
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)
545 pdf2pdf_preview_pages(fname, out_pdf_fname, printer_ppd_fname,
546 paper_size, orientation,
547 n_up, scale, is_grayscale);
549 res = generate_pdf_preview_pages(TEMP_PDFTOPDF_FNAME,
550 &(paper_size->s), available_size_px,
557 int process_image_downscale(const char *fname, const char *out_fname,
558 const struct size_px *available_size_px)
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);
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)
576 PGEN_RETV_IF(!fname || !out_pdf_fname || !printer_ppd_fname ||
577 !paper_size || !available_size_px || !scale,
578 -1, "Invalid argument");
580 struct pdfgen_settings pg_settings;
582 /* generate result pdf */
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;
591 int result = call_imagetopdf(fname, out_pdf_fname, &pg_settings);
592 PGEN_RETV_IF(result < 0, -1, "ERROR in call_imagetopdf()");
599 int img_files2pdf_preview_pages(char **const fnames,
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)
609 struct pdfgen_settings pg_settings;
610 int out_fname_size = sizeof(PREVIEW_TEMP_DIR "/mobileprint_xxxx.pdf ");
617 PGEN_RETV_IF(files_count <= 0 || files_count > 9999,
618 -1, "Incorrect files_count");
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;
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) {
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",
642 res = call_imagetopdf(fnames[i], cur_fname, &pg_settings);
643 PGEN_DEBUG("res = %d, i = %d", res, i);
646 /* connect images pdf files into one file */
648 res = call_pdfunite(out_fnames, out_pdf_fname);
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);
655 PGEN_RETV_IF(1, -1, "Out of memory");