Working with documentation v4

This commit is contained in:
Dimitry Ivanov 2019-06-17 14:08:33 +03:00
parent 8b0edc32c3
commit 8944f39592
26 changed files with 1038 additions and 2 deletions

View File

@ -1,5 +1,9 @@
# https://docs.travis-ci.com/user/languages/android/ # https://docs.travis-ci.com/user/languages/android/
language: android language: android
# so, out of blue travis requires this now (without it build would not even execute, immediate failure when downloading jdk)
dist: trusty
jdk: openjdk8 jdk: openjdk8
sudo: false sudo: false

View File

@ -1,4 +1,4 @@
// this is a generated file, do not modify. To update it run 'collectArtifacts.js' script // this is a generated file, do not modify. To update it run 'collectArtifacts.js' script
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"}]; 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-glide","name":"Image Glide","group":"io.noties.markwon","description":"Markwon image loading module (based on Glide library)"},{"id":"image-picasso","name":"Image Picasso","group":"io.noties.markwon","description":"Markwon image loading module (based on Picasso library)"},{"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 }; export { artifacts };

View File

@ -0,0 +1,24 @@
<template>
<a :href="mavenSearchUrl()"><img :src="shieldImgageUrl()" :alt="displayLabel"></a>
</template>
<script>
export default {
name: 'MavenBadge',
props: ['artifact', 'label'],
methods: {
mavenSearchUrl: function() {
return `http://search.maven.org/#search|ga|1|g%3A%22io.noties.markwon%22%20AND%20a%3A%22${this.artifact}%22`;
},
shieldImgageUrl: function() {
return `https://img.shields.io/maven-central/v/io.noties.markwon/${this.artifact}.svg?label=${this.displayLabel}`;
}
},
computed: {
displayLabel() {
return this.label || this.artifact;
}
}
}
</script>

View File

@ -54,6 +54,17 @@ module.exports = {
'/docs/v4/core/render-props.md' '/docs/v4/core/render-props.md'
] ]
}, },
'/docs/v4/ext-latex/',
'/docs/v4/ext-strikethrough/',
'/docs/v4/ext-tables/',
'/docs/v4/ext-tasklist/',
'/docs/v4/html/',
'/docs/v4/image/',
'/docs/v4/image-glide/',
'/docs/v4/image-picasso/',
'/docs/v4/recycler/',
'/docs/v4/recycler-table/',
'/docs/v4/syntax-highlight/',
'/docs/v4/recipes.md', '/docs/v4/recipes.md',
'/docs/v4/migration-3-4.md' '/docs/v4/migration-3-4.md'
], ],

View File

@ -7,6 +7,8 @@ title: 'Overview'
<br><br> <br><br>
<MavenBadges2xx/> <MavenBadges2xx/>
<LegacyWarning />
**Markwon** is a markdown library for Android. It parses markdown following **Markwon** is a markdown library for Android. It parses markdown following
<Link name="commonmark-spec" /> with the help of amazing <Link name="commonmark-java" /> library <Link name="commonmark-spec" /> with the help of amazing <Link name="commonmark-java" /> library
and renders result as _Android-native_ Spannables. **No HTML** is involved and renders result as _Android-native_ Spannables. **No HTML** is involved

View File

@ -1,5 +1,7 @@
# Configuration # Configuration
<LegacyWarning />
`SpannableConfiguration` is the core component that controls how markdown is parsed and rendered. `SpannableConfiguration` is the core component that controls how markdown is parsed and rendered.
It can be obtained via factory methods: It can be obtained via factory methods:

View File

@ -1,5 +1,7 @@
# Factory <Badge text="1.1.0" /> # Factory <Badge text="1.1.0" />
<LegacyWarning />
`SpannableFactory` is used to create Span implementations. `SpannableFactory` is used to create Span implementations.
```java ```java

View File

@ -1,5 +1,7 @@
# Getting started # Getting started
<LegacyWarning />
## Quick one ## Quick one
This is the most simple way to set markdown to a `TextView` or any of its siblings: This is the most simple way to set markdown to a `TextView` or any of its siblings:

View File

