question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Question] Can't seem to get SimpleTagHandler to work with custom html tags

See original GitHub issue
  • Markwon version: 4.3.1

Behaviour

I’ve been having trouble getting markwon to register iframe tags, I’ve got bunch of html with some iframe that embed youtube content e.g. <iframe src="https://www.youtube.com/embed/luWcue3t2OU" width="640" height="360"></iframe>

I tried to debug the issue, by adding a breakpoint on getSpans but the method never ran, suggesting that no iframe tags were found. The html is fairly plain, consisting of a few basic tags.

Here’s an example of the input taken from an rss feed:

<p class="p1"><img title="JUMP FORCE" src="https://img1.ak.crunchyroll.com/i/spire1/f0c009039dd9f8dff5907fff148adfca1587067000_full.jpg" alt="JUMP FORCE" width="640" height="362" /></p>
<p class="p2">&nbsp;</p>
<p class="p1">Switch owners will soon get to take part in the ultimate <em>Shonen Jump </em>rumble. Bandai Namco announced plans to bring <strong><em>Jump Force </em></strong>to <strong>Switch</strong> as <strong><em>Jump Force Deluxe Edition</em></strong>, with a release set for sometime this year. This version will include all of the original playable characters and those from Character Pass 1, and <strong>Character Pass 2 is also in the works </strong>for all versions, starting with <strong>Shoto Todoroki from </strong><span style="color: #ff9900;"><a href="/my-hero-academia?utm_source=editorial_cr&amp;utm_medium=news&amp;utm_campaign=article_driven&amp;referrer=editorial_cr_news_article_driven"><span style="color: #ff9900;"><strong><em>My Hero Academia</em></strong></span></a></span>.</p>
<p class="p2">&nbsp;</p>
<p class="p1">Other than Todoroki, Bandai Namco hinted that the four other Character Pass 2 characters will hail from <span style="color: #ff9900;"><a href="/hunter-x-hunter?utm_source=editorial_cr&amp;utm_medium=news&amp;utm_campaign=article_driven&amp;referrer=editorial_cr_news_article_driven"><span style="color: #ff9900;"><em>Hunter x Hunter</em></span></a></span>, <em>Yu Yu Hakusho</em>, <span style="color: #ff9900;"><a href="/bleach?utm_source=editorial_cr&amp;utm_medium=news&amp;utm_campaign=article_driven&amp;referrer=editorial_cr_news_article_driven"><span style="color: #ff9900;"><em>Bleach</em></span></a></span>, and <span style="color: #ff9900;"><a href="/jojos-bizarre-adventure?utm_source=editorial_cr&amp;utm_medium=news&amp;utm_campaign=article_driven&amp;referrer=editorial_cr_news_article_driven"><span style="color: #ff9900;"><em>JoJo's Bizarre Adventure</em></span></a></span>. Character Pass 2 will be priced at $17.99, and Todoroki launches this spring.<span class="Apple-converted-space">&nbsp;</span></p>
<p class="p2">&nbsp;</p>
<p class="p1"><iframe style="display: block; margin-left: auto; margin-right: auto;" src="https://www.youtube.com/embed/At1qTj-LWCc" frameborder="0" width="640" height="360"></iframe></p>
<p class="p2">&nbsp;</p>
<p class="p1">Character Pass 2 promo:</p>
<p class="p2">&nbsp;</p>
<p class="p1"><iframe style="display: block; margin-left: auto; margin-right: auto;" src="https://www.youtube.com/embed/CukwN6kV4R4" frameborder="0" width="640" height="360"></iframe></p>
<p class="p2">&nbsp;</p>
<p class="p1"><a href="https://got.cr/PremiumTrial-NewsBanner4"><img style="display: block; margin-left: auto; margin-right: auto;" src="https://img1.ak.crunchyroll.com/i/spire4/78f5441d927cf160a93e037b567c2b1f1587067041_full.png" alt="" width="640" height="43" /></a></p>
<p class="p2">&nbsp;</p>
<p class="p1">-------</p>
<p class="p1"><em>Joseph Luster is the Games and Web editor at </em><a href="http://www.otakuusamagazine.com/ME2/Default.asp"><em>Otaku USA Magazine</em></a><em>. You can read his webcomic, </em><a href="http://subhumanzoids.com/comics/big-dumb-fighting-idiots/">BIG DUMB FIGHTING IDIOTS</a><em> at </em><a href="http://subhumanzoids.com/"><em>subhumanzoids</em></a><em>. Follow him on Twitter </em><a href="https://twitter.com/Moldilox"><em>@Moldilox</em></a><em>.</em><span class="Apple-converted-space">&nbsp;</span></p>

Reproduction

I have setup markwon as follows:

