Skip to content

Commit

Permalink
Markdown and HTML: support image alt text and title
Browse files Browse the repository at this point in the history
It's a required CommonMark feature:
https://spec.commonmark.org/0.29/#images
and alt text is also required in HTML:
https://www.w3.org/wiki/Html/Elements/img#Requirements_for_providing_text_to_act_as_an_alternative_for_images
Now we are able to read these attributes from either html or markdown
and rewrite either an html or markdown document that preserves them.

This patch does not add viewing or editing support in QTextEdit etc.

Change-Id: I51307389f8f9fc00809808390e583a83111a7b33
Reviewed-by: Gatis Paeglis <[email protected]>
  • Loading branch information
ec1oud committed Jun 1, 2019
1 parent ca0c9f8 commit 237fa21
Show file tree
Hide file tree
Showing 10 changed files with 51 additions and 12 deletions.
6 changes: 6 additions & 0 deletions src/gui/text/qtextdocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2709,6 +2709,12 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
if (imgFmt.hasProperty(QTextFormat::ImageName))
emitAttribute("src", imgFmt.name());

if (imgFmt.hasProperty(QTextFormat::ImageAltText))
emitAttribute("alt", imgFmt.stringProperty(QTextFormat::ImageAltText));

if (imgFmt.hasProperty(QTextFormat::ImageTitle))
emitAttribute("title", imgFmt.stringProperty(QTextFormat::ImageTitle));

if (imgFmt.hasProperty(QTextFormat::ImageWidth))
emitAttribute("width", QString::number(imgFmt.width()));

Expand Down
4 changes: 4 additions & 0 deletions src/gui/text/qtextdocumentfragment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,10 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes()
case Html_img: {
QTextImageFormat fmt;
fmt.setName(currentNode->imageName);
if (!currentNode->text.isEmpty())
fmt.setProperty(QTextFormat::ImageTitle, currentNode->text);
if (!currentNode->imageAlt.isEmpty())
fmt.setProperty(QTextFormat::ImageAltText, currentNode->imageAlt);

fmt.merge(currentNode->charFormat);

Expand Down
8 changes: 7 additions & 1 deletion src/gui/text/qtextformat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,13 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
Image properties
\value ImageName
\value ImageName The filename or source of the image.
\value ImageTitle The title attribute of an HTML image tag, or
the quoted string that comes after the URL in a Markdown image link.
This enum value has been added in Qt 5.14.
\value ImageAltText The alt attribute of an HTML image tag, or
the image description in a Markdown image link.
This enum value has been added in Qt 5.14.
\value ImageWidth
\value ImageHeight
\value ImageQuality
Expand Down
2 changes: 2 additions & 0 deletions src/gui/text/qtextformat.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ class Q_GUI_EXPORT QTextFormat

// image properties
ImageName = 0x5000,
ImageTitle = 0x5001,
ImageAltText = 0x5002,
ImageWidth = 0x5010,
ImageHeight = 0x5011,
ImageQuality = 0x5014,
Expand Down
4 changes: 4 additions & 0 deletions src/gui/text/qtexthtmlparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1556,6 +1556,10 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
} else if (key == QLatin1String("height")) {
node->imageHeight = -2; // register that there is a value for it.
setFloatAttribute(&node->imageHeight, value);
} else if (key == QLatin1String("alt")) {
node->imageAlt = value;
} else if (key == QLatin1String("title")) {
node->text = value;
}
break;
case Html_tr:
Expand Down
1 change: 1 addition & 0 deletions src/gui/text/qtexthtmlparser_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ struct QTextHtmlParserNode {
QString textListNumberPrefix;
QString textListNumberSuffix;
QString imageName;
QString imageAlt;
qreal imageWidth;
qreal imageHeight;
QTextLength width;
Expand Down
24 changes: 14 additions & 10 deletions src/gui/text/qtextmarkdownimporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,15 +362,10 @@ int QTextMarkdownImporter::cbEnterSpan(int spanType, void *det)
} break;
case MD_SPAN_IMG: {
m_imageSpan = true;
m_imageFormat = QTextImageFormat();
MD_SPAN_IMG_DETAIL *detail = static_cast<MD_SPAN_IMG_DETAIL *>(det);
QString src = QString::fromUtf8(detail->src.text, int(detail->src.size));
QString title = QString::fromUtf8(detail->title.text, int(detail->title.size));
QTextImageFormat img;
img.setName(src);
if (m_needsInsertBlock)
insertBlock();
qCDebug(lcMD) << "image" << src << "title" << title << "relative to" << m_doc->baseUrl();
m_cursor->insertImage(img);
m_imageFormat.setName(QString::fromUtf8(detail->src.text, int(detail->src.size)));
m_imageFormat.setProperty(QTextFormat::ImageTitle, QString::fromUtf8(detail->title.text, int(detail->title.size)));
break;
}
case MD_SPAN_CODE:
Expand Down Expand Up @@ -406,8 +401,6 @@ int QTextMarkdownImporter::cbLeaveSpan(int spanType, void *detail)

