1
/* GIMP - The GNU Image Manipulation Program
3
* file-pdf-save.c - PDF file exporter, based on the cairo PDF surface
5
* Copyright (C) 2010 Barak Itkin <lightningismyname@gmail.com>
7
* This program is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 3 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21
/* The PDF export plugin has 3 main procedures:
23
* This is the main procedure. It has 3 options for optimizations of
24
* the pdf file, and it can show a gui. This procedure works on a single
26
* 2. file-pdf-save-defaults
27
* This procedures is the one that will be invoked by gimp's file-save,
28
* when the pdf extension is chosen. If it's in RUN_INTERACTIVE, it will
29
* pop a user interface with more options, like file-pdf-save. If it's in
30
* RUN_NONINTERACTIVE, it will simply use the default values. Note that on
31
* RUN_WITH_LAST_VALS there will be no gui, however the values will be the
32
* ones that were used in the last interactive run (or the defaults if none
34
* 3. file-pdf-save-multi
35
* This procedures is more advanced, and it allows the creation of multiple
36
* paged pdf files. It will be located in File/Create/Multiple page PDF...
38
* It was suggested that file-pdf-save-multi will be removed from the UI as it
39
* does not match the product vision (GIMP isn't a program for editing multiple
43
/* Known Issues (except for the coding style issues):
44
* 1. Grayscale layers are inverted (although layer masks which are not grayscale,
46
* 2. Exporting some fonts doesn't work since gimp_text_layer_get_font Returns a
47
* font which is sometimes incompatiable with pango_font_description_from_string
48
* (gimp_text_layer_get_font sometimes returns suffixes such as "semi-expanded" to
49
* the font's name although the GIMP's font selection dialog shows the don'ts name
50
* normally - This should be checked again in GIMP 2.7)
51
* 3. Indexed layers can't be optimized yet (Since gimp_histogram won't work on
53
* 4. Rendering the pango layout requires multiplying the size in PANGO_SCALE. This
54
* means I'll need to do some hacking on the markup returned from GIMP.
55
* 5. When accessing the contents of layer groups is supported, we should do use it
56
* (since this plugin should preserve layers).
58
* Also, there are 2 things which we should warn the user about:
59
* 1. Cairo does not support bitmap masks for text.
60
* 2. Currently layer modes are ignored. We do support layers, including
61
* transparency and opacity, but layer modes are not supported.
66
* April 29, 2009 | Barak Itkin <lightningismyname@gmail.com>
67
* First version of the plugin. This is only a proof of concept and not a full
70
* May 6, 2009 Barak | Itkin <lightningismyname@gmail.com>
71
* Added new features and several bugfixes:
72
* - Added handling for image resolutions
73
* - fixed the behaviour of getting font sizes
74
* - Added various optimizations (solid rectangles instead of bitmaps, ignoring
75
* invisible layers, etc.) as a macro flag.
76
* - Added handling for layer masks, use CAIRO_FORMAT_A8 for grayscale drawables.
77
* - Indexed layers are now supported
79
* August 17, 2009 | Barak Itkin <lightningismyname@gmail.com>
80
* Most of the plugin was rewritten from scratch and it now has several new
82
* - Got rid of the optimization macros in the code. The gui now allows to
83
* select which optimizations to apply.
84
* - Added a procedure to allow the creation of multiple paged PDF's
85
* - Registered the plugin on "<Image>/File/Create/PDF"
87
* August 21, 2009 | Barak Itkin <lightningismyname@gmail.com>
88
* Fixed a typo that prevented the plugin from compiling...
89
* A migration to the new GIMP 2.8 api, which includes:
90
* - Now using gimp_export_dialog_new
91
* - Using gimp_text_layer_get_hint_style (2.8) instead of the depreceated
92
* gimp_text_layer_get_hinting (2.6).
94
* August 24, 2010 | Barak Itkin <lightningismyname@gmail.com>
95
* More migrations to the new GIMP 2.8 api:
96
* - Now using the GimpItem api
97
* - Using gimp_text_layer_get_markup where possible
98
* - Fixed some compiler warnings
99
* Also merged the header and c file into one file, Updated some of the comments
100
* and documentation, and moved this into the main source repository.
105
#include <cairo-pdf.h>
106
#include <pango/pangocairo.h>
108
#include <libgimp/gimp.h>
109
#include <libgimp/gimpui.h>
111
#include "libgimp/stdplugins-intl.h"
113
#define SAVE_PROC "file-pdf-save"
114
#define SAVE_MULTI_PROC "file-pdf-save-multi"
115
#define PLUG_IN_BINARY "file-pdf-save"
116
#define PLUG_IN_ROLE "gimp-file-pdf-save"
118
#define DATA_OPTIMIZE "file-pdf-data-optimize"
119
#define DATA_IMAGE_LIST "file-pdf-data-multi-page"
121
/* Gimp will crash before you reach this limitation :D */
122
#define MAX_PAGE_COUNT 350
123
#define MAX_FILE_NAME_LENGTH 350
125
#define THUMB_WIDTH 90
126
#define THUMB_HEIGHT 120
130
gboolean ignore_hidden;
131
gboolean apply_masks;
135
gint32 images[MAX_PAGE_COUNT];
137
gchar file_name[MAX_FILE_NAME_LENGTH];
141
PdfOptimize optimize;
159
static gboolean init_vals (const gchar *name,
161
const GimpParam *param,
164
GimpRunMode *run_mode);
166
static void init_image_list_defaults (gint32 image);
168
static void validate_image_list (void);
170
static gboolean gui_single (void);
171
static gboolean gui_multi (void);
173
static void choose_file_call (GtkWidget* browse_button,
174
gpointer file_entry);
176
static gboolean get_image_list (void);
177
static GtkTreeModel* create_model (void);
179
static void add_image_call (GtkWidget *widget,
181
static void del_image_call (GtkWidget *widget,
183
static void remove_call (GtkTreeModel *tree_model,
186
static void recount_pages (void);
188
static cairo_surface_t *get_drawable_image (GimpDrawable *drawable);
189
static GimpRGB get_layer_color (GimpDrawable *layer,
191
static void drawText (GimpDrawable* text_layer,
197
static void query (void);
198
static void run (const gchar *name,
200
const GimpParam *param,
202
GimpParam **return_vals);
204
static gboolean dnd_remove = TRUE;
205
static PdfMultiPage multi_page;
207
static PdfOptimize optimize = {
208
TRUE, /* vectorize */
209
TRUE, /* ignore_hidden */
210
TRUE /* apply_masks */
213
static GtkTreeModel *model;
214
static GtkWidget *file_choose;
215
static gchar* file_name;
217
GimpPlugInInfo PLUG_IN_INFO =
239
#define SA_ARG_COUNT_DEFAULT 5
256
static GimpParamDef save_args[] =
258
{GIMP_PDB_INT32, "run-mode", "Run mode"},
259
{GIMP_PDB_IMAGE, "image", "Input image"},
260
{GIMP_PDB_DRAWABLE, "drawable", "Input drawable"},
261
{GIMP_PDB_STRING, "filename", "The name of the file to save the image in"},
262
{GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in"},
263
{GIMP_PDB_INT32, "vectorize", "Convert bitmaps to vector graphics where possible. TRUE or FALSE"},
264
{GIMP_PDB_INT32, "ignore-hidden","Omit hidden layers and layers with zero opacity. TRUE or FALSE"},
265
{GIMP_PDB_INT32, "apply-masks", "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)"}
268
static GimpParamDef save_multi_args[] =
270
{GIMP_PDB_INT32, "run-mode", "Run mode"},
271
{GIMP_PDB_INT32ARRAY, "images", "Input image for each page (An image can appear more than once)"},
272
{GIMP_PDB_INT32, "count", "The amount of images entered (This will be the amount of pages). 1 <= count <= MAX_PAGE_COUNT"},
273
{GIMP_PDB_INT32, "vectorize", "Convert bitmaps to vector graphics where possible. TRUE or FALSE"},
274
{GIMP_PDB_INT32, "ignore-hidden","Omit hidden layers and layers with zero opacity. TRUE or FALSE"},
275
{GIMP_PDB_INT32, "apply-masks", "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)"},
276
{GIMP_PDB_STRING, "filename", "The name of the file to save the image in"},
277
{GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in"}
280
gimp_install_procedure (SAVE_PROC,
281
"Save files in PDF format",
282
"Saves files in Adobe's Portable Document Format. "
283
"PDF is designed to be easily processed by a variety "
284
"of different platforms, and is a distant cousin of "
287
"Copyright Barak Itkin",
289
N_("Portable Document Format"),
290
"RGB*, GRAY*, INDEXED*",
292
G_N_ELEMENTS (save_args), 0,
295
gimp_install_procedure (SAVE_MULTI_PROC,
296
"Save files in PDF format",
297
"Saves files in Adobe's Portable Document Format. "
298
"PDF is designed to be easily processed by a variety "
299
"of different platforms, and is a distant cousin of "
302
"Copyright Barak Itkin",
304
N_("_Create multipage PDF..."),
305
"RGB*, GRAY*, INDEXED*",
307
G_N_ELEMENTS (save_multi_args), 0,
308
save_multi_args, NULL);
310
/* gimp_plugin_menu_register (SAVE_MULTI_PROC,
311
"<Image>/File/Create/PDF"); */
313
gimp_register_file_handler_mime (SAVE_PROC, "application/pdf");
314
gimp_register_save_handler (SAVE_PROC, "pdf", "");
318
run (const gchar *name,
320
const GimpParam *param,
322
GimpParam **return_vals)
324
static GimpParam values[1];
325
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
326
GimpRunMode run_mode;
328
/* Plug-in variables */
329
gboolean single_image;
330
gboolean defaults_proc;
332
/* Plug-In variables */
333
cairo_surface_t *pdf_file;
335
GimpExportCapabilities capabilities;
340
gdouble x_res, y_res;
341
gdouble x_scale, y_scale;
345
GimpImageBaseType type;
350
gint32 num_of_layers;
352
cairo_surface_t *layer_image;
356
gboolean single_color;
359
GimpDrawable *mask = NULL;
360
cairo_surface_t *mask_image = NULL;
364
/* Setting mandatory output values */
366
*return_vals = values;
368
values[0].type = GIMP_PDB_STATUS;
369
values[0].data.d_status = status;
371
/* Initializing all the settings */
372
multi_page.image_count = 0;
374
if (! init_vals (name, nparams, param, &single_image,
375
&defaults_proc, &run_mode))
377
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
381
/* Starting the executions */
382
if (run_mode == GIMP_RUN_INTERACTIVE)
388
values[0].data.d_status = GIMP_PDB_CANCEL;
392
else if (! gui_multi ())
394
values[0].data.d_status = GIMP_PDB_CANCEL;
398
if (file_name == NULL)
400
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
401
gimp_message (_("You must select a file to save!"));
406
pdf_file = cairo_pdf_surface_create (file_name, 1, 1);
407
if (cairo_surface_status (pdf_file) != CAIRO_STATUS_SUCCESS)
409
char *str = g_strdup_printf
410
(_("An error occured while creating the PDF file:\n"
412
"Make sure you entered a valid filename and that the selected location isn't read only!"),
413
cairo_status_to_string (cairo_surface_status (pdf_file)));
418
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
421
cr = cairo_create (pdf_file);
423
capabilities = GIMP_EXPORT_CAN_HANDLE_RGB | GIMP_EXPORT_CAN_HANDLE_ALPHA |
424
GIMP_EXPORT_CAN_HANDLE_GRAY | GIMP_EXPORT_CAN_HANDLE_LAYERS |
425
GIMP_EXPORT_CAN_HANDLE_INDEXED;
426
if (optimize.apply_masks)
427
capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS;
429
for (i = 0; i < multi_page.image_count; i++)
431
/* Save the state of the surface before any changes, so that settings
432
* from one page won't affect all the others */
435
image_id = multi_page.images[i];
437
/* We need the active layer in order to use gimp_image_export */
438
temp = gimp_image_get_active_drawable (image_id);
440
exported = gimp_export_image (&image_id, &temp, NULL, capabilities) == GIMP_EXPORT_EXPORT;
443
type = gimp_image_base_type (image_id);
445
gimp_image_get_resolution (image_id, &x_res, &y_res);
446
x_scale = 72.0 / x_res;
447
y_scale = 72.0 / y_res;
449
cairo_pdf_surface_set_size (pdf_file,
450
gimp_image_width (image_id) * x_scale,
451
gimp_image_height (image_id) * y_scale);
453
/* This way we set how many pixels are there in every inch.
454
* It's very important for PangoCairo */
455
cairo_surface_set_fallback_resolution (pdf_file, x_res, y_res);
457
/* PDF is usually 72 points per inch. If we have a different resolution,
458
* we will need this to fit our drawings */
459
cairo_scale (cr, x_scale, y_scale);
461
/* Now, we should loop over the layers of each image */
462
layers = gimp_image_get_layers (image_id, &num_of_layers);
464
for (j = 0; j < num_of_layers; j++)
466
layer = gimp_drawable_get (layers [num_of_layers-j-1]);
467
opacity = gimp_layer_get_opacity (layer->drawable_id)/100.0;
469
/* Gimp doesn't display indexed layers with opacity below 50%
470
* And if it's above 50%, it will be rounded to 100% */
471
if (type == GIMP_INDEXED)
479
if (gimp_item_get_visible (layer->drawable_id)
480
&& (! optimize.ignore_hidden || (optimize.ignore_hidden && opacity > 0.0)))
482
mask_id = gimp_layer_get_mask (layer->drawable_id);
485
mask = gimp_drawable_get (mask_id);
486
mask_image = get_drawable_image (mask);
489
gimp_drawable_offsets (layer->drawable_id, &x, &y);
491
/* For raster layers */
492
if (!gimp_item_is_text_layer (layer->drawable_id))
494
layer_color = get_layer_color (layer, &single_color);
495
cairo_rectangle (cr, x, y, layer->width, layer->height);
497
if (optimize.vectorize && single_color)
499
cairo_set_source_rgba (cr, layer_color.r, layer_color.g, layer_color.b, layer_color.a * opacity);
501
cairo_mask_surface (cr, mask_image, x, y);
508
layer_image = get_drawable_image (layer);
509
cairo_set_source_surface (cr, layer_image, x, y);
510
cairo_push_group (cr);
511
cairo_paint_with_alpha (cr, opacity);
512
cairo_pop_group_to_source (cr);
514
cairo_mask_surface (cr, mask_image, x, y);
517
cairo_reset_clip (cr);
519
cairo_surface_destroy (layer_image);
522
/* For text layers */
525
drawText (layer, opacity, cr, x_res, y_res);
529
/* We are done with the layer - time to free some resources */
530
gimp_drawable_detach (layer);
533
gimp_drawable_detach (mask);
534
cairo_surface_destroy (mask_image);
537
/* We are done with this image - Show it! */
538
cairo_show_page (cr);
542
gimp_image_delete (image_id);
545
/* We are done with all the images - time to free the resources */
546
cairo_surface_destroy (pdf_file);
549
/* Finally done, let's save the parameters */
550
gimp_set_data (DATA_OPTIMIZE, &optimize, sizeof (optimize));
553
g_strlcpy (multi_page.file_name, file_name, MAX_FILE_NAME_LENGTH);
554
gimp_set_data (DATA_IMAGE_LIST, &multi_page, sizeof (multi_page));
559
/******************************************************/
560
/* Begining of parameter handling functions */
561
/******************************************************/
563
/* A function that takes care of loading the basic
566
init_vals (const gchar *name,
568
const GimpParam *param,
569
gboolean *single_image,
570
gboolean *defaults_proc,
571
GimpRunMode *run_mode)
573
gboolean had_saved_list = FALSE;
575
gboolean defaults = FALSE;
579
if (g_str_equal (name, SAVE_PROC))
582
if (nparams != SA_ARG_COUNT && nparams != SA_ARG_COUNT_DEFAULT)
585
*run_mode = param[SA_RUN_MODE].data.d_int32;
586
image = param[SA_IMAGE].data.d_int32;
587
file_name = param[SA_FILENAME].data.d_string;
589
if (nparams == SA_ARG_COUNT)
591
optimize.apply_masks = param[SA_APPLY_MASKS].data.d_int32;
592
optimize.vectorize = param[SA_VECTORIZE].data.d_int32;
593
optimize.ignore_hidden = param[SA_IGNORE_HIDDEN].data.d_int32;
598
else if (g_str_equal (name, SAVE_MULTI_PROC))
601
if (nparams != SMA_ARG_COUNT)
604
*run_mode = param[SMA_RUN_MODE].data.d_int32;
606
file_name = param[SA_FILENAME].data.d_string;
608
optimize.apply_masks = param[SMA_APPLY_MASKS].data.d_int32;
609
optimize.vectorize = param[SMA_VECTORIZE].data.d_int32;
610
optimize.ignore_hidden = param[SMA_IGNORE_HIDDEN].data.d_int32;
618
case GIMP_RUN_NONINTERACTIVE:
621
init_image_list_defaults (image);
625
multi_page.image_count = param[SMA_COUNT].data.d_int32;
626
if (param[SMA_IMAGES].data.d_int32array != NULL)
627
for (i = 0; i < param[SMA_COUNT].data.d_int32; i++)
628
multi_page.images[i] = param[SMA_IMAGES].data.d_int32array[i];
632
case GIMP_RUN_INTERACTIVE:
633
/* Possibly retrieve data */
634
gimp_get_data (DATA_OPTIMIZE, &optimize);
635
had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
637
if (had_saved_list && (file_name == NULL || strlen (file_name) == 0))
639
file_name = multi_page.file_name;
642
if (single || ! had_saved_list )
643
init_image_list_defaults (image);
647
case GIMP_RUN_WITH_LAST_VALS:
648
/* Possibly retrieve data */
651
had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
654
file_name = multi_page.file_name;
659
init_image_list_defaults (image);
661
gimp_get_data (DATA_OPTIMIZE, &optimize);
666
*defaults_proc = defaults;
667
*single_image = single;
669
validate_image_list ();
674
/* A function that initializes the image list to default values */
676
init_image_list_defaults (gint32 image)
680
multi_page.images[0] = image;
681
multi_page.image_count = 1;
684
multi_page.image_count = 0;
688
/* A function that removes images that are no longer valid from
691
validate_image_list (void)
696
for (i = 0 ; i < MAX_PAGE_COUNT && i < multi_page.image_count ; i++)
698
if (gimp_image_is_valid (multi_page.images[i]))
700
multi_page.images[valid] = multi_page.images[i];
704
multi_page.image_count = valid;
707
/******************************************************/
708
/* Begining of GUI functions */
709
/******************************************************/
710
/* The main GUI function for saving single-paged PDFs */
717
GtkWidget *vectorize_c;
718
GtkWidget *ignore_hidden_c;
723
gimp_ui_init (PLUG_IN_BINARY, FALSE);
725
window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_PROC);
727
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
728
gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
729
vbox, TRUE, TRUE, 0);
731
gtk_container_set_border_width (GTK_CONTAINER (window), 12);
733
ignore_hidden_c = gtk_check_button_new_with_label (_("Omit hidden layers and layers with zero opacity"));
734
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c), optimize.ignore_hidden);
735
gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, TRUE, TRUE, 0);
737
vectorize_c = gtk_check_button_new_with_label (_("Convert bitmaps to vector graphics where possible"));
738
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c), optimize.vectorize);
739
gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, TRUE, TRUE, 0);
741
apply_c = gtk_check_button_new_with_label (_("Apply layer masks before saving"));
742
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c), optimize.apply_masks);
743
gtk_box_pack_end (GTK_BOX (vbox), apply_c, TRUE, TRUE, 0);
744
gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
746
gtk_widget_show_all (window);
748
run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;
750
optimize.ignore_hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
751
optimize.vectorize = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
752
optimize.apply_masks = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
754
gtk_widget_destroy (window);
757
/* The main GUI function for saving multi-paged PDFs */
764
GtkWidget *file_label;
765
GtkWidget *file_entry;
766
GtkWidget *file_browse;
767
GtkWidget *file_hbox;
769
GtkWidget *vectorize_c;
770
GtkWidget *ignore_hidden_c;
774
GtkWidget *page_view;
776
GtkWidget *h_but_box;
780
GtkWidget *img_combo;
781
GtkWidget *add_image;
786
gimp_ui_init (PLUG_IN_BINARY, FALSE);
788
window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_MULTI_PROC);
790
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
791
gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
792
vbox, TRUE, TRUE, 0);
794
gtk_container_set_border_width (GTK_CONTAINER (window), 12);
796
file_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
797
file_label = gtk_label_new (_("Save to:"));
798
file_entry = gtk_entry_new ();
799
if (file_name != NULL)
800
gtk_entry_set_text (GTK_ENTRY (file_entry), file_name);
801
file_browse = gtk_button_new_with_label (_("Browse..."));
802
file_choose = gtk_file_chooser_dialog_new (_("Multipage PDF export"),
803
GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_SAVE,
804
"gtk-save", GTK_RESPONSE_OK,
805
"gtk-cancel", GTK_RESPONSE_CANCEL,
808
gtk_box_pack_start (GTK_BOX (file_hbox), file_label, FALSE, FALSE, 0);
809
gtk_box_pack_start (GTK_BOX (file_hbox), file_entry, TRUE, TRUE, 0);
810
gtk_box_pack_start (GTK_BOX (file_hbox), file_browse, FALSE, FALSE, 0);
812
gtk_box_pack_start (GTK_BOX (vbox), file_hbox, TRUE, TRUE, 0);
814
page_view = gtk_icon_view_new ();
815
model = create_model ();
816
gtk_icon_view_set_model (GTK_ICON_VIEW (page_view), model);
817
gtk_icon_view_set_reorderable (GTK_ICON_VIEW (page_view), TRUE);
818
gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (page_view), GTK_SELECTION_MULTIPLE);
820
gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (page_view), THUMB);
821
gtk_icon_view_set_text_column (GTK_ICON_VIEW (page_view), PAGE_NUMBER);
822
gtk_icon_view_set_tooltip_column (GTK_ICON_VIEW (page_view), IMAGE_NAME);
824
scroll = gtk_scrolled_window_new (NULL, NULL);
825
gtk_widget_set_size_request (scroll, -1, 300);
827
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
828
gtk_container_add (GTK_CONTAINER (scroll), page_view);
830
gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
832
h_but_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
833
gtk_button_box_set_layout (GTK_BUTTON_BOX (h_but_box), GTK_BUTTONBOX_START);
835
del = gtk_button_new_with_label (_("Remove the selected pages"));
836
gtk_box_pack_start (GTK_BOX (h_but_box), del, TRUE, TRUE, 0);
838
gtk_box_pack_start (GTK_BOX (vbox), h_but_box, FALSE, FALSE, 0);
840
h_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
842
img_combo = gimp_image_combo_box_new (NULL, NULL);
843
gtk_box_pack_start (GTK_BOX (h_box), img_combo, FALSE, FALSE, 0);
845
add_image = gtk_button_new_with_label (_("Add this image"));
846
gtk_box_pack_start (GTK_BOX (h_box), add_image, FALSE, FALSE, 0);
848
gtk_box_pack_start (GTK_BOX (vbox), h_box, FALSE, FALSE, 0);
850
ignore_hidden_c = gtk_check_button_new_with_label (_("Omit hidden layers and layers with zero opacity"));
851
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c), optimize.ignore_hidden);
852
gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, FALSE, FALSE, 0);
854
vectorize_c = gtk_check_button_new_with_label (_("Convert bitmaps to vector graphics where possible"));
855
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c), optimize.vectorize);
856
gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, FALSE, FALSE, 0);
858
apply_c = gtk_check_button_new_with_label (_("Apply layer masks before saving"));
859
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c), optimize.apply_masks);
860
gtk_box_pack_end (GTK_BOX (vbox), apply_c, FALSE, FALSE, 0);
861
gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
863
gtk_widget_show_all (window);
865
g_signal_connect (G_OBJECT (file_browse), "clicked",
866
G_CALLBACK (choose_file_call), G_OBJECT (file_entry));
868
g_signal_connect (G_OBJECT (add_image), "clicked",
869
G_CALLBACK (add_image_call), G_OBJECT (img_combo));
871
g_signal_connect (G_OBJECT (del), "clicked",
872
G_CALLBACK (del_image_call), G_OBJECT (page_view));
874
g_signal_connect (G_OBJECT (model), "row-deleted",
875
G_CALLBACK (remove_call), NULL);
877
run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;
879
run &= get_image_list ();
881
temp = gtk_entry_get_text (GTK_ENTRY (file_entry));
882
g_stpcpy (file_name, temp);
884
optimize.ignore_hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
885
optimize.vectorize = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
886
optimize.apply_masks = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
888
gtk_widget_destroy (window);
892
/* A function that is called when the button for browsing for file
893
* locations was clicked */
895
choose_file_call (GtkWidget *browse_button,
898
GFile *file = g_file_new_for_path (gtk_entry_get_text (GTK_ENTRY (file_entry)));
899
gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (file_choose), g_file_get_uri (file));
901
if (gtk_dialog_run (GTK_DIALOG (file_choose)) == GTK_RESPONSE_OK)
903
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (file_choose));
904
gtk_entry_set_text (GTK_ENTRY (file_entry), g_file_get_path (file));
907
file_name = g_file_get_path (file);
908
gtk_widget_hide (file_choose);
911
/* A function to create the basic GtkTreeModel for the icon view */
918
gint32 image = multi_page.images[0];
920
/* validate_image_list was called earlier, so all the images
921
* up to multi_page.image_count are valid */
922
model = gtk_list_store_new (4,
923
GDK_TYPE_PIXBUF, /* THUMB */
924
G_TYPE_STRING, /* PAGE_NUMBER */
925
G_TYPE_STRING, /* IMAGE_NAME */
926
G_TYPE_INT); /* IMAGE_ID */
928
for (i = 0 ; i < multi_page.image_count && i < MAX_PAGE_COUNT ; i++)
930
image = multi_page.images[i];
932
gtk_list_store_append (model, &iter);
933
gtk_list_store_set (model, &iter,
934
THUMB, gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT, GIMP_PIXBUF_SMALL_CHECKS),
935
PAGE_NUMBER, g_strdup_printf ("Page %d", i+1),
936
IMAGE_NAME, gimp_image_get_name (image),
942
return GTK_TREE_MODEL (model);
945
/* A function that puts the images from the model inside the
946
* images (pages) array */
948
get_image_list (void)
951
gboolean valid = gtk_tree_model_get_iter_first (model, &iter);
954
multi_page.image_count = 0;
958
gimp_message (_("Error! In order to save the file, at least one image should be added!"));
964
gtk_tree_model_get (model, &iter,
967
multi_page.images[multi_page.image_count] = image;
969
valid = gtk_tree_model_iter_next (model, &iter);
970
multi_page.image_count++;
976
/* A function that is called when the button for adding an image
979
add_image_call (GtkWidget *widget,
988
gimp_int_combo_box_get_active (img_combo, &image);
990
store = GTK_LIST_STORE (model);
992
gtk_list_store_append (store, &iter);
993
gtk_list_store_set (store, &iter,
994
PAGE_NUMBER, g_strdup_printf ("Page %d", multi_page.image_count+1),
995
THUMB, gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT, GIMP_PIXBUF_SMALL_CHECKS),
996
IMAGE_NAME, gimp_image_get_name (image),
1001
multi_page.image_count++;
1006
/* A function that is called when the button for deleting the
1007
* selected images was clicked */
1009
del_image_call (GtkWidget *widget,
1013
GtkTreeRowReference **items;
1014
GtkTreePath *item_path;
1026
list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view));
1028
len = g_list_length (list);
1031
items = g_newa (GtkTreeRowReference*, len);
1033
for (i = 0; i < len; i++)
1035
temp = g_list_nth_data (list, i);
1036
items[i] = gtk_tree_row_reference_new (model, temp);
1037
gtk_tree_path_free (temp);
1041
for (i = 0; i < len; i++)
1043
item_path = gtk_tree_row_reference_get_path (items[i]);
1044
gtk_tree_model_get_iter (model, &item, item_path);
1046
/* Get the data that should be freed */
1047
gtk_tree_model_get (model, &item,
1048
THUMB, &thumb, IMAGE_NAME, &name, -1);
1050
/* Only after you have the pointers, remove them from the tree */
1051
gtk_list_store_remove (GTK_LIST_STORE (model), &item);
1053
/* Now you can free the data */
1054
g_object_unref(thumb);
1057
gtk_tree_path_free (item_path);
1058
gtk_tree_row_reference_free (items[i]);
1059
multi_page.image_count--;
1070
/* A function that is called on rows-deleted signal. It will
1071
* call the function to relabel the pages */
1073
remove_call (GtkTreeModel *tree_model,
1079
/* The gtk documentation says that we should not free the indices array */
1083
/* A function to relabel the pages in the icon view, when
1084
* their order was changed */
1086
recount_pages (void)
1088
GtkListStore *store;
1093
store = GTK_LIST_STORE (model);
1095
valid = gtk_tree_model_get_iter_first (model, &iter);
1098
gtk_list_store_set (store, &iter,
1099
PAGE_NUMBER, g_strdup_printf ("Page %d", i + 1),
1101
valid = gtk_tree_model_iter_next (model, &iter);
1106
/******************************************************/
1107
/* Begining of the actual PDF functions */
1108
/******************************************************/
1110
/* A function to get a cairo image surface from a drawable.
1111
* Some of the code was taken from the gimp-print plugin */
1113
/* Gimp RGB (24 bit) to Cairo RGB (24 bit) */
1115
convert_from_rgb_to_rgb (const guchar *src,
1121
GIMP_CAIRO_RGB24_SET_PIXEL (dest,
1122
src[0], src[1], src[2]);
1129
/* Gimp RGBA (32 bit) to Cairo RGBA (32 bit) */
1131
convert_from_rgba_to_rgba (const guchar *src,
1137
GIMP_CAIRO_ARGB32_SET_PIXEL (dest,
1138
src[0], src[1], src[2], src[3]);
1145
/* Gimp Gray (8 bit) to Cairo RGB (24 bit) */
1147
convert_from_gray_to_rgb (const guchar *src,
1153
GIMP_CAIRO_RGB24_SET_PIXEL (dest,
1154
src[0], src[0], src[0]);
1161
/* Gimp GrayA (16 bit) to Cairo RGBA (32 bit) */
1163
convert_from_graya_to_rgba (const guchar *src,
1169
GIMP_CAIRO_ARGB32_SET_PIXEL (dest,
1170
src[0], src[0], src[0], src[1]);
1177
/* Gimp Indexed (8 bit) to Cairo RGB (24 bit) */
1179
convert_from_indexed_to_rgb (const guchar *src,
1186
const gint i = 3 * src[0];
1188
GIMP_CAIRO_RGB24_SET_PIXEL (dest,
1189
cmap[i], cmap[i + 1], cmap[i + 2]);
1196
/* Gimp IndexedA (16 bit) to Cairo RGBA (32 bit) */
1198
convert_from_indexeda_to_rgba (const guchar *src,
1205
const gint i = 3 * src[0];
1207
GIMP_CAIRO_ARGB32_SET_PIXEL (dest,
1208
cmap[i], cmap[i + 1], cmap[i + 2], src[1]);
1215
static cairo_surface_t *
1216
get_drawable_image (GimpDrawable *drawable)
1218
gint32 drawable_ID = drawable->drawable_id;
1219
GimpPixelRgn region;
1220
GimpImageType image_type = gimp_drawable_type (drawable_ID);
1221
cairo_surface_t *surface;
1222
cairo_format_t format;
1223
const gint width = drawable->width;
1224
const gint height = drawable->height;
1225
guchar cmap[3 * 256] = { 0, };
1229
gboolean indexed = FALSE;
1230
int bpp = drawable->bpp;
1232
if (gimp_drawable_is_indexed (drawable_ID))
1238
colors = gimp_image_get_colormap (gimp_item_get_image (drawable_ID),
1240
memcpy (cmap, colors, 3 * num_colors);
1246
case 1: /* GRAY or INDEXED */
1249
format = CAIRO_FORMAT_RGB24;
1253
format = CAIRO_FORMAT_RGB24;
1257
format = CAIRO_FORMAT_RGB24;
1260
case 2: /* GRAYA or INDEXEDA */
1262
format = CAIRO_FORMAT_ARGB32;
1266
g_assert_not_reached ();
1270
surface = cairo_image_surface_create (format, width, height);
1272
pixels = cairo_image_surface_get_data (surface);
1273
stride = cairo_image_surface_get_stride (surface);
1275
gimp_pixel_rgn_init (®ion, drawable, 0, 0, width, height, FALSE, FALSE);
1277
for (pr = gimp_pixel_rgns_register (1, ®ion);
1279
pr = gimp_pixel_rgns_process (pr))
1281
const guchar *src = region.data;
1282
guchar *dest = pixels + region.y * stride + region.x * 4;
1285
for (y = 0; y < region.h; y++)
1289
case GIMP_RGB_IMAGE:
1290
convert_from_rgb_to_rgb (src, dest, region.w);
1293
case GIMP_RGBA_IMAGE:
1294
convert_from_rgba_to_rgba (src, dest, region.w);
1297
case GIMP_GRAY_IMAGE:
1298
convert_from_gray_to_rgb (src, dest, region.w);
1301
case GIMP_GRAYA_IMAGE:
1302
convert_from_graya_to_rgba (src, dest, region.w);
1305
case GIMP_INDEXED_IMAGE:
1306
convert_from_indexed_to_rgb (src, dest, region.w, cmap);
1309
case GIMP_INDEXEDA_IMAGE:
1310
convert_from_indexeda_to_rgba (src, dest, region.w, cmap);
1314
src += region.rowstride;
1319
cairo_surface_mark_dirty (surface);
1324
/* A function to check if a drawable is single colored
1325
* This allows to convert bitmaps to vector where possible */
1327
get_layer_color (GimpDrawable *layer,
1331
gdouble red, green, blue, alpha;
1332
gdouble dev, devSum;
1333
gdouble median, pixels, count, precentile;
1336
id = layer->drawable_id;
1344
if (gimp_drawable_is_indexed (id))
1346
/* FIXME: We can't do a propper histogram on indexed layers! */
1348
col. r = col.g = col.b = col.a = 0;
1352
/* Are we in RGB mode? */
1353
if (layer->bpp >= 3)
1355
gimp_histogram (id, GIMP_HISTOGRAM_RED, 0, 255, &red, &dev, &median, &pixels, &count, &precentile);
1357
gimp_histogram (id, GIMP_HISTOGRAM_GREEN, 0, 255, &green, &dev, &median, &pixels, &count, &precentile);
1359
gimp_histogram (id, GIMP_HISTOGRAM_BLUE, 0, 255, &blue, &dev, &median, &pixels, &count, &precentile);
1362
/* We are in Grayscale mode (or Indexed) */
1365
gimp_histogram (id, GIMP_HISTOGRAM_VALUE, 0, 255, &red, &dev, &median, &pixels, &count, &precentile);
1370
if (gimp_drawable_has_alpha (id))
1371
gimp_histogram (id, GIMP_HISTOGRAM_ALPHA, 0, 255, &alpha, &dev, &median, &pixels, &count, &precentile);
1376
*single = devSum == 0;
1386
/* A function that uses Pango to render the text to our cairo surface,
1387
* in the same way it was the user saw it inside gimp.
1388
* Needs some work on choosing the font name better, and on hinting
1389
* (freetype and pango differences)
1392
drawText (GimpDrawable *text_layer,
1398
gint32 text_id = text_layer->drawable_id;
1399
GimpImageBaseType type = gimp_drawable_type (text_id);
1401
gchar *text = gimp_text_layer_get_text (text_id);
1402
gchar *markup = gimp_text_layer_get_markup (text_id);
1405
cairo_font_options_t *options;
1415
GimpTextHintStyle hinting;
1417
GimpTextJustification j;
1419
PangoAlignment align;
1420
GimpTextDirection dir;
1421
PangoDirection pango_dir;
1423
PangoLayout *layout;
1424
PangoContext *context;
1425
PangoFontDescription *font_description;
1428
gdouble line_spacing;
1429
gdouble letter_spacing;
1430
PangoAttribute *letter_spacing_at;
1431
PangoAttrList *attr_list = pango_attr_list_new ();
1435
options = cairo_font_options_create ();
1436
attr_list = pango_attr_list_new ();
1437
cairo_get_font_options (cr, options);
1440
gimp_drawable_offsets (text_id, &x, &y);
1441
cairo_move_to (cr, x, y);
1444
/* When dealing with a gray/indexed image, the viewed color of the text layer
1445
* can be different than the one kept in the memory */
1446
if (type == GIMP_RGB)
1447
gimp_text_layer_get_color (text_id, &color);
1449
gimp_image_pick_color (gimp_item_get_image (text_id), text_id, x, y, FALSE, FALSE, 0, &color);
1451
cairo_set_source_rgba (cr, color.r, color.g, color.b, opacity);
1454
hinting = gimp_text_layer_get_hint_style (text_id);
1457
case GIMP_TEXT_HINT_STYLE_NONE:
1458
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
1461
case GIMP_TEXT_HINT_STYLE_SLIGHT:
1462
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_SLIGHT);
1465
case GIMP_TEXT_HINT_STYLE_MEDIUM:
1466
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_MEDIUM);
1469
case GIMP_TEXT_HINT_STYLE_FULL:
1470
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_FULL);
1475
if (gimp_text_layer_get_antialias (text_id))
1476
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_DEFAULT);
1478
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
1480
/* We are done with cairo's settings.
1481
* It's time to create the context */
1482
cairo_set_font_options (cr, options);
1483
context = pango_cairo_create_context (cr);
1484
pango_cairo_context_set_font_options (context, options);
1486
/* Text Direction */
1487
dir = gimp_text_layer_get_base_direction (text_id);
1489
if (dir == GIMP_TEXT_DIRECTION_RTL)
1490
pango_dir = PANGO_DIRECTION_RTL;
1492
pango_dir = PANGO_DIRECTION_LTR;
1494
pango_context_set_base_dir (context, pango_dir);
1496
/* We are done with the context's settings.
1497
* It's time to create the layout */
1498
layout = pango_layout_new (context);
1501
font_family = gimp_text_layer_get_font (text_id);
1502
/* We need to find a way to convert GIMP's returned font name to
1503
* a normal Pango name... Hopefully GIMP 2.8 with Pango will fix it. */
1504
font_description = pango_font_description_from_string (font_family);
1507
size = gimp_text_layer_get_font_size (text_id, &unit);
1508
if (! g_strcmp0 (gimp_unit_get_abbreviation (unit), "px") == 0)
1509
size *= 1.0 / gimp_unit_get_factor (unit) * x_res / y_res;
1510
pango_font_description_set_absolute_size (font_description, size * PANGO_SCALE);
1512
pango_layout_set_font_description (layout, font_description);
1514
/* Width and height */
1515
pango_layout_set_width (layout, text_layer->width * PANGO_SCALE);
1516
pango_layout_set_height (layout, text_layer->height * PANGO_SCALE);
1518
/* Justification, and Alignment */
1520
j = gimp_text_layer_get_justification (text_id);
1522
if (j == GIMP_TEXT_JUSTIFY_CENTER)
1523
align = PANGO_ALIGN_CENTER;
1524
else if (j == GIMP_TEXT_JUSTIFY_LEFT)
1525
align = PANGO_ALIGN_LEFT;
1526
else if (j == GIMP_TEXT_JUSTIFY_RIGHT)
1527
align = PANGO_ALIGN_RIGHT;
1528
else /* We have GIMP_TEXT_JUSTIFY_FILL */
1530
if (dir == GIMP_TEXT_DIRECTION_LTR)
1531
align = PANGO_ALIGN_LEFT;
1533
align = PANGO_ALIGN_RIGHT;
1538
indent = gimp_text_layer_get_indent (text_id);
1539
pango_layout_set_indent (layout, (int)(PANGO_SCALE * indent));
1542
line_spacing = gimp_text_layer_get_line_spacing (text_id);
1543
pango_layout_set_spacing (layout, (int)(PANGO_SCALE * line_spacing));
1545
/* Letter Spacing */
1546
letter_spacing = gimp_text_layer_get_letter_spacing (text_id);
1547
letter_spacing_at = pango_attr_letter_spacing_new ((int)(PANGO_SCALE * letter_spacing));
1548
pango_attr_list_insert (attr_list, letter_spacing_at);
1550
pango_layout_set_justify (layout, justify);
1551
pango_layout_set_alignment (layout, align);
1553
pango_layout_set_attributes (layout, attr_list);
1555
/* Use the pango markup of the text layer */
1557
if (markup != NULL && markup[0] != '\0')
1558
pango_layout_set_markup (layout, markup, -1);
1559
else /* If we can't find a markup, then it has just text */
1560
pango_layout_set_text (layout, text, -1);
1562
pango_cairo_show_layout (cr, layout);
1565
g_free (font_family);
1567
g_object_unref (layout);
1568
pango_font_description_free (font_description);
1569
g_object_unref (context);
1570
pango_attr_list_unref (attr_list);
1572
cairo_font_options_destroy (options);