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/
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
sudo: false

View File

@ -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":"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 };

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/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/migration-3-4.md'
],

View File

@ -7,6 +7,8 @@ title: 'Overview'
<br><br>
<MavenBadges2xx/>
<LegacyWarning />
**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
and renders result as _Android-native_ Spannables. **No HTML** is involved

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,7 @@
# Syntax highlight
<LegacyWarning />
<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.

View File

@ -1,5 +1,7 @@
# Theme
<LegacyWarning />
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)
abstraction which lets you to gather full control of Spans that are used to display markdown.

View File

@ -1,5 +1,7 @@
# MarkwonView
<LegacyWarning />
<MavenBadge2xx artifact="markwon-view" />
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
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))
```