Sanitize latex text placeholder (no new lines)
This commit is contained in:
		
							parent
							
								
									85f201702e
								
							
						
					
					
						commit
						8daa59709b
					
				| @ -8,7 +8,7 @@ android.enableJetifier=true | |||||||
| android.enableBuildCache=true | android.enableBuildCache=true | ||||||
| android.buildCacheDir=build/pre-dex-cache | android.buildCacheDir=build/pre-dex-cache | ||||||
| 
 | 
 | ||||||
| VERSION_NAME=4.0.1 | VERSION_NAME=4.0.2-SNAPSHOT | ||||||
| 
 | 
 | ||||||
| GROUP=io.noties.markwon | GROUP=io.noties.markwon | ||||||
| POM_DESCRIPTION=Markwon markdown for Android | POM_DESCRIPTION=Markwon markdown for Android | ||||||
|  | |||||||
| @ -16,8 +16,14 @@ android { | |||||||
| dependencies { | dependencies { | ||||||
| 
 | 
 | ||||||
|     api project(':markwon-core') |     api project(':markwon-core') | ||||||
|     api project(':markwon-image') | 
 | ||||||
|     api deps['jlatexmath-android'] |     api deps['jlatexmath-android'] | ||||||
|  | 
 | ||||||
|  |     deps['test'].with { | ||||||
|  |         testImplementation it['junit'] | ||||||
|  |         testImplementation it['robolectric'] | ||||||
|  |         testImplementation it['mockito'] | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| registerArtifact(this) | registerArtifact(this) | ||||||
| @ -12,6 +12,7 @@ import android.widget.TextView; | |||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||||
| import androidx.annotation.Px; | import androidx.annotation.Px; | ||||||
|  | import androidx.annotation.VisibleForTesting; | ||||||
| 
 | 
 | ||||||
| import org.commonmark.parser.Parser; | import org.commonmark.parser.Parser; | ||||||
| 
 | 
 | ||||||
| @ -84,8 +85,8 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | |||||||
| 
 | 
 | ||||||
|         // @since 4.0.0 |         // @since 4.0.0 | ||||||
|         private final int paddingHorizontal; |         private final int paddingHorizontal; | ||||||
|         // @since 4.0.0 |  | ||||||
| 
 | 
 | ||||||
|  |         // @since 4.0.0 | ||||||
|         private final int paddingVertical; |         private final int paddingVertical; | ||||||
| 
 | 
 | ||||||
|         // @since 4.0.0 |         // @since 4.0.0 | ||||||
| @ -132,7 +133,10 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | |||||||
| 
 | 
 | ||||||
|                 final int length = visitor.length(); |                 final int length = visitor.length(); | ||||||
| 
 | 
 | ||||||
|                 visitor.builder().append(latex); |                 // @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 MarkwonConfiguration configuration = visitor.configuration(); | ||||||
| 
 | 
 | ||||||
| @ -161,6 +165,13 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | |||||||
|         AsyncDrawableScheduler.schedule(textView); |         AsyncDrawableScheduler.schedule(textView); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // @since 4.0.2 | ||||||
|  |     @VisibleForTesting | ||||||
|  |     @NonNull | ||||||
|  |     static String prepareLatexTextPlaceholder(@NonNull String latex) { | ||||||
|  |         return latex.replace('\n', ' ').trim(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public static class Builder { |     public static class Builder { | ||||||
| 
 | 
 | ||||||
|         private final float textSize; |         private final float textSize; | ||||||
| @ -350,6 +361,8 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | |||||||
|             final Rect imageBounds = drawable.getResult().getBounds(); |             final Rect imageBounds = drawable.getResult().getBounds(); | ||||||
|             final int canvasWidth = drawable.getLastKnownCanvasWidth(); |             final int canvasWidth = drawable.getLastKnownCanvasWidth(); | ||||||
| 
 | 
 | ||||||
|  |             // todo: scale down when formula is greater than width (keep ratio and apply height) | ||||||
|  | 
 | ||||||
|             if (fitCanvas |             if (fitCanvas | ||||||
|                     && imageBounds.width() < canvasWidth) { |                     && imageBounds.width() < canvasWidth) { | ||||||
|                 // we increase only width (keep height as-is) |                 // we increase only width (keep height as-is) | ||||||
|  | |||||||
| @ -0,0 +1,113 @@ | |||||||
|  | package io.noties.markwon.ext.latex; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | 
 | ||||||
|  | import org.commonmark.parser.Parser; | ||||||
|  | import org.commonmark.parser.block.BlockParserFactory; | ||||||
|  | import org.junit.Test; | ||||||
|  | import org.junit.runner.RunWith; | ||||||
|  | import org.mockito.ArgumentCaptor; | ||||||
|  | import org.robolectric.RobolectricTestRunner; | ||||||
|  | import org.robolectric.annotation.Config; | ||||||
|  | 
 | ||||||
|  | import java.util.concurrent.ExecutorService; | ||||||
|  | 
 | ||||||
|  | import io.noties.markwon.MarkwonConfiguration; | ||||||
|  | import io.noties.markwon.MarkwonVisitor; | ||||||
|  | import io.noties.markwon.SpannableBuilder; | ||||||
|  | 
 | ||||||
|  | import static org.junit.Assert.assertFalse; | ||||||
|  | import static org.junit.Assert.assertTrue; | ||||||
|  | import static org.mockito.ArgumentMatchers.any; | ||||||
|  | import static org.mockito.ArgumentMatchers.eq; | ||||||
|  | import static org.mockito.Mockito.mock; | ||||||
|  | import static org.mockito.Mockito.times; | ||||||
|  | import static org.mockito.Mockito.verify; | ||||||
|  | import static org.mockito.Mockito.when; | ||||||
|  | 
 | ||||||
|  | @RunWith(RobolectricTestRunner.class) | ||||||
|  | @Config(manifest = Config.NONE) | ||||||
|  | public class JLatexMathPluginTest { | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void latex_text_placeholder() { | ||||||
|  |         // text placeholder cannot have new-line characters and should be trimmed from ends | ||||||
|  | 
 | ||||||
|  |         final String[] in = { | ||||||
|  |                 "hello", | ||||||
|  |                 "he\nllo", | ||||||
|  |                 " hello\n\n", | ||||||
|  |                 "\n\nhello\n\n", | ||||||
|  |                 "\n", | ||||||
|  |                 " \nhello\n " | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         for (String latex : in) { | ||||||
|  |             final String placeholder = JLatexMathPlugin.prepareLatexTextPlaceholder(latex); | ||||||
|  |             assertTrue(placeholder, placeholder.indexOf('\n') < 0); | ||||||
|  |             if (placeholder.length() > 0) { | ||||||
|  |                 assertFalse(placeholder, Character.isWhitespace(placeholder.charAt(0))); | ||||||
|  |                 assertFalse(placeholder, Character.isWhitespace(placeholder.charAt(placeholder.length() - 1))); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void block_parser_registered() { | ||||||
|  |         final JLatexMathPlugin plugin = JLatexMathPlugin.create(0); | ||||||
|  |         final Parser.Builder builder = mock(Parser.Builder.class); | ||||||
|  |         plugin.configureParser(builder); | ||||||
|  |         verify(builder, times(1)).customBlockParserFactory(any(BlockParserFactory.class)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void visitor_registered() { | ||||||
|  |         final JLatexMathPlugin plugin = JLatexMathPlugin.create(0); | ||||||
|  |         final MarkwonVisitor.Builder builder = mock(MarkwonVisitor.Builder.class); | ||||||
|  |         plugin.configureVisitor(builder); | ||||||
|  |         //noinspection unchecked | ||||||
|  |         verify(builder, times(1)) | ||||||
|  |                 .on(eq(JLatexMathBlock.class), any(MarkwonVisitor.NodeVisitor.class)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void visit() { | ||||||
|  |         final JLatexMathPlugin plugin = JLatexMathPlugin.create(0, new JLatexMathPlugin.BuilderConfigure() { | ||||||
|  |             @Override | ||||||
|  |             public void configureBuilder(@NonNull JLatexMathPlugin.Builder builder) { | ||||||
|  |                 // no async in test (nooped for this test) | ||||||
|  |                 builder.executorService(mock(ExecutorService.class)); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         final MarkwonVisitor.Builder builder = mock(MarkwonVisitor.Builder.class); | ||||||
|  |         final ArgumentCaptor<MarkwonVisitor.NodeVisitor> captor = | ||||||
|  |                 ArgumentCaptor.forClass(MarkwonVisitor.NodeVisitor.class); | ||||||
|  |         plugin.configureVisitor(builder); | ||||||
|  |         //noinspection unchecked | ||||||
|  |         verify(builder, times(1)) | ||||||
|  |                 .on(eq(JLatexMathBlock.class), captor.capture()); | ||||||
|  |         final MarkwonVisitor.NodeVisitor nodeVisitor = captor.getValue(); | ||||||
|  | 
 | ||||||
|  |         final MarkwonVisitor visitor = mock(MarkwonVisitor.class); | ||||||
|  |         final JLatexMathBlock block = mock(JLatexMathBlock.class); | ||||||
|  |         when(block.latex()).thenReturn(" first\nsecond\n "); | ||||||
|  | 
 | ||||||
|  |         final SpannableBuilder spannableBuilder = mock(SpannableBuilder.class); | ||||||
|  |         when(visitor.builder()).thenReturn(spannableBuilder); | ||||||
|  |         when(visitor.configuration()).thenReturn(mock(MarkwonConfiguration.class)); | ||||||
|  | 
 | ||||||
|  |         //noinspection unchecked | ||||||
|  |         nodeVisitor.visit(visitor, block); | ||||||
|  | 
 | ||||||
|  |         verify(block, times(1)).latex(); | ||||||
|  |         verify(visitor, times(1)).length(); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class); | ||||||
|  |         verify(spannableBuilder, times(1)).append(stringArgumentCaptor.capture()); | ||||||
|  | 
 | ||||||
|  |         final String placeholder = stringArgumentCaptor.getValue(); | ||||||
|  |         assertTrue(placeholder, placeholder.indexOf('\n') < 0); | ||||||
|  | 
 | ||||||
|  |         verify(visitor, times(1)).setSpans(eq(0), any()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -68,20 +68,23 @@ public class LatexActivity extends Activity { | |||||||
|                 .build(); |                 .build(); | ||||||
| 
 | 
 | ||||||
|         if (true) { |         if (true) { | ||||||
|             final String l = "$$\n" + | //            final String l = "$$\n" + | ||||||
|                     "  P(X=r)=\\frac{\\lambda^r e^{-\\lambda}}{r!}\n" + | //                    "  P(X=r)=\\frac{\\lambda^r e^{-\\lambda}}{r!}\n" + | ||||||
|                     "$$\n" + | //                    "$$\n" + | ||||||
|                     "\n" + | //                    "\n" + | ||||||
|                     "$$\n" + | //                    "$$\n" + | ||||||
|                     "  P(X<r)=P(X<r-1)\n" + | //                    "  P(X<r)=P(X<r-1)\n" + | ||||||
|                     "$$\n" + | //                    "$$\n" + | ||||||
|                     "\n" + | //                    "\n" + | ||||||
|                     "$$\n" + | //                    "$$\n" + | ||||||
|                     "  P(X>r)=1-P(X<r=1)\n" + | //                    "  P(X>r)=1-P(X<r=1)\n" + | ||||||
|                     "$$\n" + | //                    "$$\n" + | ||||||
|                     "\n" + | //                    "\n" + | ||||||
|                     "$$\n" + | //                    "$$\n" + | ||||||
|                     "  \\text{Variance} = \\lambda\n" + | //                    "  \\text{Variance} = \\lambda\n" + | ||||||
|  | //                    "$$"; | ||||||
|  |             final String l = "$$ \n" + | ||||||
|  |                     "    \\sigma_T^2 = \\frac{1-p}{p^2}\n" + | ||||||
|                     "$$"; |                     "$$"; | ||||||
|             markwon.setMarkdown(textView, l); |             markwon.setMarkdown(textView, l); | ||||||
|             return; |             return; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov