Started with v4 documentation
This commit is contained in:
		
							parent
							
								
									14591508b5
								
							
						
					
					
						commit
						8b0edc32c3
					
				@ -97,15 +97,6 @@ Please visit [documentation] web-site for reference
 | 
			
		||||
 | 
			
		||||
[documentation]: https://noties.github.io/Markwon
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Applications using Markwon
 | 
			
		||||
 | 
			
		||||
* [Partiko](https://partiko.app)
 | 
			
		||||
* [FairNote Notepad](https://play.google.com/store/apps/details?id=com.rgiskard.fairnote)
 | 
			
		||||
* [Boxcryptor](https://www.boxcryptor.com)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Demo
 | 
			
		||||
 | 
			
		||||
@ -9,4 +9,5 @@
 | 
			
		||||
* images-plugin moved to standalone again
 | 
			
		||||
* removed MarkwonPlugin#configureHtmlRenderer -> now part of HtmlPlugin
 | 
			
		||||
* TagHandler now has `supportedTags()` method
 | 
			
		||||
* html is moved completely to html-plugin
 | 
			
		||||
* html is moved completely to html-plugin
 | 
			
		||||
* OnTextAddedListener
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
 | 
			
		||||
// this is a generated file, do not modify. To update it run 'collectArtifacts.js' script
 | 
			
		||||
const artifacts = [{"id":"core","name":"Core","group":"ru.noties.markwon","description":"Core Markwon artifact that includes basic markdown parsing and rendering"},{"id":"ext-latex","name":"LaTeX","group":"ru.noties.markwon","description":"Extension to add LaTeX formulas to Markwon markdown"},{"id":"ext-strikethrough","name":"Strikethrough","group":"ru.noties.markwon","description":"Extension to add strikethrough markup to Markwon markdown"},{"id":"ext-tables","name":"Tables","group":"ru.noties.markwon","description":"Extension to add tables markup (GFM) to Markwon markdown"},{"id":"ext-tasklist","name":"Task List","group":"ru.noties.markwon","description":"Extension to add task lists (GFM) to Markwon markdown"},{"id":"html","name":"HTML","group":"ru.noties.markwon","description":"Provides HTML parsing functionality"},{"id":"image-gif","name":"Image GIF","group":"ru.noties.markwon","description":"Adds GIF media support to Markwon markdown"},{"id":"image-okhttp","name":"Image OkHttp","group":"ru.noties.markwon","description":"Adds OkHttp client to retrieve images data from network"},{"id":"image-svg","name":"Image SVG","group":"ru.noties.markwon","description":"Adds SVG media support to Markwon markdown"},{"id":"recycler","name":"Recycler","group":"ru.noties.markwon","description":"Provides RecyclerView.Adapter to display Markwon markdown"},{"id":"recycler-table","name":"Recycler Table","group":"ru.noties.markwon","description":"Provides MarkwonAdapter.Entry to render TableBlocks inside Android-native TableLayout widget"},{"id":"syntax-highlight","name":"Syntax Highlight","group":"ru.noties.markwon","description":"Add syntax highlight to Markwon markdown via Prism4j library"}];
 | 
			
		||||
const artifacts = [{"id":"core","name":"Core","group":"io.noties.markwon","description":"Core Markwon artifact that includes basic markdown parsing and rendering"},{"id":"ext-latex","name":"LaTeX","group":"io.noties.markwon","description":"Extension to add LaTeX formulas to Markwon markdown"},{"id":"ext-strikethrough","name":"Strikethrough","group":"io.noties.markwon","description":"Extension to add strikethrough markup to Markwon markdown"},{"id":"ext-tables","name":"Tables","group":"io.noties.markwon","description":"Extension to add tables markup (GFM) to Markwon markdown"},{"id":"ext-tasklist","name":"Task List","group":"io.noties.markwon","description":"Extension to add task lists (GFM) to Markwon markdown"},{"id":"html","name":"HTML","group":"io.noties.markwon","description":"Provides HTML parsing functionality"},{"id":"image","name":"Image","group":"io.noties.markwon","description":"Markwon image loading module (with optional GIF and SVG support)"},{"id":"image-gif","name":"Image GIF","group":"io.noties.markwon","description":"Adds GIF media support to Markwon markdown"},{"id":"image-glide","name":"Image Glide","group":"io.noties.markwon","description":"Markwon image loading module (based on Glide library)"},{"id":"image-okhttp","name":"Image OkHttp","group":"io.noties.markwon","description":"Adds OkHttp client to retrieve images data from network"},{"id":"image-picasso","name":"Image Picasso","group":"io.noties.markwon","description":"Markwon image loading module (based on Picasso library)"},{"id":"image-svg","name":"Image SVG","group":"io.noties.markwon","description":"Adds SVG media support to Markwon markdown"},{"id":"linkify","name":"Linkify","group":"io.noties.markwon","description":"Markwon plugin to linkify text (based on Android Linkify)"},{"id":"recycler","name":"Recycler","group":"io.noties.markwon","description":"Provides RecyclerView.Adapter to display Markwon markdown"},{"id":"recycler-table","name":"Recycler Table","group":"io.noties.markwon","description":"Provides MarkwonAdapter.Entry to render TableBlocks inside Android-native TableLayout widget"},{"id":"syntax-highlight","name":"Syntax Highlight","group":"io.noties.markwon","description":"Add syntax highlight to Markwon markdown via Prism4j library"}];
 | 
			
		||||
export { artifacts };
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								docs/.vuepress/.artifacts.v3.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								docs/.vuepress/.artifacts.v3.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
 | 
			
		||||
// this is a generated file, do not modify. To update it run 'collectArtifacts.js' script
 | 
			
		||||
const artifacts = [{"id":"core","name":"Core","group":"ru.noties.markwon","description":"Core Markwon artifact that includes basic markdown parsing and rendering"},{"id":"ext-latex","name":"LaTeX","group":"ru.noties.markwon","description":"Extension to add LaTeX formulas to Markwon markdown"},{"id":"ext-strikethrough","name":"Strikethrough","group":"ru.noties.markwon","description":"Extension to add strikethrough markup to Markwon markdown"},{"id":"ext-tables","name":"Tables","group":"ru.noties.markwon","description":"Extension to add tables markup (GFM) to Markwon markdown"},{"id":"ext-tasklist","name":"Task List","group":"ru.noties.markwon","description":"Extension to add task lists (GFM) to Markwon markdown"},{"id":"html","name":"HTML","group":"ru.noties.markwon","description":"Provides HTML parsing functionality"},{"id":"image-gif","name":"Image GIF","group":"ru.noties.markwon","description":"Adds GIF media support to Markwon markdown"},{"id":"image-okhttp","name":"Image OkHttp","group":"ru.noties.markwon","description":"Adds OkHttp client to retrieve images data from network"},{"id":"image-svg","name":"Image SVG","group":"ru.noties.markwon","description":"Adds SVG media support to Markwon markdown"},{"id":"recycler","name":"Recycler","group":"ru.noties.markwon","description":"Provides RecyclerView.Adapter to display Markwon markdown"},{"id":"recycler-table","name":"Recycler Table","group":"ru.noties.markwon","description":"Provides MarkwonAdapter.Entry to render TableBlocks inside Android-native TableLayout widget"},{"id":"syntax-highlight","name":"Syntax Highlight","group":"ru.noties.markwon","description":"Add syntax highlight to Markwon markdown via Prism4j library"}];
 | 
			
		||||
export { artifacts };
 | 
			
		||||
@ -29,7 +29,7 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { artifacts } from "../.artifacts.js";
 | 
			
		||||
import { artifacts } from "../.artifacts.v3.js";
 | 
			
		||||
 | 
			
		||||
if (!artifacts) {
 | 
			
		||||
  throw "Artifacts not found. Use `collectArtifacts.js` script to obtain artifacts metadata.";
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										105
									
								
								docs/.vuepress/components/ArtifactPicker4.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								docs/.vuepress/components/ArtifactPicker4.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,105 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <div class="artifact-container">
 | 
			
		||||
      <div v-for="artifact in artifacts" class="artifact" @click="toggleSelection(artifact)">
 | 
			
		||||
        <div class="artifact-header">
 | 
			
		||||
          <input type="checkbox" v-model="selected" :value="artifact.id" :id="artifact.id">
 | 
			
		||||
          <strong>
 | 
			
		||||
            <label :for="artifact.id">{{artifact.name}}</label>
 | 
			
		||||
          </strong>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="artifact-description" v-if="artifact.description">{{artifact.description}}</div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="extra-class language-gradle selected-artifacts" v-if="selected.length > 0">
 | 
			
		||||
      <div class="selected-artifact-script">
 | 
			
		||||
        <span class="token keyword">final def</span>
 | 
			
		||||
        <span> markwon_version = </span>
 | 
			
		||||
        <span class="token string">'{{latestVersion}}'</span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <br>
 | 
			
		||||
      <div class="selected-artifact-script" v-for="artifact in selectedArtifacts">
 | 
			
		||||
        <span>implementation </span>
 | 
			
		||||
        <span class="token string">"{{artifact.group}}:{{artifact.id}}:</span>
 | 
			
		||||
        <span>$markwon_version</span>
 | 
			
		||||
        <span class="token string">"</span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { artifacts } from "../.artifacts.js";
 | 
			
		||||
 | 
			
		||||
if (!artifacts) {
 | 
			
		||||
  throw "Artifacts not found. Use `collectArtifacts.js` script to obtain artifacts metadata.";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "ArtifactPicker",
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      artifacts,
 | 
			
		||||
      selected: ["core"],
 | 
			
		||||
      latestVersion: "latest_version"
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    toggleSelection(artifact) {
 | 
			
		||||
      const index = this.selected.indexOf(artifact.id);
 | 
			
		||||
      if (index < 0) {
 | 
			
		||||
        this.selected.push(artifact.id);
 | 
			
		||||
      } else {
 | 
			
		||||
        this.selected.splice(index, 1);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    selectedArtifacts() {
 | 
			
		||||
      return this.artifacts.filter(a => this.selected.indexOf(a.id) >= 0);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.artifact-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  margin-top: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
.artifact {
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  border: 1px #ccc solid;
 | 
			
		||||
  background-color: #fafafa;
 | 
			
		||||
  padding: 0.5em;
 | 
			
		||||
  margin: 0.2em;
 | 
			
		||||
  border-radius: 0.25em;
 | 
			
		||||
  min-width: 10em;
 | 
			
		||||
  max-width: 10em;
 | 
			
		||||
}
 | 
			
		||||
.artifact-description {
 | 
			
		||||
  font-size: 0.85em;
 | 
			
		||||
  margin-top: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
.selected-artifacts {
 | 
			
		||||
  color: white;
 | 
			
		||||
  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
 | 
			
		||||
    monospace;
 | 
			
		||||
  padding: 16px;
 | 
			
		||||
  text-align: left;
 | 
			
		||||
  word-spacing: normal;
 | 
			
		||||
  word-break: normal;
 | 
			
		||||
  word-wrap: normal;
 | 
			
		||||
  line-height: 1.5;
 | 
			
		||||
  -moz-tab-size: 4;
 | 
			
		||||
  hyphens: none;
 | 
			
		||||
  font-size: 0.85em;
 | 
			
		||||
  margin-top: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
.selected-artifact-script {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -18,10 +18,10 @@ module.exports = {
 | 
			
		||||
                text: 'API Version',
 | 
			
		||||
                items: [
 | 
			
		||||
                    { text: 'Current (3.x.x)', link: '/' },
 | 
			
		||||
                    { text: 'Beta (4.x.x)', link: '/docs/v4/install.md' },
 | 
			
		||||
                    { text: 'Legacy (2.x.x)', link: '/docs/v2/' }
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            { text: 'Sandbox', link: '/sandbox.md' },
 | 
			
		||||
            { text: 'Github', link: 'https://github.com/noties/Markwon' }
 | 
			
		||||
        ],
 | 
			
		||||
        sidebar: {
 | 
			
		||||
@ -36,6 +36,27 @@ module.exports = {
 | 
			
		||||
                '/docs/v2/html.md',
 | 
			
		||||
                '/docs/v2/view.md'
 | 
			
		||||
            ],
 | 
			
		||||
            '/docs/v4': [
 | 
			
		||||
                '/docs/v4/install.md',
 | 
			
		||||
                {
 | 
			
		||||
                    title: 'Core',
 | 
			
		||||
                    collapsable: false,
 | 
			
		||||
                    children: [
 | 
			
		||||
                        '/docs/v4/core/getting-started.md',
 | 
			
		||||
                        '/docs/v4/core/plugins.md',
 | 
			
		||||
                        '/docs/v4/core/registry.md',
 | 
			
		||||
                        '/docs/v4/core/theme.md',
 | 
			
		||||
                        '/docs/v4/core/configuration.md',
 | 
			
		||||
                        '/docs/v4/core/visitor.md',
 | 
			
		||||
                        '/docs/v4/core/spans-factory.md',
 | 
			
		||||
                        '/docs/v4/core/core-plugin.md',
 | 
			
		||||
                        '/docs/v4/core/movement-method-plugin.md',
 | 
			
		||||
                        '/docs/v4/core/render-props.md'
 | 
			
		||||
                    ]
 | 
			
		||||
                },
 | 
			
		||||
                '/docs/v4/recipes.md',
 | 
			
		||||
                '/docs/v4/migration-3-4.md'
 | 
			
		||||
            ],
 | 
			
		||||
            '/': [
 | 
			
		||||
                '',
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										173
									
								
								docs/docs/v4/core/configuration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								docs/docs/v4/core/configuration.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,173 @@
 | 
			
		||||
# Configuration
 | 
			
		||||
 | 
			
		||||
`MarkwonConfiguration` class holds common Markwon functionality.
 | 
			
		||||
These are _configurable_ properties:
 | 
			
		||||
* `AsyncDrawableLoader` (back here since <Badge text="4.0.0" />)
 | 
			
		||||
* `SyntaxHighlight`
 | 
			
		||||
* `LinkSpan.Resolver`
 | 
			
		||||
* `UrlProcessor`
 | 
			
		||||
* `ImageSizeResolver`
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
Additionally `MarkwonConfiguration` holds:
 | 
			
		||||
* `MarkwonTheme`
 | 
			
		||||
* `MarkwonSpansFactory`
 | 
			
		||||
 | 
			
		||||
Please note that these values can be retrieved from `MarkwonConfiguration`
 | 
			
		||||
instance, but their _configuration_ must be done by a `Plugin` by overriding
 | 
			
		||||
one of the methods:
 | 
			
		||||
* `Plugin#configureTheme`
 | 
			
		||||
* `Plugin#configureSpansFactory`
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## AsyncDrawableLoader
 | 
			
		||||
 | 
			
		||||
Allows loading and displaying of images in markdown. Please note that if one is not specified
 | 
			
		||||
directly (or via plugin) no images will be displayed.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
 | 
			
		||||
                builder.asyncDrawableLoader(AsyncDrawableLoader.noOp());
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Currently `Markwon` provides 3 implementations for loading images:
 | 
			
		||||
* [own implementation](/docs/v4/image.md) with SVG, GIF, data uri and android_assets support
 | 
			
		||||
* [based on Picasso](/docs/v4/image-picasso.md)
 | 
			
		||||
* [based on Glide](/docs/v4/image-glide.md)
 | 
			
		||||
 | 
			
		||||
## SyntaxHighlight
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(this)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
 | 
			
		||||
                builder.syntaxHighlight(new SyntaxHighlightNoOp());
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
Use [syntax-highlight](/docs/v4/syntax-highlight/) to add syntax highlighting
 | 
			
		||||
to your application
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## LinkSpan.Resolver
 | 
			
		||||
 | 
			
		||||
React to a link click event. By default `LinkResolverDef` is used,
 | 
			
		||||
which tries to start an Activity given the `link` argument. If no
 | 
			
		||||
Activity can handle `link` `LinkResolverDef` silently ignores click event
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(this)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
 | 
			
		||||
                builder.linkResolver(new LinkSpan.Resolver() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void resolve(View view, @NonNull String link) {
 | 
			
		||||
                        // react to link click here
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
Please note that `Markwon` will apply `LinkMovementMethod` to a resulting TextView
 | 
			
		||||
if there is none registered. if you wish to register own instance of a `MovementMethod`
 | 
			
		||||
apply it directly to a TextView or use [MovementMethodPlugin](/docs/v4/core/movement-method-plugin.md)
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## UrlProcessor
 | 
			
		||||
 | 
			
		||||
Process URLs in your markdown (for links and images). If not provided explicitly, 
 | 
			
		||||
default **no-op** implementation will be used, which does not modify URLs (keeping them as-is).
 | 
			
		||||
 | 
			
		||||
`Markwon` provides 2 implementations of `UrlProcessor`:
 | 
			
		||||
* `UrlProcessorRelativeToAbsolute`
 | 
			
		||||
* `UrlProcessorAndroidAssets`
 | 
			
		||||
 | 
			
		||||
### UrlProcessorRelativeToAbsolute
 | 
			
		||||
 | 
			
		||||
`UrlProcessorRelativeToAbsolute` can be used to make relative URL absolute. For example if an image is
 | 
			
		||||
defined like this: `` and `UrlProcessorRelativeToAbsolute`
 | 
			
		||||
is created with `https://github.com/noties/Markwon/raw/master/` as the base: 
 | 
			
		||||
`new UrlProcessorRelativeToAbsolute("https://github.com/noties/Markwon/raw/master/")`,
 | 
			
		||||
then final image will have `https://github.com/noties/Markwon/raw/master/art/image.JPG`
 | 
			
		||||
as the destination.
 | 
			
		||||
 | 
			
		||||
### UrlProcessorAndroidAssets
 | 
			
		||||
 | 
			
		||||
`UrlProcessorAndroidAssets` can be used to make processed links to point to Android assets folder.
 | 
			
		||||
So an image: `` will have `file:///android_asset/art/image.JPG` as the
 | 
			
		||||
destination.
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
Please note that `UrlProcessorAndroidAssets` will process only URLs that have no `scheme` information,
 | 
			
		||||
so a `./art/image.png` will become `file:///android_asset/art/image.JPG` whilst `https://so.me/where.png`
 | 
			
		||||
will be kept as-is.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## ImageSizeResolver
 | 
			
		||||
 | 
			
		||||
`ImageSizeResolver` controls the size of an image to be displayed. Currently it
 | 
			
		||||
handles only HTML images (specified via `img` tag).
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(this)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
 | 
			
		||||
                builder.imageSizeResolver(new ImageSizeResolver() {
 | 
			
		||||
                    @NonNull
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public Rect resolveImageSize(
 | 
			
		||||
                            @Nullable ImageSize imageSize,
 | 
			
		||||
                            @NonNull Rect imageBounds,
 | 
			
		||||
                            int canvasWidth,
 | 
			
		||||
                            float textSize) {
 | 
			
		||||
                        return null;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If not provided explicitly, default `ImageSizeResolverDef` implementation will be used.
 | 
			
		||||
It handles 3 dimension units:
 | 
			
		||||
* `%` (percent, relative to Canvas width)
 | 
			
		||||
* `em` (relative to text size)
 | 
			
		||||
* `px` (absolute size, every dimension that is not `%` or `em` is considered to be _absolute_)
 | 
			
		||||
 | 
			
		||||
```html
 | 
			
		||||
<img width="100%">
 | 
			
		||||
<img width="2em" height="10px">
 | 
			
		||||
<img style="{width: 100%; height: 8em;}">
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`ImageSizeResolverDef` keeps the ratio of original image if one of the dimensions is missing.
 | 
			
		||||
 | 
			
		||||
:::warning Height%
 | 
			
		||||
There is no support for `%` units for `height` dimension. This is due to the fact that
 | 
			
		||||
height of an TextView in which markdown is displayed is non-stable and changes with time
 | 
			
		||||
(for example when image is loaded and applied to a TextView it will _increase_ TextView's height),
 | 
			
		||||
so we will have no point-of-reference from which to _calculate_ image height.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
`ImageSizeResolverDef` also takes care for an image to **not** exceed
 | 
			
		||||
canvas width. If an image has greater width than a TextView Canvas, then
 | 
			
		||||
image will be _scaled-down_ to fit the canvas. Please note that this rule
 | 
			
		||||
applies only if image has no absolute sizes (for example width is specified
 | 
			
		||||
in pixels).
 | 
			
		||||
:::
 | 
			
		||||
							
								
								
									
										141
									
								
								docs/docs/v4/core/core-plugin.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								docs/docs/v4/core/core-plugin.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,141 @@
 | 
			
		||||
# Core plugin <Badge text="3.0.0" />
 | 
			
		||||
 | 
			
		||||
Since <Badge text="3.0.0" /> with introduction of _plugins_, Markwon
 | 
			
		||||
**core** functionality was moved to a dedicated plugin.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
CorePlugin.create();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Node visitors
 | 
			
		||||
 | 
			
		||||
`CorePlugin` registers these `commonmark-java` node visitors:
 | 
			
		||||
* `Text`
 | 
			
		||||
* `StrongEmphasis`
 | 
			
		||||
* `Emphasis`
 | 
			
		||||
* `BlockQuote`
 | 
			
		||||
* `Code`
 | 
			
		||||
* `Image`
 | 
			
		||||
* `FencedCodeBlock`
 | 
			
		||||
* `IndentedCodeBlock`
 | 
			
		||||
* `BulletList`
 | 
			
		||||
* `OrderedList`
 | 
			
		||||
* `ListItem`
 | 
			
		||||
* `ThematicBreak`
 | 
			
		||||
* `Heading`
 | 
			
		||||
* `SoftLineBreak`
 | 
			
		||||
* `HardLineBreak`
 | 
			
		||||
* `Paragraph`
 | 
			
		||||
* `Link`
 | 
			
		||||
 | 
			
		||||
## Span factories
 | 
			
		||||
 | 
			
		||||
`CorePlugin` adds these `SpanFactory`s:
 | 
			
		||||
* `StrongEmphasis`
 | 
			
		||||
* `Emphasis`
 | 
			
		||||
* `BlockQuote`
 | 
			
		||||
* `Code`
 | 
			
		||||
* `FencedCodeBlock`
 | 
			
		||||
* `IndentedCodeBlock`
 | 
			
		||||
* `ListItem`
 | 
			
		||||
* `Heading`
 | 
			
		||||
* `Link`
 | 
			
		||||
* `ThematicBreak`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
By default `CorePlugin` does not register a `Paragraph` `SpanFactory` but
 | 
			
		||||
this can be done in your custom plugin:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
 | 
			
		||||
                builder.setFactory(Paragraph.class, (configuration, props) -> 
 | 
			
		||||
                        new ForegroundColorSpan(Color.RED));
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
```
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Props
 | 
			
		||||
These props are exported by `CorePlugin` and can be found in `CoreProps`:
 | 
			
		||||
* `Prop<ListItemType> LIST_ITEM_TYPE` (BULLET | ORDERED)
 | 
			
		||||
* `Prop<Integer> BULLET_LIST_ITEM_LEVEL`
 | 
			
		||||
* `Prop<Integer> ORDERED_LIST_ITEM_NUMBER`
 | 
			
		||||
* `Prop<Integer> HEADING_LEVEL`
 | 
			
		||||
* `Prop<String> LINK_DESTINATION`
 | 
			
		||||
* `Prop<Boolean> PARAGRAPH_IS_IN_TIGHT_LIST`
 | 
			
		||||
 | 
			
		||||
:::warning List item type
 | 
			
		||||
Before <Badge text="3.0.0" /> `Markwon` had 2 distinct lists (bullet and ordered). 
 | 
			
		||||
Since <Badge text="3.0.0" /> a single `SpanFactory` is used, which internally checks 
 | 
			
		||||
for `Prop<ListItemType> LIST_ITEM_TYPE`.
 | 
			
		||||
Beware of this if you would like to override only one of the list types. This is
 | 
			
		||||
done to correspond to `commonmark-java` implementation.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
More information about props can be found [here](/docs/v4/core/render-props.md)
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
:::tip Soft line break
 | 
			
		||||
Since <Badge text="3.0.0" /> Markwon core does not give an option to
 | 
			
		||||
insert a new line when there is a soft line break in markdown. Instead a
 | 
			
		||||
custom plugin can be used:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(this)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
 | 
			
		||||
                builder.on(SoftLineBreak.class, (visitor, softLineBreak) ->
 | 
			
		||||
                        visitor.forceNewLine());
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::warning
 | 
			
		||||
Please note that `CorePlugin` will implicitly set a `LinkMovementMethod` on a TextView
 | 
			
		||||
if one is not present. If you wish to customize a MovementMethod that is used, apply
 | 
			
		||||
one manually to a TextView (before applying markdown) or use the [MovementMethodPlugin](/docs/v4/core/movement-method-plugin.md)
 | 
			
		||||
which accepts a MovementMethod as an argument.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## OnTextAddedListener <Badge text="4.0.0"/>
 | 
			
		||||
 | 
			
		||||
Since `4.0.0` `CorePlugin` provides ability to receive text-added event. This can
 | 
			
		||||
be useful in order to process raw text (for example to [linkify](/docs/v4/linkify.md) it):
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configure(@NonNull Registry registry) {
 | 
			
		||||
                registry.require(CorePlugin.class, new Action<CorePlugin>() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void apply(@NonNull CorePlugin corePlugin) {
 | 
			
		||||
                        corePlugin.addOnTextAddedListener(new CorePlugin.OnTextAddedListener() {
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void onTextAdded(@NonNull MarkwonVisitor visitor, @NonNull String text, int start) {
 | 
			
		||||
                                
 | 
			
		||||
                                // NB text is already added and you are __strongly__ adviced not to
 | 
			
		||||
                                // modify visitor here, but only add spans
 | 
			
		||||
                                //
 | 
			
		||||
                                // this will make all text BLUE
 | 
			
		||||
                                visitor.builder().setSpan(
 | 
			
		||||
                                        new ForegroundColorSpan(Color.BLUE),
 | 
			
		||||
                                        start,
 | 
			
		||||
                                        visitor.length()
 | 
			
		||||
                                );
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										52
									
								
								docs/docs/v4/core/getting-started.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								docs/docs/v4/core/getting-started.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
# Getting started
 | 
			
		||||
 | 
			
		||||
:::tip Installation
 | 
			
		||||
Please follow [installation](/docs/v4/install.md) instructions
 | 
			
		||||
to learn how to add `Markwon` to your project
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Quick one
 | 
			
		||||
 | 
			
		||||
This is the most simple way to set markdown to a `TextView` or any of its siblings:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
// obtain an instance of Markwon
 | 
			
		||||
final Markwon markwon = Markwon.create(context);
 | 
			
		||||
 | 
			
		||||
// set markdown
 | 
			
		||||
markwon.setMarkdown(textView, "**Hello there!**");
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The most simple way to obtain markdown to be applied _somewhere_ else:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
// obtain an instance of Markwon
 | 
			
		||||
final Markwon markwon = Markwon.create(context);
 | 
			
		||||
 | 
			
		||||
// parse markdown and create styled text
 | 
			
		||||
final Spanned markdown = markwon.toMarkdown("**Hello there!**");
 | 
			
		||||
 | 
			
		||||
// use it
 | 
			
		||||
Toast.makeText(context, markdown, Toast.LENGTH_LONG).show();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Longer one
 | 
			
		||||
 | 
			
		||||
With explicit `parse` and `render` methods:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
// obtain an instance of Markwon
 | 
			
		||||
final Markwon markwon = Markwon.create(context);
 | 
			
		||||
 | 
			
		||||
// parse markdown to commonmark-java Node
 | 
			
		||||
final Node node = markwon.parse("Are **you** still there?");
 | 
			
		||||
 | 
			
		||||
// create styled text from parsed Node
 | 
			
		||||
final Spanned markdown = markwon.render(node);
 | 
			
		||||
 | 
			
		||||
// use it on a TextView
 | 
			
		||||
markwon.setParsedMarkdown(textView, markdown);
 | 
			
		||||
 | 
			
		||||
// or a Toast
 | 
			
		||||
Toast.makeText(context, markdown, Toast.LENGTH_LONG).show();
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										17
									
								
								docs/docs/v4/core/movement-method-plugin.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								docs/docs/v4/core/movement-method-plugin.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
# Movement method plugin
 | 
			
		||||
 | 
			
		||||
`MovementMethodPlugin` can be used to apply a `MovementMethod` to a TextView
 | 
			
		||||
(important if you have links inside your markdown). By default `CorePlugin`
 | 
			
		||||
will set a `LinkMovementMethod` on a TextView if one is missing. If you have
 | 
			
		||||
specific needs for a `MovementMethod` and `LinkMovementMethod` doesn't answer
 | 
			
		||||
your needs use `MovementMethodPlugin`:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
Markwon.builder(context)
 | 
			
		||||
        .usePlugin(MovementMethodPlugin.create(ScrollingMovementMethod.getInstance()))
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
If you are having trouble with system `LinkMovementMethod` as an alternative
 | 
			
		||||
[BetterLinkMovementMethod](https://github.com/saket/Better-Link-Movement-Method) library can be used.
 | 
			
		||||
:::
 | 
			
		||||
							
								
								
									
										361
									
								
								docs/docs/v4/core/plugins.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								docs/docs/v4/core/plugins.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,361 @@
 | 
			
		||||
# Plugins <Badge text="3.0.0" />
 | 
			
		||||
 | 
			
		||||
Since <Badge text="3.0.0" /> `MarkwonPlugin` takes the key role in
 | 
			
		||||
processing and rendering markdown. Even **core** functionaly is abstracted
 | 
			
		||||
into a `CorePlugin`. So it's still possible to use `Markwon` with a completely
 | 
			
		||||
own set of plugins.
 | 
			
		||||
 | 
			
		||||
To register a plugin `Markwon.Builder` must be used:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
Markwon.builder(context)
 | 
			
		||||
    // @since 4.0.0 there is no need to register CorePlugin, as it's registered automatically
 | 
			
		||||
//    .usePlugin(CorePlugin.create())
 | 
			
		||||
    .usePlugin(MyPlugin.create())
 | 
			
		||||
    .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
All the process of transforming _raw_ markdown into a styled text (Spanned)
 | 
			
		||||
will go through plugins. A plugin can:
 | 
			
		||||
 | 
			
		||||
* [configure plugin registry](#registry)
 | 
			
		||||
* [configure commonmark-java `Parser`](#parser)
 | 
			
		||||
* [configure `MarkwonTheme`](#markwontheme)
 | 
			
		||||
* [configure `AsyncDrawableLoader` (used to display images in markdown)](#images)
 | 
			
		||||
* [configure `MarkwonConfiguration`](#configuration)
 | 
			
		||||
* [configure `MarkwonVisitor` (extensible commonmark-java Node visitor)](#visitor)
 | 
			
		||||
* [configure `MarkwonSpansFactory` (factory to hold spans information for each Node)](#spans-factory)
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
* [process raw input markdown before parsing it](#process-markdown)
 | 
			
		||||
* [inspect/modify commonmark-java Node after it's been parsed, but before rendering](#inspect-modify-node)
 | 
			
		||||
* [inspect commonmark-java Node after it's been rendered](#inspect-node-after-render)
 | 
			
		||||
* [prepare TextView to display markdown _before_ markdown is applied to a TextView](#prepare-textview)
 | 
			
		||||
* [post-process TextView _after_ markdown was applied](#textview-after-markdown-applied)
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
if you need to override only few methods of `MarkwonPlugin` (since it is an interface),
 | 
			
		||||
`AbstractMarkwonPlugin` can be used.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Registry <Badge text="4.0.0" />
 | 
			
		||||
 | 
			
		||||
Registry is a special step to pre-configure all registered plugins. It is also
 | 
			
		||||
used to determine the order of plugins inside `Markwon` instance.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configure(@NonNull Registry registry) {
 | 
			
		||||
 | 
			
		||||
                final CorePlugin corePlugin = registry.require(CorePlugin.class);
 | 
			
		||||
 | 
			
		||||
                // or
 | 
			
		||||
                registry.require(CorePlugin.class, new Action<CorePlugin>() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void apply(@NonNull CorePlugin corePlugin) {
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
More information about registry can be found [here](/docs/v4/core/registry.md)
 | 
			
		||||
 | 
			
		||||
## Parser
 | 
			
		||||
 | 
			
		||||
For example, let's register a new commonmark-java Parser extension:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureParser(@NonNull Parser.Builder builder) {
 | 
			
		||||
                // no need to call `super.configureParser(builder)`
 | 
			
		||||
                builder.extensions(Collections.singleton(StrikethroughExtension.create()));
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
There are no limitations on what to do with commonmark-java Parser. For more info
 | 
			
		||||
_what_ can be done please refer to <Link name="commonmark-java" displayName="commonmark-java documentation" />.
 | 
			
		||||
 | 
			
		||||
## MarkwonTheme
 | 
			
		||||
 | 
			
		||||
Starting <Badge text="3.0.0" /> `MarkwonTheme` represents _core_ theme. Aka theme for
 | 
			
		||||
things core module knows of. For example it doesn't know anything about `strikethrough`
 | 
			
		||||
or `tables` (as they belong to different modules).
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureTheme(@NonNull MarkwonTheme.Builder builder) {
 | 
			
		||||
                builder
 | 
			
		||||
                        .codeTextColor(Color.BLACK)
 | 
			
		||||
                        .codeBackgroundColor(Color.GREEN);
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
`CorePlugin` has special handling - it will be added automatically 
 | 
			
		||||
when `Markwon.builder(Context)` method is used. If you wish to create 
 | 
			
		||||
Markwon instance _without_ CorePlugin registered - 
 | 
			
		||||
use `Markwon.builderNoCore(Context)` method instead
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
More information about `MarkwonTheme` can be found [here](/docs/v4/core/theme.md).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Configuration
 | 
			
		||||
 | 
			
		||||
`MarkwonConfiguration` is a set of common tools that are used by different parts
 | 
			
		||||
of `Markwon`. It allows configurations of these:
 | 
			
		||||
 | 
			
		||||
* `AsyncDrawableLoader` (image loading)
 | 
			
		||||
* `SyntaxHighlight` (highlighting code blocks)
 | 
			
		||||
* `LinkResolver` (opens links in markdown)
 | 
			
		||||
* `UrlProcessor` (process URLs in markdown for both links and images)
 | 
			
		||||
* `ImageSizeResolver` (resolve image sizes, like `fit-to-canvas`, etc)
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
 | 
			
		||||
                builder.linkResolver(new LinkResolverDef());
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
More information about `MarkwonConfiguration` can be found [here](/docs/v4/core/configuration.md)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Visitor
 | 
			
		||||
 | 
			
		||||
`MarkwonVisitor` <Badge text="3.0.0" /> is commonmark-java Visitor that allows
 | 
			
		||||
configuration of how each Node is visited. There is no longer need to create
 | 
			
		||||
own subclass of Visitor and override required methods (like in `2.x.x` versions).
 | 
			
		||||
`MarkwonVisitor` also allows registration of Nodes, that `core` module knows
 | 
			
		||||
nothing about (instead of relying on `visit(CustomNode)` method)).
 | 
			
		||||
 | 
			
		||||
For example, let's add `strikethrough` Node visitor:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
 | 
			
		||||
                // please note that strike-through parser extension must be registered
 | 
			
		||||
                // in order to receive such callback
 | 
			
		||||
                builder
 | 
			
		||||
                        .on(Strikethrough.class, new MarkwonVisitor.NodeVisitor<Strikethrough>() {
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void visit(@NonNull MarkwonVisitor visitor, @NonNull Strikethrough strikethrough) {
 | 
			
		||||
                                final int length = visitor.length();
 | 
			
		||||
                                visitor.visitChildren(strikethrough);
 | 
			
		||||
                                visitor.setSpansForNodeOptional(strikethrough, length);
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
`MarkwonVisitor` also allows _overriding_ already registered nodes. For example,
 | 
			
		||||
you can disable `Heading` Node rendering:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
builder.on(Heading.class, null);
 | 
			
		||||
```
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
More information about `MarkwonVisitor` can be found [here](/docs/v4/core/visitor.md)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Spans Factory
 | 
			
		||||
 | 
			
		||||
`MarkwonSpansFactory` <Badge text="3.0.0" /> is an abstract factory (factory that produces other factories)
 | 
			
		||||
for spans that `Markwon` uses. It controls what spans to use for certain Nodes.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
 | 
			
		||||
                // override emphasis factory to make all emphasis nodes underlined
 | 
			
		||||
                builder.setFactory(Emphasis.class, new SpanFactory() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
 | 
			
		||||
                        return new UnderlineSpan();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
`SpanFactory` allows to return an _array_ of spans to apply multiple spans
 | 
			
		||||
for a Node:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
@Override
 | 
			
		||||
public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
 | 
			
		||||
    // make underlined and set text color to red
 | 
			
		||||
    return new Object[]{
 | 
			
		||||
            new UnderlineSpan(),
 | 
			
		||||
            new ForegroundColorSpan(Color.RED)
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
More information about spans factory can be found [here](/docs/v4/core/spans-factory.md)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Process markdown
 | 
			
		||||
 | 
			
		||||
A plugin can be used to _pre-process_ input markdown (this will be called before _parsing_):
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @NonNull
 | 
			
		||||
            @Override
 | 
			
		||||
            public String processMarkdown(@NonNull String markdown) {
 | 
			
		||||
                return markdown.replaceAll("foo", "bar");
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Inspect/modify Node
 | 
			
		||||
 | 
			
		||||
A plugin can inspect/modify commonmark-java Node _before_ it's being rendered.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void beforeRender(@NonNull Node node) {
 | 
			
		||||
 | 
			
		||||
                // for example inspect it with custom visitor
 | 
			
		||||
                node.accept(new MyVisitor());
 | 
			
		||||
 | 
			
		||||
                // or modify (you know what you are doing, right?)
 | 
			
		||||
                node.appendChild(new Text("Appended"));
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Inspect Node after render
 | 
			
		||||
 | 
			
		||||
A plugin can inspect commonmark-java Node after it's been rendered.
 | 
			
		||||
Modifying Node at this point makes not much sense (it's already been
 | 
			
		||||
rendered and all modifications won't change anything). But this method can be used,
 | 
			
		||||
for example, to clean-up some internal state (after rendering). Generally
 | 
			
		||||
speaking, a plugin must be stateless, but if it cannot, then this method is
 | 
			
		||||
the best place to clean-up.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void afterRender(@NonNull Node node, @NonNull MarkwonVisitor visitor) {
 | 
			
		||||
                cleanUp();
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Prepare TextView
 | 
			
		||||
 | 
			
		||||
A plugin can _prepare_ a TextView before markdown is applied. For example `images`
 | 
			
		||||
unschedules all previously scheduled `AsyncDrawableSpans` (if any) here. This way
 | 
			
		||||
when new markdown (and set of Spannables) arrives, previous set won't be kept in
 | 
			
		||||
memory and could be garbage-collected.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
 | 
			
		||||
                // clean-up previous
 | 
			
		||||
                AsyncDrawableScheduler.unschedule(textView);
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## TextView after markdown applied
 | 
			
		||||
 | 
			
		||||
A plugin will receive a callback _after_ markdown is applied to a TextView.
 | 
			
		||||
For example `images` uses this callback to schedule new set of Spannables.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void afterSetText(@NonNull TextView textView) {
 | 
			
		||||
                AsyncDrawableScheduler.schedule(textView);
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
Please note that unlike `#beforeSetText`, `#afterSetText` won't receive
 | 
			
		||||
`Spanned` markdown. This happens because at this point spans must be
 | 
			
		||||
queried directly from a TextView.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## What happens underneath
 | 
			
		||||
 | 
			
		||||
Here is what happens inside `Markwon` when `setMarkdown` method is called:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.create(context);
 | 
			
		||||
 | 
			
		||||
// warning: pseudo-code
 | 
			
		||||
 | 
			
		||||
// 0. each plugin will be called to _pre-process_ raw input markdown
 | 
			
		||||
rawInput = plugins.reduce(rawInput, (input, plugin) -> plugin.processMarkdown(input));
 | 
			
		||||
 | 
			
		||||
// 1. after input is processed it's being parsed to a Node
 | 
			
		||||
node = parser.parse(rawInput);
 | 
			
		||||
 | 
			
		||||
// 2. each plugin will be able to inspect or manipulate resulting Node
 | 
			
		||||
//  before rendering
 | 
			
		||||
plugins.forEach(plugin -> plugin.beforeRender(node));
 | 
			
		||||
 | 
			
		||||
// 3. node is being visited by a visitor
 | 
			
		||||
node.accept(visitor);
 | 
			
		||||
 | 
			
		||||
// 4. each plugin will be called after node is being visited (aka rendered)
 | 
			
		||||
plugins.forEach(plugin -> plugin.afterRender(node, visitor));
 | 
			
		||||
 | 
			
		||||
// 5. styled markdown ready at this point
 | 
			
		||||
final Spanned markdown = visitor.markdown();
 | 
			
		||||
 | 
			
		||||
// NB, points 6-8 are applied **only** if markdown is set to a TextView
 | 
			
		||||
 | 
			
		||||
// 6. each plugin will be called before styled markdown is applied to a TextView
 | 
			
		||||
plugins.forEach(plugin -> plugin.beforeSetText(textView, markdown));
 | 
			
		||||
 | 
			
		||||
// 7. markdown is applied to a TextView
 | 
			
		||||
textView.setText(markdown);
 | 
			
		||||
 | 
			
		||||
// 8. each plugin will be called after markdown is applied to a TextView
 | 
			
		||||
plugins.forEach(plugin -> plugin.afterSetText(textView));
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										97
									
								
								docs/docs/v4/core/registry.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								docs/docs/v4/core/registry.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
			
		||||
# Registry <Badge text="4.0.0" />
 | 
			
		||||
 | 
			
		||||
`Registry` allows to pre-configure other plugins and/or declare a dependency on a plugin,
 | 
			
		||||
which also will modify internal order of plugins inside a `Markwon` instance.
 | 
			
		||||
 | 
			
		||||
For example, you have a configurable plugin:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public class MyPlugin extends AbstractMarkwonPlugin {
 | 
			
		||||
    
 | 
			
		||||
    private boolean enabled;
 | 
			
		||||
 | 
			
		||||
    public boolean enabled() {
 | 
			
		||||
        return enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public MyPlugin enabled(boolean enabled) {
 | 
			
		||||
        this.enabled = enabled;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    {...}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
and other plugin that needs to access `MyPlugin` or modify/configure it:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public class MyOtherPlugin extends AbstractMarkwonPlugin {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void configure(@NonNull Registry registry) {
 | 
			
		||||
        registry.require(MyPlugin.class, new Action<MyPlugin>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void apply(@NonNull MyPlugin myPlugin) {
 | 
			
		||||
                myPlugin.enabled(false);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new MyOtherPlugin())
 | 
			
		||||
        .usePlugin(new MyPlugin())
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
_Internal_ plugins order (in this case) will be:
 | 
			
		||||
* `CorePlugin` (added automatically and always the first one)
 | 
			
		||||
* `MyPlugin` (was required by `MyOtherPlugin`)
 | 
			
		||||
* `MyOtherPlugin`
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
There is no need to _require_ `CorePlugin` as it will be the first one inside
 | 
			
		||||
`Markwon` instance.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
The order matters if you want to _override_ some plugin. For example, `CoolPlugin`
 | 
			
		||||
adds a `SpanFactory` for a `Cool` markdown node. Other `NotCoolPlugin` wants to
 | 
			
		||||
use a different `SpanFactory`, then:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(CoolPlugin.create())
 | 
			
		||||
        .usePlugin(new NotCoolPlugin() {
 | 
			
		||||
            
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configure(@NonNull MarkwonPlugin.Registry registry) {
 | 
			
		||||
                registry.require(CoolPlugin.class);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
 | 
			
		||||
                builder.setFactory(Cool.class, new NotCoolSpanFactory());
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
All `require` calls to the `Registry` will also validate at runtime that
 | 
			
		||||
_required_ plugins are registered.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configure(@NonNull Registry registry) {
 | 
			
		||||
                // will throw an exception if `NotPresentPlugin` is not present
 | 
			
		||||
                registry.require(NotPresentPlugin.class);
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										75
									
								
								docs/docs/v4/core/render-props.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								docs/docs/v4/core/render-props.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,75 @@
 | 
			
		||||
# RenderProps <Badge text="3.0.0" />
 | 
			
		||||
 | 
			
		||||
`RenderProps` encapsulates passing arguments from a node visitor to a node renderer.
 | 
			
		||||
Without hardcoding arguments into an API method calls.
 | 
			
		||||
 | 
			
		||||
`RenderProps` is the state collection for `Props` that are set by a node visitor and
 | 
			
		||||
retrieved by a node renderer.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public class Prop<T> {
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static <T> Prop<T> of(@NonNull String name) {
 | 
			
		||||
        return new Prop<>(name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* ... */
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For example `CorePlugin` defines a _Heading level_ prop (inside `CoreProps` class):
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public static final Prop<Integer> HEADING_LEVEL = Prop.of("heading-level");
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then CorePlugin registers a `Heading` node visitor and applies heading value:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
@Override
 | 
			
		||||
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
 | 
			
		||||
    builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) {
 | 
			
		||||
            
 | 
			
		||||
            /* Heading node handling logic */
 | 
			
		||||
 | 
			
		||||
            // set heading level
 | 
			
		||||
            CoreProps.HEADING_LEVEL.set(visitor.renderProps(), heading.getLevel());
 | 
			
		||||
            
 | 
			
		||||
            // a helper method to apply span(s) for a node 
 | 
			
		||||
            // (internally obtains a SpanFactory for Heading or silently ignores
 | 
			
		||||
            // this call if no factory for a Heading is registered)
 | 
			
		||||
            visitor.setSpansForNodeOptional(heading, start);
 | 
			
		||||
 | 
			
		||||
            /* Heading node handling logic */
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
And finally `HeadingSpanFactory` (which is also registered by `CorePlugin`):
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
public class HeadingSpanFactory implements SpanFactory {
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
 | 
			
		||||
        return new HeadingSpan(
 | 
			
		||||
                configuration.theme(),
 | 
			
		||||
                CoreProps.HEADING_LEVEL.require(props)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
`Prop<T>` has these methods:
 | 
			
		||||
 | 
			
		||||
* `@Nullable T get(RenderProps)` - returns value stored in RenderProps or `null` if none is present
 | 
			
		||||
* `@NonNull T get(RenderProps, @NonNull T defValue)` - returns value stored in RenderProps or default value (this method always return non-null value)
 | 
			
		||||
* `@NonNull T require(RenderProps)` - returns value stored in RenderProps or _throws an exception_ if none is present
 | 
			
		||||
* `void set(RenderProps, @Nullable T value)` - updates value stored in RenderProps, passing `null` as value is the same as calling `clear`
 | 
			
		||||
* `void clear(RenderProps)` - clears value stored in RenderProps
 | 
			
		||||
							
								
								
									
										103
									
								
								docs/docs/v4/core/spans-factory.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								docs/docs/v4/core/spans-factory.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,103 @@
 | 
			
		||||
# Spans Factory
 | 
			
		||||
 | 
			
		||||
Starting with <Badge text="3.0.0" /> `MarkwonSpansFactory` controls what spans are displayed
 | 
			
		||||
for markdown nodes.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
 | 
			
		||||
                // passing null as second argument will remove previously added 
 | 
			
		||||
                // factory for the Link node
 | 
			
		||||
                builder.setFactory(Link.class, null);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## SpanFactory
 | 
			
		||||
 | 
			
		||||
In order to create a _generic_ interface for all possible Nodes, a `SpanFactory`
 | 
			
		||||
was added:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
builder.setFactory(Link.class, new SpanFactory() {
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
All possible arguments are passed via [RenderProps](/docs/v4/core/render-props.md):
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
builder.setFactory(Link.class, new SpanFactory() {
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
 | 
			
		||||
        final String href = CoreProps.LINK_DESTINATION.require(props);
 | 
			
		||||
        return new LinkSpan(configuration.theme(), href, configuration.linkResolver());
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`SpanFactory` allows returning `null` for a certain span (no span will be applied).
 | 
			
		||||
Or an array of spans (you _can_ go deeper):
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
builder.setFactory(Link.class, new SpanFactory() {
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
 | 
			
		||||
        return new Object[]{
 | 
			
		||||
                new LinkSpan(
 | 
			
		||||
                        configuration.theme(),
 | 
			
		||||
                        CoreProps.LINK_DESTINATION.require(props),
 | 
			
		||||
                        configuration.linkResolver()),
 | 
			
		||||
                new ForegroundColorSpan(Color.RED)
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Since <Badge text="3.0.1" /> you can _add_ multiple `SpanFactory` for a single node:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
 | 
			
		||||
                // this factory will be used _along_ with all other factories for specified node
 | 
			
		||||
                builder.addFactory(Code.class, new SpanFactory() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
 | 
			
		||||
                        return new ForegroundColorSpan(Color.GREEN);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
If you wish to inspect existing factory you can use:
 | 
			
		||||
* `builder#getFactory()` -> returns registered factory or `null`
 | 
			
		||||
* `builder#requireFactory()` -> returns registered factory or throws <Badge text="3.0.1" />
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
 | 
			
		||||
                final SpanFactory codeFactory = builder.requireFactory(Code.class);
 | 
			
		||||
                final SpanFactory linkFactory = builder.getFactory(Link.class);
 | 
			
		||||
                if (linkFactory != null) {
 | 
			
		||||
                    {...}
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										187
									
								
								docs/docs/v4/core/theme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								docs/docs/v4/core/theme.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,187 @@
 | 
			
		||||
# Theme
 | 
			
		||||
 | 
			
		||||
Here is the list of properties that can be configured via `MarkwonTheme.Builder` class. 
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
Starting with <Badge text="3.0.0" /> there is no need to manually construct a `MarkwonTheme`.
 | 
			
		||||
Instead a `Plugin` should be used:
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(context)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureTheme(@NonNull MarkwonTheme.Builder builder) {
 | 
			
		||||
                builder
 | 
			
		||||
                        .codeTextColor(Color.BLACK)
 | 
			
		||||
                        .codeBackgroundColor(Color.GREEN);
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .build();
 | 
			
		||||
```
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Link color
 | 
			
		||||
 | 
			
		||||
Controls the color of a [link](#)
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="linkColor" type="@ColorInt int" defaults="Default link color of a context where markdown is displayed <sup>*</sup>" />
 | 
			
		||||
 | 
			
		||||
<sup>*</sup> `TextPaint#linkColor` will be used to determine linkColor of a context
 | 
			
		||||
 | 
			
		||||
## Block margin
 | 
			
		||||
 | 
			
		||||
Starting margin before text content for the:
 | 
			
		||||
* lists
 | 
			
		||||
* blockquotes
 | 
			
		||||
* task lists
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="blockMargin" type="@Px int" defaults="24dp" />
 | 
			
		||||
 | 
			
		||||
## Block quote
 | 
			
		||||
 | 
			
		||||
Customizations for the `blockquote` stripe
 | 
			
		||||
 | 
			
		||||
> Quote
 | 
			
		||||
 | 
			
		||||
### Stripe width
 | 
			
		||||
 | 
			
		||||
Width of a blockquote stripe
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="blockQuoteWidth" type="@Px int" defaults="1/4 of the <a href='#block-margin'>block margin</a>" />
 | 
			
		||||
 | 
			
		||||
### Stripe color
 | 
			
		||||
 | 
			
		||||
Color of a blockquote stripe
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="blockQuoteColor" type="@ColorInt int" defaults="textColor with <code>25</code> (0-255) alpha value" />
 | 
			
		||||
 | 
			
		||||
## List
 | 
			
		||||
 | 
			
		||||
### List item color
 | 
			
		||||
 | 
			
		||||
Controls the color of a list item. For ordered list: leading number,
 | 
			
		||||
for unordered list: bullet.
 | 
			
		||||
 | 
			
		||||
* UL
 | 
			
		||||
1. OL
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="listItemColor" type="@ColorInt int" defaults="Text color" />
 | 
			
		||||
 | 
			
		||||
### Bullet item stroke width
 | 
			
		||||
 | 
			
		||||
Border width of a bullet list item (level 2)
 | 
			
		||||
 | 
			
		||||
* First
 | 
			
		||||
* * Second
 | 
			
		||||
* * * Third
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="bulletListItemStrokeWidth" type="@Px int" defaults="Stroke width of TextPaint" />
 | 
			
		||||
 | 
			
		||||
### Bullet width
 | 
			
		||||
 | 
			
		||||
The width of the bullet item
 | 
			
		||||
 | 
			
		||||
* First
 | 
			
		||||
  * Second
 | 
			
		||||
    * Third
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="bulletWidth" type="@Px int" defaults="min(<a href='#block-margin'>blockMargin</a>, lineHeight) / 2" />
 | 
			
		||||
 | 
			
		||||
## Code
 | 
			
		||||
 | 
			
		||||
### Inline code text color
 | 
			
		||||
 | 
			
		||||
The color of the `code` content
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="codeTextColor" type="@ColorInt int" defaults="Content text color" />
 | 
			
		||||
 | 
			
		||||
### Inline code background color
 | 
			
		||||
 | 
			
		||||
The color of `background` of a code content
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="codeBackgroundColor" type="@ColorInt int" defaults="<a href='#inline-code-text-color'>inline code text color</a> with 25 (0-255) alpha" />
 | 
			
		||||
 | 
			
		||||
### Block code text color
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
The color of code block text
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="codeBlockTextColor" type="@ColorInt int" defaults="<a href='#inline-code-text-color'>inline code text color</a>" />
 | 
			
		||||
 | 
			
		||||
### Block code background color
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
The color of background of code block text
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="codeBlockBackgroundColor" type="@ColorInt int" defaults="<a href='#inline-code-background-color'>inline code background color</a>" />
 | 
			
		||||
 | 
			
		||||
### Block code leading margin
 | 
			
		||||
 | 
			
		||||
Leading margin for the block code content
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="codeMultilineMargin" type="@Px int" defaults="8dip" />
 | 
			
		||||
 | 
			
		||||
### Code typeface
 | 
			
		||||
 | 
			
		||||
Typeface of code content
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="codeTypeface" type="android.graphics.Typeface" defaults="Typeface.MONOSPACE" />
 | 
			
		||||
 | 
			
		||||
### Block code typeface <Badge text="3.0.0" />
 | 
			
		||||
 | 
			
		||||
Typeface of block code content
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="codeBlockTypeface" type="android.graphics.Typeface" defaults="<code>codeTypeface</code> if set or Typeface.MONOSPACE" />
 | 
			
		||||
 | 
			
		||||
### Code text size
 | 
			
		||||
 | 
			
		||||
Text size of code content
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="codeTextSize" type="@Px int" defaults="(Content text size) * 0.87 if no custom <a href='#code-typeface'>Typeface</a> was set, otherwise (content text size)" />
 | 
			
		||||
 | 
			
		||||
### Block code text size <Badge text="3.0.0" />
 | 
			
		||||
 | 
			
		||||
Text size of block code content
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="codeBlockTextSize" type="@Px int" defaults="<code>codeTextSize</code> if set or (content text size) * 0.87 if no custom <a href='#code-typeface'>Typeface</a> was set, otherwise (content text size)" />
 | 
			
		||||
 | 
			
		||||
## Heading
 | 
			
		||||
 | 
			
		||||
### Break height
 | 
			
		||||
 | 
			
		||||
The height of a brake under H1 & H2
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="headingBreakHeight" type="@Px int" defaults="Stroke width of context TextPaint" />
 | 
			
		||||
 | 
			
		||||
### Break color
 | 
			
		||||
 | 
			
		||||
The color of a brake under H1 & H2
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="headingBreakColor" type="@ColorInt int" defaults="(text color) with 75 (0-255) alpha" />
 | 
			
		||||
 | 
			
		||||
### Typeface <Badge text="1.1.0" />
 | 
			
		||||
 | 
			
		||||
The typeface of heading elements
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="headingTypeface" type="android.graphics.Typeface" defaults="default text Typeface" />
 | 
			
		||||
 | 
			
		||||
### Text size <Badge text="1.1.0" />
 | 
			
		||||
 | 
			
		||||
Array of heading text sizes _ratio_ that is applied to text size
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="headingTextSizeMultipliers" type="float[]" defaults="<code>{2.F, 1.5F, 1.17F, 1.F, .83F, .67F}</code> (HTML spec)" />
 | 
			
		||||
 | 
			
		||||
## Thematic break
 | 
			
		||||
 | 
			
		||||
### Color
 | 
			
		||||
 | 
			
		||||
Color of a thematic break
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="thematicBreakColor" type="@ColorInt int" defaults="(text color) with 25 (0-255) alpha" />
 | 
			
		||||
 | 
			
		||||
### Height
 | 
			
		||||
 | 
			
		||||
Height of a thematic break
 | 
			
		||||
 | 
			
		||||
<ThemeProperty name="thematicBreakHeight" type="@Px int" defaults="Stroke width of context TextPaint" />
 | 
			
		||||
							
								
								
									
										73
									
								
								docs/docs/v4/core/visitor.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								docs/docs/v4/core/visitor.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
			
		||||
# Visitor
 | 
			
		||||
 | 
			
		||||
Starting with <Badge text="3.0.0" /> _visiting_ of parsed markdown
 | 
			
		||||
nodes does not require creating own instance of commonmark-java `Visitor`,
 | 
			
		||||
instead a composable/configurable `MarkwonVisitor` is used.
 | 
			
		||||
 | 
			
		||||
## Visitor.Builder
 | 
			
		||||
There is no need to create own instance of `MarkwonVisitor.Builder` as
 | 
			
		||||
it is done by `Markwon` itself. One still can configure it as one wishes:
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
final Markwon markwon = Markwon.builder(contex)
 | 
			
		||||
        .usePlugin(new AbstractMarkwonPlugin() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
 | 
			
		||||
                builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) {
 | 
			
		||||
                        visitor.forceNewLine();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
`MarkwonVisitor` encapsulates most of the functionality of rendering parsed markdown.
 | 
			
		||||
 | 
			
		||||
It holds rendering configuration:
 | 
			
		||||
* `MarkwonVisitor#configuration` - getter for current [MarkwonConfiguration](/docs/v4/core/configuration.md)
 | 
			
		||||
* `MarkwonVisitor#renderProps` - getter for current [RenderProps](/docs/v4/core/render-props.md)
 | 
			
		||||
* `MarkwonVisitor#builder` - getter for current `SpannableBuilder`
 | 
			
		||||
 | 
			
		||||
It contains also a number of utility functions:
 | 
			
		||||
* `visitChildren(Node)` - will visit all children of supplied Node
 | 
			
		||||
* `hasNext(Node)` - utility function to check if supplied Node has a Node after it (useful for white-space management, so there should be no blank new line after last BlockNode)
 | 
			
		||||
* `ensureNewLine` - will insert a new line at current `SpannableBuilder` position only if current (last) character is not a new-line
 | 
			
		||||
* `forceNewLine` - will insert a new line character without any condition checking
 | 
			
		||||
* `length` - helper function to call `visitor.builder().length()`, returns current length of `SpannableBuilder`
 | 
			
		||||
* `clear` - will clear state for `RenderProps` and `SpannableBuilder`, this is done by `Markwon` automatically after each render call
 | 
			
		||||
 | 
			
		||||
And some utility functions to control the spans:
 | 
			
		||||
* `setSpans(int start, Object spans)` - will apply supplied `spans` on `SpannableBuilder` starting at `start` position and ending at `SpannableBuilder#length`. `spans` can be `null` (no spans will be applied) or an array of spans (each span of this array will be applied)
 | 
			
		||||
* `setSpansForNodeOptional(N node, int start)` - helper method to set spans for specified `node` (internally obtains `SpanFactory` for that node and uses it to apply spans)
 | 
			
		||||
* `setSpansForNode(N node, int start)` - almost the same as `setSpansForNodeOptional` but instead of silently ignoring call if none `SpanFactory` is registered, this method will throw an exception.
 | 
			
		||||
 | 
			
		||||
```java
 | 
			
		||||
@Override
 | 
			
		||||
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
 | 
			
		||||
    builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) {
 | 
			
		||||
 | 
			
		||||
            // or just `visitor.length()`
 | 
			
		||||
            final int start = visitor.builder().length();
 | 
			
		||||
 | 
			
		||||
            visitor.visitChildren(heading);
 | 
			
		||||
 | 
			
		||||
            // or just `visitor.setSpansForNodeOptional(heading, start)`
 | 
			
		||||
            final SpanFactory factory = visitor.configuration().spansFactory().get(heading.getClass());
 | 
			
		||||
            if (factory != null) {
 | 
			
		||||
                visitor.setSpans(start, factory.getSpans(visitor.configuration(), visitor.renderProps()));
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            if (visitor.hasNext(heading)) {
 | 
			
		||||
                visitor.ensureNewLine();
 | 
			
		||||
                visitor.forceNewLine();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										34
									
								
								docs/docs/v4/install.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								docs/docs/v4/install.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
---
 | 
			
		||||
prev: false
 | 
			
		||||
next: /docs/v4/core/getting-started.md
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Installation
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
<ArtifactPicker4 />
 | 
			
		||||
 | 
			
		||||
## Snapshot
 | 
			
		||||
 | 
			
		||||
In order to use latest `SNAPSHOT` version add snapshot repository 
 | 
			
		||||
to your root project's `build.gradle` file:
 | 
			
		||||
 | 
			
		||||
```groovy
 | 
			
		||||
allprojects {
 | 
			
		||||
    repositories {
 | 
			
		||||
        jcenter()
 | 
			
		||||
        google()
 | 
			
		||||
        // this one 👇
 | 
			
		||||
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } // 👈 this one
 | 
			
		||||
        // this one 👆
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::tip Info
 | 
			
		||||
All official artifacts share the same version number and all 
 | 
			
		||||
are uploaded to **release** and **snapshot** repositories
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								docs/docs/v4/migration-3-4.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/docs/v4/migration-3-4.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
# Migration 3.x.x -> 4.x.x
 | 
			
		||||
 | 
			
		||||
todo
 | 
			
		||||
							
								
								
									
										3
									
								
								docs/docs/v4/recipes.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/docs/v4/recipes.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
# Recipes
 | 
			
		||||
 | 
			
		||||
todo
 | 
			
		||||
@ -1,25 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.library'
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
 | 
			
		||||
    compileSdkVersion config['compile-sdk']
 | 
			
		||||
    buildToolsVersion config['build-tools']
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        minSdkVersion config['min-sdk']
 | 
			
		||||
        targetSdkVersion config['target-sdk']
 | 
			
		||||
        versionCode 1
 | 
			
		||||
        versionName version
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
 | 
			
		||||
    api project(':markwon-core')
 | 
			
		||||
 | 
			
		||||
    deps.with {
 | 
			
		||||
        api it['android-gif']
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
registerArtifact(this)
 | 
			
		||||
@ -1,4 +0,0 @@
 | 
			
		||||
POM_NAME=Image GIF
 | 
			
		||||
POM_ARTIFACT_ID=image-gif
 | 
			
		||||
POM_DESCRIPTION=Adds GIF media support to Markwon markdown
 | 
			
		||||
POM_PACKAGING=aar
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
<manifest package="ru.noties.markwon.image.gif" />
 | 
			
		||||
@ -1,81 +0,0 @@
 | 
			
		||||
package ru.noties.markwon.image.gif;
 | 
			
		||||
 | 
			
		||||
import android.graphics.drawable.Drawable;
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
import pl.droidsonroids.gif.GifDrawable;
 | 
			
		||||
import ru.noties.markwon.image.DrawableUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @since 1.1.0
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("WeakerAccess")
 | 
			
		||||
public class GifMediaDecoder extends MediaDecoder {
 | 
			
		||||
 | 
			
		||||
    public static final String CONTENT_TYPE = "image/gif";
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static GifMediaDecoder create(boolean autoPlayGif) {
 | 
			
		||||
        return new GifMediaDecoder(autoPlayGif);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final boolean autoPlayGif;
 | 
			
		||||
 | 
			
		||||
    protected GifMediaDecoder(boolean autoPlayGif) {
 | 
			
		||||
        this.autoPlayGif = autoPlayGif;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public Drawable decode(@NonNull InputStream inputStream) {
 | 
			
		||||
 | 
			
		||||
        Drawable out = null;
 | 
			
		||||
 | 
			
		||||
        final byte[] bytes = readBytes(inputStream);
 | 
			
		||||
        if (bytes != null) {
 | 
			
		||||
            try {
 | 
			
		||||
                out = newGifDrawable(bytes);
 | 
			
		||||
                DrawableUtils.applyIntrinsicBounds(out);
 | 
			
		||||
 | 
			
		||||
                if (!autoPlayGif) {
 | 
			
		||||
                    ((GifDrawable) out).pause();
 | 
			
		||||
                }
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                e.printStackTrace();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    protected Drawable newGifDrawable(@NonNull byte[] bytes) throws IOException {
 | 
			
		||||
        return new GifDrawable(bytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    protected static byte[] readBytes(@NonNull InputStream stream) {
 | 
			
		||||
 | 
			
		||||
        byte[] out = null;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 | 
			
		||||
            final int length = 1024 * 8;
 | 
			
		||||
            final byte[] buffer = new byte[length];
 | 
			
		||||
            int read;
 | 
			
		||||
            while ((read = stream.read(buffer, 0, length)) != -1) {
 | 
			
		||||
                outputStream.write(buffer, 0, read);
 | 
			
		||||
            }
 | 
			
		||||
            out = outputStream.toByteArray();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,37 +0,0 @@
 | 
			
		||||
package ru.noties.markwon.image.gif;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import ru.noties.markwon.AbstractMarkwonPlugin;
 | 
			
		||||
import ru.noties.markwon.image.AsyncDrawableLoader;
 | 
			
		||||
import ru.noties.markwon.priority.Priority;
 | 
			
		||||
 | 
			
		||||
public class GifPlugin extends AbstractMarkwonPlugin {
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static GifPlugin create() {
 | 
			
		||||
        return create(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static GifPlugin create(boolean autoPlay) {
 | 
			
		||||
        return new GifPlugin(autoPlay);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final boolean autoPlay;
 | 
			
		||||
 | 
			
		||||
    public GifPlugin(boolean autoPlay) {
 | 
			
		||||
        this.autoPlay = autoPlay;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) {
 | 
			
		||||
        builder.addMediaDecoder(GifMediaDecoder.CONTENT_TYPE, GifMediaDecoder.create(autoPlay));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public Priority priority() {
 | 
			
		||||
        return Priority.after(ImagesPlugin.class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,25 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.library'
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
 | 
			
		||||
    compileSdkVersion config['compile-sdk']
 | 
			
		||||
    buildToolsVersion config['build-tools']
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        minSdkVersion config['min-sdk']
 | 
			
		||||
        targetSdkVersion config['target-sdk']
 | 
			
		||||
        versionCode 1
 | 
			
		||||
        versionName version
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
 | 
			
		||||
    api project(':markwon-core')
 | 
			
		||||
 | 
			
		||||
    deps.with {
 | 
			
		||||
        api it['okhttp']
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
registerArtifact(this)
 | 
			
		||||
@ -1,4 +0,0 @@
 | 
			
		||||
POM_NAME=Image OkHttp
 | 
			
		||||
POM_ARTIFACT_ID=image-okhttp
 | 
			
		||||
POM_DESCRIPTION=Adds OkHttp client to retrieve images data from network
 | 
			
		||||
POM_PACKAGING=aar
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
<manifest package="ru.noties.markwon.image.okhttp" />
 | 
			
		||||
@ -1,51 +0,0 @@
 | 
			
		||||
package ru.noties.markwon.image.okhttp;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
import okhttp3.OkHttpClient;
 | 
			
		||||
import ru.noties.markwon.AbstractMarkwonPlugin;
 | 
			
		||||
import ru.noties.markwon.image.AsyncDrawableLoader;
 | 
			
		||||
import ru.noties.markwon.priority.Priority;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Plugin to use OkHttpClient to obtain images from network (http and https schemes)
 | 
			
		||||
 *
 | 
			
		||||
 * @see #create()
 | 
			
		||||
 * @see #create(OkHttpClient)
 | 
			
		||||
 * @since 3.0.0
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("WeakerAccess")
 | 
			
		||||
public class OkHttpImagesPlugin extends AbstractMarkwonPlugin {
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static OkHttpImagesPlugin create() {
 | 
			
		||||
        return new OkHttpImagesPlugin(new OkHttpClient());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static OkHttpImagesPlugin create(@NonNull OkHttpClient okHttpClient) {
 | 
			
		||||
        return new OkHttpImagesPlugin(okHttpClient);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final OkHttpClient client;
 | 
			
		||||
 | 
			
		||||
    OkHttpImagesPlugin(@NonNull OkHttpClient client) {
 | 
			
		||||
        this.client = client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) {
 | 
			
		||||
        builder.addSchemeHandler(
 | 
			
		||||
                Arrays.asList(NetworkSchemeHandler.SCHEME_HTTP, NetworkSchemeHandler.SCHEME_HTTPS),
 | 
			
		||||
                new OkHttpSchemeHandler(client)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public Priority priority() {
 | 
			
		||||
        return Priority.after(ImagesPlugin.class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,55 +0,0 @@
 | 
			
		||||
package ru.noties.markwon.image.okhttp;
 | 
			
		||||
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
import okhttp3.OkHttpClient;
 | 
			
		||||
import okhttp3.Request;
 | 
			
		||||
import okhttp3.Response;
 | 
			
		||||
import okhttp3.ResponseBody;
 | 
			
		||||
 | 
			
		||||
class OkHttpSchemeHandler extends SchemeHandler {
 | 
			
		||||
 | 
			
		||||
    private static final String HEADER_CONTENT_TYPE = "Content-Type";
 | 
			
		||||
 | 
			
		||||
    private final OkHttpClient client;
 | 
			
		||||
 | 
			
		||||
    OkHttpSchemeHandler(@NonNull OkHttpClient client) {
 | 
			
		||||
        this.client = client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
 | 
			
		||||
        ImageItem out = null;
 | 
			
		||||
 | 
			
		||||
        final Request request = new Request.Builder()
 | 
			
		||||
                .url(raw)
 | 
			
		||||
                .tag(raw)
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        Response response = null;
 | 
			
		||||
        try {
 | 
			
		||||
            response = client.newCall(request).execute();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (response != null) {
 | 
			
		||||
            final ResponseBody body = response.body();
 | 
			
		||||
            if (body != null) {
 | 
			
		||||
                final InputStream inputStream = body.byteStream();
 | 
			
		||||
                if (inputStream != null) {
 | 
			
		||||
                    final String contentType = response.header(HEADER_CONTENT_TYPE);
 | 
			
		||||
                    out = new ImageItem(contentType, inputStream);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,25 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.library'
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
 | 
			
		||||
    compileSdkVersion config['compile-sdk']
 | 
			
		||||
    buildToolsVersion config['build-tools']
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        minSdkVersion config['min-sdk']
 | 
			
		||||
        targetSdkVersion config['target-sdk']
 | 
			
		||||
        versionCode 1
 | 
			
		||||
        versionName version
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
 | 
			
		||||
    api project(':markwon-core')
 | 
			
		||||
 | 
			
		||||
    deps.with {
 | 
			
		||||
        api it['android-svg']
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
registerArtifact(this)
 | 
			
		||||
@ -1,4 +0,0 @@
 | 
			
		||||
POM_NAME=Image SVG
 | 
			
		||||
POM_ARTIFACT_ID=image-svg
 | 
			
		||||
POM_DESCRIPTION=Adds SVG media support to Markwon markdown
 | 
			
		||||
POM_PACKAGING=aar
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
<manifest package="ru.noties.markwon.image.svg" />
 | 
			
		||||
@ -1,72 +0,0 @@
 | 
			
		||||
package ru.noties.markwon.image.svg;
 | 
			
		||||
 | 
			
		||||
import android.content.res.Resources;
 | 
			
		||||
import android.graphics.Bitmap;
 | 
			
		||||
import android.graphics.Canvas;
 | 
			
		||||
import android.graphics.drawable.BitmapDrawable;
 | 
			
		||||
import android.graphics.drawable.Drawable;
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
import com.caverock.androidsvg.SVG;
 | 
			
		||||
import com.caverock.androidsvg.SVGParseException;
 | 
			
		||||
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
import ru.noties.markwon.image.DrawableUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @since 1.1.0
 | 
			
		||||
 */
 | 
			
		||||
public class SvgMediaDecoder extends MediaDecoder {
 | 
			
		||||
 | 
			
		||||
    public static final String CONTENT_TYPE = "image/svg+xml";
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static SvgMediaDecoder create(@NonNull Resources resources) {
 | 
			
		||||
        return new SvgMediaDecoder(resources);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final Resources resources;
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("WeakerAccess")
 | 
			
		||||
    SvgMediaDecoder(Resources resources) {
 | 
			
		||||
        this.resources = resources;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public Drawable decode(@NonNull InputStream inputStream) {
 | 
			
		||||
 | 
			
		||||
        final Drawable out;
 | 
			
		||||
 | 
			
		||||
        SVG svg = null;
 | 
			
		||||
        try {
 | 
			
		||||
            svg = SVG.getFromInputStream(inputStream);
 | 
			
		||||
        } catch (SVGParseException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (svg == null) {
 | 
			
		||||
            out = null;
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            final float w = svg.getDocumentWidth();
 | 
			
		||||
            final float h = svg.getDocumentHeight();
 | 
			
		||||
            final float density = resources.getDisplayMetrics().density;
 | 
			
		||||
 | 
			
		||||
            final int width = (int) (w * density + .5F);
 | 
			
		||||
            final int height = (int) (h * density + .5F);
 | 
			
		||||
 | 
			
		||||
            final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
 | 
			
		||||
            final Canvas canvas = new Canvas(bitmap);
 | 
			
		||||
            canvas.scale(density, density);
 | 
			
		||||
            svg.renderToCanvas(canvas);
 | 
			
		||||
 | 
			
		||||
            out = new BitmapDrawable(resources, bitmap);
 | 
			
		||||
            DrawableUtils.applyIntrinsicBounds(out);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,33 +0,0 @@
 | 
			
		||||
package ru.noties.markwon.image.svg;
 | 
			
		||||
 | 
			
		||||
import android.content.res.Resources;
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import ru.noties.markwon.AbstractMarkwonPlugin;
 | 
			
		||||
import ru.noties.markwon.image.AsyncDrawableLoader;
 | 
			
		||||
import ru.noties.markwon.priority.Priority;
 | 
			
		||||
 | 
			
		||||
public class SvgPlugin extends AbstractMarkwonPlugin {
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    public static SvgPlugin create(@NonNull Resources resources) {
 | 
			
		||||
        return new SvgPlugin(resources);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final Resources resources;
 | 
			
		||||
 | 
			
		||||
    public SvgPlugin(@NonNull Resources resources) {
 | 
			
		||||
        this.resources = resources;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) {
 | 
			
		||||
        builder.addMediaDecoder(SvgMediaDecoder.CONTENT_TYPE, SvgMediaDecoder.create(resources));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public Priority priority() {
 | 
			
		||||
        return Priority.after(ImagesPlugin.class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user