Latex inline parsing WIP
This commit is contained in:
parent
a298016ac2
commit
d78b278b86
@ -1,5 +1,6 @@
|
|||||||
package io.noties.markwon.ext.latex;
|
package io.noties.markwon.ext.latex;
|
||||||
|
|
||||||
|
import org.commonmark.internal.util.Parsing;
|
||||||
import org.commonmark.node.Block;
|
import org.commonmark.node.Block;
|
||||||
import org.commonmark.parser.block.AbstractBlockParser;
|
import org.commonmark.parser.block.AbstractBlockParser;
|
||||||
import org.commonmark.parser.block.AbstractBlockParserFactory;
|
import org.commonmark.parser.block.AbstractBlockParserFactory;
|
||||||
@ -60,18 +61,30 @@ public class JLatexMathBlockParser extends AbstractBlockParser {
|
|||||||
@Override
|
@Override
|
||||||
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
|
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
|
||||||
|
|
||||||
// ^\s{0,3}\$\$\s*$ as a regex to star the block
|
final int indent = state.getIndent();
|
||||||
|
|
||||||
|
// check if it's an indented code block
|
||||||
|
if (indent < Parsing.CODE_BLOCK_INDENT) {
|
||||||
|
final int nextNonSpaceIndex = state.getNextNonSpaceIndex();
|
||||||
final CharSequence line = state.getLine();
|
final CharSequence line = state.getLine();
|
||||||
final int length = line != null
|
final int length = line.length();
|
||||||
? line.length()
|
// we are looking for 2 `$$` subsequent signs
|
||||||
: 0;
|
// and immediate new-line or arbitrary number of white spaces (we check for the first one)
|
||||||
|
// so, nextNonSpaceIndex + 2 >= length and both symbols are `$`s
|
||||||
|
final int diff = length - (nextNonSpaceIndex + 2);
|
||||||
|
if (diff >= 0) {
|
||||||
|
// check for both `$`
|
||||||
|
if (line.charAt(nextNonSpaceIndex) == '$'
|
||||||
|
&& line.charAt(nextNonSpaceIndex + 1) == '$') {
|
||||||
|
|
||||||
if (length > 1) {
|
if (diff > 0) {
|
||||||
if ('$' == line.charAt(0)
|
if (!Character.isWhitespace(line.charAt(nextNonSpaceIndex + 2))) {
|
||||||
&& '$' == line.charAt(1)) {
|
return BlockStart.none();
|
||||||
return BlockStart.of(new JLatexMathBlockParser())
|
}
|
||||||
.atIndex(state.getIndex() + 2);
|
// consume all until new-line or first not-white-space char
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
package io.noties.markwon.ext.latex;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import io.noties.markwon.inlineparser.InlineProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.3.0-SNAPSHOT
|
||||||
|
*/
|
||||||
|
public class JLatexMathInlineProcessor extends InlineProcessor {
|
||||||
|
|
||||||
|
private static final Pattern RE = Pattern.compile("(\\${2})([\\s\\S]+?)\\1");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char specialCharacter() {
|
||||||
|
return '$';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected Node parse() {
|
||||||
|
|
||||||
|
final String latex = match(RE);
|
||||||
|
if (latex == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final JLatexMathNode node = new JLatexMathNode();
|
||||||
|
node.latex(latex.substring(2, latex.length() - 2));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,9 @@ package io.noties.markwon.ext.latex;
|
|||||||
import org.commonmark.node.CustomNode;
|
import org.commonmark.node.CustomNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.2.1-SNAPSHOT
|
* @since 4.3.0-SNAPSHOT
|
||||||
*/
|
*/
|
||||||
public class JLatexMathInline extends CustomNode {
|
public class JLatexMathNode extends CustomNode {
|
||||||
|
|
||||||
private String latex;
|
private String latex;
|
||||||
|
|
@ -14,7 +14,6 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.annotation.Px;
|
import androidx.annotation.Px;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
|
||||||
import org.commonmark.parser.InlineParserFactory;
|
import org.commonmark.parser.InlineParserFactory;
|
||||||
import org.commonmark.parser.Parser;
|
import org.commonmark.parser.Parser;
|
||||||
|
|
||||||
@ -33,7 +32,6 @@ import io.noties.markwon.image.AsyncDrawableScheduler;
|
|||||||
import io.noties.markwon.image.AsyncDrawableSpan;
|
import io.noties.markwon.image.AsyncDrawableSpan;
|
||||||
import io.noties.markwon.image.ImageSizeResolver;
|
import io.noties.markwon.image.ImageSizeResolver;
|
||||||
import io.noties.markwon.image.ImageSizeResolverDef;
|
import io.noties.markwon.image.ImageSizeResolverDef;
|
||||||
import io.noties.markwon.inlineparser.InlineProcessor;
|
|
||||||
import io.noties.markwon.inlineparser.MarkwonInlineParser;
|
import io.noties.markwon.inlineparser.MarkwonInlineParser;
|
||||||
import ru.noties.jlatexmath.JLatexMathDrawable;
|
import ru.noties.jlatexmath.JLatexMathDrawable;
|
||||||
|
|
||||||
@ -132,8 +130,8 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
// if it's $$\\dhdsfjh$$ -> inline
|
// if it's $$\\dhdsfjh$$ -> inline
|
||||||
|
|
||||||
// builder.customBlockParserFactory(new JLatexMathBlockParser.Factory());
|
// builder.customBlockParserFactory(new JLatexMathBlockParser.Factory());
|
||||||
final InlineParserFactory factory = MarkwonInlineParser.factoryBuilderNoDefaults()
|
final InlineParserFactory factory = MarkwonInlineParser.factoryBuilder()
|
||||||
.addInlineProcessor(new LatexInlineProcessor())
|
.addInlineProcessor(new JLatexMathInlineProcessor())
|
||||||
.build();
|
.build();
|
||||||
builder.inlineParserFactory(factory);
|
builder.inlineParserFactory(factory);
|
||||||
}
|
}
|
||||||
@ -155,6 +153,33 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
|
|
||||||
final MarkwonConfiguration configuration = visitor.configuration();
|
final MarkwonConfiguration configuration = visitor.configuration();
|
||||||
|
|
||||||
|
final AsyncDrawableSpan span = new AsyncDrawableSpan(
|
||||||
|
configuration.theme(),
|
||||||
|
new AsyncDrawable(
|
||||||
|
latex,
|
||||||
|
jLatextAsyncDrawableLoader,
|
||||||
|
jLatexImageSizeResolver,
|
||||||
|
null),
|
||||||
|
AsyncDrawableSpan.ALIGN_CENTER,
|
||||||
|
false);
|
||||||
|
|
||||||
|
visitor.setSpans(length, span);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.on(JLatexMathNode.class, new MarkwonVisitor.NodeVisitor<JLatexMathNode>() {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull JLatexMathNode jLatexMathNode) {
|
||||||
|
final String latex = jLatexMathNode.latex();
|
||||||
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
|
||||||
|
// @since 4.0.2 we cannot append _raw_ latex as a placeholder-text,
|
||||||
|
// because Android will draw formula for each line of text, thus
|
||||||
|
// leading to formula duplicated (drawn on each line of text)
|
||||||
|
visitor.builder().append(prepareLatexTextPlaceholder(latex));
|
||||||
|
|
||||||
|
final MarkwonConfiguration configuration = visitor.configuration();
|
||||||
|
|
||||||
final AsyncDrawableSpan span = new AsyncDrawableSpan(
|
final AsyncDrawableSpan span = new AsyncDrawableSpan(
|
||||||
configuration.theme(),
|
configuration.theme(),
|
||||||
new AsyncDrawable(
|
new AsyncDrawable(
|
||||||
@ -170,76 +195,76 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class LatexInlineProcessor extends InlineProcessor {
|
// private static class LatexInlineProcessor extends InlineProcessor {
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public char specialCharacter() {
|
// public char specialCharacter() {
|
||||||
return '$';
|
// return '$';
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Nullable
|
// @Nullable
|
||||||
@Override
|
// @Override
|
||||||
protected Node parse() {
|
// protected Node parse() {
|
||||||
|
//
|
||||||
final int start = index;
|
// final int start = index;
|
||||||
|
//
|
||||||
index += 1;
|
// index += 1;
|
||||||
if (peek() != '$') {
|
// if (peek() != '$') {
|
||||||
index = start;
|
// index = start;
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// must be not $
|
// // must be not $
|
||||||
index += 1;
|
// index += 1;
|
||||||
if (peek() == '$') {
|
// if (peek() == '$') {
|
||||||
return text("$");
|
// return text("$");
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// find next '$$', but not broken with 2(or more) new lines
|
// // find next '$$', but not broken with 2(or more) new lines
|
||||||
|
//
|
||||||
boolean dollar = false;
|
// boolean dollar = false;
|
||||||
boolean newLine = false;
|
// boolean newLine = false;
|
||||||
boolean found = false;
|
// boolean found = false;
|
||||||
|
//
|
||||||
index += 1;
|
// index += 1;
|
||||||
final int length = input.length();
|
// final int length = input.length();
|
||||||
|
//
|
||||||
while (index < length) {
|
// while (index < length) {
|
||||||
final char c = peek();
|
// final char c = peek();
|
||||||
if (c == '\n') {
|
// if (c == '\n') {
|
||||||
if (newLine) {
|
// if (newLine) {
|
||||||
// second new line
|
// // second new line
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
newLine = true;
|
// newLine = true;
|
||||||
dollar = false; // cannot be on another line
|
// dollar = false; // cannot be on another line
|
||||||
} else {
|
// } else {
|
||||||
newLine = false;
|
// newLine = false;
|
||||||
if (c == '$') {
|
// if (c == '$') {
|
||||||
if (dollar) {
|
// if (dollar) {
|
||||||
found = true;
|
// found = true;
|
||||||
// advance
|
// // advance
|
||||||
index += 1;
|
// index += 1;
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
dollar = true;
|
// dollar = true;
|
||||||
} else {
|
// } else {
|
||||||
dollar = false;
|
// dollar = false;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
index += 1;
|
// index += 1;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (found) {
|
// if (found) {
|
||||||
final JLatexMathBlock block = new JLatexMathBlock();
|
// final JLatexMathBlock block = new JLatexMathBlock();
|
||||||
block.latex(input.substring(start + 2, index - 2));
|
// block.latex(input.substring(start + 2, index - 2));
|
||||||
index += 1;
|
// index += 1;
|
||||||
return block;
|
// return block;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
|
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
|
||||||
@ -383,7 +408,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
.textSize(config.textSize)
|
.textSize(config.textSize)
|
||||||
.background(backgroundProvider != null ? backgroundProvider.provide() : null)
|
.background(backgroundProvider != null ? backgroundProvider.provide() : null)
|
||||||
.align(config.align)
|
.align(config.align)
|
||||||
.fitCanvas(config.fitCanvas)
|
.fitCanvas(false /*config.fitCanvas*/)
|
||||||
.padding(
|
.padding(
|
||||||
config.paddingHorizontal,
|
config.paddingHorizontal,
|
||||||
config.paddingVertical,
|
config.paddingVertical,
|
||||||
|
@ -24,18 +24,18 @@ public class LatexActivity extends Activity {
|
|||||||
|
|
||||||
final TextView textView = findViewById(R.id.text_view);
|
final TextView textView = findViewById(R.id.text_view);
|
||||||
|
|
||||||
String latex = "\\begin{array}{l}";
|
// 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 += "\\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 += "\\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 += "\\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_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_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 += "\\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 += "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 += "\\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}";
|
// latex += "\\end{array}";
|
||||||
|
|
||||||
// String latex = "\\text{A long division \\longdiv{12345}{13}";
|
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 = "{a \\bangle b} {c \\brace d} {e \\brack f} {g \\choose h}";
|
||||||
|
|
||||||
// String latex = "\\begin{array}{cc}";
|
// String latex = "\\begin{array}{cc}";
|
||||||
@ -43,7 +43,7 @@ public class LatexActivity extends Activity {
|
|||||||
// latex += "\\doublebox{\\text{A double framed box}}&\\ovalbox{\\text{An oval framed box}}\\cr";
|
// latex += "\\doublebox{\\text{A double framed box}}&\\ovalbox{\\text{An oval framed box}}\\cr";
|
||||||
// latex += "\\end{array}";
|
// latex += "\\end{array}";
|
||||||
|
|
||||||
final String markdown = "# Example of LaTeX\n\n$$"
|
final String markdown = "# Example of LaTeX\n\nhello there: $$"
|
||||||
+ latex + "$$\n\n something like **this**";
|
+ latex + "$$\n\n something like **this**";
|
||||||
|
|
||||||
final Markwon markwon = Markwon.builder(this)
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user