@ -1,5 +1,7 @@
# HTML <Badge text="2.0.0" /> # HTML <Badge text="2.0.0" />
<LegacyWarning />
Starting with version `2.0.0` `Markwon` brings the whole HTML parsing/rendering Starting with version `2.0.0` `Markwon` brings the whole HTML parsing/rendering
stack _on-site_. The main reason for this are _special_ definitions of HTML nodes stack _on-site_. The main reason for this are _special_ definitions of HTML nodes
by <Link name="commonmark-spec" />. More specifically: <Link name="commonmark-spec#inline" displayName="inline" /> by <Link name="commonmark-spec" />. More specifically: <Link name="commonmark-spec#inline" displayName="inline" />

View File

@ -1,5 +1,7 @@
# Images # Images
<LegacyWarning />
By default `Markwon` doesn't handle images. Although `AsyncDrawable.Loader` is By default `Markwon` doesn't handle images. Although `AsyncDrawable.Loader` is
defined in main artifact, it does not provide implementation. defined in main artifact, it does not provide implementation.

View File

@ -1,5 +1,7 @@
# Installation # Installation
<LegacyWarning />
<MavenBadges2xx /> <MavenBadges2xx />
In order to start using `Markwon` add this to your dependencies block In order to start using `Markwon` add this to your dependencies block

View File

