Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

put svg with xmlns in img src tag #1538

Merged
merged 11 commits into from
Mar 17, 2021
47 changes: 46 additions & 1 deletion src/Writers/HTMLWriter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1748,7 +1748,52 @@ function mdconvert(d::Dict{MIME,Any}, parent; kwargs...)
if haskey(d, MIME"text/html"())
out = Documents.RawHTML(d[MIME"text/html"()])
elseif haskey(d, MIME"image/svg+xml"())
out = Documents.RawHTML(d[MIME"image/svg+xml"()])
svg = d[MIME"image/svg+xml"()]


svg_tag_match = match(r"<svg[^>]*>", svg)

jkrumbiegel marked this conversation as resolved.
Show resolved Hide resolved
if svg_tag_match === nothing
# There is no svg tag so we don't do any more advanced
# processing and just return the svg as RawHTML.
# The svg string should be invalid but that's not our concern here.
out = Documents.RawHTML(svg)
else
# The xmlns attribute has to be present for data:image/svg+xml
# to work (https://stackoverflow.com/questions/18467982).
# If it doesn't exist, we splice it into the first svg tag.
# This should never invalidate otherwise valid svg.
svg_tag = svg_tag_match.match
xmlns_present = occursin("xmlns", svg_tag)
if !xmlns_present
svg = replace(svg, "<svg" => "<svg xmlns=\"http://www.w3.org/2000/svg\"", count = 1)
end

# We can leave the svg as utf8, but the minimum safety precaution we need
# is to ensure the src string separator is not in the svg.
# That can be either " or ', and the svg will most likely use only one of them
# so we check which one occurs more often and use the other as the separator.
# This should leave most svg basically intact.

# Replace % with %25 and # with %23 https://github.com/jakubpawlowicz/clean-css/issues/763#issuecomment-215283553
svg = replace(svg, "%" => "%25")
svg = replace(svg, "#" => "%23")

singles = count(==('\''), svg)
doubles = count(==('"'), svg)
if singles > doubles
# Replace every " with %22 because it terminates the src=" string otherwise
svg = replace(svg, "\"" => "%22")
sep = "\""
else
# Replace every ' with %27 because it terminates the src=' string otherwise
svg = replace(svg, "\'" => "%27")
sep = "'"
end

out = Documents.RawHTML(string("<img src=", sep, "data:image/svg+xml;utf-8,", svg, sep, "/>"))
end

elseif haskey(d, MIME"image/png"())
out = Documents.RawHTML(string("<img src=\"data:image/png;base64,", d[MIME"image/png"()], "\" />"))
elseif haskey(d, MIME"image/webp"())
Expand Down
77 changes: 77 additions & 0 deletions test/examples/src/man/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ Base.show(io, ::MIME"image/svg+xml", svg::SVG) = write(io, svg.code)
end # module
```

Without xmlns tag:

```@example inlinesvg
using .InlineSVG
SVG("""
Expand All @@ -195,6 +197,81 @@ SVG("""
""")
```

With xmlns tag:

```@example inlinesvg
using .InlineSVG
SVG("""
<svg width="82" height="76" xmlns="http://www.w3.org/2000/svg">
<g style="stroke-width: 3">
<circle cx="20" cy="56" r="16" style="stroke: #cb3c33; fill: #d5635c" />
<circle cx="41" cy="20" r="16" style="stroke: #389826; fill: #60ad51" />
<circle cx="62" cy="56" r="16" style="stroke: #9558b2; fill: #aa79c1" />
</g>
</svg>
""")
```

With single quotes:

```@example inlinesvg
using .InlineSVG
SVG("""
<svg width='82' height='76' xmlns='http://www.w3.org/2000/svg'>
<g style='stroke-width: 3'>
<circle cx='20' cy='56' r='16' style='stroke: #cb3c33; fill: #d5635c' />
<circle cx='41' cy='20' r='16' style='stroke: #389826; fill: #60ad51' />
<circle cx='62' cy='56' r='16' style='stroke: #9558b2; fill: #aa79c1' />
</g>
</svg>
""")
```

With a mixture of single and double quotes:

```@example inlinesvg
using .InlineSVG
SVG("""
<svg width='82' height='76' xmlns='http://www.w3.org/2000/svg'>
<g style='stroke-width: 3'>
<circle cx='20' cy='56' r='16' style='stroke: #cb3c33; fill: #d5635c' />
<circle cx="41" cy="20" r="16" style="stroke: #389826; fill: #60ad51" />
<circle cx='62' cy='56' r='16' style='stroke: #9558b2; fill: #aa79c1' />
</g>
</svg>
""")
```

With viewBox and without xmlns, making the svg really large to test that it is resized correctly:

```@example inlinesvg
using .InlineSVG
SVG("""
<svg width="8200" height="7600" viewBox="0 0 82 76">
<g style="stroke-width: 3">
<circle cx="20" cy="56" r="16" style="stroke: #cb3c33; fill: #d5635c" />
<circle cx="41" cy="20" r="16" style="stroke: #389826; fill: #60ad51" />
<circle cx="62" cy="56" r="16" style="stroke: #9558b2; fill: #aa79c1" />
</g>
</svg>
""")
```

Without viewBox and without xmlns, making the svg really large to test that it is resized correctly:

```@example inlinesvg
using .InlineSVG
SVG("""
<svg width="8200" height="7600">
<g style="stroke-width: 300">
<circle cx="2000" cy="5600" r="1600" style="stroke: #cb3c33; fill: #d5635c" />
<circle cx="4100" cy="2000" r="1600" style="stroke: #389826; fill: #60ad51" />
<circle cx="6200" cy="5600" r="1600" style="stroke: #9558b2; fill: #aa79c1" />
</g>
</svg>
""")
```

_Note: we can't define the `show` method in the `@example` block due to the world age
counter in Julia 0.6 (Documenter's `makedocs` is not aware of any of the new method
definitions happening in `eval`s)._
Expand Down