10#include "printpainter.h"
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"
21#include <KLocalizedString>
27using namespace KatePrinter;
29class KatePrinter::PageLayout
47 bool selectionOnly =
false;
53 uint headerHeight = 0;
54 QStringList headerTagList;
55 uint footerHeight = 0;
56 QStringList footerTagList;
58 KTextEditor::Range selectionRange;
65 , m_printLineNumbers(false)
68 , m_useBackground(false)
70 , m_useHeaderBackground(false)
71 , m_useFooterBackground(false)
84 m_renderer->setPrinterFriendly(
true);
89PrintPainter::~PrintPainter()
94void PrintPainter::setTextFont(
const QFont &font)
96 m_renderer->config()->setFont(font);
99void PrintPainter::setUseBox(
const bool on)
102 setBoxWidth(m_boxWidth);
105void PrintPainter::setBoxWidth(
const int width)
117void PrintPainter::setBoxColor(
const QColor &color)
124void PrintPainter::setHeaderBackground(
const QColor &color)
127 m_headerBackground = color;
131void PrintPainter::setHeaderForeground(
const QColor &color)
134 m_headerForeground = color;
138void PrintPainter::setFooterBackground(
const QColor &color)
141 m_footerBackground = color;
144void PrintPainter::setFooterForeground(
const QColor &color)
147 m_footerForeground = color;
151void PrintPainter::setColorScheme(
const QString &scheme)
154 m_renderer->config()->setSchema(scheme);
160void PrintPainter::updateCache()
162 m_fontHeight = m_renderer->fontHeight();
165 QString s = QStringLiteral(
"%1 ").arg(m_doc->lines());
166 s.
fill(QLatin1Char(
'5'), -1);
168 m_lineNumberWidth = m_renderer->currentFontMetrics().boundingRect(s).width();
171void PrintPainter::paint(QPrinter *printer)
const
173 QPainter painter(printer);
176 configure(printer, pl);
178 uint lineCount = pl.firstline;
181 bool pageStarted =
true;
184 auto &f = m_renderer->folding();
187 while (lineCount <= pl.lastline) {
188 if (y + m_fontHeight > pl.maxHeight) {
189 if ((
int)currentPage == printer->
toPage()) {
193 painter.resetTransform();
200 qCDebug(LOG_KTE) <<
"Starting new page," << lineCount <<
"lines up to now.";
201 paintNewPage(painter, currentPage, y, pl);
203 painter.translate(pl.xstart, y);
206 const bool skipLine = m_dontPrintFoldedCode && !f.isLineVisible(lineCount);
208 if (!skipLine && m_printLineNumbers ) {
209 paintLineNumber(painter, lineCount, pl);
213 paintLine(painter, lineCount, y, remainder, pl);
224void PrintPainter::configure(
const QPrinter *printer, PageLayout &pl)
const
226 pl.pageHeight = printer->
height();
227 pl.pageWidth = printer->
width();
228 pl.headerWidth = printer->
width();
229 pl.innerMargin = m_useBox ? m_boxMargin : 6;
230 pl.maxWidth = printer->
width();
231 pl.maxHeight = (m_useBox ? printer->
height() - pl.innerMargin : printer->
height());
233 pl.lastline = m_doc->lastLine();
235 if (m_view && pl.selectionOnly) {
237 pl.selectionRange = m_view->selectionRange();
238 pl.firstline = pl.selectionRange.start().line();
239 pl.lastline = pl.selectionRange.end().line();
242 if (m_printLineNumbers) {
244 int _adj = m_renderer->currentFontMetrics().horizontalAdvance(QStringLiteral(
"5"));
246 pl.maxWidth -= m_lineNumberWidth + _adj;
247 pl.xstart += m_lineNumberWidth + _adj;
250 if (m_useHeader || m_useFooter) {
256 std::map<QString, QString> tags;
259 tags[QStringLiteral(
"u")] = u.loginName();
266 tags[QStringLiteral(
"f")] = m_doc->url().fileName();
267 tags[QStringLiteral(
"U")] = m_doc->url().toString();
268 if (pl.selectionOnly) {
269 QString s(
i18n(
"(Selection of) "));
270 tags[QStringLiteral(
"f")].prepend(s);
271 tags[QStringLiteral(
"U")].prepend(s);
274 static const QRegularExpression reTags(QStringLiteral(
"%([dDfUhuyY])"));
277 pl.headerHeight = QFontMetrics(m_fhFont).height();
278 if (m_useBox || m_useHeaderBackground) {
279 pl.headerHeight += pl.innerMargin * 2;
281 pl.headerHeight += 1 + QFontMetrics(m_fhFont).leading();
284 pl.headerTagList = m_headerFormat;
285 QMutableStringListIterator it(pl.headerTagList);
286 while (it.hasNext()) {
287 QString tag = it.next();
288 QRegularExpressionMatch
match;
289 int pos = tag.
indexOf(reTags, 0, &match);
292 rep = tags[
match.captured(1)];
293 tag.
replace((uint)pos, 2, rep);
295 pos = tag.
indexOf(reTags, pos, &match);
302 pl.footerHeight = QFontMetrics(m_fhFont).height();
303 if (m_useBox || m_useFooterBackground) {
304 pl.footerHeight += 2 * pl.innerMargin;
306 pl.footerHeight += 1;
309 pl.footerTagList = m_footerFormat;
310 QMutableStringListIterator it(pl.footerTagList);
311 while (it.hasNext()) {
312 QString tag = it.next();
313 QRegularExpressionMatch
match;
314 int pos = tag.
indexOf(reTags, 0, &match);
317 rep = tags[
match.captured(1)];
318 tag.
replace((uint)pos, 2, rep);
320 pos = tag.
indexOf(reTags, pos, &match);
325 pl.maxHeight -= pl.footerHeight;
329 if (m_useBackground) {
331 pl.xstart += pl.innerMargin;
332 pl.maxWidth -= pl.innerMargin * 2;
338 pl.maxWidth -= (m_boxWidth + pl.innerMargin) * 2;
339 pl.xstart += m_boxWidth + pl.innerMargin;
341 pl.maxHeight -= m_boxWidth;
344 int pageHeight = pl.maxHeight;
346 pageHeight -= pl.headerHeight + pl.innerMargin;
349 pageHeight -= pl.footerHeight + pl.innerMargin;
352 const int linesPerPage = pageHeight / m_fontHeight;
355 pl.firstline = (printer->
fromPage() - 1) * linesPerPage;
361 if (!pl.headerTagList.filter(QStringLiteral(
"%P")).isEmpty() || !pl.footerTagList.filter(QStringLiteral(
"%P")).isEmpty()) {
362 qCDebug(LOG_KTE) <<
"'%P' found! calculating number of pages...";
367 for (
unsigned int i = pl.firstline; i <= pl.lastline; ++i) {
368 KateLineLayout rangeptr(*m_renderer);
370 m_renderer->layoutLine(&rangeptr, (
int)pl.maxWidth,
false);
371 totalLines += rangeptr.viewLineCount();
374 const int totalPages = (totalLines / linesPerPage) + ((totalLines % linesPerPage) > 0 ? 1 : 0);
381 QString re(QStringLiteral(
"%P"));
384 for (it = pl.headerTagList.begin(); it != pl.headerTagList.end(); ++it) {
388 for (it = pl.footerTagList.begin(); it != pl.footerTagList.end(); ++it) {
394void PrintPainter::paintNewPage(QPainter &painter,
const uint currentPage, uint &y,
const PageLayout &pl)
const
397 paintHeader(painter, currentPage, y, pl);
401 paintFooter(painter, currentPage, pl);
404 if (m_useBackground) {
405 paintBackground(painter, y, pl);
409 paintBox(painter, y, pl);
412 if (m_printGuide && currentPage == 1) {
413 paintGuide(painter, y, pl);
417void PrintPainter::paintHeader(QPainter &painter,
const uint currentPage, uint &y,
const PageLayout &pl)
const
420 painter.
setPen(QPen(m_headerForeground, 0.5));
423 if (m_useHeaderBackground) {
424 painter.
fillRect(0, 0, pl.headerWidth, pl.headerHeight, m_headerBackground);
427 if (pl.headerTagList.count() == 3) {
430 int marg = (m_useBox || m_useHeaderBackground) ? pl.innerMargin : 0;
436 for (
int i = 0; i < 3; i++) {
437 s = pl.headerTagList[i];
438 if (s.
indexOf(QLatin1String(
"%p")) != -1) {
442 painter.
drawText(marg, 0, pl.headerWidth - (marg * 2), pl.headerHeight, align, s);
447 if (!(m_useHeaderBackground || m_useBox || m_useBackground)) {
448 painter.
drawLine(0, pl.headerHeight - 1, pl.headerWidth, pl.headerHeight - 1);
454 y += pl.headerHeight + pl.innerMargin;
457void PrintPainter::paintFooter(QPainter &painter,
const uint currentPage,
const PageLayout &pl)
const
460 painter.
setPen(QPen(m_footerForeground, 0.5));
463 if (!(m_useFooterBackground || m_useBox || m_useBackground)) {
464 painter.
drawLine(0, pl.pageHeight - pl.footerHeight - 1, pl.headerWidth, pl.pageHeight - pl.footerHeight - 1);
466 if (m_useFooterBackground) {
467 painter.
fillRect(0, pl.pageHeight - pl.footerHeight, pl.headerWidth, pl.footerHeight, m_footerBackground);
470 if (pl.footerTagList.count() == 3) {
472 int marg = (m_useBox || m_useFooterBackground) ? pl.innerMargin : 0;
478 for (
int i = 0; i < 3; i++) {
479 s = pl.footerTagList[i];
480 if (s.
indexOf(QLatin1String(
"%p")) != -1) {
483 painter.
drawText(marg, pl.pageHeight - pl.footerHeight, pl.headerWidth - (marg * 2), pl.footerHeight, align, s);
490void PrintPainter::paintGuide(QPainter &painter, uint &y,
const PageLayout &pl)
const
495 QString _hlName = m_doc->highlight()->name();
498 const auto _attributes = m_doc->highlight()->attributesForDefinition(m_renderer->config()->schema());
499 const QColor _defaultPen = _attributes.at(0)->foreground().color();
502 painter.
setPen(_defaultPen);
506 _marg += (2 * m_boxWidth) + (2 * pl.innerMargin);
508 if (m_useBackground) {
509 _marg += 2 * pl.innerMargin;
512 y += 1 + pl.innerMargin;
516 QFont _titleFont = m_renderer->currentFont();
520 painter.
drawText(QRect(_marg, y, pl.pageWidth - (2 * _marg), pl.maxHeight - y),
522 i18n(
"Typographical Conventions for %1", _hlName),
524 const int _w = pl.pageWidth - (_marg * 2) - (pl.innerMargin * 2);
525 const int _x = _marg + pl.innerMargin;
526 y += _r.
height() + pl.innerMargin;
527 painter.
drawLine(_x, y, _x + _w, y);
528 y += 1 + pl.innerMargin;
532 const QString _name = attribute->name().section(QLatin1Char(
':'), 1, 1);
533 _widest = qMax(QFontMetrics(attribute->font()).boundingRect(_name).width(), _widest);
536 const int _guideCols = _w / (_widest + pl.innerMargin);
539 const int _cw = _w / _guideCols;
543 QString _currentHlName;
545 QString _hl = attribute->name().section(QLatin1Char(
':'), 0, 0);
546 QString _name = attribute->name().
section(QLatin1Char(
':'), 1, 1);
547 if (_hl != _hlName && _hl != _currentHlName) {
548 _currentHlName = _hl;
549 if (_i % _guideCols) {
554 painter.
setPen(_defaultPen);
560 painter.
setPen(attribute->foreground().color());
561 painter.
setFont(attribute->font());
564 QRect _rect = QFontMetrics(attribute->font()).boundingRect(_name);
565 _rect.
moveTo(_x + ((_i % _guideCols) * _cw), y);
566 painter.
fillRect(_rect, attribute->background());
572 if (_i && !(_i % _guideCols)) {
577 if (_i % _guideCols) {
581 painter.
setPen(_defaultPen);
584 painter.
fillRect(0, y + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor);
587 painter.
drawRect(_marg, _ystart, pl.pageWidth - (2 * _marg), y - _ystart + pl.innerMargin);
592 y += (m_useBox ? m_boxWidth : 1) + (pl.innerMargin * 2);
595void PrintPainter::paintBox(QPainter &painter, uint &y,
const PageLayout &pl)
const
598 painter.
setPen(QPen(m_boxColor, m_boxWidth));
599 painter.
drawRect(0, 0, pl.pageWidth, pl.pageHeight);
602 painter.
drawLine(0, pl.headerHeight, pl.headerWidth, pl.headerHeight);
608 painter.
fillRect(0, pl.maxHeight + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor);
614void PrintPainter::paintBackground(QPainter &painter,
const uint y,
const PageLayout &pl)
const
619 int _h = pl.maxHeight - y;
621 _y -= pl.innerMargin;
622 _h += 2 * pl.innerMargin;
624 if (m_useHeaderBackground) {
625 _y -= pl.innerMargin;
626 _h += pl.innerMargin;
628 if (m_useFooterBackground) {
629 _h += pl.innerMargin;
632 painter.
fillRect(0, _y, pl.pageWidth, _h, m_renderer->config()->backgroundColor());
635void PrintPainter::paintLine(QPainter &painter,
const uint line, uint &y, uint &remainder,
const PageLayout &pl)
const
638 KateLineLayout rangeptr(*m_renderer);
639 rangeptr.setLine(line);
640 m_renderer->layoutLine(&rangeptr, (
int)pl.maxWidth,
false);
644 if (pl.selectionOnly) {
645 if (m_view && m_view->blockSelection()) {
646 int _x = m_renderer->cursorToX(rangeptr.viewLine(0), pl.selectionRange.start());
647 int _x1 = m_renderer->cursorToX(rangeptr.viewLine(rangeptr.viewLineCount() - 1), pl.selectionRange.end());
650 painter.
setClipRegion(QRegion(_x, 0, _x1 - _x, rangeptr.viewLineCount() * m_fontHeight));
652 }
else if (line == pl.firstline || line == pl.lastline) {
653 QRegion region(0, 0, pl.maxWidth, rangeptr.viewLineCount() * m_fontHeight);
655 if (line == pl.firstline) {
656 int l = rangeptr.viewLineForColumn(pl.selectionRange.start().column());
658 for (
int vl = 0; vl < l; ++vl) {
659 region = region.subtracted(QRegion(0, vl * m_fontHeight, pl.maxWidth, m_fontHeight));
661 region = region.subtracted(QRegion(0, 0, m_renderer->cursorToX(rangeptr.viewLine(l), pl.selectionRange.start()), m_fontHeight));
664 if (line == pl.lastline) {
665 int vl = rangeptr.viewLineForColumn(pl.selectionRange.end().column());
666 int x = m_renderer->cursorToX(rangeptr.viewLine(vl), pl.selectionRange.end());
668 region = region.subtracted(QRegion(x, (vl)*m_fontHeight, pl.maxWidth - x, m_fontHeight));
671 for (; vl < rangeptr.viewLineCount(); ++vl) {
672 region = region.subtracted(QRegion(0, vl * m_fontHeight, pl.maxWidth, m_fontHeight));
682 int _lines = rangeptr.viewLineCount();
684 int proceedLines = _lines;
686 proceedLines = qMin((pl.maxHeight - y) / m_fontHeight, remainder);
688 painter.
translate(0, -(_lines -
int(remainder)) * m_fontHeight + 1);
690 (_lines -
int(remainder)) * m_fontHeight + 1,
692 proceedLines * m_fontHeight);
693 remainder -= proceedLines;
694 }
else if (y + m_fontHeight * _lines > pl.maxHeight) {
695 remainder = _lines - ((pl.maxHeight - y) / m_fontHeight);
696 painter.
setClipRect(0, 0, pl.maxWidth, (_lines -
int(remainder)) * m_fontHeight + 1);
697 }
else if (!pl.selectionOnly) {
702 KateRenderer::PaintTextLineFlags flags;
703 if (!m_dontPrintFoldedCode) {
707 m_renderer->paintTextLine(painter, &rangeptr, 0, (
int)pl.maxWidth, QRectF{},
nullptr, flags);
710 painter.
translate(_xadjust, (m_fontHeight * (_lines - remainder)));
712 y += m_fontHeight * proceedLines;
715void PrintPainter::paintLineNumber(QPainter &painter,
const uint number,
const PageLayout &pl)
const
717 const int left = ((m_useBox || m_useBackground) ? pl.innerMargin : 0) - pl.xstart;
720 painter.
setFont(m_renderer->currentFont());
721 painter.
setPen(m_renderer->config()->lineNumberColor());
QExplicitlySharedDataPointer< Attribute > Ptr
Shared data pointer for Attribute.
Backend of KTextEditor::Document related public KTextEditor interfaces.
Handles all of the work of rendering the text (used for the views and printing)
Kate::TextFolding & folding() const
Returns the folding info to which this renderer is bound.
@ SkipDrawFirstInvisibleLineUnderlined
Skip drawing the dashed underline at the start of a folded block of text?
QString i18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
bool isValid() const const
QDateTime currentDateTime()
QFlags< T > & setFlag(Enum flag, bool on)
void setBold(bool enable)
void setUnderline(bool enable)
void drawLine(const QLine &line)
void drawRect(const QRect &rectangle)
void drawText(const QPoint &position, const QString &text)
void fillRect(const QRect &rectangle, QGradient::Preset preset)
void setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
void setClipRegion(const QRegion ®ion, Qt::ClipOperation operation)
void setClipping(bool enable)
void setFont(const QFont &font)
void setPen(Qt::PenStyle style)
void translate(const QPoint &offset)
int fromPage() const const
virtual bool newPage() override
PrintRange printRange() const const
void moveTo(const QPoint &position)
QString & fill(QChar ch, qsizetype size)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
QTextStream & left(QTextStream &stream)