diff --git a/AndroidMath.iml b/AndroidMath.iml deleted file mode 100644 index 4e2a15d..0000000 --- a/AndroidMath.iml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle index dcd9d11..3e28720 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:3.5.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' // Add this line diff --git a/mathdisplaylib/mathdisplaylib.iml b/mathdisplaylib/mathdisplaylib.iml deleted file mode 100644 index 585934b..0000000 --- a/mathdisplaylib/mathdisplaylib.iml +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/mathdisplaylib/src/main/java/com/agog/mathdisplay/MTMathGenerator.kt b/mathdisplaylib/src/main/java/com/agog/mathdisplay/MTMathGenerator.kt index 383bafb..9e39949 100644 --- a/mathdisplaylib/src/main/java/com/agog/mathdisplay/MTMathGenerator.kt +++ b/mathdisplaylib/src/main/java/com/agog/mathdisplay/MTMathGenerator.kt @@ -2,7 +2,7 @@ package com.agog.mathdisplay import android.graphics.Bitmap import android.graphics.Canvas -import android.graphics.Color +import android.util.Log import com.agog.mathdisplay.parse.MTLineStyle import com.agog.mathdisplay.parse.MTMathListBuilder import com.agog.mathdisplay.render.MTFont @@ -11,28 +11,30 @@ import com.agog.mathdisplay.render.trim object MTMathGenerator { + private const val TAG = "MTMathGenerator" private const val defaultWidth = 640 private const val defaultHeight = 480 private const val defaultMargin = 20 private const val defaultFontSize = 20f var defaultFont: MTFont? = null + var isDebugOn: Boolean = false @JvmStatic - fun createBitmap(latexString: String): Bitmap? { + fun createBitmap(latexString: String, isDebugOn: Boolean = false): Bitmap? { if (defaultFont == null) initializeDefaultFont() - return createBitmap(latexString, defaultFont, defaultWidth, defaultHeight, defaultMargin) + return createBitmap(latexString, defaultFont, defaultWidth, defaultHeight, defaultMargin, isDebugOn) } @JvmStatic - fun createBitmap(latexString: String, fontColor: Int): Bitmap? { + fun createBitmap(latexString: String, fontColor: Int, isDebugOn: Boolean = false): Bitmap? { defaultFont?.color = fontColor - return createBitmap(latexString, defaultFont, defaultWidth, defaultHeight, defaultMargin) + return createBitmap(latexString, defaultFont, defaultWidth, defaultHeight, defaultMargin, isDebugOn) } @JvmStatic - fun createBitmap(latexString: String, font: MTFont): Bitmap? { - return createBitmap(latexString, font, defaultWidth, defaultHeight, defaultMargin) + fun createBitmap(latexString: String, font: MTFont, isDebugOn: Boolean = false): Bitmap? { + return createBitmap(latexString, font, defaultWidth, defaultHeight, defaultMargin, isDebugOn) } @JvmStatic @@ -41,15 +43,19 @@ object MTMathGenerator { fontParam: MTFont? = defaultFont, bitmapWidth: Int = defaultWidth, bitmapHeight: Int = defaultHeight, - bitmapMargin: Int = defaultMargin + bitmapMargin: Int = defaultMargin, + isDebugOn: Boolean = false ): Bitmap? { + this.isDebugOn = isDebugOn + if (!passScreening(latexString)) return null + var font = fontParam if (defaultFont == null) { initializeDefaultFont() if (font == null) font = defaultFont } - val mathList = MTMathListBuilder.buildFromString(latexString) + val mathList = MTMathListBuilder.buildFromString(sanitize(latexString)) if (mathList != null && font != null) { val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888) @@ -68,9 +74,60 @@ object MTMathGenerator { return null } + private fun passScreening(latexString: String): Boolean { + val str = latexString.toLowerCase() + + if (str.contains("\\color")) { + log("[filter] denied \\color syntax on: $str") + return false + } + + if (str.contains("{array}")) { + log("[filter] denied {array} syntax on: $str") + return false + } + + if (str.contains("matrix}")) { + log("[filter] denied *matrix} syntax on: $str") + return false + } + + if (str.contains("\\cancel}")) { + log("[filter] denied \\cancel syntax on: $str") + return false + } + + return true + } + + private fun log(message: String) { + if (isDebugOn) { + Log.i(TAG, message) + } + } + private fun initializeDefaultFont() { if (defaultFont == null) { defaultFont = MTFontManager.latinModernFontWithSize(defaultFontSize) } } + + private fun sanitize(str: String): String { + var sanitizedStr = str + log("[sanitize] before : $sanitizedStr") + + // convert to single line + sanitizedStr = sanitizedStr.replace("\n", "") + sanitizedStr = sanitizedStr.replace("\r", "") + + // fix long minus sign + sanitizedStr = sanitizedStr.replace("−", "-") + + // fix align + sanitizedStr = sanitizedStr.replace("{align}", "{aligned}") + sanitizedStr = sanitizedStr.replace("{align*}", "{aligned}") + + log("[sanitize] after : $sanitizedStr") + return sanitizedStr + } } diff --git a/sampleapp/build.gradle b/sampleapp/build.gradle index b3845d0..5b96bba 100644 --- a/sampleapp/build.gradle +++ b/sampleapp/build.gradle @@ -3,12 +3,10 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' - android { compileSdkVersion 27 - defaultConfig { applicationId "com.agog.latexmathsample" minSdkVersion 23 @@ -39,4 +37,3 @@ dependencies { androidTestImplementation 'com.android.support.test:runner:1.0.1' implementation project(':mathdisplaylib') } - diff --git a/sampleapp/sampleapp.iml b/sampleapp/sampleapp.iml deleted file mode 100644 index 46b422e..0000000 --- a/sampleapp/sampleapp.iml +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sampleapp/src/main/AndroidManifest.xml b/sampleapp/src/main/AndroidManifest.xml index a8c62f5..9497db6 100644 --- a/sampleapp/src/main/AndroidManifest.xml +++ b/sampleapp/src/main/AndroidManifest.xml @@ -9,8 +9,9 @@ android:roundIcon="@mipmap/icon" android:supportsRtl="true" android:theme="@style/AppTheme"> + diff --git a/sampleapp/src/main/java/com/agog/latexmathsample/CheckActivity.kt b/sampleapp/src/main/java/com/agog/latexmathsample/CheckActivity.kt new file mode 100644 index 0000000..1885511 --- /dev/null +++ b/sampleapp/src/main/java/com/agog/latexmathsample/CheckActivity.kt @@ -0,0 +1,145 @@ +package com.agog.latexmathsample + +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.Matrix +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.agog.mathdisplay.MTMathGenerator +import kotlinx.android.synthetic.main.activity_check.* + + +class CheckActivity : AppCompatActivity() { + + companion object { + fun createIntent(context: Context): Intent { + return Intent(context, CheckActivity::class.java) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_check) + + checkLatexCompatibility() + } + + private fun bitmapToDrawable(bitmap: Bitmap): Drawable { + val drawable = BitmapDrawable(resources, bitmap) + drawable.setBounds(0, 0, bitmap.width, bitmap.height) + + return drawable + } + + private fun checkLatexCompatibility() { + val layoutPad = 16 + val layoutParams = LinearLayout.LayoutParams(0, 0) + layoutParams.setMargins(layoutPad, layoutPad, layoutPad, layoutPad) + + val inputStream = resources.openRawResource(R.raw.checklatex) + val lineList = mutableListOf() + + inputStream.bufferedReader().useLines { lines -> lines.forEach { lineList.add(it) } } + + // create test(s) from checklatex.txt file + lineList.forEach { + if (it.isNotBlank()) { + if (it[0] == '#') { + val tv = TextView(this) + tv.text = it.trim() + tv.setTextColor(Color.DKGRAY) + println("textSize ${tv.textSize}") + checkLatexLayout.addView(tv) + } else { + createLatexImageView(it) + + } + } + } + + runMultilineTest() + } + + private fun createLatexImageView(strLatex: String) { + val latexBitmap: Bitmap? = MTMathGenerator.createBitmap(strLatex, isDebugOn = true) + + if (latexBitmap != null) { + val latexImageView = createImageView(latexBitmap) + checkLatexLayout.addView(latexImageView) + } else { + val errorRenderTV = TextView(this) + errorRenderTV.text = "error rendering: $strLatex" + errorRenderTV.setTextColor(Color.RED) + checkLatexLayout.addView(errorRenderTV) + } + } + + private fun runMultilineTest() { + val multilineStr = "\\left|\\begin{matrix}\n" + + "6 & 1 & 3\\\\ \n" + + "2 & 3 & 2\\\\ \n" + + "1 & 1 & 0\\end{matrix}\\right|\n" + + "=\n" + + "\\left|\\begin{matrix}\n" + + "\\require{cancel}\\cancel{6} & \\require{cancel}\\cancel{1} & \\require{cancel}\\cancel{3}\\\\ \n" + + "\\require{cancel}\\cancel{2} & \\require{cancel}\\cancel{3} & \\require{cancel}\\cancel{2}\\\\ \n" + + "\\require{cancel}\\cancel{1} & \\require{cancel}\\cancel{1} & \\require{cancel}\\cancel{0}\\end{matrix}\\right|\n" + + "\\begin{matrix}\\require{cancel}\\cancel{6} & \\require{cancel}\\cancel{1}\\\\ \n" + + "\\require{cancel}\\cancel{2} & \\require{cancel}\\cancel{3} \\\\ \n" + + "\\require{cancel}\\cancel{1} & \\require{cancel}\\cancel{1}\\end{matrix} " + + createLatexImageView(multilineStr) + } + + // from this: https://stackoverflow.com/questions/8232608/fit-image-into-imageview-keep-aspect-ratio-and-then-resize-imageview-to-image-d + // change it to have bitmap as param and return new ImageView instead + @Throws(NoSuchElementException::class) + private fun createImageView(bitmap: Bitmap): ImageView { // Get bitmap from the the ImageView. + val imageView = ImageView(this) + imageView.setImageBitmap(bitmap) + + val drawing = imageView.drawable + val viewBitmap = (drawing as BitmapDrawable).bitmap + + // Get current dimensions AND the desired bounding box + var width = viewBitmap.width + var height = viewBitmap.height + val bounding = dpToPx(120) + val xScale = bounding.toFloat() / width + val yScale = bounding.toFloat() / height + val scale = if (xScale <= yScale) xScale else yScale + + // Create a matrix for the scaling and add the scaling data + val matrix = Matrix() + matrix.postScale(scale, scale) + + // Create a new bitmap and convert it to a format understood by the ImageView + val scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true) + width = scaledBitmap.width // re-use + height = scaledBitmap.height // re-use + val result = BitmapDrawable(scaledBitmap) + + // Apply the scaled bitmap + imageView.setImageDrawable(result) + + // Now change ImageView's dimensions to match the scaled image + val params = LinearLayout.LayoutParams(0, 0) + params.width = width + params.height = height + imageView.layoutParams = params + + return imageView + } + + private fun dpToPx(dp: Int): Int { + val density = applicationContext.resources.displayMetrics.density + return Math.round(dp.toFloat() * density) + } +} diff --git a/sampleapp/src/main/java/com/agog/latexmathsample/MainActivity.kt b/sampleapp/src/main/java/com/agog/latexmathsample/MainActivity.kt index f870a2c..023ff45 100644 --- a/sampleapp/src/main/java/com/agog/latexmathsample/MainActivity.kt +++ b/sampleapp/src/main/java/com/agog/latexmathsample/MainActivity.kt @@ -8,6 +8,7 @@ import android.view.MenuItem import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView +import android.widget.Toast import com.agog.mathdisplay.MTFontManager import com.agog.mathdisplay.MTMathGenerator import com.agog.mathdisplay.MTMathView @@ -199,6 +200,11 @@ class MainActivity : AppCompatActivity() { } return true } + R.id.checkMenu -> { + val checkActivityIntent = CheckActivity.createIntent(this) + startActivity(checkActivityIntent) + return true + } else -> super.onOptionsItemSelected(item) diff --git a/sampleapp/src/main/res/layout/activity_check.xml b/sampleapp/src/main/res/layout/activity_check.xml new file mode 100644 index 0000000..86ffde7 --- /dev/null +++ b/sampleapp/src/main/res/layout/activity_check.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/sampleapp/src/main/res/menu/menu_main.xml b/sampleapp/src/main/res/menu/menu_main.xml index 41d936d..a45f42b 100644 --- a/sampleapp/src/main/res/menu/menu_main.xml +++ b/sampleapp/src/main/res/menu/menu_main.xml @@ -4,8 +4,8 @@ tools:context="com.agog.latexmathsample.com.agog.latexmath.MainActivity"> + + - - - - - - - - + android:id="@+id/checkMenu" + android:title="Check" + app:showAsAction="always" /> diff --git a/sampleapp/src/main/res/raw/checklatex.txt b/sampleapp/src/main/res/raw/checklatex.txt new file mode 100644 index 0000000..ccf3a7a --- /dev/null +++ b/sampleapp/src/main/res/raw/checklatex.txt @@ -0,0 +1,7 @@ +# -- Require Cancel Command -- + +# cancel command? + +\cancel{1} + +# cancel not supported