@ -1,5 +1,7 @@
# Syntax highlight # Syntax highlight
<LegacyWarning />
<MavenBadge2xx artifact="markwon-syntax-highlight" /> <MavenBadge2xx artifact="markwon-syntax-highlight" />
This is a simple module to add **syntax highlight** functionality to your markdown rendered with `Markwon` library. It is based on [Prism4j](https://github.com/noties/Prism4j) so lead there to understand how to configure `Prism4j` instance. This is a simple module to add **syntax highlight** functionality to your markdown rendered with `Markwon` library. It is based on [Prism4j](https://github.com/noties/Prism4j) so lead there to understand how to configure `Prism4j` instance.

View File

@ -1,5 +1,7 @@
# Theme # Theme
<LegacyWarning />
Here is the list of properties that can be configured via `SpannableTheme`. If you wish to control what Here is the list of properties that can be configured via `SpannableTheme`. If you wish to control what
is out of this list, you can use [SpannableFactory](/docs/v2/factory.md) is out of this list, you can use [SpannableFactory](/docs/v2/factory.md)
abstraction which lets you to gather full control of Spans that are used to display markdown. abstraction which lets you to gather full control of Spans that are used to display markdown.

View File

@ -1,5 +1,7 @@
# MarkwonView # MarkwonView
<LegacyWarning />
<MavenBadge2xx artifact="markwon-view" /> <MavenBadge2xx artifact="markwon-view" />
This is simple library containing 2 views that are able to display markdown: This is simple library containing 2 views that are able to display markdown:

View File

@ -0,0 +1,49 @@
# LaTeX extension
<MavenBadge4 :artifact="'ext-latex'" />
This is an extension that will help you display LaTeX formulas in your markdown.
Syntax is pretty simple: pre-fix and post-fix your latex with `$$` (double dollar sign).
`$$` should be the first characters in a line.
```markdown
$$
\\text{A long division \\longdiv{12345}{13}
$$
```
```markdown
$$\\text{A long division \\longdiv{12345}{13}$$
```
```java
Markwon.builder(context)
.use(JLatexMathPlugin.create(textSize))
.build();
```
This extension uses [jlatexmath-android](https://github.com/noties/jlatexmath-android) artifact to create LaTeX drawable.
## Config
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(JLatexMathPlugin.create(textSize, new BuilderConfigure() {
@Override
public void configureBuilder(@NonNull Builder builder) {
builder
.background(backgroundDrawable)
.align(JLatexMathDrawable.ALIGN_CENTER)
.fitCanvas(true)
.padding(paddingPx)
// @since 4.0.0 - optional, by default cached-thread-pool will be used
.executorService(Executors.newCachedThreadPool());
}
}))
.build();
```
:::tip
Since <Badge text="4.0.0" /> `JLatexMathPlugin` operates independently of `ImagesPlugin`
:::

View File

@ -0,0 +1,29 @@
# Strikethrough extension
<MavenBadge4 :artifact="'ext-strikethrough'" />
This module adds `strikethrough` functionality to `Markwon` via `StrikethroughPlugin`:
```java
Markwon.builder(context)
.usePlugin(StrikethroughPlugin.create())
```
This plugin registers `SpanFactory` for `Strikethrough` node, so it's possible to customize Strikethrough Span that is used in rendering:
```java
Markwon.builder(context)
.usePlugin(StrikethroughPlugin.create())
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder.setFactory(Strikethrough.class, new SpanFactory() {
@Override
public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
// will use Underline span instead of Strikethrough
return new UnderlineSpan();
}
});
}
})
```

View File

@ -0,0 +1,99 @@
# Tables extension
<MavenBadge4 :artifact="'ext-tables'" />
This extension adds support for GFM tables.
```java
final Markwon markwon = Markwon.builder(context)
// create default instance of TablePlugin
.usePlugin(TablePlugin.create(context))
```
```java
final TableTheme tableTheme = TableTheme.builder()
.tableBorderColor(Color.RED)
.tableBorderWidth(0)
.tableCellPadding(0)
.tableHeaderRowBackgroundColor(Color.BLACK)
.tableEvenRowBackgroundColor(Color.GREEN)
.tableOddRowBackgroundColor(Color.YELLOW)
.build();
final Markwon markwon = Markwon.builder(context)
.usePlugin(TablePlugin.create(tableTheme))
```
```java
Markwon.builder(context)
.usePlugin(TablePlugin.create(builder ->
builder
.tableBorderColor(Color.RED)
.tableBorderWidth(0)
.tableCellPadding(0)
.tableHeaderRowBackgroundColor(Color.BLACK)
.tableEvenRowBackgroundColor(Color.GREEN)
.tableOddRowBackgroundColor(Color.YELLOW)
))
```
Please note, that _by default_ tables have limitations. For example, there is no support
for images inside table cells. And table contents won't be copied to clipboard if a TextView
has such functionality. Table will always take full width of a TextView in which it is displayed.
All columns will always be of the same width. So, _default_ implementation provides basic
functionality which can answer some needs. These all come from the limited nature of the TextView
to display such content.
In order to provide full-fledged experience, tables must be displayed in a special widget.
Since version `3.0.0` Markwon provides a special artifact `markwon-recycler` that allows
to render markdown in a set of widgets in a RecyclerView. It also gives ability to change
display widget form TextView to any other.
```java
final Table table = Table.parse(Markwon, TableBlock);
myTableWidget.setTable(table);
```
:::tip
To take advantage of this functionality and render tables without limitations (including
horizontally scrollable layout when its contents exceed screen width), refer to [recycler-table](/docs/v3/recycler-table/)
module documentation that adds support for rendering `TableBlock` markdown node inside Android-native `TableLayout` widget.
:::
## Theme
### Cell padding
Padding inside a table cell
<ThemeProperty name="tableCellPadding" type="@Px int" defaults="0" />
### Border color
The color of table borders
<ThemeProperty name="tableBorderColor" type="@ColorInt int" defaults="(text color) with 75 (0-255) alpha" />
### Border width
The width of table borders
<ThemeProperty name="tableBorderWidth" type="@Px int" defaults="Stroke with of context TextPaint" />
### Odd row background
Background of an odd table row
<ThemeProperty name="tableOddRowBackgroundColor" type="@ColorInt int" defaults="(text color) with 22 (0-255) alpha" />
### Even row background <Badge text="1.1.1" />
Background of an even table row
<ThemeProperty name="tableEventRowBackgroundColor" type="@ColorInt int" defaults="0" />
### Header row background <Badge text="1.1.1" />
Background of header table row
<ThemeProperty name="tableHeaderRowBackgroundColor" type="@ColorInt int" defaults="0" />

View File

@ -0,0 +1,146 @@
# Task list extension
<MavenBadge4 :artifact="'ext-tasklist'" />
Adds support for GFM (Github-flavored markdown) task-lists:
```java
Markwon.builder(context)
.usePlugin(TaskListPlugin.create(context));
```
---
Create a default instance of `TaskListPlugin` with `TaskListDrawable` initialized to use
`android.R.attr.textColorLink` as primary color and `android.R.attr.colorBackground` as background
```java
TaskListPlugin.create(context);
```
---
Create an instance of `TaskListPlugin` with exact color values to use:
```java
// obtain color values
final int checkedFillColor = /* */;
final int normalOutlineColor = /* */;
final int checkMarkColor = /* */;
TaskListPlugin.create(checkedFillColor, normalOutlineColor, checkMarkColor);
```
---
Specify own drawable for a task list item:
```java
// obtain drawable
final Drawable drawable = /* */;
TaskListPlugin.create(drawable);
```
:::warning
Please note that custom drawable for a task list item must correctly handle state
in order to display done/not-done:
```java
public class MyTaskListDrawable extends Drawable {
private boolean isChecked;
@Override
public void draw(@NonNull Canvas canvas) {
// draw accordingly to the isChecked value
}
/* implementation omitted */
@Override
protected boolean onStateChange(int[] state) {
final boolean isChecked = contains(state, android.R.attr.state_checked);
final boolean result = this.isChecked != isChecked;
if (result) {
this.isChecked = isChecked;
}
return result;
}
private static boolean contains(@Nullable int[] states, int value) {
if (states != null) {
for (int state : states) {
if (state == value) {
// NB return here
return true;
}
}
}
return false;
}
}
```
:::
## Task list mutation
It is possible to mutate task list item state (toggle done/not-done). But note
that `Markwon` won't handle state change internally by any means and this change
is merely a visual one. If you need to persist state of a task list
item change you have to implement it yourself. This should get your started:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(TaskListPlugin.create(context))
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
// obtain original SpanFactory set by TaskListPlugin
final SpanFactory origin = builder.getFactory(TaskListItem.class);
if (origin == null) {
// or throw, as it's a bit weird state and we expect
// this factory to be present
return;
}
builder.setFactory(TaskListItem.class, new SpanFactory() {
@Override
public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
// it's a bit non-secure behavior and we should validate
// the type of returned span first, but for the sake of brevity
// we skip this step
final TaskListSpan span = (TaskListSpan) origin.getSpans(configuration, props);
if (span == null) {
// or throw
return null;
}
// return an array of spans
return new Object[]{
span,
new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
// toggle VISUAL state
span.setDone(!span.isDone());
// do not forget to invalidate widget
widget.invalidate();
// execute your persistence logic
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
// no-op, so appearance is not changed (otherwise
// task list item will look like a link)
}
}
};
}
});
}
})
.build();
```

