#! /bin/sh /usr/share/dpatch/dpatch-run
## 50_printing.dpatch by Alexey Antipov (1a_antipov@mail.ru)
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: Printing function revised
## DP: - Fix UTF-8 character processing
## DP: - Add more correct word wrap (with pango)
## DP: - Improve paging calculation
## DP: - Overall code rewrite
Index: ./src/sciwrappers.c
===================================================================
--- ./src/sciwrappers.c (revision 5796)
+++ ./src/sciwrappers.c (working copy)
@@ -871,6 +871,23 @@
return SSM(sci, SCI_GETSTYLEAT, position, 0);
}
+/** Gets styled text between @a start and @a end.
+ * @param sci Scintilla widget.
+ * @param start Start.
+ * @param end End.
+ * @return The text with styling information inside the given range. The even bytes
+ * (0,2,4 etc) contain characters, while the odd bytes contain style bits
+ * Should be freed when no longer needed.
+ */
+gchar *sci_get_styled_text(ScintillaObject *sci, gint start, gint end)
+{
+ struct Sci_TextRange tr;
+ tr.chrg.cpMin = start;
+ tr.chrg.cpMax = end;
+ tr.lpstrText = g_malloc(2*(end-start+1));
+ SSM(sci, SCI_GETSTYLEDTEXT, 0, (long)&tr);
+ return tr.lpstrText;
+}
void sci_set_codepage(ScintillaObject *sci, gint cp)
{
Index: ./src/sciwrappers.h
===================================================================
--- ./src/sciwrappers.h (revision 5796)
+++ ./src/sciwrappers.h (working copy)
@@ -130,6 +130,7 @@
void sci_goto_line (ScintillaObject *sci, gint line, gboolean unfold);
void sci_marker_delete_all (ScintillaObject *sci, gint marker);
gint sci_get_style_at (ScintillaObject *sci, gint position);
+gchar* sci_get_styled_text (ScintillaObject *sci, gint start, gint end);
void sci_set_symbol_margin (ScintillaObject *sci, gboolean set);
void sci_set_codepage (ScintillaObject *sci, gint cp);
void sci_clear_cmdkey (ScintillaObject *sci, gint key);
Index: ./src/printing.c
===================================================================
--- ./src/printing.c (revision 5796)
+++ ./src/printing.c (working copy)
@@ -53,12 +53,7 @@
#define ROTATE_RGB(color) \
(((color) & 0xFF0000) >> 16) + ((color) & 0x00FF00) + (((color) & 0x0000FF) << 16)
-#define ADD_ATTR(l, a) \
- pango_attr_list_insert((l), (a)); \
- (a)->start_index = 0; \
- (a)->end_index = -1;
-
enum
{
FORE = 0,
@@ -73,13 +68,15 @@
typedef struct
{
GeanyDocument *doc;
- gint font_width;
gint lines;
gint n_pages;
gint lines_per_page;
+ gint max_line_number_arity;
gint max_line_number_margin;
gint cur_line;
gint cur_pos;
+ gint cur_page;
+ gint cur_style;
gint styles[STYLE_MAX + 1][MAX_TYPES];
gdouble line_height;
/* whether we have a wrapped line on page end to take care of on next page */
@@ -105,8 +102,8 @@
static GtkPrintSettings *settings = NULL;
static GtkPageSetup *page_setup = NULL;
+static void process_page(GtkPrintContext *context, DocInfo *dinfo, gboolean do_print);
-
/* returns the "width" (count of needed characters) for the given number */
static gint get_line_numbers_arity(gint x)
{
@@ -150,126 +147,6 @@
}
-static gboolean utils_font_desc_check_monospace(PangoContext *pc, PangoFontDescription *desc)
-{
- PangoFontFamily **families;
- gint n_families, i;
- const gchar *font;
- gboolean ret = TRUE;
-
- font = pango_font_description_get_family(desc);
- pango_context_list_families(pc, &families, &n_families);
- for (i = 0; i < n_families; i++)
- {
- if (utils_str_equal(font, pango_font_family_get_name(families[i])))
- {
- if (!pango_font_family_is_monospace(families[i]))
- {
- ret = FALSE;
- }
- }
- }
- g_free(families);
- return ret;
-}
-
-
-/* We don't support variable width fonts (yet) */
-static gint get_font_width(GtkPrintContext *context, PangoFontDescription *desc)
-{
- PangoContext *pc;
- PangoFontMetrics *metrics;
- gint width;
-
- pc = gtk_print_context_create_pango_context(context);
-
- if (!utils_font_desc_check_monospace(pc, desc))
- dialogs_show_msgbox_with_secondary(GTK_MESSAGE_WARNING,
- _("The editor font is not a monospaced font!"),
- _("Text will be wrongly spaced."));
-
- metrics = pango_context_get_metrics(pc, desc, pango_context_get_language(pc));
- /** TODO is this the best result we can get? */
- /* digit and char width are mostly equal for monospace fonts, char width might be
- * for dual width characters(e.g. Japanese) so use digit width to get sure we get the width
- * for one character */
- width = pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE;
-
- pango_font_metrics_unref(metrics);
- g_object_unref(pc);
-
- return width;
-}
-
-
-static gint get_page_count(GtkPrintContext *context, DocInfo *dinfo)
-{
- gdouble width, height;
- gint layout_h;
- gint i, j, lines_left;
- gchar *line_buf;
-
- if (dinfo == NULL)
- return -1;
-
- width = gtk_print_context_get_width(context);
- height = gtk_print_context_get_height(context);
-
- if (printing_prefs.print_line_numbers)
- /* remove line number margin space from overall width */
- width -= dinfo->max_line_number_margin * dinfo->font_width;
-
- pango_layout_set_width(dinfo->layout, width * PANGO_SCALE);
-
- /* add test text to get line height */
- pango_layout_set_text(dinfo->layout, "Test 1", -1);
- pango_layout_get_size(dinfo->layout, NULL, &layout_h);
- if (layout_h <= 0)
- {
- geany_debug("Invalid layout_h (%d). Falling back to default height (%d)",
- layout_h, 100 * PANGO_SCALE);
- layout_h = 100 * PANGO_SCALE;
- }
- dinfo->line_height = (gdouble)layout_h / PANGO_SCALE;
- dinfo->lines_per_page = ceil((height - dinfo->line_height) / dinfo->line_height);
-#ifdef GEANY_PRINT_DEBUG
- geany_debug("max lines_per_page: %d", dinfo->lines_per_page);
-#endif
- if (printing_prefs.print_page_numbers)
- dinfo->lines_per_page -= 2;
- if (printing_prefs.print_page_header)
- dinfo->lines_per_page -= 3;
-
- lines_left = dinfo->lines_per_page;
-
- i = 0;
- for (j = 0; j < dinfo->lines; j++)
- {
- gint lines = 1;
- gint line_width;
-
- line_buf = sci_get_line(dinfo->doc->editor->sci, j);
- line_width = (g_utf8_strlen(line_buf, -1) + 1) * dinfo->font_width;
- if (line_width > width)
- lines = ceil(line_width / width);
-#ifdef GEANY_PRINT_DEBUG
- if (lines != 1) geany_debug("%d %d", j+1, lines);
-#endif
-
- while (lines_left < lines)
- {
- lines -= lines_left;
- lines_left = dinfo->lines_per_page;
- i++;
- }
- lines_left -= lines;
- g_free(line_buf);
- }
-
- return i + 1;
-}
-
-
static void add_page_header(DocInfo *dinfo, cairo_t *cr, gint width, gint page_nr)
{
gint ph_height = dinfo->line_height * 3;
@@ -291,7 +168,6 @@
* and this on left and right side, so (2 + 2) * 2 */
pango_layout_set_width(layout, (width - 8) * PANGO_SCALE);
- if ((g_utf8_strlen(file_name, -1) * dinfo->font_width) >= ((width - 4) - (dinfo->font_width * 2)))
/* if the filename is wider than the available space on the line, skip parts of it */
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_MIDDLE);
@@ -447,6 +323,13 @@
PangoFontDescription *desc;
gint i;
gint style_max;
+ gdouble width, height;
+ gint layout_h;
+ gint prev_h = -1;
+ gchar *str = g_strdup("");
+ gint nchars = 1;
+ width = gtk_print_context_get_width(context);
+ height = gtk_print_context_get_height(context);
if (dinfo == NULL)
return;
@@ -455,23 +338,23 @@
desc = pango_font_description_from_string(interface_prefs.editor_font);
- /* init dinfo fields */
+ /* Stage 0: init dinfo fields */
dinfo->lines = sci_get_line_count(dinfo->doc->editor->sci);
dinfo->lines_per_page = 0;
- dinfo->cur_line = 0;
+ dinfo->cur_line = 1;
dinfo->cur_pos = 0;
+ dinfo->n_pages = 0;
+ dinfo->max_line_number_margin = 0;
+ dinfo->max_line_number_arity = 0;
dinfo->long_line = FALSE;
dinfo->print_time = time(NULL);
- dinfo->max_line_number_margin = get_line_numbers_arity(dinfo->lines) + 1;
- /* increase font width by 1 (looks better) */
- dinfo->font_width = get_font_width(context, desc) + 1;
+
/* create a PangoLayout to be commonly used in get_page_count and draw_page */
dinfo->layout = setup_pango_layout(context, desc);
- /* this is necessary because of possible line breaks on the printed page and then
- * lines_per_page differs from document line count */
- dinfo->n_pages = get_page_count(context, dinfo);
+ pango_layout_set_width(dinfo->layout, width * PANGO_SCALE);
+ pango_layout_set_height(dinfo->layout, height * PANGO_SCALE);
- /* read all styles from Scintilla */
+ /* Stage 1: read all styles from Scintilla */
style_max = pow(2, scintilla_send_message(dinfo->doc->editor->sci, SCI_GETSTYLEBITS, 0, 0));
/* if the lexer uses only the first 32 styles(style bits = 5),
* we need to add the pre-defined styles */
@@ -500,281 +383,395 @@
scintilla_send_message(dinfo->doc->editor->sci, SCI_STYLEGETITALIC, i, 0);
}
+ /* Stage 2: get lines per page and line height */
+ /* Add test text to get lines per page until page limits exceed */
+ pango_layout_get_size(dinfo->layout, NULL, &layout_h);
+ while (layout_h != prev_h)
+ {
+ nchars += 5;
+ str = g_realloc(str, nchars);
+ strcat(str, "Test\n");
+ pango_layout_set_text(dinfo->layout, str, -1);
+ prev_h = layout_h;
+ pango_layout_get_size(dinfo->layout, NULL, &layout_h);
+ }
+ if (str)
+ g_free(str);
+ dinfo->lines_per_page = (nchars - 1)/5;
+ dinfo->line_height = layout_h/dinfo->lines_per_page/PANGO_SCALE;
+
+ /* if we have a page header, we should reserve place for it*/
+ if (printing_prefs.print_page_header)
+ dinfo->lines_per_page -= 3;
+ if (printing_prefs.print_page_numbers)
+ dinfo->lines_per_page -= 2;
+
+ /* Stage 3: calculate line number margin and arity */
+ if (printing_prefs.print_line_numbers)
+ {
+ pango_layout_set_width(dinfo->layout, -1);
+ dinfo->max_line_number_arity = get_line_numbers_arity(dinfo->lines) + 1;
+ char c;
+ int w;
+ str = g_malloc(dinfo->max_line_number_arity+3);
+ for (c = '0'; c<='9'; c++)
+ {
+ memset(str, c, dinfo->max_line_number_arity);
+ str[dinfo->max_line_number_arity] = ' ';
+ str[dinfo->max_line_number_arity+1] = ' ';
+ str[dinfo->max_line_number_arity+2] = '\0';
+ pango_layout_set_text(dinfo->layout, str, -1);
+ pango_layout_get_size(dinfo->layout, &w, NULL);
+ if (w > dinfo->max_line_number_margin) dinfo->max_line_number_margin = w;
+ }
+ dinfo->max_line_number_margin /= PANGO_SCALE;
+ g_free(str);
+ }
+
+ /* Stage 4: get pages count by simulating "print" */
+ for (dinfo->n_pages = 0; dinfo->cur_pos != -1; dinfo->n_pages++)
+ {
+ gchar *text = g_strdup_printf(_("Calculating pages: %d"), dinfo->n_pages);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(main_widgets.progressbar), 1);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(main_widgets.progressbar), text);
+ g_free(text);
+ process_page(context, dinfo, FALSE);
+ }
+
+ dinfo->cur_pos = 0;
+ dinfo->cur_line = 1;
+ dinfo->cur_page = 0;
+
if (dinfo->n_pages >= 0)
gtk_print_operation_set_n_pages(operation, dinfo->n_pages);
pango_font_description_free(desc);
}
-
-static void draw_page(GtkPrintOperation *operation, GtkPrintContext *context,
- gint page_nr, gpointer user_data)
+static gint parse_styled_text(DocInfo *dinfo, gint start_pos, gchar *styled_text, PangoAttrList *layout_attr, GString *str)
{
- DocInfo *dinfo = user_data;
- GeanyEditor *editor;
- cairo_t *cr;
- gdouble width, height;
- gdouble x, y;
- /*gint layout_h;*/
- gint count;
- GString *str;
+ PangoAttribute *attr[4] = { NULL };
+ gint colours[3] = { 0 };
+ gint old_style = -1;
+ gint style = -1;
+ gint attr_start;
+ gchar c;
+ int i,j;
+ for (i=0; (c=styled_text[i]); i+=2) {
+ /* Append character to string */
+ g_string_append_c(str, c);
+ /* Obtain style bits */
+ style = styled_text[i+1];
- if (dinfo == NULL || page_nr >= dinfo->n_pages)
- return;
+ if (style != old_style) {
+ /* Calculate start index*/
+ attr_start = start_pos + i/2;
- editor = dinfo->doc->editor;
+ old_style = style;
- if (dinfo->n_pages > 0)
- {
- gdouble fraction = (page_nr + 1) / (gdouble) dinfo->n_pages;
- gchar *text = g_strdup_printf(_("Page %d of %d"), page_nr, dinfo->n_pages);
- gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(main_widgets.progressbar), fraction);
- gtk_progress_bar_set_text(GTK_PROGRESS_BAR(main_widgets.progressbar), text);
- g_free(text);
- }
+ /* Set previous block attributes*/
+ for (j=0;j<3;j++)
+ {
+ if (attr[j])
+ {
+ attr[j]->end_index = attr_start;
+ pango_attr_list_insert(layout_attr, attr[j]);
+ }
+ }
+ /* Get new attributes */
+ /* foreground colour */
+ get_rgb_values(dinfo->styles[style][FORE], &colours[0], &colours[1], &colours[2]);
+ attr[0] = pango_attr_foreground_new(colours[0], colours[1], colours[2]);
+ attr[0]->start_index = attr_start;
+
+ /* background colour */
+ get_rgb_values(dinfo->styles[style][BACK], &colours[0], &colours[1], &colours[2]);
+ attr[1] = pango_attr_background_new(colours[0], colours[1], colours[2]);
+ attr[1]->start_index = attr_start;
+
+ /* bold text */
+ if (dinfo->styles[style][BOLD])
+ {
+ attr[2] = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+ attr[2]->start_index = attr_start;
+ }
+ else
+ {
+ attr[2] = NULL;
+ }
+ /* italic text */
+ if (dinfo->styles[style][ITALIC])
+ {
+ attr[3] = pango_attr_style_new(PANGO_STYLE_ITALIC);
+ attr[3]->start_index = attr_start;
+ }
+ else
+ {
+ attr[3] = NULL;
+ }
+ }
+ }
+ /* Set last block attributes*/
+ for (j=0;j<3;j++)
+ {
+ if (attr[j])
+ {
+ attr[j]->end_index = start_pos + i/2;
+ pango_attr_list_insert(layout_attr, attr[j]);
+ }
+ }
+ return style;
+}
-#ifdef GEANY_PRINT_DEBUG
- geany_debug("draw_page = %d, pages = %d, (real) lines_per_page = %d",
- page_nr, dinfo->n_pages, dinfo->lines_per_page);
-#endif
+#define PAGE_BUFFER_SIZE 1024
- str = g_string_sized_new(256);
- cr = gtk_print_context_get_cairo_context(context);
+static void process_page(GtkPrintContext *context, DocInfo *dinfo, gboolean do_print)
+{
+ cairo_t *cr = NULL;
+ gdouble width, height, ystart = 0;
+ GString *str;
+ gint page_start = dinfo->cur_pos;
+ gboolean long_line = FALSE;
+ PangoAttrList *layout_attr;
+ int linecount;
+ int old_linecount = -1;
+ if (dinfo == NULL)
+ return;
+
+ layout_attr = pango_attr_list_new();
+ str = g_string_sized_new(PAGE_BUFFER_SIZE*2);
width = gtk_print_context_get_width(context);
height = gtk_print_context_get_height(context);
- cairo_set_source_rgb(cr, 0, 0, 0);
+ if (printing_prefs.print_line_numbers)
+ /* remove line number margin space from overall width */
+ width -= dinfo->max_line_number_margin;
+
+ if (do_print) {
+ cr = gtk_print_context_get_cairo_context(context);
+ cairo_set_source_rgb(cr, 0, 0, 0);
#ifdef GEANY_PRINT_DEBUG
- cairo_set_line_width(cr, 0.2);
- cairo_rectangle(cr, 0, 0, width, height);
- cairo_stroke(cr);
+ cairo_set_line_width(cr, 0.2);
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_stroke(cr);
#endif
- cairo_move_to(cr, 0, 0);
+ cairo_move_to(cr, 0, 0);
+ if (printing_prefs.print_page_header) {
+ add_page_header(dinfo, cr, width, dinfo->cur_page);
+ }
+ if (printing_prefs.print_line_numbers) {
+ cairo_get_current_point(cr, NULL, &ystart);
+ cairo_move_to(cr, dinfo->max_line_number_margin, ystart);
+ }
+ }
+ /* Initialize pango layout */
pango_layout_set_width(dinfo->layout, width * PANGO_SCALE);
+ pango_layout_set_height(dinfo->layout, height * PANGO_SCALE);
pango_layout_set_alignment(dinfo->layout, PANGO_ALIGN_LEFT);
pango_layout_set_ellipsize(dinfo->layout, FALSE);
pango_layout_set_justify(dinfo->layout, FALSE);
+ pango_layout_set_text(dinfo->layout, "", -1);
+ pango_layout_set_attributes(dinfo->layout, NULL);
- if (printing_prefs.print_page_header)
- add_page_header(dinfo, cr, width, page_nr);
- count = 0; /* the actual line counter for the current page, might be different from
- * dinfo->cur_line due to possible line breaks */
- while (count < dinfo->lines_per_page)
+ /* We'll read text piece-by-piece, parse Scintilla layout to Pango layout
+ * and detect when it will exceed our page height
+ *
+ * One major disadvantage of this approach is that page count cannot be
+ * determined until we do all this stuff - i.e. we need to perform the same
+ * actions twice. But that's the only reliable way to obtain nice page
+ * separation.
+ *
+ * The read buffer should not be too small, because pango and scintilla
+ * operations are rather costly. Performance is subject to measure with
+ * different buffers, I have taken an arbitrary number of 1Kb
+ *
+ * We want to protect ourself from endless loops here, so we should detect
+ * when linecount stops changing (as Pango won't show strings past layout
+ * height)
+ *
+ */
+ while (((linecount=pango_layout_get_line_count(dinfo->layout)) <= dinfo->lines_per_page) && (old_linecount != linecount))
{
- gchar c = 'a';
- gint style = -1;
- PangoAttrList *layout_attr;
- PangoAttribute *attr;
- gint colours[3] = { 0 };
- gboolean add_linenumber = TRUE;
- gboolean at_eol;
+ old_linecount = linecount;
+ gint bytes_parsed;
+ gboolean at_eof = FALSE;
+ gint i;
+ /* Get text and style data for a piece */
+ gchar *buffer = sci_get_styled_text(dinfo->doc->editor->sci, dinfo->cur_pos, dinfo->cur_pos + PAGE_BUFFER_SIZE);
- while (count < dinfo->lines_per_page && c != '\0')
- {
- at_eol = FALSE;
+ /* Obtain real text length (i.e. if we have reached the end of the document)*/
+ for (i = 0; buffer[i]; i+=2);
+ bytes_parsed = i/2;
- g_string_erase(str, 0, str->len); /* clear the string */
+ if (bytes_parsed < PAGE_BUFFER_SIZE) {
+ /* We are at the end of the document, so process data as it is */
+ at_eof = TRUE;
+ }
+ else if ((i > 0) && (buffer[i-2] < 0))
+ {
+ /* truncate buffer to previous Unicode character
+ * UTF-8 symbols are sequences like 11xxxxxx 10xxxxxx 10xxxxxx etc.
+ * So we have to check the 2nd most significant bit for sequence start
+ * or end */
+ do {
+ i-=2;
+ bytes_parsed--;
+ } while ((i > 0) && ((buffer[i] & 0x40) == 0));
+ buffer[i] = 0;
+ }
+
+ dinfo->cur_pos += bytes_parsed;
- /* line numbers */
- if (printing_prefs.print_line_numbers && add_linenumber)
- {
- /* if we had a wrapped line on the last page which needs to be continued, don't
- * add a line number */
- if (dinfo->long_line)
- {
- add_linenumber = FALSE;
- }
- else
- {
- gchar *line_number = NULL;
- gint cur_line_number_margin = get_line_numbers_arity(dinfo->cur_line + 1);
- gchar *fill = g_strnfill(
- dinfo->max_line_number_margin - cur_line_number_margin - 1, ' ');
+ /* Parse text with styling information and free memory*/
+ dinfo->cur_style = parse_styled_text(dinfo, dinfo->cur_pos - page_start - bytes_parsed, buffer, layout_attr, str);
+ g_free(buffer);
- line_number = g_strdup_printf("%s%d ", fill, dinfo->cur_line + 1);
- g_string_append(str, line_number);
- dinfo->cur_line++; /* increase document line */
- add_linenumber = FALSE;
- style = STYLE_LINENUMBER;
- c = 'a'; /* dummy value */
- g_free(fill);
- g_free(line_number);
- }
- }
- /* data */
- else
- {
- style = sci_get_style_at(dinfo->doc->editor->sci, dinfo->cur_pos);
- c = sci_get_char_at(dinfo->doc->editor->sci, dinfo->cur_pos);
- if (c == '\0' || style == -1)
- { /* if c gets 0, we are probably out of document boundaries,
- * so stop to break out of outer loop */
- count = dinfo->lines_per_page;
- break;
- }
- dinfo->cur_pos++;
+ /* Set text and wrapping*/
+ pango_layout_set_text(dinfo->layout, str->str, -1);
+ pango_layout_set_wrap(dinfo->layout, PANGO_WRAP_WORD_CHAR);
+
+ /* Set attributes (must go here to correctly calculate page dimensions) */
+ pango_layout_set_attributes(dinfo->layout, layout_attr);
+ pango_layout_context_changed(dinfo->layout);
- /* convert tabs to spaces which seems to be better than using Pango tabs */
- if (c == '\t')
- {
- gint tab_width = sci_get_tab_width(editor->sci);
- gchar *s = g_strnfill(tab_width, ' ');
- g_string_append(str, s);
- g_free(s);
- }
- /* don't add line breaks, they are handled manually below */
- else if (c == '\r' || c == '\n')
- {
- gchar c_next = sci_get_char_at(dinfo->doc->editor->sci, dinfo->cur_pos);
- at_eol = TRUE;
- if (c == '\r' && c_next == '\n')
- dinfo->cur_pos++; /* skip LF part of CR/LF */
- }
- else
- {
- g_string_append_c(str, c); /* finally add the character */
+ if (at_eof) {
+ /* We are at the end of the document*/
+ dinfo->cur_pos = -1;
+ break;
+ }
+ }
- /* handle UTF-8: since we add char by char (better: byte by byte), we need to
- * keep UTF-8 characters together(e.g. two bytes for one character)
- * the input is always UTF-8 and c is signed, so all non-Ascii
- * characters are less than 0 and consist of all bytes less than 0.
- * style doesn't change since it is only one character with multiple bytes. */
- while (c < 0)
- {
- c = sci_get_char_at(dinfo->doc->editor->sci, dinfo->cur_pos);
- if (c < 0)
- { /* only add the byte when it is part of the UTF-8 character
- * otherwise we could add e.g. a '\n' and it won't be visible in the
- * printed document */
- g_string_append_c(str, c);
- dinfo->cur_pos++;
- }
- }
- }
- }
+ if (dinfo->cur_pos != -1)
+ {
+ /* If we have too many lines to be printed on 1 page, let's cut some */
+ PangoLayoutLine *line = pango_layout_get_line_readonly(dinfo->layout, ((linecount > dinfo->lines_per_page) ? dinfo->lines_per_page : linecount)-1);
+ gint offset = line->start_index + line->length;
+
+ /* Pango won't show strings past layout height, so when that occurs we have
+ * to check for truncation manually */
+ if (str->len >= offset)
+ {
+ dinfo->cur_pos = page_start + offset;
+ /* Consider last line break*/
+ if (str->str[offset] == '\r')
+ {
+ dinfo->cur_pos++;
+ if (str->str[offset+1] == '\n')
+ dinfo->cur_pos++;
+ }
+ else if(str->str[offset] == '\n')
+ {
+ dinfo->cur_pos++;
+ if (str->str[offset+1] == '\r')
+ /* Impossible, though should be processed */
+ dinfo->cur_pos++;
+ } else {
+ long_line = TRUE;
+ }
+ g_string_truncate(str, offset);
+ pango_layout_set_text(dinfo->layout, str->str, -1);
+ dinfo->cur_style = sci_get_style_at(dinfo->doc->editor->sci, dinfo->cur_pos);
+ }
+ }
- if (! at_eol)
- {
- /* set text */
- pango_layout_set_text(dinfo->layout, str->str, -1);
- /* attributes */
- layout_attr = pango_attr_list_new();
- /* foreground colour */
- get_rgb_values(dinfo->styles[style][FORE], &colours[0], &colours[1], &colours[2]);
- attr = pango_attr_foreground_new(colours[0], colours[1], colours[2]);
- ADD_ATTR(layout_attr, attr);
- /* background colour */
- get_rgb_values(dinfo->styles[style][BACK], &colours[0], &colours[1], &colours[2]);
- attr = pango_attr_background_new(colours[0], colours[1], colours[2]);
- ADD_ATTR(layout_attr, attr);
- /* bold text */
- if (dinfo->styles[style][BOLD])
- {
- attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
- ADD_ATTR(layout_attr, attr);
- }
- /* italic text */
- if (dinfo->styles[style][ITALIC])
- {
- attr = pango_attr_style_new(PANGO_STYLE_ITALIC);
- ADD_ATTR(layout_attr, attr);
- }
- pango_layout_set_attributes(dinfo->layout, layout_attr);
- pango_layout_context_changed(dinfo->layout);
- pango_attr_list_unref(layout_attr);
- }
+ /* If printing requested, draw layout */
+ if (do_print)
+ {
+ pango_cairo_show_layout(cr, dinfo->layout);
+ gint yend;
+ pango_layout_get_size(dinfo->layout, NULL, ¥d);
+ if (printing_prefs.print_line_numbers)
+ {
+ PangoLayoutIter *iter;
+ PangoLayoutLine *line;
+ GString *linenumbers = g_string_sized_new(256);
+ for (iter = pango_layout_get_iter(dinfo->layout);; pango_layout_iter_next_line(iter))
+ {
+ line = pango_layout_iter_get_line_readonly(iter);
+ if (line->is_paragraph_start)
+ {
+ if (dinfo->long_line)
+ {
+ dinfo->long_line = FALSE;
+ g_string_append(linenumbers, "\n");
+ }
+ else
+ {
+ /*Show lineno*/
+ g_string_append_printf(linenumbers, "%d \n", dinfo->cur_line);
+ dinfo->cur_line++;
+ }
+ }
+ else
+ {
+ g_string_append(linenumbers, "\n");
+ }
+ if (pango_layout_iter_at_last_line(iter))
+ break;
+ }
+ pango_layout_iter_free(iter);
+ cairo_move_to(cr, 0, ystart);
+ pango_layout_set_attributes(dinfo->layout, NULL);
+ pango_layout_set_text(dinfo->layout, linenumbers->str, -1);
+ pango_layout_set_alignment(dinfo->layout, PANGO_ALIGN_RIGHT);
+ pango_layout_set_width(dinfo->layout, dinfo->max_line_number_margin * PANGO_SCALE);
+ pango_cairo_show_layout(cr, dinfo->layout);
- cairo_get_current_point(cr, &x, &y);
+ /* Vertical line between numbers and text */
+ cairo_set_line_width(cr, 0.3);
+ gint line_x = dinfo->max_line_number_margin * (dinfo->max_line_number_arity + 1.5)/(dinfo->max_line_number_arity + 2);
+ cairo_move_to(cr, line_x, ystart-2);
+ cairo_rel_line_to(cr, 0, yend / PANGO_SCALE);
+ cairo_stroke(cr);
+ }
+ if (printing_prefs.print_page_numbers)
+ {
+ pango_layout_set_width(dinfo->layout, gtk_print_context_get_width(context) * PANGO_SCALE);
+ gchar *line = g_strdup_printf("- %d -", dinfo->cur_page + 1);
+ pango_layout_set_markup(dinfo->layout, line, -1);
+ pango_layout_set_alignment(dinfo->layout, PANGO_ALIGN_CENTER);
+ cairo_move_to(cr, 0, height - dinfo->line_height);
+ pango_cairo_show_layout(cr, dinfo->layout);
+ g_free(line);
+ }
+ }
+ dinfo->cur_page++;
+ dinfo->long_line = long_line;
+ /* Free memory */
+ pango_attr_list_unref(layout_attr);
+ g_string_free(str, TRUE);
+}
- /* normal line break at eol character in document */
- if (at_eol)
- {
- /*pango_layout_get_size(dinfo->layout, NULL, &layout_h);*/
- /*cairo_move_to(cr, 0, y + (gdouble)layout_h / PANGO_SCALE);*/
- cairo_move_to(cr, 0, y + dinfo->line_height);
- count++;
- /* we added a new document line so request a new line number */
- add_linenumber = TRUE;
- }
- else
- {
- gint x_offset = 0;
- /* maybe we need to force a line break because of too long line */
- if (x >= (width - dinfo->font_width))
- {
- /* don't start the line at horizontal origin because we need to skip the
- * line number margin */
- if (printing_prefs.print_line_numbers)
- {
- x_offset = (dinfo->max_line_number_margin + 1) * dinfo->font_width;
- }
- /*pango_layout_get_size(dinfo->layout, NULL, &layout_h);*/
- /*cairo_move_to(cr, x_offset, y + (gdouble)layout_h / PANGO_SCALE);*/
- /* this is faster but not exactly the same as above */
- cairo_move_to(cr, x_offset, y + dinfo->line_height);
- cairo_get_current_point(cr, &x, &y);
- count++;
- }
- if (count < dinfo->lines_per_page)
- {
- /* str->len is counted in bytes not characters, so use g_utf8_strlen() */
- x_offset = (g_utf8_strlen(str->str, -1) * dinfo->font_width);
+static void draw_page(GtkPrintOperation *operation, GtkPrintContext *context,
+ gint page_nr, gpointer user_data)
+ {
+ DocInfo *dinfo = user_data;
- if (dinfo->long_line && count == 0)
- {
- x_offset = (dinfo->max_line_number_margin + 1) * dinfo->font_width;
- dinfo->long_line = FALSE;
- }
+ if (dinfo == NULL || page_nr >= dinfo->n_pages)
+ return;
- pango_cairo_show_layout(cr, dinfo->layout);
- cairo_move_to(cr, x + x_offset, y);
- }
- else
- /* we are on a wrapped line but we are out of lines on this page, so continue
- * the current line on the next page and remember to continue in current line */
- dinfo->long_line = TRUE;
- }
- }
- }
+ process_page(context, dinfo, TRUE);
- if (printing_prefs.print_line_numbers)
- { /* print a thin line between the line number margin and the data */
- gint y_start = 0;
-
- if (printing_prefs.print_page_header)
- y_start = (dinfo->line_height * 3) - 2; /* "- 2": to connect the line number line to
- * the page header frame */
-
- cairo_set_line_width(cr, 0.3);
- cairo_move_to(cr, (dinfo->max_line_number_margin * dinfo->font_width) + 1, y_start);
- cairo_line_to(cr, (dinfo->max_line_number_margin * dinfo->font_width) + 1,
- y + dinfo->line_height); /* y is last added line, we reuse it */
- cairo_stroke(cr);
+ if (dinfo->n_pages > 0)
+ {
+ gdouble fraction = (page_nr + 1) / (gdouble) dinfo->n_pages;
+ gchar *text = g_strdup_printf(_("Page %d of %d"), page_nr, dinfo->n_pages);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(main_widgets.progressbar), fraction);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(main_widgets.progressbar), text);
+ g_free(text);
}
- if (printing_prefs.print_page_numbers)
- {
- gchar *line = g_strdup_printf("- %d -", page_nr + 1);
- pango_layout_set_markup(dinfo->layout, line, -1);
- pango_layout_set_alignment(dinfo->layout, PANGO_ALIGN_CENTER);
- cairo_move_to(cr, 0, height - dinfo->line_height);
- pango_cairo_show_layout(cr, dinfo->layout);
- g_free(line);
-
#ifdef GEANY_PRINT_DEBUG
- cairo_set_line_width(cr, 0.3);
- cairo_move_to(cr, 0, height - (1.25 * dinfo->line_height));
- cairo_line_to(cr, width - 1, height - (1.25 * dinfo->line_height));
- cairo_stroke(cr);
+ geany_debug("draw_page = %d, pages = %d, (real) lines_per_page = %d",
+ page_nr, dinfo->n_pages, dinfo->lines_per_page);
#endif
+
}
- g_string_free(str, TRUE);
-}
static void status_changed(GtkPrintOperation *op, gpointer data)