Sub6Resources / flutter_html

A Flutter widget for rendering static html as Flutter widgets (Will render over 80 different html tags!)
https://pub.dev/packages/flutter_html
MIT License
1.76k stars 814 forks source link

support needed for vertical-align property with images #556

Closed ngaurav closed 1 year ago

ngaurav commented 3 years ago

I am storing text with some latex code for math part. Using python markdown extensions that text can be converted into html, which is mostly text but the latex is rendered as SVG in tag. To match the baseline of image with the text, there is a vertical-align attribute. Flutter_html currently ignores inline vertical-align attributes for img tag. To suit my requirements, I have created a hacky fork of flutter_html = which uses a class called BaselineBox to define a baseline for its child (SvgContentElement class). If this sounds like a worthy feature for flutter_html, then please implement it. Here is a sample html node, which can be used as a test case.

Screen Shot 2021-02-24 at 11 53 52 PM

<p>If <img class="latex-inline math" style="vertical-align:-0.353876pt" alt="" id="4b09cb9bb985eb9" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48IS0tIFRoaXMgZmlsZSB3YXMgZ2VuZXJhdGVkIGJ5IGR2aXN2Z20gMi44LjEgLS0+PHN2ZyB2ZXJzaW9uPScxLjEnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZycgeG1sbnM6eGxpbms9J2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsnIHdpZHRoPScyMC4wODkxNTVwdCcgaGVpZ2h0PScxMS4yOTIwODNwdCcgdmlld0JveD0nMCAtMTAuOTM5NTMgMjAuMDg5MTU1IDExLjI5MjA4Myc+PGRlZnM+PHBhdGggaWQ9J2cyLTQ2JyBkPSdNLjY2MjIyOS0uNTYyMThDLjQ5NTQ4MS0uNTYyMTggLjM1NzMxOC0uNDI4NzgxIC4zNTczMTgtLjI1NzI2OVMuNDk1NDgxIC4wNDI4NzggLjY2MjIyOSAuMDQyODc4Uy45NjIzNzYtLjA5MDUyIC45NjIzNzYtLjI1NzI2OUMuOTYyMzc2LS40Mjg3ODEgLjgyODk3Ny0uNTYyMTggLjY2MjIyOS0uNTYyMThaJy8+PHBhdGggaWQ9J2cyLTU1JyBkPSdNLjMzODI2MS0zLjE5NjgwM1YtMi40MDExNzVILjU2MjE4TC42Mzg0MDgtMi44MzQ3MjFIMi4xMDU3OTNMLjU1MjY1MSAuMTc2Mjc3SC45NDgwODNWLjA3MTQ2NEwyLjUyOTgxLTMuMDA2MjMzVi0zLjE5NjgwM0guMzM4MjYxWicvPjxwYXRoIGlkPSdnMS00OScgZD0nTTMuMTY4MjE3LTEuNjQ4NDI2QzIuNzU4NDkzLTIuMTE1MzIxIDIuMjgyMDY5LTIuNTQ0MTAyIDEuNjY3NDgzLTIuNTQ0MTAyQy45MDk5NjktMi41NDQxMDIgLjUzMzU5NS0xLjk1ODEwMSAuNTMzNTk1LTEuMzcyMVMuOTA5OTY5LS4yMDQ4NjIgMS42Njc0ODMtLjIwNDg2MkMyLjI1ODI0OC0uMjA0ODYyIDIuNzEwODUxLS42MjQxMTUgMy4wOTE5OS0xLjA5NTc3NEMzLjUwNjQ3OC0uNjI4ODc5IDMuOTgyOTAyLS4yMDQ4NjIgNC41OTI3MjQtLjIwNDg2MkM1LjM1MDIzOC0uMjA0ODYyIDUuNzMxMzc3LS43ODYwOTkgNS43MzEzNzctMS4zNzIxUzUuMzUwMjM4LTIuNTQ0MTAyIDQuNTkyNzI0LTIuNTQ0MTAyQzQuMDAxOTU5LTIuNTQ0MTAyIDMuNTQ5MzU2LTIuMTIwMDg1IDMuMTY4MjE3LTEuNjQ4NDI2Wk0zLjM1ODc4Ny0xLjQzNDAzNUMzLjY4NzUxOS0xLjg1MzI4OCA0LjA2ODY1OC0yLjI1ODI0OCA0LjU5MjcyNC0yLjI1ODI0OEM1LjA1MDA5MS0yLjI1ODI0OCA1LjQxMjE3My0xLjg4NjYzOCA1LjQxMjE3My0xLjQzODc5OUM1LjQxMjE3My0xLjQxOTc0MyA1LjQxMjE3My0xLjM5NTkyMSA1LjQwNzQwOS0xLjM3MjFDNS4zNzQwNTktLjk1Mjg0NyA1LjAxNjc0MS0uNjMzNjQzIDQuNTkyNzI0LS42MzM2NDNDNC4wNzgxODctLjYzMzY0MyAzLjY5NzA0OC0xLjAzMzgzOSAzLjM1ODc4Ny0xLjQzNDAzNVpNMi45MDYxODQtMS4zMTAxNjVDMi41NzI2ODgtLjg5MDkxMiAyLjE5MTU0OS0uNDkwNzE2IDEuNjY3NDgzLS40OTA3MTZDMS4yMTQ4OC0uNDkwNzE2IC44NDgwMzQtLjg1NzU2MyAuODQ4MDM0LTEuMzA1NDAxQy44NDgwMzQtMS4zMjQ0NTggLjg1Mjc5OC0xLjM0ODI3OSAuODUyNzk4LTEuMzcyMUMuODg2MTQ4LTEuNzkxMzUzIDEuMjQzNDY2LTIuMTE1MzIxIDEuNjY3NDgzLTIuMTE1MzIxQzIuMTg2Nzg1LTIuMTE1MzIxIDIuNTY3OTI0LTEuNzEwMzYxIDIuOTA2MTg0LTEuMzEwMTY1WicvPjx1c2UgaWQ9J2c2LTQ2JyB4bGluazpocmVmPScjZzItNDYnLz48dXNlIGlkPSdnOS01NScgeGxpbms6aHJlZj0nI2cyLTU1Jy8+PHVzZSBpZD0nZzEyLTU1JyB4bGluazpocmVmPScjZzItNTUnIHRyYW5zZm9ybT0nc2NhbGUoMS40KScvPjx1c2UgaWQ9J2cxNS01NScgeGxpbms6aHJlZj0nI2cyLTU1JyB0cmFuc2Zvcm09J3NjYWxlKDIpJy8+PC9kZWZzPjxnIGlkPSdwYWdlMSc+PHVzZSB4PScwJyB5PScwJyB4bGluazpocmVmPScjZzE1LTU1Jy8+PHVzZSB4PSc1LjMxNzY5NicgeT0nLTMuODE2MTAzJyB4bGluazpocmVmPScjZzEyLTU1Jy8+PHVzZSB4PSc5LjA0MDA4MycgeT0nLTYuNDg3Mzc2JyB4bGluazpocmVmPScjZzktNTUnLz48dXNlIHg9JzExLjY5ODkzJyB5PSctOC4zOTU0MjgnIHhsaW5rOmhyZWY9JyNnNi00NicvPjx1c2UgeD0nMTMuMDI4MzU0JyB5PSctOC4zOTU0MjgnIHhsaW5rOmhyZWY9JyNnNi00NicvPjx1c2UgeD0nMTQuMzU3Nzc4JyB5PSctOC4zOTU0MjgnIHhsaW5rOmhyZWY9JyNnMS00OScvPjwvZz48L3N2Zz4="> is divided by 5 what will be the remainder?</p>

