Statistics
| Revision:

root / src / printing.c @ 3255

History | View | Annotate | Download (15.5 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2013 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19

    
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#include "defs.h"
25
#include "printing.h"
26

    
27
#include <glib.h>
28
#include <glib/gi18n.h>
29
#if GTK_CHECK_VERSION(2, 10, 0)
30
#include <gtk/gtkprintoperation.h>
31
#endif
32

    
33
#include <stdio.h>
34

    
35
#include "mainwindow.h"
36
#include "procmsg.h"
37
#include "procheader.h"
38
#include "prefs_common.h"
39
#include "alertpanel.h"
40
#include "utils.h"
41

    
42
#if GTK_CHECK_VERSION(2, 10, 0)
43

    
44
#define SPACING        2.0
45

    
46
typedef struct
47
{
48
        MsgInfo *msginfo;
49
        gint n_pages;
50
        gchar *hdr_data;
51
        gchar *msg_text_file;
52
        FILE *fp;
53
} MsgPrintInfo;
54

    
55
typedef struct
56
{
57
        gint page_nr_per_msg;
58
        MsgPrintInfo *mpinfo;
59
        glong pos;
60
        gint lines;
61
} PageInfo;
62

    
63
typedef struct
64
{
65
        GSList *mlist;
66
        gint n_msgs;
67
        MsgPrintInfo *msgs;
68
        GPtrArray *pages;
69
        gint n_pages;
70

    
71
        MimeInfo *partinfo;
72

    
73
        gdouble line_h;
74
        gint lines_per_page;
75

    
76
        gboolean all_headers;
77
} PrintData;
78

    
79
static GtkPrintSettings *settings = NULL;
80
static GtkPageSetup *page_setup = NULL;
81

    
82
static gint get_header_data(MsgPrintInfo *mpinfo, PrintData *print_data)
83
{
84
        MsgInfo *msginfo;
85
        FILE *fp;
86
        GPtrArray *headers;
87
        GString *str;
88
        gint i;
89

    
90
        msginfo = mpinfo->msginfo;
91

    
92
        if ((fp = procmsg_open_message(msginfo)) == NULL)
93
                return -1;
94

    
95
        if (print_data->all_headers)
96
                headers = procheader_get_header_array_asis(fp, NULL);
97
        else
98
                headers = procheader_get_header_array_for_display(fp, NULL);
99

    
100
        fclose(fp);
101

    
102
        str = g_string_new(NULL);
103

    
104
        for (i = 0; i < headers->len; i++) {
105
                Header *hdr;
106
                gchar *text;
107
                const gchar *body;
108

    
109
                hdr = g_ptr_array_index(headers, i);
110

    
111
                if (!g_ascii_strcasecmp(hdr->name, "Subject"))
112
                        body = msginfo->subject;
113
                else if (!g_ascii_strcasecmp(hdr->name, "From"))
114
                        body = msginfo->from;
115
                else if (!g_ascii_strcasecmp(hdr->name, "To"))
116
                        body = msginfo->to;
117
                else if (!g_ascii_strcasecmp(hdr->name, "Cc")) {
118
                        unfold_line(hdr->body);
119
                        body = hdr->body;
120
                        while (g_ascii_isspace(*body))
121
                                body++;
122
                } else {
123
                        body = hdr->body;
124
                        while (g_ascii_isspace(*body))
125
                                body++;
126
                }
127

    
128
                if (body && *body != '\0')
129
                        text = g_markup_printf_escaped("<b>%s:</b> %s\n",
130
                                                       hdr->name, body);
131
                else
132
                        text = g_markup_printf_escaped("<b>%s:</b> (none)\n",
133
                                                       hdr->name);
134
                g_string_append(str, text);
135
                g_free(text);
136
        }
137

    
138
        mpinfo->hdr_data = g_string_free(str, FALSE);
139
        procheader_header_array_destroy(headers);
140

    
141
        return 0;
142
}
143

    
144
static gint layout_set_headers(PangoLayout *layout, MsgPrintInfo *mpinfo,
145
                               PrintData *print_data)
146
{
147
        PangoFontDescription *desc;
148
        gint size;
149

    
150
        g_return_val_if_fail(mpinfo->hdr_data != NULL, -1);
151

    
152
        desc = pango_font_description_from_string(prefs_common_get()->textfont);
153
        size = pango_font_description_get_size(desc);
154
        pango_font_description_free(desc);
155
        desc = gtkut_get_default_font_desc();
156
        pango_font_description_set_size(desc, size);
157
        pango_layout_set_font_description(layout, desc);
158
        pango_font_description_free(desc);
159

    
160
        pango_layout_set_markup(layout, mpinfo->hdr_data, -1);
161

    
162
        return 0;
163
}
164

    
165
static gint message_count_page(MsgPrintInfo *mpinfo, GtkPrintContext *context,
166
                               PrintData *print_data)
167
{
168
        cairo_t *cr;
169
        gdouble width, height, line_h, hdr_h = 0.0, body_h;
170
        PangoLayout *layout;
171
        PangoFontDescription *desc;
172
        gint layout_h;
173
        gint lines_per_page, lines_left;
174
        gint n_pages = 1;
175
        PageInfo *pinfo;
176
        gint i;
177
        FILE *fp = NULL;
178
        gchar buf[BUFFSIZE];
179
        glong pos = 0;
180

    
181
        cr = gtk_print_context_get_cairo_context(context);
182
        width = gtk_print_context_get_width(context);
183
        height = gtk_print_context_get_height(context);
184

    
185
        layout = gtk_print_context_create_pango_layout(context);
186
        pango_layout_set_width(layout, width * PANGO_SCALE);
187
        pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
188
        pango_layout_set_spacing(layout, SPACING * PANGO_SCALE);
189

    
190
        if (!print_data->partinfo) {
191
                if (get_header_data(mpinfo, print_data) < 0) {
192
                        g_object_unref(layout);
193
                        return 0;
194
                }
195
                layout_set_headers(layout, mpinfo, print_data);
196

    
197
                pango_layout_get_size(layout, NULL, &layout_h);
198
                hdr_h = (gdouble)layout_h / PANGO_SCALE;
199
        }
200

    
201
        pango_layout_set_attributes(layout, NULL);
202
        desc = pango_font_description_from_string(prefs_common_get()->textfont);
203
        pango_layout_set_font_description(layout, desc);
204
        pango_font_description_free(desc);
205

    
206
        pango_layout_set_text(layout, "Test", -1);
207
        pango_layout_get_size(layout, NULL, &layout_h);
208
        if (layout_h <= 0) {
209
                g_warning("invalid layout_h (%d) ! falling back to default height (%d)\n", layout_h, 12 * PANGO_SCALE);
210
                layout_h = 12 * PANGO_SCALE;
211
        }
212
        line_h = (gdouble)layout_h / PANGO_SCALE + SPACING;
213
        print_data->line_h = line_h;
214
        lines_per_page = (height - line_h) / line_h;
215
        print_data->lines_per_page = lines_per_page;
216
        body_h = height - hdr_h - line_h;
217
        if (body_h < 0)
218
                body_h = 0.0;
219
        lines_left = body_h / line_h;
220

    
221
        debug_print("width = %g, height = %g\n", width, height);
222
        debug_print("dpi_x = %g, dpi_y = %g\n", gtk_print_context_get_dpi_x(context), gtk_print_context_get_dpi_y(context));
223
        debug_print("layout_h = %d, line_h = %g, lines_per_page = %d\n", layout_h, line_h, lines_per_page);
224
        debug_print("hdr_h = %g, body_h = %g, lines_left = %d\n", hdr_h, body_h, lines_left);
225

    
226
        if (print_data->partinfo) {
227
                FILE *msgfp;
228

    
229
                if ((msgfp = procmsg_open_message(mpinfo->msginfo)) == NULL)
230
                        return -1;
231
                fp = procmime_get_text_content(print_data->partinfo, msgfp,
232
                                               NULL);
233
                fclose(msgfp);
234
        } else {
235
                mpinfo->msg_text_file = get_tmp_file();
236
                if (procmsg_save_message_as_text(mpinfo->msginfo, mpinfo->msg_text_file, NULL, print_data->all_headers) == 0) {
237
                        if ((fp = g_fopen(mpinfo->msg_text_file, "rb")) != NULL) {
238
                                while (fgets(buf, sizeof(buf), fp) != NULL)
239
                                        if (buf[0] == '\r' || buf[0] == '\n')
240
                                                break;
241
                        }
242
                }
243
        }
244
        if (!fp) {
245
                g_warning("Can't get text part\n");
246
                return -1;
247
        }
248

    
249
        i = 0;
250
        pos = ftell(fp);
251
        pinfo = g_new(PageInfo, 1);
252
        pinfo->page_nr_per_msg = 0;
253
        pinfo->mpinfo = mpinfo;
254
        pinfo->pos = pos;
255
        pinfo->lines = lines_left;
256
        g_ptr_array_add(print_data->pages, pinfo);
257

    
258
        while (fgets(buf, sizeof(buf), fp) != NULL) {
259
                gint lines;
260
                gint line_offset = 0;
261

    
262
                strretchomp(buf);
263
                pango_layout_set_text(layout, buf, -1);
264
                lines = pango_layout_get_line_count(layout);
265

    
266
                while (lines_left < lines) {
267
                        PangoLayoutLine *line;
268

    
269
                        debug_print("page increment: %d: lines_left = %d, lines = %d\n", i, lines_left, lines);
270
                        line_offset += lines_left;
271
                        line = pango_layout_get_line(layout, line_offset);
272
                        lines -= lines_left;
273
                        lines_left = lines_per_page;
274

    
275
                        pinfo = g_new(PageInfo, 1);
276
                        pinfo->page_nr_per_msg = ++i;
277
                        pinfo->mpinfo = mpinfo;
278
                        pinfo->pos = pos + line->start_index;
279
                        pinfo->lines = lines_left;
280
                        g_ptr_array_add(print_data->pages, pinfo);
281
                }
282

    
283
                lines_left -= lines;
284
                pos = ftell(fp);
285
        }
286

    
287
        rewind(fp);
288
        mpinfo->fp = fp;
289

    
290
        g_object_unref(layout);
291

    
292
        n_pages = i + 1;
293
        debug_print("n_pages = %d\n", n_pages);
294

    
295
        return n_pages;
296
}
297

    
298
static void begin_print(GtkPrintOperation *operation, GtkPrintContext *context,
299
                        gpointer data)
300
{
301
        PrintData *print_data = data;
302
        gint n_pages = 0;
303
        gint i;
304

    
305
        debug_print("begin_print\n");
306

    
307
        for (i = 0; i < print_data->n_msgs; i++) {
308
                n_pages += message_count_page(&print_data->msgs[i], context,
309
                                              print_data);
310
        }
311

    
312
        print_data->n_pages = n_pages;
313
        gtk_print_operation_set_n_pages(operation, n_pages);
314
}
315

    
316
static void draw_page(GtkPrintOperation *operation, GtkPrintContext *context,
317
                      gint page_nr, gpointer data)
318
{
319
        PrintData *print_data = data;
320
        PageInfo *pinfo;
321
        MsgPrintInfo *mpinfo;
322
        MsgInfo *msginfo;
323
        cairo_t *cr;
324
        PangoLayout *layout;
325
        gdouble width, height;
326
        gint layout_h;
327
        PangoFontDescription *desc;
328
        gint font_size;
329
        gchar buf[BUFFSIZE];
330
        gint count = 0;
331

    
332
        if (page_nr >= print_data->n_pages)
333
                return;
334

    
335
        pinfo = g_ptr_array_index(print_data->pages, page_nr);
336
        mpinfo = pinfo->mpinfo;
337
        msginfo = mpinfo->msginfo;
338

    
339
        debug_print("draw_page: %d (%d), pos = %ld\n", page_nr, pinfo->page_nr_per_msg, pinfo->pos);
340
        debug_print("msg pages = %d\n", mpinfo->n_pages);
341

    
342
        cr = gtk_print_context_get_cairo_context(context);
343
        width = gtk_print_context_get_width(context);
344
        height = gtk_print_context_get_height(context);
345

    
346
#if 0
347
        cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
348
        cairo_rectangle(cr, 0, 0, width, font_size * 5);
349
        cairo_fill(cr);
350
#endif
351

    
352
        cairo_set_source_rgb(cr, 0, 0, 0);
353
        cairo_move_to(cr, 0, 0);
354

    
355
        layout = gtk_print_context_create_pango_layout(context);
356
        pango_layout_set_width(layout, width * PANGO_SCALE);
357
        pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
358
        pango_layout_set_spacing(layout, SPACING * PANGO_SCALE);
359

    
360
        if (pinfo->page_nr_per_msg == 0 && mpinfo->hdr_data) {
361
                if (layout_set_headers(layout, mpinfo, print_data) < 0) {
362
                        g_object_unref(layout);
363
                        return;
364
                }
365

    
366
                pango_cairo_show_layout(cr, layout);
367
                pango_layout_get_size(layout, NULL, &layout_h);
368
                cairo_rel_move_to(cr, 0, (double)layout_h / PANGO_SCALE);
369
        }
370

    
371
        pango_layout_set_attributes(layout, NULL);
372
        desc = pango_font_description_from_string(prefs_common_get()->textfont);
373
        font_size = pango_font_description_get_size(desc);
374
        pango_layout_set_font_description(layout, desc);
375
        pango_font_description_free(desc);
376

    
377
        if (fseek(mpinfo->fp, pinfo->pos, SEEK_SET) < 0) {
378
                FILE_OP_ERROR("draw_page", "fseek");
379
                g_object_unref(layout);
380
                return;
381
        }
382

    
383
        while (count < pinfo->lines) {
384
                gint lines;
385
                gint i;
386
                PangoLayoutIter *iter;
387
                PangoLayoutLine *layout_line;
388
                gint baseline;
389
                gdouble x, y;
390

    
391
                if (fgets(buf, sizeof(buf), mpinfo->fp) == NULL)
392
                        break;
393
                strretchomp(buf);
394

    
395
                pango_layout_set_text(layout, buf, -1);
396
                lines = pango_layout_get_line_count(layout);
397
                iter = pango_layout_get_iter(layout);
398
                cairo_get_current_point(cr, &x, &y);
399

    
400
                for (i = 0; i < lines; i++) {
401
                        layout_line = pango_layout_iter_get_line(iter);
402
                        baseline = pango_layout_iter_get_baseline(iter);
403
                        cairo_move_to(cr, 0,
404
                                      y + (gdouble)baseline / PANGO_SCALE);
405
                        pango_cairo_show_layout_line(cr, layout_line);
406
                        count++;
407
                        if (count >= pinfo->lines && i + 1 < lines)
408
                                break;
409
                        pango_layout_iter_next_line(iter);
410
                }
411

    
412
                pango_layout_iter_free(iter);
413

    
414
                pango_layout_get_size(layout, NULL, &layout_h);
415
                cairo_move_to(cr, 0,
416
                              y + (gdouble)layout_h / PANGO_SCALE + SPACING);
417
        }
418
        debug_print("count = %d\n", count);
419

    
420
        desc = gtkut_get_default_font_desc();
421
        pango_font_description_set_size(desc, font_size);
422
        pango_layout_set_font_description(layout, desc);
423
        pango_font_description_free(desc);
424
        g_snprintf(buf, sizeof(buf), "- %d -", pinfo->page_nr_per_msg + 1);
425
        pango_layout_set_text(layout, buf, -1);
426
        pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
427
        cairo_move_to(cr, 0, height - (gdouble)font_size / PANGO_SCALE);
428
        pango_cairo_show_layout(cr, layout);
429

    
430
        g_object_unref(layout);
431
}
432

    
433
gint printing_print_messages_gtk(GSList *mlist, MimeInfo *partinfo,
434
                                 gboolean all_headers)
435
{
436
        GtkPrintOperation *op;
437
        GtkPrintOperationResult res;
438
        PrintData *print_data;
439
        GSList *cur;
440
        gchar *prev_dir;
441
        gint i;
442

    
443
        g_return_val_if_fail(mlist != NULL, -1);
444

    
445
        debug_print("printing start\n");
446

    
447
        prev_dir = g_get_current_dir();
448
        change_dir(get_document_dir());
449

    
450
        print_data = g_new0(PrintData, 1);
451
        print_data->mlist = mlist;
452
        print_data->n_msgs = g_slist_length(mlist);
453
        print_data->msgs = g_new0(MsgPrintInfo, print_data->n_msgs);
454
        for (i = 0, cur = mlist; cur != NULL; i++, cur = cur->next) {
455
                print_data->msgs[i].msginfo = (MsgInfo *)cur->data;
456
        }
457
        print_data->pages = g_ptr_array_new();
458
        print_data->n_pages = 0;
459
        print_data->partinfo = partinfo;
460
        print_data->line_h = 0.0;
461
        print_data->all_headers = all_headers;
462

    
463
        op = gtk_print_operation_new();
464

    
465
        gtk_print_operation_set_unit(op, GTK_UNIT_POINTS);
466

    
467
        g_signal_connect(op, "begin-print", G_CALLBACK(begin_print),
468
                         print_data);
469
        g_signal_connect(op, "draw-page", G_CALLBACK(draw_page), print_data);
470

    
471
        if (settings)
472
                gtk_print_operation_set_print_settings(op, settings);
473
        if (page_setup)
474
                gtk_print_operation_set_default_page_setup(op, page_setup);
475

    
476
        res = gtk_print_operation_run
477
                (op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
478
                 GTK_WINDOW(main_window_get()->window), NULL);
479

    
480
        if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
481
                debug_print("save settings\n");
482
                if (settings)
483
                        g_object_unref(settings);
484
                settings = g_object_ref
485
                        (gtk_print_operation_get_print_settings(op));
486
        }
487

    
488
        g_object_unref(op);
489
        for (i = 0; i < print_data->pages->len; i++) {
490
                PageInfo *pinfo;
491
                pinfo = g_ptr_array_index(print_data->pages, i);
492
                g_free(pinfo);
493
        }
494
        g_ptr_array_free(print_data->pages, TRUE);
495
        for (i = 0; i < print_data->n_msgs; i++) {
496
                g_free(print_data->msgs[i].hdr_data);
497
                if (print_data->msgs[i].fp)
498
                        fclose(print_data->msgs[i].fp);
499
                if (print_data->msgs[i].msg_text_file) {
500
                        g_unlink(print_data->msgs[i].msg_text_file);
501
                        g_free(print_data->msgs[i].msg_text_file);
502
                }
503
        }
504
        g_free(print_data);
505

    
506
        change_dir(prev_dir);
507
        g_free(prev_dir);
508

    
509
        debug_print("printing finished\n");
510

    
511
        return 0;
512
}
513

    
514
void printing_page_setup_gtk(void)
515
{
516
        GtkPageSetup *new_page_setup;
517

    
518
        if (settings == NULL)
519
                settings = gtk_print_settings_new();
520

    
521
        new_page_setup = gtk_print_run_page_setup_dialog
522
                (GTK_WINDOW(main_window_get()->window), page_setup, settings);
523

    
524
        if (page_setup)
525
                g_object_unref(page_setup);
526

    
527
        page_setup = new_page_setup;
528
}
529

    
530
#endif /* GTK_CHECK_VERSION(2, 10, 0) */
531

    
532
static gint check_command_line(const gchar *cmdline)
533
{
534
        gchar *msg;
535

    
536
        msg = g_strconcat
537
                (_("The message will be printed with the following command:"),
538
                 "\n\n", cmdline ? cmdline : _("(Default print command)"),
539
                 NULL);
540
        if (alertpanel(_("Print"), msg, GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL)
541
            != G_ALERTDEFAULT) {
542
                g_free(msg);
543
                return -2;
544
        }
545
        g_free(msg);
546

    
547
        if (cmdline && str_find_format_times(cmdline, 's') != 1) {
548
                alertpanel_error(_("Print command line is invalid:\n`%s'"),
549
                                 cmdline);
550
                return -1;
551
        }
552

    
553
        return 0;
554
}
555

    
556
gint printing_print_messages_with_command(GSList *mlist, gboolean all_headers,
557
                                          const gchar *cmdline)
