Kitlist  1.1.0
printing.cpp
Go to the documentation of this file.
1 /*
2 
3  This file is part of Kitlist, a program to maintain a simple list
4  of items and assign items to one or more categories.
5 
6  Copyright (C) 2008,2009 Frank Dean
7 
8  Kitlist is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  Kitlist is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with Kitlist. If not, see <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #include "printing.hpp"
24 #include <sstream>
25 #include <glibmm/i18n.h>
26 #include <config.h>
27 
29 const int PAGE_TOLERANCE = 40;
30 
32 const int HEADER_SPACING = 10;
33 
35 const int FOOTER_SPACING = 10;
36 
38 const int BORDER_SPACING = 10;
39 
41 const Glib::ustring FOOTER_TEXT = _("Page %1 of %2");
42 
45 // g_debug("~KitPrintOperation()");
46  if (m_items)
47  delete m_items;
48 }
49 
51 Glib::RefPtr<KitPrintOperation> KitPrintOperation::create() {
52  return Glib::RefPtr<KitPrintOperation>(new KitPrintOperation());
53 }
54 
60 layout_refptr KitPrintOperation::new_header(const Glib::RefPtr<Gtk::PrintContext>& context) {
61  Pango::FontDescription header_font("sans 16");
62  layout_refptr retval = context->create_pango_layout();
63  retval->set_font_description(header_font);
64  retval->set_markup(Glib::ustring::compose("<b>%1</b>", m_page_title));
65  retval->set_alignment(Pango::ALIGN_CENTER);
66  retval->set_width(static_cast<int>(context->get_width() * Pango::SCALE));
67  m_ref_headers.push_back(retval);
68  return retval;
69 }
70 
76 layout_refptr KitPrintOperation::new_footer(const Glib::RefPtr<Gtk::PrintContext>& context) {
77  Pango::FontDescription font_desc("sans 12");
78  layout_refptr retval = context->create_pango_layout();
79  retval->set_font_description(font_desc);
80  Glib::ustring footer = FOOTER_TEXT;
81  retval->set_text(footer);
82  retval->set_alignment(Pango::ALIGN_CENTER);
83  retval->set_width(static_cast<int>(context->get_width() * Pango::SCALE));
84  m_ref_footers.push_back(retval);
85  return retval;
86 }
87 
93 void KitPrintOperation::on_begin_print(const Glib::RefPtr<Gtk::PrintContext>& context) {
94  Pango::FontDescription font_desc("sans 12");
95  layout_refptr ref_footer = new_footer(context);
96  layout_refptr ref_header = new_header(context);
97  m_ref_layout = context->create_pango_layout();
98  const double width = context->get_width();
99  double height = context->get_height();
100  m_ref_layout->set_width(static_cast<int>(width * Pango::SCALE));
101 
102  // Decrement remaining height to allow for header and footer height");
103  int w;
104  int h;
105  ref_header->get_size(w, h);
106  height -= static_cast<int>(h / Pango::SCALE);
107  ref_footer->get_size(w, h);
108  height -= static_cast<int>(h / Pango::SCALE);
109  // Knock off a little more to allow for spacing between the body, header and footer
111 
112  // Build up all the text to be printed on the page
113  Glib::ustring page_text;
114  for (ItemIter i = m_items->begin(); i != m_items->end(); ++i) {
115  Item* item = *i;
116  page_text += item->get_description() + "\n";
117  }
118  m_ref_layout->set_text(page_text);
119  const int line_count = m_ref_layout->get_line_count();
120 
121  Glib::RefPtr<Pango::LayoutLine> layout_line;
122  double page_height = 0;
123  for (int line = 0; line < line_count; ++line) {
124  Pango::Rectangle ink_rect, logical_rect;
125  layout_line = m_ref_layout->get_line(line);
126  layout_line->get_extents(ink_rect, logical_rect);
127  const double line_height = logical_rect.get_height() / Pango::SCALE;
128 
129  if (page_height + line_height > height) {
130  // Start a new page
131  page_height = 0;
132  // Save the current line number and header and footer elements
133  m_page_breaks.push_back(line);
134  ref_header = new_header(context);
135  ref_footer = new_footer(context);
136  }
137  page_height += line_height;
138  }
139 
140  const int n_pages = m_page_breaks.size() + 1;
141  set_n_pages(n_pages);
142 
143  // rewrite page numbers in the footers
144  for (int page_number = 0; page_number < n_pages; page_number++) {
145  // page number x of n pages
146  Glib::ustring footer = Glib::ustring::compose(FOOTER_TEXT,
147  page_number + 1,
148  n_pages);
149  m_ref_footers[page_number]->set_text(footer);
150  }
151 }
152 
159 void KitPrintOperation::on_draw_page(const Glib::RefPtr<Gtk::PrintContext>& context, int page_number) {
160  int start_page_line = 0;
161  int end_page_line = 0;
162 
163  if (page_number == 0) {
164  start_page_line = 0;
165  } else {
166  start_page_line = m_page_breaks[page_number - 1];
167  }
168 
169  if (page_number < static_cast<int>(m_page_breaks.size())) {
170  end_page_line = m_page_breaks[page_number];
171  } else {
172  end_page_line = m_ref_layout->get_line_count();
173  }
174 
175 // g_debug("Printing lines from %d to %d", start_page_line, end_page_line);
176  Cairo::RefPtr<Cairo::Context> cairo_ctx = context->get_cairo_context();
177  cairo_ctx->set_source_rgb(0, 0, 0);
178 
179  Pango::LayoutIter iter;
180  double start_pos = 0;
181  int line_index = 0;
182  double header_height = 0;
183 
184  // Header
185  layout_refptr ref_header = m_ref_headers[page_number];
186  ref_header->get_iter(iter);
187  do {
188  Glib::RefPtr<Pango::LayoutLine> header_line = iter.get_line();
189  Pango::Rectangle hr = iter.get_line_logical_extents();
190  int bl = iter.get_baseline();
191  double hx = hr.get_x() / Pango::SCALE;
192  double hy = bl / Pango::SCALE;
193  header_height = hy;
194  cairo_ctx->move_to(hx, hy);
195  header_line->show_in_cairo_context(cairo_ctx);
196  } while (iter.next_line());
197 
198  // Horizontal line between header and body
199  cairo_ctx->set_line_width(0.5);
200  cairo_ctx->set_line_cap(Cairo::LINE_CAP_SQUARE);
201  cairo_ctx->set_line_join(Cairo::LINE_JOIN_MITER);
202  header_height += BORDER_SPACING;
203  cairo_ctx->move_to(0.0, header_height);
204  cairo_ctx->line_to(context->get_width(), header_height);
205  cairo_ctx->stroke();
206  header_height += HEADER_SPACING;
207 
208  // Page body
209  m_ref_layout->get_iter(iter);
210  do {
211  if (line_index >= start_page_line) {
212  Glib::RefPtr<Pango::LayoutLine> layout_line = iter.get_line();
213  Pango::Rectangle logical_rect = iter.get_line_logical_extents();
214  int baseline = iter.get_baseline();
215  if (line_index == start_page_line) {
216  start_pos = logical_rect.get_y() / Pango::SCALE;
217  }
218  double x = logical_rect.get_x() / Pango::SCALE;
219  double y = baseline / Pango::SCALE - start_pos + header_height;
220  cairo_ctx->move_to(x, y);
221  layout_line->show_in_cairo_context(cairo_ctx);
222  }
223  line_index++;
224  } while (line_index < end_page_line && iter.next_line());
225 
226  // Footer
227  layout_refptr ref_footer = m_ref_footers[page_number];
228  int w;
229  int h;
230  ref_footer->get_size(w, h);
231  start_pos = context->get_height() - h / Pango::SCALE - BORDER_SPACING;
232 
233  // Horizontal line between body and footer
234  cairo_ctx->set_source_rgb(0, 0, 0);
235  cairo_ctx->set_line_width(0.5);
236  cairo_ctx->set_line_cap(Cairo::LINE_CAP_SQUARE);
237  cairo_ctx->set_line_join(Cairo::LINE_JOIN_MITER);
238  cairo_ctx->move_to(0.0, start_pos);
239  cairo_ctx->line_to(context->get_width(), start_pos);
240  cairo_ctx->stroke();
241  start_pos += BORDER_SPACING;
242 
243  ref_footer->get_iter(iter);
244  do {
245  Glib::RefPtr<Pango::LayoutLine> footer_line = iter.get_line();
246  Pango::Rectangle fr = iter.get_line_logical_extents();
247  int bl = iter.get_baseline();
248  double fx = fr.get_x() / Pango::SCALE;
249  double fy = bl / Pango::SCALE + start_pos;
250  cairo_ctx->move_to(fx, fy);
251  footer_line->show_in_cairo_context(cairo_ctx);
252  } while (iter.next_line());
253 
254 }
Prints the kitlist.
Definition: printing.hpp:35
virtual void on_begin_print(const Glib::RefPtr< Gtk::PrintContext > &context)
Definition: printing.cpp:93
ItemContainer::iterator ItemIter
Definition: item.hpp:92
layout_refptr new_header(const Glib::RefPtr< Gtk::PrintContext > &context)
Definition: printing.cpp:60
std::vector< int > m_page_breaks
A list of line numbers where a page break is required.
Definition: printing.hpp:52
std::vector< layout_refptr > m_ref_footers
A list of footers, one for each page.
Definition: printing.hpp:56
const int FOOTER_SPACING
The spacing between the footer and the body.
Definition: printing.cpp:35
layout_refptr new_footer(const Glib::RefPtr< Gtk::PrintContext > &context)
Definition: printing.cpp:76
std::vector< layout_refptr > m_ref_headers
A list of headers, one for each page.
Definition: printing.hpp:54
const int PAGE_TOLERANCE
Seems calculating page body height is inaccurate.
Definition: printing.cpp:29
Represents an Item.
Definition: item.hpp:37
virtual void on_draw_page(const Glib::RefPtr< Gtk::PrintContext > &context, int page_number)
Definition: printing.cpp:159
std::string get_description()
Definition: item.hpp:48
static Glib::RefPtr< KitPrintOperation > create()
Factory to create instances.
Definition: printing.cpp:51
Glib::RefPtr< Pango::Layout > layout_refptr
Definition: printing.hpp:30
const Glib::ustring FOOTER_TEXT
Text used to print page number in footer - Page x of n.
Definition: printing.cpp:41
layout_refptr m_ref_layout
A layout to hold the body of the entire kitlist to be printed.
Definition: printing.hpp:50
const int BORDER_SPACING
The space above and below the horizontal lines at the top and bottom of each page.
Definition: printing.cpp:38
Glib::ustring m_page_title
Definition: printing.hpp:37
ItemContainer * m_items
Definition: printing.hpp:36
const int HEADER_SPACING
The spacing between the header and the body.
Definition: printing.cpp:32
~KitPrintOperation()
Destructor.
Definition: printing.cpp:44

Copyright 2008-2021 Frank Dean