int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
{
if (m_imageSpan)
return 0; // it's the alt-text
if (m_needsInsertBlock)
insertBlock();
#if QT_CONFIG(regularexpression)
Expand Down Expand Up @@ -481,6 +474,17 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
break;
}

if (m_imageSpan) {
// TODO we don't yet support alt text with formatting, because of the cases where m_cursor
// already inserted the text above. Rather need to accumulate it in case we need it here.
m_imageFormat.setProperty(QTextFormat::ImageAltText, s);
qCDebug(lcMD) << "image" << m_imageFormat.name()
<< "title" << m_imageFormat.stringProperty(QTextFormat::ImageTitle)
<< "alt" << s << "relative to" << m_doc->baseUrl();
m_cursor->insertImage(m_imageFormat);
return 0; // no error
}

if (!s.isEmpty())
m_cursor->insertText(s);
if (m_cursor->currentList()) {
Expand Down
1 change: 1 addition & 0 deletions src/gui/text/qtextmarkdownimporter_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class Q_GUI_EXPORT QTextMarkdownImporter
int m_paragraphMargin = 0;
int m_blockType = 0;
Features m_features;
QTextImageFormat m_imageFormat;
QTextListFormat m_listFormat;
QTextBlockFormat::MarkerType m_markerType = QTextBlockFormat::NoMarker;
bool m_needsInsertBlock = false;
Expand Down
10 changes: 9 additions & 1 deletion src/gui/text/qtextmarkdownwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Q_LOGGING_CATEGORY(lcMDW, "qt.text.markdown.writer")
static const QChar Space = QLatin1Char(' ');
static const QChar Newline = QLatin1Char('\n');
static const QChar LineBreak = QChar(0x2028);
static const QChar DoubleQuote = QLatin1Char('"');
static const QChar Backtick = QLatin1Char('`');
static const QChar Period = QLatin1Char('.');

Expand Down Expand Up @@ -372,7 +373,14 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
QTextCharFormat fmt = frag.fragment().charFormat();
if (fmt.isImageFormat()) {
QTextImageFormat ifmt = fmt.toImageFormat();
QString s = QLatin1String("![image](") + ifmt.name() + QLatin1Char(')');
QString desc = ifmt.stringProperty(QTextFormat::ImageAltText);
if (desc.isEmpty())
desc = QLatin1String("image");
QString s = QLatin1String("![") + desc + QLatin1String("](") + ifmt.name();
QString title = ifmt.stringProperty(QTextFormat::ImageTitle);
if (!title.isEmpty())
s += Space + DoubleQuote + title + DoubleQuote;
s += QLatin1Char(')');
if (wrap && col + s.length() > ColumnLimit) {
m_stream << Newline << wrapIndentString;
col = m_wrappedLineIndent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,9 @@ void tst_QTextMarkdownWriter::fromHtml_data()
QTest::newRow("block quote") <<
"<p>In 1958, Mahatma Gandhi was quoted as follows:</p><blockquote>The Earth provides enough to satisfy every man's need but not for every man's greed.</blockquote>" <<
"In 1958, Mahatma Gandhi was quoted as follows:\n\n> The Earth provides enough to satisfy every man's need but not for every man's\n> greed.\n\n";
QTest::newRow("image") <<
"<img src=\"/url\" alt=\"foo\" title=\"title\"/>" <<
"![foo](/url \"title\")\n\n";
// TODO
// QTest::newRow("escaped number and paren after double newline") <<
// "<p>(The first sentence of this paragraph is a line, the next paragraph has a number</p>13) but that's not part of an ordered list" <<
Expand Down

0 comments on commit 237fa21

Please sign in to comment.