21 #include "markupdirector.h"
22 #include "markupdirector_p.h"
24 #include "abstractmarkupbuilder.h"
26 #include <QtCore/QFlags>
27 #include <QtCore/QMap>
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)
50 QTextFrame::iterator end)
52 while (!start.atEnd() && start != end) {
53 auto frame = start.currentFrame();
55 auto table = qobject_cast<QTextTable *>(frame);
62 auto block = start.currentBlock();
63 Q_ASSERT(block.isValid());
81 const QTextBlock &block)
83 if (block.isValid()) {
84 auto fmt = block.blockFormat();
85 auto object = block.document()->objectForFormat(fmt);
101 auto format = table->format();
103 auto colLengths = format.columnWidthConstraints();
105 auto tableWidth = format.width();
108 if (tableWidth.type() == QTextLength::PercentageLength) {
109 sWidth = QStringLiteral(
"%1%");
110 sWidth = sWidth.arg(tableWidth.rawValue());
111 }
else if (tableWidth.type() == QTextLength::FixedLength) {
112 sWidth = QStringLiteral(
"%1");
113 sWidth = sWidth.arg(tableWidth.rawValue());
118 auto headerRowCount = format.headerRowCount();
120 QVector<QTextTableCell> alreadyProcessedCells;
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);
140 auto columnSpan = tableCell.columnSpan();
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);
155 if (cellWidth.type() == QTextLength::PercentageLength) {
156 sCellWidth = QStringLiteral(
"%1%");
157 sCellWidth = sCellWidth.arg(cellWidth.rawValue());
158 }
else if (cellWidth.type() == QTextLength::FixedLength) {
159 sCellWidth = QStringLiteral(
"%1");
160 sCellWidth = sCellWidth.arg(cellWidth.rawValue());
164 if (row < headerRowCount) {
172 if (row < headerRowCount) {
194 std::pair<QTextFrame::iterator, QTextBlock>
198 auto style = list->format().style();
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) {
225 const QTextBlock &block)
227 auto blockFormat = block.blockFormat();
228 auto blockAlignment = blockFormat.alignment();
232 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
234 if (!frameIt.atEnd())
239 auto it = block.begin();
246 if (!frameIt.atEnd())
252 if (!block.textList()) {
267 while (!it.atEnd()) {
272 if (!block.textList()) {
276 if (!frameIt.atEnd())
283 const QTextFragment &fragment,
284 QTextDocument
const *doc)
287 auto charFormat = fragment.charFormat();
289 if (charFormat.objectType() >= QTextFormat::UserObject) {
296 auto textObject = doc->objectForFormat(charFormat);
300 if (fragment.text().at(0).category() == QChar::Separator_Line) {
352 auto sl = fragment.text().split(QChar(QChar::LineSeparator));
355 auto paraClosed =
false;
366 }
else if (paraClosed) {
381 const QTextDocument *doc)
388 const QTextBlock &block,
391 auto group = qobject_cast<QTextBlockGroup *>(
object);
400 std::pair<QTextFrame::iterator, QTextBlock>
402 const QTextBlock &_block,
403 QTextBlockGroup *blockGroup)
406 auto lastBlock = _block;
408 auto obj = block.document()->objectForFormat(block.blockFormat());
409 QTextBlockGroup *nextGroup;
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};
441 std::pair<QTextFrame::iterator, QTextBlock>
443 const QTextBlock &block,
444 QTextBlockGroup *blockGroup)
446 auto list = qobject_cast<QTextList *>(blockGroup);
460 const QTextFragment &fragment,
461 QTextObject *textObject)
463 auto fragmentFormat = fragment.charFormat();
464 if (fragmentFormat.isImageFormat()) {
465 auto imageFormat = fragmentFormat.toImageFormat();
466 return processImage(it, imageFormat, textObject->document());
475 const QTextImageFormat &imageFormat,
481 imageFormat.height());
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());
566 auto fragment = it.fragment();
568 if (!fragment.isValid())
571 auto fragmentFormat = fragment.charFormat();
574 Q_FOREACH (
int tag, elementsToOpenList) {
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();
602 d->m_openBackground = fragmentFormat.background();
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()) {
629 d->m_openAnchorHref = fragmentFormat.anchorHref();
641 d->m_openElements.append(tag);
642 d->m_elementsToOpen.remove(tag);
649 QSet<int> closedElements;
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);
661 auto fragment = it.fragment();
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();
680 auto fontFamily = !fontFamilies.empty() ? fontFamilies.first() :
QString();
682 auto fontPointSize = fragmentFormat.font().pointSize();
683 auto anchorHref = fragmentFormat.anchorHref();
685 auto vAlign = fragmentFormat.verticalAlignment();
686 auto superscript = (vAlign == QTextCharFormat::AlignSuperScript);
687 auto subscript = (vAlign == QTextCharFormat::AlignSubScript);
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);
709 if (fontWeight != QFont::Bold
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;
765 auto fragment = it.fragment();
766 if (!fragment.isValid()) {
769 auto fragmentFormat = fragment.charFormat();
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();
783 auto fontFamily = !fontFamilies.empty() ? fontFamilies.first() :
QString();
785 auto fontPointSize = fragmentFormat.font().pointSize();
786 auto anchorHref = fragmentFormat.anchorHref();
788 auto vAlign = fragmentFormat.verticalAlignment();
789 auto superscript = (vAlign == QTextCharFormat::AlignSuperScript);
790 auto subscript = (vAlign == QTextCharFormat::AlignSubScript);
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;
806 if (fontForeground != Qt::NoBrush
810 && (fontForeground != d->m_openForeground)
811 && !((d->m_openElements.contains(
813 || d->m_elementsToOpen.contains(
Anchor)))) {
815 d->m_foregroundToOpen = fontForeground;
818 if (fontBackground != Qt::NoBrush
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;
831 if ((QTextCharFormat().font().pointSize()
834 && (fontPointSize != d->m_openFontPointSize)) {
836 d->m_fontPointSizeToOpen = fontPointSize;
843 if (fontWeight == QFont::Bold && !(d->m_openElements.contains(
Strong))) {
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());
874 QTextBlock::iterator it)
const
876 QList<int> sortedOpenedElements;
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 void endTableRow()=0
virtual void beginAnchor(const QString &href={}, const QString &name={})=0
virtual void beginForeground(const QBrush &brush)=0
virtual void beginListItem()=0
virtual void beginParagraph(Qt::Alignment a=Qt::AlignLeft, qreal top=0.0, qreal bottom=0.0, qreal left=0.0, qreal right=0.0)=0
virtual void beginStrong()=0
virtual void endBackground()=0
virtual void endStrong()=0
virtual void endStrikeout()=0
virtual void endAnchor()=0
virtual void beginFontFamily(const QString &family)=0
virtual void beginTableRow()=0
virtual void endParagraph()=0
virtual void beginList(QTextListFormat::Style style)=0
virtual void endTableHeaderCell()=0
virtual void beginEmph()=0
virtual void beginTableHeaderCell(const QString &width, int colSpan, int rowSpan)=0
virtual void endForeground()=0
virtual void beginFontPointSize(int size)=0
virtual void endTableCell()=0
virtual void addNewline()=0
virtual void beginStrikeout()=0
virtual void endFontPointSize()=0
virtual void beginTableCell(const QString &width, int colSpan, int rowSpan)=0
virtual void beginSuperscript()=0
virtual void beginTable(qreal cellpadding, qreal cellspacing, const QString &width)=0
virtual void endTable()=0
virtual void insertImage(const QString &url, qreal width, qreal height)=0
virtual void endSuperscript()=0
virtual void endListItem()=0
virtual void beginBackground(const QBrush &brush)=0
virtual void beginUnderline()=0
virtual void beginSubscript()=0
virtual void appendLiteralText(const QString &text)=0
virtual void endSubscript()=0
virtual void endUnderline()=0
virtual void insertHorizontalRule(int width=-1)=0
virtual void endFontFamily()=0
Instructs a builder object to create markup 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.