Add sample-latex-math module
This commit is contained in:
parent
fde9712454
commit
073f41b5f6
23
sample-latex-math/build.gradle
Normal file
23
sample-latex-math/build.gradle
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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')
|
||||||
|
implementation project(':markwon-image-loader')
|
||||||
|
implementation 'ru.noties:jlatexmath-android:0.1.0'
|
||||||
|
}
|
18
sample-latex-math/src/main/AndroidManifest.xml
Normal file
18
sample-latex-math/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?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>
|
@ -0,0 +1,16 @@
|
|||||||
|
package ru.noties.markwon.sample.jlatexmath;
|
||||||
|
|
||||||
|
import org.commonmark.node.CustomBlock;
|
||||||
|
|
||||||
|
public class JLatexMathBlock extends CustomBlock {
|
||||||
|
|
||||||
|
private String latex;
|
||||||
|
|
||||||
|
public String latex() {
|
||||||
|
return latex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void latex(String latex) {
|
||||||
|
this.latex = latex;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package ru.noties.markwon.sample.jlatexmath;
|
||||||
|
|
||||||
|
import org.commonmark.node.Block;
|
||||||
|
import org.commonmark.parser.block.AbstractBlockParser;
|
||||||
|
import org.commonmark.parser.block.AbstractBlockParserFactory;
|
||||||
|
import org.commonmark.parser.block.BlockContinue;
|
||||||
|
import org.commonmark.parser.block.BlockStart;
|
||||||
|
import org.commonmark.parser.block.MatchedBlockParser;
|
||||||
|
import org.commonmark.parser.block.ParserState;
|
||||||
|
|
||||||
|
public class JLatexMathBlockParser extends AbstractBlockParser {
|
||||||
|
|
||||||
|
private final JLatexMathBlock block = new JLatexMathBlock();
|
||||||
|
|
||||||
|
private final StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
private boolean isClosed;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Block getBlock() {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockContinue tryContinue(ParserState parserState) {
|
||||||
|
|
||||||
|
if (isClosed) {
|
||||||
|
return BlockContinue.finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlockContinue.atIndex(parserState.getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLine(CharSequence line) {
|
||||||
|
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(line);
|
||||||
|
|
||||||
|
final int length = builder.length();
|
||||||
|
if (length > 1) {
|
||||||
|
isClosed = '$' == builder.charAt(length - 1)
|
||||||
|
&& '$' == builder.charAt(length - 2);
|
||||||
|
if (isClosed) {
|
||||||
|
builder.replace(length - 2, length, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeBlock() {
|
||||||
|
block.latex(builder.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory extends AbstractBlockParserFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
|
||||||
|
|
||||||
|
final CharSequence line = state.getLine();
|
||||||
|
final int length = line != null
|
||||||
|
? line.length()
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
if (length > 1) {
|
||||||
|
if ('$' == line.charAt(0)
|
||||||
|
&& '$' == line.charAt(1)) {
|
||||||
|
return BlockStart.of(new JLatexMathBlockParser())
|
||||||
|
.atIndex(state.getIndex() + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlockStart.none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
package ru.noties.markwon.sample.jlatexmath;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import ru.noties.jlatexmath.JLatexMathDrawable;
|
||||||
|
import ru.noties.markwon.il.ImageItem;
|
||||||
|
import ru.noties.markwon.il.MediaDecoder;
|
||||||
|
import ru.noties.markwon.il.SchemeHandler;
|
||||||
|
|
||||||
|
public class JLatexMathMedia {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public Config(float textSize) {
|
||||||
|
this.textSize = textSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String SCHEME = "jlatexmath";
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static String makeDestination(@NonNull String latex) {
|
||||||
|
return SCHEME + "://" + latex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String CONTENT_TYPE = "text/jlatexmath";
|
||||||
|
|
||||||
|
private final Config config;
|
||||||
|
|
||||||
|
public JLatexMathMedia(@NonNull Config config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public SchemeHandler schemeHandler() {
|
||||||
|
return new SchemeHandlerImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public MediaDecoder mediaDecoder() {
|
||||||
|
return new MediaDecoderImpl(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SchemeHandlerImpl extends SchemeHandler {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
|
||||||
|
|
||||||
|
Log.e("handle", raw);
|
||||||
|
|
||||||
|
ImageItem item = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final byte[] bytes = raw.substring(SCHEME.length()).getBytes("UTF-8");
|
||||||
|
item = new ImageItem(
|
||||||
|
CONTENT_TYPE,
|
||||||
|
new ByteArrayInputStream(bytes),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(@NonNull String raw) {
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MediaDecoderImpl extends MediaDecoder {
|
||||||
|
|
||||||
|
private final Config config;
|
||||||
|
|
||||||
|
MediaDecoderImpl(@NonNull Config config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canDecodeByContentType(@Nullable String contentType) {
|
||||||
|
return CONTENT_TYPE.equals(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canDecodeByFileName(@NonNull String fileName) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Drawable decode(@NonNull InputStream inputStream) {
|
||||||
|
|
||||||
|
final Scanner scanner = new Scanner(inputStream, "UTF-8").useDelimiter("\\A");
|
||||||
|
final String latex = scanner.hasNext()
|
||||||
|
? scanner.next()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
Log.e("decode", latex);
|
||||||
|
|
||||||
|
if (latex == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: change to float
|
||||||
|
return JLatexMathDrawable.builder(latex)
|
||||||
|
.textSize((int) config.textSize)
|
||||||
|
.background(config.background)
|
||||||
|
.align(config.align)
|
||||||
|
.fitCanvas(config.fitCanvas)
|
||||||
|
.padding(config.padding)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
package ru.noties.markwon.sample.jlatexmath;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.commonmark.node.CustomBlock;
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
import org.commonmark.parser.Parser;
|
||||||
|
|
||||||
|
import ru.noties.jlatexmath.JLatexMathAndroid;
|
||||||
|
import ru.noties.jlatexmath.JLatexMathDrawable;
|
||||||
|
import ru.noties.markwon.Markwon;
|
||||||
|
import ru.noties.markwon.SpannableBuilder;
|
||||||
|
import ru.noties.markwon.SpannableConfiguration;
|
||||||
|
import ru.noties.markwon.il.AsyncDrawableLoader;
|
||||||
|
import ru.noties.markwon.renderer.ImageSize;
|
||||||
|
import ru.noties.markwon.renderer.SpannableMarkdownVisitor;
|
||||||
|
|
||||||
|
public class MainActivity extends Activity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
// todo: later version has automatic initialization
|
||||||
|
JLatexMathAndroid.init(this);
|
||||||
|
|
||||||
|
final TextView textView = findViewById(R.id.text_view);
|
||||||
|
|
||||||
|
// this one fails due input sanitizing on commonmark-java side
|
||||||
|
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}";
|
||||||
|
|
||||||
|
// mention another way of doing this: through async drawable to process formulas in background
|
||||||
|
|
||||||
|
|
||||||
|
final JLatexMathMedia.Config config = new JLatexMathMedia.Config(textView.getTextSize()) {{
|
||||||
|
// align = JLatexMathDrawable.ALIGN_RIGHT;
|
||||||
|
}};
|
||||||
|
final JLatexMathMedia jLatexMathMedia = new JLatexMathMedia(config);
|
||||||
|
|
||||||
|
final AsyncDrawableLoader asyncDrawableLoader = AsyncDrawableLoader.builder()
|
||||||
|
.schemeHandler(JLatexMathMedia.SCHEME, jLatexMathMedia.schemeHandler())
|
||||||
|
.mediaDecoders(jLatexMathMedia.mediaDecoder())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final SpannableConfiguration configuration = SpannableConfiguration.builder(this)
|
||||||
|
.asyncDrawableLoader(asyncDrawableLoader)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final String markdown = "# Example of LaTeX\n\n$$"
|
||||||
|
+ latex + "$$\n\n something like **this**";
|
||||||
|
|
||||||
|
final Parser parser = new Parser.Builder()
|
||||||
|
.customBlockParserFactory(new JLatexMathBlockParser.Factory())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final Node node = parser.parse(markdown);
|
||||||
|
final SpannableBuilder builder = new SpannableBuilder();
|
||||||
|
final SpannableMarkdownVisitor visitor = new SpannableMarkdownVisitor(SpannableConfiguration.create(this), builder) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CustomBlock customBlock) {
|
||||||
|
|
||||||
|
if (!(customBlock instanceof JLatexMathBlock)) {
|
||||||
|
super.visit(customBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String latex = ((JLatexMathBlock) customBlock).latex();
|
||||||
|
|
||||||
|
final int length = builder.length();
|
||||||
|
builder.append(latex);
|
||||||
|
|
||||||
|
SpannableBuilder.setSpans(
|
||||||
|
builder,
|
||||||
|
configuration.factory().image(
|
||||||
|
configuration.theme(),
|
||||||
|
JLatexMathMedia.makeDestination(latex),
|
||||||
|
configuration.asyncDrawableLoader(),
|
||||||
|
configuration.imageSizeResolver(),
|
||||||
|
new ImageSize(new ImageSize.Dimension(100, "%"), null),
|
||||||
|
false
|
||||||
|
),
|
||||||
|
length,
|
||||||
|
builder.length()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
node.accept(visitor);
|
||||||
|
|
||||||
|
Markwon.setText(textView, builder.text());
|
||||||
|
}
|
||||||
|
}
|
14
sample-latex-math/src/main/res/layout/activity_main.xml
Normal file
14
sample-latex-math/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?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>
|
3
sample-latex-math/src/main/res/values/strings.xml
Normal file
3
sample-latex-math/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Markwon-JLatexMath</string>
|
||||||
|
</resources>
|
6
sample-latex-math/src/main/res/values/styles.xml
Normal file
6
sample-latex-math/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
@ -1,3 +1,3 @@
|
|||||||
rootProject.name = 'MarkwonProject'
|
rootProject.name = 'MarkwonProject'
|
||||||
include ':app', ':markwon', ':markwon-image-loader', ':markwon-view', ':sample-custom-extension',
|
include ':app', ':markwon', ':markwon-image-loader', ':markwon-view', ':sample-custom-extension', ':sample-latex-math',
|
||||||
':markwon-syntax-highlight', ':markwon-html-parser-api', ':markwon-html-parser-impl'
|
':markwon-syntax-highlight', ':markwon-html-parser-api', ':markwon-html-parser-impl'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user