-
Notifications
You must be signed in to change notification settings - Fork 526
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #3201: Setup Kotlitex library to render raw latex #3194
Changes from all commits
cea9656
aff03cd
8bcf1a8
60284b0
9c111bf
bdd7cb6
c0ee037
6b1edbb
08441ad
8cc0541
ad683e3
4ee9606
1a1a8ff
b6709c4
9d16797
f553c8b
9c88465
a236847
8605427
c8356ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,7 @@ dependencies { | |
'androidx.appcompat:appcompat:1.0.2', | ||
'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha03', | ||
'androidx.work:work-runtime-ktx:2.4.0', | ||
'com.github.anandwana001:kotlitex:3e8a0804041121ece4b57099ac3de386d78e0996', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the latest commit, we might use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we actually will want to snapshot to a specific version (like we do elsewhere). Even though we control the repository, it's helpful to keep builds as deterministic as possible so there are no unexpected surprises. |
||
'com.github.oppia:androidsvg:6bd15f69caee3e6857fcfcd123023716b4adec1d', | ||
'com.github.bumptech.glide:glide:4.11.0', | ||
'com.google.dagger:dagger:2.24', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
package org.oppia.android.util.parser.html | ||
|
||
import android.content.res.AssetManager | ||
import android.text.Editable | ||
import android.text.Spannable | ||
import android.text.style.ImageSpan | ||
import io.github.karino2.kotlitex.view.MathExpressionSpan | ||
import org.json.JSONObject | ||
import org.oppia.android.util.logging.ConsoleLogger | ||
import org.xml.sax.Attributes | ||
|
@@ -16,7 +18,8 @@ private const val CUSTOM_MATH_SVG_PATH_ATTRIBUTE = "math_content-with-value" | |
* [CustomHtmlContentHandler]. | ||
*/ | ||
class MathTagHandler( | ||
private val consoleLogger: ConsoleLogger | ||
private val consoleLogger: ConsoleLogger, | ||
private val assetManager: AssetManager | ||
) : CustomHtmlContentHandler.CustomTagHandler { | ||
override fun handleTag( | ||
attributes: Attributes, | ||
|
@@ -30,36 +33,48 @@ class MathTagHandler( | |
attributes.getJsonObjectValue(CUSTOM_MATH_SVG_PATH_ATTRIBUTE) | ||
) | ||
if (content != null) { | ||
// Insert an image span where the custom tag currently is to load the SVG. In the future, this | ||
// could also load a LaTeX span, instead. Note that this approach is based on Android's Html | ||
// parser. | ||
val drawable = | ||
imageRetriever.loadDrawable( | ||
content.svgFilename, | ||
CustomHtmlContentHandler.ImageRetriever.Type.INLINE_TEXT_IMAGE | ||
) | ||
val (startIndex, endIndex) = output.run { | ||
// Use a control character to ensure that there's at least 1 character on which to "attach" | ||
// the image when rendering the HTML. | ||
val startIndex = length | ||
append('\uFFFC') | ||
return@run startIndex to length | ||
} | ||
output.setSpan( | ||
ImageSpan(drawable, content.svgFilename), | ||
startIndex, | ||
endIndex, | ||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE | ||
) | ||
if (content.svgFilename != null) { | ||
// Insert an image span where the custom tag currently is to load the SVG. In the future, this | ||
// could also load a LaTeX span, instead. Note that this approach is based on Android's Html | ||
// parser. | ||
val drawable = | ||
imageRetriever.loadDrawable( | ||
content.svgFilename, | ||
CustomHtmlContentHandler.ImageRetriever.Type.INLINE_TEXT_IMAGE | ||
) | ||
output.setSpan( | ||
ImageSpan(drawable, content.svgFilename), | ||
startIndex, | ||
endIndex, | ||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE | ||
) | ||
} else if (content.rawLatex != null) { | ||
output.setSpan( | ||
MathExpressionSpan(content.rawLatex, 72f, assetManager, true), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to come up with the base text height or might need a way to calculate and pass it here as an argument. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use 16 sp |
||
startIndex, | ||
endIndex, | ||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE | ||
) | ||
} | ||
} else consoleLogger.e("MathTagHandler", "Failed to parse math tag") | ||
} | ||
|
||
private data class MathContent(val rawLatex: String, val svgFilename: String) { | ||
private data class MathContent(val rawLatex: String?, val svgFilename: String?) { | ||
companion object { | ||
internal fun parseMathContent(obj: JSONObject?): MathContent? { | ||
val rawLatex = obj?.getOptionalString("raw_latex") | ||
val svgFilename = obj?.getOptionalString("svg_filename") | ||
return if (rawLatex != null && svgFilename != null) { | ||
return if (rawLatex != null && svgFilename == null) { | ||
rawLatex.replace("\\", "\\\\") | ||
MathContent(rawLatex, svgFilename) | ||
} else if (svgFilename != null) { | ||
MathContent(rawLatex, svgFilename) | ||
} else null | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ import dagger.BindsInstance | |
import dagger.Component | ||
import dagger.Module | ||
import org.junit.Before | ||
import org.junit.Ignore | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
@@ -65,12 +66,20 @@ class MathTagHandlerTest { | |
@JvmField | ||
val mockitoRule: MockitoRule = MockitoJUnit.rule() | ||
|
||
@Mock lateinit var mockImageRetriever: FakeImageRetriever | ||
@Captor lateinit var stringCaptor: ArgumentCaptor<String> | ||
@Captor lateinit var retrieverTypeCaptor: ArgumentCaptor<ImageRetriever.Type> | ||
@Mock | ||
lateinit var mockImageRetriever: FakeImageRetriever | ||
|
||
@Inject lateinit var context: Context | ||
@Inject lateinit var consoleLogger: ConsoleLogger | ||
@Captor | ||
lateinit var stringCaptor: ArgumentCaptor<String> | ||
|
||
@Captor | ||
lateinit var retrieverTypeCaptor: ArgumentCaptor<ImageRetriever.Type> | ||
|
||
@Inject | ||
lateinit var context: Context | ||
|
||
@Inject | ||
lateinit var consoleLogger: ConsoleLogger | ||
|
||
private lateinit var noTagHandlers: Map<String, CustomTagHandler> | ||
private lateinit var tagHandlersWithMathSupport: Map<String, CustomTagHandler> | ||
|
@@ -80,7 +89,7 @@ class MathTagHandlerTest { | |
setUpTestApplicationComponent() | ||
noTagHandlers = mapOf() | ||
tagHandlersWithMathSupport = mapOf( | ||
CUSTOM_MATH_TAG to MathTagHandler(consoleLogger) | ||
CUSTOM_MATH_TAG to MathTagHandler(consoleLogger, context.assets) | ||
) | ||
} | ||
|
||
|
@@ -141,6 +150,9 @@ class MathTagHandlerTest { | |
} | ||
|
||
@Test | ||
@Ignore | ||
// There are some cases in alpha content where we don't have raw latex but we do have | ||
// svg image, so can we use that image without raw latex? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. earlier we show only when both raw latex and SVG image is available in data, but as per our last discussion and from the alpha data, there are cases where we might have raw latex or svg or both of them, so I had conditioned them here. |
||
fun testParseHtml_withMathMarkup_missingRawLatex_doesNotIncludeImageSpan() { | ||
val parsedHtml = | ||
CustomHtmlContentHandler.fromHtml( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
once confirmed, we will move this repo to org and update in this PR only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The need of creating our own version is because we need to use this in Bazel and also the repo's current min SDK is 21 and our min SDK is 19, so need to update as well.
Also, I had removed the sample apps from the kotlitex repo, as it contains various resources like drawable, mipmap, and other colors, styles, etc which cause errors for our Bazel and Gradle for duplicate of items.