Statistics
| Revision:

root / src / printing.c @ 3249

History | View | Annotate | Download (15.3 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
        g_print("layout_h = %d, line_h = %g, lines_per_page = %d\n", layout_h, line_h, lines_per_page);
222
        g_print("hdr_h = %g, body_h = %g, lines_left = %d\n", hdr_h, body_h, lines_left);
223

    
224
        if (print_data->partinfo) {
225
                FILE *msgfp;
226

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

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

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

    
260
                strretchomp(buf);
261
                pango_layout_set_text(layout, buf, -1);
262
                lines = pango_layout_get_line_count(layout);
263

    
264
                while (lines_left < lines) {
265
                        PangoLayoutLine *line;
266

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

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

    
281
                lines_left -= lines;
282
                pos = ftell(fp);
283
        }
284

    
285
        rewind(fp);
286
        mpinfo->fp = fp;
287

    
288
        g_object_unref(layout);
289

    
290
        n_pages = i + 1;
291
        g_print("n_pages = %d\n", n_pages);
292

    
293
        return n_pages;
294
}
295

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

    
303
        debug_print("begin_print\n");
304

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

    
310
        print_data->n_pages = n_pages;
311
        gtk_print_operation_set_n_pages(operation, n_pages);
312
}
313

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

    
330
        if (page_nr >= print_data->n_pages)
331
                return;
332

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

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

    
340
        cr = gtk_print_context_get_cairo_context(context);
341
        width = gtk_print_context_get_width(context);
342
        height = gtk_print_context_get_height(context);
343

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

    
350
        cairo_set_source_rgb(cr, 0, 0, 0);
351
        cairo_move_to(cr, 0, 0);
352

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

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

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

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

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

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

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

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

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

    
410
                pango_layout_iter_free(iter);
411

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

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

    
428
        g_object_unref(layout);
429
}
430

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

    
441
        g_return_val_if_fail(mlist != NULL, -1);
442

    
443
        debug_print("printing start\n");
444

    
445
        prev_dir = g_get_current_dir();
446
        change_dir(get_document_dir());
447

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

    
461
        op = gtk_print_operation_new();
462

    
463
        gtk_print_operation_set_unit(op, GTK_UNIT_POINTS);
464

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

    
469
        if (settings)
470
                gtk_print_operation_set_print_settings(op, settings);
471
        if (page_setup)
472
                gtk_print_operation_set_default_page_setup(op, page_setup);
473

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

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

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

    
504
        change_dir(prev_dir);
505
        g_free(prev_dir);
506

    
507
        debug_print("printing finished\n");
508

    
509
        return 0;
510
}
511

    
512
void printing_page_setup_gtk(void)
513
{
514
        GtkPageSetup *new_page_setup;
515

    
516
        if (settings == NULL)
517
                settings = gtk_print_settings_new();
518

    
519
        new_page_setup = gtk_print_run_page_setup_dialog
520
                (GTK_WINDOW(main_window_get()->window), page_setup, settings);
521

    
522
        if (page_setup)
523
                g_object_unref(page_setup);
524

    
525
        page_setup = new_page_setup;
526
}
527

    
528
#endif /* GTK_CHECK_VERSION(2, 10, 0) */
529

    
530
static gint check_command_line(const gchar *cmdline)
531
{
532
        gchar *msg;
533

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

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

    
551
        return 0;
552
}
553

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

    
560
        g_return_val_if_fail(mlist != NULL, -1);
561

    
562
        if (check_command_line(cmdline) < 0)
563
                return -1;
564

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

    
571
        return 0;
572
}
573

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

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

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

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

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