21#include "markupdirector.h"
22#include "markupdirector_p.h"
24#include "abstractmarkupbuilder.h"
26#include <QtCore/QFlags>
28#include <QtCore/QStack>
29#include <QtCore/QString>
30#include <QtGui/QBrush>
31#include <QtGui/QColor>
32#include <QtGui/QTextCharFormat>
33#include <QtGui/QTextCursor>
34#include <QtGui/QTextDocument>
35#include <QtGui/QTextDocumentFragment>
36#include <QtGui/QTextFrame>
37#include <QtGui/QTextList>
38#include <QtGui/QTextTable>
43 : d_ptr(new MarkupDirectorPrivate(this)),
m_builder(builder)
52 while (!start.
atEnd() && start != end) {
55 auto table = qobject_cast<QTextTable *>(frame);
63 Q_ASSERT(block.isValid());
101 auto format = table->
format();
105 auto tableWidth = format.width();
109 sWidth = QStringLiteral(
"%1%");
110 sWidth = sWidth.
arg(tableWidth.rawValue());
112 sWidth = QStringLiteral(
"%1");
113 sWidth = sWidth.
arg(tableWidth.rawValue());
116 m_builder->beginTable(format.cellPadding(), format.cellSpacing(), sWidth);
118 auto headerRowCount = format.headerRowCount();
122 for (
auto row = 0; row < table->
rows(); ++row) {
136 for (
auto column = 0; column < table->
columns(); ++column) {
138 auto tableCell = table->
cellAt(row, column);
141 auto rowSpan = tableCell.rowSpan();
142 if ((rowSpan > 1) || (columnSpan > 1)) {
143 if (alreadyProcessedCells.
contains(tableCell)) {
147 alreadyProcessedCells.
append(tableCell);
151 auto cellWidth = colLengths.at(column);
156 sCellWidth = QStringLiteral(
"%1%");
157 sCellWidth = sCellWidth.
arg(cellWidth.rawValue());
159 sCellWidth = QStringLiteral(
"%1");
160 sCellWidth = sCellWidth.
arg(cellWidth.rawValue());
164 if (row < headerRowCount) {
165 m_builder->beginTableHeaderCell(sCellWidth, columnSpan, rowSpan);
167 m_builder->beginTableCell(sCellWidth, columnSpan, rowSpan);
172 if (row < headerRowCount) {
194std::pair<QTextFrame::iterator, QTextBlock>
201 while (block.isValid() && block.textList()) {
208 block = block.next();
209 if (block.isValid()) {
210 auto obj = block.document()->objectForFormat(block.blockFormat());
211 auto group = qobject_cast<QTextBlockGroup *>(obj);
212 if (group && group != list) {
228 auto blockAlignment = blockFormat.
alignment();
234 if (!frameIt.
atEnd())
239 auto it = block.
begin();
246 if (!frameIt.
atEnd())
267 while (!it.atEnd()) {
276 if (!frameIt.
atEnd())
355 auto paraClosed =
false;
366 }
else if (paraClosed) {
391 auto group = qobject_cast<QTextBlockGroup *>(
object);
400std::pair<QTextFrame::iterator, QTextBlock>
406 auto lastBlock = _block;
408 auto obj = block.document()->objectForFormat(block.blockFormat());
412 return {lastIt, lastBlock};
414 auto group = qobject_cast<QTextBlockGroup *>(obj);
416 return {lastIt, lastBlock};
418 while (block.isValid()) {
422 block = block.next();
426 obj = block.document()->objectForFormat(block.blockFormat());
430 nextGroup = qobject_cast<QTextBlockGroup *>(obj);
432 if (group == blockGroup || !nextGroup) {
438 return {lastIt, lastBlock};
441std::pair<QTextFrame::iterator, QTextBlock>
446 auto list = qobject_cast<QTextList *>(blockGroup);
464 if (fragmentFormat.isImageFormat()) {
465 auto imageFormat = fragmentFormat.toImageFormat();
496 if (d->m_openElements.isEmpty())
502 auto remainingSize = elementsToClose.size();
503 while (!elementsToClose.isEmpty()) {
504 auto tag = d->m_openElements.last();
505 if (elementsToClose.contains(tag)) {
544 d->m_openElements.removeLast();
545 elementsToClose.remove(tag);
547 previousSize = remainingSize;
548 remainingSize = elementsToClose.size();
550 if (previousSize == remainingSize) {
558 elementsToClose.insert(d->m_openElements.last());
568 if (!fragment.isValid())
574 Q_FOREACH (
int tag, elementsToOpenList) {
589 m_builder->beginFontPointSize(fragmentFormat.font().pointSize());
590 d->m_openFontPointSize = fragmentFormat.font().pointSize();
593#if QT_VERSION < QT_VERSION_CHECK(6, 1, 0)
594 d->m_openFontFamily = fragmentFormat.fontFamily();
596 d->m_openFontFamily = fragmentFormat.fontFamilies().toStringList().first();
598 m_builder->beginFontFamily(d->m_openFontFamily);
601 m_builder->beginBackground(fragmentFormat.background());
602 d->m_openBackground = fragmentFormat.background();
605 m_builder->beginForeground(fragmentFormat.foreground());
606 d->m_openForeground = fragmentFormat.foreground();
610 auto anchorNames = fragmentFormat.anchorNames();
611 if (!anchorNames.isEmpty()) {
612 while (!anchorNames.isEmpty()) {
613 auto n = anchorNames.last();
614 anchorNames.removeLast();
615 if (anchorNames.isEmpty()) {
617 m_builder->beginAnchor(fragmentFormat.anchorHref(), n);
627 m_builder->beginAnchor(fragmentFormat.anchorHref());
629 d->m_openAnchorHref = fragmentFormat.anchorHref();
641 d->m_openElements.append(tag);
642 d->m_elementsToOpen.remove(tag);
653#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
654 auto elementsToClose = d->m_openElements.toSet();
656 auto elementsToClose =
QSet<int>(d->m_openElements.begin(), d->m_openElements.end());
658 return elementsToClose.unite(d->m_elementsToOpen);
663 if (!fragment.isValid())
664 return closedElements;
666 auto fragmentFormat = fragment.charFormat();
668 auto fontWeight = fragmentFormat.fontWeight();
669 auto fontItalic = fragmentFormat.fontItalic();
670 auto fontUnderline = fragmentFormat.fontUnderline();
671 auto fontStrikeout = fragmentFormat.fontStrikeOut();
673 auto fontForeground = fragmentFormat.foreground();
674 auto fontBackground = fragmentFormat.background();
676#if QT_VERSION < QT_VERSION_CHECK(6, 1, 0)
677 auto fontFamily = fragmentFormat.fontFamily();
679 const QStringList fontFamilies = fragmentFormat.fontFamilies().toStringList();
682 auto fontPointSize = fragmentFormat.font().pointSize();
683 auto anchorHref = fragmentFormat.anchorHref();
685 auto vAlign = fragmentFormat.verticalAlignment();
690 && (d->m_openElements.contains(
StrikeOut)
691 || d->m_elementsToOpen.contains(
StrikeOut))) {
696 && (d->m_openElements.contains(
Underline)
697 || d->m_elementsToOpen.contains(
Underline))
698 && !(d->m_openElements.contains(
Anchor)
699 || d->m_elementsToOpen.contains(
Anchor))) {
704 && (d->m_openElements.contains(
Emph)
705 || d->m_elementsToOpen.contains(
Emph))) {
706 closedElements.insert(
Emph);
710 && (d->m_openElements.contains(
Strong)
711 || d->m_elementsToOpen.contains(
Strong))) {
712 closedElements.insert(
Strong);
717 && (d->m_openFontPointSize != fontPointSize)) {
723 && (d->m_openFontFamily != fontFamily)) {
728 && (d->m_openBackground != fontBackground))
730 && (d->m_backgroundToOpen != fontBackground))) {
735 && (d->m_openForeground != fontForeground))
737 && (d->m_foregroundToOpen != fontForeground))) {
741 if ((d->m_openElements.contains(
Anchor)
742 && (d->m_openAnchorHref != anchorHref))
743 || (d->m_elementsToOpen.contains(
Anchor)
744 && (d->m_anchorHrefToOpen != anchorHref))) {
745 closedElements.insert(
Anchor);
749 && (d->m_openElements.contains(
SubScript)
750 || d->m_elementsToOpen.contains(
SubScript))) {
759 return closedElements;
766 if (!fragment.isValid()) {
771 auto fontWeight = fragmentFormat.
fontWeight();
772 auto fontItalic = fragmentFormat.fontItalic();
773 auto fontUnderline = fragmentFormat.fontUnderline();
774 auto fontStrikeout = fragmentFormat.fontStrikeOut();
776 auto fontForeground = fragmentFormat.foreground();
777 auto fontBackground = fragmentFormat.background();
779#if QT_VERSION < QT_VERSION_CHECK(6, 1, 0)
780 auto fontFamily = fragmentFormat.fontFamily();
782 const QStringList fontFamilies = fragmentFormat.fontFamilies().toStringList();
785 auto fontPointSize = fragmentFormat.font().pointSize();
786 auto anchorHref = fragmentFormat.anchorHref();
788 auto vAlign = fragmentFormat.verticalAlignment();
792 if (superscript && !(d->m_openElements.contains(
SuperScript))) {
796 if (subscript && !(d->m_openElements.contains(
SubScript))) {
800 if (!anchorHref.isEmpty() && !(d->m_openElements.contains(
Anchor))
801 && (d->m_openAnchorHref != anchorHref)) {
802 d->m_elementsToOpen.insert(
Anchor);
803 d->m_anchorHrefToOpen = anchorHref;
810 && (fontForeground != d->m_openForeground)
811 && !((d->m_openElements.contains(
813 || d->m_elementsToOpen.contains(
Anchor)))) {
815 d->m_foregroundToOpen = fontForeground;
820 && (fontBackground != d->m_openBackground)) {
822 d->m_backgroundToOpen = fontBackground;
825 if (!fontFamily.isEmpty() && !(d->m_openElements.contains(
SpanFontFamily))
826 && (fontFamily != d->m_openFontFamily)) {
828 d->m_fontFamilyToOpen = fontFamily;
834 && (fontPointSize != d->m_openFontPointSize)) {
836 d->m_fontPointSizeToOpen = fontPointSize;
844 d->m_elementsToOpen.insert(
Strong);
847 if (fontItalic && !(d->m_openElements.contains(
Emph))) {
848 d->m_elementsToOpen.insert(
Emph);
851 if (fontUnderline && !(d->m_openElements.contains(
Underline))
852 && !(d->m_openElements.contains(
Anchor)
853 || d->m_elementsToOpen.contains(
859 if (fontStrikeout && !(d->m_openElements.contains(
StrikeOut))) {
863 if (d->m_elementsToOpen.size() <= 1) {
864#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
865 auto elementsToClose = d->m_elementsToOpen.toList();
867 return QList<int>(d->m_elementsToOpen.begin(), d->m_elementsToOpen.end());
883 while (openingOrder.
size() != 0) {
896 Q_FOREACH (
int tag, elementsToClose) {
897 if (openingOrder.
remove(tag)) {
898 sortedOpenedElements.
prepend(tag);
905 Q_FOREACH (
int tag, openingOrder) {
906 sortedOpenedElements.
prepend(tag);
911 return sortedOpenedElements;
Interface for creating marked-up text output.
virtual QTextFrame::iterator processBlock(QTextFrame::iterator it, const QTextBlock &block)
virtual void processTableCell(const QTextTableCell &tableCell, QTextTable *table)
virtual void processOpeningElements(QTextBlock::iterator it)
virtual QTextBlock::iterator processCharTextObject(QTextBlock::iterator it, const QTextFragment &fragment, QTextObject *textObject)
virtual QTextFrame::iterator processTable(QTextFrame::iterator it, QTextTable *table)
virtual void processCustomFragment(const QTextFragment &fragment, QTextDocument const *doc)
virtual void processDocument(QTextDocument *doc)
virtual QList< int > getElementsToOpen(QTextBlock::iterator it)
AbstractMarkupBuilder * m_builder
virtual QTextBlock::iterator processImage(QTextBlock::iterator it, const QTextImageFormat &imageFormat, QTextDocument *doc)
@ SpanFontPointSize
A font family altering span tag is open.
@ SpanForeground
An anchor tag is open.
@ SuperScript
No tags are open.
@ Underline
A emphasis tag is open.
@ SubScript
A superscript tag is open.
@ Emph
A strong tag is open.
@ Anchor
A subscript tag is open.
@ SpanFontFamily
A background altering span tag is open.
@ Strong
A font size altering span tag is open.
@ StrikeOut
An underline tag is open.
@ SpanBackground
A foreground altering span tag is open.
MarkupDirector(AbstractMarkupBuilder *builder)
virtual QTextFrame::iterator processFrame(QTextFrame::iterator it, QTextFrame *frame)
virtual std::pair< QTextFrame::iterator, QTextBlock > processBlockGroup(QTextFrame::iterator it, const QTextBlock &block, QTextBlockGroup *textBlockGroup)
virtual std::pair< QTextFrame::iterator, QTextBlock > processList(QTextFrame::iterator it, const QTextBlock &block, QTextList *textList)
virtual QSet< int > getElementsToClose(QTextBlock::iterator it) const
QList< int > sortOpeningOrder(QSet< int > openingTags, QTextBlock::iterator it) const
void processDocumentContents(QTextFrame::iterator begin, QTextFrame::iterator end)
virtual void processClosingElements(QTextBlock::iterator it)
virtual QTextFrame::iterator processObject(QTextFrame::iterator it, const QTextBlock &block, QTextObject *textObject)
virtual QTextFrame::iterator processBlockContents(QTextFrame::iterator it, const QTextBlock &block)
std::pair< QTextFrame::iterator, QTextBlock > skipBlockGroup(QTextFrame::iterator it, const QTextBlock &_block, QTextBlockGroup *blockGroup)
virtual QTextBlock::iterator processFragment(QTextBlock::iterator it, const QTextFragment &fragment, QTextDocument const *doc)
virtual ~MarkupDirector()
The Cutelee namespace holds all public Cutelee API.
Category category() const const
void prepend(const T &value)
bool remove(const T &value)
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QChar at(int position) const const
QTextFragment fragment() const const
iterator begin() const const
QTextBlockFormat blockFormat() const const
const QTextDocument * document() const const
bool isValid() const const
QTextList * textList() const const
Qt::Alignment alignment() const const
int fontWeight() const const
QTextObject * objectForFormat(const QTextFormat &f) const const
QTextFrame * rootFrame() const const
BlockTrailingHorizontalRulerWidth
QTextCharFormat charFormat() const const
QString text() const const
QTextBlock currentBlock() const const
QTextFrame * currentFrame() const const
iterator begin() const const
iterator end() const const
qreal height() const const
QString name() const const
qreal width() const const
QTextListFormat format() const const
Style style() const const
QTextDocument * document() const const
QTextTableCell cellAt(int row, int column) const const
int columns() const const
QTextTableFormat format() const const
QTextFrame::iterator begin() const const
int columnSpan() const const
QTextFrame::iterator end() const const
QVector< QTextLength > columnWidthConstraints() const const
void append(const T &value)
bool contains(const T &value) const const