107
docs/docs/v4/html/README.md Normal file
View File

@ -0,0 +1,107 @@
# HTML
<MavenBadge4 :artifact="'html'" />
This artifact encapsulates HTML parsing from the core artifact and provides
few predefined `TagHandlers`
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(HtmlPlugin.create())
.build();
```
As this artifact brings modified [jsoup](https://github.com/jhy/jsoup) library
it was moved to a standalone module in order to minimize dependencies and unused code
in applications that does not require HTML render capabilities.
Before <Badge text="2.0.0" /> `Markwon` used android `Html` class for parsing and
rendering. Unfortunately, according to markdown specification, markdown can contain
HTML in _unpredictable_ way if rendered _outside_ of browser. For example:
```markdown{4}
<i>
Hello from italics tag
</i><b>bold></b>
```
This snippet could be represented as:
* HtmlBlock (`<i>\nHello from italics tag`)
* HtmlInline (`<i>`)
* HtmlInline (`<b>`)
* Text (`bold`)
* HtmlInline (`</b>`)
:::tip A bit of background
<br>
<GithubIssue id="52" displayName="This issue" /> had brought attention to differences between HTML &amp; commonmark implementations. <br><br>
:::
Unfortunately Android `HTML` class cannot parse a _fragment_ of HTML to later
be included in a bigger set of content. This is why the decision was made to bring
HTML parsing _in-markwon-house_
## Predefined TagHandlers
* `<img>`
* `<a>`
* `<blockquote>`
* `<sub>`
* `<sup>`
* `<b>, <strong>`
* `<s>, <del>`
* `<u>, <ins>`
* `<ul>, <ol>`
* `<i>, <cite>, <em>, <dfn>`
* `<h1>, <h2>, <h3>, <h4>, <h5>, <h6>`
:::tip
All predefined tag handlers will use styling spans for native markdown content.
So, if your `Markwon` instance was configured to, for example, render Emphasis
nodes as a <span style="color: #FF0000">red text</span> then HTML tag handler will
use the same span. This includes images, links, UrlResolver, LinkProcessor, etc
:::
---
Staring with <Badge text="4.0.0" /> you can exclude all default tag handlers:
```java
.usePlugin(HtmlPlugin.create(new HtmlPlugin.HtmlConfigure() {
@Override
public void configureHtml(@NonNull HtmlPlugin plugin) {
plugin.excludeDefaults(true);
}
}))
```
or via plugin:
```java
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configure(@NonNull Registry registry) {
registry.require(HtmlPlugin.class, new Action<HtmlPlugin>() {
@Override
public void apply(@NonNull HtmlPlugin htmlPlugin) {
htmlPlugin.excludeDefaults(true);
}
});
}
})
```
If you wish to exclude some of them `TagHandlerNoOp` can be used:
```java
.usePlugin(HtmlPlugin.create(new HtmlPlugin.HtmlConfigure() {
@Override
public void configureHtml(@NonNull HtmlPlugin plugin) {
plugin.addHandler(TagHandlerNoOp.create("h4", "h5", "h6", "img"));
}
}))
```
## TagHandler

View File

@ -0,0 +1,3 @@
# Image Glide
<MavenBadge4 :artifact="'image-glide'" />

View File

@ -0,0 +1,3 @@
# Image Picasso
<MavenBadge4 :artifact="'image-picasso'" />

View File

@ -0,0 +1,172 @@
# Image
<MavenBadge4 :artifact="'image'" />
In order to display images in your markdown `ImagesPlugin` can be used.
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create())
```
:::tip
There are also modules that add image loading capabilities to markdown
based on image-loading libraries: [image-glide](/docs/v4/image-glide/) and
[image-picasso](/docs/v4/image-picasso/)
:::
`ImagesPlugin` splits the image-loading into 2 parts: scheme-handling and media-decoding.
## SchemeHandler
To add a scheme-handler to `ImagesPlugin`:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create())
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configure(@NonNull Registry registry) {
registry.require(ImagesPlugin.class, new Action<ImagesPlugin>() {
@Override
public void apply(@NonNull ImagesPlugin imagesPlugin) {
imagesPlugin.addSchemeHandler(DataUriSchemeHandler.create());
}
});
}
})
```
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
plugin.addSchemeHandler(DataUriSchemeHandler.create());
}
}))
```
`ImagesPlugin` comes with a set of predefined scheme-handlers:
* `FileSchemeHandler` - `file://`
* `DataUriSchemeHandler` - `data:`
* `NetworkSchemeHandler` - `http`, `https`
* `OkHttpNetworkSchemeHandler` - `http`, `https`
### FileSchemeHandler
Loads images via `file://` scheme. Allows loading images from `assets` folder.
```java
// default implementation, no assets handling
FileSchemeHandler.create();
// assets loading
FileSchemeHandler.createWithAssets(context);
```
:::warning
Assets loading will work when your URL will include `android_asset` the the path,
for example: `file:///android_asset/image.png` (mind the 3 slashes `///`). If you wish
to _assume_ all images without proper scheme to point to assets folder, then you can use
[UrlProcessorAndroidAssets](/docs/v4/core/configuration.html#urlprocessorandroidassets)
:::
By default `ImagesPlugin` includes _plain_ `FileSchemeHandler` (without assets support),
so if you wish to change that you can explicitly specify it:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
plugin.addSchemeHandler(FileSchemeHandler.createWithAssets(context));
}
}))
```
### DataUriSchemeHandler
`DataUriSchemeHandler` allows _inlining_ images with `data:` scheme (`data:image/svg+xml;base64,MTIz`).
This scheme-handler is registered by default, so you do not need to add it explicitly.
### NetworkSchemeHandler
`NetworkSchemeHandler` allows obtaining images from `http://` and `https://` uris
(internally it uses `HttpURLConnection`). This scheme-handler is registered by default
### OkHttpNetworkSchemeHandler
`OkHttpNetworkSchemeHandler` allows obtaining images from `http://` and `https://` uris
via [okhttp library](https://github.com/square/okhttp). Please note that in order to use
this scheme-handler you must explicitly add `okhttp` library to your project.
```java
// default instance
OkHttpNetworkSchemeHandler.create();
// specify OkHttpClient to use
OkHttpNetworkSchemeHandler.create(new OkHttpClient());
// @since 4.0.0
OkHttpNetworkSchemeHandler.create(Call.Factory);
```
### Custom SchemeHandler
```java
public abstract class SchemeHandler {
@NonNull
public abstract ImageItem handle(@NonNull String raw, @NonNull Uri uri);
@NonNull
public abstract Collection<String> supportedSchemes();
}
```
Starting with <Badge text="4.0.0" /> `SchemeHandler` can return a result (when no
further decoding is required):
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
// for example to return a drawable resource
plugin.addSchemeHandler(new SchemeHandler() {
@NonNull
@Override
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
final int resourceId = context.getResources().getIdentifier(
raw.substring("resources://".length()),
"drawable",
context.getPackageName());
// it's fine if it throws, async-image-loader will catch exception
final Drawable drawable = context.getDrawable(resourceId);
// it's important to apply bounds to resulting drawable
DrawableUtils.applyIntrinsicBounds(drawable);
return ImageItem.withResult(drawable);
}
@NonNull
@Override
public Collection<String> supportedSchemes() {
return Collections.singleton("resources");
}
});
}
}))
```
:::tip
If you are using [html](/docs/v4/html/) you do not have to additionally setup
images displayed via `<img>` tag, as `HtmlPlugin` automatically uses configured
image loader. But images referenced in HTML come with additional support for
sizes, which is not supported natively by markdown, allowing absolute or relative sizes:
```html
<img src="./assets/my-image" width="100%">
```
:::

View File

@ -1,3 +1,53 @@
# Recipes # Recipes
todo
## SpannableFactory
Consider using `NoCopySpannableFactory` when a `TextView` will be used to display markdown
multiple times (for example in a `RecyclerView`):
```java
// call after inflation and before setting markdown
textView.setSpannableFactory(NoCopySpannableFactory.getInstance());
```
## Autolink
Do not use `autolink` XML attribute on your `TextView` as it will remove all links except autolinked ones.
Consider using [linkify plugin](/docs/v4/linkify.md) or commonmark-java [autolink extension](https://github.com/atlassian/commonmark-java)
## Custom typeface
When using a custom typeface on a `TextView` you might find that **bold** and *italic* nodes
are displayed incorrectly. Consider registering own `SpanFactories` for `StrongEmphasis` and `Emphasis` nodes:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder
.setFactory(StrongEmphasis.class, new SpanFactory() {
@Override
public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
return new StyleSpan(Typeface.BOLD);
}
})
.setFactory(Emphasis.class, new SpanFactory() {
@Override
public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
return new StyleSpan(Typeface.ITALIC);
}
});
}
})
.build();
```
Please check that `StyleSpan` works for you. If it doesn't consider
using `CustomTypefaceSpan` with your typeface directly.