private val coreModule = module {
    single {
        Markwon.builder(androidApplication())
            .usePlugin(HtmlPlugin.create())
            .usePlugin(LinkifyPlugin.create())
            .usePlugin(TagPlugin.create())
            .usePlugin(
                GlideImagesPlugin.create(
                    object : GlideImagesPlugin.GlideStore {
                        override fun cancel(target: Target<*>) {
                            Glide.with(androidApplication()).clear(target)
                        }

                        override fun load(drawable: AsyncDrawable): RequestBuilder<Drawable> {
                            return Glide.with(androidApplication()).load(drawable.destination)
                                .transform(
                                    CenterCrop(),
                                    RoundedCorners(
                                        androidApplication().resources
                                            .getDimensionPixelSize(
                                                R.dimen.md_margin
                                            )
                                    )
                                ).placeholder(R.drawable.ic_launcher_foreground)
                        }
                    }
                )
            )
            .build()
    }
}
class TagPlugin private constructor(): AbstractMarkwonPlugin() {

    override fun configure(registry: MarkwonPlugin.Registry) {
        registry.require(HtmlPlugin::class.java) {
            it.addHandler(EmbedTagHandler.create())
            it.addHandler(TagAlignmentHandler.create())
        }
    }

    companion object {
        fun create() = TagPlugin()
    }
}
/**
 * Allows us to handle iframes and extract images from it, we will only target youtube iframes
 * to return a clickable image which should trigger the youtube
 *
 * <iframe src="https://www.youtube.com/embed/luWcue3t2OU" width="640" height="360" />
 */
class EmbedTagHandler private constructor() : SimpleTagHandler() {

    private fun getVideoId(src: String) = src.split('/').last()

    private fun getVideoUrl(src: String): String {
        val videoId = getVideoId(src)
        return "https://youtube.com/watch?v=$videoId"
    }

    private fun createImageLink(src: String): String {
        val videoId = getVideoId(src)
        return "https://img.youtube.com/vi/$videoId/hqdefault.jpg"
    }

    override fun getSpans(
        configuration: MarkwonConfiguration,
        renderProps: RenderProps,
        tag: HtmlTag
    ): Any? {
        val attributes = tag.attributes()
        val source = attributes["src"]
        return if (source?.contains("youtube") == true) {
            val imageSpanFactory = configuration
                .spansFactory()
                .get(Image::class.java)

           // ignore this, I'm actually not sure if this will work :'D (trying to make image clickable)
            val linkSpanFactory = configuration
                .spansFactory()
                .get(Link::class.java)

            val width = attributes["width"]?.toFloat()
            val height = attributes["height"]?.toFloat()
            val imageSize = ImageSize(
                width?.let { ImageSize.Dimension(it, "px") },
                height?.let { ImageSize.Dimension(it, "px") }
            )

            ImageProps.DESTINATION.set(renderProps, createImageLink(source))
            ImageProps.IMAGE_SIZE.set(renderProps, imageSize);
            ImageProps.REPLACEMENT_TEXT_IS_LINK.set(renderProps, false);
            CoreProps.LINK_DESTINATION.set(renderProps, getVideoUrl(source))

            arrayOf(
                imageSpanFactory?.getSpans(configuration, renderProps),
                linkSpanFactory?.getSpans(configuration, renderProps)
            )
        } else {
            // return some sort of unsupported span
            val textSpan = configuration.spansFactory().get(Text::class.java)
            renderProps.set(
                Prop.of("text-literal"),
                "Unsupported embedded element"
            )
            textSpan?.getSpans(configuration, renderProps)
        }
    }

    override fun supportedTags() = listOf("iframe")

    companion object {
        fun create() = EmbedTagHandler()
    }
}

Besides this everything else works perfectly! Thank you in advance 😺

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:9 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
notiescommented, May 25, 2020

center tag is considered to be a block one and iframe is its child. You will need to use TagHandler instead of SimpleTagHandler:

class CenterTagHandler extends TagHandler {

    @Override
    public void handle(@NonNull MarkwonVisitor visitor, @NonNull MarkwonHtmlRenderer renderer, @NonNull HtmlTag tag) {
        if (tag.isBlock()) {
            visitChildren(visitor, renderer, tag.getAsBlock());
        }
        SpannableBuilder.setSpans(
                visitor.builder(),
                new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER),
                tag.start(),
                tag.end()
        );
    }

    @NonNull
    @Override
    public Collection<String> supportedTags() {
        return Collections.singleton("center");
    }
}

it feels like SimpleTagHandler could have this functionality build-in

1reaction
notiescommented, Apr 17, 2020

If you do not control the content, then yes, adding a dummy character (even space would work)

P.S. leaving it here as you’d mentioned regex and parsing html in one sentence 😄

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using custom HTML Tags - css - Stack Overflow
If you do not use standard html tags when designing pages, they will not appear in any organized manner when styles are disabled....
Read more >
Using custom elements - Web Components | MDN
Customized built-in elements inherit from basic HTML elements. To create one of these, you have to specify which element they extend (as implied ......
Read more >
Chapter 8 Custom Tags in JSP Pages (The Java EE 5 Tutorial)
Simple tag handlers can be used only for tags that do not use scripting elements in attribute ... A simple tag can contain...
Read more >
JavaServer Pages (JSP) - A Tutorial
Use a programming text editor to enter the following HTML/JSP codes and save as " first.jsp " under the context root " hellojsp...
Read more >
Custom Elements v1 - Reusable Web Components
It's called HTML. You may have heard of it! It's declarative, portable, well supported, and easy to work with. Great as HTML may...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found