/* Katoob * Copyright (c) 2003 Arabeyes, Mohammed Sameer. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Many parts are borrowed from paps A postscript printing program using pango. * Copyright (C) 2002 Dov Grobgeld * Paps is distributed under the terms of the GNU Library General Public * License */ #ifdef HAVE_CONFIG_H #include #endif #ifdef ENABLE_PRINT #include "katoob.h" #include #include /* unlink() */ #include #include #include #include #include #include "print.h" #include "bidi.h" #include "print-private.h" #include "katoobdocument.h" #include "misc.h" #define PDF_COMMAND "gs -q -dSAFER -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=%s -" extern PConfig *PConf; PangoContext *pango_context; PangoFontDescription *font_description; gdouble pt_to_pixel; gdouble pixel_to_pt; gint page_width; gint page_height; gint text_width; gint text_height; gint top_margin; gint left_margin; gint right_margin; gint bottom_margin; FILE *OUT; gint page_idx; gint page_y_pos; gint pango_text_height; gchar *tmp; gboolean katoob_print_init (); gboolean katoob_print_finish (); void katoob_print_header (); void katoob_print_line (gchar * line); /* void katoob_print_lines (gchar * text); */ void katoob_print_footer (); void katoob_print_cleanup (); void start_page (); void eject_page (); static void draw_line_to_page (FILE * OUT, gint _y_pos, PangoLayoutLine * line, FriBidiCharType dir); static void postscript_draw_bitmap (FILE * OUT, FT_Bitmap * bitmap, double x, double y); gboolean katoob_do_print (KatoobDocument * doc, gboolean selection_only) { gchar *buff = NULL; GtkTextIter start, end; GtkTextBuffer *buffer; gint r, x; gchar *_tmp; /* Check the file existance 1st. */ if ((PConf->printer == PRINTER_PDF) || ((PConf->printer == PRINTER_POSTSCRIPT) && (PConf->location == LOCATION_FILE))) { if (g_file_test (PConf->file_entry, G_FILE_TEST_IS_DIR)) { katoob_error (_("You choosed a directory.")); return FALSE; } if (g_file_test (PConf->file_entry, G_FILE_TEST_EXISTS)) { _tmp = g_strdup_printf (_ ("Are you sure you want to overwrite the file %s ?"), PConf->file_entry); r = create_messagedialog (_tmp); g_free (_tmp); switch (r) { case GTK_RESPONSE_YES: break; default: return FALSE; } } } if (!katoob_print_init ()) { /* katoob_print_cleanup (); */ return FALSE; } katoob_print_header (); buffer = katoob_document_get_buffer (doc); if (selection_only) { if (!katoob_document_get_selection_bounds (doc, &start, &end)) { katoob_error (_("Can't get selection")); katoob_print_cleanup (); return FALSE; } x = gtk_text_iter_get_line (&start); r = gtk_text_iter_get_line (&end); r++; } else { r = gtk_text_buffer_get_line_count (buffer); x = 0; } for (; x < r; x++) { gtk_text_buffer_get_iter_at_line (buffer, &start, x); end = start; if (!gtk_text_iter_ends_line (&end)) { gtk_text_iter_forward_to_line_end (&end); } buff = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); katoob_print_line (buff); g_free (buff); } katoob_print_footer (); if (!katoob_print_finish ()) { katoob_print_cleanup (); return FALSE; } return TRUE; } gboolean katoob_print_init () { gchar *_tmp; page_idx = 1; page_y_pos = 0; pango_context = pango_ft2_get_context (PConf->dpi_x, PConf->dpi_y); pt_to_pixel = 1.0 * PConf->dpi_x / 72; pixel_to_pt = 1.0 * 72 / PConf->dpi_x; /* pt_to_pixel = 1.0 / 72 * PConf->dpi_x; pixel_to_pt = 1.0 / PConf->dpi_x * 72; */ pango_context_set_language (pango_context, pango_language_from_string ("en_US")); /* font_description = pango_font_description_new (); pango_font_description_set_family (font_description, g_strdup (PConf->font_family)); pango_font_description_set_style (font_description, PANGO_STYLE_NORMAL); pango_font_description_set_variant (font_description, PANGO_VARIANT_NORMAL); pango_font_description_set_weight (font_description, PANGO_WEIGHT_NORMAL); pango_font_description_set_stretch (font_description, PANGO_STRETCH_NORMAL); pango_font_description_set_size (font_description, atoi(PConf->font_scale) * PANGO_SCALE); */ _tmp = g_strdup_printf ("%s %s %s", PConf->font_family, PConf->font_style, PConf->font_scale); font_description = pango_font_description_from_string (_tmp); g_free (_tmp); pango_context_set_font_description (pango_context, font_description); pango_font_description_free (font_description); if (PConf->page_orientation == PAGE_ORIENTATION_LANDSCAPE) { page_height = Papers[PConf->paper_size].width; page_width = Papers[PConf->paper_size].height; } else { page_height = Papers[PConf->paper_size].height; page_width = Papers[PConf->paper_size].width; } _tmp = g_strdup_printf ("Page width = %i, Height = %i", page_width, page_height); katoob_debug (_tmp); g_free (_tmp); if (PConf->paper_size_unite == PAPER_SIZE_UNITS_POINTS) { left_margin = PConf->left_margin; right_margin = PConf->right_margin; /* FIXME: This may not be accurate. */ top_margin = PConf->top_margin; /* Add 9 to the top margin ? */ bottom_margin = PConf->bottom_margin; } else { /* 1 Inch = 72 points. */ left_margin = PConf->left_margin * 72; right_margin = PConf->right_margin * 72; /* FIXME: This may not be accurate. */ top_margin = PConf->top_margin * 72; /* Add 9 to the top margin ? */ bottom_margin = PConf->bottom_margin * 72; } text_width = page_width - left_margin - right_margin; text_height = page_height - top_margin - bottom_margin; pango_text_height = text_height * pt_to_pixel * PANGO_SCALE; _tmp = g_strdup_printf ("top_margin: %i\nbottom_margin: %i\nleft_margin: %i\nright_margin: %i\ntext_width: %i\ntext_height: %i", top_margin, bottom_margin, left_margin, right_margin, text_width, text_height); katoob_debug (_tmp); g_free (_tmp); if (PConf->printer == PRINTER_PDF) { /* PDF: We'll feed gs from the stdin. */ gchar *command = g_strdup_printf (PDF_COMMAND, PConf->pdf_entry); _tmp = g_strdup_printf ("About to execute command: %s", command); katoob_debug (_tmp); g_free (_tmp); OUT = popen (command, "w"); g_free (command); if (!OUT) { katoob_error (_("Couldn't execute gs to generate PDF.")); return FALSE; } } else if (PConf->location == LOCATION_LPR) { /* LPR: We'll feed lpr from the stdin. */ _tmp = g_strdup_printf ("lpr -# %i", PConf->copies); OUT = popen (_tmp, "w"); g_free (_tmp); if (!OUT) { katoob_error (_("Couldn't print the file.")); return FALSE; } } else if (PConf->location == LOCATION_FILE) { /* FILE: We'll write to a file. */ OUT = fopen (PConf->file_entry, "w"); if (!OUT) { katoob_error (_("Couldn't open the requested file for writing.")); return FALSE; } } else { /* Custom: We'll create a temp. file and run the command. */ gint fd; tmp = g_strdup_printf ("%s/%sXXXXXX", g_get_tmp_dir (), PACKAGE); fd = g_mkstemp (tmp); if (fd == -1) { g_free (tmp); katoob_error (_("Can't create temp file")); return FALSE; } OUT = fdopen (fd, "w"); if (!OUT) { katoob_error (_("Can't open temp file")); g_free (tmp); return FALSE; } } return TRUE; } gboolean katoob_print_finish () { gchar *_tmp; if ((PConf->printer == PRINTER_POSTSCRIPT) && (PConf->location == LOCATION_CUSTOM)) { gint x; fclose (OUT); /* Run the custom command. */ _tmp = g_strdup_printf ("%s %s", PConf->custom_entry, tmp); x = system (_tmp); g_free (_tmp); if (x == -1) { katoob_error (_("Error executing custom command.")); } unlink (tmp); g_free (tmp); return FALSE; } else { extern int errno; if (pclose (OUT) == -1) { katoob_error ((gchar *) g_strerror (errno)); return FALSE; } } katoob_print_cleanup (); return TRUE; } void katoob_print_header () { fprintf (OUT, "%%!PS-Adobe-3.0\n" "%%%%Creator: %s version %s\n" "%%%%Pages: (atend)\n" "%%%%BeginProlog\n" "\n" "/inch {72 mul} bind def\n" "/mm {1 inch 25.4 div mul} bind def\n" "\n" "%% override setpagedevice if it is not defined\n" "/setpagedevice where {\n" " pop %% get rid of its dictionary\n" " /setpagesize { \n" " 2 dict begin\n" " /pageheight exch def \n" " /pagewidth exch def\n" " %% Exchange pagewidth and pageheight so that pagewidth is bigger\n" " pagewidth pageheight gt { \n" " pagewidth\n" " /pagewidth pageheight def\n" " /pageheight exch def\n" " } if\n" " 2 dict dup /PageSize [pagewidth pageheight] put setpagedevice \n" " end\n" " } def\n" "}\n" "{\n" " /setpagesize { pop pop } def\n" "} ifelse\n" "%% Turn the page around\n", PACKAGE, VERSION); fprintf (OUT, "%% User settings\n" "/pagewidth %d def\n" "/pageheight %d def\n" "/column_width %d def\n" "/bodyheight %d def\n" "/lmarg %d def\n" "/ytop %d def\n", page_width, page_height, text_width, text_height, left_margin, page_height - top_margin); fprintf (OUT, "/dpi_x %d def\n" "/dpi_y %d def\n", PConf->dpi_x, PConf->dpi_y); fprintf (OUT, "/bop { %% Beginning of page definitions\n" " gsave\n" "} def\n" "\n" "/eop { %% End of page cleanups\n" " grestore \n" "} def\n"); /* Outline support */ fprintf (OUT, "/conicto {\n" " /to_y exch def\n" " /to_x exch def\n" " /conic_cntrl_y exch def\n" " /conic_cntrl_x exch def\n" " currentpoint\n" " /p0_y exch def\n" " /p0_x exch def\n" " /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def\n" " /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def\n" " /p2_x p1_x to_x p0_x sub 1 3 div mul add def\n" " /p2_y p1_y to_y p0_y sub 1 3 div mul add def\n" " p1_x p1_y p2_x p2_y to_x to_y curveto\n" "} bind def\n" "/start_ol { gsave 1.1 dpi_x div dup scale} bind def\n" "/end_ol { closepath fill grestore } bind def\n"); fprintf (OUT, "%%%%EndPrologue\n"); start_page (); } /* void katoob_print_lines (gchar * text) { } */ void katoob_print_line (gchar * line) { PangoLayout *layout; glong l; gint num_lines; FriBidiCharType dir = FRIBIDI_TYPE_WL; gunichar *ch = NULL; gint len = strlen (line); gint x = 0; gchar *_tmp; katoob_debug (__FUNCTION__); ch = g_utf8_to_ucs4_fast (line, len, &l); dir = katoob_bidi_get_line_dir (ch, l); g_free (ch); layout = pango_layout_new (pango_context); pango_layout_set_text (layout, line, len); if (dir == FRIBIDI_TYPE_RTL) { pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT); } else if (dir == FRIBIDI_TYPE_LTR) { pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT); } pango_layout_set_width (layout, text_width * pt_to_pixel * PANGO_SCALE); num_lines = pango_layout_get_line_count (layout); for (x = 0; x < num_lines; x++) { PangoRectangle logical_rect, ink_rect; PangoLayoutLine *p_line; p_line = pango_layout_get_line (layout, x); pango_layout_line_get_extents (p_line, &ink_rect, &logical_rect); /* Check if we need to move to next page */ _tmp = g_strdup_printf ("page_y_pos: %i", page_y_pos); katoob_debug (_tmp); g_free (_tmp); if (page_y_pos + logical_rect.height >= pango_text_height) { page_y_pos = 0; eject_page (); page_idx++; start_page (); } _tmp = g_strdup_printf ("page_y_pos: %i", page_y_pos); katoob_debug (_tmp); g_free (_tmp); _tmp = g_strdup_printf ("page_y_pos + logical_rect.height: %i", page_y_pos + logical_rect.height); katoob_debug (_tmp); g_free (_tmp); draw_line_to_page (OUT, page_y_pos + logical_rect.height, p_line, dir); page_y_pos += logical_rect.height; } } void katoob_print_footer () { eject_page (); fprintf (OUT, "%%%%Pages: %d\n" "%%%%Trailer\n" "%%%%EOF\n", page_idx); } void start_page () { fprintf (OUT, "%%%%Page: %d %d\n", page_idx, page_idx); fprintf (OUT, "bop\n"); } void eject_page () { fprintf (OUT, "eop\n" "showpage\n"); } static void draw_line_to_page (FILE * OUT, gint _y_pos, PangoLayoutLine * line, FriBidiCharType dir) { gchar *_tmp; /* Assume square aspect ratio for now */ double y_pos = page_height - top_margin - _y_pos / PANGO_SCALE * pixel_to_pt; double x_pos = left_margin; PangoRectangle ink_rect, logical_rect; gint byte_width; FT_Bitmap bitmap; guchar *buf; _tmp = g_strdup_printf ("y_pos: %f", y_pos); katoob_debug (_tmp); g_free (_tmp); pango_layout_line_get_pixel_extents (line, &ink_rect, &logical_rect); /* Round pixel width to be divisible by 8 */ byte_width = (logical_rect.width + 7) / 8 * 8; /* Set up the bitmap to hold the logical_rect */ buf = g_malloc (byte_width * logical_rect.height); memset (buf, 0x00, byte_width * logical_rect.height); bitmap.rows = logical_rect.height; bitmap.width = byte_width; bitmap.pitch = byte_width; bitmap.buffer = buf; bitmap.num_grays = 256; bitmap.pixel_mode = ft_pixel_mode_grays; pango_ft2_render_layout_line (&bitmap, line, 0, bitmap.rows * 2 / 3); if (bitmap.width > 0 && bitmap.rows > 0) { /* Hopefully this should do right align */ if (dir == FRIBIDI_TYPE_RTL) { x_pos += text_width - bitmap.width * pixel_to_pt; } postscript_draw_bitmap (OUT, &bitmap, x_pos, y_pos); } g_free (buf); } static void postscript_draw_bitmap (FILE * OUT, FT_Bitmap * bitmap, double x, double y) { int b_idx; fprintf (OUT, "gsave\n"); fprintf (OUT, "%f %f translate\n", x, y); fprintf (OUT, "/img_width %d def\n", bitmap->width); fprintf (OUT, "/img_height %d def\n", bitmap->rows); fprintf (OUT, "/picstr img_width 8 idiv string def\n"); fprintf (OUT, " img_width 72 dpi_x div mul\n" " img_height 72 dpi_y div mul scale\n" " 0 setgray\n" " img_width img_height\n" " true\n" " [img_width 0 0 img_height neg 0 img_height 0.67 mul]\n" " { currentfile\n" " picstr readhexstring pop }\n" " imagemask"); /* Output the bitmap in hexadecimal */ for (b_idx = 0; b_idx < bitmap->width / 8 * bitmap->rows; b_idx++) { guchar packed_b = 0; int bit_idx; if (b_idx % (bitmap->width / 8) == 0) fprintf (OUT, "\n"); for (bit_idx = 0; bit_idx < 8; bit_idx++) { guchar this_bit = bitmap->buffer[b_idx * 8 + bit_idx] > 128; packed_b = (packed_b << 1) + this_bit; } fprintf (OUT, "%02x", packed_b); } fprintf (OUT, "\n" "grestore\n"); } void katoob_print_cleanup () { g_object_unref (G_OBJECT (pango_context)); } #endif /* ENABLE_PRINT */