Skip to content

fix(html): keep the whitespace of a whitespace-only inline element - #2507

Open
Lukas (L4XB) wants to merge 1 commit into
microsoft:mainfrom
L4XB:fix/whitespace-only-inline-elements
Open

Lukas (L4XB) wants to merge 1 commit into
microsoft:mainfrom
L4XB:fix/whitespace-only-inline-elements

Conversation

@L4XB

Copy link
Copy Markdown

Closes #1539

The bug

markdownify.chomp() lifts the whitespace surrounding an inline element's text
out of that text, and the conversion functions then return '' once nothing is
left. A whitespace-only element therefore takes its whitespace with it and the
words on either side run together.

input on main with this branch
further<strong> </strong>reference furtherreference further reference
<b>First</b><b> </b><b>Last</b> **First****Last** **First** **Last**
Hello<code> </code>world Helloworld Hello world

This is #1539, which was closed pointing at an upstream fix. markdownify 1.2.3
is the current release and abstract_inline_conversion still reads:

prefix, suffix, text = chomp(text)
if not text:
    return ''

so the behaviour is unchanged on main today, with that version installed.

It is not only an HTML-input problem. A word processor keeps a differently
formatted space as a run of its own, so a .docx where the space between two
plain words is bold reaches mammoth as First<strong> </strong>Last and comes
out of MarkItDown as FirstLast. tests/test_docx_inline_whitespace.py builds
exactly that document; three of its four formattings fail on main.

The fix

#2477 solved this for <u> by overriding convert_u. There are twelve more
tags with the same conversion shape, so rather than twelve more overrides this
wraps the function markdownify resolves for a tag in get_conv_fn: a
whitespace-only element returns its text unchanged, everything else takes the
normal path.

b, code, del, em, i, kbd, s, samp, strike, strong, sub, sup, u

a is deliberately not in that set. #2396 is open for anchors and covers more
than the whitespace-only case, including <a> middle </a>, which this branch
does not touch.

get_conv_fn arrived in markdownify 1.0. On an older release the dispatcher
never calls it, so the override is simply never reached and nothing changes.

Tests

  • tests/test_html_converter.py gains the whitespace-only case for all thirteen
    tags, the exact-character case (&#160; stays  ), the empty-element
    case, the joined-emphasis case, and controls that an element with real content
    still converts to **word**, `word`, ~~word~~, <u>word</u>.
  • tests/test_docx_inline_whitespace.py is new: it builds a minimal .docx
    whose middle run is a single space with bold, italic, underline or
    strikethrough, and asserts the result is First Last.

15 of the new cases fail on main. Seven mutants of the changed lines are
killed by them: the wrapper removed, the guard never firing, the guard inverted,
every element returning unconverted text, and b, i, s dropped from the tag
set one at a time.

packages/markitdown      919 passed, 14 skipped   (main: 892 passed, 14 skipped)
packages/markitdown-ocr   53 passed
packages/markitdown-mcp   21 passed
black 23.7.0 --check      97 files unchanged

Python 3.12, macOS, markdownify 1.2.3.

markdownify's chomp() lifts the surrounding whitespace out of an inline
element's text and then returns an empty string once nothing is left, so a
whitespace-only element takes its whitespace with it: further<strong>
</strong>reference converts to furtherreference, and
<b>First</b><b> </b><b>Last</b> to **First****Last**.

A word processor keeps a differently formatted space as a run of its own, so
a document with a bolded space between two plain words converts through
exactly that shape. microsoft#2477 fixed it for <u> alone. Wrap the conversion
function markdownify resolves per tag instead, which covers the other twelve
inline tags at once.

Anchors are left out: microsoft#2396 is open for those and goes further than the
whitespace-only case.

Closes microsoft#1539
@L4XB

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Whitespace lost when DOCX has formatting boundaries mid-text (upstream markdownify issue)

1 participant