View File

@ -0,0 +1,92 @@
# Recycler Table <Badge text="3.0.0" />
<MavenBadge4 :artifact="'recycler-table'" />
Artifact that provides [MarkwonAdapter.Entry](/docs/v3/recycler/) to render `TableBlock` inside
Android-native `TableLayout` widget.
<img :src="$withBase('/assets/recycler-table-screenshot.png')" alt="screenshot" width="45%">
<br>
<small><em><sup>*</sup> It's possible to wrap `TableLayout` inside a `HorizontalScrollView` to include all table content</em></small>
---
Register instance of `TableEntry` with `MarkwonAdapter` to render TableBlocks:
```java
final MarkwonAdapter adapter = MarkwonAdapter.builder(R.layout.adapter_default_entry, R.id.text)
.include(TableBlock.class, TableEntry.create(builder -> builder
.tableLayout(R.layout.adapter_table_block, R.id.table_layout)
.textLayoutIsRoot(R.layout.view_table_entry_cell)))
.build();
```
`TableEntry` requires at least 2 arguments:
* `tableLayout` - layout with `TableLayout` inside
* `textLayout` - layout with `TextView` inside (represents independent table cell)
In case when required view is the root of layout specific builder methods can be used:
* `tableLayoutIsRoot(int)`
* `textLayoutIsRoot(int)`
If your layouts have different structure (for example wrap a `TableView` inside a `HorizontalScrollView`)
then you should use methods that accept ID of required view inside layout:
* `tableLayout(int, int)`
* `textLayout(int, int)`
---
To display `TableBlock` as a `TableLayout` specific `MarkwonPlugin` must be used: `TableEntryPlugin`.
:::warning
Do not use `TablePlugin` if you wish to display markdown tables via `TableEntry`. Use **TableEntryPlugin** instead
:::
`TableEntryPlugin` can reuse existing `TablePlugin` to make appearance of tables the same in both contexts:
when rendering _natively_ in a TextView and when rendering in RecyclerView with TableEntry.
* `TableEntryPlugin.create(Context)` - creates plugin with default `TableTheme`
* `TableEntryPlugin.create(TableTheme)` - creates plugin with provided `TableTheme`
* `TableEntryPlugin.create(TablePlugin.ThemeConfigure)` - creates plugin with theme configured by `ThemeConfigure`
* `TableEntryPlugin.create(TablePlugin)` - creates plugin with `TableTheme` used in provided `TablePlugin`
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(TableEntryPlugin.create(context))
// other plugins
.build();
```
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(TableEntryPlugin.create(builder -> builder
.tableBorderWidth(0)
.tableHeaderRowBackgroundColor(Color.RED)))
// other plugins
.build();
```
## Table with scrollable content
To stretch table columns to fit the width of screen or to make table scrollable when content exceeds screen width
this layout can be used:
```xml
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingLeft="16dip"
android:paddingTop="8dip"
android:paddingRight="16dip"
android:paddingBottom="8dip"
android:scrollbarStyle="outsideInset">
<TableLayout
android:id="@+id/table_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="*" />
</HorizontalScrollView>
```

