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