Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageio/format/pdf.c
Go to the documentation of this file.
1/*
2 * This file is part of darktable,
3 * Copyright (C) 2015 Jérémy Rosen.
4 * Copyright (C) 2015-2017, 2019-2020 Tobias Ellinghaus.
5 * Copyright (C) 2016 Roman Lebedev.
6 * Copyright (C) 2019, 2022, 2025-2026 Aurélien PIERRE.
7 * Copyright (C) 2019 jakubfi.
8 * Copyright (C) 2019 Philippe Weyland.
9 * Copyright (C) 2020 Andreas Schneider.
10 * Copyright (C) 2020-2022 Diederik Ter Rahe.
11 * Copyright (C) 2020 Hubert Kowalski.
12 * Copyright (C) 2020-2021 Pascal Obry.
13 * Copyright (C) 2021 Ralf Brown.
14 * Copyright (C) 2022 Martin Bařinka.
15 * Copyright (C) 2024 Alynx Zhou.
16 *
17 * darktable is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * darktable is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with darktable. If not, see <http://www.gnu.org/licenses/>.
29 */
30
31#include "common/pdf.h"
32#include "common/conf.h"
33#include "widgets/bauhaus.h"
35#include "system/macros.h"
36#include "system/mem_alloc.h"
38#include <glib/gstdio.h>
44
45#include <strings.h>
47#include "widgets/label.h"
49
50DT_MODULE(1)
51
52// clang-format off
53
54// gui data
69
75
82
89
90static const struct
91{
92 char *name;
93 int bpp;
94} _pdf_bpp[] =
95{
96 { N_("8 bit"), 8 },
97 { N_("16 bit"), 16 },
98 { NULL, 0 }
99};
100
106
107// saved params -- just there to get the sizeof() without worrying about padding, ...
126
127// the real type used in the code
137
138// clang-format on
139
141{
142}
143
147
148static int _paper_size(dt_imageio_pdf_params_t *d, float *page_width, float *page_height, float *page_border)
149{
150 float width, height, border;
151
152 if(!dt_pdf_parse_paper_size(d->size, &width, &height))
153 {
154 fprintf(stderr, "[imageio_format_pdf] invalid paper size: `%s'!\n", d->size);
155 dt_control_log(_("invalid paper size"));
156 return 1;
157 }
158
159 if(!dt_pdf_parse_length(d->border, &border))
160 {
161 fprintf(stderr, "[imageio_format_pdf] invalid border size: `%s'! using 0\n", d->border);
162 dt_control_log(_("invalid border size, using 0"));
163// return 1;
164 border = 0.0;
165 }
166
167 if(d->orientation == ORIENTATION_LANDSCAPE)
168 {
169 float w = width, h = height;
170 width = MAX(w, h);
171 height = MIN(w, h);
172 }
173 else
174 {
175 float w = width, h = height;
176 width = MIN(w, h);
177 height = MAX(w, h);
178 }
179
180 *page_width = width;
181 *page_height = height;
182 *page_border = border;
183
184 return 0;
185}
186
187
188int write_image(dt_imageio_module_data_t *data, const char *filename, const void *in,
189 dt_colorspaces_color_profile_type_t over_type, const char *over_filename,
190 void *exif, int exif_len, int32_t imgid, int num, int total, struct dt_dev_pixelpipe_t *pipe,
191 const gboolean export_masks)
192{
194
195 // init the pdf. we start counting with 1.
196 if(num == 1)
197 {
198 float page_width, page_height, page_border;
199 float page_dpi = d->params.dpi;
200
201 if(_paper_size(&d->params, &page_width, &page_height, &page_border))
202 return 1;
203
204 unsigned int compression = d->params.compression;
205 compression = MIN(compression, DT_PDF_STREAM_ENCODER_FLATE);
206
207
208 dt_pdf_t *pdf = dt_pdf_start(filename, page_width, page_height, page_dpi, compression);
209 if(IS_NULL_PTR(pdf))
210 {
211 fprintf(stderr, "[imageio_format_pdf] could not export to file: `%s'!\n", filename);
212 dt_control_log(_("could not export to file `%s'!"), filename);
213 return 1;
214 }
215
216 // TODO: escape ')' and maybe also '('
217 pdf->title = *d->params.title ? d->params.title : NULL;
218
219 d->pdf = pdf;
220 d->actual_filename = g_strdup(filename);
221 d->page_border = page_border;
222 } // init the pdf
223
224 // add the icc profile
225 int icc_id = 0;
226 if(imgid > 0 && d->params.icc && d->params.mode == MODE_NORMAL)
227 {
228 // get the id of the profile
229 const dt_colorspaces_color_profile_t *profile = dt_colorspaces_get_output_profile(imgid, &over_type, over_filename);
230
231 // look it up in the list
232 for(GList *iter = d->icc_profiles; iter; iter = g_list_next(iter))
233 {
234 _pdf_icc_t *icc = (_pdf_icc_t *)iter->data;
235 if(icc->profile == profile)
236 {
237 icc_id = icc->icc_id;
238 break;
239 }
240 }
241 if(icc_id == 0)
242 {
243 uint32_t len = 0;
244 cmsSaveProfileToMem(profile->profile, 0, &len);
245 if(len > 0)
246 {
247 unsigned char *buf = malloc(sizeof(unsigned char) * len);
248 cmsSaveProfileToMem(profile->profile, buf, &len);
249 icc_id = dt_pdf_add_icc_from_data(d->pdf, buf, len);
250 dt_free(buf);
251 _pdf_icc_t *icc = (_pdf_icc_t *)malloc(sizeof(_pdf_icc_t));
252 icc->profile = profile;
253 icc->icc_id = icc_id;
254 d->icc_profiles = g_list_append(d->icc_profiles, icc);
255 }
256 }
257 }
258
259 void *image_data = NULL;
260
261 // TODO
262 // decide if we want to push that conversion step into the pdf lib and maybe do it on the fly while writing.
263 // that would get rid of one buffer in the case of ASCII_HEX
264 if(d->params.mode == MODE_NORMAL)
265 {
266 if(d->params.bpp == 8)
267 {
269 (size_t)3 * data->width * data->height,
270 0);
271 const uint8_t *in_ptr = (const uint8_t *)in;
272 uint8_t *out_ptr = (uint8_t *)image_data;
273 for(int y = 0; y < data->height; y++)
274 {
275 for(int x = 0; x < data->width; x++, in_ptr += 4, out_ptr += 3)
276 memcpy(out_ptr, in_ptr, 3);
277 }
278 }
279 else
280 {
282 sizeof(uint16_t) * 3 * data->width * data->height,
283 0);
284 const uint16_t *in_ptr = (const uint16_t *)in;
285 uint16_t *out_ptr = (uint16_t *)image_data;
286 for(int y = 0; y < data->height; y++)
287 {
288 for(int x = 0; x < data->width; x++, in_ptr += 4, out_ptr += 3)
289 {
290 for(int c = 0; c < 3; c++)
291 out_ptr[c] = (0xff00 & (in_ptr[c] << 8)) | (in_ptr[c] >> 8);
292 }
293 }
294 }
295 }
296
297 dt_pdf_image_t *image = dt_pdf_add_image(d->pdf, image_data, d->params.global.width, d->params.global.height, d->params.bpp, icc_id, d->page_border);
298
300
301 d->images = g_list_append(d->images, image);
302
303
304 // finish the pdf
305 if(num == total)
306 {
307 int n_images = g_list_length(d->images);
308 dt_pdf_page_t **pages = malloc(sizeof(dt_pdf_page_t *) * n_images);
309
310 gboolean outline_mode = d->params.mode != MODE_NORMAL;
311 gboolean show_bb = d->params.mode == MODE_DEBUG;
312
313 // add a page for every image
314 int i = 0;
315 for(const GList *iter = d->images; iter; iter = g_list_next(iter))
316 {
317 dt_pdf_image_t *page = (dt_pdf_image_t *)iter->data;
318 page->outline_mode = outline_mode;
319 page->show_bb = show_bb;
320 page->rotate_to_fit = d->params.rotate;
321 pages[i] = dt_pdf_add_page(d->pdf, &page, 1);
322 i++;
323 }
324
325 // add the contact sheet(s)
326 // TODO
327
328 dt_pdf_finish(d->pdf, pages, n_images);
329
330 // we allocated the images and pages. the main pdf object gets free'ed in dt_pdf_finish().
331 g_list_free_full(d->images, dt_free_gpointer);
332 d->images = NULL;
333 for(i = 0; i < n_images; i++) dt_free(pages[i]);
334 dt_free(pages);
335 dt_free(d->actual_filename);
336 g_list_free_full(d->icc_profiles, dt_free_gpointer);
337 d->icc_profiles = NULL;
338
339 d->pdf = NULL;
340 d->images = NULL;
341 d->actual_filename = NULL;
342 d->icc_profiles = NULL;
343 } // finish the pdf
344
345 return 0;
346}
347
349{
350 return ((dt_imageio_pdf_params_t *)p)->bpp;
351}
352
357
359{
360 return "application/pdf";
361}
362
364{
365 return "pdf";
366}
367
368const char *name()
369{
370 return _("PDF");
371}
372
377
378int dimension(struct dt_imageio_module_format_t *self, dt_imageio_module_data_t *data, uint32_t *width, uint32_t *height)
379{
380 if(data)
381 {
383
384 float page_width, page_height, page_border;
385 float page_dpi = d->params.dpi;
386
387 if(_paper_size(&d->params, &page_width, &page_height, &page_border))
388 return 1;
389
390 *width = dt_pdf_point_to_pixel(page_width - 2 * page_border, page_dpi) + 0.5;
391 *height = dt_pdf_point_to_pixel(page_height - 2 * page_border, page_dpi) + 0.5;
392
393 if(d->params.rotate)
394 *width = *height = MAX(*width, *height);
395 }
396 return 0;
397}
398
399static void size_toggle_callback(GtkWidget *widget, gpointer user_data);
400
401// set the paper size dropdown from the UNTRANSLATED string
402static void _set_paper_size(dt_imageio_module_format_t *self, const char *text)
403{
404 pdf_t *d = (pdf_t *)self->gui_data;
405
406 if(IS_NULL_PTR(text) || *text == '\0')
407 {
409 return;
410 }
411
412 g_signal_handlers_block_by_func(d->size, size_toggle_callback, self);
413
414 int pos = 0;
415 for(; pos < dt_bauhaus_combobox_length(d->size); pos++)
416 {
417 if((pos < dt_pdf_paper_sizes_n && !strcasecmp(text, dt_pdf_paper_sizes[pos].name))
418 || !strcasecmp(text, dt_bauhaus_combobox_get_entry(d->size, pos)))
419 break;
420 }
421
422 if(pos < dt_bauhaus_combobox_length(d->size))
423 {
424 // we jumped out of the loop -> found it
425 dt_bauhaus_combobox_set(d->size, pos);
426 dt_conf_set_string("plugins/imageio/format/pdf/size", text);
427 }
428 else
429 {
430 // newly seen -- check if it is valid
431 float width, height;
433 {
434 // seems to be ok
435 dt_bauhaus_combobox_add(d->size, text);
436 dt_bauhaus_combobox_set(d->size, pos);
437 dt_conf_set_string("plugins/imageio/format/pdf/size", text);
438 }
439 else
440 {
441 dt_control_log(_("invalid paper size"));
442 gchar *old_size = dt_conf_get_string("plugins/imageio/format/pdf/size");
443 if(old_size)
444 {
445 // safeguard against strange stuff in config
446 if(dt_pdf_parse_paper_size(old_size, &width, &height))
447 _set_paper_size(self, old_size);
448 else
450
451 dt_free(old_size);
452 }
453 }
454 }
455
456 g_signal_handlers_unblock_by_func(d->size, size_toggle_callback, self);
457
458}
459
460static void title_changed_callback(GtkWidget *widget, gpointer user_data)
461{
462 dt_conf_set_string("plugins/imageio/format/pdf/title", gtk_entry_get_text(GTK_ENTRY(widget)));
463}
464
465static void border_changed_callback(GtkWidget *widget, gpointer user_data)
466{
467 dt_conf_set_string("plugins/imageio/format/pdf/border", gtk_entry_get_text(GTK_ENTRY(widget)));
468}
469
470static void size_toggle_callback(GtkWidget *widget, gpointer user_data)
471{
472 unsigned int pos = dt_bauhaus_combobox_get(widget);
473 if(pos < dt_pdf_paper_sizes_n)
474 _set_paper_size(user_data, dt_pdf_paper_sizes[pos].name); // has to be untranslated
475 else
477}
478
479static void orientation_toggle_callback(GtkWidget *widget, gpointer user_data)
480{
481 dt_conf_set_int("plugins/imageio/format/pdf/orientation", dt_bauhaus_combobox_get(widget));
482}
483
484static void dpi_changed_callback(GtkWidget *widget, gpointer user_data)
485{
486 dt_conf_set_float("plugins/imageio/format/pdf/dpi", gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)));
487}
488
489static void rotate_toggle_callback(GtkWidget *widget, gpointer user_data)
490{
491 dt_conf_set_bool("plugins/imageio/format/pdf/rotate", dt_bauhaus_combobox_get(widget) == 1);
492}
493
494static void pages_toggle_callback(GtkWidget *widget, gpointer user_data)
495{
496 dt_conf_set_int("plugins/imageio/format/pdf/pages", dt_bauhaus_combobox_get(widget));
497}
498
499static void icc_toggle_callback(GtkWidget *widget, gpointer user_data)
500{
501 dt_conf_set_bool("plugins/imageio/format/pdf/icc", dt_bauhaus_combobox_get(widget) == 1);
502}
503
504static void mode_toggle_callback(GtkWidget *widget, gpointer user_data)
505{
506 dt_conf_set_int("plugins/imageio/format/pdf/mode", dt_bauhaus_combobox_get(widget));
507}
508
509static void bpp_toggle_callback(GtkWidget *widget, gpointer user_data)
510{
511 const int sel = dt_bauhaus_combobox_get(widget);
512 // we don't allow typing in that dropdown so -1 shouldn't happen, but coverity doesn't know that
513 if(sel >= 0)
514 dt_conf_set_int("plugins/imageio/format/pdf/bpp", _pdf_bpp[sel].bpp);
515}
516
517static void compression_toggle_callback(GtkWidget *widget, gpointer user_data)
518{
519 dt_conf_set_int("plugins/imageio/format/pdf/compression", dt_bauhaus_combobox_get(widget));
520}
521
523{
524 pdf_t *d = calloc(1, sizeof(pdf_t));
525 self->gui_data = (void *)d;
526 self->widget = gtk_grid_new();
527 GtkGrid *grid = GTK_GRID(self->widget);
528 gtk_grid_set_row_spacing(grid, DT_GUI_BOX_SPACING);
529 gtk_grid_set_column_spacing(grid, DT_GUI_BOX_SPACING);
530
531 int line = 0;
532
533 // title
534
535 gtk_grid_attach(grid, dt_ui_label_new(_("title")), 0, ++line, 1, 1);
536
537 d->title = GTK_ENTRY(gtk_entry_new());
538 dt_accels_disconnect_on_text_input(GTK_WIDGET(d->title));
539 gtk_entry_set_placeholder_text(d->title, "untitled");
540 gtk_entry_set_width_chars(d->title, 5);
541 gtk_widget_set_hexpand(GTK_WIDGET(d->title), TRUE);
542 gtk_grid_attach(grid, GTK_WIDGET(d->title), 1, line, 1, 1);
543 gtk_widget_set_tooltip_text(GTK_WIDGET(d->title), _("enter the title of the pdf"));
544 const char *str = dt_conf_get_string_const("plugins/imageio/format/pdf/title");
545 if(str)
546 {
547 gtk_entry_set_text(GTK_ENTRY(d->title), str);
548 }
549 g_signal_connect(G_OBJECT(d->title), "changed", G_CALLBACK(title_changed_callback), self);
550
551 // paper size
552
555 dt_bauhaus_widget_set_label(d->size, N_("paper size"));
556 for(int i = 0; dt_pdf_paper_sizes[i].name; i++)
558 gtk_grid_attach(grid, GTK_WIDGET(d->size), 0, ++line, 2, 1);
559 g_signal_connect(G_OBJECT(d->size), "value-changed", G_CALLBACK(size_toggle_callback), self);
560 gtk_widget_set_tooltip_text(d->size, _("paper size of the pdf\neither one from the list or "
561 "\"<width> [unit] x <height> <unit>\n"
562 "example: 210 mm x 2.97 cm"));
563 gchar *size_str = dt_conf_get_string("plugins/imageio/format/pdf/size");
564 _set_paper_size(self, size_str);
565 dt_free(size_str);
566
567 // orientation
568
570 dt_bauhaus_widget_set_label(d->orientation, N_("page orientation"));
571 dt_bauhaus_combobox_add(d->orientation, _("portrait"));
572 dt_bauhaus_combobox_add(d->orientation, _("landscape"));
573 gtk_grid_attach(grid, GTK_WIDGET(d->orientation), 0, ++line, 2, 1);
574 g_signal_connect(G_OBJECT(d->orientation), "value-changed", G_CALLBACK(orientation_toggle_callback), self);
575 gtk_widget_set_tooltip_text(d->orientation, _("paper orientation of the pdf"));
576 dt_bauhaus_combobox_set(d->orientation, dt_conf_get_int("plugins/imageio/format/pdf/orientation"));
577
578 // border
579
580 gtk_grid_attach(grid, dt_ui_label_new(_("border")), 0, ++line, 1, 1);
581
582 d->border = GTK_ENTRY(gtk_entry_new());
583 dt_accels_disconnect_on_text_input(GTK_WIDGET(d->border));
584 gtk_entry_set_width_chars(d->border, 5);
585 gtk_entry_set_max_length(d->border, sizeof(((dt_imageio_pdf_params_t *)NULL)->border) - 1);
586 gtk_entry_set_placeholder_text(d->border, "0 mm");
587 gtk_grid_attach(grid, GTK_WIDGET(d->border), 1, line, 1, 1);
588 gtk_widget_set_tooltip_text(GTK_WIDGET(d->border), _("empty space around the pdf\n"
589 "format: size + unit\nexamples: 10 mm, 1 inch"));
590 str = dt_conf_get_string_const("plugins/imageio/format/pdf/border");
591 if(str)
592 {
593 gtk_entry_set_text(GTK_ENTRY(d->border), str);
594 }
595 g_signal_connect(G_OBJECT(d->border), "changed", G_CALLBACK(border_changed_callback), self);
596
597 // dpi
598
599 gtk_grid_attach(grid, dt_ui_label_new(_("dpi")), 0, ++line, 1, 1);
600
601 d->dpi = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1, 5000, 1));
602 gtk_grid_attach(grid, GTK_WIDGET(d->dpi), 1, line, 1, 1);
603 gtk_widget_set_tooltip_text(GTK_WIDGET(d->dpi), _("dpi of the images inside the pdf"));
604 gtk_spin_button_set_value(d->dpi, dt_conf_get_float("plugins/imageio/format/pdf/dpi"));
605 g_signal_connect(G_OBJECT(d->dpi), "value-changed", G_CALLBACK(dpi_changed_callback), self);
606
607 // rotate images yes|no
608
610 dt_bauhaus_widget_set_label(d->rotate, N_("rotate images"));
611 dt_bauhaus_combobox_add(d->rotate, _("no"));
612 dt_bauhaus_combobox_add(d->rotate, _("yes"));
613 gtk_grid_attach(grid, GTK_WIDGET(d->rotate), 0, ++line, 2, 1);
614 g_signal_connect(G_OBJECT(d->rotate), "value-changed", G_CALLBACK(rotate_toggle_callback), self);
615 gtk_widget_set_tooltip_text(d->rotate, _("images can be rotated to match the pdf orientation "
616 "to waste less space when printing"));
617 dt_bauhaus_combobox_set(d->rotate, dt_conf_get_bool("plugins/imageio/format/pdf/rotate"));
618
619 // pages all|single images|contact sheet
620
622 dt_bauhaus_widget_set_label(d->pages, N_("TODO: pages"));
623 dt_bauhaus_combobox_add(d->pages, _("all"));
624 dt_bauhaus_combobox_add(d->pages, _("single images"));
625 dt_bauhaus_combobox_add(d->pages, _("contact sheet"));
626// gtk_grid_attach(grid, GTK_WIDGET(d->pages), 0, ++line, 2, 1);
627// g_signal_connect(G_OBJECT(d->pages), "value-changed", G_CALLBACK(pages_toggle_callback), self);
628 gtk_widget_set_tooltip_text(d->pages, _("what pages should be added to the pdf"));
629 dt_bauhaus_combobox_set(d->pages, dt_conf_get_int("plugins/imageio/format/pdf/pages"));
630 gtk_widget_set_sensitive(d->pages, FALSE); // TODO
631
632 // embedded icc profile yes|no
633
635 dt_bauhaus_widget_set_label(d->icc, N_("embed icc profiles"));
636 dt_bauhaus_combobox_add(d->icc, _("no"));
637 dt_bauhaus_combobox_add(d->icc, _("yes"));
638 gtk_grid_attach(grid, GTK_WIDGET(d->icc), 0, ++line, 2, 1);
639 g_signal_connect(G_OBJECT(d->icc), "value-changed", G_CALLBACK(icc_toggle_callback), self);
640 gtk_widget_set_tooltip_text(d->icc, _("images can be tagged with their icc profile"));
641 dt_bauhaus_combobox_set(d->icc, dt_conf_get_bool("plugins/imageio/format/pdf/icc"));
642
643 // bpp
644
646 dt_bauhaus_widget_set_label(d->bpp, N_("bit depth"));
647 int sel = 0;
648 int bpp = dt_conf_get_int("plugins/imageio/format/pdf/bpp");
649 for(int i = 0; _pdf_bpp[i].name; i++)
650 {
652 if(_pdf_bpp[i].bpp == bpp) sel = i;
653 }
654 gtk_grid_attach(grid, GTK_WIDGET(d->bpp), 0, ++line, 2, 1);
655 g_signal_connect(G_OBJECT(d->bpp), "value-changed", G_CALLBACK(bpp_toggle_callback), self);
656 gtk_widget_set_tooltip_text(d->bpp, _("bits per channel of the embedded images"));
657 dt_bauhaus_combobox_set(d->bpp, sel);
658
659 // compression
660
662 dt_bauhaus_widget_set_label(d->compression, N_("compression"));
663 dt_bauhaus_combobox_add(d->compression, _("uncompressed"));
664 dt_bauhaus_combobox_add(d->compression, _("deflate"));
665 gtk_grid_attach(grid, GTK_WIDGET(d->compression), 0, ++line, 2, 1);
666 g_signal_connect(G_OBJECT(d->compression), "value-changed", G_CALLBACK(compression_toggle_callback), self);
667 gtk_widget_set_tooltip_text(d->compression, _("method used for image compression\n"
668 "uncompressed -- fast but big files\n"
669 "deflate -- smaller files but slower"));
670 dt_bauhaus_combobox_set(d->compression, dt_conf_get_int("plugins/imageio/format/pdf/compression"));
671
672 // image mode normal|draft|debug
673
675 dt_bauhaus_widget_set_label(d->mode, N_("image mode"));
676 dt_bauhaus_combobox_add(d->mode, _("normal"));
677 dt_bauhaus_combobox_add(d->mode, _("draft"));
678 dt_bauhaus_combobox_add(d->mode, _("debug"));
679 gtk_grid_attach(grid, GTK_WIDGET(d->mode), 0, ++line, 2, 1);
680 g_signal_connect(G_OBJECT(d->mode), "value-changed", G_CALLBACK(mode_toggle_callback), self);
681 gtk_widget_set_tooltip_text(d->mode, _("normal -- just put the images into the pdf\n"
682 "draft -- images are replaced with boxes\n"
683 "debug -- only show the outlines and bounding boxen"));
684 dt_bauhaus_combobox_set(d->mode, dt_conf_get_int("plugins/imageio/format/pdf/mode"));
685}
686
688{
689 dt_free(self->gui_data);
690}
691
693{
694 pdf_t *d = (pdf_t *)self->gui_data;
695
696 dpi_changed_callback(GTK_WIDGET(d->dpi), self);
697 icc_toggle_callback(GTK_WIDGET(d->icc), self);
698 mode_toggle_callback(GTK_WIDGET(d->mode), self);
699 orientation_toggle_callback(GTK_WIDGET(d->orientation), self);
700 pages_toggle_callback(GTK_WIDGET(d->pages), self);
701 rotate_toggle_callback(GTK_WIDGET(d->rotate), self);
702 size_toggle_callback(GTK_WIDGET(d->size), self);
703 title_changed_callback(GTK_WIDGET(d->title), self);
704 bpp_toggle_callback(GTK_WIDGET(d->bpp), self);
705 compression_toggle_callback(GTK_WIDGET(d->compression), self);
706}
707
709{
710 return sizeof(dt_imageio_pdf_params_t);
711}
712
714{
715 dt_imageio_pdf_t *d = (dt_imageio_pdf_t *)calloc(1, sizeof(dt_imageio_pdf_t));
716
717 if(d)
718 {
719 const char *text = dt_conf_get_string_const("plugins/imageio/format/pdf/title");
720 g_strlcpy(d->params.title, text, sizeof(d->params.title));
721
722 text = dt_conf_get_string_const("plugins/imageio/format/pdf/border");
723 g_strlcpy(d->params.border, text, sizeof(d->params.border));
724
725 text = dt_conf_get_string_const("plugins/imageio/format/pdf/size");
726 g_strlcpy(d->params.size, text, sizeof(d->params.size));
727
728 d->params.bpp = dt_conf_get_int("plugins/imageio/format/pdf/bpp");
729 d->params.compression = dt_conf_get_int("plugins/imageio/format/pdf/compression");
730 d->params.dpi = dt_conf_get_float("plugins/imageio/format/pdf/dpi");
731 d->params.icc = dt_conf_get_bool("plugins/imageio/format/pdf/icc");
732 d->params.mode = dt_conf_get_int("plugins/imageio/format/pdf/mode");
733 d->params.orientation = dt_conf_get_int("plugins/imageio/format/pdf/orientation");
734 d->params.pages = dt_conf_get_int("plugins/imageio/format/pdf/pages");
735 d->params.rotate = dt_conf_get_bool("plugins/imageio/format/pdf/rotate");
736 }
737
738 return d;
739}
740
741// in normal operations we free these after exporting the last image, but when an export
742// gets cancelled that last image doesn't get exported, so we have to take care of it here.
744{
746
747 if(d->pdf)
748 dt_pdf_finish(d->pdf, NULL, 0);
749
750 g_list_free_full(d->images, dt_free_gpointer);
751 d->images = NULL;
752
753 if(d->actual_filename)
754 {
755 g_unlink(d->actual_filename); // no need to leave broken files on disk
756 dt_free(d->actual_filename);
757 }
758
759 g_list_free_full(d->icc_profiles, dt_free_gpointer);
760 d->icc_profiles = NULL;
761
762 d->pdf = NULL;
763 d->images = NULL;
764 d->actual_filename = NULL;
765 d->icc_profiles = NULL;
766
767 dt_free(params);
768}
769
770int set_params(dt_imageio_module_format_t *self, const void *params, const int size)
771{
772 if(size != self->params_size(self)) return 1;
773 const dt_imageio_pdf_t *d = (dt_imageio_pdf_t *)params;
774 pdf_t *g = (pdf_t *)self->gui_data;
775
776 for(int i = 0; _pdf_bpp[i].name; i++)
777 {
778 if(_pdf_bpp[i].bpp == d->params.bpp)
780 }
781
782 gtk_entry_set_text(g->title, d->params.title);
783 gtk_entry_set_text(g->border, d->params.border);
784 dt_bauhaus_combobox_set(g->compression, d->params.compression);
785 gtk_spin_button_set_value(g->dpi, d->params.dpi);
786 dt_bauhaus_combobox_set(g->icc, d->params.icc);
787 dt_bauhaus_combobox_set(g->mode, d->params.mode);
788 dt_bauhaus_combobox_set(g->orientation, d->params.orientation);
789 dt_bauhaus_combobox_set(g->pages, d->params.pages);
790 dt_bauhaus_combobox_set(g->rotate, d->params.rotate);
791 _set_paper_size(self, d->params.size);
792
793 dt_conf_set_string("plugins/imageio/format/pdf/title", d->params.title);
794 dt_conf_set_string("plugins/imageio/format/pdf/border", d->params.border);
795 dt_conf_set_int("plugins/imageio/format/pdf/bpp", d->params.bpp);
796 dt_conf_set_int("plugins/imageio/format/pdf/compression", d->params.compression);
797 dt_conf_set_float("plugins/imageio/format/pdf/dpi", d->params.dpi);
798 dt_conf_set_bool("plugins/imageio/format/pdf/icc", d->params.icc);
799 dt_conf_set_int("plugins/imageio/format/pdf/mode", d->params.mode);
800 dt_conf_set_int("plugins/imageio/format/pdf/orientation", d->params.orientation);
801 dt_conf_set_int("plugins/imageio/format/pdf/pages", d->params.pages);
802 dt_conf_set_bool("plugins/imageio/format/pdf/rotate", d->params.rotate);
803
804 return 0;
805}
806
807// clang-format off
808// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
809// vim: shiftwidth=2 expandtab tabstop=2 cindent
810// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
811// clang-format on
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnect accels while a text or search entry has the focus, and reconnect them when it loses it....
Handle default and user-set shortcuts (accelerators)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_combobox_set_editable(GtkWidget *widget, int editable)
Definition bauhaus.c:1882
const char * dt_bauhaus_combobox_get_entry(GtkWidget *widget, int pos)
Definition bauhaus.c:2007
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
int dt_bauhaus_combobox_length(GtkWidget *widget)
Definition bauhaus.c:1963
const char * dt_bauhaus_combobox_get_text(GtkWidget *widget)
Definition bauhaus.c:1970
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
static const float x
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
dt_pdf_page_t * dt_pdf_add_page(dt_pdf_t *pdf, dt_pdf_image_t **images, int n_images)
Definition common/pdf.c:457
dt_pdf_image_t * dt_pdf_add_image(dt_pdf_t *pdf, const unsigned char *image, int width, int height, int bpp, int icc_id, float border)
Definition common/pdf.c:375
int dt_pdf_parse_length(const char *str, float *length)
Definition common/pdf.c:69
int dt_pdf_add_icc_from_data(dt_pdf_t *pdf, const unsigned char *data, size_t size)
Definition common/pdf.c:331
int dt_pdf_parse_paper_size(const char *str, float *width, float *height)
Definition common/pdf.c:117
void dt_pdf_finish(dt_pdf_t *pdf, dt_pdf_page_t **pages, int n_pages)
Definition common/pdf.c:639
dt_pdf_t * dt_pdf_start(const char *filename, float width, float height, float dpi, dt_pdf_stream_encoder_t default_encoder)
Definition common/pdf.c:217
void dt_control_log(const char *msg,...)
Definition control.c:824
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
GHashTable * pages
row label -> path of the illustration, relative to the destination
#define DT_GUI_MODULE(x)
int bpp
const char * mime(dt_imageio_module_data_t *data)
size_t params_size(dt_imageio_module_format_t *self)
static void border_changed_callback(GtkWidget *widget, gpointer user_data)
static void _set_paper_size(dt_imageio_module_format_t *self, const char *text)
static void rotate_toggle_callback(GtkWidget *widget, gpointer user_data)
static void dpi_changed_callback(GtkWidget *widget, gpointer user_data)
int dimension(struct dt_imageio_module_format_t *self, dt_imageio_module_data_t *data, uint32_t *width, uint32_t *height)
void gui_reset(dt_imageio_module_format_t *self)
void gui_init(dt_imageio_module_format_t *self)
static void bpp_toggle_callback(GtkWidget *widget, gpointer user_data)
const char * extension(dt_imageio_module_data_t *data)
int set_params(dt_imageio_module_format_t *self, const void *params, const int size)
char * name
void cleanup(dt_imageio_module_format_t *self)
int levels(dt_imageio_module_data_t *p)
@ MODE_DRAFT
@ MODE_DEBUG
@ MODE_NORMAL
void free_params(dt_imageio_module_format_t *self, dt_imageio_module_data_t *params)
static void title_changed_callback(GtkWidget *widget, gpointer user_data)
static void pages_toggle_callback(GtkWidget *widget, gpointer user_data)
static void icc_toggle_callback(GtkWidget *widget, gpointer user_data)
_pdf_orientation_t
@ ORIENTATION_PORTRAIT
@ ORIENTATION_LANDSCAPE
static void mode_toggle_callback(GtkWidget *widget, gpointer user_data)
void init(dt_imageio_module_format_t *self)
void * get_params(dt_imageio_module_format_t *self)
@ PAGES_ALL
@ PAGES_SINGLE
@ PAGES_CONTACT
static int _paper_size(dt_imageio_pdf_params_t *d, float *page_width, float *page_height, float *page_border)
static const struct @39 _pdf_bpp[]
static void compression_toggle_callback(GtkWidget *widget, gpointer user_data)
void gui_cleanup(dt_imageio_module_format_t *self)
static void orientation_toggle_callback(GtkWidget *widget, gpointer user_data)
int write_image(dt_imageio_module_data_t *data, const char *filename, const void *in, dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len, int32_t imgid, int num, int total, struct dt_dev_pixelpipe_t *pipe, const gboolean export_masks)
static void size_toggle_callback(GtkWidget *widget, gpointer user_data)
@ IMAGEIO_INT16
@ IMAGEIO_RGB
@ IMAGEIO_INT8
@ FORMAT_FLAGS_NO_TMPFILE
const dt_colorspaces_color_profile_t * dt_colorspaces_get_output_profile(const int32_t imgid, dt_colorspaces_color_profile_type_t *over_type, const char *over_filename)
GtkWidget * dt_ui_label_new(const gchar *str)
Definition label.c:125
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
#define dt_pdf_point_to_pixel(pt, dpi)
Definition pdf.h:44
static const int dt_pdf_paper_sizes_n
Definition pdf.h:119
static const struct @6 dt_pdf_paper_sizes[]
dt_pdf_stream_encoder_t
Definition pdf.h:48
@ DT_PDF_STREAM_ENCODER_FLATE
Definition pdf.h:50
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
dt_colorspaces_color_profile_type_t
const dt_colorspaces_color_profile_t * profile
One registered profile: its identity, its LCMS handle, and where it sits in each combo box.
cmsHPROFILE profile
the actual profile; NULL for the three category entries
GModule *GtkWidget * widget
dt_imageio_module_data_t global
dt_pdf_stream_encoder_t compression
_pdf_orientation_t orientation
dt_imageio_pdf_params_t params
gboolean rotate_to_fit
Definition pdf.h:76
gboolean show_bb
Definition pdf.h:79
gboolean outline_mode
Definition pdf.h:78
Definition pdf.h:54
char * title
Definition pdf.h:62
GtkWidget * rotate
GtkSpinButton * dpi
GtkWidget * icc
GtkWidget * size
GtkWidget * pages
GtkWidget * compression
GtkWidget * orientation
GtkWidget * mode
GtkWidget * bpp
GtkEntry * title
GtkEntry * border
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
Telling the user something happened.
#define DT_GUI_BOX_SPACING