tneotia commented 3 years ago

If you'd like you can submit a PR and @erickok will review it, since you already have created a solution. It definitely seems like a great feature! :)

erickok commented 3 years ago

To support vertical-align styling beyond just the use case here, maybe we could use transform? I'd have to read up on what this css property does exactly on the web.

ngaurav commented 3 years ago

Transform works, but the issue is that when you shift the image by some pixels, the line height of the widget does not change. This leads to overlap of two lines as shown below WhatsApp Image 2021-02-25 at 10 29 55 AM

erickok commented 3 years ago

Oh yeah that's an issue then. Okay we have to take a good look. I saw your commits and they look promising, but I hope to find something beyond just support for this css property for just inline svg images.

ngaurav commented 3 years ago

I understand that it seems like a corner use-case. But, we can look at it like a general support for vertical-align inline style. It will work on all kinds of images (SVG, PNG) and also can be helpful when mixing fonts. Some people mix symbols with text on the same line, and they expect it to be aligned. https://stackoverflow.com/questions/62371976/align-text-baseline-with-text-inside-a-column-using-flutter https://stackoverflow.com/questions/56592377/align-icon-by-baseline-in-stack

And, some people just want to shift the text alignment. https://stackoverflow.com/questions/56170779/base-align-text-and-dashed-line-in-flutter https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align

If image alignment is implemented, then whatever people can convert every complex html attribute into SVG and use it with this library.

tneotia commented 3 years ago

@ngaurav I tried to replicate your setup but its not working for me, the image still renders in the middle of the text. Any ideas?

ngaurav commented 3 years ago

@tneotia , For me it is working with BaselineBox class Refer to https://gitlab.com/nishant.gaurav/flutter_html/-/tree/dev. This is a hack, where I am adding a depth attribute to SVGContentElement. But the text is getting rendered properly. Here is a sample Html WhatsApp Image 2021-03-19 at 3 36 45 AM

