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)
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()) {
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()) {
282 painter.
rotate(m_watermarkRotation);
283 painter.
setFont(m_watermarkFont);
284 painter.
setPen(m_watermarkColor);
286 const QRect textRect(-textSize.width() / 2, -textSize.height() / 2, textSize.width(), textSize.height());
291 if (!m_watermarkImage.isNull()) {
297 QImage img = m_watermarkImage;
298 if (m_watermarkImage.width() > textDocRect.
width() || m_watermarkImage.height() > textDocRect.
height()) {
311 m_layout->paintPageContent(pageNumber, painter);
316 if (header && !skipHeadersFooters) {
318 const int top = qRound(
mmToPixels(m_marginTop));
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);
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;
369 if (!painter.
begin(printer)) {
370 qWarning() <<
"QPainter failed to initialize on the given printer";
375 int toPage = pageCount;
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);
408 const QString htmlText = m_layout->toHtml();
412 bool oldLayoutDirty =
true;
413 m_pageContentSizeDirty =
false;
415 q->setupPrinter(&printer);
417 doPrint(&printer,
nullptr);
419 m_pageContentSizeDirty = oldLayoutDirty;
424 Q_GLOBAL_STATIC(
ModelMap, globalModelMap)
428 return globalModelMap()->value(key, 0);
433 return paperSize().width() -
mmToPixels(m_marginLeft + m_marginRight);
458 if (d->m_reportMode != WordProcessing) {
459 qWarning(
"KDReports: addInlineElement is only supported in WordProcessing mode");
461 d->builder()->addInlineElementPublic(element);
468 if (d->m_reportMode != WordProcessing) {
469 qWarning(
"KDReports: addElement is only supported in WordProcessing mode");
471 d->builder()->addBlockElementPublic(element, horizontalAlignment, backgroundColor);
478 if (d->m_reportMode != WordProcessing) {
479 qWarning(
"KDReports: addVerticalSpacing is only supported in WordProcessing mode");
481 d->builder()->addVerticalSpacingPublic(space);
485 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
499 d->m_pageSize = size;
500 d->m_paperSize =
QSizeF();
501 d->m_pageContentSizeDirty =
true;
520 qWarning(
"Unsupported printer unit %d", unit);
522 d->m_paperSize =
QSizeF(paperSize.
width() * factor, paperSize.
height() * factor);
523 d->m_pageContentSizeDirty =
true;
526 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
534 return d->m_pageSize;
538 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
542 d->m_paperSize =
QSizeF();
543 d->m_pageContentSizeDirty =
true;
554 d->m_orientation = orientation;
555 d->m_paperSize =
QSizeF();
556 d->m_pageContentSizeDirty =
true;
561 return d->m_orientation;
566 d->m_marginTop = top;
567 d->m_marginLeft = left;
568 d->m_marginBottom = bottom;
569 d->m_marginRight = right;
572 d->m_pageContentSizeDirty =
true;
577 *top = d->m_marginTop;
578 *left = d->m_marginLeft;
579 *bottom = d->m_marginBottom;
580 *right = d->m_marginRight;
585 d->m_marginLeft = left;
588 d->m_pageContentSizeDirty =
true;
593 return d->m_marginLeft;
598 d->m_marginRight = right;
601 d->m_pageContentSizeDirty =
true;
606 return d->m_marginRight;
611 d->m_marginTop = top;
614 d->m_pageContentSizeDirty =
true;
619 return d->m_marginTop;
624 d->m_marginBottom = bottom;
627 d->m_pageContentSizeDirty =
true;
632 return d->m_marginBottom;
638 d->m_endlessPrinterWidth = widthMM;
640 d->m_pageContentSizeDirty =
true;
643 d->m_layoutWidth = 0;
644 d->m_pageContentSizeDirty =
true;
651 d->paintPage(pageNumber, painter);
657 return d->m_layout->numberOfPages();
662 qDebug() << asHtml();
665 QString KDReports::Report::asHtml()
const
667 return d->m_layout->toHtml();
673 setupPrinter(&printer);
675 dialog->setMinMax(1, numberOfPages());
680 ok = d->doPrint(&printer, parent);
689 const auto savePageSize = pageSize();
690 if (d->wantEndlessPrinting()) {
705 const bool ret = d->doPrint(printer, parent);
708 setPageSize(savePageSize);
718 setupPrinter(&printer);
719 const bool ret = d->doPrint(&printer, parent);
727 QFile file(fileName);
730 d->m_layout->finishHtmlExport();
740 const auto savePageSize = pageSize();
741 const qreal saveLayoutWidth = d->m_layoutWidth;
742 d->m_layoutWidth = d->m_layout->idealWidth() +
mmToPixels(d->m_marginLeft + d->m_marginRight);
743 d->m_pageContentSizeDirty =
true;
746 const qreal zoomFactor = qMin((qreal)size.
width() / d->m_paperSize.width(), (qreal)size.
height() / d->m_paperSize.height());
753 if (!painter.
begin(&image)) {
754 qWarning() <<
"QPainter failed to initialize on the given image of size" << size;
760 painter.
scale(zoomFactor, zoomFactor);
761 d->paintPage(0, painter);
764 d->m_layoutWidth = saveLayoutWidth;
765 setPageSize(savePageSize);
766 qDebug() <<
"Saving pixmap" << image.
size() <<
"into" << fileName <<
"with format" << format;
767 return image.
save(fileName, format);
772 if (!d->m_headers.contains(hl))
773 d->m_headers.insert(hl,
new Header(
this));
774 return *d->m_headers.value(hl);
780 HeaderLocations loc = d->m_headers.headerLocation(header);
781 d->m_headers.remove(loc);
782 d->m_headers.insert(hl, header);
787 if (!d->m_footers.contains(hl))
788 d->m_footers.insert(hl,
new Header(
this));
789 return *d->m_footers.value(hl);
795 HeaderLocations loc = d->m_footers.headerLocation(footer);
796 d->m_footers.remove(loc);
797 d->m_footers.insert(hl, footer);
817 bool ret = doc.
setContent(iodevice,
true, &errorMsg, &errorLine, &errorColumn);
824 qWarning(
"Malformed XML read in KDReports::Report::loadFromXML(): error message = %s, error line = %d, error column = %d", qPrintable(errorMsg), errorLine, errorColumn);
827 return loadFromXML(doc, details);
832 XmlParser parser(d->m_textValues, d->m_imageValues, d->m_xmlElementHandler,
this, details);
833 d->m_pageContentSizeDirty =
true;
839 globalModelMap()->insert(modelKey, model);
844 Q_ASSERT(d->m_reportMode == WordProcessing);
850 d->builder()->contentDocumentCursor().beginEditBlock();
855 d->builder()->contentDocumentCursor().endEditBlock();
860 const QRect textDocRect = d->mainTextDocRect();
862 return d->m_layout->anchorAt(pageNumber, textPos);
867 if (d->m_reportMode == WordProcessing) {
876 d->m_watermarkText = text;
877 d->m_watermarkRotation = rotation;
878 d->m_watermarkColor = color;
879 d->m_watermarkFont = font;
884 return d->m_watermarkText;
889 return d->m_watermarkRotation;
894 return d->m_watermarkColor;
899 return d->m_watermarkFont;
908 pix = qApp->style()->generatedIconPixmap(
QIcon::Disabled, pixmap, &opt);
910 setWatermarkImage(pix.
toImage());
920 d->m_watermarkImage = image;
925 return d->m_watermarkImage;
930 d->builder()->addPageBreakPublic();
935 d->m_layout->updateTextValue(
id, value);
936 d->m_headers.updateTextValue(
id, value);
937 d->m_footers.updateTextValue(
id, value);
938 d->m_textValues.insert(
id, value);
943 d->m_imageValues.insert(
id, value.
toImage());
948 d->m_imageValues.insert(
id, value);
953 return d->paperSize();
958 d->builder()->insertFragmentPublic(fragment);
963 d->m_layout->setDefaultFont(font);
964 d->m_pageContentSizeDirty =
true;
969 return d->m_layout->defaultFont();
974 d->m_headerBodySpacing = spacing;
975 d->m_pageContentSizeDirty =
true;
980 return d->m_headerBodySpacing;
985 d->m_footerBodySpacing = spacing;
986 d->m_pageContentSizeDirty =
true;
991 return d->m_footerBodySpacing;
996 d->m_layout->scaleTo(numPagesHorizontally, numPagesVertically);
1001 return d->m_layout->maximumNumberOfPagesForHorizontalScaling();
1006 return d->m_layout->maximumNumberOfPagesForVerticalScaling();
1011 d->m_layout->setFixedRowHeight(
mmToPixels(mm));
1016 d->m_layout->setUserRequestedFontScalingFactor(factor);
1021 return d->m_layout->userRequestedFontScalingFactor();
1026 return maximumNumberOfPagesForHorizontalScaling() != 1 || maximumNumberOfPagesForVerticalScaling() > 0;
1031 if (d->m_reportMode != SpreadSheet) {
1032 qWarning(
"setTableBreakingPageOrder is only supported in SpreadSheet mode");
1034 mainTable()->setTableBreakingPageOrder(pageOrder);
1040 if (d->m_reportMode != SpreadSheet) {
1041 qWarning(
"tableBreakingPageOrder is only supported in SpreadSheet mode");
1042 return DownThenRight;
1044 return mainTable()->tableBreakingPageOrder();
1050 if (d->m_reportMode == WordProcessing)
1051 doc().regenerateAutoTables();
1056 if (d->m_reportMode == WordProcessing)
1057 doc().regenerateAutoTableForModel(model);
1062 if (d->m_reportMode == WordProcessing)
1063 return doc().autoTableElements();
1069 d->m_xmlElementHandler = handler;
1074 d->m_currentModel = model;
1075 d->m_currentRow = row;
1080 d->m_documentName = name;
1085 return d->m_documentName;
1090 d->builder()->setTabPositions(tabs);
1095 d->builder()->setParagraphMargins(left, top, right, bottom);
1100 if (d->m_reportMode != reportMode) {
1101 d->m_reportMode = reportMode;
1103 switch (reportMode) {
1104 case WordProcessing:
1109 d->m_layout = sslayout;
1110 mainTable()->setLayout(sslayout);
1118 return d->m_reportMode;
1123 Q_ASSERT(d->m_reportMode == SpreadSheet);
1124 return d->m_mainTable;
1147 return d->m_headers.headerLocation(header);
1152 return d->m_footers.headerLocation(footer);
1157 return d->builder()->currentPosition();
1162 d->m_firstPageNumber = num;
1167 return d->m_firstPageNumber;
1170 void KDReports::Report::setupPrinter(
QPrinter *printer)