Bring back legacy 2.x.x documentation
This commit is contained in:
parent
b3c685bfbc
commit
7a598829a9
@ -1,5 +1,4 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply from: '../markwon-bundle.gradle'
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
@ -27,12 +26,6 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.markwon = [
|
|
||||||
'version': '3.0.0-SNAPSHOT',
|
|
||||||
'includeAll': true,
|
|
||||||
'exclude': ['ext-latex', 'core']
|
|
||||||
]
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation project(':markwon-core')
|
implementation project(':markwon-core')
|
||||||
|
13
docs/.vuepress/components/LegacyWarning.vue
Normal file
13
docs/.vuepress/components/LegacyWarning.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="warning custom-block">
|
||||||
|
<p class="custom-block-title">WARNING</p>
|
||||||
|
<p>This is documentation for <u>legacy 2.x.x</u> versions. For the most current version <a :href="$withBase('/')">click here.</a></p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'LegacyWarning'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
24
docs/.vuepress/components/MavenBadge2xx.vue
Normal file
24
docs/.vuepress/components/MavenBadge2xx.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<a :href="mavenSearchUrl()"><img :src="shieldImgageUrl()" :alt="displayLabel"></a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'MavenBadge2xx',
|
||||||
|
props: ['artifact', 'label'],
|
||||||
|
methods: {
|
||||||
|
mavenSearchUrl: function() {
|
||||||
|
return `http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22${this.artifact}%22`;
|
||||||
|
},
|
||||||
|
shieldImgageUrl: function() {
|
||||||
|
return `https://img.shields.io/maven-central/v/ru.noties/${this.artifact}.svg?label=${this.displayLabel}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
displayLabel() {
|
||||||
|
return this.label || this.artifact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
20
docs/.vuepress/components/MavenBadges2xx.vue
Normal file
20
docs/.vuepress/components/MavenBadges2xx.vue
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<MavenBadge2xx :artifact="'markwon'" />
|
||||||
|
<MavenBadge2xx :artifact="'markwon-image-loader'" />
|
||||||
|
<MavenBadge2xx :artifact="'markwon-syntax-highlight'"/>
|
||||||
|
<MavenBadge2xx :artifact="'markwon-view'"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MavenBadge2xx from "./MavenBadge2xx.vue";
|
||||||
|
export default {
|
||||||
|
name: "MavenBadges2xx",
|
||||||
|
components: {
|
||||||
|
MavenBadge2xx
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -16,38 +16,31 @@ module.exports = {
|
|||||||
{ text: 'Sandbox', link: '/sandbox.md' },
|
{ text: 'Sandbox', link: '/sandbox.md' },
|
||||||
{ text: 'Github', link: 'https://github.com/noties/Markwon' }
|
{ text: 'Github', link: 'https://github.com/noties/Markwon' }
|
||||||
],
|
],
|
||||||
sidebar: [
|
sidebar: {
|
||||||
'/',
|
'/docs/v2/': [
|
||||||
|
'install.md',
|
||||||
|
'getting-started.md',
|
||||||
|
'configure.md',
|
||||||
|
'theme.md',
|
||||||
|
'factory.md',
|
||||||
|
'image-loader.md',
|
||||||
|
'syntax-highlight.md',
|
||||||
|
'html.md',
|
||||||
|
'view.md'
|
||||||
|
],
|
||||||
|
'/': [
|
||||||
|
'',
|
||||||
{
|
{
|
||||||
title: 'Core',
|
title: 'Core',
|
||||||
children: [
|
children: [
|
||||||
'/docs/core/getting-started.md'
|
'/docs/core/getting-started.md',
|
||||||
]
|
'/docs/core/theme.md'
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'LaTeX extension',
|
|
||||||
children: [
|
|
||||||
'/docs/ext-latex/latex.md'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Strikethrough extension',
|
|
||||||
children: [
|
|
||||||
'/docs/ext-strikethrough/strikethrough.md'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Tables extension',
|
|
||||||
children: [
|
|
||||||
'/docs/ext-tables/tables.md'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Task list extension',
|
|
||||||
children: [
|
|
||||||
'/docs/ext-tasklist/tasklist.md'
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
'/docs/ext-latex/latex.md',
|
||||||
|
'/docs/ext-strikethrough/strikethrough.md',
|
||||||
|
'/docs/ext-tables/tables.md',
|
||||||
|
'/docs/ext-tasklist/tasklist.md',
|
||||||
{
|
{
|
||||||
title: 'HTML',
|
title: 'HTML',
|
||||||
children: [
|
children: [
|
||||||
@ -62,21 +55,12 @@ module.exports = {
|
|||||||
'/docs/image/svg.md'
|
'/docs/image/svg.md'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
'/docs/recycler/recycler.md',
|
||||||
title: 'Recycler',
|
'/docs/syntax-highlight/syntax-highlight.md',
|
||||||
children: [
|
'/docs/migration-2-3.md',
|
||||||
'/docs/recycler/recycler.md'
|
['/docs/v2/install.md', 'Legacy 2.x.x documentation']
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'Syntax highlight',
|
|
||||||
children: [
|
|
||||||
'/docs/syntax-highlight/syntax-highlight.md'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'/docs/migration-2-3.md'
|
|
||||||
|
|
||||||
],
|
|
||||||
sidebarDepth: 2,
|
sidebarDepth: 2,
|
||||||
lastUpdated: true
|
lastUpdated: true
|
||||||
},
|
},
|
||||||
|
158
docs/docs/core/theme.md
Normal file
158
docs/docs/core/theme.md
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
# Theme
|
||||||
|
|
||||||
|
Here is the list of properties that can be configured via `MarkwonTheme.Builder` class.
|
||||||
|
|
||||||
|
## 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="Width of the space character" />
|
||||||
|
|
||||||
|
### Code typeface
|
||||||
|
|
||||||
|
Typeface of code content
|
||||||
|
|
||||||
|
<ThemeProperty name="codeTypeface" type="android.graphics.Typeface" defaults="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)" />
|
||||||
|
|
||||||
|
## 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" />
|
@ -1,7 +1,46 @@
|
|||||||
---
|
|
||||||
title: 'Overview'
|
|
||||||
---
|
|
||||||
|
|
||||||
# LaTeX extension
|
# LaTeX extension
|
||||||
|
|
||||||
<MavenBadge :artifact="'ext-latex'" />
|
<MavenBadge :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(ImagesPlugin.create(context))
|
||||||
|
.use(JLatexMathPlugin.create(new Config(textSize))
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
This extension uses [jlatexmath-android](https://github.com/noties/jlatexmath-android) artifact to create LaTeX drawable. Then it
|
||||||
|
registers special `latex` image scheme handler and uses `AsyncDrawableLoader` to display
|
||||||
|
final result
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static class Config {
|
||||||
|
|
||||||
|
protected final float textSize;
|
||||||
|
|
||||||
|
protected Drawable background;
|
||||||
|
|
||||||
|
@JLatexMathDrawable.Align
|
||||||
|
protected int align = JLatexMathDrawable.ALIGN_CENTER;
|
||||||
|
|
||||||
|
protected boolean fitCanvas = true;
|
||||||
|
|
||||||
|
protected int padding;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -1,7 +1,29 @@
|
|||||||
---
|
|
||||||
title: 'Overview'
|
|
||||||
---
|
|
||||||
|
|
||||||
# Strikethrough extension
|
# Strikethrough extension
|
||||||
|
|
||||||
<MavenBadge :artifact="'ext-strikethrough'" />
|
<MavenBadge :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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
@ -1,7 +1,83 @@
|
|||||||
---
|
|
||||||
title: 'Overview'
|
|
||||||
---
|
|
||||||
|
|
||||||
# Tables extension
|
# Tables extension
|
||||||
|
|
||||||
<MavenBadge :artifact="'ext-tables'" />
|
<MavenBadge :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))
|
||||||
|
```
|
||||||
|
|
||||||
|
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 the 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);
|
||||||
|
```
|
||||||
|
|
||||||
|
Unfortunately Markwon does not provide a widget that can be used for tables. But it does
|
||||||
|
provide API that can be used to achieve desired result.
|
||||||
|
|
||||||
|
## 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" />
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
title: 'Overview'
|
|
||||||
---
|
|
||||||
|
|
||||||
# Task list extension
|
# Task list extension
|
||||||
|
|
||||||
<MavenBadge :artifact="'ext-tasklist'" />
|
<MavenBadge :artifact="'ext-tasklist'" />
|
@ -1 +1 @@
|
|||||||
# Migration v2 -> v3
|
# Migration 2.x.x -> 3.x.x
|
@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
title: 'Overview'
|
|
||||||
---
|
|
||||||
|
|
||||||
# Recycler
|
# Recycler
|
||||||
|
|
||||||
<MavenBadge :artifact="'recycler'" />
|
<MavenBadge :artifact="'recycler'" />
|
@ -1,7 +1,69 @@
|
|||||||
---
|
|
||||||
title: 'Overview'
|
|
||||||
---
|
|
||||||
|
|
||||||
# Syntax highlight
|
# Syntax highlight
|
||||||
|
|
||||||
<MavenBadge :artifact="'syntax-highlight'" />
|
<MavenBadge :artifact="'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.
|
||||||
|
|
||||||
|
<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
|
||||||
|
:::
|
228
docs/docs/v2/configure.md
Normal file
228
docs/docs/v2/configure.md
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
<LegacyWarning />
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
`SpannableConfiguration` is the core component that controls how markdown is parsed and rendered.
|
||||||
|
It can be obtained via factory methods:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// creates default implementation
|
||||||
|
final SpannableConfiguration configuration = SpannableConfiguration.create(context);
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
// creates configurablable instance via `#builder` method
|
||||||
|
final SpannableConfiguration configuration = SpannableConfiguration.builder(context)
|
||||||
|
.asyncDrawableLoader(AsyncDrawableLoader.create())
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip Note
|
||||||
|
If `#builder` factory method is used, you do not need to specify default
|
||||||
|
values as they will be applied automatically
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::warning Images
|
||||||
|
If you plan on using images inside your markdown/HTML, you will have to **explicitly**
|
||||||
|
register an implementation of `AsyncDrawable.Loader` via `#asyncDrawableLoader` builder method.
|
||||||
|
`Markwon` comes with ready implementation for that and it can be found in
|
||||||
|
`markwon-image-loader` module. Refer to module [documentation](/docs/v2/image-loader.md)
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Theme
|
||||||
|
|
||||||
|
`SpannableTheme` controls how markdown is rendered. It has pretty extensive number of
|
||||||
|
options that can be found [here](/docs/v2/theme.md)
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.theme(SpannableTheme)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If `SpannableTheme` is not provided explicitly, `SpannableTheme.create(context)` will be used
|
||||||
|
|
||||||
|
## Images
|
||||||
|
|
||||||
|
### Async loader
|
||||||
|
|
||||||
|
`AsyncDrawable.Loader` handles images in your markdown and HTML
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.asyncDrawableLoader(AsyncDrawable.Loader)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If `AsyncDrawable.Loader` is not provided explicitly, default **no-op** implementation will be used.
|
||||||
|
|
||||||
|
:::tip Implementation
|
||||||
|
There are no restrictions on what implementation to use, but `Markwon` has artifact that can
|
||||||
|
answer the most common needs of displaying SVG, GIF and other image formats. It can be found [here](/docs/v2/image-loader.md)
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Size resolver <Badge text="1.0.1" />
|
||||||
|
|
||||||
|
`ImageSizeResolver` controls the size of an image to be displayed. Currently it
|
||||||
|
handles only HTML images (specified via `img` tag).
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.imageSizeResolver(ImageSizeResolver)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If not provided explicitly, default `ImageSizeResolverDef` implementation will be used.
|
||||||
|
It handles 3 dimention units:
|
||||||
|
* `%` (percent)
|
||||||
|
* `em` (relative to text size)
|
||||||
|
* `px` (absolute size, every dimention 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 dimentions is missing.
|
||||||
|
|
||||||
|
:::warning Height%
|
||||||
|
There is no support for `%` units for `height` dimention. 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-refence from which to _calculate_ image height.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Syntax highlight
|
||||||
|
|
||||||
|
`SyntaxHighlight` controls the syntax highlight for code blocks (in markdown).
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.syntaxHighlight(SyntaxHighlight)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If not provided explicitly, default **no-op** implementation will be used.
|
||||||
|
|
||||||
|
:::tip Syntax highlight
|
||||||
|
Although `SyntaxHighlight` interface was included with the very first version
|
||||||
|
of `Markwon` there were no ready-to-use implementations. But starting with <Badge text="1.1.0" />
|
||||||
|
`Markwon` provides one. It can be found in `markwon-syntax-highlight` artifact. Refer
|
||||||
|
to module [documentation](/docs/v2/syntax-highlight.md)
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Link resolver
|
||||||
|
|
||||||
|
`LinkSpan.Resolver` is triggered when a link is clicked in markdown/HTML.
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.linkResolver(LinkSpan.Resolver)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If not provided explicitly, default `LinkResolverDef` implementation will be used.
|
||||||
|
Underneath it constructs an `Intent` and _tries_ to start an Activity associated with it.
|
||||||
|
It no Activity is found, it will silently fail (no runtime exceptions)
|
||||||
|
|
||||||
|
## URL processor
|
||||||
|
|
||||||
|
`UrlProcessor` is used to process found URLs in markdown/HTML.
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.urlProcessor(UrlProcessor)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If not provided explicitly, default **no-op** implementation will be used.
|
||||||
|
|
||||||
|
`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
|
||||||
|
|
||||||
|
## Factory <Badge text="1.1.0" />
|
||||||
|
|
||||||
|
`SpannableFactory` is used to control _what_ span implementations to be used
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.factory(SpannableFactory)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If not provided explicitly, default `SpannableFactoryDef` implementation will be used. It is documented
|
||||||
|
in [this section](/docs/v2/factory.md)
|
||||||
|
|
||||||
|
## Soft line break <Badge text="1.1.1" />
|
||||||
|
|
||||||
|
`softBreakAddsNewLine` option controls how _soft breaks_ are treated in the final result.
|
||||||
|
If `true` -> soft break will add a new line, else it will add a ` ` (space) char.
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.softBreakAddsNewLine(boolean)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If not provided explicitly, default `false` value will be used.
|
||||||
|
|
||||||
|
<Link name="commonmark-spec#soft-break" displayName="Commonmark specification" />
|
||||||
|
|
||||||
|
## HTML <Badge text="2.0.0" />
|
||||||
|
|
||||||
|
### Parser
|
||||||
|
|
||||||
|
`MarkwonHtmlParser` is used to parse HTML content
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.htmlParser(MarkwonHtmlParser)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
if not provided explicitly, default `MarkwonHtmlParserImpl` will be used
|
||||||
|
**if** it can be found in classpath, otherwise default **no-op** implementation
|
||||||
|
wiil be used. Refer to [HTML](/docs/v2/html.md#parser) document for more information about this behavior.
|
||||||
|
|
||||||
|
### Renderer
|
||||||
|
|
||||||
|
`MarkwonHtmlRenderer` controls how parsed HTML content will be rendered.
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.htmlRenderer(MarkwonHtmlRenderer)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If not provided explicitly, default `MarkwonHtmlRenderer` implementation will be used.
|
||||||
|
It is documented [here](/docs/v2/html.md#renderer)
|
||||||
|
|
||||||
|
### HTML allow non-closed tags
|
||||||
|
|
||||||
|
`htmlAllowNonClosedTags` option is used to control whether or not to
|
||||||
|
render non-closed HTML tags
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.htmlAllowNonClosedTags(boolean)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If not provided explicitly, default value `false` will be used (non-closed tags **won't** be rendered).
|
63
docs/docs/v2/factory.md
Normal file
63
docs/docs/v2/factory.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<LegacyWarning />
|
||||||
|
|
||||||
|
# Factory <Badge text="1.1.0" />
|
||||||
|
|
||||||
|
`SpannableFactory` is used to create Span implementations.
|
||||||
|
|
||||||
|
```java
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.factory(SpannableFactory)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
`Markwon` provides default `SpannableFactoryDef` implementation that is
|
||||||
|
used by default.
|
||||||
|
|
||||||
|
Spans:
|
||||||
|
* `strongEmphasis`
|
||||||
|
* `emphasis`
|
||||||
|
* `blockQuote`
|
||||||
|
* `code`
|
||||||
|
* `orderedListItem`
|
||||||
|
* `bulletListItem`
|
||||||
|
* `thematicBreak`
|
||||||
|
* `heading`
|
||||||
|
* `strikethrough`
|
||||||
|
* `taskListItem`
|
||||||
|
* `tableRow`
|
||||||
|
* `paragraph` <Badge text="1.1.1" />
|
||||||
|
* `image`
|
||||||
|
* `link`
|
||||||
|
* `superScript` (HTML content only)
|
||||||
|
* `subScript` (HTML content only)
|
||||||
|
* `underline` (HTML content only)
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
`SpannableFactory` can be used to ignore some kinds of text markup. If, for example,
|
||||||
|
you do not wish to apply _emphasis_ styling to your final result, just return `null`
|
||||||
|
from `emphasis` factory method:
|
||||||
|
```java
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object emphasis() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
All factory methods in `SpannableFactory` return an `Object`, but you can actually
|
||||||
|
return an **array of Objects** if you wish to apply multiple Spans to a single styling node.
|
||||||
|
For example, let's make all _emphasis_ also <span :style="{color: '#F00'}">red</span>:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object emphasis() {
|
||||||
|
return new Object[] {
|
||||||
|
super.emphasis(),
|
||||||
|
new ForegroundColorSpan(Color.RED)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
:::
|
102
docs/docs/v2/getting-started.md
Normal file
102
docs/docs/v2/getting-started.md
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<LegacyWarning />
|
||||||
|
|
||||||
|
# Getting started
|
||||||
|
|
||||||
|
:::tip Installation
|
||||||
|
Please follow [installation](/docs/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
|
||||||
|
Markwon.setMarkdown(textView, "**Hello there!**");
|
||||||
|
```
|
||||||
|
|
||||||
|
The most simple way to obtain markdown to be applied _somewhere_ else:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// parsed and styled markdown
|
||||||
|
final CharSequence markdown = Markwon.markdown(context, "**Hello there!**");
|
||||||
|
|
||||||
|
// use it
|
||||||
|
Toast.makeText(context, markdown, Toast.LENGTH_LONG).show();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Longer one
|
||||||
|
|
||||||
|
When you need to customize markdown parsing/rendering you can use [SpannableConfiguration](/docs/configure.md):
|
||||||
|
|
||||||
|
```java
|
||||||
|
final SpannableConfiguration configuration = SpannableConfiguration.builder(context)
|
||||||
|
.asyncDrawableLoader(AsyncDrawableLoader.create())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Markwon.setMarkdown(textView, configuration, "Are **you** still there?");
|
||||||
|
|
||||||
|
final CharSequence markdown = Markwon.markdown(configuration, "Are **you** still there?");
|
||||||
|
Toast.makeText(context, markdown, Toast.LENGTH_LONG).show();
|
||||||
|
```
|
||||||
|
|
||||||
|
## No magic one
|
||||||
|
|
||||||
|
In order to understand how previous examples work, let's break them down:
|
||||||
|
|
||||||
|
* construct a `Parser` (see: <Link name="commonmark-java" />) and parse markdown
|
||||||
|
* construct a `SpannableConfiguration` (if it's not provided)
|
||||||
|
* *render* parsed markdown to Spannable (via `SpannableRenderer`)
|
||||||
|
* prepares TextView to display images, tables and links
|
||||||
|
* sets text
|
||||||
|
|
||||||
|
This flow answers the most simple usage of displaying markdown: one shot parsing
|
||||||
|
& configuration of relatively small markdown chunks. If your markdown contains
|
||||||
|
a lot of text or you plan to display multiple UI widgets with markdown you might
|
||||||
|
consider *stepping in* and taking control of this flow.
|
||||||
|
|
||||||
|
The candidate requirements to *step in*:
|
||||||
|
* parsing and processing of parsed markdown in a background thread
|
||||||
|
* reusing `Parser` and/or `SpannableConfiguration` between multiple calls
|
||||||
|
* ignore images or tables specific logic (you know that markdown won't contain them)
|
||||||
|
|
||||||
|
So, if we expand `Markwon.setMarkdown(textView, markdown)` method we will see the following:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// create a Parser instance (can be done manually)
|
||||||
|
// internally creates default Parser instance & registers `strike-through` & `tables` extension
|
||||||
|
final Parser parser = Markwon.createParser();
|
||||||
|
|
||||||
|
// core class to display markdown, can be obtained via this method,
|
||||||
|
// which creates default instance (no images handling though),
|
||||||
|
// or via `builder` method, which lets you to configure this instance
|
||||||
|
final SpannableConfiguration configuration = SpannableConfiguration.create(context);
|
||||||
|
|
||||||
|
final SpannableRenderer renderer = new SpannableRenderer();
|
||||||
|
|
||||||
|
final Node node = parser.parse(markdown);
|
||||||
|
final CharSequence text = renderer.render(configuration, node);
|
||||||
|
|
||||||
|
// for links in markdown to be clickable
|
||||||
|
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
|
||||||
|
// we need these due to the limited nature of Spannables to invalidate TextView
|
||||||
|
Markwon.unscheduleDrawables(textView);
|
||||||
|
Markwon.unscheduleTableRows(textView);
|
||||||
|
|
||||||
|
// @since 2.0.1 we must measure ordered list items _before_ they are rendered
|
||||||
|
OrderedListItemSpan.measure(view, text);
|
||||||
|
|
||||||
|
textView.setText(text);
|
||||||
|
|
||||||
|
Markwon.scheduleDrawables(textView);
|
||||||
|
Markwon.scheduleTableRows(textView);
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip Note
|
||||||
|
If you are having trouble with `LinkMovementMethod` you can use
|
||||||
|
`Markwon.setText(textView, markdown, movementMethod)` method <Badge text="1.0.6" /> to specify _no_ movement
|
||||||
|
method (aka `null`) or own implementation. As an alternative to the system `LinkMovementMethod`
|
||||||
|
you can use [Better-Link-Movement-Method](https://github.com/saket/Better-Link-Movement-Method).
|
||||||
|
Please note that `Markwon.setText` method expects _parsed_ markdown as the second argument.
|
||||||
|
:::
|
305
docs/docs/v2/html.md
Normal file
305
docs/docs/v2/html.md
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
<LegacyWarning />
|
||||||
|
|
||||||
|
# HTML <Badge text="2.0.0" />
|
||||||
|
|
||||||
|
Starting with version `2.0.0` `Markwon` brings the whole HTML parsing/rendering
|
||||||
|
stack _on-site_. The main reason for this are _special_ definitions of HTML nodes
|
||||||
|
by <Link name="commonmark-spec" />. More specifically: <Link name="commonmark-spec#inline" displayName="inline" />
|
||||||
|
and <Link name="commonmark-spec#block" displayName="block" />.
|
||||||
|
These two are _a bit_ different from _native_ HTML understanding.
|
||||||
|
Well, they are _completely_ different and share only the same names as
|
||||||
|
<Link name="html-inlines" displayName="HTML-inline"/> and <Link name="html-blocks" displayName="HTML-block"/>
|
||||||
|
elements. This leads to situations when for example an `<i>` tag is considered
|
||||||
|
a block when it's used like this:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
<i>
|
||||||
|
Hello from italics tag
|
||||||
|
</i>
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip A bit of background
|
||||||
|
<br>
|
||||||
|
<GithubIssue id="52" displayName="This issue" /> had brought attention to differences between HTML & commonmark implementations. <br><br>
|
||||||
|
:::
|
||||||
|
|
||||||
|
Let's modify code snippet above _a bit_:
|
||||||
|
|
||||||
|
```markdown{3}
|
||||||
|
<i>
|
||||||
|
Hello from italics tag
|
||||||
|
|
||||||
|
</i>
|
||||||
|
```
|
||||||
|
|
||||||
|
We have just added a `new-line` before closing `</i>` tag. And this
|
||||||
|
changes everything as now, according to the <Link name="commonmark-dingus" />,
|
||||||
|
we have 2 HtmlBlocks: one before `new-line` (containing open `<i>` tag and text content)
|
||||||
|
and one after (containing as little as closing `</i>` tag).
|
||||||
|
|
||||||
|
If we modify code snippet _a bit_ again:
|
||||||
|
|
||||||
|
```markdown{4}
|
||||||
|
<i>
|
||||||
|
Hello from italics tag
|
||||||
|
|
||||||
|
</i><b>bold></b>
|
||||||
|
```
|
||||||
|
|
||||||
|
We will have 1 HtmlBlock (from previous snippet) and a bunch of HtmlInlines:
|
||||||
|
* HtmlInline (`<i>`)
|
||||||
|
* HtmlInline (`<b>`)
|
||||||
|
* Text (`bold`)
|
||||||
|
* HtmlInline (`</b>`)
|
||||||
|
|
||||||
|
Those _little_ differences render `Html.fromHtml` (which was used in `1.x.x` versions)
|
||||||
|
useless. And actually it renders most of the HTML parsers implementations useless,
|
||||||
|
as most of them do not allow processing of HTML fragments in a raw fashion
|
||||||
|
without _fixing_ content on-the-fly.
|
||||||
|
|
||||||
|
Both `TagSoup` and `Jsoup` HTML parsers (that were considered for this project) are built to deal with
|
||||||
|
_malicious_ HTML code (*all HTML code*? :no_mouth:). So, when supplied
|
||||||
|
with a `<i>italic` fragment they will make it `<i>italic</i>`.
|
||||||
|
And it's a good thing, but consider these fragments for the sake of markdown:
|
||||||
|
|
||||||
|
* `<i>italic `
|
||||||
|
* `<b>bold italic`
|
||||||
|
* `</b><i>`
|
||||||
|
|
||||||
|
We will get:
|
||||||
|
|
||||||
|
* `<i>italic </i>`
|
||||||
|
* `<b>bold italic</b>`
|
||||||
|
|
||||||
|
_<sup>*</sup> Or to be precise: `<html><head></head><body><i>italic </i></body></html>` &
|
||||||
|
`<html><head></head><body><b>bold italic</b></body></html>`_
|
||||||
|
|
||||||
|
Which will be rendered in a final document:
|
||||||
|
|
||||||
|
|
||||||
|
|expected|actual|
|
||||||
|
|---|---|
|
||||||
|
|<i>italic <b>bold italic</b></i>|<i>italic </i><b>bold italic</b>|
|
||||||
|
|
||||||
|
This might seem like a minor problem, but add more tags to a document,
|
||||||
|
introduce some deeply nested structures, spice openning and closing tags up
|
||||||
|
by adding markdown markup between them and finally write _malicious_ HTML code :laughing:!
|
||||||
|
|
||||||
|
There is no such problem on the _frontend_ for which commonmark specification is mostly
|
||||||
|
aimed as _frontend_ runs in a web-browser environment. After all _parsed_ markdown
|
||||||
|
will become HTML tags (most common usage). And web-browser will know how to render final result.
|
||||||
|
|
||||||
|
We, on the other hand, do not posess HTML heritage (*thank :robot:!*), but still
|
||||||
|
want to display some HTML to style resulting markdown a bit. That's why `Markwon`
|
||||||
|
incorporated own HTML parsing logic. It is based on the <Link name="jsoup" /> project.
|
||||||
|
And makes usage of the `Tokekiser` class that allows to _tokenise_ input HTML.
|
||||||
|
All other code that doesn't follow this purpose was removed. It's safe to use
|
||||||
|
in projects that already have `jsoup` dependency as `Markwon` repackaged **jsoup** source classes
|
||||||
|
(which could be found <Link name="markwon-jsoup" displayName="here"/>)
|
||||||
|
|
||||||
|
## Parser
|
||||||
|
|
||||||
|
There are no additional steps to configure HTML parsing. It's enabled by default.
|
||||||
|
If you wish to _exclude_ it, please follow the [exclude](#exclude-html-parsing) section below.
|
||||||
|
|
||||||
|
The key class here is: `MarkwonHtmlParser` that is defined in `markwon-html-parser-api` module.
|
||||||
|
`markwon-html-parser-api` is a simple module that defines HTML parsing contract and
|
||||||
|
does not provide implementation.
|
||||||
|
|
||||||
|
To change what implementation `Markwon` should use, `SpannableConfiguration` can be used:
|
||||||
|
|
||||||
|
```java{2}
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.htmlParser(MarkwonHtmlParser)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
`markwon-html-parser-impl` on the other hand provides `MarkwonHtmlParser` implementation.
|
||||||
|
It's called `MarkwonHtmlParserImpl`. It can be created like this:
|
||||||
|
|
||||||
|
```java
|
||||||
|
final MarkwonHtmlParser htmlParser = MarkwonHtmlParserImpl.create();
|
||||||
|
// or
|
||||||
|
final MarkwonHtmlParser htmlParser = MarkwonHtmlParserImpl.create(HtmlEmptyTagReplacement);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Empty tag replacement
|
||||||
|
|
||||||
|
In order to append text content for self-closing, void or just _empty_ HTML tags,
|
||||||
|
`HtmlEmptyTagReplacement` can be used. As we cannot set Span for empty content,
|
||||||
|
we must represent empty tag with text during parsing stage (if we want it to be represented).
|
||||||
|
|
||||||
|
Consider this:
|
||||||
|
* `<img src="me-sad.JPG">`
|
||||||
|
* `<br />`
|
||||||
|
* `<who-am-i></who-am-i>`
|
||||||
|
|
||||||
|
By default (`HtmlEmptyTagReplacement.create()`) will handle `img` and `br` tags.
|
||||||
|
`img` will be replaced with `alt` property if it is present and `\uFFFC` if it is not.
|
||||||
|
And `br` will insert a new line.
|
||||||
|
|
||||||
|
### Non-closed tags
|
||||||
|
|
||||||
|
It's possible that your HTML can contain non-closed tags. By default `Markwon` will ignore them,
|
||||||
|
but if you wish to get a bit closer to a web-browser experience, you can allow this behaviour:
|
||||||
|
|
||||||
|
```java{2}
|
||||||
|
SpannableConfiguration.builder(context)
|
||||||
|
.htmlAllowNonClosedTags(true)
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
:::warning Note
|
||||||
|
If there is (for example) an `<i>` tag at the start of a document and it's not closed
|
||||||
|
and `Markwon` is configured to **not** ignore non-closed tags (`.htmlAllowNonClosedTags(true)`),
|
||||||
|
it will make the whole document in italics
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Implementation note
|
||||||
|
|
||||||
|
`MarkwonHtmlParserImpl` does not create a unified HTML node. Instead it creates
|
||||||
|
2 collections: inline tags and block tags. Inline tags are represented as a `List`
|
||||||
|
of inline tags (<Link name="html-inlines" displayName="reference" />). And
|
||||||
|
block tags are structured in a tree. This helps to achieve _browser_-like behaviour,
|
||||||
|
when open inline tag is applied to all content (even if inside blocks) until closing tag.
|
||||||
|
All tags that are not _inline_ are considered to be _block_ ones.
|
||||||
|
|
||||||
|
## Renderer
|
||||||
|
|
||||||
|
Unlike `MarkwonHtmlParser` `Markwon` comes with a `MarkwonHtmlRenderer` by default.
|
||||||
|
|
||||||
|
Default implementation can be obtain like this:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MarkwonHtmlRenderer.create();
|
||||||
|
```
|
||||||
|
|
||||||
|
Default instance have these tags _handled_:
|
||||||
|
* emphasis
|
||||||
|
* `i`
|
||||||
|
* `em`
|
||||||
|
* `cite`
|
||||||
|
* `dfn`
|
||||||
|
* strong emphasis
|
||||||
|
* `b`
|
||||||
|
* `strong`
|
||||||
|
* `sup` (super script)
|
||||||
|
* `sub` (sub script)
|
||||||
|
* underline
|
||||||
|
* `u`
|
||||||
|
* `ins`
|
||||||
|
* strike through
|
||||||
|
* `del`
|
||||||
|
* `s`
|
||||||
|
* `strike`
|
||||||
|
* `a` (link)
|
||||||
|
* `ul` (unordered list)
|
||||||
|
* `ol` (ordered list)
|
||||||
|
* `img` (image)
|
||||||
|
* `blockquote` (block quote)
|
||||||
|
* `h{1-6}` (heading)
|
||||||
|
|
||||||
|
If you wish to _extend_ default handling (or override existing),
|
||||||
|
`#builderWithDefaults` factory method can be used:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MarkwonHtmlRenderer.builderWithDefaults();
|
||||||
|
```
|
||||||
|
|
||||||
|
For a completely _clean_ configurable instance `#builder` method can be used:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MarkwonHtmlRenderer.builder();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom tag handler
|
||||||
|
|
||||||
|
To configure `MarkwonHtmlRenderer` to handle tags differently or
|
||||||
|
create a new tag handler - `TagHandler` can be used
|
||||||
|
|
||||||
|
```java
|
||||||
|
public abstract class TagHandler {
|
||||||
|
|
||||||
|
public abstract void handle(
|
||||||
|
@NonNull SpannableConfiguration configuration,
|
||||||
|
@NonNull SpannableBuilder builder,
|
||||||
|
@NonNull HtmlTag tag
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For the most simple _inline_ tag handler a `SimpleTagHandler` can be used:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public abstract class SimpleTagHandler extends TagHandler {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public abstract Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, `EmphasisHandler`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class EmphasisHandler extends SimpleTagHandler {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
|
||||||
|
return configuration.factory().emphasis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you wish to handle a _block_ HTML node (for example `<ul><li>First<li>Second</ul>`) refer
|
||||||
|
to `ListHandler` source code for reference.
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
The most important thing when implementing custom `TagHandler` is to know
|
||||||
|
what type of `HtmlTag` we are dealing with. There are 2: inline & block.
|
||||||
|
Inline tag cannot contain children. Block _can_ contain children. And they
|
||||||
|
_most likely_ should also be visited and _handled_ by registered `TagHandler` (if any)
|
||||||
|
accordingly. See `TagHandler#visitChildren(configuration, builder, child);`
|
||||||
|
:::
|
||||||
|
|
||||||
|
#### Css inline style parser
|
||||||
|
|
||||||
|
When implementing own `TagHandler` you might want to inspect inline CSS styles
|
||||||
|
of a HTML element. `Markwon` provides an utility parser for that purpose:
|
||||||
|
|
||||||
|
```java
|
||||||
|
final CssInlineStyleParser inlineStyleParser = CssInlineStyleParser.create();
|
||||||
|
for (CssProperty property: inlineStyleParser.parse("width: 100%; height: 100%;")) {
|
||||||
|
// [0] = CssProperty({width=100%}),
|
||||||
|
// [1] = CssProperty({height=100%})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exclude HTML parsing
|
||||||
|
|
||||||
|
If you wish to exclude HTML parsing altogether, you can manually
|
||||||
|
exclude `markwon-html-parser-impl` artifact from your projects compile classpath.
|
||||||
|
This can be beneficial if you know that markdown input won't contain
|
||||||
|
HTML and/or you wish to ignore it. Excluding HTML parsing
|
||||||
|
can speed up `Markwon` parsing and will decrease final size of
|
||||||
|
`Markwon` dependency by around `100kb`.
|
||||||
|
|
||||||
|
<MavenBadge :artifact="'markwon'" />
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
dependencies {
|
||||||
|
implementation("ru.noties:markwon:${markwonVersion}") {
|
||||||
|
exclude module: 'markwon-html-parser-impl'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Excluding `markwon-html-parser-impl` this way will result in
|
||||||
|
`MarkwonHtmlParser#noOp` implementation. No further steps are
|
||||||
|
required.
|
||||||
|
|
||||||
|
:::warning Note
|
||||||
|
Excluding `markwon-html-parser-impl` won't remove *all* the content between
|
||||||
|
HTML tags. It will if `commonmark` decides that a specific fragment is a
|
||||||
|
`HtmlBlock`, but it won't if fragment is considered a `HtmlInline` as `HtmlInline`
|
||||||
|
does not contain content (just a tag definition).
|
||||||
|
:::
|
@ -1,3 +1,5 @@
|
|||||||
|
<LegacyWarning />
|
||||||
|
|
||||||
# Images
|
# Images
|
||||||
|
|
||||||
By default `Markwon` doesn't handle images. Although `AsyncDrawable.Loader` is
|
By default `Markwon` doesn't handle images. Although `AsyncDrawable.Loader` is
|
||||||
@ -16,12 +18,12 @@ public interface Loader {
|
|||||||
|
|
||||||
## AsyncDrawableLoader
|
## AsyncDrawableLoader
|
||||||
|
|
||||||
<MavenBadge artifact="markwon-image-loader" />
|
<MavenBadge2xx artifact="markwon-image-loader" />
|
||||||
|
|
||||||
`AsyncDrawableLoader` from `markwon-image-loader` artifact can be used.
|
`AsyncDrawableLoader` from `markwon-image-loader` artifact can be used.
|
||||||
|
|
||||||
:::tip Install
|
:::tip Install
|
||||||
[Learn how to add](/docs/install.md#image-loader) `markwon-image-loader` to your project
|
[Learn how to add](/docs/v2/install.md#image-loader) `markwon-image-loader` to your project
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Default instance of `AsyncDrawableLoader` can be obtain like this:
|
Default instance of `AsyncDrawableLoader` can be obtain like this:
|
79
docs/docs/v2/install.md
Normal file
79
docs/docs/v2/install.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<LegacyWarning />
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
<MavenBadges2xx />
|
||||||
|
|
||||||
|
In order to start using `Markwon` add this to your dependencies block
|
||||||
|
in your projects `build.gradle`:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
implementation "ru.noties:markwon:${markwonVersion}"
|
||||||
|
```
|
||||||
|
|
||||||
|
This is core artifact that is sufficient to start displaying markdown in your Android applications.
|
||||||
|
|
||||||
|
`Markwon` comes with more artifacts that cover additional functionality, but they are
|
||||||
|
**not** required to be used, as most of them provide implementations for functionality
|
||||||
|
that is _interfaced_ in the core artifact
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
implementation "ru.noties:markwon-image-loader:${markwonVersion}"
|
||||||
|
implementation "ru.noties:markwon-syntax-highlight:${markwonVersion}"
|
||||||
|
implementation "ru.noties:markwon-view:${markwonVersion}"
|
||||||
|
```
|
||||||
|
|
||||||
|
These artifacts share the same _version_ as the core artifact
|
||||||
|
|
||||||
|
### Image loader
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
implementation "ru.noties:markwon-image-loader:${markwonVersion}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Provides implementation of `AsyncDrawable.Loader` and comes with support for:
|
||||||
|
* SVG
|
||||||
|
* GIF
|
||||||
|
* Other image formats
|
||||||
|
|
||||||
|
Please refer to documentation for [image loader](/docs/v2/image-loader.md) module
|
||||||
|
|
||||||
|
### Syntax highlight
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
implementation "ru.noties:markwon-syntax-highlight:${markwonVersion}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Provides implementation of `SyntaxHighlight` and allows various syntax highlighting
|
||||||
|
in your markdown based Android applications. Comes with 2 ready-to-be-used themes: `light` and `dark`.
|
||||||
|
Please refer to documentation for [syntax highlight](/docs/v2/syntax-highlight.md) module
|
||||||
|
|
||||||
|
### View
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
implementation "ru.noties:markwon-view:${markwonVersion}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Provides 2 widgets to display markdown: `MarkwonView` and `MarkwonViewCompat` (subclasses
|
||||||
|
of `TextView` and `AppCompatTextView` respectively).
|
||||||
|
Please refer to documentation for [view](/docs/v2/view.md) module
|
||||||
|
|
||||||
|
## Proguard
|
||||||
|
|
||||||
|
When using `markwon-image-loader` artifact and Proguard is enabled, add these rules
|
||||||
|
to your proguard configuration:
|
||||||
|
|
||||||
|
```proguard
|
||||||
|
-dontwarn okhttp3.**
|
||||||
|
-dontwarn okio.**
|
||||||
|
|
||||||
|
-keep class com.caverock.androidsvg.** { *; }
|
||||||
|
-dontwarn com.caverock.androidsvg.**
|
||||||
|
```
|
||||||
|
|
||||||
|
They come from dependencies that `markwon-image-loader` is using.
|
||||||
|
|
||||||
|
:::tip Other artifacts
|
||||||
|
Other artifacts do not require special Proguard rules
|
||||||
|
:::
|
||||||
|
|
@ -1,6 +1,8 @@
|
|||||||
|
<LegacyWarning />
|
||||||
|
|
||||||
# Syntax highlight
|
# Syntax highlight
|
||||||
|
|
||||||
<MavenBadge 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.
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
|
<LegacyWarning />
|
||||||
|
|
||||||
# Theme
|
# Theme
|
||||||
|
|
||||||
Here is the list of properties that can be configured via `SpannableTheme#builder` factory
|
Here is the list of properties that can be configured via `SpannableTheme#builder` factory
|
||||||
@ -209,4 +211,4 @@ Background of header table row
|
|||||||
|
|
||||||
Drawable of task list item
|
Drawable of task list item
|
||||||
|
|
||||||
<ThemeProperty name="taskListDrawable" type="android.graphics.drawable.Drawable" defaults="ru.noties.markwon.tasklist.TaskListDrawable" />
|
<ThemeProperty name="taskListDrawable" type="android.graphics.drawable.Drawable" defaults="ru.noties.markwon.spans.TaskListDrawable" />
|
@ -1,6 +1,8 @@
|
|||||||
|
<LegacyWarning />
|
||||||
|
|
||||||
# MarkwonView
|
# MarkwonView
|
||||||
|
|
||||||
<MavenBadge 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:
|
||||||
* MarkwonView - extends `android.view.TextView`
|
* MarkwonView - extends `android.view.TextView`
|
||||||
@ -27,7 +29,8 @@ public interface IMarkwonView {
|
|||||||
|
|
||||||
Both views support layout-preview in Android Studio (with some exceptions, for example, bold span is not rendered due to some limitations of layout preview).
|
Both views support layout-preview in Android Studio (with some exceptions, for example, bold span is not rendered due to some limitations of layout preview).
|
||||||
These are XML attributes:
|
These are XML attributes:
|
||||||
```
|
|
||||||
|
```xml
|
||||||
app:mv_markdown="string"
|
app:mv_markdown="string"
|
||||||
app:mv_configurationProvider="string"
|
app:mv_configurationProvider="string"
|
||||||
```
|
```
|
@ -1,6 +1,8 @@
|
|||||||
# LaTeX
|
# LaTeX
|
||||||
|
|
||||||
This is a small extension that will help you display LaTeX formulas in your markdown.
|
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties.markwon%22%20AND%20a%3A%22ext-latex%22)
|
||||||
|
|
||||||
|
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).
|
Syntax is pretty simple: pre-fix and post-fix your latex with `$$` (double dollar sign).
|
||||||
`$$` should be the first characters in a line.
|
`$$` should be the first characters in a line.
|
||||||
|
|
||||||
@ -16,7 +18,6 @@ $$\\text{A long division \\longdiv{12345}{13}$$
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
Markwon.builder(context)
|
Markwon.builder(context)
|
||||||
.use(CorePlugin.create())
|
|
||||||
.use(ImagesPlugin.create(context))
|
.use(ImagesPlugin.create(context))
|
||||||
.use(JLatexMathPlugin.create(new Config(textSize))
|
.use(JLatexMathPlugin.create(new Config(textSize))
|
||||||
.build();
|
.build();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Strikethrough
|
# Strikethrough
|
||||||
|
|
||||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-ext-strikethrough%22)
|
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties.markwon%22%20AND%20a%3A%22ext-strikethrough%22)
|
||||||
|
|
||||||
This module adds `strikethrough` functionality to `Markwon` via `StrikethroughPlugin`:
|
This module adds `strikethrough` functionality to `Markwon` via `StrikethroughPlugin`:
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ This plugin registers `SpanFactory` for `Strikethrough` node, so it's possible t
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
Markwon.builder(context)
|
Markwon.builder(context)
|
||||||
.usePlugin(StrikethroughPlugin.class)
|
.usePlugin(StrikethroughPlugin.create())
|
||||||
.usePlugin(new AbstractMarkwonPlugin() {
|
.usePlugin(new AbstractMarkwonPlugin() {
|
||||||
@Override
|
@Override
|
||||||
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
|
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
|
||||||
|
45
markwon-ext-tables/README.md
Normal file
45
markwon-ext-tables/README.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Tables
|
||||||
|
|
||||||
|
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties.markwon%22%20AND%20a%3A%22ext-tables%22)
|
||||||
|
|
||||||
|
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))
|
||||||
|
```
|
||||||
|
|
||||||
|
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 the 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);
|
||||||
|
```
|
||||||
|
|
||||||
|
Unfortunately Markwon does not provide a widget that can be used for tables. But it does
|
||||||
|
provide API that can be used to achieve desired result.
|
@ -52,6 +52,7 @@ public class TablePlugin extends AbstractMarkwonPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeRender(@NonNull Node node) {
|
public void beforeRender(@NonNull Node node) {
|
||||||
|
// clear before rendering (as visitor has some internal mutable state)
|
||||||
visitor.clear();
|
visitor.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
# Markwon View
|
|
||||||
|
|
||||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon-view%22)
|
|
||||||
|
|
||||||
This is simple library containing 2 views that are able to display markdown:
|
|
||||||
* MarkwonView - extends `android.view.TextView`
|
|
||||||
* MarkwonViewCompat - extends `android.support.v7.widget.AppCompatTextView`
|
|
||||||
|
|
||||||
Both of them implement common `IMarkwonView` interface:
|
|
||||||
```java
|
|
||||||
public interface IMarkwonView {
|
|
||||||
|
|
||||||
interface ConfigurationProvider {
|
|
||||||
@NonNull
|
|
||||||
SpannableConfiguration provide(@NonNull Context context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setConfigurationProvider(@NonNull ConfigurationProvider provider);
|
|
||||||
|
|
||||||
void setMarkdown(@Nullable String markdown);
|
|
||||||
void setMarkdown(@Nullable SpannableConfiguration configuration, @Nullable String markdown);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
String getMarkdown();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Both views support layout-preview in Android Studio (with some exceptions, for example, bold span is not rendered due to some limitations of layout preview).
|
|
||||||
These are XML attributes:
|
|
||||||
```
|
|
||||||
app:mv_markdown="string"
|
|
||||||
app:mv_configurationProvider="string"
|
|
||||||
```
|
|
||||||
|
|
||||||
`mv_markdown` accepts a string and represents raw markdown
|
|
||||||
|
|
||||||
`mv_configurationProvider` accepts a string and represents a full class name of a class of type `ConfigurationProvider`,
|
|
||||||
for example: `com.example.my.package.MyConfigurationProvider` (this class must have an empty constructor
|
|
||||||
in order to be instantiated via reflection).
|
|
||||||
|
|
||||||
Please note that those views parse markdown in main thread, so their usage must be for relatively small markdown portions only
|
|
@ -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')
|
|
||||||
|
|
||||||
deps.with {
|
|
||||||
compileOnly it['support-app-compat']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerArtifact(this)
|
|
@ -1,31 +0,0 @@
|
|||||||
package ru.noties.markwon.view.debug;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import ru.noties.markwon.MarkwonConfiguration;
|
|
||||||
import ru.noties.markwon.core.MarkwonTheme;
|
|
||||||
import ru.noties.markwon.view.IMarkwonView;
|
|
||||||
|
|
||||||
public class DebugConfigurationProvider implements IMarkwonView.ConfigurationProvider {
|
|
||||||
|
|
||||||
private MarkwonConfiguration cached;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public MarkwonConfiguration provide(@NonNull Context context) {
|
|
||||||
if (cached == null) {
|
|
||||||
cached = MarkwonConfiguration.builder(context)
|
|
||||||
.theme(debugTheme(context))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MarkwonTheme debugTheme(@NonNull Context context) {
|
|
||||||
return MarkwonTheme.builderWithDefaults(context)
|
|
||||||
.blockQuoteColor(0xFFff0000)
|
|
||||||
.codeBackgroundColor(0x40FF0000)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ru.noties.markwon.view.MarkwonView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:paddingLeft="16dip"
|
|
||||||
android:paddingRight="16dip"
|
|
||||||
android:textColor="#333"
|
|
||||||
app:mv_configurationProvider="ru.noties.markwon.view.debug.DebugConfigurationProvider"
|
|
||||||
app:mv_markdown="@string/debug_markdown"/>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ru.noties.markwon.view.MarkwonViewCompat
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:paddingLeft="16dip"
|
|
||||||
android:paddingRight="16dip"
|
|
||||||
android:textColor="#333"
|
|
||||||
app:mv_configurationProvider="ru.noties.markwon.view.debug.DebugConfigurationProvider"
|
|
||||||
app:mv_markdown="@string/debug_markdown"/>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
@ -1,39 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<string name="debug_markdown">
|
|
||||||
<![CDATA[
|
|
||||||
|
|
||||||
# Hello
|
|
||||||
\n
|
|
||||||
**Bold title, right (layout preview somehow cannot render it)?**
|
|
||||||
\n
|
|
||||||
> Quote
|
|
||||||
\n
|
|
||||||
>> Quote #2
|
|
||||||
\n
|
|
||||||
>>> Quote `#3`
|
|
||||||
\n
|
|
||||||
---
|
|
||||||
\n
|
|
||||||
```
|
|
||||||
\n
|
|
||||||
// this is some amazing code block
|
|
||||||
\n
|
|
||||||
```
|
|
||||||
\n
|
|
||||||
* First
|
|
||||||
\n
|
|
||||||
* Second
|
|
||||||
\n
|
|
||||||
* * Second-First
|
|
||||||
\n
|
|
||||||
* * Second-Second
|
|
||||||
\n
|
|
||||||
* * * Second-Second-First
|
|
||||||
\n
|
|
||||||
* And out of blue - Third
|
|
||||||
]]>
|
|
||||||
</string>
|
|
||||||
|
|
||||||
</resources>
|
|
@ -1 +0,0 @@
|
|||||||
<manifest package="ru.noties.markwon.view" />
|
|
@ -1,23 +0,0 @@
|
|||||||
package ru.noties.markwon.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import ru.noties.markwon.MarkwonConfiguration;
|
|
||||||
|
|
||||||
public interface IMarkwonView {
|
|
||||||
|
|
||||||
interface ConfigurationProvider {
|
|
||||||
@NonNull
|
|
||||||
MarkwonConfiguration provide(@NonNull Context context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setConfigurationProvider(@NonNull ConfigurationProvider provider);
|
|
||||||
|
|
||||||
void setMarkdown(@Nullable String markdown);
|
|
||||||
void setMarkdown(@Nullable MarkwonConfiguration configuration, @Nullable String markdown);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
String getMarkdown();
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package ru.noties.markwon.view;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import ru.noties.markwon.MarkwonConfiguration;
|
|
||||||
|
|
||||||
@SuppressLint("AppCompatCustomView")
|
|
||||||
public class MarkwonView extends TextView implements IMarkwonView {
|
|
||||||
|
|
||||||
private MarkwonViewHelper helper;
|
|
||||||
|
|
||||||
public MarkwonView(Context context) {
|
|
||||||
super(context);
|
|
||||||
init(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MarkwonView(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
init(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(Context context, AttributeSet attributeSet) {
|
|
||||||
helper = MarkwonViewHelper.create(this);
|
|
||||||
helper.init(context, attributeSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setConfigurationProvider(@NonNull ConfigurationProvider provider) {
|
|
||||||
helper.setConfigurationProvider(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMarkdown(@Nullable String markdown) {
|
|
||||||
helper.setMarkdown(markdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMarkdown(@Nullable MarkwonConfiguration configuration, @Nullable String markdown) {
|
|
||||||
helper.setMarkdown(configuration, markdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getMarkdown() {
|
|
||||||
return helper.getMarkdown();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package ru.noties.markwon.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v7.widget.AppCompatTextView;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
|
|
||||||
import ru.noties.markwon.MarkwonConfiguration;
|
|
||||||
|
|
||||||
public class MarkwonViewCompat extends AppCompatTextView implements IMarkwonView {
|
|
||||||
|
|
||||||
private MarkwonViewHelper helper;
|
|
||||||
|
|
||||||
public MarkwonViewCompat(Context context) {
|
|
||||||
super(context);
|
|
||||||
init(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MarkwonViewCompat(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
init(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(Context context, AttributeSet attributeSet) {
|
|
||||||
helper = MarkwonViewHelper.create(this);
|
|
||||||
helper.init(context, attributeSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setConfigurationProvider(@NonNull ConfigurationProvider provider) {
|
|
||||||
helper.setConfigurationProvider(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setMarkdown(@Nullable String markdown) {
|
|
||||||
helper.setMarkdown(markdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setMarkdown(@Nullable MarkwonConfiguration configuration, @Nullable String markdown) {
|
|
||||||
helper.setMarkdown(configuration, markdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getMarkdown() {
|
|
||||||
return helper.getMarkdown();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
package ru.noties.markwon.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import ru.noties.markwon.Markwon;
|
|
||||||
import ru.noties.markwon.MarkwonConfiguration;
|
|
||||||
|
|
||||||
public class MarkwonViewHelper implements IMarkwonView {
|
|
||||||
|
|
||||||
public static <V extends TextView> MarkwonViewHelper create(@NonNull V view) {
|
|
||||||
return new MarkwonViewHelper(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final TextView textView;
|
|
||||||
|
|
||||||
private ConfigurationProvider provider;
|
|
||||||
|
|
||||||
private MarkwonConfiguration configuration;
|
|
||||||
private String markdown;
|
|
||||||
|
|
||||||
private MarkwonViewHelper(@NonNull TextView textView) {
|
|
||||||
this.textView = textView;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(Context context, AttributeSet attributeSet) {
|
|
||||||
|
|
||||||
if (attributeSet != null) {
|
|
||||||
final TypedArray array = context.obtainStyledAttributes(attributeSet, R.styleable.MarkwonView);
|
|
||||||
try {
|
|
||||||
|
|
||||||
final String configurationProvider = array.getString(R.styleable.MarkwonView_mv_configurationProvider);
|
|
||||||
final ConfigurationProvider provider;
|
|
||||||
if (!TextUtils.isEmpty(configurationProvider)) {
|
|
||||||
provider = MarkwonViewHelper.obtainProvider(configurationProvider);
|
|
||||||
} else {
|
|
||||||
provider = null;
|
|
||||||
}
|
|
||||||
if (provider != null) {
|
|
||||||
setConfigurationProvider(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String markdown = array.getString(R.styleable.MarkwonView_mv_markdown);
|
|
||||||
if (!TextUtils.isEmpty(markdown)) {
|
|
||||||
setMarkdown(markdown);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
array.recycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setConfigurationProvider(@NonNull ConfigurationProvider provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
this.configuration = provider.provide(textView.getContext());
|
|
||||||
if (!TextUtils.isEmpty(markdown)) {
|
|
||||||
// invalidate rendered markdown
|
|
||||||
setMarkdown(markdown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setMarkdown(@Nullable String markdown) {
|
|
||||||
setMarkdown(null, markdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setMarkdown(@Nullable MarkwonConfiguration configuration, @Nullable String markdown) {
|
|
||||||
this.markdown = markdown;
|
|
||||||
if (configuration == null) {
|
|
||||||
if (this.configuration == null) {
|
|
||||||
if (provider != null) {
|
|
||||||
this.configuration = provider.provide(textView.getContext());
|
|
||||||
} else {
|
|
||||||
this.configuration = MarkwonConfiguration.create(textView.getContext());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
configuration = this.configuration;
|
|
||||||
}
|
|
||||||
Markwon.setMarkdown(textView, configuration, markdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getMarkdown() {
|
|
||||||
return markdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static IMarkwonView.ConfigurationProvider obtainProvider(@NonNull String className) {
|
|
||||||
try {
|
|
||||||
final Class<?> cl = Class.forName(className);
|
|
||||||
return (IMarkwonView.ConfigurationProvider) cl.newInstance();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
t.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<declare-styleable name="MarkwonView">
|
|
||||||
<attr name="mv_configurationProvider" format="string" />
|
|
||||||
<attr name="mv_markdown" format="string" />
|
|
||||||
</declare-styleable>
|
|
||||||
|
|
||||||
</resources>
|
|
@ -1,26 +0,0 @@
|
|||||||
# Custom extension
|
|
||||||
|
|
||||||
This module provides a simple implementation for icons that are bundled in your application resources using custom `DelimiterProcessor`. It can be used as a reference when dealing with new functionality based on _delimiters_.
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Hello @ic-android-black-24
|
|
||||||
|
|
||||||
**Please** click @ic-home-green-24 (home icon) if you want to go home.
|
|
||||||
```
|
|
||||||
|
|
||||||
Here we will substitute elements starting with `@ic-` for icons that we have in our resources:
|
|
||||||
|
|
||||||
* `@ic-android-black-24` -> `R.drawable.ic_android_black_24dp`
|
|
||||||
* `@ic-home-green-24` -> `R.drawable.ic_home_green_24dp`
|
|
||||||
|
|
||||||
In order to provide reliable parsing we need to have delimiters _around_ desired content. So, `@ic-home-green-24` would become `@ic-home-green-24@`. This is current limitation of [commonmark-java](https://github.com/atlassian/commonmark-java) library that Markwon uses underneath. There is an ongoing [issue](https://github.com/atlassian/commonmark-java/issues/113) that might change this in future thought.
|
|
||||||
|
|
||||||
But as we known the pattern beforehand it's pretty easy to pre-process raw markdown and make it the way we want it. Please refer to `IconProcessor#process` method for the reference.
|
|
||||||
|
|
||||||
So, the our steps would be:
|
|
||||||
|
|
||||||
* prepare raw markdown (wrap icons with `@` if it's not already)
|
|
||||||
* construct a Parser with our registered delimiter processor
|
|
||||||
* parse markdown and obtain a `Node`
|
|
||||||
* create a node visitor that will additionally visit custom node (`IconNode`)
|
|
||||||
* use markdown
|
|
@ -1,29 +0,0 @@
|
|||||||
apply plugin: 'com.android.application'
|
|
||||||
|
|
||||||
android {
|
|
||||||
|
|
||||||
compileSdkVersion config['compile-sdk']
|
|
||||||
buildToolsVersion config['build-tools']
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
|
|
||||||
applicationId "ru.noties.markwon.sample.extension"
|
|
||||||
|
|
||||||
// using 21 as minimum only to be able to vector assets
|
|
||||||
minSdkVersion 21
|
|
||||||
targetSdkVersion config['target-sdk']
|
|
||||||
versionCode 1
|
|
||||||
versionName version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(':markwon-core')
|
|
||||||
|
|
||||||
implementation project(':markwon-image-svg')
|
|
||||||
implementation project(':markwon-recycler')
|
|
||||||
implementation project(':markwon-ext-tables')
|
|
||||||
implementation project(':markwon-html')
|
|
||||||
|
|
||||||
implementation deps['debug']
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="ru.noties.markwon.sample.extension">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:allowBackup="true"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:supportsRtl="true"
|
|
||||||
android:theme="@style/AppTheme"
|
|
||||||
tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon">
|
|
||||||
|
|
||||||
<activity android:name=".MainActivity">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".recycler.MarkwonRecyclerActivity"
|
|
||||||
android:exported="true" />
|
|
||||||
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
@ -1,313 +0,0 @@
|
|||||||

|
|
||||||
|
|
||||||
# Markwon
|
|
||||||
|
|
||||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon%22)
|
|
||||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-image-loader%22)
|
|
||||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-syntax-highlight%22)
|
|
||||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-view%22)
|
|
||||||
|
|
||||||
[](https://travis-ci.org/noties/Markwon)
|
|
||||||
|
|
||||||
**Markwon** is a markdown library for Android. It parses markdown
|
|
||||||
following [commonmark-spec] with the help of amazing [commonmark-java]
|
|
||||||
library and renders result as _Android-native_ Spannables. **No HTML**
|
|
||||||
is involved as an intermediate step. <u>**No WebView** is required</u>.
|
|
||||||
It's extremely fast, feature-rich and extensible.
|
|
||||||
|
|
||||||
It gives ability to display markdown in all TextView widgets
|
|
||||||
(**TextView**, **Button**, **Switch**, **CheckBox**, etc), **Toasts**
|
|
||||||
and all other places that accept **Spanned content**. Library provides
|
|
||||||
reasonable defaults to display style of a markdown content but also
|
|
||||||
gives all the means to tweak the appearance if desired. All markdown
|
|
||||||
features listed in [commonmark-spec] are supported
|
|
||||||
(including support for **inlined/block HTML code**, **markdown tables**,
|
|
||||||
**images** and **syntax highlight**).
|
|
||||||
|
|
||||||
[commonmark-spec]: https://spec.commonmark.org/0.28/
|
|
||||||
[commonmark-java]: https://github.com/atlassian/commonmark-java/blob/master/README.md
|
|
||||||
|
|
||||||
<sup>*</sup>*This file is displayed by default in the [sample-apk] (`markwon-sample-{latest-version}-debug.apk`) application. Which is a generic markdown viewer with support to display markdown via `http`, `https` & `file` schemes and 2 themes included: Light & Dark*
|
|
||||||
|
|
||||||
[sample-apk]: https://github.com/noties/Markwon/releases
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
```groovy
|
|
||||||
implementation "ru.noties:markwon:${markwonVersion}"
|
|
||||||
implementation "ru.noties:markwon-image-loader:${markwonVersion}" // optional
|
|
||||||
implementation "ru.noties:markwon-syntax-highlight:${markwonVersion}" // optional
|
|
||||||
implementation "ru.noties:markwon-view:${markwonVersion}" // optional
|
|
||||||
```
|
|
||||||
|
|
||||||
Please visit [documentation] web-site for further reference
|
|
||||||
|
|
||||||
## Supported markdown features:
|
|
||||||
* Emphasis (`*`, `_`)
|
|
||||||
* Strong emphasis (`**`, `__`)
|
|
||||||
* Strike-through (`~~`)
|
|
||||||
* Headers (`#{1,6}`)
|
|
||||||
* Links (`[]()` && `[][]`)
|
|
||||||
* Images
|
|
||||||
* Thematic break (`---`, `***`, `___`)
|
|
||||||
* Quotes & nested quotes (`>{1,}`)
|
|
||||||
* Ordered & non-ordered lists & nested ones
|
|
||||||
* Inline code
|
|
||||||
* Code blocks
|
|
||||||
* Tables (*with limitations*)
|
|
||||||
* Syntax highlight
|
|
||||||
* HTML
|
|
||||||
* Emphasis (`<i>`, `<em>`, `<cite>`, `<dfn>`)
|
|
||||||
* Strong emphasis (`<b>`, `<strong>`)
|
|
||||||
* SuperScript (`<sup>`)
|
|
||||||
* SubScript (`<sub>`)
|
|
||||||
* Underline (`<u>`, `ins`)
|
|
||||||
* Strike-through (`<s>`, `<strike>`, `<del>`)
|
|
||||||
* Link (`a`)
|
|
||||||
* Lists (`ul`, `ol`)
|
|
||||||
* Images (`img` will require configured image loader)
|
|
||||||
* Blockquote (`blockquote`)
|
|
||||||
* Heading (`h1`, `h2`, `h3`, `h4`, `h5`, `h6`)
|
|
||||||
* there is support to render any HTML tag
|
|
||||||
* Task lists:
|
|
||||||
- [ ] Not _done_
|
|
||||||
- [X] **Done** with `X`
|
|
||||||
- [x] ~~and~~ **or** small `x`
|
|
||||||
---
|
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
Taken with default configuration (except for image loading):
|
|
||||||
|
|
||||||
<a href="./art/mw_light_01.png"><img src="./art/mw_light_01.png" width="30%" /></a>
|
|
||||||
<a href="./art/mw_light_02.png"><img src="./art/mw_light_02.png" width="30%" /></a>
|
|
||||||
<a href="./art/mw_light_03.png"><img src="./art/mw_light_03.png" width="30%" /></a>
|
|
||||||
<a href="./art/mw_dark_01.png"><img src="./art/mw_dark_01.png" width="30%" /></a>
|
|
||||||
|
|
||||||
By default configuration uses TextView textColor for styling, so changing textColor changes style
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Demo
|
|
||||||
Based on [this cheatsheet][cheatsheet]
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Headers
|
|
||||||
---
|
|
||||||
# Header 1
|
|
||||||
## Header 2
|
|
||||||
### Header 3
|
|
||||||
#### Header 4
|
|
||||||
##### Header 5
|
|
||||||
###### Header 6
|
|
||||||
---
|
|
||||||
|
|
||||||
## Emphasis
|
|
||||||
|
|
||||||
Emphasis, aka italics, with *asterisks* or _underscores_.
|
|
||||||
|
|
||||||
Strong emphasis, aka bold, with **asterisks** or __underscores__.
|
|
||||||
|
|
||||||
Combined emphasis with **asterisks and _underscores_**.
|
|
||||||
|
|
||||||
Strikethrough uses two tildes. ~~Scratch this.~~
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Lists
|
|
||||||
1. First ordered list item
|
|
||||||
2. Another item
|
|
||||||
* Unordered sub-list.
|
|
||||||
1. Actual numbers don't matter, just that it's a number
|
|
||||||
1. Ordered sub-list
|
|
||||||
4. And another item.
|
|
||||||
|
|
||||||
You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown).
|
|
||||||
|
|
||||||
To have a line break without a paragraph, you will need to use two trailing spaces.
|
|
||||||
Note that this line is separate, but within the same paragraph.
|
|
||||||
(This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.)
|
|
||||||
|
|
||||||
* Unordered list can use asterisks
|
|
||||||
- Or minuses
|
|
||||||
+ Or pluses
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
[I'm an inline-style link](https://www.google.com)
|
|
||||||
|
|
||||||
[I'm a reference-style link][Arbitrary case-insensitive reference text]
|
|
||||||
|
|
||||||
[I'm a relative reference to a repository file](../blob/master/LICENSE)
|
|
||||||
|
|
||||||
[You can use numbers for reference-style link definitions][1]
|
|
||||||
|
|
||||||
Or leave it empty and use the [link text itself].
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code
|
|
||||||
|
|
||||||
Inline `code` has `back-ticks around` it.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var s = "JavaScript syntax highlighting";
|
|
||||||
alert(s);
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
s = "Python syntax highlighting"
|
|
||||||
print s
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* Helper method to obtain a Parser with registered strike-through & table extensions
|
|
||||||
* & task lists (added in 1.0.1)
|
|
||||||
*
|
|
||||||
* @return a Parser instance that is supported by this library
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public static Parser createParser() {
|
|
||||||
return new Parser.Builder()
|
|
||||||
.extensions(Arrays.asList(
|
|
||||||
StrikethroughExtension.create(),
|
|
||||||
TablesExtension.create(),
|
|
||||||
TaskListExtension.create()
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<ScrollView
|
|
||||||
android:id="@+id/scroll_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginTop="?android:attr/actionBarSize">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="16dip"
|
|
||||||
android:lineSpacingExtra="2dip"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:context="ru.noties.markwon.MainActivity"
|
|
||||||
tools:text="yo\nman" />
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
No language indicated, so no syntax highlighting.
|
|
||||||
But let's throw in a <b>tag</b>.
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tables
|
|
||||||
|
|
||||||
Colons can be used to align columns.
|
|
||||||
|
|
||||||
| Tables | Are | Cool |
|
|
||||||
| ------------- |:-------------:| -----:|
|
|
||||||
| col 3 is | right-aligned | $1600 |
|
|
||||||
| col 2 is | centered | $12 |
|
|
||||||
| zebra stripes | are neat | $1 |
|
|
||||||
|
|
||||||
There must be at least 3 dashes separating each header cell.
|
|
||||||
The outer pipes (|) are optional, and you don't need to make the
|
|
||||||
raw Markdown line up prettily. You can also use inline Markdown.
|
|
||||||
|
|
||||||
Markdown | Less | Pretty
|
|
||||||
--- | --- | ---
|
|
||||||
*Still* | `renders` | **nicely**
|
|
||||||
1 | 2 | 3
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Blockquotes
|
|
||||||
|
|
||||||
> Blockquotes are very handy in email to emulate reply text.
|
|
||||||
> This line is part of the same quote.
|
|
||||||
|
|
||||||
Quote break.
|
|
||||||
|
|
||||||
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
|
|
||||||
|
|
||||||
Nested quotes
|
|
||||||
> Hello!
|
|
||||||
>> And to you!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Inline HTML
|
|
||||||
|
|
||||||
```html
|
|
||||||
<u><i>H<sup>T<sub>M</sub></sup><b><s>L</s></b></i></u>
|
|
||||||
```
|
|
||||||
|
|
||||||
<u><i>H<sup>T<sub>M</sub></sup><b><s>L</s></b></i></u>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Horizontal Rule
|
|
||||||
|
|
||||||
Three or more...
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Hyphens (`-`)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
Asterisks (`*`)
|
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
Underscores (`_`)
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
```
|
|
||||||
Copyright 2017 Dimitry Ivanov (mail@dimitryivanov.ru)
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
```
|
|
||||||
|
|
||||||
[cheatsheet]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
|
|
||||||
|
|
||||||
[arbitrary case-insensitive reference text]: https://www.mozilla.org
|
|
||||||
[1]: http://slashdot.org
|
|
||||||
[link text itself]: http://www.reddit.com
|
|
@ -1,40 +0,0 @@
|
|||||||
package ru.noties.markwon.sample.extension;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import ru.noties.markwon.AbstractMarkwonPlugin;
|
|
||||||
import ru.noties.markwon.Markwon;
|
|
||||||
import ru.noties.markwon.core.MarkwonTheme;
|
|
||||||
|
|
||||||
public class MainActivity extends Activity {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_main);
|
|
||||||
|
|
||||||
final TextView textView = findViewById(R.id.text_view);
|
|
||||||
|
|
||||||
final Markwon markwon = Markwon.builder(this)
|
|
||||||
.usePlugin(IconPlugin.create(IconSpanProvider.create(this, 0)))
|
|
||||||
.usePlugin(new AbstractMarkwonPlugin() {
|
|
||||||
@Override
|
|
||||||
public void configureTheme(@NonNull MarkwonTheme.Builder builder) {
|
|
||||||
// this part has nothing to do with actual IconPlugin
|
|
||||||
// this part is used to showcase that headers can be controlled via Theme
|
|
||||||
final float[] textSizeMultipliers = new float[]{3f, 2f, 1.5f, 1f, .5f, .25f};
|
|
||||||
builder
|
|
||||||
.headingTypeface(Typeface.MONOSPACE)
|
|
||||||
.headingTextSizeMultipliers(textSizeMultipliers);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
markwon.setMarkdown(textView, getString(R.string.input));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
package ru.noties.markwon.sample.extension.recycler;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import org.commonmark.ext.gfm.tables.TableBlock;
|
|
||||||
import org.commonmark.node.FencedCodeBlock;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
import ru.noties.debug.AndroidLogDebugOutput;
|
|
||||||
import ru.noties.debug.Debug;
|
|
||||||
import ru.noties.markwon.AbstractMarkwonPlugin;
|
|
||||||
import ru.noties.markwon.Markwon;
|
|
||||||
import ru.noties.markwon.MarkwonConfiguration;
|
|
||||||
import ru.noties.markwon.core.CorePlugin;
|
|
||||||
import ru.noties.markwon.ext.tables.TablePlugin;
|
|
||||||
import ru.noties.markwon.html.HtmlPlugin;
|
|
||||||
import ru.noties.markwon.image.ImagesPlugin;
|
|
||||||
import ru.noties.markwon.image.svg.SvgPlugin;
|
|
||||||
import ru.noties.markwon.recycler.MarkwonAdapter;
|
|
||||||
import ru.noties.markwon.recycler.SimpleEntry;
|
|
||||||
import ru.noties.markwon.sample.extension.R;
|
|
||||||
import ru.noties.markwon.urlprocessor.UrlProcessor;
|
|
||||||
import ru.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute;
|
|
||||||
|
|
||||||
// we will create a standalone `sample` module with all these samples, right now this
|
|
||||||
// module has unrelated things
|
|
||||||
public class MarkwonRecyclerActivity extends Activity {
|
|
||||||
|
|
||||||
static {
|
|
||||||
Debug.init(new AndroidLogDebugOutput(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_recycler);
|
|
||||||
|
|
||||||
final MarkwonAdapter adapter = MarkwonAdapter.builder()
|
|
||||||
.include(FencedCodeBlock.class, new SimpleEntry(R.layout.adapter_fenced_code_block))
|
|
||||||
.include(TableBlock.class, new TableEntry())
|
|
||||||
.defaultEntry(new SimpleEntry(R.layout.adapter_default_entry))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
final RecyclerView recyclerView = findViewById(R.id.recycler_view);
|
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
|
||||||
recyclerView.setHasFixedSize(true);
|
|
||||||
recyclerView.setAdapter(adapter);
|
|
||||||
|
|
||||||
final Markwon markwon = markwon(this);
|
|
||||||
adapter.setMarkdown(markwon, loadReadMe(this));
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static Markwon markwon(@NonNull Context context) {
|
|
||||||
return Markwon.builder(context)
|
|
||||||
.usePlugin(CorePlugin.create())
|
|
||||||
.usePlugin(ImagesPlugin.createWithAssets(context))
|
|
||||||
.usePlugin(SvgPlugin.create(context.getResources()))
|
|
||||||
.usePlugin(TablePlugin.create(context))
|
|
||||||
.usePlugin(HtmlPlugin.create())
|
|
||||||
.usePlugin(new AbstractMarkwonPlugin() {
|
|
||||||
@Override
|
|
||||||
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
|
||||||
builder.urlProcessor(new UrlProcessorInitialReadme());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String loadReadMe(@NonNull Context context) {
|
|
||||||
InputStream stream = null;
|
|
||||||
try {
|
|
||||||
stream = context.getAssets().open("README.md");
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return readStream(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String readStream(@Nullable InputStream inputStream) {
|
|
||||||
|
|
||||||
String out = null;
|
|
||||||
|
|
||||||
if (inputStream != null) {
|
|
||||||
BufferedReader reader = null;
|
|
||||||
try {
|
|
||||||
reader = new BufferedReader(new InputStreamReader(inputStream));
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
builder.append(line)
|
|
||||||
.append('\n');
|
|
||||||
}
|
|
||||||
out = builder.toString();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
if (reader != null) {
|
|
||||||
try {
|
|
||||||
reader.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// no op
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class UrlProcessorInitialReadme implements UrlProcessor {
|
|
||||||
|
|
||||||
private static final String GITHUB_BASE = "https://github.com/noties/Markwon/raw/master/";
|
|
||||||
|
|
||||||
private final UrlProcessorRelativeToAbsolute processor
|
|
||||||
= new UrlProcessorRelativeToAbsolute(GITHUB_BASE);
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String process(@NonNull String destination) {
|
|
||||||
String out;
|
|
||||||
final Uri uri = Uri.parse(destination);
|
|
||||||
if (TextUtils.isEmpty(uri.getScheme())) {
|
|
||||||
out = processor.process(destination);
|
|
||||||
} else {
|
|
||||||
out = destination;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package ru.noties.markwon.sample.extension.recycler;
|
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import org.commonmark.ext.gfm.tables.TableBlock;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import ru.noties.markwon.Markwon;
|
|
||||||
import ru.noties.markwon.ext.tables.Table;
|
|
||||||
import ru.noties.markwon.recycler.MarkwonAdapter;
|
|
||||||
import ru.noties.markwon.sample.extension.R;
|
|
||||||
|
|
||||||
// do not use in real applications, this is just a showcase
|
|
||||||
public class TableEntry implements MarkwonAdapter.Entry<TableEntry.TableNodeHolder, TableBlock> {
|
|
||||||
|
|
||||||
private final Map<TableBlock, Table> cache = new HashMap<>(2);
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public TableNodeHolder createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
|
|
||||||
return new TableNodeHolder(inflater.inflate(R.layout.adapter_table_block, parent, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindHolder(@NonNull Markwon markwon, @NonNull TableNodeHolder holder, @NonNull TableBlock node) {
|
|
||||||
|
|
||||||
Table table = cache.get(node);
|
|
||||||
if (table == null) {
|
|
||||||
table = Table.parse(markwon, node);
|
|
||||||
cache.put(node, table);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table != null) {
|
|
||||||
holder.tableEntryView.setTable(table);
|
|
||||||
// render table
|
|
||||||
} // we need to do something with null table...
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long id(@NonNull TableBlock node) {
|
|
||||||
return node.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
cache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TableNodeHolder extends MarkwonAdapter.Holder {
|
|
||||||
|
|
||||||
final TableEntryView tableEntryView;
|
|
||||||
|
|
||||||
TableNodeHolder(@NonNull View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
|
|
||||||
this.tableEntryView = requireView(R.id.table_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,205 +0,0 @@
|
|||||||
package ru.noties.markwon.sample.extension.recycler;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.text.SpannedString;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ru.noties.markwon.ext.tables.Table;
|
|
||||||
import ru.noties.markwon.sample.extension.R;
|
|
||||||
|
|
||||||
public class TableEntryView extends LinearLayout {
|
|
||||||
|
|
||||||
// paint and rect to draw borders
|
|
||||||
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
private final Rect rect = new Rect();
|
|
||||||
|
|
||||||
private LayoutInflater inflater;
|
|
||||||
|
|
||||||
private int rowEvenBackgroundColor;
|
|
||||||
|
|
||||||
public TableEntryView(Context context) {
|
|
||||||
super(context);
|
|
||||||
init(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TableEntryView(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
init(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(Context context, @Nullable AttributeSet attrs) {
|
|
||||||
inflater = LayoutInflater.from(context);
|
|
||||||
setOrientation(VERTICAL);
|
|
||||||
|
|
||||||
if (attrs != null) {
|
|
||||||
final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TableEntryView);
|
|
||||||
try {
|
|
||||||
|
|
||||||
rowEvenBackgroundColor = array.getColor(R.styleable.TableEntryView_tev_rowEvenBackgroundColor, 0);
|
|
||||||
|
|
||||||
|
|
||||||
final int stroke = array.getDimensionPixelSize(R.styleable.TableEntryView_tev_borderWidth, 0);
|
|
||||||
|
|
||||||
// half of requested
|
|
||||||
final float strokeWidth = stroke > 0
|
|
||||||
? stroke / 2.F
|
|
||||||
: context.getResources().getDisplayMetrics().density / 2.F;
|
|
||||||
|
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
|
||||||
paint.setStrokeWidth(strokeWidth);
|
|
||||||
paint.setColor(array.getColor(R.styleable.TableEntryView_tev_borderColor, Color.BLACK));
|
|
||||||
|
|
||||||
if (isInEditMode()) {
|
|
||||||
final String data = array.getString(R.styleable.TableEntryView_tev_debugData);
|
|
||||||
if (data != null) {
|
|
||||||
|
|
||||||
boolean first = true;
|
|
||||||
|
|
||||||
final List<Table.Row> rows = new ArrayList<>();
|
|
||||||
for (String row : data.split("\\|")) {
|
|
||||||
final List<Table.Column> columns = new ArrayList<>();
|
|
||||||
for (String column : row.split(",")) {
|
|
||||||
columns.add(new Table.Column(Table.Alignment.LEFT, new SpannedString(column)));
|
|
||||||
}
|
|
||||||
final boolean header = first;
|
|
||||||
first = false;
|
|
||||||
rows.add(new Table.Row(header, columns));
|
|
||||||
}
|
|
||||||
final Table table = new Table(rows);
|
|
||||||
setTable(table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
array.recycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setWillNotDraw(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTable(@NonNull Table table) {
|
|
||||||
final List<Table.Row> rows = table.rows();
|
|
||||||
for (int i = 0, size = rows.size(); i < size; i++) {
|
|
||||||
addRow(i, rows.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addRow(int index, @NonNull Table.Row row) {
|
|
||||||
|
|
||||||
final ViewGroup group = ensureRow(index);
|
|
||||||
|
|
||||||
final int backgroundColor = !row.header() && (index % 2) == 0
|
|
||||||
? rowEvenBackgroundColor
|
|
||||||
: 0;
|
|
||||||
group.setBackgroundColor(backgroundColor);
|
|
||||||
|
|
||||||
final List<Table.Column> columns = row.columns();
|
|
||||||
|
|
||||||
TextView textView;
|
|
||||||
Table.Column column;
|
|
||||||
|
|
||||||
for (int i = 0, size = columns.size(); i < size; i++) {
|
|
||||||
textView = ensureCell(group, i);
|
|
||||||
column = columns.get(i);
|
|
||||||
textView.setTextAlignment(textAlignment(column.alignment()));
|
|
||||||
textView.setText(column.content());
|
|
||||||
textView.getPaint().setFakeBoldText(row.header());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private ViewGroup ensureRow(int index) {
|
|
||||||
|
|
||||||
final int count = getChildCount();
|
|
||||||
if (index >= count) {
|
|
||||||
|
|
||||||
// count=0,index=1, diff=2
|
|
||||||
// count=0,index=5, diff=6
|
|
||||||
// count=1,index=2, diff=2
|
|
||||||
int diff = index - count + 1;
|
|
||||||
while (diff > 0) {
|
|
||||||
addView(inflater.inflate(R.layout.view_table_entry_row, this, false));
|
|
||||||
diff -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ViewGroup) getChildAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private TextView ensureCell(@NonNull ViewGroup group, int index) {
|
|
||||||
|
|
||||||
final int count = group.getChildCount();
|
|
||||||
if (index >= count) {
|
|
||||||
int diff = index - count + 1;
|
|
||||||
while (diff > 0) {
|
|
||||||
group.addView(inflater.inflate(R.layout.view_table_entry_cell, group, false));
|
|
||||||
diff -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (TextView) group.getChildAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
super.onDraw(canvas);
|
|
||||||
|
|
||||||
final int rows = getChildCount();
|
|
||||||
if (rows == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// first draw the whole border
|
|
||||||
rect.set(0, 0, getWidth(), getHeight());
|
|
||||||
canvas.drawRect(rect, paint);
|
|
||||||
|
|
||||||
ViewGroup group;
|
|
||||||
View view;
|
|
||||||
|
|
||||||
int top;
|
|
||||||
|
|
||||||
for (int row = 0; row < rows; row++) {
|
|
||||||
group = (ViewGroup) getChildAt(row);
|
|
||||||
top = group.getTop();
|
|
||||||
for (int col = 0, cols = group.getChildCount(); col < cols; col++) {
|
|
||||||
view = group.getChildAt(col);
|
|
||||||
rect.set(view.getLeft(), top + view.getTop(), view.getRight(), top + view.getBottom());
|
|
||||||
canvas.drawRect(rect, paint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int textAlignment(@NonNull Table.Alignment alignment) {
|
|
||||||
final int out;
|
|
||||||
switch (alignment) {
|
|
||||||
case LEFT:
|
|
||||||
out = TextView.TEXT_ALIGNMENT_TEXT_START;
|
|
||||||
break;
|
|
||||||
case CENTER:
|
|
||||||
out = TextView.TEXT_ALIGNMENT_CENTER;
|
|
||||||
break;
|
|
||||||
case RIGHT:
|
|
||||||
out = TextView.TEXT_ALIGNMENT_TEXT_END;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unexpected alignment: " + alignment);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/text"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="16dip"
|
|
||||||
android:layout_marginRight="16dip"
|
|
||||||
android:lineSpacingExtra="2dip"
|
|
||||||
android:paddingTop="8dip"
|
|
||||||
android:paddingBottom="8dip"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:textColor="#000"
|
|
||||||
android:textSize="16sp" />
|
|
@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clipChildren="false"
|
|
||||||
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:lineSpacingExtra="2dip"
|
|
||||||
android:paddingTop="8dip"
|
|
||||||
android:paddingBottom="8dip"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="# Hello there! and taskjs" />
|
|
||||||
|
|
||||||
</HorizontalScrollView>
|
|
@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"-->
|
|
||||||
<!--xmlns:app="http://schemas.android.com/apk/res-auto"-->
|
|
||||||
<!--android:layout_width="match_parent"-->
|
|
||||||
<!--android:layout_height="wrap_content"-->
|
|
||||||
<!--android:clipChildren="false"-->
|
|
||||||
<!--android:clipToPadding="false"-->
|
|
||||||
<!--android:fillViewport="true"-->
|
|
||||||
<!--android:paddingLeft="16dip"-->
|
|
||||||
<!--android:paddingTop="8dip"-->
|
|
||||||
<!--android:paddingRight="16dip"-->
|
|
||||||
<!--android:paddingBottom="8dip">-->
|
|
||||||
|
|
||||||
<ru.noties.markwon.sample.extension.recycler.TableEntryView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/table_entry"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:tev_debugData="head1,head2,head3|col1,col2,col3|col1,col2,col3|col1,col2,col3"
|
|
||||||
app:tev_rowEvenBackgroundColor="#40ff0000" />
|
|
||||||
|
|
||||||
<!--</HorizontalScrollView>-->
|
|
@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="0px"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:padding="4dip"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:textColor="#000"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="Table content" />
|
|
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal" />
|
|
@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<declare-styleable name="TableEntryView">
|
|
||||||
<attr name="tev_rowEvenBackgroundColor" format="color" />
|
|
||||||
<attr name="tev_borderColor" format="color" />
|
|
||||||
<attr name="tev_borderWidth" format="dimension" />
|
|
||||||
<attr name="tev_debugData" format="string" />
|
|
||||||
</declare-styleable>
|
|
||||||
</resources>
|
|
@ -1,14 +0,0 @@
|
|||||||
<resources>
|
|
||||||
|
|
||||||
<string name="app_name">Markwon-SampleCustomExtension</string>
|
|
||||||
|
|
||||||
<string name="input"><![CDATA[
|
|
||||||
# Hello! @ic-android-black-24\n\n
|
|
||||||
Home 36 black: @ic-home-black-36\n\n
|
|
||||||
Memory 48 black: @ic-memory-black-48\n\n
|
|
||||||
### I AM ANOTHER HEADER\n\n
|
|
||||||
Sentiment Satisfied 64 red: @ic-sentiment_satisfied-red-64
|
|
||||||
]]>
|
|
||||||
</string>
|
|
||||||
|
|
||||||
</resources>
|
|
@ -1,5 +0,0 @@
|
|||||||
<resources>
|
|
||||||
|
|
||||||
<style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar"/>
|
|
||||||
|
|
||||||
</resources>
|
|
@ -1,24 +0,0 @@
|
|||||||
apply plugin: 'com.android.application'
|
|
||||||
|
|
||||||
android {
|
|
||||||
|
|
||||||
compileSdkVersion config['compile-sdk']
|
|
||||||
buildToolsVersion config['build-tools']
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
|
|
||||||
applicationId "ru.noties.markwon.sample.jlatexmath"
|
|
||||||
|
|
||||||
minSdkVersion config['min-sdk']
|
|
||||||
targetSdkVersion config['target-sdk']
|
|
||||||
versionCode 1
|
|
||||||
versionName version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(':markwon-core')
|
|
||||||
implementation project(':markwon-ext-latex')
|
|
||||||
// implementation project(':markwon-image-loader')
|
|
||||||
// implementation 'ru.noties:jlatexmath-android:0.1.0'
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
package="ru.noties.markwon.sample.jlatexmath">
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:allowBackup="true"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:supportsRtl="true"
|
|
||||||
android:theme="@style/AppTheme">
|
|
||||||
<activity android:name=".MainActivity">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
@ -1,58 +0,0 @@
|
|||||||
package ru.noties.markwon.sample.jlatexmath;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import ru.noties.markwon.Markwon;
|
|
||||||
import ru.noties.markwon.core.CorePlugin;
|
|
||||||
import ru.noties.markwon.ext.latex.JLatexMathPlugin;
|
|
||||||
import ru.noties.markwon.image.ImagesPlugin;
|
|
||||||
|
|
||||||
public class MainActivity extends Activity {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_main);
|
|
||||||
|
|
||||||
final TextView textView = findViewById(R.id.text_view);
|
|
||||||
|
|
||||||
String latex = "\\begin{array}{l}";
|
|
||||||
latex += "\\forall\\varepsilon\\in\\mathbb{R}_+^*\\ \\exists\\eta>0\\ |x-x_0|\\leq\\eta\\Longrightarrow|f(x)-f(x_0)|\\leq\\varepsilon\\\\";
|
|
||||||
latex += "\\det\\begin{bmatrix}a_{11}&a_{12}&\\cdots&a_{1n}\\\\a_{21}&\\ddots&&\\vdots\\\\\\vdots&&\\ddots&\\vdots\\\\a_{n1}&\\cdots&\\cdots&a_{nn}\\end{bmatrix}\\overset{\\mathrm{def}}{=}\\sum_{\\sigma\\in\\mathfrak{S}_n}\\varepsilon(\\sigma)\\prod_{k=1}^n a_{k\\sigma(k)}\\\\";
|
|
||||||
latex += "\\sideset{_\\alpha^\\beta}{_\\gamma^\\delta}{\\begin{pmatrix}a&b\\\\c&d\\end{pmatrix}}\\\\";
|
|
||||||
latex += "\\int_0^\\infty{x^{2n} e^{-a x^2}\\,dx} = \\frac{2n-1}{2a} \\int_0^\\infty{x^{2(n-1)} e^{-a x^2}\\,dx} = \\frac{(2n-1)!!}{2^{n+1}} \\sqrt{\\frac{\\pi}{a^{2n+1}}}\\\\";
|
|
||||||
latex += "\\int_a^b{f(x)\\,dx} = (b - a) \\sum\\limits_{n = 1}^\\infty {\\sum\\limits_{m = 1}^{2^n - 1} {\\left( { - 1} \\right)^{m + 1} } } 2^{ - n} f(a + m\\left( {b - a} \\right)2^{-n} )\\\\";
|
|
||||||
latex += "\\int_{-\\pi}^{\\pi} \\sin(\\alpha x) \\sin^n(\\beta x) dx = \\textstyle{\\left \\{ \\begin{array}{cc} (-1)^{(n+1)/2} (-1)^m \\frac{2 \\pi}{2^n} \\binom{n}{m} & n \\mbox{ odd},\\ \\alpha = \\beta (2m-n) \\\\ 0 & \\mbox{otherwise} \\\\ \\end{array} \\right .}\\\\";
|
|
||||||
latex += "L = \\int_a^b \\sqrt{ \\left|\\sum_{i,j=1}^ng_{ij}(\\gamma(t))\\left(\\frac{d}{dt}x^i\\circ\\gamma(t)\\right)\\left(\\frac{d}{dt}x^j\\circ\\gamma(t)\\right)\\right|}\\,dt\\\\";
|
|
||||||
latex += "\\begin{array}{rl} s &= \\int_a^b\\left\\|\\frac{d}{dt}\\vec{r}\\,(u(t),v(t))\\right\\|\\,dt \\\\ &= \\int_a^b \\sqrt{u'(t)^2\\,\\vec{r}_u\\cdot\\vec{r}_u + 2u'(t)v'(t)\\, \\vec{r}_u\\cdot\\vec{r}_v+ v'(t)^2\\,\\vec{r}_v\\cdot\\vec{r}_v}\\,\\,\\, dt. \\end{array}\\\\";
|
|
||||||
latex += "\\end{array}";
|
|
||||||
|
|
||||||
// String latex = "\\text{A long division \\longdiv{12345}{13}";
|
|
||||||
// String latex = "{a \\bangle b} {c \\brace d} {e \\brack f} {g \\choose h}";
|
|
||||||
|
|
||||||
// String latex = "\\begin{array}{cc}";
|
|
||||||
// latex += "\\fbox{\\text{A framed box with \\textdbend}}&\\shadowbox{\\text{A shadowed box}}\\cr";
|
|
||||||
// latex += "\\doublebox{\\text{A double framed box}}&\\ovalbox{\\text{An oval framed box}}\\cr";
|
|
||||||
// latex += "\\end{array}";
|
|
||||||
|
|
||||||
|
|
||||||
final JLatexMathPlugin.Config config = new JLatexMathPlugin.Config(textView.getTextSize()) {{
|
|
||||||
// align = JLatexMathDrawable.ALIGN_RIGHT;
|
|
||||||
}};
|
|
||||||
|
|
||||||
final String markdown = "# Example of LaTeX\n\n$$"
|
|
||||||
+ latex + "$$\n\n something like **this**";
|
|
||||||
|
|
||||||
final Markwon markwon = Markwon.builder(this)
|
|
||||||
.usePlugin(CorePlugin.create())
|
|
||||||
// strictly speaking this one is not required as long as JLatexMathPlugin schedules
|
|
||||||
// drawables on it's own
|
|
||||||
.usePlugin(ImagesPlugin.create(this))
|
|
||||||
.usePlugin(JLatexMathPlugin.create(config))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
markwon.setMarkdown(textView, markdown);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipChildren="false"
|
|
||||||
android:clipToPadding="false">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="16dip" />
|
|
||||||
|
|
||||||
</ScrollView>
|
|
@ -1,3 +0,0 @@
|
|||||||
<resources>
|
|
||||||
<string name="app_name">Markwon-JLatexMath</string>
|
|
||||||
</resources>
|
|
@ -1,6 +0,0 @@
|
|||||||
<resources>
|
|
||||||
|
|
||||||
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</resources>
|
|
@ -42,6 +42,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation project(':markwon-core')
|
implementation project(':markwon-core')
|
||||||
|
implementation project(':markwon-ext-latex')
|
||||||
implementation project(':markwon-ext-strikethrough')
|
implementation project(':markwon-ext-strikethrough')
|
||||||
implementation project(':markwon-ext-tables')
|
implementation project(':markwon-ext-tables')
|
||||||
implementation project(':markwon-ext-tasklist')
|
implementation project(':markwon-ext-tasklist')
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
<activity android:name=".customextension.CustomExtensionActivity" />
|
<activity android:name=".customextension.CustomExtensionActivity" />
|
||||||
<activity android:name=".basicplugins.BasicPluginsActivity" />
|
<activity android:name=".basicplugins.BasicPluginsActivity" />
|
||||||
<activity android:name=".recycler.RecyclerActivity" />
|
<activity android:name=".recycler.RecyclerActivity" />
|
||||||
|
<activity android:name=".theme.ThemeActivity" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
@ -1,6 +1,36 @@
|
|||||||
package ru.noties.markwon.sample.customextension;
|
package ru.noties.markwon.sample.customextension;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import ru.noties.markwon.Markwon;
|
||||||
|
import ru.noties.markwon.image.ImagesPlugin;
|
||||||
|
import ru.noties.markwon.sample.R;
|
||||||
|
|
||||||
public class CustomExtensionActivity extends Activity {
|
public class CustomExtensionActivity extends Activity {
|
||||||
|
|
||||||
|
// please note that this sample won't work on a device with SDK level < 21
|
||||||
|
// as we are using vector drawables for the sake of brevity. Other than resources
|
||||||
|
// used, this is fully functional sample on all SDK levels
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_text_view);
|
||||||
|
|
||||||
|
final TextView textView = findViewById(R.id.text_view);
|
||||||
|
|
||||||
|
// note that we haven't registered CorePlugin, as it's the only one that can be
|
||||||
|
// implicitly deducted and added automatically. All other plugins require explicit
|
||||||
|
// `usePlugin` call
|
||||||
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
// try commenting out this line to see runtime dependency resolution
|
||||||
|
.usePlugin(ImagesPlugin.create(this))
|
||||||
|
.usePlugin(IconPlugin.create(IconSpanProvider.create(this, 0)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
markwon.setMarkdown(textView, getString(R.string.input));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package ru.noties.markwon.sample.extension;
|
package ru.noties.markwon.sample.customextension;
|
||||||
|
|
||||||
import org.commonmark.node.CustomNode;
|
import org.commonmark.node.CustomNode;
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package ru.noties.markwon.sample.extension;
|
package ru.noties.markwon.sample.customextension;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package ru.noties.markwon.sample.extension;
|
package ru.noties.markwon.sample.customextension;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@ -7,6 +7,8 @@ import org.commonmark.parser.Parser;
|
|||||||
|
|
||||||
import ru.noties.markwon.AbstractMarkwonPlugin;
|
import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import ru.noties.markwon.MarkwonVisitor;
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
import ru.noties.markwon.image.ImagesPlugin;
|
||||||
|
import ru.noties.markwon.priority.Priority;
|
||||||
|
|
||||||
public class IconPlugin extends AbstractMarkwonPlugin {
|
public class IconPlugin extends AbstractMarkwonPlugin {
|
||||||
|
|
||||||
@ -21,6 +23,13 @@ public class IconPlugin extends AbstractMarkwonPlugin {
|
|||||||
this.iconSpanProvider = iconSpanProvider;
|
this.iconSpanProvider = iconSpanProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Priority priority() {
|
||||||
|
// define images dependency
|
||||||
|
return Priority.after(ImagesPlugin.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureParser(@NonNull Parser.Builder builder) {
|
public void configureParser(@NonNull Parser.Builder builder) {
|
||||||
builder.customDelimiterProcessor(IconProcessor.create());
|
builder.customDelimiterProcessor(IconProcessor.create());
|
@ -1,4 +1,4 @@
|
|||||||
package ru.noties.markwon.sample.extension;
|
package ru.noties.markwon.sample.customextension;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
@ -1,4 +1,4 @@
|
|||||||
package ru.noties.markwon.sample.extension;
|
package ru.noties.markwon.sample.customextension;
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
@ -1,4 +1,4 @@
|
|||||||
package ru.noties.markwon.sample.extension;
|
package ru.noties.markwon.sample.customextension;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
@ -3,6 +3,13 @@ package ru.noties.markwon.sample.latex;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import ru.noties.markwon.Markwon;
|
||||||
|
import ru.noties.markwon.core.CorePlugin;
|
||||||
|
import ru.noties.markwon.ext.latex.JLatexMathPlugin;
|
||||||
|
import ru.noties.markwon.image.ImagesPlugin;
|
||||||
|
import ru.noties.markwon.sample.R;
|
||||||
|
|
||||||
public class LatexActivity extends Activity {
|
public class LatexActivity extends Activity {
|
||||||
|
|
||||||
@ -10,6 +17,42 @@ public class LatexActivity extends Activity {
|
|||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_text_view);
|
||||||
|
|
||||||
|
final TextView textView = findViewById(R.id.text_view);
|
||||||
|
|
||||||
|
String latex = "\\begin{array}{l}";
|
||||||
|
latex += "\\forall\\varepsilon\\in\\mathbb{R}_+^*\\ \\exists\\eta>0\\ |x-x_0|\\leq\\eta\\Longrightarrow|f(x)-f(x_0)|\\leq\\varepsilon\\\\";
|
||||||
|
latex += "\\det\\begin{bmatrix}a_{11}&a_{12}&\\cdots&a_{1n}\\\\a_{21}&\\ddots&&\\vdots\\\\\\vdots&&\\ddots&\\vdots\\\\a_{n1}&\\cdots&\\cdots&a_{nn}\\end{bmatrix}\\overset{\\mathrm{def}}{=}\\sum_{\\sigma\\in\\mathfrak{S}_n}\\varepsilon(\\sigma)\\prod_{k=1}^n a_{k\\sigma(k)}\\\\";
|
||||||
|
latex += "\\sideset{_\\alpha^\\beta}{_\\gamma^\\delta}{\\begin{pmatrix}a&b\\\\c&d\\end{pmatrix}}\\\\";
|
||||||
|
latex += "\\int_0^\\infty{x^{2n} e^{-a x^2}\\,dx} = \\frac{2n-1}{2a} \\int_0^\\infty{x^{2(n-1)} e^{-a x^2}\\,dx} = \\frac{(2n-1)!!}{2^{n+1}} \\sqrt{\\frac{\\pi}{a^{2n+1}}}\\\\";
|
||||||
|
latex += "\\int_a^b{f(x)\\,dx} = (b - a) \\sum\\limits_{n = 1}^\\infty {\\sum\\limits_{m = 1}^{2^n - 1} {\\left( { - 1} \\right)^{m + 1} } } 2^{ - n} f(a + m\\left( {b - a} \\right)2^{-n} )\\\\";
|
||||||
|
latex += "\\int_{-\\pi}^{\\pi} \\sin(\\alpha x) \\sin^n(\\beta x) dx = \\textstyle{\\left \\{ \\begin{array}{cc} (-1)^{(n+1)/2} (-1)^m \\frac{2 \\pi}{2^n} \\binom{n}{m} & n \\mbox{ odd},\\ \\alpha = \\beta (2m-n) \\\\ 0 & \\mbox{otherwise} \\\\ \\end{array} \\right .}\\\\";
|
||||||
|
latex += "L = \\int_a^b \\sqrt{ \\left|\\sum_{i,j=1}^ng_{ij}(\\gamma(t))\\left(\\frac{d}{dt}x^i\\circ\\gamma(t)\\right)\\left(\\frac{d}{dt}x^j\\circ\\gamma(t)\\right)\\right|}\\,dt\\\\";
|
||||||
|
latex += "\\begin{array}{rl} s &= \\int_a^b\\left\\|\\frac{d}{dt}\\vec{r}\\,(u(t),v(t))\\right\\|\\,dt \\\\ &= \\int_a^b \\sqrt{u'(t)^2\\,\\vec{r}_u\\cdot\\vec{r}_u + 2u'(t)v'(t)\\, \\vec{r}_u\\cdot\\vec{r}_v+ v'(t)^2\\,\\vec{r}_v\\cdot\\vec{r}_v}\\,\\,\\, dt. \\end{array}\\\\";
|
||||||
|
latex += "\\end{array}";
|
||||||
|
|
||||||
|
// String latex = "\\text{A long division \\longdiv{12345}{13}";
|
||||||
|
// String latex = "{a \\bangle b} {c \\brace d} {e \\brack f} {g \\choose h}";
|
||||||
|
|
||||||
|
// String latex = "\\begin{array}{cc}";
|
||||||
|
// latex += "\\fbox{\\text{A framed box with \\textdbend}}&\\shadowbox{\\text{A shadowed box}}\\cr";
|
||||||
|
// latex += "\\doublebox{\\text{A double framed box}}&\\ovalbox{\\text{An oval framed box}}\\cr";
|
||||||
|
// latex += "\\end{array}";
|
||||||
|
|
||||||
|
|
||||||
|
final JLatexMathPlugin.Config config = new JLatexMathPlugin.Config(textView.getTextSize()) {{
|
||||||
|
// align = JLatexMathDrawable.ALIGN_RIGHT;
|
||||||
|
}};
|
||||||
|
|
||||||
|
final String markdown = "# Example of LaTeX\n\n$$"
|
||||||
|
+ latex + "$$\n\n something like **this**";
|
||||||
|
|
||||||
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
.usePlugin(ImagesPlugin.create(this))
|
||||||
|
.usePlugin(JLatexMathPlugin.create(config))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
markwon.setMarkdown(textView, markdown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import ru.noties.markwon.image.svg.SvgPlugin;
|
|||||||
import ru.noties.markwon.recycler.MarkwonAdapter;
|
import ru.noties.markwon.recycler.MarkwonAdapter;
|
||||||
import ru.noties.markwon.recycler.SimpleEntry;
|
import ru.noties.markwon.recycler.SimpleEntry;
|
||||||
import ru.noties.markwon.sample.R;
|
import ru.noties.markwon.sample.R;
|
||||||
|
import ru.noties.markwon.syntax.SyntaxHighlightPlugin;
|
||||||
import ru.noties.markwon.urlprocessor.UrlProcessor;
|
import ru.noties.markwon.urlprocessor.UrlProcessor;
|
||||||
import ru.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute;
|
import ru.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute;
|
||||||
|
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package ru.noties.markwon.sample.theme;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.markwon.sample.R;
|
||||||
|
|
||||||
|
public class ThemeActivity extends Activity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_text_view);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<ScrollView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@ -12,6 +10,6 @@
|
|||||||
android:padding="8dip"
|
android:padding="8dip"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
tools:text="@string/input"/>
|
tools:text="whatever" />
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
@ -1,3 +1,14 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<string name="app_name">MarkwonSample</string>
|
<string name="app_name">MarkwonSample</string>
|
||||||
|
|
||||||
|
<string name="input"><![CDATA[
|
||||||
|
# Hello! @ic-android-black-24\n\n
|
||||||
|
Home 36 black: @ic-home-black-36\n\n
|
||||||
|
Memory 48 black: @ic-memory-black-48\n\n
|
||||||
|
### I AM ANOTHER HEADER\n\n
|
||||||
|
Sentiment Satisfied 64 red: @ic-sentiment_satisfied-red-64
|
||||||
|
]]>
|
||||||
|
</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -11,6 +11,4 @@ include ':app', ':sample',
|
|||||||
':markwon-image-svg',
|
':markwon-image-svg',
|
||||||
':markwon-recycler',
|
':markwon-recycler',
|
||||||
':markwon-syntax-highlight',
|
':markwon-syntax-highlight',
|
||||||
':markwon-test-span',
|
':markwon-test-span'
|
||||||
':sample-custom-extension',
|
|
||||||
':sample-latex-math'
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user