"<p>What is the remainder when <img class='latex-inline math' style='vertical-align:-0.105206pt' alt='' id='c3130ab88ff1525' src='data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48IS0tIFRoaXMgZmlsZSB3YXMgZ2VuZXJhdGVkIGJ5IGR2aXN2Z20gMi44LjEgLS0+PHN2ZyB2ZXJzaW9uPScxLjEnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZycgeG1sbnM6eGxpbms9J2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsnIHdpZHRoPScxNi43NjM3MThwdCcgaGVpZ2h0PScxMy42NjIyNjZwdCcgdmlld0JveD0nMCAtMTMuNTU3NDUzIDE2Ljc2MzcxOCAxMy42NjIyNjYnPjxkZWZzPjxwYXRoIGlkPSdnMi01MCcgZD0nTS42NzE3NTctLjM3NjM3NUMxLjcyNDY1NC0xLjIxNDg4IDIuMzAxMTI2LTEuNzUzMjM5IDIuMzAxMTI2LTIuMzUzNTMzQzIuMzAxMTI2LTIuODkxODkyIDEuOTI0NzUyLTMuMjUzOTc0IDEuMzEwMTY1LTMuMjUzOTc0Qy43MTk0LTMuMjUzOTc0IC4zMDAxNDctMi45MTU3MTMgLjMwMDE0Ny0yLjU4MjIxNkMuMzAwMTQ3LTIuNDIwMjMyIC40MDAxOTYtMi4zMTU0MTkgLjU2Njk0NC0yLjMxNTQxOUMuNjQzMTcyLTIuMzE1NDE5IC43MTQ2MzUtMi4zMzkyNCAuNzgxMzM1LTIuMzg2ODgzVi0yLjg5NjY1NkMuOTI0MjYyLTIuOTc3NjQ4IDEuMDYyNDI1LTMuMDEwOTk4IDEuMjA1MzUyLTMuMDEwOTk4QzEuNjAwNzg0LTMuMDEwOTk4IDEuODM4OTk1LTIuNzQ0MiAxLjgzODk5NS0yLjI5MTU5OEMxLjgzODk5NS0xLjY2NzQ4MyAxLjI0MzQ2Ni0xLjE0ODE4MSAuMjAwMDk4LS4zMDAxNDdWMEgyLjQwNTk0Vi0uNzgxMzM1SDIuMTkxNTQ5TDIuMTA1NzkzLS4zNzYzNzVILjY3MTc1N1onLz48cGF0aCBpZD0nZzItNTEnIGQ9J00xLjUxMDI2My0xLjY4MTc3NkMxLjk3NzE1OC0xLjgxNTE3NCAyLjIyMDEzNC0yLjA4NjczNiAyLjIyMDEzNC0yLjQ1MzU4MkMyLjIyMDEzNC0yLjkzNDc3IDEuODUzMjg4LTMuMjUzOTc0IDEuMjgxNTgtMy4yNTM5NzRDLjY4NjA1LTMuMjUzOTc0IC4yNzYzMjYtMi45MzQ3NyAuMjc2MzI2LTIuNjIwMzNDLjI3NjMyNi0yLjQ2Nzg3NSAuMzgxMTM5LTIuMzYzMDYxIC41MzM1OTUtMi4zNjMwNjFDLjYwOTgyMi0yLjM2MzA2MSAuNjcxNzU3LTIuMzgyMTE4IC43Mjg5MjgtMi40MjQ5OTZWLTIuODcyODM1Qy44NjcwOTEtMi45NjgxMTkgMS4wMTQ3ODItMy4wMTA5OTggMS4xODE1MzEtMy4wMTA5OThDMS41NDgzNzctMy4wMTA5OTggMS43ODY1ODktMi43NjgwMjEgMS43ODY1ODktMi4zOTE2NDdDMS43ODY1ODktMi4wMDU3NDQgMS41MjkzMi0xLjc3NzA2IDEuMDQ4MTMyLTEuNzc3MDZDMS4wMTQ3ODItMS43NzcwNiAuOTgxNDMzLTEuNzgxODI1IC45NDgwODMtMS43ODE4MjVWLTEuNTAwNzM1QzEuMDM4NjA0LTEuNTE1MDI3IDEuMTI0MzYtMS41MTk3OTEgMS4yMDA1ODgtMS41MTk3OTFDMS42NTc5NTQtMS41MTk3OTEgMS45MDU2OTUtMS4zMDA2MzcgMS45MDU2OTUtLjkyNDI2MkMxLjkwNTY5NS0uNTAwMjQ1IDEuNTkxMjU1LS4xNzE1MTMgMS4xNTc3MS0uMTcxNTEzQzEuMDE0NzgyLS4xNzE1MTMgLjg4MTM4NC0uMjA5NjI2IC43NTI3NDktLjI4NTg1NEwuNjA1MDU4LS42OTA4MTRDLjU1MjY1MS0uNzA5ODcxIC41MDUwMDktLjcxNDYzNSAuNDYyMTMxLS43MTQ2MzVDLjMwOTY3NS0uNzE0NjM1IC4xOTUzMzQtLjYwOTgyMiAuMTk1MzM0LS40NzE2NTlDLjE5NTMzNC0uMjI4NjgzIC41NzE3MDggLjA1MjQwNyAxLjEzODY1MyAuMDUyNDA3QzEuODcyMzQ1IC4wNTI0MDcgMi4zNDQwMDQtLjM4MTEzOSAyLjM0NDAwNC0uOTE0NzMzQzIuMzQ0MDA0LTEuMzQ4Mjc5IDIuMDM5MDkzLTEuNjQzNjYyIDEuNTEwMjYzLTEuNjgxNzc2WicvPjx1c2UgaWQ9J2c1LTUwJyB4bGluazpocmVmPScjZzItNTAnIHRyYW5zZm9ybT0nc2NhbGUoMS40KScvPjx1c2UgaWQ9J2c4LTUxJyB4bGluazpocmVmPScjZzItNTEnIHRyYW5zZm9ybT0nc2NhbGUoMiknLz48L2RlZnM+PGcgaWQ9J3BhZ2UxJz48dXNlIHg9JzAnIHk9JzAnIHhsaW5rOmhyZWY9JyNnOC01MScvPjx1c2UgeD0nNS4zMTc2OTYnIHk9Jy0zLjgxNjEwMycgeGxpbms6aHJlZj0nI2c1LTUwJy8+PHVzZSB4PSc5LjA0MDA4MycgeT0nLTYuNDg3Mzc2JyB4bGluazpocmVmPScjZzItNTAnLz48dXNlIHg9JzExLjY5ODkzJyB5PSctOC4zOTU0MjgnIHhsaW5rOmhyZWY9JyNnMi01MCcvPjx1c2UgeD0nMTQuMzU3Nzc4JyB5PSctMTAuMzAzNDc5JyB4bGluazpocmVmPScjZzItNTAnLz48L2c+PC9zdmc+'> is divided by 10?</p>"

