6#include <QLoggingCategory>
7#if QT_CONFIG(regularexpression)
8#include <QRegularExpression>
11#include <QTextDocument>
12#include <QTextDocumentFragment>
15#if QT_CONFIG(system_textmarkdownreader)
18#include "../../3rdparty/md4c/md4c.h"
101 case MD_ALIGN_CENTER:
106 return defaultAlignment;
113 , m_features(features)
126 unsigned(m_features),
137 m_paragraphMargin = defaultFont.pointSize() * 2 / 3;
139 if (defaultFont.pointSize() != -1)
143 qCDebug(lcMD) <<
"default font" << defaultFont <<
"mono font" << m_monoFont;
148 if (endMarkerPos > 4) {
150 while (md.at(firstLinePos) ==
'\n'_L1 || md.at(firstLinePos) ==
'\r'_L1)
152 auto frontMatter = md.sliced(firstLinePos, endMarkerPos - firstLinePos);
153 firstLinePos = endMarkerPos + 4;
154 while (md.size() > firstLinePos && (md.at(firstLinePos) ==
'\n'_L1 || md.at(firstLinePos) ==
'\r'_L1))
156 md = md.sliced(firstLinePos);
158 qCDebug(lcMD) <<
"extracted FrontMatter: size" << frontMatter.size();
161 const auto mdUtf8 = md.toUtf8();
163 md_parse(mdUtf8.constData(), MD_SIZE(mdUtf8.size()), &
callbacks,
this);
169 m_blockType = blockType;
173 qCDebug(lcMD, m_listItem ?
"P of LI at level %d" :
"P continuation inside LI at level %d", int(m_listStack.
size()));
176 m_needsInsertBlock =
true;
180 qCDebug(lcMD,
"QUOTE level %d", m_blockQuoteDepth);
182 case MD_BLOCK_CODE: {
183 MD_BLOCK_CODE_DETAIL *
detail =
static_cast<MD_BLOCK_CODE_DETAIL *
>(det);
186 m_blockCodeFence =
detail->fence_char;
188 m_needsInsertBlock =
true;
189 if (m_blockQuoteDepth)
190 qCDebug(lcMD,
"CODE lang '%s' info '%s' fenced with '%c' inside QUOTE %d",
qPrintable(m_blockCodeLanguage),
qPrintable(
info), m_blockCodeFence, m_blockQuoteDepth);
195 MD_BLOCK_H_DETAIL *
detail =
static_cast<MD_BLOCK_H_DETAIL *
>(det);
198 int sizeAdjustment = 4 - int(
detail->level);
201 blockFmt.setHeadingLevel(
int(
detail->level));
202 m_needsInsertBlock =
false;
212 m_needsInsertBlock =
true;
214 MD_BLOCK_LI_DETAIL *
detail =
static_cast<MD_BLOCK_LI_DETAIL *
>(det);
215 m_markerType =
detail->is_task ?
221 if (m_needsInsertList)
224 m_needsInsertList =
true;
225 MD_BLOCK_UL_DETAIL *
detail =
static_cast<MD_BLOCK_UL_DETAIL *
>(det);
242 if (m_needsInsertList)
245 m_needsInsertList =
true;
246 MD_BLOCK_OL_DETAIL *
detail =
static_cast<MD_BLOCK_OL_DETAIL *
>(det);
252 qCDebug(lcMD,
"OL xx%d level %d start %d",
detail->mark_delimiter,
int(m_listStack.
size()) + 1,
detail->start);
255 MD_BLOCK_TD_DETAIL *
detail =
static_cast<MD_BLOCK_TD_DETAIL *
>(det);
261 qWarning(
"malformed table in Markdown input");
271 ++m_tableColumnCount;
273 if (m_currentTable->
columns() < m_tableColumnCount)
275 auto cell = m_currentTable->
cellAt(m_tableRowCount - 1, m_tableCol);
276 if (!cell.isValid()) {
277 qWarning(
"malformed table in Markdown input");
280 auto fmt = cell.format();
286 m_nonEmptyTableCells.
clear();
287 if (m_currentTable->
rows() < m_tableRowCount)
293 m_tableColumnCount = 0;
321 qCWarning(lcMD,
"list ended unexpectedly");
323 qCDebug(lcMD,
"list at level %d ended",
int(m_listStack.
size()));
333 for (
int col = m_tableCol; col >= 0; --col) {
334 if (m_nonEmptyTableCells.
contains(col)) {
335 if (mergeEnd >= 0 && mergeBegin >= 0) {
336 qCDebug(lcMD) <<
"merging cells" << mergeBegin <<
"to" << mergeEnd <<
"inclusive, on row" << m_currentTable->
rows() - 1;
337 m_currentTable->
mergeCells(m_currentTable->
rows() - 1, mergeBegin - 1, 1, mergeEnd - mergeBegin + 2);
349 case MD_BLOCK_QUOTE: {
350 qCDebug(lcMD,
"QUOTE level %d ended", m_blockQuoteDepth);
352 m_needsInsertBlock =
true;
355 qCDebug(lcMD) <<
"table ended with" << m_currentTable->
columns() <<
"cols and" << m_currentTable->
rows() <<
"rows";
356 m_currentTable =
nullptr;
360 qCDebug(lcMD,
"LI at level %d ended",
int(m_listStack.
size()));
363 case MD_BLOCK_CODE: {
365 m_blockCodeLanguage.
clear();
366 m_blockCodeFence = 0;
367 if (m_blockQuoteDepth)
368 qCDebug(lcMD,
"CODE ended inside QUOTE %d", m_blockQuoteDepth);
371 m_needsInsertBlock =
true;
385 if (!m_spanFormatStack.
isEmpty())
386 charFmt = m_spanFormatStack.
top();
395 charFmt.setFontUnderline(
true);
398 MD_SPAN_A_DETAIL *
detail =
static_cast<MD_SPAN_A_DETAIL *
>(det);
401 charFmt.setAnchor(
true);
402 charFmt.setAnchorHref(
url);
404 charFmt.setToolTip(
title);
405 charFmt.setForeground(m_palette.
link());
411 MD_SPAN_IMG_DETAIL *
detail =
static_cast<MD_SPAN_IMG_DETAIL *
>(det);
417 charFmt.setFont(m_monoFont);
418 charFmt.setFontFixedPitch(
true);
421 charFmt.setFontStrikeOut(
true);
424 m_spanFormatStack.
push(charFmt);
425 qCDebug(lcMD) << spanType <<
"setCharFormat" << charFmt.font().families().constFirst()
426 << charFmt.fontWeight() << (charFmt.fontItalic() ?
"italic" :
"")
427 << charFmt.foreground().color().name();
436 if (!m_spanFormatStack.
isEmpty()) {
437 m_spanFormatStack.
pop();
438 if (!m_spanFormatStack.
isEmpty())
439 charFmt = m_spanFormatStack.
top();
442 qCDebug(lcMD) << spanType <<
"setCharFormat" << charFmt.font().families().constFirst()
443 << charFmt.fontWeight() << (charFmt.fontItalic() ?
"italic" :
"")
444 << charFmt.foreground().color().name();
445 if (spanType ==
int(MD_SPAN_IMG))
452 if (m_needsInsertBlock)
454#if QT_CONFIG(regularexpression)
462#if QT_CONFIG(regularexpression)
463 if (m_htmlTagDepth) {
464 m_htmlAccumulator +=
s;
469 case MD_TEXT_NULLCHAR:
481#if QT_CONFIG(texthtmlparser)
484 m_htmlAccumulator +=
s;
492#if QT_CONFIG(regularexpression) && QT_CONFIG(texthtmlparser)
495 while ((startIdx =
s.indexOf(openingBracket, startIdx)) >= 0) {
500 while ((startIdx =
s.indexOf(closingBracket, startIdx)) >= 0) {
505 m_htmlAccumulator +=
s;
506 if (!m_htmlTagDepth) {
507 qCDebug(lcMD) <<
"HTML" << m_htmlAccumulator;
509 if (m_spanFormatStack.
isEmpty())
520 switch (m_blockType) {
522 m_nonEmptyTableCells.
append(m_tableCol);
528 m_needsInsertBlock =
true;
561 debugInfo +=
"in blockquote at depth "_L1 +
564 debugInfo +=
"in a code block"_L1;
566 <<
"bindent" << bfmt.indent() <<
"tindent" << bfmt.textIndent()
567 <<
"margins" << bfmt.leftMargin() << bfmt.topMargin() << bfmt.bottomMargin() << bfmt.rightMargin();
583void QTextMarkdownImporter::insertBlock()
586 if (!m_spanFormatStack.
isEmpty())
587 charFormat = m_spanFormatStack.
top();
589 if (!m_listStack.
isEmpty() && !m_needsInsertList && m_listItem) {
594 qWarning() <<
"attempted to insert into a list that no longer exists";
596 if (m_blockQuoteDepth) {
603 if (m_blockCodeFence) {
607 charFormat.
setFont(m_monoFont);
621 }
else if (m_listItem) {
627 if (m_needsInsertList) {
629 }
else if (!m_listStack.
isEmpty() && m_listItem && m_listStack.
top()) {
632 m_needsInsertList =
false;
633 m_needsInsertBlock =
false;
\threadsafe \inmodule QtGui
void setPointSize(int)
Sets the point size to pointSize.
void setPixelSize(int)
Sets the font size to pixelSize pixels, with a maxiumum size of an unsigned 16-bit integer.
qsizetype size() const noexcept
bool isEmpty() const noexcept
qsizetype count() const noexcept
void append(parameter_type t)
const QBrush & link() const
Returns the unvisited link text brush of the current color group.
\inmodule QtCore \reentrant
T & top()
Returns a reference to the stack's top item.
T pop()
Removes the top item from the stack and returns it.
void push(const T &t)
Adds element t to the top of the stack.
qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
void clear()
Clears the contents of the string and makes it null.
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setLeftMargin(qreal margin)
Sets the paragraph's left margin.
void setAlignment(Qt::Alignment alignment)
Sets the paragraph's alignment.
void setMarker(MarkerType marker)
void setBottomMargin(qreal margin)
Sets the paragraph's bottom margin.
void setNonBreakableLines(bool b)
If b is true, the lines in the paragraph are treated as non-breakable; otherwise they are breakable.
void setRightMargin(qreal margin)
Sets the paragraph's right margin.
void setIndent(int indent)
Sets the paragraph's indentation.
void setTopMargin(qreal margin)
Sets the paragraph's top margin.
void setFontItalic(bool italic)
If italic is true, sets the text format's font to be italic; otherwise the font will be non-italic.
void setFont(const QFont &font, FontPropertiesInheritanceBehavior behavior=FontPropertiesAll)
QTextBlockFormat blockFormat() const
Returns the block format of the block the cursor is in.
QTextDocument * document() const
QTextBlock block() const
Returns the block that contains the cursor.
void beginEditBlock()
Indicates the start of a block of editing operations on the document that should appear as a single o...
bool movePosition(MoveOperation op, MoveMode=MoveAnchor, int n=1)
Moves the cursor by performing the given operation n times, using the specified mode,...
void insertHtml(const QString &html)
void setBlockFormat(const QTextBlockFormat &format)
Sets the block format of the current block (or all blocks that are contained in the selection) to for...
void setCharFormat(const QTextCharFormat &format)
Sets the cursor's current character format to the given format.
void insertText(const QString &text)
Inserts text at the current position, using the current character format.
QTextList * currentList() const
Returns the current list if the cursor position() is inside a block that is part of a list; otherwise...
void insertImage(const QTextImageFormat &format, QTextFrameFormat::Position alignment)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void insertBlock()
Inserts a new empty block at the cursor position() with the current blockFormat() and charFormat().
void endEditBlock()
Indicates the end of a block of editing operations on the document that should appear as a single ope...
QTextList * insertList(const QTextListFormat &format)
Inserts a new block at the current position and makes it the first list item of a newly created list ...
QTextTable * insertTable(int rows, int cols, const QTextTableFormat &format)
Creates a new table with the given number of rows and columns in the specified format,...
QTextList * createList(const QTextListFormat &format)
Creates and returns a new list with the given format, and makes the current paragraph the cursor is i...
\reentrant \inmodule QtGui
bool isEmpty() const
Returns true if the document is empty; otherwise returns false.
QFont defaultFont
the default font used to display the document's text
virtual void clear()
Clears the document.
QUrl baseUrl
the base URL used to resolve relative resource URLs within the document.
void setMetaInformation(MetaInformation info, const QString &)
Sets the document's meta information of the type specified by info to the given string.
QString stringProperty(int propertyId) const
Returns the value of the property given by propertyId; if the property isn't of QMetaType::QString ty...
@ BlockTrailingHorizontalRulerWidth
void setProperty(int propertyId, const QVariant &value)
Sets the property specified by the propertyId to the given value.
void clearProperty(int propertyId)
Clears the value of the property given by propertyId.
QString name() const
Returns the name of the image.
void setName(const QString &name)
Sets the name of the image.
void setStyle(Style style)
Sets the list format's style.
void setIndent(int indent)
Sets the list format's indentation.
void setNumberSuffix(const QString &numberSuffix)
int indent() const
Returns the list format's indentation.
void setStart(int indent)
void add(const QTextBlock &block)
Makes the given block part of the list.
QTextListFormat format() const
Returns the list's format.
QTextMarkdownImporter(QTextDocument *doc, Features features)
int cbLeaveBlock(int blockType, void *detail)
int cbText(int textType, const char *text, unsigned size)
int cbEnterBlock(int blockType, void *detail)
int cbEnterSpan(int spanType, void *detail)
@ FeatureCollapseWhitespace
@ FeatureNoIndentedCodeBlocks
@ FeaturePermissiveMailAutoLinks
@ FeaturePermissiveATXHeaders
@ FeaturePermissiveURLAutoLinks
@ FeaturePermissiveAutoLinks
@ FeaturePermissiveWWWAutoLinks
int cbLeaveSpan(int spanType, void *detail)
void import(const QString &markdown)
QTextCursor firstCursorPosition() const
Returns the first valid cursor position in this cell.
bool isValid() const
Returns true if this is a valid table cell; otherwise returns false.
int columns() const
Returns the number of columns in the table.
void appendRows(int count)
void appendColumns(int count)
int rows() const
Returns the number of rows in the table.
QTextTableCell cellAt(int row, int col) const
Returns the table cell at the given row and column in the table.
void mergeCells(int row, int col, int numRows, int numCols)
Combined button and popup list for selecting options.
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
PromiseCallbacks callbacks
#define qPrintable(string)
#define QStringLiteral(str)
static void CbDebugLog(const char *msg, void *userdata)
static int CbEnterSpan(MD_SPANTYPE type, void *detail, void *userdata)
static int CbLeaveBlock(MD_BLOCKTYPE type, void *detail, void *userdata)
static int CbText(MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size, void *userdata)
static const QChar qtmi_Newline
static const QChar qtmi_Space
static const int qtmi_BlockQuoteIndent
static constexpr auto markerString() noexcept
static Qt::Alignment MdAlignment(MD_ALIGN a, Qt::Alignment defaultAlignment=Qt::AlignLeft|Qt::AlignVCenter)
static int CbEnterBlock(MD_BLOCKTYPE type, void *detail, void *userdata)
static int CbLeaveSpan(MD_SPANTYPE type, void *detail, void *userdata)
QVideoFrameFormat::PixelFormat fmt
QUrl url("example.com")
[constructor-url-reference]
\inmodule QtCore \reentrant
bool contains(const AT &t) const noexcept