558
{
559
        MsgInfo *msginfo;
560
        GSList *cur;
561

    
562
        g_return_val_if_fail(mlist != NULL, -1);
563

    
564
        if (check_command_line(cmdline) < 0)
565
                return -1;
566

    
567
        for (cur = mlist; cur != NULL; cur = cur->next) {
568
                msginfo = (MsgInfo *)cur->data;
569
                if (msginfo)
570
                        procmsg_print_message(msginfo, cmdline, all_headers);
571
        }
572

    
573
        return 0;
574
}
575

    
576
gint printing_print_messages(GSList *mlist, gboolean all_headers)
577
{
578
#if GTK_CHECK_VERSION(2, 10, 0)
579
        if (!prefs_common.use_print_cmd)
580
                return printing_print_messages_gtk(mlist, NULL, all_headers);
581
        else
582
#endif /* GTK_CHECK_VERSION(2, 10, 0) */
583
                return printing_print_messages_with_command
584
                        (mlist, all_headers, prefs_common.print_cmd);
585
}
586

    
587
gint printing_print_message(MsgInfo *msginfo, gboolean all_headers)
588
{
589
        GSList mlist;
590

    
591
        mlist.data = msginfo;
592
        mlist.next = NULL;
593
        return printing_print_messages(&mlist, all_headers);
594
}
595

    
596
gint printing_print_message_part(MsgInfo *msginfo, MimeInfo *partinfo)
597
{
598
#if GTK_CHECK_VERSION(2, 10, 0)
599
        if (!prefs_common.use_print_cmd) {
600
                GSList mlist;
601

    
602
                mlist.data = msginfo;
603
                mlist.next = NULL;
604
                return printing_print_messages_gtk(&mlist, partinfo, FALSE);
605
        }
606
#endif /* GTK_CHECK_VERSION(2, 10, 0) */
607
        if (check_command_line(prefs_common.print_cmd) < 0)
608
                return -1;
609
        procmsg_print_message_part(msginfo, partinfo, prefs_common.print_cmd,
610
                                   FALSE);
611
        return 0;
612
}