tneotia commented 3 years ago

@ngaurav I tried your branch (I had to update the dependencies bc of conflicts) and the SVG does not show up, is there code I am missing?

ngaurav commented 3 years ago

@tneotia Actually the SVG is showing up. I had used a color filter which was making SVGs white for dark backgrounds. I have updated the branch now. It should be visible.

tneotia commented 3 years ago

Yes it works now and I identified why my implementation wasn't working. Something interesting:

Using alignment: PlaceholderAlignment.aboveBaseline, aligns your SVG perfectly without needing any vertical align, but then manually setting the baseline appears to do nothing.

Only when using alignment: PlaceholderAlignment.baseline,, setting vertical-align will actually do something - but vertical-align 0 appears to align the top of the element with the baseline, rather than the bottom.

Now this brings up a dilemma - I can try and use alignment: PlaceholderAlignment.baseline,, but for that we would have to compensate for the height of the element so that the bottom of the element is aligned with the baseline.

Edit: Also intrinsics aren't available for PlaceholderAlignment.baseline, PlaceholderAlignment.aboveBaseline, or PlaceholderAlignment.belowBaseline, so this breaks any HTML with tables in it.

ngaurav commented 3 years ago

@tneotia alignment: PlaceholderAlignment.aboveBaseline will always align the entire widget above baseline. In the example given by me, the ideal rendering should paint everything above baseline. Therefore, it was appearing perfect. If you have fraction term to render, where the denominator is usually rendered below the baseline, then alignment: PlaceholderAlignment.aboveBaseline will not help. And I understand the dilemma, that flutter asks us to specify the baseline from the top, and therefore we must know the intrinsic height. I do not have a good understanding of intrinsics. But I found this link which uses intrinsics – https://github.com/flutter/flutter/issues/65895. Their code is very similar to the BaselineBox class in my branch of flutter_svg.

ngaurav commented 3 years ago

Another idea could be to try getting the baseline from child and then shifting it in BaselineBox Class:

  double computeDistanceToActualBaseline(TextBaseline baselineType) {
    return _baseline + child!.computeDistanceToActualBaseline(baselineType)! ;
  }

EDIT: This allow _baseline to just have the vertical-align, while in previous case _baseline will be sum of height and vertical-align.

Sub6Resources commented 1 year ago

Closing as duplicate of #481. Follow that issue for updates.