27 #include <QAbstractTextDocumentLayout>
28 #include <QApplication>
30 #include <QDomDocument>
31 #include <QDomElement>
36 #include <QPrintDialog>
37 #include <QProgressDialog>
39 #include <QStyleOption>
50 , m_endlessPrinterWidth(0)
51 , m_orientation(QPageLayout::Portrait)
52 , m_pageSize(QPageSize::A4)
56 , m_marginBottom(20.0)
58 , m_headerBodySpacing(0)
59 , m_footerBodySpacing(0)
62 , m_watermarkRotation(0)
63 , m_watermarkColor(QColor(204, 204, 204))
64 , m_watermarkFont(QFont(QStringLiteral(
"Helvetica"), 48))
68 m_numHorizontalPages( 1 ),
69 m_numVerticalPages( 0 ),
70 m_scaleFontsBy( 1.0 ),
75 , m_pageContentSizeDirty(true)
76 , m_xmlElementHandler(nullptr)
78 , m_currentModel(nullptr)
94 return m_layoutWidth > 0;
100 if (m_paperSize.isEmpty()) {
101 const auto mmSize = m_pageSize.size(QPageSize::Millimeter);
103 if (m_orientation == QPageLayout::Landscape) {
104 m_paperSize.transpose();
115 if (m_pageContentSizeDirty) {
116 if (!wantEndlessPrinting()) {
117 setPaperSizeFromPrinter(paperSize());
120 Q_ASSERT(m_layoutWidth != 0);
121 qreal textDocWidth = m_layoutWidth -
mmToPixels(m_marginLeft + m_marginRight);
122 m_paperSize = layoutAsOnePage(textDocWidth);
124 qDebug() <<
"setPaperSizeFromPrinter: endless printer. m_layoutWidth=" << m_layoutWidth <<
"textDocWidth=" << textDocWidth <<
"single page has size" << m_paperSize <<
"pixels";
127 Q_ASSERT(m_layout->numberOfPages() == 1);
132 m_layout->ensureLayouted();
138 qreal textDocHeight = paperSize().height() -
mmToPixels(m_marginTop + m_marginBottom);
139 const qreal headerHeight = m_headers.height();
140 textDocHeight -= headerHeight;
141 textDocHeight -=
mmToPixels(m_headerBodySpacing);
142 const qreal footerHeight = m_footers.height();
143 textDocHeight -=
mmToPixels(m_footerBodySpacing);
144 textDocHeight -= footerHeight;
147 return textDocHeight;
152 const bool skip = rawMainTextDocHeight() <= 0;
154 qDebug() <<
"Not enough height for headers and footers in this page size, hiding headers and footers.";
161 const qreal height = rawMainTextDocHeight();
162 const bool skip = height <= 0;
164 qreal textDocHeight = paperSize().height() -
mmToPixels(m_marginTop + m_marginBottom);
165 textDocHeight -=
mmToPixels(m_headerBodySpacing);
166 textDocHeight -=
mmToPixels(m_footerBodySpacing);
167 return textDocHeight;
174 const int left = qRound(
mmToPixels(m_marginLeft));
175 const int top = qRound(
mmToPixels(m_marginTop));
176 const int headerHeightWithSpacing = qRound((skipHeadersFooters() ? 0 : m_headers.height()) +
mmToPixels(m_headerBodySpacing));
177 const int textDocWidth = qRound(m_paperSize.width() -
mmToPixels(m_marginLeft + m_marginRight));
178 const int textDocHeight = qRound(mainTextDocHeight());
179 return {left, top + headerHeightWithSpacing, textDocWidth, textDocHeight};
193 Q_ASSERT(!wantEndlessPrinting());
195 m_paperSize = paperSize;
196 const qreal marginsInPixels =
mmToPixels(m_marginLeft + m_marginRight);
197 qreal textDocWidth = m_paperSize.width() - marginsInPixels;
199 m_headers.layoutWithTextWidth(textDocWidth);
200 m_footers.layoutWithTextWidth(textDocWidth);
202 const qreal textDocHeight = mainTextDocHeight();
211 m_layout->setPageContentSize(QSizeF(textDocWidth, textDocHeight));
213 m_pageContentSizeDirty =
false;
218 Header *firstPageHeader =
nullptr;
219 Header *lastPageHeader =
nullptr;
220 Header *evenPagesHeader =
nullptr;
221 Header *oddPagesHeader =
nullptr;
222 for (const_iterator it = begin(); it != end(); ++it) {
223 const KDReports::HeaderLocations loc = it.key();
224 Header *
const h = it.value();
234 if (pageNumber == 1 && firstPageHeader)
235 return firstPageHeader;
236 if (pageNumber == pageCount && lastPageHeader)
237 return lastPageHeader;
239 return oddPagesHeader;
241 return evenPagesHeader;
247 for (const_iterator it = begin(); it != end(); ++it) {
248 const KDReports::HeaderLocations loc = it.key();
249 Header *
const h = it.value();
254 KDReports::HeaderLocations loc;
262 const int pageCount = m_layout->numberOfPages();
265 header->preparePaintingPage(pageNumber + m_firstPageNumber - 1);
269 footer->preparePaintingPage(pageNumber + m_firstPageNumber - 1);
272 const QRect textDocRect = mainTextDocRect();
273 const bool skipHeadersFooters = this->skipHeadersFooters();
279 if (!m_watermarkText.isEmpty()) {
281 painter.translate(textDocRect.center());
282 painter.rotate(m_watermarkRotation);
283 painter.setFont(m_watermarkFont);
284 painter.setPen(m_watermarkColor);
285 const QSize textSize(painter.fontMetrics().size(Qt::TextSingleLine, m_watermarkText));
286 const QRect textRect(-textSize.width() / 2, -textSize.height() / 2, textSize.width(), textSize.height());
287 painter.drawText(textRect, Qt::AlignCenter, m_watermarkText);
291 if (!m_watermarkImage.isNull()) {
297 QImage img = m_watermarkImage;
298 if (m_watermarkImage.width() > textDocRect.width() || m_watermarkImage.height() > textDocRect.height()) {
300 img = m_watermarkImage.scaled(textDocRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
302 const QRect imageRect = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, img.size(), textDocRect);
304 painter.drawImage(imageRect.topLeft(), img);
309 painter.setClipRect(textDocRect);
310 painter.translate(textDocRect.topLeft());
311 m_layout->paintPageContent(pageNumber, painter);
314 QAbstractTextDocumentLayout::PaintContext ctx;
315 ctx.palette.setColor(QPalette::Text, Qt::black);
316 if (header && !skipHeadersFooters) {
318 const int top = qRound(
mmToPixels(m_marginTop));
319 painter.translate(textDocRect.left(), top);
320 ctx.clip = painter.clipRegion().boundingRect();
324 if (footer && !skipHeadersFooters) {
326 const int bottom = qRound(
mmToPixels(m_marginBottom));
327 const int footerHeight = qRound(m_footers.height());
328 painter.translate(textDocRect.left(), m_paperSize.height() - bottom - footerHeight);
329 ctx.clip = painter.clipRegion().boundingRect();
338 m_headers.layoutWithTextWidth(docWidth);
339 m_footers.layoutWithTextWidth(docWidth);
341 const qreal docHeight = m_layout->layoutAsOnePage(docWidth);
343 qreal pageWidth = docWidth +
mmToPixels(m_marginLeft + m_marginRight);
344 qreal pageHeight = docHeight +
mmToPixels(m_marginTop + m_marginBottom);
345 pageHeight += m_headers.height();
346 pageHeight += m_footers.height();
348 m_pageContentSizeDirty =
false;
351 return QSizeF(pageWidth, pageHeight);
356 m_pageContentSizeDirty =
true;
362 const int pageCount = m_layout->numberOfPages();
363 std::unique_ptr<QProgressDialog> dialog;
364 if (QThread::currentThread() == qApp->thread()) {
365 dialog.reset(
new QProgressDialog(QObject::tr(
"Printing"), QObject::tr(
"Cancel"), 0, pageCount, parent));
366 dialog->setWindowModality(Qt::ApplicationModal);
369 if (!painter.begin(printer)) {
370 qWarning() <<
"QPainter failed to initialize on the given printer";
375 int toPage = pageCount;
376 if (printer->printRange() == QPrinter::PageRange) {
377 fromPage = printer->fromPage() - 1;
378 toPage = printer->toPage();
383 bool firstPage =
true;
384 for (
int pageIndex = fromPage; pageIndex < toPage; ++pageIndex) {
386 dialog->setValue(pageIndex);
387 if (dialog->wasCanceled())
394 paintPage(pageIndex, painter);
406 QFile html(QFile::decodeName(fileName) + QStringLiteral(
".html"));
407 Q_ASSERT(html.open(QIODevice::WriteOnly));
408 const QString htmlText = m_layout->toHtml();
409 html.write(htmlText.toUtf8());
412 bool oldLayoutDirty =
true;
413 m_pageContentSizeDirty =
false;
415 q->setupPrinter(&printer);
416 printer.setOutputFileName(QFile::decodeName(fileName));
417 doPrint(&printer,
nullptr);
418 printer.setOutputFileName(QString());
419 m_pageContentSizeDirty = oldLayoutDirty;
423 typedef QMap<QString, QAbstractItemModel *>
ModelMap;
424 Q_GLOBAL_STATIC(
ModelMap, globalModelMap)
428 return globalModelMap()->value(key, 0);
433 return paperSize().width() -
mmToPixels(m_marginLeft + m_marginRight);
459 if (d->m_reportMode != WordProcessing) {
460 qWarning(
"KDReports: addInlineElement is only supported in WordProcessing mode");
462 d->builder()->addInlineElementPublic(element);
469 if (d->m_reportMode != WordProcessing) {
470 qWarning(
"KDReports: addElement is only supported in WordProcessing mode");
472 d->builder()->addBlockElementPublic(element, horizontalAlignment, backgroundColor);
479 if (d->m_reportMode != WordProcessing) {
480 qWarning(
"KDReports: addVerticalSpacing is only supported in WordProcessing mode");
482 d->builder()->addVerticalSpacingPublic(space);
486 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
489 setPageSize(
static_cast<QPageSize::PageSizeId
>(size));
495 setPageSize(QPageSize{size});
500 d->m_pageSize = size;
501 d->m_paperSize = QSizeF();
502 d->m_pageContentSizeDirty =
true;
509 case QPrinter::DevicePixel:
511 case QPrinter::Millimeter:
514 case QPrinter::Point:
521 qWarning(
"Unsupported printer unit %d", unit);
523 d->m_paperSize = QSizeF(paperSize.width() * factor, paperSize.height() * factor);
524 d->m_pageContentSizeDirty =
true;
527 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
530 return static_cast<QPrinter::PageSize
>(d->m_pageSize.id());
535 return d->m_pageSize;
539 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
540 void KDReports::Report::setOrientation(QPrinter::Orientation orientation)
542 d->m_orientation =
static_cast<QPageLayout::Orientation
>(orientation);
543 d->m_paperSize = QSizeF();
544 d->m_pageContentSizeDirty =
true;
547 QPrinter::Orientation KDReports::Report::orientation()
const
549 return static_cast<QPrinter::Orientation
>(d->m_orientation);
555 d->m_orientation = orientation;
556 d->m_paperSize = QSizeF();
557 d->m_pageContentSizeDirty =
true;
562 return d->m_orientation;
567 d->m_marginTop = top;
568 d->m_marginLeft = left;
569 d->m_marginBottom = bottom;
570 d->m_marginRight = right;
573 d->m_pageContentSizeDirty =
true;
578 *top = d->m_marginTop;
579 *left = d->m_marginLeft;
580 *bottom = d->m_marginBottom;
581 *right = d->m_marginRight;
586 d->m_marginLeft = left;
589 d->m_pageContentSizeDirty =
true;
594 return d->m_marginLeft;
599 d->m_marginRight = right;
602 d->m_pageContentSizeDirty =
true;
607 return d->m_marginRight;
612 d->m_marginTop = top;
615 d->m_pageContentSizeDirty =
true;
620 return d->m_marginTop;
625 d->m_marginBottom = bottom;
628 d->m_pageContentSizeDirty =
true;
633 return d->m_marginBottom;
639 d->m_endlessPrinterWidth = widthMM;
641 d->m_pageContentSizeDirty =
true;
644 d->m_layoutWidth = 0;
645 d->m_pageContentSizeDirty =
true;
652 d->paintPage(pageNumber, painter);
658 return d->m_layout->numberOfPages();
663 qDebug() << asHtml();
666 QString KDReports::Report::asHtml()
const
668 return d->m_layout->toHtml();
674 setupPrinter(&printer);
675 QPointer<QPrintDialog> dialog =
new QPrintDialog(&printer, parent);
676 dialog->setMinMax(1, numberOfPages());
678 if (dialog->exec() == QDialog::Accepted) {
681 ok = d->doPrint(&printer, parent);
690 const auto savePageSize = pageSize();
691 if (d->wantEndlessPrinting()) {
695 printer->setPageSize(QPageSize(d->m_paperSize *
pixelsToPointsMultiplier(printer->resolution()), QPageSize::Point));
699 d->setPaperSizeFromPrinter(printer->pageLayout().fullRectPixels(printer->resolution()).size());
702 printer->setFullPage(
true);
706 const bool ret = d->doPrint(printer, parent);
709 setPageSize(savePageSize);
718 printer.setOutputFileName(fileName);
719 setupPrinter(&printer);
720 const bool ret = d->doPrint(&printer, parent);
721 printer.setOutputFileName(QString());
727 const QString html = asHtml();
728 QFile file(fileName);
729 if (file.open(QIODevice::WriteOnly)) {
730 file.write(html.toUtf8());
731 d->m_layout->finishHtmlExport();
741 const auto savePageSize = pageSize();
742 const qreal saveLayoutWidth = d->m_layoutWidth;
743 d->m_layoutWidth = d->m_layout->idealWidth() +
mmToPixels(d->m_marginLeft + d->m_marginRight);
744 d->m_pageContentSizeDirty =
true;
747 const qreal zoomFactor = qMin((qreal)size.width() / d->m_paperSize.width(), (qreal)size.height() / d->m_paperSize.height());
750 QImage image(size, QImage::Format_ARGB32_Premultiplied);
751 image.fill(Qt::white);
754 if (!painter.begin(&image)) {
755 qWarning() <<
"QPainter failed to initialize on the given image of size" << size;
758 painter.setRenderHint(QPainter::Antialiasing);
759 painter.setRenderHint(QPainter::SmoothPixmapTransform);
760 painter.fillRect(QRectF(0, 0, size.width(), size.height()), QBrush(Qt::white));
761 painter.scale(zoomFactor, zoomFactor);
762 d->paintPage(0, painter);
765 d->m_layoutWidth = saveLayoutWidth;
766 setPageSize(savePageSize);
767 qDebug() <<
"Saving pixmap" << image.size() <<
"into" << fileName <<
"with format" << format;
768 return image.save(fileName, format);
773 if (!d->m_headers.contains(hl))
774 d->m_headers.insert(hl,
new Header(
this));
775 return *d->m_headers.value(hl);
781 HeaderLocations loc = d->m_headers.headerLocation(header);
782 d->m_headers.remove(loc);
783 d->m_headers.insert(hl, header);
788 if (!d->m_footers.contains(hl))
789 d->m_footers.insert(hl,
new Header(
this));
790 return *d->m_footers.value(hl);
796 HeaderLocations loc = d->m_footers.headerLocation(footer);
797 d->m_footers.remove(loc);
798 d->m_footers.insert(hl, footer);
810 if (iodevice->isOpen())
813 iodevice->open(QIODevice::ReadOnly);
818 bool ret = doc.setContent(iodevice,
true, &errorMsg, &errorLine, &errorColumn);
825 qWarning(
"Malformed XML read in KDReports::Report::loadFromXML(): error message = %s, error line = %d, error column = %d", qPrintable(errorMsg), errorLine, errorColumn);
828 return loadFromXML(doc, details);
833 XmlParser parser(d->m_textValues, d->m_imageValues, d->m_xmlElementHandler,
this, details);
834 d->m_pageContentSizeDirty =
true;
840 globalModelMap()->insert(modelKey, model);
845 Q_ASSERT(d->m_reportMode == WordProcessing);
851 d->builder()->contentDocumentCursor().beginEditBlock();
856 d->builder()->contentDocumentCursor().endEditBlock();
861 const QRect textDocRect = d->mainTextDocRect();
862 const QPoint textPos = pos - textDocRect.topLeft();
863 return d->m_layout->anchorAt(pageNumber, textPos);
868 d->m_watermarkText = text;
869 d->m_watermarkRotation = rotation;
870 d->m_watermarkColor = color;
871 d->m_watermarkFont = font;
876 return d->m_watermarkText;
881 return d->m_watermarkRotation;
886 return d->m_watermarkColor;
891 return d->m_watermarkFont;
899 opt.palette = QApplication::palette();
900 pix = qApp->style()->generatedIconPixmap(QIcon::Disabled, pixmap, &opt);
902 setWatermarkImage(pix.toImage());
907 return QPixmap::fromImage(d->m_watermarkImage);
912 d->m_watermarkImage = image;
917 return d->m_watermarkImage;
922 d->builder()->addPageBreakPublic();
927 d->m_layout->updateTextValue(
id, value);
928 d->m_headers.updateTextValue(
id, value);
929 d->m_footers.updateTextValue(
id, value);
930 d->m_textValues.insert(
id, value);
935 d->m_imageValues.insert(
id, value.toImage());
940 d->m_imageValues.insert(
id, value);
945 return d->paperSize();
950 d->builder()->insertFragmentPublic(fragment);
955 d->m_layout->setDefaultFont(font);
956 d->m_pageContentSizeDirty =
true;
961 return d->m_layout->defaultFont();
966 d->m_headerBodySpacing = spacing;
967 d->m_pageContentSizeDirty =
true;
972 return d->m_headerBodySpacing;
977 d->m_footerBodySpacing = spacing;
978 d->m_pageContentSizeDirty =
true;
983 return d->m_footerBodySpacing;
988 d->m_layout->scaleTo(numPagesHorizontally, numPagesVertically);
993 return d->m_layout->maximumNumberOfPagesForHorizontalScaling();
998 return d->m_layout->maximumNumberOfPagesForVerticalScaling();
1003 d->m_layout->setFixedRowHeight(
mmToPixels(mm));
1008 d->m_layout->setUserRequestedFontScalingFactor(factor);
1013 return d->m_layout->userRequestedFontScalingFactor();
1018 return maximumNumberOfPagesForHorizontalScaling() != 1 || maximumNumberOfPagesForVerticalScaling() > 0;
1023 if (d->m_reportMode != SpreadSheet) {
1024 qWarning(
"setTableBreakingPageOrder is only supported in SpreadSheet mode");
1026 mainTable()->setTableBreakingPageOrder(pageOrder);
1032 if (d->m_reportMode != SpreadSheet) {
1033 qWarning(
"tableBreakingPageOrder is only supported in SpreadSheet mode");
1034 return DownThenRight;
1036 return mainTable()->tableBreakingPageOrder();
1042 if (d->m_reportMode == WordProcessing)
1043 doc().regenerateAutoTables();
1048 if (d->m_reportMode == WordProcessing)
1049 doc().regenerateAutoTableForModel(model);
1054 if (d->m_reportMode == WordProcessing)
1055 return doc().autoTableElements();
1061 d->m_xmlElementHandler = handler;
1066 d->m_currentModel = model;
1067 d->m_currentRow = row;
1072 d->m_documentName = name;
1077 return d->m_documentName;
1082 d->builder()->setTabPositions(tabs);
1087 d->builder()->setParagraphMargins(left, top, right, bottom);
1092 if (d->m_reportMode != reportMode) {
1093 d->m_reportMode = reportMode;
1095 switch (reportMode) {
1096 case WordProcessing:
1101 d->m_layout = sslayout;
1102 mainTable()->setLayout(sslayout);
1110 return d->m_reportMode;
1115 Q_ASSERT(d->m_reportMode == SpreadSheet);
1116 return d->m_mainTable;
1121 QTextOption::Tab tab;
1123 tab.type = QTextOption::RightTab;
1124 tab.delimiter = QChar::fromLatin1(
'P');
1130 QTextOption::Tab tab;
1132 tab.type = QTextOption::CenterTab;
1133 tab.delimiter = QChar::fromLatin1(
'P');
1139 return d->m_headers.headerLocation(header);
1144 return d->m_footers.headerLocation(footer);
1149 return d->builder()->currentPosition();
1154 d->m_firstPageNumber = num;
1159 return d->m_firstPageNumber;
1162 void KDReports::Report::setupPrinter(QPrinter *printer)
1164 printer->setFullPage(
true);
1165 printer->setPageOrientation(d->m_orientation);
1167 printer->setPageSize(d->m_pageSize);
1168 printer->setDocName(d->m_documentName);