1 /*
2 SPDX-FileCopyrightText: 2002-2010 Anders Lund <anders@alweb.dk>
3
4 Rewritten based on code of:
5 SPDX-FileCopyrightText: 2002 Michael Goffioul <kdeprint@swing.be>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10 #include "printpainter.h"
11
12 #include "katebuffer.h"
13 #include "katedocument.h"
14 #include "katehighlight.h"
15 #include "katepartdebug.h"
16 #include "katerenderer.h"
17 #include "katetextfolding.h"
18 #include "katetextlayout.h"
19 #include "kateview.h"
20
21 #include <KLocalizedString>
22 #include <KUser>
23
24 #include <QPainter>
25 #include <QPrinter>
26
27 using namespace KatePrinter;
28
29 class KatePrinter::PageLayout
30 {
31 public:
PageLayout()32 PageLayout()
33 : headerTagList()
34 , footerTagList()
35 , selectionRange()
36 {
37 }
38
39 uint pageWidth = 0;
40 uint pageHeight = 0;
41 uint headerWidth = 0;
42 uint maxWidth = 0;
43 uint maxHeight = 0;
44 int xstart = 0; // beginning point for painting lines
45 int innerMargin = 0;
46
47 bool selectionOnly = false;
48
49 uint firstline = 0;
50 uint lastline = 0;
51
52 // Header/Footer Page
53 uint headerHeight = 0;
54 QStringList headerTagList;
55 uint footerHeight = 0;
56 QStringList footerTagList;
57
58 KTextEditor::Range selectionRange;
59 };
60
PrintPainter(KTextEditor::DocumentPrivate * doc,KTextEditor::ViewPrivate * view)61 PrintPainter::PrintPainter(KTextEditor::DocumentPrivate *doc, KTextEditor::ViewPrivate *view)
62 : m_view(view)
63 , m_doc(doc)
64 , m_printGuide(false)
65 , m_printLineNumbers(false)
66 , m_useHeader(false)
67 , m_useFooter(false)
68 , m_useBackground(false)
69 , m_useBox(false)
70 , m_useHeaderBackground(false)
71 , m_useFooterBackground(false)
72 , m_boxMargin(0)
73 , m_boxWidth(1)
74 , m_boxColor(Qt::black)
75 , m_headerBackground(Qt::lightGray)
76 , m_headerForeground(Qt::black)
77 , m_footerBackground(Qt::lightGray)
78 , m_footerForeground(Qt::black)
79 , m_fhFont()
80 , m_headerFormat()
81 , m_footerFormat()
82 {
83 m_folding = new Kate::TextFolding(m_doc->buffer());
84
85 m_renderer = new KateRenderer(m_doc, *m_folding, m_view);
86 m_renderer->setPrinterFriendly(true);
87
88 updateCache();
89 }
90
~PrintPainter()91 PrintPainter::~PrintPainter()
92 {
93 delete m_renderer;
94 delete m_folding;
95 }
96
setUseBox(const bool on)97 void PrintPainter::setUseBox(const bool on)
98 {
99 m_useBox = on;
100 setBoxWidth(m_boxWidth); // reset the width
101 }
102
setBoxWidth(const int width)103 void PrintPainter::setBoxWidth(const int width)
104 {
105 if (m_useBox) {
106 m_boxWidth = width;
107 if (width < 1) {
108 m_boxWidth = 1;
109 }
110 } else {
111 m_boxWidth = 0;
112 }
113 }
114
setBoxColor(const QColor & color)115 void PrintPainter::setBoxColor(const QColor &color)
116 {
117 if (color.isValid()) {
118 m_boxColor = color;
119 }
120 }
121
setHeaderBackground(const QColor & color)122 void PrintPainter::setHeaderBackground(const QColor &color)
123 {
124 if (color.isValid()) {
125 m_headerBackground = color;
126 }
127 }
128
setHeaderForeground(const QColor & color)129 void PrintPainter::setHeaderForeground(const QColor &color)
130 {
131 if (color.isValid()) {
132 m_headerForeground = color;
133 }
134 }
135
setFooterBackground(const QColor & color)136 void PrintPainter::setFooterBackground(const QColor &color)
137 {
138 if (color.isValid()) {
139 m_footerBackground = color;
140 }
141 }
setFooterForeground(const QColor & color)142 void PrintPainter::setFooterForeground(const QColor &color)
143 {
144 if (color.isValid()) {
145 m_footerForeground = color;
146 }
147 }
148
setColorScheme(const QString & scheme)149 void PrintPainter::setColorScheme(const QString &scheme)
150 {
151 // directly set that for the renderer
152 m_renderer->config()->setSchema(scheme);
153
154 // changed renderer requires cache updates
155 updateCache();
156 }
157
updateCache()158 void PrintPainter::updateCache()
159 {
160 m_fontHeight = m_renderer->fontHeight();
161
162 // figure out the horizontal space required
163 QString s = QStringLiteral("%1 ").arg(m_doc->lines());
164 s.fill(QLatin1Char('5'), -1); // some non-fixed fonts haven't equally wide numbers
165 // FIXME calculate which is actually the widest...
166 m_lineNumberWidth = m_renderer->currentFontMetrics().boundingRect(s).width();
167 }
168
paint(QPrinter * printer) const169 void PrintPainter::paint(QPrinter *printer) const
170 {
171 QPainter painter(printer);
172 PageLayout pl;
173
174 configure(printer, pl);
175
176 uint lineCount = pl.firstline;
177 uint y = 0;
178 uint currentPage = (printer->fromPage() == 0) ? 1 : printer->fromPage();
179 bool pageStarted = true;
180 uint remainder = 0;
181
182 // On to draw something :-)
183 while (lineCount <= pl.lastline) {
184 if (y + m_fontHeight > pl.maxHeight) {
185 if ((int)currentPage == printer->toPage()) { // we've reached the page break of last page to be printed
186 break;
187 }
188 printer->newPage();
189 painter.resetTransform();
190 currentPage++;
191 pageStarted = true;
192 y = 0;
193 }
194
195 if (pageStarted) {
196 qCDebug(LOG_KTE) << "Starting new page," << lineCount << "lines up to now.";
197 paintNewPage(painter, currentPage, y, pl);
198 pageStarted = false;
199 painter.translate(pl.xstart, y);
200 }
201
202 if (m_printLineNumbers /*&& ! startCol*/) { // don't repeat!
203 paintLineNumber(painter, lineCount, pl);
204 }
205
206 paintLine(painter, lineCount, y, remainder, pl);
207
208 if (!remainder) {
209 lineCount++;
210 }
211 }
212
213 painter.end();
214 }
215
configure(const QPrinter * printer,PageLayout & pl) const216 void PrintPainter::configure(const QPrinter *printer, PageLayout &pl) const
217 {
218 pl.pageHeight = printer->height();
219 pl.pageWidth = printer->width();
220 pl.headerWidth = printer->width();
221 pl.innerMargin = m_useBox ? m_boxMargin : 6;
222 pl.maxWidth = printer->width();
223 pl.maxHeight = (m_useBox ? printer->height() - pl.innerMargin : printer->height());
224 pl.selectionOnly = (printer->printRange() == QPrinter::Selection);
225 pl.lastline = m_doc->lastLine();
226
227 if (m_view && pl.selectionOnly) {
228 // set a line range from the first selected line to the last
229 pl.selectionRange = m_view->selectionRange();
230 pl.firstline = pl.selectionRange.start().line();
231 pl.lastline = pl.selectionRange.end().line();
232 }
233
234 if (m_printLineNumbers) {
235 // a small space between the line numbers and the text
236 int _adj = m_renderer->currentFontMetrics().horizontalAdvance(QStringLiteral("5"));
237 // adjust available width and set horizontal start point for data
238 pl.maxWidth -= m_lineNumberWidth + _adj;
239 pl.xstart += m_lineNumberWidth + _adj;
240 }
241
242 if (m_useHeader || m_useFooter) {
243 // Set up a tag map
244 // This retrieves all tags, used or not, but
245 // none of these operations should be expensive,
246 // and searching each tag in the format strings is avoided.
247 QDateTime dt = QDateTime::currentDateTime();
248 QMap<QString, QString> tags;
249
250 KUser u(KUser::UseRealUserID);
251 tags[QStringLiteral("u")] = u.loginName();
252
253 tags[QStringLiteral("d")] = QLocale().toString(dt, QLocale::ShortFormat);
254 tags[QStringLiteral("D")] = QLocale().toString(dt, QLocale::LongFormat);
255 tags[QStringLiteral("h")] = QLocale().toString(dt.time(), QLocale::ShortFormat);
256 tags[QStringLiteral("y")] = QLocale().toString(dt.date(), QLocale::ShortFormat);
257 tags[QStringLiteral("Y")] = QLocale().toString(dt.date(), QLocale::LongFormat);
258 tags[QStringLiteral("f")] = m_doc->url().fileName();
259 tags[QStringLiteral("U")] = m_doc->url().toString();
260 if (pl.selectionOnly) {
261 QString s(i18n("(Selection of) "));
262 tags[QStringLiteral("f")].prepend(s);
263 tags[QStringLiteral("U")].prepend(s);
264 }
265
266 static const QRegularExpression reTags(QStringLiteral("%([dDfUhuyY])")); // TODO check for "%%<TAG>"
267
268 if (m_useHeader) {
269 pl.headerHeight = QFontMetrics(m_fhFont).height();
270 if (m_useBox || m_useHeaderBackground) {
271 pl.headerHeight += pl.innerMargin * 2;
272 } else {
273 pl.headerHeight += 1 + QFontMetrics(m_fhFont).leading();
274 }
275
276 pl.headerTagList = m_headerFormat;
277 QMutableStringListIterator it(pl.headerTagList);
278 while (it.hasNext()) {
279 QString tag = it.next();
280 QRegularExpressionMatch match;
281 int pos = tag.indexOf(reTags, 0, &match);
282 QString rep;
283 while (pos > -1) {
284 rep = tags[match.captured(1)];
285 tag.replace((uint)pos, 2, rep);
286 pos += rep.length();
287 pos = tag.indexOf(reTags, pos, &match);
288 }
289 it.setValue(tag);
290 }
291 }
292
293 if (m_useFooter) {
294 pl.footerHeight = QFontMetrics(m_fhFont).height();
295 if (m_useBox || m_useFooterBackground) {
296 pl.footerHeight += 2 * pl.innerMargin;
297 } else {
298 pl.footerHeight += 1; // line only
299 }
300
301 pl.footerTagList = m_footerFormat;
302 QMutableStringListIterator it(pl.footerTagList);
303 while (it.hasNext()) {
304 QString tag = it.next();
305 QRegularExpressionMatch match;
306 int pos = tag.indexOf(reTags, 0, &match);
307 QString rep;
308 while (pos > -1) {
309 rep = tags[match.captured(1)];
310 tag.replace((uint)pos, 2, rep);
311 pos += rep.length();
312 pos = tag.indexOf(reTags, pos, &match);
313 }
314 it.setValue(tag);
315 }
316
317 pl.maxHeight -= pl.footerHeight;
318 }
319 } // if ( useHeader || useFooter )
320
321 if (m_useBackground) {
322 if (!m_useBox) {
323 pl.xstart += pl.innerMargin;
324 pl.maxWidth -= pl.innerMargin * 2;
325 }
326 }
327
328 if (m_useBox) {
329 // set maxwidth to something sensible
330 pl.maxWidth -= (m_boxWidth + pl.innerMargin) * 2;
331 pl.xstart += m_boxWidth + pl.innerMargin;
332 // maxheight too..
333 pl.maxHeight -= m_boxWidth;
334 }
335
336 int pageHeight = pl.maxHeight;
337 if (m_useHeader) {
338 pageHeight -= pl.headerHeight + pl.innerMargin;
339 }
340 if (m_useFooter) {
341 pageHeight -= pl.footerHeight + pl.innerMargin;
342 }
343
344 const int linesPerPage = pageHeight / m_fontHeight;
345
346 if (printer->fromPage() > 0) {
347 pl.firstline = (printer->fromPage() - 1) * linesPerPage;
348 }
349
350 // now that we know the vertical amount of space needed,
351 // it is possible to calculate the total number of pages
352 // if needed, that is if any header/footer tag contains "%P".
353 if (!pl.headerTagList.filter(QStringLiteral("%P")).isEmpty() || !pl.footerTagList.filter(QStringLiteral("%P")).isEmpty()) {
354 qCDebug(LOG_KTE) << "'%P' found! calculating number of pages...";
355
356 // calculate total layouted lines in the document
357 int totalLines = 0;
358 // TODO: right now ignores selection printing
359 for (unsigned int i = pl.firstline; i <= pl.lastline; ++i) {
360 KateLineLayoutPtr rangeptr(new KateLineLayout(*m_renderer));
361 rangeptr->setLine(i);
362 m_renderer->layoutLine(rangeptr, (int)pl.maxWidth, false);
363 totalLines += rangeptr->viewLineCount();
364 }
365
366 const int totalPages = (totalLines / linesPerPage) + ((totalLines % linesPerPage) > 0 ? 1 : 0);
367
368 // TODO: add space for guide if required
369 // if ( useGuide )
370 // _lt += (guideHeight + (fontHeight /2)) / fontHeight;
371
372 // substitute both tag lists
373 QString re(QStringLiteral("%P"));
374 QStringList::Iterator it;
375
376 for (it = pl.headerTagList.begin(); it != pl.headerTagList.end(); ++it) {
377 it->replace(re, QString::number(totalPages));
378 }
379
380 for (it = pl.footerTagList.begin(); it != pl.footerTagList.end(); ++it) {
381 (*it).replace(re, QString::number(totalPages));
382 }
383 }
384 }
385
paintNewPage(QPainter & painter,const uint currentPage,uint & y,const PageLayout & pl) const386 void PrintPainter::paintNewPage(QPainter &painter, const uint currentPage, uint &y, const PageLayout &pl) const
387 {
388 if (m_useHeader) {
389 paintHeader(painter, currentPage, y, pl);
390 }
391
392 if (m_useFooter) {
393 paintFooter(painter, currentPage, pl);
394 }
395
396 if (m_useBackground) {
397 paintBackground(painter, y, pl);
398 }
399
400 if (m_useBox) {
401 paintBox(painter, y, pl);
402 }
403
404 if (m_printGuide && currentPage == 1) {
405 paintGuide(painter, y, pl);
406 }
407 }
408
paintHeader(QPainter & painter,const uint currentPage,uint & y,const PageLayout & pl) const409 void PrintPainter::paintHeader(QPainter &painter, const uint currentPage, uint &y, const PageLayout &pl) const
410 {
411 painter.save();
412 painter.setPen(QPen(m_headerForeground, 0.5));
413 painter.setFont(m_fhFont);
414
415 if (m_useHeaderBackground) {
416 painter.fillRect(0, 0, pl.headerWidth, pl.headerHeight, m_headerBackground);
417 }
418
419 if (pl.headerTagList.count() == 3) {
420 int valign = (m_useBox || m_useHeaderBackground || m_useBackground) ? Qt::AlignVCenter : Qt::AlignTop;
421 int align = valign | Qt::AlignLeft;
422 int marg = (m_useBox || m_useHeaderBackground) ? pl.innerMargin : 0;
423 if (m_useBox) {
424 marg += m_boxWidth;
425 }
426
427 QString s;
428 for (int i = 0; i < 3; i++) {
429 s = pl.headerTagList[i];
430 if (s.indexOf(QLatin1String("%p")) != -1) {
431 s.replace(QLatin1String("%p"), QString::number(currentPage));
432 }
433
434 painter.drawText(marg, 0, pl.headerWidth - (marg * 2), pl.headerHeight, align, s);
435 align = valign | (i == 0 ? Qt::AlignHCenter : Qt::AlignRight);
436 }
437 }
438
439 if (!(m_useHeaderBackground || m_useBox || m_useBackground)) { // draw a 1 px (!?) line to separate header from contents
440 painter.drawLine(0, pl.headerHeight - 1, pl.headerWidth, pl.headerHeight - 1);
441 // y += 1; now included in headerHeight
442 }
443
444 painter.restore();
445
446 y += pl.headerHeight + pl.innerMargin;
447 }
448
paintFooter(QPainter & painter,const uint currentPage,const PageLayout & pl) const449 void PrintPainter::paintFooter(QPainter &painter, const uint currentPage, const PageLayout &pl) const
450 {
451 painter.save();
452 painter.setPen(QPen(m_footerForeground, 0.5));
453 painter.setFont(m_fhFont);
454
455 if (!(m_useFooterBackground || m_useBox || m_useBackground)) { // draw a 1 px (!?) line to separate footer from contents
456 painter.drawLine(0, pl.pageHeight - pl.footerHeight - 1, pl.headerWidth, pl.pageHeight - pl.footerHeight - 1);
457 }
458 if (m_useFooterBackground) {
459 painter.fillRect(0, pl.pageHeight - pl.footerHeight, pl.headerWidth, pl.footerHeight, m_footerBackground);
460 }
461
462 if (pl.footerTagList.count() == 3) {
463 int align = Qt::AlignVCenter | Qt::AlignLeft;
464 int marg = (m_useBox || m_useFooterBackground) ? pl.innerMargin : 0;
465 if (m_useBox) {
466 marg += m_boxWidth;
467 }
468
469 QString s;
470 for (int i = 0; i < 3; i++) {
471 s = pl.footerTagList[i];
472 if (s.indexOf(QLatin1String("%p")) != -1) {
473 s.replace(QLatin1String("%p"), QString::number(currentPage));
474 }
475 painter.drawText(marg, pl.pageHeight - pl.footerHeight, pl.headerWidth - (marg * 2), pl.footerHeight, align, s);
476 align = Qt::AlignVCenter | (i == 0 ? Qt::AlignHCenter : Qt::AlignRight);
477 }
478 }
479 painter.restore();
480 }
481
paintGuide(QPainter & painter,uint & y,const PageLayout & pl) const482 void PrintPainter::paintGuide(QPainter &painter, uint &y, const PageLayout &pl) const
483 {
484 // FIXME - this may span more pages...
485 // draw a box unless we have boxes, in which case we end with a box line
486 int _ystart = y;
487 QString _hlName = m_doc->highlight()->name();
488
489 // list of highlight attributes for the legend
490 const auto _attributes = m_doc->highlight()->attributesForDefinition(m_renderer->config()->schema());
491 const QColor _defaultPen = _attributes.at(0)->foreground().color();
492
493 painter.save();
494 painter.setPen(_defaultPen);
495
496 int _marg = 0;
497 if (m_useBox) {
498 _marg += (2 * m_boxWidth) + (2 * pl.innerMargin);
499 } else {
500 if (m_useBackground) {
501 _marg += 2 * pl.innerMargin;
502 }
503 _marg += 1;
504 y += 1 + pl.innerMargin;
505 }
506
507 // draw a title string
508 QFont _titleFont = m_renderer->currentFont();
509 _titleFont.setBold(true);
510 painter.setFont(_titleFont);
511 QRect _r;
512 painter.drawText(QRect(_marg, y, pl.pageWidth - (2 * _marg), pl.maxHeight - y),
513 Qt::AlignTop | Qt::AlignHCenter,
514 i18n("Typographical Conventions for %1", _hlName),
515 &_r);
516 const int _w = pl.pageWidth - (_marg * 2) - (pl.innerMargin * 2);
517 const int _x = _marg + pl.innerMargin;
518 y += _r.height() + pl.innerMargin;
519 painter.drawLine(_x, y, _x + _w, y);
520 y += 1 + pl.innerMargin;
521
522 int _widest(0);
523 for (const KTextEditor::Attribute::Ptr &attribute : std::as_const(_attributes)) {
524 const QString _name = attribute->name().section(QLatin1Char(':'), 1, 1);
525 _widest = qMax(QFontMetrics(attribute->font()).boundingRect(_name).width(), _widest);
526 }
527
528 const int _guideCols = _w / (_widest + pl.innerMargin);
529
530 // draw attrib names using their styles
531 const int _cw = _w / _guideCols;
532 int _i = 0;
533
534 _titleFont.setUnderline(true);
535 QString _currentHlName;
536 for (const KTextEditor::Attribute::Ptr &attribute : std::as_const(_attributes)) {
537 QString _hl = attribute->name().section(QLatin1Char(':'), 0, 0);
538 QString _name = attribute->name().section(QLatin1Char(':'), 1, 1);
539 if (_hl != _hlName && _hl != _currentHlName) {
540 _currentHlName = _hl;
541 if (_i % _guideCols) {
542 y += m_fontHeight;
543 }
544 y += pl.innerMargin;
545 painter.setFont(_titleFont);
546 painter.setPen(_defaultPen);
547 painter.drawText(_x, y, _w, m_fontHeight, Qt::AlignTop, _hl + QLatin1Char(' ') + i18n("text"));
548 y += m_fontHeight;
549 _i = 0;
550 }
551
552 painter.setPen(attribute->foreground().color());
553 painter.setFont(attribute->font());
554
555 if (attribute->hasProperty(QTextFormat::BackgroundBrush)) {
556 QRect _rect = QFontMetrics(attribute->font()).boundingRect(_name);
557 _rect.moveTo(_x + ((_i % _guideCols) * _cw), y);
558 painter.fillRect(_rect, attribute->background());
559 }
560
561 painter.drawText((_x + ((_i % _guideCols) * _cw)), y, _cw, m_fontHeight, Qt::AlignTop, _name);
562
563 _i++;
564 if (_i && !(_i % _guideCols)) {
565 y += m_fontHeight;
566 }
567 }
568
569 if (_i % _guideCols) {
570 y += m_fontHeight; // last row not full
571 }
572 // draw a box around the legend
573 painter.setPen(_defaultPen);
574
575 if (m_useBox) {
576 painter.fillRect(0, y + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor);
577 } else {
578 _marg -= 1;
579 painter.drawRect(_marg, _ystart, pl.pageWidth - (2 * _marg), y - _ystart + pl.innerMargin);
580 }
581
582 painter.restore();
583
584 y += (m_useBox ? m_boxWidth : 1) + (pl.innerMargin * 2);
585 }
586
paintBox(QPainter & painter,uint & y,const PageLayout & pl) const587 void PrintPainter::paintBox(QPainter &painter, uint &y, const PageLayout &pl) const
588 {
589 painter.save();
590 painter.setPen(QPen(m_boxColor, m_boxWidth));
591 painter.drawRect(0, 0, pl.pageWidth, pl.pageHeight);
592
593 if (m_useHeader) {
594 painter.drawLine(0, pl.headerHeight, pl.headerWidth, pl.headerHeight);
595 } else {
596 y += pl.innerMargin;
597 }
598
599 if (m_useFooter) { // drawline is not trustable, grr.
600 painter.fillRect(0, pl.maxHeight + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor);
601 }
602
603 painter.restore();
604 }
605
paintBackground(QPainter & painter,const uint y,const PageLayout & pl) const606 void PrintPainter::paintBackground(QPainter &painter, const uint y, const PageLayout &pl) const
607 {
608 // If we have a box, or the header/footer has backgrounds, we want to paint
609 // to the border of those. Otherwise just the contents area.
610 int _y = y;
611 int _h = pl.maxHeight - y;
612 if (m_useBox) {
613 _y -= pl.innerMargin;
614 _h += 2 * pl.innerMargin;
615 } else {
616 if (m_useHeaderBackground) {
617 _y -= pl.innerMargin;
618 _h += pl.innerMargin;
619 }
620 if (m_useFooterBackground) {
621 _h += pl.innerMargin;
622 }
623 }
624 painter.fillRect(0, _y, pl.pageWidth, _h, m_renderer->config()->backgroundColor());
625 }
626
paintLine(QPainter & painter,const uint line,uint & y,uint & remainder,const PageLayout & pl) const627 void PrintPainter::paintLine(QPainter &painter, const uint line, uint &y, uint &remainder, const PageLayout &pl) const
628 {
629 // HA! this is where we print [part of] a line ;]]
630 KateLineLayoutPtr rangeptr(new KateLineLayout(*m_renderer));
631 rangeptr->setLine(line);
632 m_renderer->layoutLine(rangeptr, (int)pl.maxWidth, false);
633
634 // selectionOnly: clip non-selection parts and adjust painter position if needed
635 int _xadjust = 0;
636 if (pl.selectionOnly) {
637 if (m_view && m_view->blockSelection()) {
638 int _x = m_renderer->cursorToX(rangeptr->viewLine(0), pl.selectionRange.start());
639 int _x1 = m_renderer->cursorToX(rangeptr->viewLine(rangeptr->viewLineCount() - 1), pl.selectionRange.end());
640 _xadjust = _x;
641 painter.translate(-_xadjust, 0);
642 painter.setClipRegion(QRegion(_x, 0, _x1 - _x, rangeptr->viewLineCount() * m_fontHeight));
643
644 } else if (line == pl.firstline || line == pl.lastline) {
645 QRegion region(0, 0, pl.maxWidth, rangeptr->viewLineCount() * m_fontHeight);
646
647 if (line == pl.firstline) {
648 region = region.subtracted(QRegion(0, 0, m_renderer->cursorToX(rangeptr->viewLine(0), pl.selectionRange.start()), m_fontHeight));
649 }
650
651 if (line == pl.lastline) {
652 int _x = m_renderer->cursorToX(rangeptr->viewLine(rangeptr->viewLineCount() - 1), pl.selectionRange.end());
653 region = region.subtracted(QRegion(_x, 0, pl.maxWidth - _x, m_fontHeight));
654 }
655
656 painter.setClipRegion(region);
657 }
658 }
659
660 // If the line is too long (too many 'viewlines') to fit the remaining vertical space,
661 // clip and adjust the painter position as necessary
662 int _lines = rangeptr->viewLineCount(); // number of "sublines" to paint.
663
664 int proceedLines = _lines;
665 if (remainder) {
666 proceedLines = qMin((pl.maxHeight - y) / m_fontHeight, remainder);
667
668 painter.translate(0, -(_lines - int(remainder)) * m_fontHeight + 1);
669 painter.setClipRect(0,
670 (_lines - int(remainder)) * m_fontHeight + 1,
671 pl.maxWidth,
672 proceedLines * m_fontHeight); //### drop the crosspatch in printerfriendly mode???
673 remainder -= proceedLines;
674 } else if (y + m_fontHeight * _lines > pl.maxHeight) {
675 remainder = _lines - ((pl.maxHeight - y) / m_fontHeight);
676 painter.setClipRect(0, 0, pl.maxWidth, (_lines - int(remainder)) * m_fontHeight + 1); //### drop the crosspatch in printerfriendly mode???
677 } else if (!pl.selectionOnly) {
678 painter.setClipRegion(QRegion());
679 painter.setClipping(false);
680 }
681
682 m_renderer->paintTextLine(painter, rangeptr, 0, (int)pl.maxWidth);
683
684 painter.setClipping(false);
685 painter.translate(_xadjust, (m_fontHeight * (_lines - remainder)));
686
687 y += m_fontHeight * proceedLines;
688 }
689
paintLineNumber(QPainter & painter,const uint number,const PageLayout & pl) const690 void PrintPainter::paintLineNumber(QPainter &painter, const uint number, const PageLayout &pl) const
691 {
692 const int left = ((m_useBox || m_useBackground) ? pl.innerMargin : 0) - pl.xstart;
693
694 painter.save();
695 painter.setFont(m_renderer->currentFont());
696 painter.setPen(m_renderer->config()->lineNumberColor());
697 painter.drawText(left, 0, m_lineNumberWidth, m_fontHeight, Qt::AlignRight, QString::number(number + 1));
698 painter.restore();
699 }
700
701 // END PrintPainter
702