KD Reports API Documentation 2.2
Loading...
Searching...
No Matches
KDReportsTextDocumentData.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
17
18#include <QAbstractTextDocumentLayout>
19#include <QDebug>
20#include <QTextTable>
21#include <QUrl>
22
24 : m_usesTabPositions(false)
25{
26 m_document.setUseDesignMetrics(true);
27
29#ifdef HAVE_KDCHART
30 ChartTextObject::registerChartTextObjectHandler(&m_document);
31#endif
32}
33
37
38void KDReports::TextDocumentData::dumpTextValueCursors() const
39{
40 qDebug() << "Text value cursors: (document size=" << m_document.characterCount() << ")";
41 QMultiMap<QString, TextValueData>::const_iterator it = m_textValueCursors.begin();
42 while (it != m_textValueCursors.end()) {
43 const TextValueData &data = *it;
44 if (data.cursor.isNull()) {
45 qDebug() << it.key() << "unresolved cursor at pos" << data.initialPosition;
46 } else {
47 qDebug() << it.key() << "QTextCursor currently at pos" << data.cursor.position() << "length" << data.valueLength;
48 }
49 ++it;
50 }
51}
52
54{
55 resolveCursorPositions(mode);
56}
57
58void KDReports::TextDocumentData::resolveCursorPositions(ModificationMode mode)
59{
60 // We have to use QTextCursor in TextValueData so that it gets updated when
61 // we modify the document later on, but we can't just store the QTextCursor
62 // at insertion time; that cursor would be moved to the end of the document
63 // while the insertion keeps happening...
64 auto it = m_textValueCursors.begin();
65 for (; it != m_textValueCursors.end(); ++it) {
66 TextValueData &data = *it;
67 if (data.cursor.isNull()) {
68 // When appending, leave cursors "at end of document" unresolved.
69 // Otherwise they'll keep moving with insertions.
70 if (mode == Append && data.initialPosition >= m_document.characterCount() - 1) {
71 continue;
72 }
73 data.cursor = QTextCursor(&m_document);
74 data.cursor.setPosition(data.initialPosition);
75 // qDebug() << "Cursor for" << it.key() << "resolved at position" << data.initialPosition;
76 }
77 }
78 // dumpTextValueCursors();
79}
80
81void KDReports::TextDocumentData::setTextValueMarker(int pos, const QString &id, int valueLength, bool html)
82{
83 // qDebug() << "setTextValueMarker" << pos << id << valueLength << "in doc" << m_document;
84 TextValueData val;
85 val.valueLength = valueLength;
86 val.elementType = html ? ElementTypeHtml : ElementTypeText;
87 val.initialPosition = pos;
88 m_textValueCursors.insert(id, val);
89}
90
92{
93 aboutToModifyContents(Modify);
94
95 // qDebug() << "updateTextValue: looking for id" << id << "in doc" << m_document;
96
97 QMultiMap<QString, TextValueData>::iterator it = m_textValueCursors.find(id);
98 while (it != m_textValueCursors.end() && it.key() == id) {
99 TextValueData &data = *it;
100 // qDebug() << "Found at position" << data.cursor.position() << "length" << data.valueLength << "replacing with new value" << newValue;
101
102 QTextCursor c(data.cursor);
103 const int oldPos = data.cursor.position();
104 c.setPosition(oldPos + data.valueLength, QTextCursor::KeepAnchor);
105 const bool html = data.elementType == ElementTypeHtml;
106 if (html)
107 c.insertHtml(newValue);
108 else
109 c.insertText(newValue);
110 // update data
111 data.valueLength = c.position() - oldPos;
112 data.cursor.setPosition(oldPos);
113 // qDebug() << " stored new length" << data.valueLength;
114
115 ++it;
116 }
117
118 // dumpTextValueCursors();
119}
120
122{
123 if (!m_hasResizableImages && !m_usesTabPositions) {
124 return;
125 }
126 QTextCursor c(&m_document);
127 c.beginEditBlock();
128 if (m_hasResizableImages) {
129 do {
131 QTextCharFormat format = c.charFormat();
133 Q_ASSERT(format.isImageFormat());
134 QTextImageFormat imageFormat = format.toImageFormat();
135 updatePercentSize(imageFormat, size);
136 // qDebug() << "updatePercentSizes: setting image to " << imageFormat.width() << "," << imageFormat.height();
138 c.setCharFormat(imageFormat);
140 }
141 } while (!c.atEnd());
142 }
143
144 if (m_usesTabPositions) {
145 QTextFrameFormat rootFrameFormat = m_document.rootFrame()->frameFormat();
146 const qreal rootFrameMargins = rootFrameFormat.leftMargin() + rootFrameFormat.rightMargin();
147 QTextBlock block = m_document.firstBlock();
148 do {
149 QTextBlockFormat blockFormat = block.blockFormat();
150 QList<QTextOption::Tab> tabs = blockFormat.tabPositions();
151 // qDebug() << "Looking at block" << block.blockNumber() << "tabs:" << tabs.count();
152 if (!tabs.isEmpty()) {
153 for (QTextOption::Tab &tab : tabs) {
154 if (tab.delimiter == QLatin1Char('P') /* means Page -- see rightAlignedTab*/) {
155 const auto availableWidth = size.width() - rootFrameMargins - block.blockFormat().leftMargin() - block.blockFormat().rightMargin();
156 if (tab.type == QTextOption::RightTab) {
157 // qDebug() << "Adjusted RightTab from" << tab.position << "to" << availableWidth;
158 tab.position = availableWidth;
159 } else if (tab.type == QTextOption::CenterTab) {
160 tab.position = availableWidth / 2;
161 }
162 }
163 }
164 blockFormat.setTabPositions(tabs);
165 // qDebug() << "Adjusted tabs:" << tabs;
166 c.setPosition(block.position());
167 c.setBlockFormat(blockFormat);
168 }
169 block = block.next();
170 } while (block.isValid());
171 }
172 c.endEditBlock();
173}
174
176{
177 if (w != m_document.textWidth()) {
178 // qDebug() << "setTextWidth" << w;
179 m_document.setTextWidth(w);
180 updatePercentSizes(m_document.size());
181 }
182}
183
185{
186 if (size != m_document.pageSize()) {
187 // qDebug() << "setPageSize" << size;
188 m_document.setPageSize(size);
189 updatePercentSizes(size);
190 }
191}
192
194{
195 // "W50" means W=50%. "H60" means H=60%.
196 QString prop = imageFormat.property(ResizableImageProperty).toString();
197 const qreal imageRatio = imageFormat.height() / imageFormat.width();
198 const qreal pageWidth = size.width();
199 const qreal pageHeight = size.height();
200 const qreal pageRatio = pageWidth ? pageHeight / pageWidth : 0;
201 if (prop[0] == QLatin1Char('T')) {
202 // qDebug() << "updatePercentSize fitToPage" << imageRatio << pageRatio;
203 if (imageRatio < pageRatio) {
204 prop = QStringLiteral("W100");
205 } else {
206 prop = QStringLiteral("H100");
207 }
208 }
209 const qreal percent = prop.mid(1).toDouble();
210 switch (prop[0].toLatin1()) {
211 case 'W': {
212 const qreal newWidth = pageWidth * percent / 100.0;
213 imageFormat.setWidth(newWidth);
214 imageFormat.setHeight(newWidth * imageRatio);
215 // ### I needed to add this -2 here for 100%-width images to fit in
216 if (percent == 100.0)
217 imageFormat.setWidth(imageFormat.width() - 2);
218 } break;
219 case 'H':
220 imageFormat.setHeight(pageHeight * percent / 100.0);
221 // ### I needed to add -6 here for 100%-height images to fit in (with Qt-4.4)
222 // and it became -9 with Qt-4.5, and even QtSw doesn't know why.
223 // Task number 241890
224 if (percent == 100.0)
225 imageFormat.setHeight(imageFormat.height() - 10);
226 imageFormat.setWidth(imageRatio ? imageFormat.height() / imageRatio : 0);
227 // qDebug() << "updatePercentSize" << size << "->" << imageFormat.width() << "x" << imageFormat.height();
228 break;
229 default:
230 qWarning("Unhandled image format property type - internal error");
231 }
232}
233
235{
236 m_tables.append(table);
237}
238
240{
241 QTextCursor cursor(&m_document);
242 qreal currentPointSize = -1.0;
243 QTextCursor lastCursor(&m_document);
244 Q_FOREVER
245 {
246 qreal cursorFontPointSize = cursor.charFormat().fontPointSize();
247 // qDebug() << cursorFontPointSize << "last=" << currentPointSize << cursor.block().text() << "position=" << cursor.position();
248 if (cursorFontPointSize != currentPointSize) {
249 if (currentPointSize != -1.0) {
250 setFontSizeHelper(lastCursor, cursor.position() - 1, currentPointSize, factor);
251 lastCursor.setPosition(cursor.position() - 1, QTextCursor::MoveAnchor);
252 }
253 currentPointSize = cursorFontPointSize;
254 }
255 if (cursor.atEnd())
256 break;
258 }
259 if (currentPointSize != -1.0) {
260 setFontSizeHelper(lastCursor, cursor.position(), currentPointSize, factor);
261 }
262
263 // Also adjust the padding in the cells so that it remains proportional,
264 // and the column constraints.
265 Q_FOREACH (QTextTable *table, m_tables) {
266 QTextTableFormat format = table->format();
267 format.setCellPadding(format.cellPadding() * factor);
268
269 QVector<QTextLength> constraints = format.columnWidthConstraints();
270 for (int i = 0; i < constraints.size(); ++i) {
271 if (constraints[i].type() == QTextLength::FixedLength) {
272 constraints[i] = QTextLength(QTextLength::FixedLength, constraints[i].rawValue() * factor);
273 }
274 }
275 format.setColumnWidthConstraints(constraints);
276
277 table->setFormat(format);
278 }
279}
280
281void KDReports::TextDocumentData::setFontSizeHelper(QTextCursor &lastCursor, int endPosition, qreal pointSize, qreal factor)
282{
283 if (pointSize == 0) {
284 pointSize = m_document.defaultFont().pointSize();
285 }
286 pointSize *= factor;
287 QTextCharFormat newFormat;
288 newFormat.setFontPointSize(pointSize);
289 // qDebug() << "Applying" << pointSize << "from" << lastCursor.position() << "to" << endPosition;
290 lastCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
291 lastCursor.mergeCharFormat(newFormat);
292}
293
294//@cond PRIVATE
296{
297#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
298 QString htmlText = m_document.toHtml("utf-8");
299#else
300 QString htmlText = m_document.toHtml();
301#endif
302 htmlText.remove(QLatin1String("margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; "));
303 htmlText.remove(QLatin1String("-qt-block-indent:0; "));
304 htmlText.remove(QLatin1String("text-indent:0px;"));
305 htmlText.remove(QLatin1String("style=\"\""));
306 htmlText.remove(QLatin1String("style=\" \""));
307 return htmlText;
308}
309//@endcond
310
312{
313 registerTable(table);
314 m_autoTables.insert(table, *element); // make copy of the AutoTableElement
315}
316
317//@cond PRIVATE
319{
321 for (AutoTablesMaps::iterator it = m_autoTables.begin(); it != m_autoTables.end(); ++it)
322 lst.append(&it.value());
323 return lst;
324}
325
327{
328 // qDebug() << "regenerateAutoTables" << m_autoTables.count();
329 if (m_autoTables.isEmpty())
330 return;
331 aboutToModifyContents(Modify);
332 QTextCursor(&m_document).beginEditBlock();
333 // preciseDump();
334 AutoTablesMaps autoTables = m_autoTables; // make copy since it will be modified below.
335 m_autoTables.clear();
336 AutoTablesMaps::const_iterator it = autoTables.constBegin();
337 for (; it != autoTables.constEnd(); ++it) {
338 QTextTable *table = it.key();
339 const KDReports::AutoTableElement &tableElement = it.value();
340 regenerateOneTable(tableElement, table);
341 }
342 // preciseDump();
343 QTextCursor(&m_document).endEditBlock();
344}
345
347{
348 aboutToModifyContents(Modify);
349 QTextCursor(&m_document).beginEditBlock();
350 AutoTablesMaps::iterator it = m_autoTables.begin();
351 for (; it != m_autoTables.end(); ++it) {
352 KDReports::AutoTableElement tableElement = it.value();
353 if (tableElement.tableModel() == model) {
354 QTextTable *table = it.key();
355 m_autoTables.erase(it);
356 regenerateOneTable(tableElement, table);
357 break;
358 }
359 }
360 QTextCursor(&m_document).endEditBlock();
361}
362//@endcond
363
364void KDReports::TextDocumentData::regenerateOneTable(const KDReports::AutoTableElement &tableElement, QTextTable *table)
365{
366 QTextCursor cursor = table->firstCursorPosition();
367 cursor.beginEditBlock();
369 QTextCursor lastCurs = table->lastCursorPosition();
370 lastCurs.setPosition(lastCurs.position() + 1);
371 QTextBlockFormat blockFormat = lastCurs.blockFormat(); // preserve page breaks
373 cursor.removeSelectedText();
374 cursor.setBlockFormat(QTextBlockFormat()); // see preciseDump during TextDocument unittest
375 m_tables.removeAll(table);
376
377 ReportBuilder builder(*this, cursor, nullptr /* hack - assumes Report is not needed */);
378 bool isSet;
379 QFont font = tableElement.defaultFont(&isSet);
380 if (isSet) {
381 builder.setDefaultFont(font);
382 }
383 tableElement.build(builder); // this calls registerTable again
384
385 cursor.setBlockFormat(blockFormat);
386 cursor.endEditBlock();
387}
388
390{
391 Q_FOREACH (const QString &name, m_resourceNames) {
392 const QVariant v = m_document.resource(QTextDocument::ImageResource, QUrl(name));
393 QPixmap pix = v.value<QPixmap>();
394 if (!pix.isNull()) {
395 pix.save(name);
396 }
397 }
398}
399
401{
402 m_resourceNames.append(resourceName);
403}
404
406{
407 m_hasResizableImages = true;
408}
409
411{
412 m_usesTabPositions = usesTabs;
413}
QAbstractItemModel * tableModel() const
void build(ReportBuilder &) const override
static void registerHLineObjectHandler(QTextDocument *doc)
void addResourceName(const QString &resourceName)
void regenerateAutoTableForModel(QAbstractItemModel *model)
void aboutToModifyContents(ModificationMode mode)
QList< KDReports::AutoTableElement * > autoTableElements()
static void updatePercentSize(QTextImageFormat &format, QSizeF size)
void registerAutoTable(QTextTable *table, const KDReports::AutoTableElement *element)
void updateTextValue(const QString &id, const QString &newValue)
void setTextValueMarker(int pos, const QString &id, int valueLength, bool html)
static const int ResizableImageProperty
void append(const T &value)
bool isEmpty() const const
const Key & key() const const
const T & value() const const
const Key & key() const const
bool isNull() const const
bool save(const QString &fileName, const char *format, int quality) const const
qreal height() const const
qreal width() const const
QString mid(int position, int n) const const
QString & remove(int position, int n)
double toDouble(bool *ok) const const
QTextBlockFormat blockFormat() const const
bool isValid() const const
QTextBlock next() const const
int position() const const
qreal leftMargin() const const
qreal rightMargin() const const
void setTabPositions(const QList< QTextOption::Tab > &tabs)
QList< QTextOption::Tab > tabPositions() const const
qreal fontPointSize() const const
void setFontPointSize(qreal size)
bool atEnd() const const
void beginEditBlock()
QTextBlockFormat blockFormat() const const
QTextCharFormat charFormat() const const
void endEditBlock()
void insertHtml(const QString &html)
void insertText(const QString &text)
void mergeCharFormat(const QTextCharFormat &modifier)
bool movePosition(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode, int n)
int position() const const
void removeSelectedText()
void setBlockFormat(const QTextBlockFormat &format)
void setCharFormat(const QTextCharFormat &format)
void setPosition(int pos, QTextCursor::MoveMode m)
void setUseDesignMetrics(bool b)
bool hasProperty(int propertyId) const const
bool isImageFormat() const const
QVariant property(int propertyId) const const
QTextImageFormat toImageFormat() const const
QTextCursor firstCursorPosition() const const
QTextCursor lastCursorPosition() const const
qreal leftMargin() const const
qreal rightMargin() const const
qreal height() const const
void setHeight(qreal height)
void setWidth(qreal width)
qreal width() const const
QTextTableFormat format() const const
void setFormat(const QTextTableFormat &format)
qreal cellPadding() const const
QVector< QTextLength > columnWidthConstraints() const const
void setCellPadding(qreal padding)
void setColumnWidthConstraints(const QVector< QTextLength > &constraints)
QString toString() const const
T value() const const
int size() const const

© 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 Wed Apr 24 2024 04:08:15 for KD Reports API Documentation by doxygen 1.9.8