diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexBlockImageSizeResolver.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexBlockImageSizeResolver.java new file mode 100644 index 00000000..7e9ac95c --- /dev/null +++ b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexBlockImageSizeResolver.java @@ -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; + } +} diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexInlineAsyncDrawableSpan.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexInlineAsyncDrawableSpan.java new file mode 100644 index 00000000..10b59837 --- /dev/null +++ b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexInlineAsyncDrawableSpan.java @@ -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; + } +} diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParser.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParser.java index 3ef61a24..ef65bb15 100644 --- a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParser.java +++ b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParser.java @@ -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)) { diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParserLegacy.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParserLegacy.java new file mode 100644 index 00000000..1a5ce282 --- /dev/null +++ b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParserLegacy.java @@ -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(); + } + } +} diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathPlugin.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathPlugin.java index d758be38..5ecc0723 100644 --- a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathPlugin.java +++ b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathPlugin.java @@ -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() { - @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() { + @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(); } } } diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathTheme.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathTheme.java index 30b9e04a..8a1d8801 100644 --- a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathTheme.java +++ b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathTheme.java @@ -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); } } diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatextAsyncDrawable.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatextAsyncDrawable.java new file mode 100644 index 00000000..4376d636 --- /dev/null +++ b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatextAsyncDrawable.java @@ -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; + } +} diff --git a/sample/src/main/java/io/noties/markwon/sample/latex/LatexActivity.java b/sample/src/main/java/io/noties/markwon/sample/latex/LatexActivity.java index c669cde0..075c2ca4 100644 --- a/sample/src/main/java/io/noties/markwon/sample/latex/LatexActivity.java +++ b/sample/src/main/java/io/noties/markwon/sample/latex/LatexActivity.java @@ -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(); diff --git a/sample/src/main/res/values/dimens.xml b/sample/src/main/res/values/dimens.xml new file mode 100644 index 00000000..b88d4ed5 --- /dev/null +++ b/sample/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 8dip + 16dip + \ No newline at end of file