Latex inline parsing WIP
This commit is contained in:
parent
a298016ac2
commit
d78b278b86
@ -1,5 +1,6 @@
|
||||
package io.noties.markwon.ext.latex;
|
||||
|
||||
import org.commonmark.internal.util.Parsing;
|
||||
import org.commonmark.node.Block;
|
||||
import org.commonmark.parser.block.AbstractBlockParser;
|
||||
import org.commonmark.parser.block.AbstractBlockParserFactory;
|
||||
@ -60,18 +61,30 @@ public class JLatexMathBlockParser extends AbstractBlockParser {
|
||||
@Override
|
||||
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
|
||||
|
||||
// ^\s{0,3}\$\$\s*$ as a regex to star the block
|
||||
final int indent = state.getIndent();
|
||||
|
||||
final CharSequence line = state.getLine();
|
||||
final int length = line != null
|
||||
? line.length()
|
||||
: 0;
|
||||
// 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 int length = line.length();
|
||||
// we are looking for 2 `$$` subsequent signs
|
||||
// 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 ('$' == line.charAt(0)
|
||||
&& '$' == line.charAt(1)) {
|
||||
return BlockStart.of(new JLatexMathBlockParser())
|
||||
.atIndex(state.getIndex() + 2);
|
||||
if (diff > 0) {
|
||||
if (!Character.isWhitespace(line.charAt(nextNonSpaceIndex + 2))) {
|
||||
return BlockStart.none();
|
||||
}
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* @since 4.2.1-SNAPSHOT
|
||||
* @since 4.3.0-SNAPSHOT
|
||||
*/
|
||||
public class JLatexMathInline extends CustomNode {
|
||||
public class JLatexMathNode extends CustomNode {
|
||||
|
||||
private String latex;
|
||||
|
@ -14,7 +14,6 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
import org.commonmark.parser.InlineParserFactory;
|
||||
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.ImageSizeResolver;
|
||||
import io.noties.markwon.image.ImageSizeResolverDef;
|
||||
import io.noties.markwon.inlineparser.InlineProcessor;
|
||||
import io.noties.markwon.inlineparser.MarkwonInlineParser;
|
||||
import ru.noties.jlatexmath.JLatexMathDrawable;
|
||||
|
||||
@ -132,8 +130,8 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
// if it's $$\\dhdsfjh$$ -> inline
|
||||
|
||||
// builder.customBlockParserFactory(new JLatexMathBlockParser.Factory());
|
||||
final InlineParserFactory factory = MarkwonInlineParser.factoryBuilderNoDefaults()
|
||||
.addInlineProcessor(new LatexInlineProcessor())
|
||||
final InlineParserFactory factory = MarkwonInlineParser.factoryBuilder()
|
||||
.addInlineProcessor(new JLatexMathInlineProcessor())
|
||||
.build();
|
||||
builder.inlineParserFactory(factory);
|
||||
}
|
||||
@ -155,6 +153,33 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
|
||||
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(
|
||||
configuration.theme(),
|
||||
new AsyncDrawable(
|
||||
@ -170,76 +195,76 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
});
|
||||
}
|
||||
|
||||
private static class LatexInlineProcessor extends InlineProcessor {
|
||||
|
||||
@Override
|
||||
public char specialCharacter() {
|
||||
return '$';
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Node parse() {
|
||||
|
||||
final int start = index;
|
||||
|
||||
index += 1;
|
||||
if (peek() != '$') {
|
||||
index = start;
|
||||
return null;
|
||||
}
|
||||
|
||||
// must be not $
|
||||
index += 1;
|
||||
if (peek() == '$') {
|
||||
return text("$");
|
||||
}
|
||||
|
||||
// find next '$$', but not broken with 2(or more) new lines
|
||||
|
||||
boolean dollar = false;
|
||||
boolean newLine = false;
|
||||
boolean found = false;
|
||||
|
||||
index += 1;
|
||||
final int length = input.length();
|
||||
|
||||
while (index < length) {
|
||||
final char c = peek();
|
||||
if (c == '\n') {
|
||||
if (newLine) {
|
||||
// second new line
|
||||
break;
|
||||
}
|
||||
newLine = true;
|
||||
dollar = false; // cannot be on another line
|
||||
} else {
|
||||
newLine = false;
|
||||
if (c == '$') {
|
||||
if (dollar) {
|
||||
found = true;
|
||||
// advance
|
||||
index += 1;
|
||||
break;
|
||||
}
|
||||
dollar = true;
|
||||
} else {
|
||||
dollar = false;
|
||||
}
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
final JLatexMathBlock block = new JLatexMathBlock();
|
||||
block.latex(input.substring(start + 2, index - 2));
|
||||
index += 1;
|
||||
return block;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// private static class LatexInlineProcessor extends InlineProcessor {
|
||||
//
|
||||
// @Override
|
||||
// public char specialCharacter() {
|
||||
// return '$';
|
||||
// }
|
||||
//
|
||||
// @Nullable
|
||||
// @Override
|
||||
// protected Node parse() {
|
||||
//
|
||||
// final int start = index;
|
||||
//
|
||||
// index += 1;
|
||||
// if (peek() != '$') {
|
||||
// index = start;
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// // must be not $
|
||||
// index += 1;
|
||||
// if (peek() == '$') {
|
||||
// return text("$");
|
||||
// }
|
||||
//
|
||||
// // find next '$$', but not broken with 2(or more) new lines
|
||||
//
|
||||
// boolean dollar = false;
|
||||
// boolean newLine = false;
|
||||
// boolean found = false;
|
||||
//
|
||||
// index += 1;
|
||||
// final int length = input.length();
|
||||
//
|
||||
// while (index < length) {
|
||||
// final char c = peek();
|
||||
// if (c == '\n') {
|
||||
// if (newLine) {
|
||||
// // second new line
|
||||
// break;
|
||||
// }
|
||||
// newLine = true;
|
||||
// dollar = false; // cannot be on another line
|
||||
// } else {
|
||||
// newLine = false;
|
||||
// if (c == '$') {
|
||||
// if (dollar) {
|
||||
// found = true;
|
||||
// // advance
|
||||
// index += 1;
|
||||
// break;
|
||||
// }
|
||||
// dollar = true;
|
||||
// } else {
|
||||
// dollar = false;
|
||||
// }
|
||||
// }
|
||||
// index += 1;
|
||||
// }
|
||||
//
|
||||
// if (found) {
|
||||
// final JLatexMathBlock block = new JLatexMathBlock();
|
||||
// block.latex(input.substring(start + 2, index - 2));
|
||||
// index += 1;
|
||||
// return block;
|
||||
// }
|
||||
//
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
|
||||
@ -383,7 +408,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
.textSize(config.textSize)
|
||||
.background(backgroundProvider != null ? backgroundProvider.provide() : null)
|
||||
.align(config.align)
|
||||
.fitCanvas(config.fitCanvas)
|
||||
.fitCanvas(false /*config.fitCanvas*/)
|
||||
.padding(
|
||||
config.paddingHorizontal,
|
||||
config.paddingVertical,
|
||||
|
@ -24,26 +24,26 @@ public class LatexActivity extends Activity {
|
||||
|
||||
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 = "\\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 = "\\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 String markdown = "# Example of LaTeX\n\n$$"
|
||||
final String markdown = "# Example of LaTeX\n\nhello there: $$"
|
||||
+ latex + "$$\n\n something like **this**";
|
||||
|
||||
final Markwon markwon = Markwon.builder(this)
|
||||
|
Loading…
x
Reference in New Issue
Block a user