Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion packages/markitdown/src/markitdown/converters/_markdownify.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,18 @@ def convert_img(
"""Same as usual converter, but removes data URIs"""

alt = el.attrs.get("alt", None) or ""
src = el.attrs.get("src", None) or el.attrs.get("data-src", None) or ""
src = el.attrs.get("src", None) or ""
data_src = el.attrs.get("data-src", None) or ""
# Lazy-loading libraries commonly leave a tiny placeholder data URI in
# src and put the real image in data-src. Prefer data-src when src
# isn't a usable URL, so the placeholder doesn't win over actual
# content. When keep_data_uris is set the caller explicitly wants the
# embedded bytes, so a data URI in src is left alone.
if data_src and (
not src
or (src[:5].lower() == "data:" and not self.options["keep_data_uris"])
):
src = data_src
title = el.attrs.get("title", None) or ""
title_part = ' "%s"' % title.replace('"', r"\"") if title else ""
# Remove all line breaks from alt
Expand Down
61 changes: 60 additions & 1 deletion packages/markitdown/tests/test_html_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
from markitdown import MarkItDown


def _convert_html(html: str) -> str:
def _convert_html(html: str, **kwargs) -> str:
result = MarkItDown().convert_stream(
io.BytesIO(html.encode("utf-8")),
file_extension=".html",
**kwargs,
)
return result.markdown

Expand Down Expand Up @@ -63,3 +64,61 @@ def test_html_href_does_not_quote_query_or_fragment() -> None:
markdown = _convert_html(f'<a href="{href}">example</a>')

assert f"[example]({expected_href})" in markdown


def test_img_prefers_data_src_over_placeholder_data_uri() -> None:
placeholder = (
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBTAA7"
)
real_src = "https://example.com/photo.jpg"
html = (
f'<img src="{placeholder}" data-src="{real_src}" alt="A photo" loading="lazy">'
)

markdown = _convert_html(html)

assert f"![A photo]({real_src})" in markdown
assert placeholder not in markdown


def test_img_uses_real_src_over_data_src_when_both_present() -> None:
real_src = "https://example.com/photo.jpg"
other_src = "https://example.com/photo-alt.jpg"
html = f'<img src="{real_src}" data-src="{other_src}" alt="A photo">'

markdown = _convert_html(html)

assert f"![A photo]({real_src})" in markdown


def test_img_falls_back_to_data_src_when_src_missing() -> None:
real_src = "https://example.com/photo.jpg"
html = f'<img data-src="{real_src}" alt="A photo">'

markdown = _convert_html(html)

assert f"![A photo]({real_src})" in markdown


def test_img_keeps_truncated_data_uri_when_no_data_src() -> None:
placeholder = (
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBTAA7"
)
html = f'<img src="{placeholder}" alt="A photo">'

markdown = _convert_html(html)

assert "![A photo](data:image/gif;base64...)" in markdown


def test_img_keeps_embedded_data_uri_over_data_src_when_keeping_data_uris() -> None:
embedded = (
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBTAA7"
)
other_src = "https://example.com/photo.jpg"
html = f'<img src="{embedded}" data-src="{other_src}" alt="A photo">'

markdown = _convert_html(html, keep_data_uris=True)

assert f"![A photo]({embedded})" in markdown
assert other_src not in markdown