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

Templating engine breaks SVG component scaled rendering by lowercasing viewBox attribute #256

Closed
JeremiahSanders opened this issue May 14, 2022 · 2 comments
Labels
bug Something isn't working

Comments

@JeremiahSanders
Copy link
Contributor

TL;DR: SVGs use a viewBox attribute which is case-sensitive. This attribute is essential for rendering resizable images. The Bolero Template<> implementation converts viewBox attributes to viewbox (all lowercase), which breaks scaled rendering.

This is not a newly-introduced problem in 0.20.12. I've been using Bolero to develop an SVG-heavy realtime WASM game (Roadkill Ruffians) since Bolero 0.10.1-preview9. The problem has existed since roughly that point, but I worked around it by creating helper functions utilizing the list-based rendering to manually specify SVG attr names.

Example: let viewBox (value: obj): Attr = Attr("viewBox", box value) was the function I used extensively to create the functional SVGs.

However, the breaking changes introduced in 0.20 have required an extensive component building refactor. I can no longer just build Attr list and pass it to Html.svg. Thus, the inability to use SVG templates (due to the attribute being coerced to lowercase) is causing increased difficulty.

Sample SVG

Below is a simple, hollow circle which renders to roughly fill a 200x200 area.

<?xml version="1.0" encoding="UTF-8"?>
<svg width="200" height="200" version="1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
    <circle cx="50" cy="50" r="45" fill="none" stroke="#000" stroke-width="3"/>
</svg>

Note the case-sensitive viewBox attribute.

Sample SVG Template

Below is the same SVG, but including Bolero Holes to specify the circle fill and stroke at runtime.

<?xml version="1.0" encoding="UTF-8"?>
<svg width="200" height="200" version="1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
    <circle cx="50" cy="50" r="45" fill="${CircleFill}" stroke="${CircleStroke}" stroke-width="3"/>
</svg>

Note the case-sensitive viewBox attribute.

Rendered SVG Template

The template above, rendered by Bolero 0.20.12, generates the following (copied using "copy element" in Edge developer tools):

<svg width="200" height="200" version="1.1" viewbox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><!--!-->
    <circle cx="50" cy="50" r="45" fill="none" stroke="#000" stroke-width="3"></circle><!--!-->
</svg>

Note the conversion of viewBox to viewbox in the version rendered by Bolero.

Working Example

A minimal example project exhibiting this behavio, based on the latest NuGet project template, can be seen at: https://github.com/JeremiahSanders/fsbolero-bolero-svg-viewbox.

The commit which modifies the template with the minimum changes to show this issue: https://github.com/JeremiahSanders/fsbolero-bolero-svg-viewbox/tree/f9281c33b8793dc0fb5a282659e975767831d738

@JeremiahSanders
Copy link
Contributor Author

After examining Bolero.Templating.Parsing.GetDoc, it appears that the problem is caused by HtmlAgilityPack.
A matching symptom is addressed in this StackOverflow answer.

Based upon the above, it appears that SVG templates (and any others which need case-sensitive output) might render successfully by changing the properties applied to this object in Bolero.Templating.Parsing.GetDoc. Specifically, setting its OptionOutputOriginalCase to true.

The following script, when executed in .NET Fiddle, shows proper rendering of viewBox attribute, due to use of OptionOutputOriginalCase:

open HtmlAgilityPack

let svg = """<svg width="200" height="200" version="1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
    <circle cx="50" cy="50" r="45" fill="${CircleFill}" stroke="${CircleStroke}" stroke-width="3"/>
</svg>"""

let svgDoc = new HtmlDocument()

svgDoc.OptionOutputOriginalCase <- true
svgDoc.LoadHtml(svg)

let svgBody = svgDoc.DocumentNode.SelectSingleNode("//svg")

printfn "%O" (svgBody.OuterHtml)

@Tarmil Tarmil added the bug Something isn't working label May 15, 2022
Tarmil added a commit that referenced this issue May 15, 2022
When an element has no holes inside it, code generation uses
HtmlNode.OuterHtml instead of HtmlAttribute.Name. So both cases
must be checked.
Tarmil added a commit that referenced this issue May 15, 2022
When an element has no holes inside it, code generation uses
HtmlNode.OuterHtml instead of HtmlAttribute.Name. So both cases
must be checked.
Tarmil added a commit that referenced this issue May 15, 2022
When an element has no holes inside it, code generation uses
HtmlNode.OuterHtml instead of HtmlAttribute.Name. So both cases
must be checked.
@Tarmil
Copy link
Member

Tarmil commented May 15, 2022

Fixed in v0.20.17.

@Tarmil Tarmil closed this as completed May 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants