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

© 2007-2021 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 Fri Jul 15 2022 13:09:07 for KD Reports API Documentation by doxygen 1.8.20