View File

@ -0,0 +1,153 @@
# Recycler <Badge text="3.0.0" />
<MavenBadge4 :artifact="'recycler'" />
This artifact allows displaying markdown in a set of Android widgets
inside a RecyclerView. Can be useful when displaying lengthy markdown
content or **displaying certain markdown blocks inside specific widgets**.
```java
// create an adapter that will use a TextView for each block of markdown
// `createTextViewIsRoot` accepts a layout in which TextView is the root view
final MarkwonAdapter adapter =
MarkwonAdapter.createTextViewIsRoot(R.layout.adapter_default_entry);
```
```java
// `create` method accepts a layout with TextView and ID of a TextView
// which allows wrapping a TextView inside another widget or combine with other widgets
final MarkwonAdapter adapter =
MarkwonAdapter.create(R.layout.adapter_default_entry, R.id.text_view);
// initialize RecyclerView (LayoutManager, Decorations, etc)
final RecyclerView recyclerView = obtainRecyclerView();
// set adapter
recyclerView.setAdapter(adapter);
// obtain an instance of Markwon (register all required plugins)
final Markwon markwon = obtainMarkwon();
// set markdown to be displayed
adapter.setMarkdown(markwon, "# This is markdown!");
// NB, adapter does not handle updates on its own, please use
// whatever method appropriate for you.
adapter.notifyDataSetChanged();
```
Initialized adapter above will use a TextView for each markdown block.
In order to tell adapter to render certain blocks differently a `builder` can be used.
For example, let's render `FencedCodeBlock` inside a `HorizontalScrollView`:
```java
// we still need to have a _default_ entry
final MarkwonAdapter adapter =
MarkwonAdapter.builderTextViewIsRoot(R.layout.adapter_default_entry)
.include(FencedCodeBlock.class, new FencedCodeBlockEntry())
.build();
```
where `FencedCodeBlockEntry` is:
```java
public class FencedCodeBlockEntry extends MarkwonAdapter.Entry<FencedCodeBlock, FencedCodeBlockEntry.Holder> {
@NonNull
@Override
public Holder createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
return new Holder(inflater.inflate(R.layout.adapter_fenced_code_block, parent, false));
}
@Override
public void bindHolder(@NonNull Markwon markwon, @NonNull Holder holder, @NonNull FencedCodeBlock node) {
markwon.setParsedMarkdown(holder.textView, markwon.render(node));
}
public static class Holder extends MarkwonAdapter.Holder {
final TextView textView;
public Holder(@NonNull View itemView) {
super(itemView);
this.textView = requireView(R.id.text_view);
}
}
}
```
and its layout (`R.layout.adapter_fenced_code_block`):
```xml
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:fillViewport="true"
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:scrollbarStyle="outsideInset">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0f000000"
android:fontFamily="monospace"
android:lineSpacingExtra="2dip"
android:paddingLeft="16dip"
android:paddingTop="8dip"
android:paddingRight="16dip"
android:paddingBottom="8dip"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="14sp" />
</HorizontalScrollView>
```
As we apply styling to `FencedCodeBlock` _manually_, we no longer need
`Markwon` to apply styling spans for us, so `Markwon` initialization could be:
```java
final Markwon markwon = Markwon.builder(context)
// your other plugins
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
builder.on(FencedCodeBlock.class, (visitor, fencedCodeBlock) -> {
// we actually won't be applying code spans here, as our custom view will
// draw background and apply mono typeface
//
// NB the `trim` operation on literal (as code will have a new line at the end)
final CharSequence code = visitor.configuration()
.syntaxHighlight()
.highlight(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral().trim());
visitor.builder().append(code);
});
}
})
.build();
```
Previously we have created a `FencedCodeBlockEntry` but all it does is apply markdown to a TextView.
For such a case there is a `SimpleEntry` that could be used instead:
```java
final MarkwonAdapter adapter =
MarkwonAdapter.builderTextViewIsRoot(R.layout.adapter_default_entry)
.include(FencedCodeBlock.class, SimpleEntry.create(R.layout.adapter_fenced_code_block, R.id.text_view))
.build();
```
:::tip
`SimpleEntry` also takes care of _caching_ parsed markdown. So each node will be
parsed only once and each subsequent adapter binding call will reuse previously cached markdown.
:::
:::tip Tables
There is a standalone artifact that adds support for displaying markdown tables
natively via `TableLayout`. Please refer to its [documentation](/docs/v3/recycler-table/)
:::

