Finishing documentation
This commit is contained in:
		
							parent
							
								
									6d28817215
								
							
						
					
					
						commit
						db0936094f
					
				@ -48,14 +48,7 @@ module.exports = {
 | 
			
		||||
                '/docs/v3/ext-strikethrough/',
 | 
			
		||||
                '/docs/v3/ext-tables/',
 | 
			
		||||
                '/docs/v3/ext-tasklist/',
 | 
			
		||||
                {
 | 
			
		||||
                    title: 'HTML',
 | 
			
		||||
                    collapsable: false,
 | 
			
		||||
                    children: [
 | 
			
		||||
                        '/docs/v3/html/',
 | 
			
		||||
                        '/docs/v3/html/custom-tag-handler.md'
 | 
			
		||||
                    ]
 | 
			
		||||
                },
 | 
			
		||||
                '/docs/v3/html/',
 | 
			
		||||
                '/docs/v3/image/gif.md',
 | 
			
		||||
                '/docs/v3/image/okhttp.md',
 | 
			
		||||
                '/docs/v3/image/svg.md',
 | 
			
		||||
 | 
			
		||||
@ -79,4 +79,23 @@ builder.setHandler("a", new TagHandler() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
Sometimes HTML content might include tags that are not closed (although 
 | 
			
		||||
they are required to be by the spec, for example a `div`).
 | 
			
		||||
Markwon by default disallows such tags and ignores them. Still,
 | 
			
		||||
there is an option to allow them _explicitly_ via builder method:
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder) {
 | 
			
		||||
                builder.allowNonClosedTags(true);
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
Please note that if `allowNonClosedTags=true` then all non-closed tags will be closed
 | 
			
		||||
at the end of a document.
 | 
			
		||||
:::
 | 
			
		||||
@ -1,47 +1,21 @@
 | 
			
		||||
---
 | 
			
		||||
title: 'Overview'
 | 
			
		||||
---
 | 
			
		||||
# HTML
 | 
			
		||||
 | 
			
		||||
# HTML <Badge text="2.0.0" />
 | 
			
		||||
This artifact encapsulates HTML parsing from the core artifact and provides
 | 
			
		||||
few predefined `TagHandlers`
 | 
			
		||||
 | 
			
		||||
<MavenBadge :artifact="'html'" />
 | 
			
		||||
 | 
			
		||||
Starting with version `2.0.0` `Markwon` brings the whole HTML parsing/rendering
 | 
			
		||||
stack _on-site_. The main reason for this are _special_ definitions of HTML nodes
 | 
			
		||||
by <Link name="commonmark-spec" />. More specifically: <Link name="commonmark-spec#inline" displayName="inline" /> 
 | 
			
		||||
and <Link name="commonmark-spec#block" displayName="block" />.
 | 
			
		||||
These two are _a bit_ different from _native_ HTML understanding.
 | 
			
		||||
Well, they are _completely_ different and share only the same names as
 | 
			
		||||
<Link name="html-inlines" displayName="HTML-inline"/> and <Link name="html-blocks" displayName="HTML-block"/>
 | 
			
		||||
elements. This leads to situations when for example an `<i>` tag is considered
 | 
			
		||||
a block when it's used like this:
 | 
			
		||||
 | 
			
		||||
```markdown
 | 
			
		||||
<i>
 | 
			
		||||
Hello from italics tag
 | 
			
		||||
</i>
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(HtmlPlugin.create())
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip A bit of background
 | 
			
		||||
<br>
 | 
			
		||||
<GithubIssue id="52" displayName="This issue" /> had brought attention to differences between HTML & commonmark implementations. <br><br>
 | 
			
		||||
:::
 | 
			
		||||
As this artifact brings modified [jsoup](https://github.com/jhy/jsoup) library 
 | 
			
		||||
it was moved to a standalone module in order to minimize dependencies and unused code
 | 
			
		||||
in applications that does not require HTML render capabilities.
 | 
			
		||||
 | 
			
		||||
Let's modify code snippet above _a bit_:
 | 
			
		||||
 | 
			
		||||
```markdown{3}
 | 
			
		||||
<i>
 | 
			
		||||
Hello from italics tag
 | 
			
		||||
 | 
			
		||||
</i>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
We have just added a `new-line` before closing `</i>` tag. And this
 | 
			
		||||
changes everything as now, according to the <Link name="commonmark-dingus" />,
 | 
			
		||||
we have 2 HtmlBlocks: one before `new-line` (containing open `<i>` tag and text content)
 | 
			
		||||
and one after (containing as little as closing `</i>` tag).
 | 
			
		||||
 | 
			
		||||
If we modify code snippet _a bit_ again:
 | 
			
		||||
Before <Badge text="2.0.0" /> `Markwon` used android `Html` class for parsing and
 | 
			
		||||
rendering. Unfortunately, according to markdown specification, markdown can contain
 | 
			
		||||
HTML in _unpredictable_ way if rendered _outside_ of browser. For example:
 | 
			
		||||
 | 
			
		||||
```markdown{4}
 | 
			
		||||
<i>
 | 
			
		||||
@ -50,260 +24,38 @@ Hello from italics tag
 | 
			
		||||
</i><b>bold></b>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
We will have 1 HtmlBlock (from previous snippet) and a bunch of HtmlInlines:
 | 
			
		||||
This snippet could be represented as:
 | 
			
		||||
* HtmlBlock (`<i>\nHello from italics tag`)
 | 
			
		||||
* HtmlInline (`<i>`)
 | 
			
		||||
* HtmlInline (`<b>`)
 | 
			
		||||
* Text (`bold`)
 | 
			
		||||
* HtmlInline (`</b>`)
 | 
			
		||||
 | 
			
		||||
Those _little_ differences render `Html.fromHtml` (which was used in `1.x.x` versions)
 | 
			
		||||
useless. And actually it renders most of the HTML parsers implementations useless,
 | 
			
		||||
as most of them do not allow processing of HTML fragments in a raw fashion
 | 
			
		||||
without _fixing_ content on-the-fly.
 | 
			
		||||
 | 
			
		||||
Both `TagSoup` and `Jsoup` HTML parsers (that were considered for this project) are built to deal with 
 | 
			
		||||
_malicious_ HTML code (*all HTML code*? :no_mouth:). So, when supplied 
 | 
			
		||||
with a `<i>italic` fragment they will make it `<i>italic</i>`.
 | 
			
		||||
And it's a good thing, but consider these fragments for the sake of markdown:
 | 
			
		||||
 | 
			
		||||
* `<i>italic `
 | 
			
		||||
* `<b>bold italic`
 | 
			
		||||
* `</b><i>`
 | 
			
		||||
 | 
			
		||||
We will get:
 | 
			
		||||
 | 
			
		||||
* `<i>italic </i>`
 | 
			
		||||
* `<b>bold italic</b>`
 | 
			
		||||
 | 
			
		||||
_<sup>*</sup> Or to be precise: `<html><head></head><body><i>italic </i></body></html>` &
 | 
			
		||||
`<html><head></head><body><b>bold italic</b></body></html>`_
 | 
			
		||||
 | 
			
		||||
Which will be rendered in a final document:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
|expected|actual|
 | 
			
		||||
|---|---|
 | 
			
		||||
|<i>italic <b>bold italic</b></i>|<i>italic </i><b>bold italic</b>|
 | 
			
		||||
 | 
			
		||||
This might seem like a minor problem, but add more tags to a document,
 | 
			
		||||
introduce some deeply nested structures, spice openning and closing tags up
 | 
			
		||||
by adding markdown markup between them and finally write _malicious_ HTML code :laughing:!
 | 
			
		||||
 | 
			
		||||
There is no such problem on the _frontend_ for which commonmark specification is mostly
 | 
			
		||||
aimed as _frontend_ runs in a web-browser environment. After all _parsed_ markdown
 | 
			
		||||
will become HTML tags (most common usage). And web-browser will know how to render final result.
 | 
			
		||||
 | 
			
		||||
We, on the other hand, do not posess HTML heritage (*thank :robot:!*), but still
 | 
			
		||||
want to display some HTML to style resulting markdown a bit. That's why `Markwon`
 | 
			
		||||
incorporated own HTML parsing logic. It is based on the <Link name="jsoup" /> project.
 | 
			
		||||
And makes usage of the `Tokekiser` class that allows to _tokenise_ input HTML.
 | 
			
		||||
All other code that doesn't follow this purpose was removed. It's safe to use
 | 
			
		||||
in projects that already have `jsoup` dependency as `Markwon` repackaged **jsoup** source classes
 | 
			
		||||
(which could be found <Link name="markwon-jsoup" displayName="here"/>)
 | 
			
		||||
 | 
			
		||||
## Parser
 | 
			
		||||
 | 
			
		||||
There are no additional steps to configure HTML parsing. It's enabled by default.
 | 
			
		||||
If you wish to _exclude_ it, please follow the [exclude](#exclude-html-parsing) section below.
 | 
			
		||||
 | 
			
		||||
The key class here is: `MarkwonHtmlParser` that is defined in `markwon-html-parser-api` module.
 | 
			
		||||
`markwon-html-parser-api` is a simple module that defines HTML parsing contract and
 | 
			
		||||
does not provide implementation. 
 | 
			
		||||
 | 
			
		||||
To change what implementation `Markwon` should use, `SpannableConfiguration` can be used:
 | 
			
		||||
 | 
			
		||||
```java{2}
 | 
			
		||||
SpannableConfiguration.builder(context)
 | 
			
		||||
        .htmlParser(MarkwonHtmlParser)
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`markwon-html-parser-impl` on the other hand provides `MarkwonHtmlParser` implementation.
 | 
			
		||||
It's called `MarkwonHtmlParserImpl`. It can be created like this:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final MarkwonHtmlParser htmlParser = MarkwonHtmlParserImpl.create();
 | 
			
		||||
// or
 | 
			
		||||
final MarkwonHtmlParser htmlParser = MarkwonHtmlParserImpl.create(HtmlEmptyTagReplacement);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Empty tag replacement
 | 
			
		||||
 | 
			
		||||
In order to append text content for self-closing, void or just _empty_ HTML tags,
 | 
			
		||||
`HtmlEmptyTagReplacement` can be used. As we cannot set Span for empty content,
 | 
			
		||||
we must represent empty tag with text during parsing stage (if we want it to be represented).
 | 
			
		||||
 | 
			
		||||
Consider this:
 | 
			
		||||
* `<img src="me-sad.JPG">`
 | 
			
		||||
* `<br />`
 | 
			
		||||
* `<who-am-i></who-am-i>`
 | 
			
		||||
 | 
			
		||||
By default (`HtmlEmptyTagReplacement.create()`) will handle `img` and `br` tags.
 | 
			
		||||
`img` will be replaced with `alt` property if it is present and `\uFFFC` if it is not. 
 | 
			
		||||
And `br` will insert a new line.
 | 
			
		||||
 | 
			
		||||
### Non-closed tags
 | 
			
		||||
 | 
			
		||||
It's possible that your HTML can contain non-closed tags. By default `Markwon` will ignore them,
 | 
			
		||||
but if you wish to get a bit closer to a web-browser experience, you can allow this behaviour:
 | 
			
		||||
 | 
			
		||||
```java{2}
 | 
			
		||||
SpannableConfiguration.builder(context)
 | 
			
		||||
        .htmlAllowNonClosedTags(true)
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::warning Note
 | 
			
		||||
If there is (for example) an `<i>` tag at the start of a document and it's not closed
 | 
			
		||||
and `Markwon` is configured to **not** ignore non-closed tags (`.htmlAllowNonClosedTags(true)`),
 | 
			
		||||
it will make the whole document in italics
 | 
			
		||||
:::tip A bit of background
 | 
			
		||||
<br>
 | 
			
		||||
<GithubIssue id="52" displayName="This issue" /> had brought attention to differences between HTML & commonmark implementations. <br><br>
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
### Implementation note
 | 
			
		||||
Unfortunately Android `HTML` class cannot parse a _fragment_ of HTML to later
 | 
			
		||||
be included in a bigger set of content. This is why the decision was made to bring
 | 
			
		||||
HTML parsing _in-markwon-house_
 | 
			
		||||
 | 
			
		||||
`MarkwonHtmlParserImpl` does not create a unified HTML node. Instead it creates
 | 
			
		||||
2 collections: inline tags and block tags. Inline tags are represented as a `List`
 | 
			
		||||
of inline tags (<Link name="html-inlines" displayName="reference" />). And
 | 
			
		||||
block tags are structured in a tree. This helps to achieve _browser_-like behaviour,
 | 
			
		||||
when open inline tag is applied to all content (even if inside blocks) until closing tag.
 | 
			
		||||
All tags that are not _inline_ are considered to be _block_ ones.
 | 
			
		||||
## Predefined TagHandlers
 | 
			
		||||
* `<img>`
 | 
			
		||||
* `<a>`
 | 
			
		||||
* `<blockquote>`
 | 
			
		||||
* `<sub>`
 | 
			
		||||
* `<sup>`
 | 
			
		||||
* `<b>, <strong>`
 | 
			
		||||
* `<s>, <del>`
 | 
			
		||||
* `<u>, <ins>`
 | 
			
		||||
* `<ul>, <ol>`
 | 
			
		||||
* `<i>, <cite>, <em>, <dfn>`
 | 
			
		||||
* `<h1>, <h2>, <h3>, <h4>, <h5>, <h6>`
 | 
			
		||||
 | 
			
		||||
## Renderer
 | 
			
		||||
 | 
			
		||||
Unlike `MarkwonHtmlParser` `Markwon` comes with a `MarkwonHtmlRenderer` by default.
 | 
			
		||||
 | 
			
		||||
Default implementation can be obtain like this:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
MarkwonHtmlRenderer.create();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Default instance have these tags _handled_:
 | 
			
		||||
* emphasis
 | 
			
		||||
  * `i`
 | 
			
		||||
  * `em`
 | 
			
		||||
  * `cite`
 | 
			
		||||
  * `dfn`
 | 
			
		||||
* strong emphasis
 | 
			
		||||
  * `b`
 | 
			
		||||
  * `strong`
 | 
			
		||||
* `sup` (super script)
 | 
			
		||||
* `sub` (sub script)
 | 
			
		||||
* underline
 | 
			
		||||
  * `u`
 | 
			
		||||
  * `ins`
 | 
			
		||||
* strike through
 | 
			
		||||
  * `del`
 | 
			
		||||
  * `s`
 | 
			
		||||
  * `strike`
 | 
			
		||||
* `a` (link)
 | 
			
		||||
* `ul` (unordered list)
 | 
			
		||||
* `ol` (ordered list)
 | 
			
		||||
* `img` (image)
 | 
			
		||||
* `blockquote` (block quote)
 | 
			
		||||
* `h{1-6}` (heading)
 | 
			
		||||
 | 
			
		||||
If you wish to _extend_ default handling (or override existing),
 | 
			
		||||
`#builderWithDefaults` factory method can be used:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
MarkwonHtmlRenderer.builderWithDefaults();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For a completely _clean_ configurable instance `#builder` method can be used:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
MarkwonHtmlRenderer.builder();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Custom tag handler
 | 
			
		||||
 | 
			
		||||
To configure `MarkwonHtmlRenderer` to handle tags differently or 
 | 
			
		||||
create a new tag handler - `TagHandler` can be used
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public abstract class TagHandler {
 | 
			
		||||
 | 
			
		||||
    public abstract void handle(
 | 
			
		||||
            @NonNull SpannableConfiguration configuration,
 | 
			
		||||
            @NonNull SpannableBuilder builder,
 | 
			
		||||
            @NonNull HtmlTag tag
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For the most simple _inline_ tag handler a `SimpleTagHandler` can be used:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public abstract class SimpleTagHandler extends TagHandler {
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public abstract Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For example, `EmphasisHandler`:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public class EmphasisHandler extends SimpleTagHandler {
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
 | 
			
		||||
        return configuration.factory().emphasis();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If you wish to handle a _block_ HTML node (for example `<ul><li>First<li>Second</ul>`) refer
 | 
			
		||||
to `ListHandler` source code for reference.
 | 
			
		||||
 | 
			
		||||
:::warning
 | 
			
		||||
The most important thing when implementing custom `TagHandler` is to know
 | 
			
		||||
what type of `HtmlTag` we are dealing with. There are 2: inline & block.
 | 
			
		||||
Inline tag cannot contain children. Block _can_ contain children. And they
 | 
			
		||||
_most likely_ should also be visited and _handled_ by registered `TagHandler` (if any)
 | 
			
		||||
accordingly. See `TagHandler#visitChildren(configuration, builder, child);`
 | 
			
		||||
:::tip
 | 
			
		||||
All predefined tag handlers will use styling spans for native markdown content.
 | 
			
		||||
So, if your `Markwon` instance was configured to, for example, render Emphasis
 | 
			
		||||
nodes as a <span style="color: #FF0000">red text</span> then HTML tag handler will
 | 
			
		||||
use the same span. This includes images, links, UrlResolver, LinkProcessor, etc
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
#### Css inline style parser
 | 
			
		||||
 | 
			
		||||
When implementing own `TagHandler` you might want to inspect inline CSS styles
 | 
			
		||||
of a HTML element. `Markwon` provides an utility parser for that purpose:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final CssInlineStyleParser inlineStyleParser = CssInlineStyleParser.create();
 | 
			
		||||
for (CssProperty property: inlineStyleParser.parse("width: 100%; height: 100%;")) {
 | 
			
		||||
    // [0] = CssProperty({width=100%}),
 | 
			
		||||
    // [1] = CssProperty({height=100%})
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Exclude HTML parsing
 | 
			
		||||
 | 
			
		||||
If you wish to exclude HTML parsing altogether, you can manually
 | 
			
		||||
exclude `markwon-html-parser-impl` artifact from your projects compile classpath.
 | 
			
		||||
This can be beneficial if you know that markdown input won't contain
 | 
			
		||||
HTML and/or you wish to ignore it. Excluding HTML parsing
 | 
			
		||||
can speed up `Markwon` parsing and will decrease final size of
 | 
			
		||||
`Markwon` dependency by around `100kb`.
 | 
			
		||||
 | 
			
		||||
<MavenBadge :artifact="'markwon'" />
 | 
			
		||||
 | 
			
		||||
```groovy
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation("ru.noties:markwon:${markwonVersion}") {
 | 
			
		||||
        exclude module: 'markwon-html-parser-impl'
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Excluding `markwon-html-parser-impl` this way will result in
 | 
			
		||||
`MarkwonHtmlParser#noOp` implementation. No further steps are 
 | 
			
		||||
required.
 | 
			
		||||
 | 
			
		||||
:::warning Note
 | 
			
		||||
Excluding `markwon-html-parser-impl` won't remove *all* the content between
 | 
			
		||||
HTML tags. It will if `commonmark` decides that a specific fragment is a 
 | 
			
		||||
`HtmlBlock`, but it won't if fragment is considered a `HtmlInline` as `HtmlInline`
 | 
			
		||||
does not contain content (just a tag definition).
 | 
			
		||||
:::
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
# HTML custom tag handler
 | 
			
		||||
@ -1,3 +1,15 @@
 | 
			
		||||
# Image GIF
 | 
			
		||||
 | 
			
		||||
<MavenBadge :artifact="'image-gif'" />
 | 
			
		||||
<MavenBadge :artifact="'image-gif'" />
 | 
			
		||||
 | 
			
		||||
Adds support for GIF images inside markdown. 
 | 
			
		||||
Relies on [android-gif-drawable library](https://github.com/koral--/android-gif-drawable)
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        // it's required to register ImagesPlugin
 | 
			
		||||
        .usePlugin(ImagesPlugin.create(context))
 | 
			
		||||
        // add GIF support for images
 | 
			
		||||
        .usePlugin(GifPlugin.create())
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
@ -2,6 +2,25 @@
 | 
			
		||||
 | 
			
		||||
<MavenBadge :artifact="'image-okhttp'" />
 | 
			
		||||
 | 
			
		||||
Uses [okhttp library](https://github.com/square/okhttp) as the network transport fro images. Since <Badge text="3.0.0" />
 | 
			
		||||
`Markwon` uses a system-native `HttpUrlConnection` and does not rely on any
 | 
			
		||||
3rd-party tool to download resources from network. It can answer the most common needs,
 | 
			
		||||
but if you would like to have a custom redirect policy or add an explicit caching
 | 
			
		||||
of downloaded resources OkHttp might be a better option.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        // it's required to register ImagesPlugin
 | 
			
		||||
        .usePlugin(ImagesPlugin.create(context))
 | 
			
		||||
        
 | 
			
		||||
        // will create default instance of OkHttpClient
 | 
			
		||||
        .usePlugin(OkHttpImagesPlugin.create())
 | 
			
		||||
        
 | 
			
		||||
        // or accept a configured client
 | 
			
		||||
        .usePlugin(OkHttpImagesPlugin.create(new OkHttpClient()))
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Proguard
 | 
			
		||||
```proguard
 | 
			
		||||
-dontwarn okhttp3.**
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,21 @@
 | 
			
		||||
 | 
			
		||||
<MavenBadge :artifact="'image-svg'" />
 | 
			
		||||
 | 
			
		||||
Adds support for SVG images inside markdown. 
 | 
			
		||||
Relies on [androidsvg library](https://github.com/BigBadaboom/androidsvg)
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        // it's required to register ImagesPlugin
 | 
			
		||||
        .usePlugin(ImagesPlugin.create(context))
 | 
			
		||||
        .usePlugin(SvgPlugin.create(context.getResources()))
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
`SvgPlugin` requires `Resources` in order to scale SVG media based on display density
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Proguard
 | 
			
		||||
 | 
			
		||||
```proguard
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
# Recycler Table <Badge text="3.0.0" />
 | 
			
		||||
 | 
			
		||||
<MavenBadge :artifact="'recycler-table'" />
 | 
			
		||||
 | 
			
		||||
Artifact that provides [MarkwonAdapter.Entry](/docs/v3/recycler/) to render `TableBlock` inside 
 | 
			
		||||
Android-native `TableLayout` widget.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,153 @@
 | 
			
		||||
# Recycler
 | 
			
		||||
# Recycler <Badge text="3.0.0" />
 | 
			
		||||
 | 
			
		||||
<MavenBadge :artifact="'recycler'" />
 | 
			
		||||
<MavenBadge :artifact="'recycler'" />
 | 
			
		||||
 | 
			
		||||
This artifact allows displaying markdown in a set of Android widgets
 | 
			
		||||
inside a RecyclerView. Can be useful when displaying lengthy markdown
 | 
			
		||||
content or **displaying certain markdown blocks inside specific widgets**.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
// create an adapter that will use a TextView for each block of markdown
 | 
			
		||||
// `createTextViewIsRoot` accepts a layout in which TextView is the root view
 | 
			
		||||
final MarkwonAdapter adapter = 
 | 
			
		||||
        MarkwonAdapter.createTextViewIsRoot(R.layout.adapter_default_entry);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
// `create` method accepts a layout with TextView and ID of a TextView
 | 
			
		||||
// which allows wrapping a TextView inside another widget or combine with other widgets
 | 
			
		||||
final MarkwonAdapter adapter = 
 | 
			
		||||
        MarkwonAdapter.create(R.layout.adapter_default_entry, R.id.text_view);
 | 
			
		||||
 | 
			
		||||
// initialize RecyclerView (LayoutManager, Decorations, etc)
 | 
			
		||||
final RecyclerView recyclerView = obtainRecyclerView();
 | 
			
		||||
 | 
			
		||||
// set adapter
 | 
			
		||||
recyclerView.setAdapter(adapter);
 | 
			
		||||
 | 
			
		||||
// obtain an instance of Markwon (register all required plugins)
 | 
			
		||||
final Markwon markwon = obtainMarkwon();
 | 
			
		||||
 | 
			
		||||
// set markdown to be displayed
 | 
			
		||||
adapter.setMarkdown(markwon, "# This is markdown!");
 | 
			
		||||
 | 
			
		||||
// NB, adapter does not handle updates on its own, please use
 | 
			
		||||
// whatever method appropriate for you.
 | 
			
		||||
adapter.notifyDataSetChanged();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Initialized adapter above will use a TextView for each markdown block.
 | 
			
		||||
In order to tell adapter to render certain blocks differently a `builder` can be used.
 | 
			
		||||
For example, let's render `FencedCodeBlock` inside a `HorizontalScrollView`:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
// we still need to have a _default_ entry
 | 
			
		||||
final MarkwonAdapter adapter =
 | 
			
		||||
        MarkwonAdapter.builderTextViewIsRoot(R.layout.adapter_default_entry)
 | 
			
		||||
                .include(FencedCodeBlock.class, new FencedCodeBlockEntry())
 | 
			
		||||
                .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
where `FencedCodeBlockEntry` is:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public class FencedCodeBlockEntry extends MarkwonAdapter.Entry<FencedCodeBlock, FencedCodeBlockEntry.Holder> {
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public Holder createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
 | 
			
		||||
        return new Holder(inflater.inflate(R.layout.adapter_fenced_code_block, parent, false));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void bindHolder(@NonNull Markwon markwon, @NonNull Holder holder, @NonNull FencedCodeBlock node) {
 | 
			
		||||
        markwon.setParsedMarkdown(holder.textView, markwon.render(node));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Holder extends MarkwonAdapter.Holder {
 | 
			
		||||
 | 
			
		||||
        final TextView textView;
 | 
			
		||||
 | 
			
		||||
        public Holder(@NonNull View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
 | 
			
		||||
            this.textView = requireView(R.id.text_view);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
and its layout (`R.layout.adapter_fenced_code_block`):
 | 
			
		||||
 | 
			
		||||
```xml
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="wrap_content"
 | 
			
		||||
    android:clipChildren="false"
 | 
			
		||||
    android:clipToPadding="false"
 | 
			
		||||
    android:fillViewport="true"
 | 
			
		||||
    android:paddingLeft="16dip"
 | 
			
		||||
    android:paddingRight="16dip"
 | 
			
		||||
    android:scrollbarStyle="outsideInset">
 | 
			
		||||
 | 
			
		||||
    <TextView
 | 
			
		||||
        android:id="@+id/text"
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:background="#0f000000"
 | 
			
		||||
        android:fontFamily="monospace"
 | 
			
		||||
        android:lineSpacingExtra="2dip"
 | 
			
		||||
        android:paddingLeft="16dip"
 | 
			
		||||
        android:paddingTop="8dip"
 | 
			
		||||
        android:paddingRight="16dip"
 | 
			
		||||
        android:paddingBottom="8dip"
 | 
			
		||||
        android:textAppearance="?android:attr/textAppearanceMedium"
 | 
			
		||||
        android:textSize="14sp" />
 | 
			
		||||
 | 
			
		||||
</HorizontalScrollView>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
As we apply styling to `FencedCodeBlock` _manually_, we no longer need
 | 
			
		||||
`Markwon` to apply styling spans for us, so `Markwon` initialization could be:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        // your other plugins
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
 | 
			
		||||
                builder.on(FencedCodeBlock.class, (visitor, fencedCodeBlock) -> {
 | 
			
		||||
                    // we actually won't be applying code spans here, as our custom view will
 | 
			
		||||
                    // draw background and apply mono typeface
 | 
			
		||||
                    //
 | 
			
		||||
                    // NB the `trim` operation on literal (as code will have a new line at the end)
 | 
			
		||||
                    final CharSequence code = visitor.configuration()
 | 
			
		||||
                            .syntaxHighlight()
 | 
			
		||||
                            .highlight(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral().trim());
 | 
			
		||||
                    visitor.builder().append(code);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Previously we have created a `FencedCodeBlockEntry` but all it does is apply markdown to a TextView.
 | 
			
		||||
For such a case there is a `SimpleEntry` that could be used instead:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final MarkwonAdapter adapter =
 | 
			
		||||
        MarkwonAdapter.builderTextViewIsRoot(R.layout.adapter_default_entry)
 | 
			
		||||
                .include(FencedCodeBlock.class, SimpleEntry.create(R.layout.adapter_fenced_code_block, R.id.text_view))
 | 
			
		||||
                .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
`SimpleEntry` also takes care of _caching_ parsed markdown. So each node will be
 | 
			
		||||
parsed only once and each subsequent adapter binding call will reuse previously cached markdown.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::tip Tables
 | 
			
		||||
There is a standalone artifact that adds support for displaying markdown tables
 | 
			
		||||
natively via `TableLayout`. Please refer to its [documentation](/docs/v3/recycler-table/)
 | 
			
		||||
:::
 | 
			
		||||
@ -47,6 +47,9 @@ public abstract class MarkwonHtmlRenderer {
 | 
			
		||||
        @NonNull
 | 
			
		||||
        Builder setHandler(@NonNull Collection<String> tagNames, @Nullable TagHandler tagHandler);
 | 
			
		||||
 | 
			
		||||
        @Nullable
 | 
			
		||||
        TagHandler getHandler(@NonNull String tagName);
 | 
			
		||||
 | 
			
		||||
        @NonNull
 | 
			
		||||
        MarkwonHtmlRenderer build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -124,6 +124,12 @@ class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nullable
 | 
			
		||||
        @Override
 | 
			
		||||
        public TagHandler getHandler(@NonNull String tagName) {
 | 
			
		||||
            return tagHandlers.get(tagName);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @NonNull
 | 
			
		||||
        @Override
 | 
			
		||||
        public MarkwonHtmlRenderer build() {
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,7 @@ dependencies {
 | 
			
		||||
    implementation project(':markwon-ext-tasklist')
 | 
			
		||||
    implementation project(':markwon-html')
 | 
			
		||||
    implementation project(':markwon-image-gif')
 | 
			
		||||
    implementation project(':markwon-image-okhttp')
 | 
			
		||||
    implementation project(':markwon-image-svg')
 | 
			
		||||
    implementation project(':markwon-syntax-highlight')
 | 
			
		||||
    implementation project(':markwon-recycler')
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="wrap_content"
 | 
			
		||||
    android:clipChildren="false"
 | 
			
		||||
@ -22,7 +21,6 @@
 | 
			
		||||
        android:paddingRight="16dip"
 | 
			
		||||
        android:paddingBottom="8dip"
 | 
			
		||||
        android:textAppearance="?android:attr/textAppearanceMedium"
 | 
			
		||||
        android:textSize="14sp"
 | 
			
		||||
        tools:text="# Hello there! and tasks" />
 | 
			
		||||
        android:textSize="14sp" />
 | 
			
		||||
 | 
			
		||||
</HorizontalScrollView>
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user