flexmark-java icon indicating copy to clipboard operation
flexmark-java copied to clipboard

Attributes Not Properly Applied to Images

Open solonovamax opened this issue 1 year ago • 0 comments

Describe the Bug

When using the Attributes extension, attributes cannot be applied to an img tag, instead they are always applied to the parent tag.

Reproducing

Note: code here is in kotlin as that's what my project is in and it's easier to write. It is decently trivial to translate it back to java.

val options = MutableDataSet()

options[Parser.EXTENSIONS] = listOf(
    AttributesExtension.create(),
)

val parser = Parser.builder(flexmarkOptions).build()
val htmlRenderer = HtmlRenderer.builder(flexmarkOptions).build()

@Language("Markdown")
val document = parser.parse(
    """
        ![image](/my-image.png){class="my-class"}
    """.trimIndent()
)

val renderedResult = renderer.render(document)

println(renderedResult)

Expected Result

<p>
    <img src="/my-image.png" alt="image" class="my-class">
</p>
<p>
    <a href="/my-link" class="my-class">link</a>
</p>

Actual Result

<p class="my-class">
    <img src="/my-image.png" alt="image">
</p>
<p>
    <a href="/my-link" class="my-class">link</a>
</p>

Notice how it still adds the class to the <a> tag as expected, however it is not added to the <img> tag as expected.

Additional Context

I have determined exactly what is causing the bug, it is particular piece of code right here in AttributesNodePostProcessor.java:

https://github.com/vsch/flexmark-java/blob/cc3a2f59ba6e532833f4805f8134b4dc966ff837/flexmark-ext-attributes/src/main/java/com/vladsch/flexmark/ext/attributes/internal/AttributesNodePostProcessor.java#L90-L92

The issue has to do with the comparison between previous.getEndOffset() and attributesNode.getStartOffset(). In image tags, attributesNode.getStartOffset() is always greater than previous.getEndOffset(), which then causes it to always be true.

Here is a fix that I've implemented in my own project in the meantime:

val endOffset = previous.endOffset
val startOffset = attributesNode.startOffset - if (previous is Image || previous is ResizableImage) 1 else 0
if ((!options.assignTextAttributes && (previous is Text || previous is TextBase)) || endOffset < startOffset) {
    // ...

in java, you would do

int endOffset = previous.getEndOffset();
int startOffset = attributesNode.getStartOffset() - (previous instanceof Image || previous instanceof ResizableImage ? 1 : 0);
if ((!myOptions.assignTextAttributes && (previous instanceof Text || previous instanceof TextBase)) || endOffset < startOffset) {
    // ...

I'm checking if it's an instance of either Image or ResizableImage (to support the Resizable Image extension)

This is also likely an issue with the Media Tags extension, however I have not tested it. If it is, you would fix this by also checking if it's an instance of AbstractMediaLink.

solonovamax avatar Dec 24 '24 21:12 solonovamax