Update sample configuration for latex block_and_inline renderMode
This commit is contained in:
parent
c7494a9225
commit
a80ff09e15
@ -0,0 +1,50 @@
|
||||
package io.noties.markwon.ext.latex;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import io.noties.markwon.image.AsyncDrawable;
|
||||
import io.noties.markwon.image.ImageSizeResolver;
|
||||
|
||||
// we must make drawable fit canvas (if specified), but do not keep the ratio whilst scaling up
|
||||
// @since 4.0.0
|
||||
class JLatexBlockImageSizeResolver extends ImageSizeResolver {
|
||||
|
||||
private final boolean fitCanvas;
|
||||
|
||||
JLatexBlockImageSizeResolver(boolean fitCanvas) {
|
||||
this.fitCanvas = fitCanvas;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Rect resolveImageSize(@NonNull AsyncDrawable drawable) {
|
||||
|
||||
final Rect imageBounds = drawable.getResult().getBounds();
|
||||
final int canvasWidth = drawable.getLastKnownCanvasWidth();
|
||||
|
||||
if (fitCanvas) {
|
||||
|
||||
// we modify bounds only if `fitCanvas` is true
|
||||
final int w = imageBounds.width();
|
||||
|
||||
if (w < canvasWidth) {
|
||||
// increase width and center formula (keep height as-is)
|
||||
return new Rect(0, 0, canvasWidth, imageBounds.height());
|
||||
}
|
||||
|
||||
// @since 4.0.2 we additionally scale down the resulting formula (keeping the ratio)
|
||||
// the thing is - JLatexMathDrawable will do it anyway, but it will modify its own
|
||||
// bounds (which AsyncDrawable won't catch), thus leading to an empty space after the formula
|
||||
if (w > canvasWidth) {
|
||||
// here we must scale it down (keeping the ratio)
|
||||
final float ratio = (float) w / imageBounds.height();
|
||||
final int h = (int) (canvasWidth / ratio + .5F);
|
||||
return new Rect(0, 0, canvasWidth, h);
|
||||
}
|
||||
}
|
||||
|
||||
return imageBounds;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package io.noties.markwon.ext.latex;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import io.noties.markwon.core.MarkwonTheme;
|
||||
import io.noties.markwon.image.AsyncDrawable;
|
||||
import io.noties.markwon.image.AsyncDrawableSpan;
|
||||
|
||||
/**
|
||||
* @since 4.3.0-SNAPSHOT
|
||||
*/
|
||||
class JLatexInlineAsyncDrawableSpan extends AsyncDrawableSpan {
|
||||
|
||||
private final AsyncDrawable drawable;
|
||||
|
||||
JLatexInlineAsyncDrawableSpan(@NonNull MarkwonTheme theme, @NonNull AsyncDrawable drawable, int alignment, boolean replacementTextIsLink) {
|
||||
super(theme, drawable, alignment, replacementTextIsLink);
|
||||
this.drawable = drawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(
|
||||
@NonNull Paint paint,
|
||||
CharSequence text,
|
||||
@IntRange(from = 0) int start,
|
||||
@IntRange(from = 0) int end,
|
||||
@Nullable Paint.FontMetricsInt fm) {
|
||||
|
||||
// if we have no async drawable result - we will just render text
|
||||
|
||||
final int size;
|
||||
|
||||
if (drawable.hasResult()) {
|
||||
|
||||
final Rect rect = drawable.getBounds();
|
||||
|
||||
if (fm != null) {
|
||||
final int half = rect.bottom / 2;
|
||||
fm.ascent = -half;
|
||||
fm.descent = half;
|
||||
|
||||
fm.top = fm.ascent;
|
||||
fm.bottom = 0;
|
||||
}
|
||||
|
||||
size = rect.right;
|
||||
|
||||
} else {
|
||||
|
||||
// NB, no specific text handling (no new lines, etc)
|
||||
size = (int) (paint.measureText(text, start, end) + .5F);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package io.noties.markwon.ext.latex;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.commonmark.internal.util.Parsing;
|
||||
@ -13,6 +11,10 @@ import org.commonmark.parser.block.BlockStart;
|
||||
import org.commonmark.parser.block.MatchedBlockParser;
|
||||
import org.commonmark.parser.block.ParserState;
|
||||
|
||||
/**
|
||||
* @since 4.3.0-SNAPSHOT (although there was a class with the same name,
|
||||
* which is renamed now to {@link JLatexMathBlockParserLegacy})
|
||||
*/
|
||||
public class JLatexMathBlockParser extends AbstractBlockParser {
|
||||
|
||||
private static final char DOLLAR = '$';
|
||||
@ -22,8 +24,6 @@ public class JLatexMathBlockParser extends AbstractBlockParser {
|
||||
|
||||
private final StringBuilder builder = new StringBuilder();
|
||||
|
||||
// private boolean isClosed;
|
||||
|
||||
private final int signs;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@ -44,12 +44,9 @@ public class JLatexMathBlockParser extends AbstractBlockParser {
|
||||
|
||||
// check for closing
|
||||
if (parserState.getIndent() < Parsing.CODE_BLOCK_INDENT) {
|
||||
Log.e("LTX", String.format("signs: %d, skip dollar: %s", signs, Parsing.skip(DOLLAR, line, nextNonSpaceIndex, length)));
|
||||
// if (Parsing.skip(DOLLAR, line, nextNonSpaceIndex, length) == signs) {
|
||||
if (consume(DOLLAR, line, nextNonSpaceIndex, length) == signs) {
|
||||
// okay, we have our number of signs
|
||||
// let's consume spaces until the end
|
||||
Log.e("LTX", String.format("length; %d, skip spaces: %s", length, Parsing.skip(SPACE, line, nextNonSpaceIndex + signs, length)));
|
||||
if (Parsing.skip(SPACE, line, nextNonSpaceIndex + signs, length) == length) {
|
||||
return BlockContinue.finished();
|
||||
}
|
||||
@ -61,22 +58,6 @@ public class JLatexMathBlockParser extends AbstractBlockParser {
|
||||
|
||||
@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, "");
|
||||
// }
|
||||
// }
|
||||
Log.e("LTX", "addLine: " + line);
|
||||
builder.append(line);
|
||||
builder.append('\n');
|
||||
}
|
||||
@ -88,8 +69,6 @@ public class JLatexMathBlockParser extends AbstractBlockParser {
|
||||
|
||||
public static class Factory extends AbstractBlockParserFactory {
|
||||
|
||||
// private static final Pattern RE = Pattern.compile("(\\${2,}) *$");
|
||||
|
||||
@Override
|
||||
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
|
||||
|
||||
@ -111,7 +90,6 @@ public class JLatexMathBlockParser extends AbstractBlockParser {
|
||||
final CharSequence line = state.getLine();
|
||||
final int length = line.length();
|
||||
|
||||
// final int signs = Parsing.skip(DOLLAR, line, nextNonSpaceIndex, length) - 1;
|
||||
final int signs = consume(DOLLAR, line, nextNonSpaceIndex, length);
|
||||
|
||||
// 2 is minimum
|
||||
@ -120,73 +98,16 @@ public class JLatexMathBlockParser extends AbstractBlockParser {
|
||||
}
|
||||
|
||||
// consume spaces until the end of the line, if any other content is found -> NONE
|
||||
// TODO: here we can check mode in which we operate (legacy or not)
|
||||
if (Parsing.skip(SPACE, line, nextNonSpaceIndex + signs, length) != length) {
|
||||
return BlockStart.none();
|
||||
}
|
||||
|
||||
Log.e("LTX", String.format("signs: %s, next: %d, length: %d, line: '%s'", signs, nextNonSpaceIndex, length, line));
|
||||
|
||||
return BlockStart.of(new JLatexMathBlockParser(signs))
|
||||
.atIndex(length + 1);
|
||||
|
||||
|
||||
// // 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();
|
||||
//
|
||||
// final int signs = Parsing.skip('$', line, nextNonSpaceIndex, length);
|
||||
//
|
||||
// // 2 is minimum
|
||||
// if (signs < 2) {
|
||||
// return BlockStart.none();
|
||||
// }
|
||||
//
|
||||
// // consume spaces until the end of the line, if any other content is found -> NONE
|
||||
// if (Parsing.skip(' ', line, nextNonSpaceIndex + signs, length) != length) {
|
||||
// return BlockStart.none();
|
||||
// }
|
||||
//
|
||||
//// // consume spaces until the end of the line, if any other content is found -> NONE
|
||||
//// if ((nextNonSpaceIndex + signs) < length) {
|
||||
//// // check if more content is available
|
||||
//// if (Parsing.skip(' ', line,nextNonSpaceIndex + signs, length) != length) {
|
||||
//// return BlockStart.none();
|
||||
//// }
|
||||
//// }
|
||||
//
|
||||
//// final Matcher matcher = RE.matcher(line);
|
||||
//// matcher.region(nextNonSpaceIndex, length);
|
||||
//
|
||||
//// Log.e("LATEX", String.format("nonSpace: %d, length: %s, line: '%s'", nextNonSpaceIndex, length, line));
|
||||
//
|
||||
// // 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 (diff > 0) {
|
||||
// if (!Character.isWhitespace(line.charAt(nextNonSpaceIndex + 2))) {
|
||||
// return BlockStart.none();
|
||||
// }
|
||||
// return BlockStart.of(new JLatexMathBlockParser()).atIndex(nextNonSpaceIndex + 3);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return BlockStart.none();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static int consume(char c, @NonNull CharSequence line, int start, int end) {
|
||||
for (int i = start; i < end; i++) {
|
||||
if (c != line.charAt(i)) {
|
||||
|
@ -0,0 +1,82 @@
|
||||
package io.noties.markwon.ext.latex;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @since 4.3.0-SNAPSHOT (although it is just renamed parser from previous versions)
|
||||
*/
|
||||
public class JLatexMathBlockParserLegacy 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 JLatexMathBlockParserLegacy())
|
||||
.atIndex(state.getIndex() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
return BlockStart.none();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package io.noties.markwon.ext.latex;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
@ -10,7 +9,6 @@ import android.text.Spanned;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
@ -28,14 +26,11 @@ import java.util.concurrent.Future;
|
||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||
import io.noties.markwon.MarkwonConfiguration;
|
||||
import io.noties.markwon.MarkwonVisitor;
|
||||
import io.noties.markwon.core.MarkwonTheme;
|
||||
import io.noties.markwon.image.AsyncDrawable;
|
||||
import io.noties.markwon.image.AsyncDrawableLoader;
|
||||
import io.noties.markwon.image.AsyncDrawableScheduler;
|
||||
import io.noties.markwon.image.AsyncDrawableSpan;
|
||||
import io.noties.markwon.image.ImageSize;
|
||||
import io.noties.markwon.image.ImageSizeResolver;
|
||||
import io.noties.markwon.image.ImageSizeResolverDef;
|
||||
import io.noties.markwon.inlineparser.MarkwonInlineParser;
|
||||
import ru.noties.jlatexmath.JLatexMathDrawable;
|
||||
|
||||
@ -68,7 +63,6 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
BLOCKS_AND_INLINES
|
||||
}
|
||||
|
||||
// TODO: inlines are not moved to a new line when exceed available width.. (api 23, emulator)
|
||||
public interface BuilderConfigure {
|
||||
void configureBuilder(@NonNull Builder builder);
|
||||
}
|
||||
@ -78,52 +72,65 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
return new JLatexMathPlugin(builder(textSize).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3.0-SNAPSHOT
|
||||
*/
|
||||
@NonNull
|
||||
public static JLatexMathPlugin create(@Px float inlineTextSize, @Px float blockTextSize) {
|
||||
return new JLatexMathPlugin(builder(inlineTextSize, blockTextSize).build());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static JLatexMathPlugin create(@NonNull Config config) {
|
||||
return new JLatexMathPlugin(config);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static JLatexMathPlugin create(float textSize, @NonNull BuilderConfigure builderConfigure) {
|
||||
final Builder builder = new Builder(textSize);
|
||||
public static JLatexMathPlugin create(@Px float textSize, @NonNull BuilderConfigure builderConfigure) {
|
||||
final Builder builder = builder(textSize);
|
||||
builderConfigure.configureBuilder(builder);
|
||||
return new JLatexMathPlugin(builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3.0-SNAPSHOT
|
||||
*/
|
||||
@NonNull
|
||||
public static JLatexMathPlugin create(
|
||||
@Px float inlineTextSize,
|
||||
@Px float blockTextSize,
|
||||
@NonNull BuilderConfigure builderConfigure) {
|
||||
final Builder builder = builder(inlineTextSize, blockTextSize);
|
||||
builderConfigure.configureBuilder(builder);
|
||||
return new JLatexMathPlugin(builder.build());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static JLatexMathPlugin.Builder builder(float textSize) {
|
||||
return new Builder(textSize);
|
||||
public static JLatexMathPlugin.Builder builder(@Px float textSize) {
|
||||
return new Builder(JLatexMathTheme.builder(textSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3.0-SNAPSHOT
|
||||
*/
|
||||
@NonNull
|
||||
public static JLatexMathPlugin.Builder builder(@Px float inlineTextSize, @Px float blockTextSize) {
|
||||
return new Builder(JLatexMathTheme.builder(inlineTextSize, blockTextSize));
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
|
||||
private final float textSize;
|
||||
// @since 4.3.0-SNAPSHOT
|
||||
private final JLatexMathTheme theme;
|
||||
|
||||
// @since 4.0.0
|
||||
private final JLatexMathTheme.BackgroundProvider backgroundProvider;
|
||||
// @since 4.3.0-SNAPSHOT
|
||||
private final RenderMode renderMode;
|
||||
|
||||
@JLatexMathDrawable.Align
|
||||
private final int align;
|
||||
|
||||
private final boolean fitCanvas;
|
||||
|
||||
// @since 4.0.0
|
||||
private final int paddingHorizontal;
|
||||
|
||||
// @since 4.0.0
|
||||
private final int paddingVertical;
|
||||
|
||||
// @since 4.0.0
|
||||
private final ExecutorService executorService;
|
||||
|
||||
Config(@NonNull Builder builder) {
|
||||
this.textSize = builder.textSize;
|
||||
this.backgroundProvider = builder.backgroundProvider;
|
||||
this.align = builder.align;
|
||||
this.fitCanvas = builder.fitCanvas;
|
||||
this.paddingHorizontal = builder.paddingHorizontal;
|
||||
this.paddingVertical = builder.paddingVertical;
|
||||
|
||||
this.theme = builder.theme.build();
|
||||
this.renderMode = builder.renderMode;
|
||||
// @since 4.0.0
|
||||
ExecutorService executorService = builder.executorService;
|
||||
if (executorService == null) {
|
||||
@ -133,29 +140,46 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private final Config config;
|
||||
private final JLatextAsyncDrawableLoader jLatextAsyncDrawableLoader;
|
||||
private final JLatexImageSizeResolver jLatexImageSizeResolver;
|
||||
private final JLatexBlockImageSizeResolver jLatexBlockImageSizeResolver;
|
||||
private final ImageSizeResolver inlineImageSizeResolver;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
JLatexMathPlugin(@NonNull Config config) {
|
||||
this.config = config;
|
||||
this.jLatextAsyncDrawableLoader = new JLatextAsyncDrawableLoader(config);
|
||||
this.jLatexImageSizeResolver = new JLatexImageSizeResolver(config.fitCanvas);
|
||||
this.jLatexBlockImageSizeResolver = new JLatexBlockImageSizeResolver(config.theme.blockFitCanvas());
|
||||
this.inlineImageSizeResolver = new InlineImageSizeResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureParser(@NonNull Parser.Builder builder) {
|
||||
|
||||
// what we can do:
|
||||
// [0-3] spaces before block start/end
|
||||
// if it's $$\n -> block
|
||||
// if it's $$\\dhdsfjh$$ -> inline
|
||||
// TODO: depending on renderMode we should register our parsing here
|
||||
// * for LEGACY -> just add custom block parser
|
||||
// * for INLINE.. -> require InlinePlugin, add inline processor + add block parser
|
||||
|
||||
builder.customBlockParserFactory(new JLatexMathBlockParser.Factory());
|
||||
switch (config.renderMode) {
|
||||
|
||||
final InlineParserFactory factory = MarkwonInlineParser.factoryBuilder()
|
||||
.addInlineProcessor(new JLatexMathInlineProcessor())
|
||||
.build();
|
||||
builder.inlineParserFactory(factory);
|
||||
case LEGACY: {
|
||||
builder.customBlockParserFactory(new JLatexMathBlockParserLegacy.Factory());
|
||||
}
|
||||
break;
|
||||
|
||||
case BLOCKS_AND_INLINES: {
|
||||
builder.customBlockParserFactory(new JLatexMathBlockParser.Factory());
|
||||
|
||||
final InlineParserFactory factory = MarkwonInlineParser.factoryBuilder()
|
||||
.addInlineProcessor(new JLatexMathInlineProcessor())
|
||||
.build();
|
||||
builder.inlineParserFactory(factory);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Unexpected `renderMode`: " + config.renderMode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -182,7 +206,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
new JLatextAsyncDrawable(
|
||||
latex,
|
||||
jLatextAsyncDrawableLoader,
|
||||
jLatexImageSizeResolver,
|
||||
jLatexBlockImageSizeResolver,
|
||||
null,
|
||||
true),
|
||||
AsyncDrawableSpan.ALIGN_CENTER,
|
||||
@ -196,34 +220,39 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
}
|
||||
}
|
||||
});
|
||||
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));
|
||||
if (RenderMode.BLOCKS_AND_INLINES == config.renderMode) {
|
||||
|
||||
final MarkwonConfiguration configuration = visitor.configuration();
|
||||
builder.on(JLatexMathNode.class, new MarkwonVisitor.NodeVisitor<JLatexMathNode>() {
|
||||
@Override
|
||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull JLatexMathNode jLatexMathNode) {
|
||||
final String latex = jLatexMathNode.latex();
|
||||
|
||||
final AsyncDrawableSpan span = new JLatexAsyncDrawableSpan(
|
||||
configuration.theme(),
|
||||
new JLatextAsyncDrawable(
|
||||
latex,
|
||||
jLatextAsyncDrawableLoader,
|
||||
new ImageSizeResolverDef(),
|
||||
null,
|
||||
false),
|
||||
AsyncDrawableSpan.ALIGN_CENTER,
|
||||
false);
|
||||
final int length = visitor.length();
|
||||
|
||||
visitor.setSpans(length, span);
|
||||
}
|
||||
});
|
||||
// @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 JLatexInlineAsyncDrawableSpan(
|
||||
configuration.theme(),
|
||||
new JLatextAsyncDrawable(
|
||||
latex,
|
||||
jLatextAsyncDrawableLoader,
|
||||
inlineImageSizeResolver,
|
||||
null,
|
||||
false),
|
||||
AsyncDrawableSpan.ALIGN_CENTER,
|
||||
false);
|
||||
|
||||
visitor.setSpans(length, span);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -245,61 +274,30 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final float textSize;
|
||||
// @since 4.3.0-SNAPSHOT
|
||||
private final JLatexMathTheme.Builder theme;
|
||||
|
||||
// @since 4.0.0
|
||||
private JLatexMathTheme.BackgroundProvider backgroundProvider;
|
||||
|
||||
@JLatexMathDrawable.Align
|
||||
private int align = JLatexMathDrawable.ALIGN_CENTER;
|
||||
|
||||
private boolean fitCanvas = false;
|
||||
|
||||
// @since 4.0.0
|
||||
private int paddingHorizontal;
|
||||
|
||||
// @since 4.0.0
|
||||
private int paddingVertical;
|
||||
// @since 4.3.0-SNAPSHOT
|
||||
private RenderMode renderMode = RenderMode.BLOCKS_AND_INLINES;
|
||||
|
||||
// @since 4.0.0
|
||||
private ExecutorService executorService;
|
||||
|
||||
Builder(float textSize) {
|
||||
this.textSize = textSize;
|
||||
Builder(@NonNull JLatexMathTheme.Builder builder) {
|
||||
this.theme = builder;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder backgroundProvider(@NonNull JLatexMathTheme.BackgroundProvider backgroundProvider) {
|
||||
this.backgroundProvider = backgroundProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder align(@JLatexMathDrawable.Align int align) {
|
||||
this.align = align;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder fitCanvas(boolean fitCanvas) {
|
||||
this.fitCanvas = fitCanvas;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder padding(@Px int padding) {
|
||||
this.paddingHorizontal = padding;
|
||||
this.paddingVertical = padding;
|
||||
return this;
|
||||
public JLatexMathTheme.Builder theme() {
|
||||
return theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.0.0
|
||||
* @since 4.3.0-SNAPSHOT
|
||||
*/
|
||||
@NonNull
|
||||
public Builder builder(@Px int paddingHorizontal, @Px int paddingVertical) {
|
||||
this.paddingHorizontal = paddingHorizontal;
|
||||
this.paddingVertical = paddingVertical;
|
||||
public Builder renderMode(@NonNull RenderMode renderMode) {
|
||||
this.renderMode = renderMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -319,7 +317,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
}
|
||||
|
||||
// @since 4.0.0
|
||||
private static class JLatextAsyncDrawableLoader extends AsyncDrawableLoader {
|
||||
static class JLatextAsyncDrawableLoader extends AsyncDrawableLoader {
|
||||
|
||||
private final Config config;
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
@ -358,57 +356,15 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
|
||||
private void execute() {
|
||||
|
||||
// @since 4.0.1 (background provider can be null)
|
||||
final JLatexMathTheme.BackgroundProvider backgroundProvider = config.backgroundProvider;
|
||||
|
||||
final JLatexMathDrawable jLatexMathDrawable;
|
||||
|
||||
// TODO: obtain real values from theme (for blocks and inlines)
|
||||
final JLatextAsyncDrawable jLatextAsyncDrawable = (JLatextAsyncDrawable) drawable;
|
||||
if (jLatextAsyncDrawable.isBlock) {
|
||||
// create JLatexMathDrawable
|
||||
//noinspection ConstantConditions
|
||||
jLatexMathDrawable =
|
||||
JLatexMathDrawable.builder(drawable.getDestination())
|
||||
.textSize(config.textSize)
|
||||
.background(backgroundProvider != null ? backgroundProvider.provide() : null)
|
||||
.align(config.align)
|
||||
.fitCanvas(config.fitCanvas)
|
||||
.padding(
|
||||
config.paddingHorizontal,
|
||||
config.paddingVertical,
|
||||
config.paddingHorizontal,
|
||||
config.paddingVertical)
|
||||
.build();
|
||||
} else {
|
||||
jLatexMathDrawable =
|
||||
JLatexMathDrawable.builder(drawable.getDestination())
|
||||
.textSize(config.textSize)
|
||||
// .background(backgroundProvider != null ? backgroundProvider.provide() : null)
|
||||
// .align(config.align)
|
||||
// .fitCanvas(config.fitCanvas)
|
||||
// .padding(
|
||||
// config.paddingHorizontal,
|
||||
// config.paddingVertical,
|
||||
// config.paddingHorizontal,
|
||||
// config.paddingVertical)
|
||||
.build();
|
||||
}
|
||||
|
||||
// create JLatexMathDrawable
|
||||
// //noinspection ConstantConditions
|
||||
// final JLatexMathDrawable jLatexMathDrawable =
|
||||
// JLatexMathDrawable.builder(drawable.getDestination())
|
||||
// .textSize(config.textSize)
|
||||
// .background(backgroundProvider != null ? backgroundProvider.provide() : null)
|
||||
// .align(config.align)
|
||||
// .fitCanvas(config.fitCanvas)
|
||||
// .padding(
|
||||
// config.paddingHorizontal,
|
||||
// config.paddingVertical,
|
||||
// config.paddingHorizontal,
|
||||
// config.paddingVertical)
|
||||
// .build();
|
||||
if (jLatextAsyncDrawable.isBlock()) {
|
||||
jLatexMathDrawable = createBlockDrawable(jLatextAsyncDrawable.getDestination());
|
||||
} else {
|
||||
jLatexMathDrawable = createInlineDrawable(jLatextAsyncDrawable.getDestination());
|
||||
}
|
||||
|
||||
// we must post to handler, but also have a way to identify the drawable
|
||||
// for which we are posting (in case of cancellation)
|
||||
@ -447,109 +403,63 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
||||
public Drawable placeholder(@NonNull AsyncDrawable drawable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// @since 4.3.0-SNAPSHOT
|
||||
@NonNull
|
||||
private JLatexMathDrawable createBlockDrawable(@NonNull String latex) {
|
||||
|
||||
final JLatexMathTheme theme = config.theme;
|
||||
|
||||
final JLatexMathTheme.BackgroundProvider backgroundProvider = theme.blockBackgroundProvider();
|
||||
final JLatexMathTheme.Padding padding = theme.blockPadding();
|
||||
|
||||
final JLatexMathDrawable.Builder builder = JLatexMathDrawable.builder(latex)
|
||||
.textSize(theme.blockTextSize())
|
||||
.align(theme.blockHorizontalAlignment())
|
||||
.fitCanvas(theme.blockFitCanvas());
|
||||
|
||||
if (backgroundProvider != null) {
|
||||
builder.background(backgroundProvider.provide());
|
||||
}
|
||||
|
||||
if (padding != null) {
|
||||
builder.padding(padding.left, padding.top, padding.right, padding.bottom);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
// @since 4.3.0-SNAPSHOT
|
||||
@NonNull
|
||||
private JLatexMathDrawable createInlineDrawable(@NonNull String latex) {
|
||||
|
||||
final JLatexMathTheme theme = config.theme;
|
||||
|
||||
final JLatexMathTheme.BackgroundProvider backgroundProvider = theme.inlineBackgroundProvider();
|
||||
final JLatexMathTheme.Padding padding = theme.inlinePadding();
|
||||
|
||||
final JLatexMathDrawable.Builder builder = JLatexMathDrawable.builder(latex)
|
||||
.textSize(theme.inlineTextSize())
|
||||
.fitCanvas(false);
|
||||
|
||||
if (backgroundProvider != null) {
|
||||
builder.background(backgroundProvider.provide());
|
||||
}
|
||||
|
||||
if (padding != null) {
|
||||
builder.padding(padding.left, padding.top, padding.right, padding.bottom);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
// we must make drawable fit canvas (if specified), but do not keep the ratio whilst scaling up
|
||||
// @since 4.0.0
|
||||
private static class JLatexImageSizeResolver extends ImageSizeResolver {
|
||||
|
||||
private final boolean fitCanvas;
|
||||
|
||||
JLatexImageSizeResolver(boolean fitCanvas) {
|
||||
this.fitCanvas = fitCanvas;
|
||||
}
|
||||
private static class InlineImageSizeResolver extends ImageSizeResolver {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Rect resolveImageSize(@NonNull AsyncDrawable drawable) {
|
||||
|
||||
final Rect imageBounds = drawable.getResult().getBounds();
|
||||
final int canvasWidth = drawable.getLastKnownCanvasWidth();
|
||||
|
||||
if (fitCanvas) {
|
||||
|
||||
// we modify bounds only if `fitCanvas` is true
|
||||
final int w = imageBounds.width();
|
||||
|
||||
if (w < canvasWidth) {
|
||||
// increase width and center formula (keep height as-is)
|
||||
return new Rect(0, 0, canvasWidth, imageBounds.height());
|
||||
}
|
||||
|
||||
// @since 4.0.2 we additionally scale down the resulting formula (keeping the ratio)
|
||||
// the thing is - JLatexMathDrawable will do it anyway, but it will modify its own
|
||||
// bounds (which AsyncDrawable won't catch), thus leading to an empty space after the formula
|
||||
if (w > canvasWidth) {
|
||||
// here we must scale it down (keeping the ratio)
|
||||
final float ratio = (float) w / imageBounds.height();
|
||||
final int h = (int) (canvasWidth / ratio + .5F);
|
||||
return new Rect(0, 0, canvasWidth, h);
|
||||
}
|
||||
}
|
||||
|
||||
return imageBounds;
|
||||
}
|
||||
}
|
||||
|
||||
private static class JLatextAsyncDrawable extends AsyncDrawable {
|
||||
|
||||
private final boolean isBlock;
|
||||
|
||||
public JLatextAsyncDrawable(
|
||||
@NonNull String destination,
|
||||
@NonNull AsyncDrawableLoader loader,
|
||||
@NonNull ImageSizeResolver imageSizeResolver,
|
||||
@Nullable ImageSize imageSize,
|
||||
boolean isBlock
|
||||
) {
|
||||
super(destination, loader, imageSizeResolver, imageSize);
|
||||
this.isBlock = isBlock;
|
||||
}
|
||||
}
|
||||
|
||||
private static class JLatexAsyncDrawableSpan extends AsyncDrawableSpan {
|
||||
|
||||
private final AsyncDrawable drawable;
|
||||
|
||||
public JLatexAsyncDrawableSpan(@NonNull MarkwonTheme theme, @NonNull AsyncDrawable drawable, int alignment, boolean replacementTextIsLink) {
|
||||
super(theme, drawable, alignment, replacementTextIsLink);
|
||||
this.drawable = drawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(
|
||||
@NonNull Paint paint,
|
||||
CharSequence text,
|
||||
@IntRange(from = 0) int start,
|
||||
@IntRange(from = 0) int end,
|
||||
@Nullable Paint.FontMetricsInt fm) {
|
||||
|
||||
// if we have no async drawable result - we will just render text
|
||||
|
||||
final int size;
|
||||
|
||||
if (drawable.hasResult()) {
|
||||
|
||||
final Rect rect = drawable.getBounds();
|
||||
|
||||
if (fm != null) {
|
||||
final int half = rect.bottom / 2;
|
||||
fm.ascent = -half;
|
||||
fm.descent = half;
|
||||
|
||||
fm.top = fm.ascent;
|
||||
fm.bottom = 0;
|
||||
}
|
||||
|
||||
size = rect.right;
|
||||
|
||||
} else {
|
||||
|
||||
// NB, no specific text handling (no new lines, etc)
|
||||
size = (int) (paint.measureText(text, start, end) + .5F);
|
||||
}
|
||||
|
||||
return size;
|
||||
return drawable.getResult().getBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,9 +128,9 @@ public abstract class JLatexMathTheme {
|
||||
private BackgroundProvider inlineBackgroundProvider;
|
||||
private BackgroundProvider blockBackgroundProvider;
|
||||
|
||||
private boolean blockFitCanvas;
|
||||
private boolean blockFitCanvas = true;
|
||||
// horizontal alignment (when there is additional horizontal space)
|
||||
private int blockHorizontalAlignment;
|
||||
private int blockHorizontalAlignment = JLatexMathDrawable.ALIGN_CENTER;
|
||||
|
||||
private Padding padding;
|
||||
private Padding inlinePadding;
|
||||
@ -196,7 +196,7 @@ public abstract class JLatexMathTheme {
|
||||
|
||||
@NonNull
|
||||
public JLatexMathTheme build() {
|
||||
return null;
|
||||
return new Impl(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,32 @@
|
||||
package io.noties.markwon.ext.latex;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import io.noties.markwon.image.AsyncDrawable;
|
||||
import io.noties.markwon.image.AsyncDrawableLoader;
|
||||
import io.noties.markwon.image.ImageSize;
|
||||
import io.noties.markwon.image.ImageSizeResolver;
|
||||
|
||||
/**
|
||||
* @since 4.3.0-SNAPSHOT
|
||||
*/
|
||||
class JLatextAsyncDrawable extends AsyncDrawable {
|
||||
|
||||
private final boolean isBlock;
|
||||
|
||||
JLatextAsyncDrawable(
|
||||
@NonNull String destination,
|
||||
@NonNull AsyncDrawableLoader loader,
|
||||
@NonNull ImageSizeResolver imageSizeResolver,
|
||||
@Nullable ImageSize imageSize,
|
||||
boolean isBlock
|
||||
) {
|
||||
super(destination, loader, imageSizeResolver, imageSize);
|
||||
this.isBlock = isBlock;
|
||||
}
|
||||
|
||||
public boolean isBlock() {
|
||||
return isBlock;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package io.noties.markwon.sample.latex;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.widget.TextView;
|
||||
|
||||
@ -14,7 +14,6 @@ import io.noties.markwon.ext.latex.JLatexMathTheme;
|
||||
import io.noties.markwon.sample.ActivityWithMenuOptions;
|
||||
import io.noties.markwon.sample.MenuOptions;
|
||||
import io.noties.markwon.sample.R;
|
||||
import ru.noties.jlatexmath.JLatexMathDrawable;
|
||||
|
||||
public class LatexActivity extends ActivityWithMenuOptions {
|
||||
|
||||
@ -87,7 +86,7 @@ public class LatexActivity extends ActivityWithMenuOptions {
|
||||
private static String wrapLatexInSampleMarkdown(@NonNull String latex) {
|
||||
return "" +
|
||||
"# Example of LaTeX\n\n" +
|
||||
"(inline): $$" + latex + "$$ so nice, really? Now, (block):\n\n" +
|
||||
"(inline): $$" + latex + "$$ so nice, really-really really-really really-really? Now, (block):\n\n" +
|
||||
"$$\n" +
|
||||
"" + latex + "\n" +
|
||||
"$$\n\n" +
|
||||
@ -95,23 +94,22 @@ public class LatexActivity extends ActivityWithMenuOptions {
|
||||
}
|
||||
|
||||
private void render(@NonNull String markdown) {
|
||||
|
||||
final float textSize = textView.getTextSize();
|
||||
final Resources r = getResources();
|
||||
|
||||
final Markwon markwon = Markwon.builder(this)
|
||||
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), new JLatexMathPlugin.BuilderConfigure() {
|
||||
@Override
|
||||
public void configureBuilder(@NonNull JLatexMathPlugin.Builder builder) {
|
||||
builder
|
||||
.backgroundProvider(new JLatexMathTheme.BackgroundProvider() {
|
||||
@NonNull
|
||||
@Override
|
||||
public Drawable provide() {
|
||||
return new ColorDrawable(0x40ff0000);
|
||||
}
|
||||
})
|
||||
.fitCanvas(true)
|
||||
.align(JLatexMathDrawable.ALIGN_CENTER)
|
||||
.padding(48)
|
||||
;
|
||||
}
|
||||
.usePlugin(JLatexMathPlugin.create(textSize, textSize * 1.25F, builder -> {
|
||||
builder.theme()
|
||||
.inlineBackgroundProvider(() -> new ColorDrawable(0x1000ff00))
|
||||
.blockBackgroundProvider(() -> new ColorDrawable(0x10ff0000))
|
||||
.blockPadding(JLatexMathTheme.Padding.symmetric(
|
||||
r.getDimensionPixelSize(R.dimen.latex_block_padding_vertical),
|
||||
r.getDimensionPixelSize(R.dimen.latex_block_padding_horizontal)
|
||||
));
|
||||
|
||||
// explicitly request LEGACY rendering mode
|
||||
// builder.renderMode(JLatexMathPlugin.RenderMode.LEGACY);
|
||||
}))
|
||||
.build();
|
||||
|
||||
|
5
sample/src/main/res/values/dimens.xml
Normal file
5
sample/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="latex_block_padding_vertical">8dip</dimen>
|
||||
<dimen name="latex_block_padding_horizontal">16dip</dimen>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user