KD Reports API Documentation 2.2
Loading...
Searching...
No Matches
KDReportsReport.cpp
Go to the documentation of this file.
1/****************************************************************************
2**
3** This file is part of the KD Reports library.
4**
5** SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6**
7** SPDX-License-Identifier: MIT
8**
9****************************************************************************/
10
11#include "KDReportsReport.h"
12#include "KDReportsElement.h"
13#include "KDReportsHeader.h"
15#include "KDReportsMainTable.h"
16#include "KDReportsReport_p.h"
20
21#include <QAbstractTextDocumentLayout>
22#include <QApplication>
23#include <QDebug>
24#include <QDomDocument>
25#include <QDomElement>
26#include <QFile>
27#include <QMap>
28#include <QPainter>
29#include <QPointer>
30#include <QPrintDialog>
31#include <QProgressDialog>
32#include <QStyle>
33#include <QStyleOption>
34#include <QThread>
35
36#include <memory>
37
38QT_BEGIN_NAMESPACE
39Q_GUI_EXPORT extern int qt_defaultDpi(); // This is what QTextDocument uses...
40QT_END_NAMESPACE
41
43 : m_layoutWidth(0)
44 , m_endlessPrinterWidth(0)
45 , m_orientation(QPageLayout::Portrait)
46 , m_pageSize(QPageSize::A4)
47 , m_marginTop(20.0)
48 , // warning, defaults are duplicated in KDReportsXmlParser.cpp
49 m_marginLeft(20.0)
50 , m_marginBottom(20.0)
51 , m_marginRight(20.0)
52 , m_headerBodySpacing(0)
53 , m_footerBodySpacing(0)
54 , m_headers()
55 , m_footers()
56 , m_watermarkRotation(0)
57 , m_watermarkColor(QColor(204, 204, 204))
58 , m_watermarkFont(QFont(QStringLiteral("Helvetica"), 48))
59 , m_watermarkImage()
60 ,
61#if 0
62 m_numHorizontalPages( 1 ),
63 m_numVerticalPages( 0 ),
64 m_scaleFontsBy( 1.0 ),
65 m_autoScale( false ),
66 m_tableBreakingPageOrder( KDReports::Report::DownThenRight ),
67#endif
68 m_firstPageNumber(1)
69 , m_pageContentSizeDirty(true)
70 , m_xmlElementHandler(nullptr)
71 , m_currentRow(-1)
72 , m_currentModel(nullptr)
73 , m_reportMode(KDReports::Report::WordProcessing)
74 , m_layout(new TextDocReportLayout(report))
75 , m_mainTable(new MainTable)
76 , q(report)
77{
78}
79
81{
82 delete m_layout;
83 delete m_mainTable;
84}
85
87{
88 return m_layoutWidth > 0;
89}
90
92{
93 // determine m_paperSize from m_pageSize if needed
94 if (m_paperSize.isEmpty()) {
95 const auto mmSize = m_pageSize.size(QPageSize::Millimeter);
96 m_paperSize = QSizeF {mmToPixels(mmSize.width()), mmToPixels(mmSize.height())};
97 if (m_orientation == QPageLayout::Landscape) {
98 m_paperSize.transpose();
99 }
100 }
101 // qDebug() << "m_paperSize=" << m_paperSize;
102 return m_paperSize;
103}
104
106{
107 // We need to do a layout if
108 // m_pageContentSizeDirty is true, i.e. page size has changed etc.
109 if (m_pageContentSizeDirty) {
110 if (!wantEndlessPrinting()) {
111 setPaperSizeFromPrinter(paperSize());
112 } else {
113 // Get the document to fit into one page
114 Q_ASSERT(m_layoutWidth != 0);
115 qreal textDocWidth = m_layoutWidth - mmToPixels(m_marginLeft + m_marginRight);
116 m_paperSize = layoutAsOnePage(textDocWidth);
117
118 qDebug() << "setPaperSizeFromPrinter: endless printer. m_layoutWidth=" << m_layoutWidth << "textDocWidth=" << textDocWidth << "single page has size" << m_paperSize << "pixels";
119
120 /* cppcheck-suppress assertWithSideEffect */
121 Q_ASSERT(m_layout->numberOfPages() == 1);
122 }
123 // at this point m_pageContentSizeDirty has been set to false in all cases
124 }
125
126 m_layout->ensureLayouted();
127}
128
129// The height of the text doc, by calculation. Adjusted by caller, if negative.
131{
132 qreal textDocHeight = paperSize().height() - mmToPixels(m_marginTop + m_marginBottom);
133 const qreal headerHeight = m_headers.height();
134 textDocHeight -= headerHeight;
135 textDocHeight -= mmToPixels(m_headerBodySpacing);
136 const qreal footerHeight = m_footers.height();
137 textDocHeight -= mmToPixels(m_footerBodySpacing);
138 textDocHeight -= footerHeight;
139 // qDebug() << "pageContent height (pixels): paper size" << m_paperSize.height() << "minus margins" << mmToPixels( m_marginTop + m_marginBottom )
140 // << "minus headerHeight" << headerHeight << "minus footerHeight" << footerHeight << "and spacings =" << textDocHeight;
141 return textDocHeight;
142}
143
145{
146 const bool skip = rawMainTextDocHeight() <= 0;
147 if (skip) {
148 qDebug() << "Not enough height for headers and footers in this page size, hiding headers and footers.";
149 }
150 return skip;
151}
152
154{
155 const qreal height = rawMainTextDocHeight();
156 const bool skip = height <= 0;
157 if (skip) {
158 qreal textDocHeight = paperSize().height() - mmToPixels(m_marginTop + m_marginBottom);
159 textDocHeight -= mmToPixels(m_headerBodySpacing);
160 textDocHeight -= mmToPixels(m_footerBodySpacing);
161 return textDocHeight;
162 }
163 return height;
164}
165
167{
168 const int left = qRound(mmToPixels(m_marginLeft));
169 const int top = qRound(mmToPixels(m_marginTop));
170 const int headerHeightWithSpacing = qRound((skipHeadersFooters() ? 0 : m_headers.height()) + mmToPixels(m_headerBodySpacing));
171 const int textDocWidth = qRound(m_paperSize.width() - mmToPixels(m_marginLeft + m_marginRight));
172 const int textDocHeight = qRound(mainTextDocHeight());
173 return {left, top + headerHeightWithSpacing, textDocWidth, textDocHeight};
174}
175
176/*
177 [top margin]
178 [header]
179 [m_headerBodySpacing]
180 [body]
181 [m_footerBodySpacing]
182 [footer]
183 [bottom margin]
184 */
186{
187 Q_ASSERT(!wantEndlessPrinting()); // call ensureLayouted instead!
188
189 m_paperSize = paperSize;
190 const qreal marginsInPixels = mmToPixels(m_marginLeft + m_marginRight);
191 qreal textDocWidth = m_paperSize.width() - marginsInPixels;
192
193 m_headers.layoutWithTextWidth(textDocWidth);
194 m_footers.layoutWithTextWidth(textDocWidth);
195
196 const qreal textDocHeight = mainTextDocHeight();
197
198 // Font scaling
199 // Problem: how to re-implement this without a layout document?
200 // We would risk cumulating rounding problems...?
201 // ### 2nd problem: what about fonts in headers and footers? shouldn't they scale too?
202 // if ( m_scaleFontsBy != 1.0 )
203 // m_textDocument.scaleFontsBy( m_scaleFontsBy );
204
205 m_layout->setPageContentSize(QSizeF(textDocWidth, textDocHeight));
206
207 m_pageContentSizeDirty = false;
208}
209
210KDReports::Header *KDReports::HeaderMap::headerForPage(int pageNumber /* 1-based */, int pageCount) const
211{
212 Header *firstPageHeader = nullptr;
213 Header *lastPageHeader = nullptr;
214 Header *evenPagesHeader = nullptr;
215 Header *oddPagesHeader = nullptr;
216 for (const_iterator it = begin(); it != end(); ++it) {
217 const KDReports::HeaderLocations loc = it.key();
218 Header *const h = it.value();
219 if (loc & KDReports::FirstPage)
220 firstPageHeader = h;
221 if (loc & KDReports::LastPage)
222 lastPageHeader = h;
223 if (loc & KDReports::EvenPages)
224 evenPagesHeader = h;
225 if (loc & KDReports::OddPages)
226 oddPagesHeader = h;
227 }
228 if (pageNumber == 1 && firstPageHeader)
229 return firstPageHeader;
230 if (pageNumber == pageCount && lastPageHeader)
231 return lastPageHeader;
232 if (pageNumber & 1) // odd
233 return oddPagesHeader;
234 else // even
235 return evenPagesHeader;
236}
237
238//@cond PRIVATE
239KDReports::HeaderLocations KDReports::HeaderMap::headerLocation(Header *header) const
240{
241 for (const_iterator it = begin(); it != end(); ++it) {
242 const KDReports::HeaderLocations loc = it.key();
243 Header *const h = it.value();
244 if (h == header) {
245 return loc;
246 }
247 }
248 KDReports::HeaderLocations loc;
249 return loc;
250}
251
252void KDReports::ReportPrivate::paintPage(int pageNumber, QPainter &painter)
253{
254 ensureLayouted();
255
256 const int pageCount = m_layout->numberOfPages();
257 KDReports::Header *header = m_headers.headerForPage(pageNumber + 1, pageCount);
258 if (header) {
259 header->preparePaintingPage(pageNumber + m_firstPageNumber - 1);
260 }
261 KDReports::Header *footer = m_footers.headerForPage(pageNumber + 1, pageCount);
262 if (footer) {
263 footer->preparePaintingPage(pageNumber + m_firstPageNumber - 1);
264 }
265
266 if (m_watermarkFunction) {
267 m_watermarkFunction(painter, pageNumber);
268 }
269
270 const QRect textDocRect = mainTextDocRect();
271 const bool skipHeadersFooters = this->skipHeadersFooters();
272
273 /*qDebug() << "paintPage: in pixels: top=" << top << " headerHeight=" << headerHeightWithSpacing
274 << " textDoc size:" << textDocRect.size()
275 << " bottom=" << bottom << " footerHeight=" << footerHeight;*/
276
277 if (!m_watermarkText.isEmpty()) {
278 painter.save();
279 painter.translate(textDocRect.center());
280 painter.rotate(m_watermarkRotation);
281 painter.setFont(m_watermarkFont);
282 painter.setPen(m_watermarkColor);
283 const QSize textSize(painter.fontMetrics().size(Qt::TextSingleLine, m_watermarkText));
284 const QRect textRect(-textSize.width() / 2, -textSize.height() / 2, textSize.width(), textSize.height());
285 painter.drawText(textRect, Qt::AlignCenter, m_watermarkText);
286 painter.restore();
287 }
288
289 if (!m_watermarkImage.isNull()) {
290 // We paint it without scaling it, for better quality.
291 // But this means the actual size depends on the zoom level or printer resolution...
292 //
293 // It also means the image could end up being bigger than the page, and we don't want that.
294 // So we scale down if necessary. But never up.
295 const QSize scaledSize = m_watermarkImage.size().scaled(textDocRect.size(), Qt::KeepAspectRatio);
296 const QRect imageRect = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, scaledSize, textDocRect);
297 // qDebug() << "textDocRect=" << textDocRect << "size=" << scaledSize << "-> imageRect=" << imageRect;
298 painter.drawImage(imageRect, m_watermarkImage);
299 }
300
301 painter.save();
302 // painter.setClipRect( textDocRect, Qt::IntersectClip ); // triggers a Qt-Windows bug when printing
303 painter.setClipRect(textDocRect);
304 painter.translate(textDocRect.topLeft());
305 m_layout->paintPageContent(pageNumber, painter);
306 painter.restore();
307
309 ctx.palette.setColor(QPalette::Text, Qt::black);
310 if (header && !skipHeadersFooters) {
311 painter.save();
312 const int top = qRound(mmToPixels(m_marginTop));
313 painter.translate(textDocRect.left(), top);
314 ctx.clip = painter.clipRegion().boundingRect();
315 header->doc().contentDocument().documentLayout()->draw(&painter, ctx);
316 painter.restore();
317 }
318 if (footer && !skipHeadersFooters) {
319 painter.save();
320 const int bottom = qRound(mmToPixels(m_marginBottom));
321 const int footerHeight = qRound(m_footers.height());
322 painter.translate(textDocRect.left(), m_paperSize.height() - bottom - footerHeight);
323 ctx.clip = painter.clipRegion().boundingRect();
324 footer->doc().contentDocument().documentLayout()->draw(&painter, ctx);
325 painter.restore();
326 }
327}
328//@endcond
329
331{
332 m_headers.layoutWithTextWidth(docWidth);
333 m_footers.layoutWithTextWidth(docWidth);
334
335 const qreal docHeight = m_layout->layoutAsOnePage(docWidth);
336
337 qreal pageWidth = docWidth + mmToPixels(m_marginLeft + m_marginRight);
338 qreal pageHeight = docHeight + mmToPixels(m_marginTop + m_marginBottom);
339 pageHeight += m_headers.height();
340 pageHeight += m_footers.height();
341
342 m_pageContentSizeDirty = false;
343
344 // qDebug() << "One-page document is" << pageWidth << "x" << pageHeight;
345 return QSizeF(pageWidth, pageHeight);
346}
347
349{
350 m_pageContentSizeDirty = true;
351}
352
354{
355 // caller has to ensure that we have been layouted for this printer already
356 const int pageCount = m_layout->numberOfPages();
357 std::unique_ptr<QProgressDialog> dialog;
358 if (m_progressDialogEnabled && QThread::currentThread() == qApp->thread()) {
359 dialog.reset(new QProgressDialog(QObject::tr("Printing"), QObject::tr("Cancel"), 0, pageCount, parent));
360 dialog->setWindowModality(Qt::ApplicationModal);
361 }
362 QPainter painter;
363 if (!painter.begin(printer)) {
364 qWarning() << "QPainter failed to initialize on the given printer";
365 return false;
366 }
367
368 int fromPage = 0;
369 int toPage = pageCount;
370 if (printer->printRange() == QPrinter::PageRange) {
371 fromPage = printer->fromPage() - 1; // it starts at 1
372 toPage = printer->toPage(); // -1 because it starts at 1, and +1 because of '<'
373 if (toPage == 0)
374 toPage = pageCount;
375 }
376
377 bool firstPage = true;
378 for (int pageIndex = fromPage; pageIndex < toPage; ++pageIndex) {
379 if (dialog) {
380 dialog->setValue(pageIndex);
381 if (dialog->wasCanceled())
382 break;
383 }
384 emit q->printingProgress(pageIndex);
385
386 if (!firstPage)
387 printer->newPage();
388
389 paintPage(pageIndex, painter);
390 firstPage = false;
391 }
392
393 return true;
394}
395
396#ifndef NDEBUG
398{
399 // for calling from gdb
400
401 QFile html(QFile::decodeName(fileName) + QStringLiteral(".html"));
402 Q_ASSERT(html.open(QIODevice::WriteOnly));
403 const QString htmlText = m_layout->toHtml();
404 html.write(htmlText.toUtf8());
405 html.close();
406
407 bool oldLayoutDirty = true;
408 m_pageContentSizeDirty = false;
409 QPrinter printer;
410 q->setupPrinter(&printer);
411 printer.setOutputFileName(QFile::decodeName(fileName));
412 doPrint(&printer, nullptr);
413 printer.setOutputFileName(QString());
414 m_pageContentSizeDirty = oldLayoutDirty;
415}
416#endif
417
419Q_GLOBAL_STATIC(ModelMap, globalModelMap)
420
422{
423 return globalModelMap()->value(key, 0);
424}
425
427{
428 return paperSize().width() - mmToPixels(m_marginLeft + m_marginRight);
429}
430
432{
433 if (m_reportMode == KDReports::Report::WordProcessing)
434 return static_cast<TextDocReportLayout *>(m_layout)->builder();
435 return nullptr;
436}
437
439
441 : QObject(parent)
442 , d(new ReportPrivate(this))
443{
445}
446
450
452{
453 if (d->m_reportMode != WordProcessing) {
454 qWarning("KDReports: addInlineElement is only supported in WordProcessing mode");
455 } else {
456 d->builder()->addInlineElementPublic(element);
457 // don't add anything else here, it won't be called from the xml parser
458 }
459}
460
461void KDReports::Report::addElement(const Element &element, Qt::AlignmentFlag horizontalAlignment, const QColor &backgroundColor)
462{
463 if (d->m_reportMode != WordProcessing) {
464 qWarning("KDReports: addElement is only supported in WordProcessing mode");
465 } else {
466 d->builder()->addBlockElementPublic(element, horizontalAlignment, backgroundColor);
467 // don't add anything else here, it won't be called from the xml parser
468 }
469}
470
472{
473 if (d->m_reportMode != WordProcessing) {
474 qWarning("KDReports: addVerticalSpacing is only supported in WordProcessing mode");
475 } else {
476 d->builder()->addVerticalSpacingPublic(space);
477 }
478}
479
480#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
482{
483 setPageSize(static_cast<QPageSize::PageSizeId>(size));
484}
485#endif
486
488{
489 setPageSize(QPageSize {size});
490}
491
493{
494 d->m_pageSize = size;
495 d->m_paperSize = QSizeF();
496 d->m_pageContentSizeDirty = true;
497}
498
500{
501 qreal factor = 1.0;
502 switch (unit) {
504 break;
506 factor = mmToPixels(1.0);
507 break;
508 case QPrinter::Point:
509 factor = 72.0 * qt_defaultDpi();
510 break;
511 case QPrinter::Inch:
512 factor = qt_defaultDpi();
513 break;
514 default:
515 qWarning("Unsupported printer unit %d", unit);
516 }
517 d->m_paperSize = QSizeF(paperSize.width() * factor, paperSize.height() * factor);
518 d->m_pageContentSizeDirty = true;
519}
520
521#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
523{
524 return static_cast<QPrinter::PageSize>(d->m_pageSize.id());
525}
526#else
528{
529 return d->m_pageSize;
530}
531#endif
532
533#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
534void KDReports::Report::setOrientation(QPrinter::Orientation orientation)
535{
536 d->m_orientation = static_cast<QPageLayout::Orientation>(orientation);
537 d->m_paperSize = QSizeF();
538 d->m_pageContentSizeDirty = true;
539}
540
541QPrinter::Orientation KDReports::Report::orientation() const
542{
543 return static_cast<QPrinter::Orientation>(d->m_orientation);
544}
545#endif
546
548{
549 d->m_orientation = orientation;
550 d->m_paperSize = QSizeF();
551 d->m_pageContentSizeDirty = true;
552}
553
555{
556 return d->m_orientation;
557}
558
559void KDReports::Report::setMargins(qreal top, qreal left, qreal bottom, qreal right)
560{
561 d->m_marginTop = top;
562 d->m_marginLeft = left;
563 d->m_marginBottom = bottom;
564 d->m_marginRight = right;
565
566 // We'll need to call setPaperSizeFromPrinter, to update % sizes in all text documents.
567 d->m_pageContentSizeDirty = true;
568}
569
570void KDReports::Report::getMargins(qreal *top, qreal *left, qreal *bottom, qreal *right) const
571{
572 *top = d->m_marginTop;
573 *left = d->m_marginLeft;
574 *bottom = d->m_marginBottom;
575 *right = d->m_marginRight;
576}
577
579{
580 d->m_marginLeft = left;
581
582 // We'll need to call setPaperSizeFromPrinter, to update % sizes in all text documents.
583 d->m_pageContentSizeDirty = true;
584}
585
587{
588 return d->m_marginLeft;
589}
590
592{
593 d->m_marginRight = right;
594
595 // We'll need to call setPaperSizeFromPrinter, to update % sizes in all text documents.
596 d->m_pageContentSizeDirty = true;
597}
598
600{
601 return d->m_marginRight;
602}
603
605{
606 d->m_marginTop = top;
607
608 // We'll need to call setPaperSizeFromPrinter, to update % sizes in all text documents.
609 d->m_pageContentSizeDirty = true;
610}
611
613{
614 return d->m_marginTop;
615}
616
618{
619 d->m_marginBottom = bottom;
620
621 // We'll need to call setPaperSizeFromPrinter, to update % sizes in all text documents.
622 d->m_pageContentSizeDirty = true;
623}
624
626{
627 return d->m_marginBottom;
628}
629
631{
632 if (widthMM) {
633 d->m_endlessPrinterWidth = widthMM;
634 d->m_layoutWidth = mmToPixels(widthMM);
635 d->m_pageContentSizeDirty = true;
636 d->ensureLayouted();
637 } else {
638 d->m_layoutWidth = 0;
639 d->m_pageContentSizeDirty = true;
640 // caller will call setPageSize...
641 }
642}
643
644void KDReports::Report::paintPage(int pageNumber, QPainter &painter)
645{
646 d->paintPage(pageNumber, painter);
647}
648
650{
651 d->ensureLayouted();
652 return d->m_layout->numberOfPages();
653}
654
656{
657 qDebug() << asHtml();
658}
659
660QString KDReports::Report::asHtml() const
661{
662 return d->m_layout->toHtml();
663}
664
666{
667 d->m_progressDialogEnabled = enable;
668}
669
671{
672 QPrinter printer;
673 setupPrinter(&printer);
674 QPointer<QPrintDialog> dialog = new QPrintDialog(&printer, parent);
675 dialog->setMinMax(1, numberOfPages());
676 bool ok = false;
677 if (dialog->exec() == QDialog::Accepted) {
678 // Well, the user can modify the page size in the printer dialog too - ensure layout matches
679 d->ensureLayouted();
680 ok = d->doPrint(&printer, parent);
681 }
682 delete dialog;
683 return ok;
684}
685
687{
688 // save the old page size
689 const auto savePageSize = pageSize();
690 if (d->wantEndlessPrinting()) {
691 // ensure that the printer is set up with the right size
692 d->ensureLayouted();
693 // was: printer->setPaperSize(d->m_paperSize, QPrinter::DevicePixel);
694 printer->setPageSize(QPageSize(d->m_paperSize * pixelsToPointsMultiplier(printer->resolution()), QPageSize::Point));
695
696 } else {
697 // ensure that the layout matches the printer
698 d->setPaperSizeFromPrinter(printer->pageLayout().fullRectPixels(printer->resolution()).size());
699 }
700
701 printer->setFullPage(true);
702
703 // don't call ensureLayouted here, it would use the wrong printer!
704
705 const bool ret = d->doPrint(printer, parent);
706
707 // Reset the page size
708 setPageSize(savePageSize);
709
710 return ret;
711}
712
713bool KDReports::Report::exportToFile(const QString &fileName, QWidget *parent)
714{
715 d->ensureLayouted();
716 QPrinter printer;
717 printer.setOutputFileName(fileName); // must be done before setupPrinter, since it affects DPI
718 setupPrinter(&printer);
719 const bool ret = d->doPrint(&printer, parent);
720 printer.setOutputFileName(QString());
721 return ret;
722}
723
725{
726 const QString html = asHtml();
727 QFile file(fileName);
728 if (file.open(QIODevice::WriteOnly)) {
729 file.write(html.toUtf8());
730 d->m_layout->finishHtmlExport();
731 return true;
732 }
733 return false;
734}
735
736bool KDReports::Report::exportToImage(QSize size, const QString &fileName, const char *format)
737{
738 // Get the document to fit into one page
739
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;
744 d->ensureLayouted();
745
746 const qreal zoomFactor = qMin(( qreal )size.width() / d->m_paperSize.width(), ( qreal )size.height() / d->m_paperSize.height());
747 // qDebug() << "zoomFactor=" << zoomFactor;
748
750 image.fill(Qt::white);
751
752 QPainter painter;
753 if (!painter.begin(&image)) {
754 qWarning() << "QPainter failed to initialize on the given image of size" << size;
755 return false;
756 }
759 painter.fillRect(QRectF(0, 0, size.width(), size.height()), QBrush(Qt::white));
760 painter.scale(zoomFactor, zoomFactor);
761 d->paintPage(0, painter);
762
763 // restore textdoc size and header widths
764 d->m_layoutWidth = saveLayoutWidth;
765 setPageSize(savePageSize); // redo layout
766 qDebug() << "Saving pixmap" << image.size() << "into" << fileName << "with format" << format;
767 return image.save(fileName, format);
768}
769
770KDReports::Header &KDReports::Report::header(HeaderLocations hl)
771{
772 if (!d->m_headers.contains(hl))
773 d->m_headers.insert(hl, new Header(this));
774 return *d->m_headers.value(hl);
775}
776
777void KDReports::Report::setHeaderLocation(HeaderLocations hl, Header *header)
778{
779 // Remove old entry for this header
780 HeaderLocations loc = d->m_headers.headerLocation(header);
781 d->m_headers.remove(loc);
782 d->m_headers.insert(hl, header);
783}
784
785KDReports::Header &KDReports::Report::footer(HeaderLocations hl)
786{
787 if (!d->m_footers.contains(hl))
788 d->m_footers.insert(hl, new Header(this));
789 return *d->m_footers.value(hl);
790}
791
792void KDReports::Report::setFooterLocation(HeaderLocations hl, Footer *footer)
793{
794 // Remove old entry for this header
795 HeaderLocations loc = d->m_footers.headerLocation(footer);
796 d->m_footers.remove(loc);
797 d->m_footers.insert(hl, footer);
798}
799
801{
802 return KDReports::mmToPixels(mm);
803}
804
806{
807 QDomDocument doc;
808 // Read document from the QIODevice, check for errors
809
810 // We need to be able to see the space in <text> </text>, this is why
811 // we activate the "report-whitespace-only-CharData" feature.
812 // Unfortunately this leads to lots of whitespace text nodes in between real
813 // elements in the rest of the document, watch out for that.
814 if (iodevice->isOpen())
815 iodevice->reset(); // need to do that to allow consecutive calls of loadFromXML()
816 else
817 iodevice->open(QIODevice::ReadOnly);
818
819 QString errorMsg;
820 int errorLine = 0;
821 int errorColumn = 0;
822 bool ret = doc.setContent(iodevice, true, &errorMsg, &errorLine, &errorColumn);
823 if (!ret) {
824 if (details) {
825 details->setLine(errorLine);
826 details->setColumn(errorColumn);
827 details->setDriverMessage(errorMsg);
828 } else
829 qWarning("Malformed XML read in KDReports::Report::loadFromXML(): error message = %s, error line = %d, error column = %d", qPrintable(errorMsg), errorLine, errorColumn);
830 return false;
831 }
832 return loadFromXML(doc, details);
833}
834
836{
837 XmlParser parser(d->m_textValues, d->m_imageValues, d->m_xmlElementHandler, this, details);
838 d->m_pageContentSizeDirty = true;
839 return parser.processDocument(doc, d->builder());
840}
841
843{
844 globalModelMap()->insert(modelKey, model);
845}
846
847KDReports::TextDocument &KDReports::Report::doc() const
848{
849 Q_ASSERT(d->m_reportMode == WordProcessing);
850 return static_cast<TextDocReportLayout *>(d->m_layout)->textDocument();
851}
852
857
859{
860 d->builder()->contentDocumentCursor().endEditBlock();
861}
862
864{
865 const QRect textDocRect = d->mainTextDocRect();
866 const QPoint textPos = pos - textDocRect.topLeft();
867 return d->m_layout->anchorAt(pageNumber, textPos);
868}
869
871{
872 if (d->m_reportMode == WordProcessing) {
873 return &static_cast<TextDocReportLayout *>(d->m_layout)->textDocument().contentDocument();
874 } else {
875 return nullptr;
876 }
877}
878
879void KDReports::Report::setWatermarkText(const QString &text, int rotation, const QColor &color, const QFont &font)
880{
881 d->m_watermarkText = text;
882 d->m_watermarkRotation = rotation;
883 d->m_watermarkColor = color;
884 d->m_watermarkFont = font;
885}
886
888{
889 return d->m_watermarkText;
890}
891
893{
894 return d->m_watermarkRotation;
895}
896
898{
899 return d->m_watermarkColor;
900}
901
903{
904 return d->m_watermarkFont;
905}
906
907void KDReports::Report::setWatermarkPixmap(const QPixmap &pixmap, bool autoGrayOut)
908{
909 QPixmap pix(pixmap);
910 if (autoGrayOut) {
911 QStyleOption opt(0);
912 opt.palette = QApplication::palette();
913 pix = qApp->style()->generatedIconPixmap(QIcon::Disabled, pixmap, &opt);
914 }
915 setWatermarkImage(pix.toImage());
916}
917
919{
920 return QPixmap::fromImage(d->m_watermarkImage);
921}
922
924{
925 d->m_watermarkImage = image;
926}
927
929{
930 return d->m_watermarkImage;
931}
932
934{
935 d->m_watermarkFunction = std::move(function);
936}
937
939{
940 return d->m_watermarkFunction;
941}
942
944{
945 d->builder()->addPageBreakPublic();
946}
947
949{
950 d->m_layout->updateTextValue(id, value); // in case the document is built already
951 d->m_headers.updateTextValue(id, value);
952 d->m_footers.updateTextValue(id, value);
953 d->m_textValues.insert(id, value); // in case the document isn't built yet
954}
955
957{
958 d->m_imageValues.insert(id, value.toImage());
959}
960
962{
963 d->m_imageValues.insert(id, value);
964}
965
967{
968 return d->paperSize();
969}
970
972{
973 d->builder()->insertFragmentPublic(fragment);
974}
975
977{
978 d->m_layout->setDefaultFont(font);
979 d->m_pageContentSizeDirty = true;
980}
981
983{
984 return d->m_layout->defaultFont();
985}
986
988{
989 d->m_headerBodySpacing = spacing;
990 d->m_pageContentSizeDirty = true;
991}
992
994{
995 return d->m_headerBodySpacing;
996}
997
999{
1000 d->m_footerBodySpacing = spacing;
1001 d->m_pageContentSizeDirty = true;
1002}
1003
1005{
1006 return d->m_footerBodySpacing;
1007}
1008
1009void KDReports::Report::scaleTo(int numPagesHorizontally, int numPagesVertically)
1010{
1011 d->m_layout->scaleTo(numPagesHorizontally, numPagesVertically);
1012}
1013
1015{
1016 return d->m_layout->maximumNumberOfPagesForHorizontalScaling();
1017}
1018
1020{
1021 return d->m_layout->maximumNumberOfPagesForVerticalScaling();
1022}
1023
1025{
1026 d->m_layout->setFixedRowHeight(mmToPixels(mm));
1027}
1028
1030{
1031 d->m_layout->setUserRequestedFontScalingFactor(factor);
1032}
1033
1035{
1036 return d->m_layout->userRequestedFontScalingFactor();
1037}
1038
1040{
1041 return maximumNumberOfPagesForHorizontalScaling() != 1 || maximumNumberOfPagesForVerticalScaling() > 0;
1042}
1043
1045{
1046 if (d->m_reportMode != SpreadSheet) {
1047 qWarning("setTableBreakingPageOrder is only supported in SpreadSheet mode");
1048 } else {
1049 mainTable()->setTableBreakingPageOrder(pageOrder);
1050 }
1051}
1052
1054{
1055 if (d->m_reportMode != SpreadSheet) {
1056 qWarning("tableBreakingPageOrder is only supported in SpreadSheet mode");
1057 return DownThenRight;
1058 } else {
1059 return mainTable()->tableBreakingPageOrder();
1060 }
1061}
1062
1064{
1065 if (d->m_reportMode == WordProcessing)
1066 doc().regenerateAutoTables();
1067}
1068
1070{
1071 if (d->m_reportMode == WordProcessing)
1072 doc().regenerateAutoTableForModel(model);
1073}
1074
1076{
1077 if (d->m_reportMode == WordProcessing)
1078 return doc().autoTableElements();
1079 return {};
1080}
1081
1083{
1084 d->m_xmlElementHandler = handler;
1085}
1086
1088{
1089 d->m_currentModel = model;
1090 d->m_currentRow = row;
1091}
1092
1094{
1095 d->m_documentName = name;
1096}
1097
1099{
1100 return d->m_documentName;
1101}
1102
1104{
1105 d->builder()->setTabPositions(tabs);
1106}
1107
1108void KDReports::Report::setParagraphMargins(qreal left, qreal top, qreal right, qreal bottom)
1109{
1110 d->builder()->setParagraphMargins(left, top, right, bottom);
1111}
1112
1114{
1115 if (d->m_reportMode != reportMode) {
1116 d->m_reportMode = reportMode;
1117 delete d->m_layout;
1118 switch (reportMode) {
1119 case WordProcessing:
1120 d->m_layout = new TextDocReportLayout(this);
1121 break;
1122 case SpreadSheet:
1123 auto *sslayout = new SpreadsheetReportLayout(this);
1124 d->m_layout = sslayout;
1125 mainTable()->setLayout(sslayout);
1126 break;
1127 };
1128 }
1129}
1130
1132{
1133 return d->m_reportMode;
1134}
1135
1137{
1138 Q_ASSERT(d->m_reportMode == SpreadSheet);
1139 return d->m_mainTable;
1140}
1141
1143{
1144 QTextOption::Tab tab;
1145 tab.position = -1;
1146 tab.type = QTextOption::RightTab;
1147 tab.delimiter = QChar::fromLatin1('P'); // a bit hackish, but this is how we tell TextDocumentData::updatePercentSize
1148 return tab;
1149}
1150
1152{
1153 QTextOption::Tab tab;
1154 tab.position = -1;
1155 tab.type = QTextOption::CenterTab;
1156 tab.delimiter = QChar::fromLatin1('P'); // a bit hackish, but this is how we tell TextDocumentData::updatePercentSize
1157 return tab;
1158}
1159
1160KDReports::HeaderLocations KDReports::Report::headerLocation(KDReports::Header *header) const
1161{
1162 return d->m_headers.headerLocation(header);
1163}
1164
1165KDReports::HeaderLocations KDReports::Report::footerLocation(KDReports::Footer *footer) const
1166{
1167 return d->m_footers.headerLocation(footer);
1168}
1169
1171{
1172 return d->builder()->currentPosition();
1173}
1174
1176{
1177 d->m_firstPageNumber = num;
1178}
1179
1181{
1182 return d->m_firstPageNumber;
1183}
1184
1185void KDReports::Report::setupPrinter(QPrinter *printer)
1186{
1187 printer->setFullPage(true);
1188 printer->setPageOrientation(d->m_orientation);
1189 // was: printer->setPaperSize(rawPaperSize(d->m_pageSize, printer), QPrinter::DevicePixel);
1190 printer->setPageSize(d->m_pageSize);
1191 printer->setDocName(d->m_documentName);
1192}
QT_BEGIN_NAMESPACE Q_GUI_EXPORT int qt_defaultDpi()
QMap< QString, QAbstractItemModel * > ModelMap
QT_BEGIN_NAMESPACE Q_GUI_EXPORT int qt_defaultDpi()
void setDriverMessage(const QString &message)
KDReports::HeaderLocations headerLocation(Header *header) const
Header * headerForPage(int pageNumber, int pageCount) const
void paintPage(int pageNumber, QPainter &painter)
void debugLayoutToPdf(const char *fileName)
void setPaperSizeFromPrinter(QSizeF paperSize)
bool doPrint(QPrinter *printer, QWidget *parent)
QSizeF layoutAsOnePage(qreal docWidth)
ReportBuilder * builder() const
void associateTextValue(const QString &id, const QString &value)
void addElement(const Element &element, Qt::AlignmentFlag horizontalAlignment=Qt::AlignLeft, const QColor &backgroundColor=QColor())
bool isTableBreakingEnabled() const
void setWatermarkPixmap(const QPixmap &pixmap, bool autoGrayOut=true)
void setWatermarkImage(const QImage &image)
QPageSize pageSize() const
qreal leftPageMargins() const
void setTopPageMargin(qreal top)
void setPageSize(QPageSize::PageSizeId size)
void setWatermarkText(const QString &text, int rotation=0, const QColor &color=QColor(204, 204, 204), const QFont &font=QFont(QStringLiteral("Helvetica"), 48))
QString watermarkText() const
Header & header(HeaderLocations hl=AllPages)
qreal headerBodySpacing() const
void setFontScalingFactor(qreal factor)
Scale the fonts in the document by a given factor.
qreal bottomPageMargins() const
QFont watermarkFont() const
qreal topPageMargins() const
bool print(QPrinter *printer, QWidget *parent=nullptr)
void setDefaultFont(const QFont &font)
static QTextOption::Tab rightAlignedTab()
void associateModel(const QString &modelKey, QAbstractItemModel *model)
void associateImageValue(const QString &id, const QPixmap &value)
Footer & footer(HeaderLocations hl=AllPages)
QSizeF paperSize() const
bool exportToImage(QSize size, const QString &fileName, const char *format)
void getMargins(qreal *top, qreal *left, qreal *bottom, qreal *right) const
void paintPage(int pageNumber, QPainter &painter)
qreal rightPageMargins() const
int maximumNumberOfPagesForVerticalScaling() const
void setFixedRowHeight(qreal mm)
void setWatermarkFunction(WatermarkFunction function)
void setTableBreakingPageOrder(TableBreakingPageOrder pageOrder)
QPageLayout::Orientation pageOrientation() const
static QTextOption::Tab middleAlignedTab()
void setPaperSize(QSizeF paperSize, QPrinter::Unit unit)
void regenerateAutoTableForModel(QAbstractItemModel *model)
int watermarkRotation() const
QColor watermarkColor() const
void setTabPositions(const QList< QTextOption::Tab > &tabs)
static qreal mmToPixels(qreal mm)
QFont defaultFont() const
bool printWithDialog(QWidget *parent)
void setMargins(qreal top, qreal left, qreal bottom, qreal right)
void addVerticalSpacing(qreal space)
void setWidthForEndlessPrinter(qreal widthMM)
void addFragment(const QTextDocumentFragment &fragment)
int maximumNumberOfPagesForHorizontalScaling() const
void setHeaderLocation(HeaderLocations hl, Header *header)
void setXmlElementHandler(KDReports::XmlElementHandler *handler)
void setHeaderBodySpacing(qreal spacing)
void setProgressDialogEnabled(bool enable)
WatermarkFunction watermarkFunction() const
MainTable * mainTable() const
qreal fontScalingFactor() const
std::function< void(QPainter &, int)> WatermarkFunction
QTextDocument * mainTextDocument() const
bool exportToFile(const QString &fileName, QWidget *parent=nullptr)
void setCurrentRow(const QAbstractItemModel *model, int row)
ReportMode reportMode() const
void setParagraphMargins(qreal left, qreal top, qreal right, qreal bottom)
void setBottomPageMargin(qreal bottom)
void scaleTo(int numPagesHorizontally, int numPagesVertically)
Ensure that the report fits into a number of pages.
QImage watermarkImage() const
void setFirstPageNumber(int num)
Report(QObject *parent=nullptr)
QPixmap watermarkPixmap() const
qreal footerBodySpacing() const
KDReports::HeaderLocations footerLocation(KDReports::Footer *footer) const
bool exportToHtml(const QString &fileName)
void setLeftPageMargin(qreal left)
KDReports::HeaderLocations headerLocation(Header *header) const
void setRightPageMargin(qreal right)
void addInlineElement(const Element &element)
TableBreakingPageOrder tableBreakingPageOrder() const
void setReportMode(ReportMode reportMode)
void setFooterLocation(HeaderLocations hl, Footer *footer)
bool loadFromXML(QIODevice *iodevice, ErrorDetails *details=nullptr)
QString anchorAt(int pageNumber, QPoint pos) const
void setDocumentName(const QString &name)
void setPageOrientation(QPageLayout::Orientation orientation)
void setFooterBodySpacing(qreal spacing)
QList< KDReports::AutoTableElement * > autoTableElements() const
bool processDocument(const QDomDocument &document, KDReports::ReportBuilder *builder)
KDREPORTS_EXPORT qreal mmToPixels(qreal mm)
KDREPORTS_EXPORT qreal pixelsToPointsMultiplier(double resolution)
QAbstractItemModel * modelForKey(const QString &key)
@ FirstPage
The first page of the report.
@ EvenPages
The even pages of the report: 2, 4, 6 etc.
@ LastPage
The last page of the report.
@ OddPages
The odd pages of the report: 1 (unless FirstPage has its own header), 3, 5, 7 etc.
QChar fromLatin1(char c)
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QString decodeName(const QByteArray &localFileName)
virtual bool open(QIODevice::OpenMode mode) override
virtual void close() override
QSize size(int flags, const QString &text, int tabStops, int *tabArray) const const
QPalette palette()
Format_ARGB32_Premultiplied
void fill(uint pixelValue)
bool save(const QString &fileName, const char *format, int quality) const const
QSize size() const const
bool isOpen() const const
virtual bool open(QIODevice::OpenMode mode)
virtual bool reset()
qint64 write(const char *data, qint64 maxSize)
QThread * thread() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QRect fullRectPixels(int resolution) const const
bool begin(QPaintDevice *device)
QRegion clipRegion() const const
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags)
void drawText(const QPointF &position, const QString &text)
void fillRect(const QRectF &rectangle, const QBrush &brush)
QFontMetrics fontMetrics() const const
void restore()
void rotate(qreal angle)
void save()
void scale(qreal sx, qreal sy)
void setClipRect(const QRectF &rectangle, Qt::ClipOperation operation)
void setFont(const QFont &font)
void setPen(const QColor &color)
void setRenderHint(QPainter::RenderHint hint, bool on)
void translate(const QPointF &offset)
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
QImage toImage() const const
int fromPage() const const
virtual bool newPage() override
QPageLayout pageLayout() const const
QPrinter::PrintRange printRange() const const
int resolution() const const
void setDocName(const QString &name)
void setFullPage(bool fp)
void setOutputFileName(const QString &fileName)
bool setPageOrientation(QPageLayout::Orientation orientation)
bool setPageSize(const QPageSize &pageSize)
int toPage() const const
QPoint center() const const
int left() const const
QSize size() const const
QPoint topLeft() const const
QRect boundingRect() const const
int height() const const
QSize scaled(int width, int height, Qt::AspectRatioMode mode) const const
int width() const const
qreal height() const const
void transpose()
qreal width() const const
QByteArray toUtf8() const const
QRect alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment, const QSize &size, const QRect &rectangle)
AlignCenter
KeepAspectRatio
LeftToRight
TextSingleLine
ApplicationModal
void beginEditBlock()
QThread * currentThread()

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-reports/
Generated on Sun Apr 14 2024 00:04:57 for KD Reports API Documentation by doxygen 1.9.8