@@ -363,6 +363,12 @@ def _normalize_split_math_expressions(text):
363363 E.g.: $4.99 \\ rightarrow $14.99 → $4.99 \\ rightarrow 14.99$
364364 $4.99$ \\ rightarrow $14.99$ → $4.99 \\ rightarrow 14.99$
365365 """
366+ def _merge_two_dollars (m ):
367+ group3 = m .group (3 )
368+ if group3 .strip ():
369+ return f"${ m .group (1 )} { m .group (2 )} { group3 } $"
370+ return m .group (0 )
371+
366372 lines = text .split ('\n ' )
367373 result = []
368374
@@ -373,12 +379,12 @@ def _normalize_split_math_expressions(text):
373379 dollar_count = line .count ('$' )
374380
375381 # If 2 dollars + LaTeX: $content \cmd $content → $content \cmd content$
376- # Only merge when group 1 has real content before the command
382+ # Only merge when group 3 has actual non-whitespace content after second $.
377383 # (e.g. $4.99 \rightarrow $14.99) to avoid swallowing text after
378384 # a standalone $\cmd$ expression (e.g. $\rightarrow$ *italic*).
379385 if dollar_count == 2 :
380386 line = re .sub (r'\$([^\$\\\s][^\$]*?)(\\[a-z]+[^\$]*?)\s*\$([^\$\n]*)' ,
381- r'$\1\2 \3$' , line )
387+ _merge_two_dollars , line )
382388 # If 3+ dollars + LaTeX: merge outer dollars
383389 elif dollar_count >= 3 :
384390 # $content1$ \cmd $content2$ → $content1 \cmd content2$
@@ -442,10 +448,12 @@ def _is_image_with_caption_paragraph(p):
442448 if not has_image or 'em' not in names :
443449 return False
444450
445- # Allow only image/link, caption (<em>), line breaks and whitespace text nodes.
451+ # Allow only image/link, caption (<em> without links ), line breaks and whitespace text nodes.
446452 for child in meaningful :
447453 name = getattr (child , 'name' , None )
448- if name in ('img' , 'em' , 'br' ):
454+ if name in ('img' , 'br' ):
455+ continue
456+ if name == 'em' and not child .find ('a' ):
449457 continue
450458 if name == 'a' :
451459 a_children = [c for c in child .children if getattr (c , 'name' , None ) or str (c ).strip ()]
@@ -460,7 +468,10 @@ def _is_image_with_caption_paragraph(p):
460468 def _is_image_caption_paragraph (p ):
461469 """Return True if a paragraph is an image caption rendered as only emphasized text."""
462470 children = [c for c in p .children if getattr (c , 'name' , None ) or str (c ).strip ()]
463- return bool (children ) and all (getattr (c , 'name' , None ) == 'em' for c in children )
471+ if not children or not all (getattr (c , 'name' , None ) == 'em' for c in children ):
472+ return False
473+ # An <em> that wraps a link is italic text, not a caption.
474+ return not any (child .find ('a' ) for child in children )
464475
465476 @staticmethod
466477 def _ensure_list_spacing (text ):
@@ -545,7 +556,7 @@ def _replace_common_latex_commands_in_text(soup):
545556
546557 for text_node in soup .find_all (string = True ):
547558 parent = text_node .parent
548- if parent and parent .name in skip_parents :
559+ if parent and ( parent .name in skip_parents or ( parent . name == 'span' and 'arithmatex' in parent . get ( 'class' , []))) :
549560 continue
550561
551562 original = str (text_node )
0 commit comments