diff --git a/doc/lua_api.md b/doc/lua_api.md
index 4020645cc..b08f7356d 100644
--- a/doc/lua_api.md
+++ b/doc/lua_api.md
@@ -3535,10 +3535,12 @@ Markup language used in `hypertext[]` elements uses tags that look like HTML tag
The markup language is currently unstable and subject to change. Use with caution.
Some tags can enclose text, they open with `` and close with ``.
Tags can have attributes, in that case, attributes are in the opening tag in
-form of a key/value separated with equal signs. Attribute values should not be quoted.
+form of a key/value separated with equal signs.
+Attribute values should be quoted using either " or '.
-If you want to insert a literal greater-than sign or a backslash into the text,
-you must escape it by preceding it with a backslash.
+If you want to insert a literal greater-than, less-than, or a backslash into the text,
+you must escape it by preceding it with a backslash. In a quoted attribute value, you
+can insert a literal quote mark by preceding it with a backslash.
These are the technically basic tags but see below for usual tags. Base tags are:
diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua
index 758bad631..c2074e6e0 100644
--- a/games/devtest/mods/testformspec/formspec.lua
+++ b/games/devtest/mods/testformspec/formspec.lua
@@ -69,9 +69,9 @@ local hypertext_basic = [[A hypertext element
This is a normal text.
style test
-
- .
-
+
+ .
+
Tag test
normal
@@ -88,20 +88,20 @@ This is a normal text.
Custom tag test
-
-
-
-
+
+
+
+
color=green
-Action: color=green
-Action: hovercolor=yellow
-Action URL: open URL
+Action: color=green
+Action: hovercolor=yellow
+Action URL: open URL
size=24
font=mono
color=green font=mono size=24
action test
-action
+action
img test
Normal:
diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp
index 3db6f0071..45e5d6e98 100644
--- a/src/gui/guiHyperText.cpp
+++ b/src/gui/guiHyperText.cpp
@@ -421,14 +421,16 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor)
AttrsList attrs;
while (c != L'>') {
std::string attr_name = "";
- core::stringw attr_val = L"";
+ std::wstring attr_val = L"";
+ // Consume whitespace
while (c == ' ') {
c = text[++cursor];
if (c == L'\0' || c == L'=')
return 0;
}
+ // Read attribute name
while (c != L' ' && c != L'=') {
attr_name += (char)c;
c = text[++cursor];
@@ -436,28 +438,51 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor)
return 0;
}
+ // Consume whitespace
while (c == L' ') {
c = text[++cursor];
if (c == L'\0' || c == L'>')
return 0;
}
+ // Skip equals
if (c != L'=')
return 0;
-
c = text[++cursor];
-
if (c == L'\0')
return 0;
- while (c != L'>' && c != L' ') {
- attr_val += c;
+ // Read optional quote
+ wchar_t quote_used = 0;
+ if (c == L'"' || c == L'\'') {
+ quote_used = c;
c = text[++cursor];
if (c == L'\0')
return 0;
}
- attrs[attr_name] = stringw_to_utf8(attr_val);
+ // Read attribute value
+ bool escape = false;
+ while (escape || (quote_used && c != quote_used) || (!quote_used && c != L'>' && c != L' ')) {
+ if (quote_used && !escape && c == L'\\') {
+ escape = true;
+ } else {
+ escape = false;
+ attr_val += c;
+ }
+ c = text[++cursor];
+ if (c == L'\0')
+ return 0;
+ }
+
+ // Remove quote
+ if (quote_used) {
+ if (c != quote_used)
+ return 0;
+ c = text[++cursor];
+ }
+
+ attrs[attr_name] = wide_to_utf8(attr_val);
}
++cursor; // Last ">"