View File

@ -0,0 +1,74 @@
# Syntax highlight
<MavenBadge4 :artifact="'syntax-highlight'" />
This is a module to add **syntax highlight** functionality to your markdown rendered with `Markwon` library. It is based on [Prism4j](https://github.com/noties/Prism4j) so lead there to understand how to configure `Prism4j` instance.
<img :src="$withBase('/art/markwon-syntax-default.png')" alt="theme-default" width="80%">
<img :src="$withBase('/art/markwon-syntax-darkula.png')" alt="theme-darkula" width="80%">
---
First, we need to obtain an instance of `Prism4jSyntaxHighlight` which implements Markwon's `SyntaxHighlight`:
```java
final SyntaxHighlight highlight =
Prism4jSyntaxHighlight.create(Prism4j, Prism4jTheme);
```
we also can obtain an instance of `Prism4jSyntaxHighlight` that has a _fallback_ option (if a language is not defined in `Prism4j` instance, fallback language can be used):
```java
final SyntaxHighlight highlight =
Prism4jSyntaxHighlight.create(Prism4j, Prism4jTheme, String);
```
Generally obtaining a `Prism4j` instance is pretty easy:
```java
final Prism4j prism4j = new Prism4j(new GrammarLocatorDef());
```
Where `GrammarLocatorDef` is a generated grammar locator (if you use `prism4j-bundler` annotation processor)
`Prism4jTheme` is a specific type that is defined in this module (`prism4j` doesn't know anything about rendering). It has 2 implementations:
* `Prism4jThemeDefault`
* `Prism4jThemeDarkula`
Both of them can be obtained via factory method `create`:
* `Prism4jThemeDefault.create()`
* `Prism4jThemeDarkula.create()`
But of cause nothing is stopping you from defining your own theme:
```java
public interface Prism4jTheme {
@ColorInt
int background();
@ColorInt
int textColor();
void apply(
@NonNull String language,
@NonNull Prism4j.Syntax syntax,
@NonNull SpannableStringBuilder builder,
int start,
int end
);
}
```
:::tip
You can extend `Prism4jThemeBase` which has some helper methods
:::
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(SyntaxHighlightPlugin.create(prism4j, prism4jTheme))
```