fix(viewer): keep balanced nested emphasis working

Restricting emphasis content to non-delimiters made `**bold with *italic*
inside**` render its outer delimiters literally. Bold spans now only require
a non-delimiter at each end (so `******` still stays literal) and italic
content may contain complete <b>..</b> spans, which keeps nesting working
while still ruling out crossed tags.
This commit is contained in:
Adrian 2026-08-26 23:01:40 +03:00
parent 87d47eb390
commit bc1b0b7ec2
2 changed files with 14 additions and 7 deletions

View file

@ -417,14 +417,15 @@ def _inline_md(text: str) -> str:
codes.append(match.group(1))
return f"\x00{len(codes) - 1}\x00"
# Emphasis content may not contain its own delimiter or a tag, so the three passes can
# never interleave into crossed markup (``<b><i>..</b></i>``), which reportlab rejects.
# A run of asterisks such as a masked secret (``******``) therefore stays literal.
# Bold spans must start and end with a non-delimiter, so a run of asterisks such as a masked
# secret (``******``) stays literal. Italic content is plain text or complete ``<b>..</b>``
# spans, so the passes can nest (``**a *b* c**``, ``*a **b** c*``) but can never interleave
# into crossed markup (``<b><i>..</b></i>``), which reportlab rejects.
seg = html.escape(re.sub(r"`([^`]+)`", _stash, text))
seg = re.sub(r"\*\*\*([^*<>\n]+?)\*\*\*", r"<b><i>\1</i></b>", seg)
seg = re.sub(r"\*\*([^*<>\n]+?)\*\*", r"<b>\1</b>", seg)
seg = re.sub(r"__([^_<>\n]+?)__", r"<b>\1</b>", seg)
seg = re.sub(r"\*([^*<>\n]+?)\*", r"<i>\1</i>", seg)
seg = re.sub(r"\*\*\*(?=[^*])(.+?)(?<=[^*])\*\*\*", r"<b><i>\1</i></b>", seg)
seg = re.sub(r"\*\*(?=[^*])(.+?)(?<=[^*])\*\*", r"<b>\1</b>", seg)
seg = re.sub(r"__(?=[^_])(.+?)(?<=[^_])__", r"<b>\1</b>", seg)
seg = re.sub(r"\*((?:[^*<>\n]|<b>[^<>*\n]*</b>)+?)\*", r"<i>\1</i>", seg)
def _restore(match: re.Match[str]) -> str:
inner = html.escape(codes[int(match.group(1))])

View file

@ -156,3 +156,9 @@ def test_generate_report_pdf_with_masked_secret_in_summary(tmp_path: Path) -> No
def test_para_falls_back_to_plain_text_on_crossed_markup() -> None:
para = _para("<b><i>x</b></i> &amp; y", ParagraphStyle("t"))
assert para.getPlainText() == "x & y"
def test_inline_md_keeps_balanced_nested_emphasis() -> None:
assert _inline_md("**bold with *italic* inside**") == "<b>bold with <i>italic</i> inside</b>"
assert _inline_md("*outer **bold** inner*") == "<i>outer <b>bold</b> inner</i>"
assert "******" in _inline_md("(observed as '******')") # a masked secret stays literal