├── example ├── test │ └── widget_test.dart ├── ios │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner.xcodeproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── .gitignore │ ├── Podfile.lock │ └── Podfile ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ └── Icon-512.png │ ├── manifest.json │ └── index.html ├── android │ ├── gradle.properties │ ├── .gitignore │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ └── values │ │ │ │ │ │ └── styles.xml │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ └── build.gradle ├── .metadata ├── README.md ├── pubspec.yaml ├── .gitignore └── lib │ ├── display.dart │ ├── main.dart │ ├── feature.dart │ └── equations.dart ├── lib ├── src │ ├── render │ │ ├── utils │ │ │ ├── get_type_of.dart │ │ │ ├── render_box_offset.dart │ │ │ └── render_box_layout.dart │ │ ├── constants.dart │ │ ├── svg │ │ │ ├── draw_svg_root.dart │ │ │ ├── static.dart │ │ │ ├── svg_string.dart │ │ │ └── delimiter.dart │ │ └── layout │ │ │ ├── remove_baseline.dart │ │ │ ├── reset_baseline.dart │ │ │ └── shift_baseline.dart │ ├── utils │ │ ├── canvas_kit │ │ │ ├── canvas_kit.dart │ │ │ ├── canvas_kit_stub.dart │ │ │ └── canvas_kit_web.dart │ │ ├── wrapper.dart │ │ ├── log.dart │ │ ├── num_extension.dart │ │ ├── render_box_extensions.dart │ │ ├── text_extension.dart │ │ ├── alpha_numeric.dart │ │ └── unicode_literal.dart │ ├── widgets │ │ ├── mode.dart │ │ ├── exception.dart │ │ ├── controller.dart │ │ └── selection │ │ │ ├── focus_manager.dart │ │ │ └── gesture_detector_builder_selectable.dart │ ├── encoder │ │ ├── tex │ │ │ ├── functions │ │ │ │ ├── sqrt.dart │ │ │ │ ├── multiscripts.dart │ │ │ │ ├── accent_under.dart │ │ │ │ ├── stretchy_op.dart │ │ │ │ ├── nary.dart │ │ │ │ └── left_right.dart │ │ │ └── functions.dart │ │ ├── exception.dart │ │ ├── optimization.dart │ │ └── encoder.dart │ ├── parser │ │ └── tex │ │ │ ├── functions │ │ │ ├── custom │ │ │ │ └── cursor.dart │ │ │ ├── katex_ext.dart │ │ │ ├── katex_base │ │ │ │ ├── char.dart │ │ │ │ ├── array.dart │ │ │ │ ├── break.dart │ │ │ │ ├── raise_box.dart │ │ │ │ ├── sqrt.dart │ │ │ │ ├── phantom.dart │ │ │ │ ├── rule.dart │ │ │ │ ├── styling.dart │ │ │ │ ├── text.dart │ │ │ │ ├── sizing.dart │ │ │ │ ├── underover.dart │ │ │ │ ├── accent_under.dart │ │ │ │ ├── mclass.dart │ │ │ │ ├── math.dart │ │ │ │ ├── color.dart │ │ │ │ ├── cr.dart │ │ │ │ └── kern.dart │ │ │ └── katex_ext │ │ │ │ └── not.dart │ │ │ ├── token.dart │ │ │ ├── source_location.dart │ │ │ ├── unicode_accents.dart │ │ │ ├── define_environment.dart │ │ │ ├── symbols_extra.dart │ │ │ └── parse_error.dart │ ├── ast │ │ ├── symbols │ │ │ ├── symbols_extra.dart │ │ │ └── unicode_accents.dart │ │ ├── nodes │ │ │ ├── style.dart │ │ │ ├── raise_box.dart │ │ │ ├── cursor_node.dart │ │ │ ├── phantom.dart │ │ │ ├── function.dart │ │ │ └── under.dart │ │ ├── types.dart │ │ └── spacing.dart │ └── font │ │ └── metrics │ │ └── unicode_scripts.dart ├── katex_fonts │ ├── fonts │ │ ├── KaTeX_Main-Bold.ttf │ │ ├── KaTeX_AMS-Regular.ttf │ │ ├── KaTeX_Fraktur-Bold.ttf │ │ ├── KaTeX_Main-Italic.ttf │ │ ├── KaTeX_Main-Regular.ttf │ │ ├── KaTeX_Math-Italic.ttf │ │ ├── KaTeX_Fraktur-Regular.ttf │ │ ├── KaTeX_Main-BoldItalic.ttf │ │ ├── KaTeX_Math-BoldItalic.ttf │ │ ├── KaTeX_SansSerif-Bold.ttf │ │ ├── KaTeX_Script-Regular.ttf │ │ ├── KaTeX_Size1-Regular.ttf │ │ ├── KaTeX_Size2-Regular.ttf │ │ ├── KaTeX_Size3-Regular.ttf │ │ ├── KaTeX_Size4-Regular.ttf │ │ ├── KaTeX_Caligraphic-Bold.ttf │ │ ├── KaTeX_SansSerif-Italic.ttf │ │ ├── KaTeX_SansSerif-Regular.ttf │ │ ├── KaTeX_Caligraphic-Regular.ttf │ │ └── KaTeX_Typewriter-Regular.ttf │ └── LICENSE ├── tex.dart ├── flutter_math.dart └── ast.dart ├── doc ├── img │ ├── delta.png │ ├── nary.png │ ├── fourier.png │ ├── matrix.png │ ├── leftright.png │ ├── stretchyop.png │ ├── underover.png │ └── schrodinger.png ├── TODO.md └── migration.0.2.0.md ├── test ├── golden │ ├── 29221604.png │ ├── eqnarray.png │ ├── 355369240.png │ ├── 750111939.png │ ├── 860616833.png │ ├── 928722428.png │ ├── enclosure.png │ ├── temp │ │ ├── 586114.png │ │ ├── 18365951.png │ │ ├── 18700801.png │ │ ├── 57682459.png │ │ ├── 62807860.png │ │ ├── 73362715.png │ │ ├── 73599379.png │ │ ├── 1038570769.png │ │ ├── 1060711029.png │ │ ├── 108564718.png │ │ ├── 132707078.png │ │ ├── 138701843.png │ │ ├── 149472409.png │ │ ├── 163922097.png │ │ ├── 167173928.png │ │ ├── 177228769.png │ │ ├── 181148755.png │ │ ├── 192078322.png │ │ ├── 229523423.png │ │ ├── 244578803.png │ │ ├── 311172474.png │ │ ├── 315666507.png │ │ ├── 334789020.png │ │ ├── 364385848.png │ │ ├── 374852144.png │ │ ├── 385527251.png │ │ ├── 387350573.png │ │ ├── 394965460.png │ │ ├── 398283101.png │ │ ├── 410885435.png │ │ ├── 460938518.png │ │ ├── 476918051.png │ │ ├── 485097675.png │ │ ├── 492747826.png │ │ ├── 510403892.png │ │ ├── 510517529.png │ │ ├── 550865336.png │ │ ├── 575741909.png │ │ ├── 627646929.png │ │ ├── 632023797.png │ │ ├── 644274436.png │ │ ├── 646192306.png │ │ ├── 677215405.png │ │ ├── 742393617.png │ │ ├── 748278783.png │ │ ├── 756284122.png │ │ ├── 760483081.png │ │ ├── 760931274.png │ │ ├── 775255816.png │ │ ├── 776562482.png │ │ ├── 780748998.png │ │ ├── 788702447.png │ │ ├── 803180120.png │ │ ├── 810379325.png │ │ ├── 826621488.png │ │ ├── 899333225.png │ │ ├── 905381913.png │ │ ├── 925644139.png │ │ └── 983122783.png │ ├── bar574947087.png │ ├── ddot349178810.png │ ├── dot270815021.png │ ├── hat823583055.png │ ├── vec928772845.png │ ├── acute267057593.png │ ├── breve107975770.png │ ├── check842862033.png │ ├── grave165690505.png │ ├── overline53006047.png │ ├── text-accents-1.png │ ├── text-accents-2.png │ ├── tilde150386117.png │ ├── unicode-accents.png │ ├── utilde829741846.png │ ├── widehat812256357.png │ ├── mathring740120027.png │ ├── underline160699299.png │ ├── widecheck478155530.png │ ├── widetilde326421305.png │ ├── undergroup274196240.png │ ├── Overrightarrow76505663.png │ ├── overleftarrow567687850.png │ ├── overrightarrow138596512.png │ ├── overleftharpoon580184352.png │ ├── overrightharpoon301951104.png │ ├── underleftarrow1031964323.png │ ├── underrightarrow963759956.png │ ├── overleftrightarrow424866188.png │ └── underleftrightarrow128845106.png ├── encoder │ ├── tex │ │ ├── recode.dart │ │ ├── functions │ │ │ ├── sqrt_test.dart │ │ │ ├── nary_test.dart │ │ │ ├── accent_under_test.dart │ │ │ ├── multiscripts_test.dart │ │ │ ├── stretchy_op_test.dart │ │ │ ├── left_right_test.dart │ │ │ ├── function_test.dart │ │ │ ├── frac_test.dart │ │ │ ├── accent_test.dart │ │ │ ├── symbol_test.dart │ │ │ └── style_test.dart │ │ └── encoder_test.dart │ └── matcher_test.dart ├── render_sample_test.dart └── widget_test.dart ├── .metadata ├── .travis.yml ├── .github └── workflows │ └── publish.yml ├── .gitignore └── pubspec.yaml /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | void main() {} 2 | -------------------------------------------------------------------------------- /lib/src/render/utils/get_type_of.dart: -------------------------------------------------------------------------------- 1 | Type getTypeOf() => T; 2 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /doc/img/delta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/doc/img/delta.png -------------------------------------------------------------------------------- /doc/img/nary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/doc/img/nary.png -------------------------------------------------------------------------------- /doc/img/fourier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/doc/img/fourier.png -------------------------------------------------------------------------------- /doc/img/matrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/doc/img/matrix.png -------------------------------------------------------------------------------- /doc/img/leftright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/doc/img/leftright.png -------------------------------------------------------------------------------- /doc/img/stretchyop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/doc/img/stretchyop.png -------------------------------------------------------------------------------- /doc/img/underover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/doc/img/underover.png -------------------------------------------------------------------------------- /doc/img/schrodinger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/doc/img/schrodinger.png -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/web/favicon.png -------------------------------------------------------------------------------- /test/golden/29221604.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/29221604.png -------------------------------------------------------------------------------- /test/golden/eqnarray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/eqnarray.png -------------------------------------------------------------------------------- /test/golden/355369240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/355369240.png -------------------------------------------------------------------------------- /test/golden/750111939.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/750111939.png -------------------------------------------------------------------------------- /test/golden/860616833.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/860616833.png -------------------------------------------------------------------------------- /test/golden/928722428.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/928722428.png -------------------------------------------------------------------------------- /test/golden/enclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/enclosure.png -------------------------------------------------------------------------------- /test/golden/temp/586114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/586114.png -------------------------------------------------------------------------------- /test/golden/bar574947087.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/bar574947087.png -------------------------------------------------------------------------------- /test/golden/ddot349178810.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/ddot349178810.png -------------------------------------------------------------------------------- /test/golden/dot270815021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/dot270815021.png -------------------------------------------------------------------------------- /test/golden/hat823583055.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/hat823583055.png -------------------------------------------------------------------------------- /test/golden/temp/18365951.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/18365951.png -------------------------------------------------------------------------------- /test/golden/temp/18700801.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/18700801.png -------------------------------------------------------------------------------- /test/golden/temp/57682459.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/57682459.png -------------------------------------------------------------------------------- /test/golden/temp/62807860.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/62807860.png -------------------------------------------------------------------------------- /test/golden/temp/73362715.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/73362715.png -------------------------------------------------------------------------------- /test/golden/temp/73599379.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/73599379.png -------------------------------------------------------------------------------- /test/golden/vec928772845.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/vec928772845.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /lib/src/utils/canvas_kit/canvas_kit.dart: -------------------------------------------------------------------------------- 1 | export 'canvas_kit_stub.dart' if (dart.library.html) 'canvas_kit_web.dart'; 2 | -------------------------------------------------------------------------------- /test/golden/acute267057593.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/acute267057593.png -------------------------------------------------------------------------------- /test/golden/breve107975770.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/breve107975770.png -------------------------------------------------------------------------------- /test/golden/check842862033.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/check842862033.png -------------------------------------------------------------------------------- /test/golden/grave165690505.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/grave165690505.png -------------------------------------------------------------------------------- /test/golden/overline53006047.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/overline53006047.png -------------------------------------------------------------------------------- /test/golden/temp/1038570769.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/1038570769.png -------------------------------------------------------------------------------- /test/golden/temp/1060711029.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/1060711029.png -------------------------------------------------------------------------------- /test/golden/temp/108564718.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/108564718.png -------------------------------------------------------------------------------- /test/golden/temp/132707078.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/132707078.png -------------------------------------------------------------------------------- /test/golden/temp/138701843.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/138701843.png -------------------------------------------------------------------------------- /test/golden/temp/149472409.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/149472409.png -------------------------------------------------------------------------------- /test/golden/temp/163922097.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/163922097.png -------------------------------------------------------------------------------- /test/golden/temp/167173928.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/167173928.png -------------------------------------------------------------------------------- /test/golden/temp/177228769.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/177228769.png -------------------------------------------------------------------------------- /test/golden/temp/181148755.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/181148755.png -------------------------------------------------------------------------------- /test/golden/temp/192078322.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/192078322.png -------------------------------------------------------------------------------- /test/golden/temp/229523423.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/229523423.png -------------------------------------------------------------------------------- /test/golden/temp/244578803.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/244578803.png -------------------------------------------------------------------------------- /test/golden/temp/311172474.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/311172474.png -------------------------------------------------------------------------------- /test/golden/temp/315666507.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/315666507.png -------------------------------------------------------------------------------- /test/golden/temp/334789020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/334789020.png -------------------------------------------------------------------------------- /test/golden/temp/364385848.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/364385848.png -------------------------------------------------------------------------------- /test/golden/temp/374852144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/374852144.png -------------------------------------------------------------------------------- /test/golden/temp/385527251.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/385527251.png -------------------------------------------------------------------------------- /test/golden/temp/387350573.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/387350573.png -------------------------------------------------------------------------------- /test/golden/temp/394965460.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/394965460.png -------------------------------------------------------------------------------- /test/golden/temp/398283101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/398283101.png -------------------------------------------------------------------------------- /test/golden/temp/410885435.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/410885435.png -------------------------------------------------------------------------------- /test/golden/temp/460938518.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/460938518.png -------------------------------------------------------------------------------- /test/golden/temp/476918051.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/476918051.png -------------------------------------------------------------------------------- /test/golden/temp/485097675.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/485097675.png -------------------------------------------------------------------------------- /test/golden/temp/492747826.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/492747826.png -------------------------------------------------------------------------------- /test/golden/temp/510403892.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/510403892.png -------------------------------------------------------------------------------- /test/golden/temp/510517529.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/510517529.png -------------------------------------------------------------------------------- /test/golden/temp/550865336.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/550865336.png -------------------------------------------------------------------------------- /test/golden/temp/575741909.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/575741909.png -------------------------------------------------------------------------------- /test/golden/temp/627646929.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/627646929.png -------------------------------------------------------------------------------- /test/golden/temp/632023797.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/632023797.png -------------------------------------------------------------------------------- /test/golden/temp/644274436.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/644274436.png -------------------------------------------------------------------------------- /test/golden/temp/646192306.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/646192306.png -------------------------------------------------------------------------------- /test/golden/temp/677215405.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/677215405.png -------------------------------------------------------------------------------- /test/golden/temp/742393617.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/742393617.png -------------------------------------------------------------------------------- /test/golden/temp/748278783.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/748278783.png -------------------------------------------------------------------------------- /test/golden/temp/756284122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/756284122.png -------------------------------------------------------------------------------- /test/golden/temp/760483081.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/760483081.png -------------------------------------------------------------------------------- /test/golden/temp/760931274.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/760931274.png -------------------------------------------------------------------------------- /test/golden/temp/775255816.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/775255816.png -------------------------------------------------------------------------------- /test/golden/temp/776562482.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/776562482.png -------------------------------------------------------------------------------- /test/golden/temp/780748998.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/780748998.png -------------------------------------------------------------------------------- /test/golden/temp/788702447.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/788702447.png -------------------------------------------------------------------------------- /test/golden/temp/803180120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/803180120.png -------------------------------------------------------------------------------- /test/golden/temp/810379325.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/810379325.png -------------------------------------------------------------------------------- /test/golden/temp/826621488.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/826621488.png -------------------------------------------------------------------------------- /test/golden/temp/899333225.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/899333225.png -------------------------------------------------------------------------------- /test/golden/temp/905381913.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/905381913.png -------------------------------------------------------------------------------- /test/golden/temp/925644139.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/925644139.png -------------------------------------------------------------------------------- /test/golden/temp/983122783.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/temp/983122783.png -------------------------------------------------------------------------------- /test/golden/text-accents-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/text-accents-1.png -------------------------------------------------------------------------------- /test/golden/text-accents-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/text-accents-2.png -------------------------------------------------------------------------------- /test/golden/tilde150386117.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/tilde150386117.png -------------------------------------------------------------------------------- /test/golden/unicode-accents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/unicode-accents.png -------------------------------------------------------------------------------- /test/golden/utilde829741846.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/utilde829741846.png -------------------------------------------------------------------------------- /test/golden/widehat812256357.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/widehat812256357.png -------------------------------------------------------------------------------- /test/golden/mathring740120027.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/mathring740120027.png -------------------------------------------------------------------------------- /test/golden/underline160699299.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/underline160699299.png -------------------------------------------------------------------------------- /test/golden/widecheck478155530.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/widecheck478155530.png -------------------------------------------------------------------------------- /test/golden/widetilde326421305.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/widetilde326421305.png -------------------------------------------------------------------------------- /test/golden/undergroup274196240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/undergroup274196240.png -------------------------------------------------------------------------------- /test/golden/Overrightarrow76505663.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/Overrightarrow76505663.png -------------------------------------------------------------------------------- /test/golden/overleftarrow567687850.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/overleftarrow567687850.png -------------------------------------------------------------------------------- /test/golden/overrightarrow138596512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/overrightarrow138596512.png -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Main-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Main-Bold.ttf -------------------------------------------------------------------------------- /test/golden/overleftharpoon580184352.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/overleftharpoon580184352.png -------------------------------------------------------------------------------- /test/golden/overrightharpoon301951104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/overrightharpoon301951104.png -------------------------------------------------------------------------------- /test/golden/underleftarrow1031964323.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/underleftarrow1031964323.png -------------------------------------------------------------------------------- /test/golden/underrightarrow963759956.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/underrightarrow963759956.png -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_AMS-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_AMS-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Fraktur-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Fraktur-Bold.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Main-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Main-Italic.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Main-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Main-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Math-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Math-Italic.ttf -------------------------------------------------------------------------------- /test/golden/overleftrightarrow424866188.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/overleftrightarrow424866188.png -------------------------------------------------------------------------------- /test/golden/underleftrightarrow128845106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/test/golden/underleftrightarrow128845106.png -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Fraktur-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Fraktur-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Main-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Main-BoldItalic.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Math-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Math-BoldItalic.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_SansSerif-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_SansSerif-Bold.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Script-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Script-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Size1-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Size1-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Size2-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Size2-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Size3-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Size3-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Size4-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Size4-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Caligraphic-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Caligraphic-Bold.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_SansSerif-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_SansSerif-Italic.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_SansSerif-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_SansSerif-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Caligraphic-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Caligraphic-Regular.ttf -------------------------------------------------------------------------------- /lib/katex_fonts/fonts/KaTeX_Typewriter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/lib/katex_fonts/fonts/KaTeX_Typewriter-Regular.ttf -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simpleclub/flutter_math/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /lib/src/widgets/mode.dart: -------------------------------------------------------------------------------- 1 | /// Mode for widget 2 | enum FlutterMathMode { 3 | /// Editable (Unimplemented) 4 | edit, 5 | 6 | /// Selectable 7 | select, 8 | 9 | /// Non-selectable 10 | view, 11 | } 12 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/src/render/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import '../ast/size.dart'; 3 | 4 | const infiniteConstraint = BoxConstraints(); 5 | 6 | const nullDelimiterSpace = Measurement(value: 0.12, unit: Unit.cssEm); 7 | -------------------------------------------------------------------------------- /lib/src/utils/canvas_kit/canvas_kit_stub.dart: -------------------------------------------------------------------------------- 1 | /// Whether the CanvasKit renderer is being used on web. 2 | /// 3 | /// Always returns `false` on non-web. 4 | /// 5 | /// See https://stackoverflow.com/a/66777112/6509751 for reference. 6 | bool get isCanvasKit => false; 7 | -------------------------------------------------------------------------------- /lib/src/encoder/tex/functions/sqrt.dart: -------------------------------------------------------------------------------- 1 | part of '../functions.dart'; 2 | 3 | EncodeResult _sqrtEncoder(GreenNode node) { 4 | final sqrtNode = node as SqrtNode; 5 | return TexCommandEncodeResult( 6 | command: '\\sqrt', 7 | args: sqrtNode.children, 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/src/encoder/exception.dart: -------------------------------------------------------------------------------- 1 | import '../widgets/exception.dart'; 2 | 3 | class EncoderException implements FlutterMathException { 4 | final String message; 5 | final dynamic token; 6 | 7 | const EncoderException(this.message, [this.token]); 8 | 9 | String get messageWithType => 'Encoder Exception: $message'; 10 | } 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 5f21edf8b66e31a39133177319414395cc5b5f48 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 5f21edf8b66e31a39133177319414395cc5b5f48 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /test/encoder/tex/recode.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_math_fork/flutter_math.dart'; 2 | import 'package:flutter_math_fork/src/parser/tex/parser.dart'; 3 | 4 | import 'package:flutter_math_fork/src/encoder/tex/encoder.dart'; 5 | 6 | String recodeTex(String tex) => TexParser(tex, const TexParserSettings()) 7 | .parse() 8 | .encodeTeX(conf: TexEncodeConf.mathParamConf); 9 | -------------------------------------------------------------------------------- /lib/src/utils/canvas_kit/canvas_kit_web.dart: -------------------------------------------------------------------------------- 1 | // ignore: avoid_web_libraries_in_flutter 2 | import 'dart:js'; 3 | 4 | /// Whether the CanvasKit renderer is being used on web. 5 | /// 6 | /// Always returns `false` on non-web. 7 | /// 8 | /// See https://stackoverflow.com/a/66777112/6509751 for reference. 9 | bool get isCanvasKit => context['flutterCanvasKit'] != null; 10 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/custom/cursor.dart: -------------------------------------------------------------------------------- 1 | import '../../../../../tex.dart'; 2 | import '../../../../ast/nodes/cursor_node.dart'; 3 | import '../../functions.dart'; 4 | 5 | const cursorEntries = { 6 | ['\\cursor']: FunctionSpec(numArgs: 1, handler: _cursorHandler) 7 | }; 8 | 9 | GreenNode _cursorHandler(TexParser parser, FunctionContext context) => 10 | CursorNode(); 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_ext.dart: -------------------------------------------------------------------------------- 1 | library katex_ext; 2 | 3 | import '../../../ast/nodes/symbol.dart'; 4 | import '../../../ast/syntax_tree.dart'; 5 | import '../../../ast/types.dart'; 6 | import '../functions.dart'; 7 | import '../parse_error.dart'; 8 | import '../parser.dart'; 9 | 10 | part 'katex_ext/not.dart'; 11 | 12 | const katexExtFunctionEntries = { 13 | ..._notEntries, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/src/encoder/tex/functions/multiscripts.dart: -------------------------------------------------------------------------------- 1 | part of '../functions.dart'; 2 | 3 | EncodeResult _multisciprtsEncoder(GreenNode node) { 4 | final scriptsNode = node as MultiscriptsNode; 5 | return TexMultiscriptEncodeResult( 6 | base: scriptsNode.base, 7 | sub: scriptsNode.sub, 8 | sup: scriptsNode.sup, 9 | presub: scriptsNode.presub, 10 | presup: scriptsNode.presup, 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /doc/TODO.md: -------------------------------------------------------------------------------- 1 | * [ ] Nullability check of required arguments! 2 | 3 | * LeftRight should remove 0.12 em nulldelimiter introduced by FracNode. 4 | 5 | * | and \u2223 needs extra moderation for the symbol (also in stacked delimiter) (\u2225Parallel & \u2016Vert) 6 | 7 | * extract nulldelimiter 8 | 9 | * modify symbols.dart to change the render config of surrogate pairs 10 | 11 | * equation breaking 12 | 13 | * gathered environment -------------------------------------------------------------------------------- /lib/src/utils/wrapper.dart: -------------------------------------------------------------------------------- 1 | class Wrapper { 2 | final T value; 3 | 4 | const Wrapper( 5 | this.value, 6 | ); 7 | 8 | @override 9 | bool operator ==(Object o) { 10 | if (identical(this, o)) return true; 11 | 12 | return o is Wrapper && o.value == value; 13 | } 14 | 15 | @override 16 | int get hashCode => value.hashCode; 17 | 18 | @override 19 | String toString() => 'Wrapper($value)'; 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/utils/log.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | void warn(String msg) => log( 4 | msg, 5 | name: 'Flutter Math', 6 | level: 900, // Level.WARNING 7 | ); 8 | 9 | void error(String msg) => log( 10 | msg, 11 | name: 'Flutter Math', 12 | level: 1000, // Level.SEVERE 13 | ); 14 | 15 | void info(String msg) => log( 16 | msg, 17 | name: 'Flutter Math', 18 | level: 800, // Level.INFO 19 | ); 20 | -------------------------------------------------------------------------------- /lib/src/utils/num_extension.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | extension IntExt on int { 4 | int clampInt(int lowerLimit, int upperLimit) { 5 | assert(upperLimit >= lowerLimit); 6 | if (this < lowerLimit) return lowerLimit; 7 | if (this > upperLimit) return upperLimit; 8 | return this; 9 | } 10 | } 11 | 12 | @pragma('dart2js:tryInline') 13 | @pragma('vm:prefer-inline') 14 | T max3(T a, T b, T c) => math.max(math.max(a, b), c); 15 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/sqrt_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import '../recode.dart'; 4 | 5 | void main() { 6 | group('SqrtNode encoding', () { 7 | test('general encoding', () { 8 | const testStrings = [ 9 | '\\sqrt{a}', 10 | '\\sqrt[a]{b}', 11 | ]; 12 | for (final testString in testStrings) { 13 | expect(recodeTex(testString), testString); 14 | } 15 | }); 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/encoder/optimization.dart: -------------------------------------------------------------------------------- 1 | import '../ast/syntax_tree.dart'; 2 | 3 | import 'matcher.dart'; 4 | 5 | class OptimizationEntry { 6 | final Matcher matcher; 7 | final void Function(GreenNode node) optimize; 8 | 9 | final int? _priority; 10 | int get priority => _priority ?? matcher.specificity; 11 | 12 | const OptimizationEntry({ 13 | required this.matcher, 14 | required this.optimize, 15 | int? priority, 16 | }) : _priority = priority; 17 | } 18 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/utils/render_box_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | extension HittestExtension on RenderBox { 5 | T? hittestFindLowest(Offset localOffset) { 6 | final result = BoxHitTestResult(); 7 | this.hitTest(result, position: localOffset); 8 | final target = result.path 9 | .firstWhereOrNull((element) => element.target is T) 10 | ?.target as T?; 11 | return target; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/utils/text_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'num_extension.dart'; 3 | 4 | extension TextSelectionExt on TextSelection { 5 | bool within(TextRange range) => 6 | this.start >= range.start && this.end <= range.end; 7 | TextSelection constrainedBy(TextRange range) => TextSelection( 8 | baseOffset: this.baseOffset.clampInt(range.start, range.end), 9 | extentOffset: this.extentOffset.clampInt(range.start, range.end), 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/widgets/exception.dart: -------------------------------------------------------------------------------- 1 | /// Base class for exceptions. 2 | abstract class FlutterMathException implements Exception { 3 | String get message; 4 | 5 | String get messageWithType; 6 | } 7 | 8 | /// Exceptions occured during build. 9 | class BuildException implements FlutterMathException { 10 | final String message; 11 | final StackTrace? trace; 12 | 13 | const BuildException(this.message, {this.trace}); 14 | 15 | String get messageWithType => 'Build Exception: $message'; 16 | } 17 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/nary_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import '../recode.dart'; 4 | 5 | void main() { 6 | group('NaryOperatorNode encoding', () { 7 | test('general encoding', () { 8 | const testStrings = [ 9 | '\\sum{a}', 10 | '\\sum_a^b{c}', 11 | '\\sum_{a0}^b{cd}', 12 | ]; 13 | for (final testString in testStrings) { 14 | expect(recodeTex(testString), testString); 15 | } 16 | }); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /lib/tex.dart: -------------------------------------------------------------------------------- 1 | /// Utilities for Tex encoding and parsing. 2 | library tex; 3 | 4 | export 'src/ast/syntax_tree.dart' 5 | show SyntaxTree, SyntaxNode, GreenNode, EquationRowNode; 6 | export 'src/encoder/tex/encoder.dart' 7 | show TexEncoder, TexEncoderExt, ListTexEncoderExt; 8 | export 'src/parser/tex/colors.dart'; 9 | export 'src/parser/tex/macros.dart' 10 | show MacroDefinition, defineMacro, MacroExpansion; 11 | export 'src/parser/tex/parser.dart' show TexParser; 12 | export 'src/parser/tex/settings.dart'; 13 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/accent_under_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_math_fork/ast.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:flutter_math_fork/src/encoder/tex/encoder.dart'; 5 | 6 | void main() { 7 | group('accent encoding test', () { 8 | test('general encoding math', () { 9 | final bar = AccentUnderNode( 10 | base: EquationRowNode.empty(), 11 | label: '\u00AF', 12 | ); 13 | expect(bar.encodeTeX(), '\\underline{}'); 14 | }); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/src/render/utils/render_box_offset.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/rendering.dart'; 3 | 4 | extension RenderBoxOffsetExt on RenderBox { 5 | Offset get offset => (this.parentData as BoxParentData).offset; 6 | set offset(Offset value) { 7 | (this.parentData as BoxParentData).offset = value; 8 | } 9 | 10 | double get layoutHeight => 11 | this.getDistanceToBaseline(TextBaseline.alphabetic)!; 12 | 13 | double get layoutDepth => 14 | this.size.height - this.getDistanceToBaseline(TextBaseline.alphabetic)!; 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/ast/symbols/symbols_extra.dart: -------------------------------------------------------------------------------- 1 | // These symbols are too complex, we need to store their widget 2 | // const complexSymbolRenderResult = { 3 | // // Stacked operator 4 | // '\u2258', 5 | // '\u2259', 6 | // '\u225A', 7 | // '\u225B', 8 | // '\u225D', //\defeq 9 | // '\u225E', 10 | // '\u225F', 11 | 12 | // // Circled char 13 | // '\u00A9', 14 | // }; 15 | 16 | const decoratedEqualSymbols = { 17 | // '\u2258', 18 | '\u2259', 19 | '\u225A', 20 | '\u225B', 21 | '\u225D', 22 | '\u225E', 23 | '\u225F', 24 | }; 25 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/multiscripts_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import '../recode.dart'; 4 | 5 | void main() { 6 | group('MultiscriptsNode encoding', () { 7 | test('general encoding', () { 8 | const testStrings = [ 9 | 'a_b^c', 10 | 'a_b', 11 | 'a^c', 12 | '{ab}_{12}^{cd}', 13 | '1_{12}^{cd}', 14 | ]; 15 | for (final testString in testStrings) { 16 | expect(recodeTex(testString), testString); 17 | } 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/utils/alpha_numeric.dart: -------------------------------------------------------------------------------- 1 | final _code0 = '0'.codeUnitAt(0); 2 | 3 | final _code9 = '9'.codeUnitAt(0); 4 | 5 | final _codeA = 'A'.codeUnitAt(0); 6 | 7 | final _codeZ = 'Z'.codeUnitAt(0); 8 | 9 | final _codea = 'a'.codeUnitAt(0); 10 | 11 | final _codez = 'z'.codeUnitAt(0); 12 | 13 | bool isAlphaNumericUnit(String symbol) { 14 | assert(symbol.length == 1); 15 | final code = symbol.codeUnitAt(0); 16 | return (code >= _code0 && code <= _code9) || 17 | (code >= _codeA && code <= _codeZ) || 18 | (code >= _codea && code <= _codez); 19 | } 20 | -------------------------------------------------------------------------------- /lib/flutter_math.dart: -------------------------------------------------------------------------------- 1 | /// Basic utilities to render math equations. 2 | /// 3 | /// Please refer to README for usage. 4 | library flutter_math_fork; 5 | 6 | export 'src/ast/options.dart' show MathOptions, FontOptions; 7 | export 'src/ast/size.dart' show MathSize; 8 | export 'src/ast/style.dart' show MathStyle; 9 | export 'src/encoder/exception.dart'; 10 | export 'src/parser/tex/parse_error.dart'; 11 | export 'src/parser/tex/settings.dart'; 12 | export 'src/widgets/exception.dart'; 13 | export 'src/widgets/math.dart'; 14 | export 'src/widgets/selectable.dart' show SelectableMath; 15 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /lib/src/render/svg/draw_svg_root.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | 4 | void drawSvgRoot(PictureInfo svgRoot, PaintingContext context, Offset offset) { 5 | final canvas = context.canvas; 6 | canvas.save(); 7 | canvas.translate(offset.dx, offset.dy); 8 | canvas.scale( 9 | svgRoot.size.width, 10 | svgRoot.size.height, 11 | ); 12 | canvas.clipRect(Rect.fromLTWH( 13 | 0.0, 14 | 0.0, 15 | svgRoot.size.width, 16 | svgRoot.size.height, 17 | )); 18 | canvas.drawPicture(svgRoot.picture); 19 | canvas.restore(); 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/utils/unicode_literal.dart: -------------------------------------------------------------------------------- 1 | String fixedHex(int number, int length) { 2 | var str = number.toRadixString(16).toUpperCase(); 3 | str = str.padLeft(length, '0'); 4 | return str; 5 | } 6 | 7 | /* Creates a unicode literal based on the string */ 8 | String unicodeLiteral(String str, {bool escape = false}) => 9 | str.split('').map((e) { 10 | if (e.codeUnitAt(0) > 126 || e.codeUnitAt(0) < 32) { 11 | return '\\u${fixedHex(e.codeUnitAt(0), 4)}'; 12 | } else if (escape && (e == '\'' || e == '\$')) { 13 | return '\\$e'; 14 | } else { 15 | return e; 16 | } 17 | }).join(); 18 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | include ':app' 6 | 7 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 8 | def properties = new Properties() 9 | 10 | assert localPropertiesFile.exists() 11 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 12 | 13 | def flutterSdkPath = properties.getProperty("flutter.sdk") 14 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 15 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 16 | -------------------------------------------------------------------------------- /lib/src/render/layout/remove_baseline.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/rendering.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class RemoveBaseline extends SingleChildRenderObjectWidget { 5 | const RemoveBaseline({ 6 | Key? key, 7 | required Widget child, 8 | }) : super(key: key, child: child); 9 | 10 | @override 11 | RenderRemoveBaseline createRenderObject(BuildContext context) => 12 | RenderRemoveBaseline(); 13 | } 14 | 15 | class RenderRemoveBaseline extends RenderProxyBox { 16 | RenderRemoveBaseline({RenderBox? child}) : super(child); 17 | 18 | @override 19 | // ignore: avoid_returning_null 20 | double? computeDistanceToActualBaseline(TextBaseline baseline) => null; 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/render/utils/render_box_layout.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/rendering.dart'; 3 | 4 | extension RenderBoxLayout on RenderBox { 5 | /// Returns the size of render box given the provided [BoxConstraints]. 6 | /// 7 | /// The `dry` flag indicates that no real layout pass but only a dry 8 | /// layout pass should be executed on the render box. 9 | /// Defaults to true. 10 | Size getLayoutSize(BoxConstraints constraints, {bool dry = true}) { 11 | final Size boxSize; 12 | if (dry) { 13 | boxSize = this.getDryLayout(constraints); 14 | } else { 15 | this.layout(constraints, parentUsesSize: true); 16 | boxSize = this.size; 17 | } 18 | return boxSize; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/encoder/tex/functions/accent_under.dart: -------------------------------------------------------------------------------- 1 | part of '../functions.dart'; 2 | 3 | EncodeResult _accentUnderEncoder(GreenNode node) { 4 | final accentNode = node as AccentUnderNode; 5 | final label = accentNode.label; 6 | final command = accentUnderMapping.entries 7 | .firstWhereOrNull((entry) => entry.value == label) 8 | ?.key; 9 | return command == null 10 | ? NonStrictEncodeResult( 11 | 'unknown accent_under', 12 | 'No strict match for accent_under symbol under math mode: ' 13 | '${unicodeLiteral(accentNode.label)}', 14 | ) 15 | : TexCommandEncodeResult( 16 | command: command, 17 | args: accentNode.children, 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_math_fork_example 2 | description: A Flutter Package (Demo) to render LaTeX with pure Flutter. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | version: 1.0.0+1 8 | 9 | environment: 10 | sdk: ">=2.12.0 <3.0.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | 16 | flutter_math_fork: 17 | path: ../ 18 | 19 | provider: any 20 | flutter_tex: ^4.0.3+4 21 | google_fonts: ^4.0.4 22 | 23 | dev_dependencies: 24 | flutter_test: 25 | sdk: flutter 26 | 27 | flutter: 28 | uses-material-design: true 29 | -------------------------------------------------------------------------------- /lib/src/encoder/tex/functions/stretchy_op.dart: -------------------------------------------------------------------------------- 1 | part of '../functions.dart'; 2 | 3 | EncodeResult _stretchyOpEncoder(GreenNode node) { 4 | final arrowNode = node as StretchyOpNode; 5 | final command = arrowCommandMapping.entries 6 | .firstWhereOrNull((entry) => entry.value == arrowNode.symbol) 7 | ?.key; 8 | return command == null 9 | ? NonStrictEncodeResult( 10 | 'unknown strechy_op', 11 | 'No strict match for stretchy_op symbol under math mode: ' 12 | '${unicodeLiteral(arrowNode.symbol)}', 13 | ) 14 | : TexCommandEncodeResult( 15 | command: command, 16 | args: [ 17 | arrowNode.above, 18 | arrowNode.below, 19 | ], 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/char.dart: -------------------------------------------------------------------------------- 1 | part of katex_base; 2 | 3 | const _charEntries = { 4 | ['\\@char']: 5 | FunctionSpec(numArgs: 1, allowedInText: true, handler: _charHandler), 6 | }; 7 | GreenNode _charHandler(TexParser parser, FunctionContext context) { 8 | final arg = assertNodeType( 9 | parser.parseArgNode(mode: null, optional: false)); 10 | final number = arg.children 11 | .map((child) => assertNodeType(child).symbol) 12 | .join(''); 13 | final code = int.tryParse(number); 14 | if (code == null) { 15 | throw ParseException('\\@char has non-numeric argument $number'); 16 | } 17 | return SymbolNode( 18 | symbol: String.fromCharCode(code), 19 | mode: parser.mode, 20 | overrideAtomType: AtomType.ord, 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/stretchy_op_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_math_fork/ast.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:flutter_math_fork/src/encoder/tex/encoder.dart'; 5 | 6 | void main() { 7 | group('StretchyOp encoding test', () { 8 | test('general encoding', () { 9 | final node1 = StretchyOpNode( 10 | symbol: '\u2192', 11 | above: EquationRowNode(children: []), 12 | below: EquationRowNode(children: []), 13 | ); 14 | expect(node1.encodeTeX(), '\\xrightarrow{}'); 15 | 16 | final node2 = StretchyOpNode( 17 | symbol: '\u2192', 18 | above: EquationRowNode(children: [SymbolNode(symbol: 'a')]), 19 | below: EquationRowNode(children: []), 20 | ); 21 | expect(node2.encodeTeX(), '\\xrightarrow[a]{}'); 22 | }); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - path_provider (0.0.1): 4 | - Flutter 5 | - webview_flutter (0.0.1): 6 | - Flutter 7 | 8 | DEPENDENCIES: 9 | - Flutter (from `Flutter`) 10 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 11 | - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) 12 | 13 | EXTERNAL SOURCES: 14 | Flutter: 15 | :path: Flutter 16 | path_provider: 17 | :path: ".symlinks/plugins/path_provider/ios" 18 | webview_flutter: 19 | :path: ".symlinks/plugins/webview_flutter/ios" 20 | 21 | SPEC CHECKSUMS: 22 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 23 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c 24 | webview_flutter: d2b4d6c66968ad042ad94cbb791f5b72b4678a96 25 | 26 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 27 | 28 | COCOAPODS: 1.11.2 29 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/left_right_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_math_fork/ast.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:flutter_math_fork/src/encoder/tex/encoder.dart'; 5 | 6 | import '../recode.dart'; 7 | 8 | void main() { 9 | group('LeftRight encoding test', () { 10 | test('general encoding', () { 11 | final node1 = LeftRightNode( 12 | leftDelim: '(', 13 | rightDelim: '}', 14 | body: [ 15 | EquationRowNode(children: [SymbolNode(symbol: 'a')]) 16 | ], 17 | ); 18 | expect(node1.encodeTeX(), '\\left(a\\right\\}'); 19 | 20 | const testStrings = [ 21 | '\\left.a\\middle|b\\middle.c\\right)', 22 | ]; 23 | for (final testString in testStrings) { 24 | expect(recodeTex(testString), testString); 25 | } 26 | }); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /test/render_sample_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'helper.dart'; 4 | import 'load_fonts.dart'; 5 | 6 | void main() { 7 | setUpAll(loadKaTeXFonts); 8 | 9 | testTexToMatchGoldenFile( 10 | 'Solution of quadratic equation', 11 | r'x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}', 12 | location: '../doc/img/delta.png', 13 | scale: 5, 14 | ); 15 | testTexToMatchGoldenFile( 16 | 'Schrodinger equation', 17 | r'i\hbar\frac{\partial}{\partial t}\Psi(\vec x,t) = -\frac{\hbar}{2m}\nabla^2\Psi(\vec x,t)+V(\vec x)\Psi(\vec x,t)', 18 | location: '../doc/img/schrodinger.png', 19 | scale: 5, 20 | ); 21 | testTexToMatchGoldenFile( 22 | 'Fourier transform', 23 | r'\hat f(\xi) = \int_{-\infty}^{+\infty}{f(x)e^{-2\pi i \xi x}\mathrm{d}x}', 24 | location: '../doc/img/fourier.png', 25 | scale: 5, 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Exceptions to above rules. 43 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 44 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/function_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import '../recode.dart'; 4 | 5 | void main() { 6 | group('FunctionNode encoding', () { 7 | test('general encoding', () { 8 | const testStrings = [ 9 | '\\operatorname{abc}{def}', 10 | ]; 11 | for (final testString in testStrings) { 12 | expect(recodeTex(testString), testString); 13 | } 14 | }); 15 | 16 | test('optimization', () { 17 | const testStrings = [ 18 | '\\sin{a}', 19 | '\\sin{abc}', 20 | '\\sin_1^2{abc}', 21 | '\\sin\\limits_1^2{abc}', 22 | '\\sin\\limits^2{abc}', 23 | '\\sin\\limits_1{abc}', 24 | '\\lim_1{abc}', 25 | '\\lim\\nolimits_1{abc}', 26 | ]; 27 | for (final testString in testStrings) { 28 | expect(recodeTex(testString), testString); 29 | } 30 | }); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # language: dart 2 | branches: 3 | only: 4 | - master 5 | 6 | dart: 7 | - stable 8 | 9 | os: 10 | - linux 11 | 12 | sudo: false 13 | 14 | addons: 15 | apt: 16 | # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18 17 | sources: 18 | - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version 19 | packages: 20 | - libstdc++6 21 | - fonts-noto 22 | 23 | env: 24 | - FLUTTER_VERSION=beta 25 | 26 | before_script: 27 | - pwd 28 | - git clone https://github.com/flutter/flutter.git -b $FLUTTER_VERSION 29 | - ./flutter/bin/flutter doctor 30 | 31 | cache: 32 | directories: 33 | - $HOME/.pub-cache 34 | 35 | script: 36 | - ./flutter/bin/flutter test --coverage 37 | # - set -e 38 | # - dartfmt -n --set-exit-if-changed lib/ 39 | # - dartfmt -n --set-exit-if-changed test/ 40 | # - set +e 41 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /test/encoder/tex/functions/frac_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import '../recode.dart'; 4 | 5 | void main() { 6 | group('frac encoding test', () { 7 | test('base frac encoding', () { 8 | expect(recodeTex('\\frac{a}{b}'), '\\frac{a}{b}'); 9 | expect(recodeTex('\\cfrac{a}{b}'), '\\cfrac{a}{b}'); 10 | expect(recodeTex('\\genfrac{}{}{1.0pt}{}{a}{b}'), 11 | '\\genfrac{}{}{1.0pt}{}{a}{b}'); 12 | }); 13 | 14 | test('frac optimization', () { 15 | expect(recodeTex('\\dfrac{a}{b}'), '\\dfrac{a}{b}'); 16 | expect(recodeTex('\\tfrac{a}{b}'), '\\tfrac{a}{b}'); 17 | expect(recodeTex('\\binom{a}{b}'), '\\binom{a}{b}'); 18 | expect(recodeTex('\\genfrac{(}{\\}}{0.0pt}{0}{a}{b}'), 19 | '\\genfrac{(}{\\}}{0.0pt}{0}{a}{b}'); 20 | expect(recodeTex('\\genfrac{}{}{0.0pt}{0}{a}{b}'), 21 | '\\genfrac{}{}{0.0pt}{0}{a}{b}'); 22 | }); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/encoder/tex/functions/nary.dart: -------------------------------------------------------------------------------- 1 | part of '../functions.dart'; 2 | 3 | EncodeResult _naryEncoder(GreenNode node) { 4 | final naryNode = node as NaryOperatorNode; 5 | final command = _naryOperatorMapping[naryNode.operator]; 6 | if (command == null) { 7 | return NonStrictEncodeResult( 8 | 'unknown Nary opertor', 9 | 'Unknown Nary opertor symbol ${unicodeLiteral(naryNode.operator)} ' 10 | 'encountered during encoding.', 11 | ); 12 | } 13 | return TransparentTexEncodeResult([ 14 | TexMultiscriptEncodeResult( 15 | base: naryNode.limits != null 16 | ? '$command\\${naryNode.limits! ? '' : 'no'}limits' 17 | : command, 18 | sub: naryNode.lowerLimit, 19 | sup: naryNode.upperLimit, 20 | ), 21 | naryNode.naryand, 22 | ]); 23 | } 24 | 25 | // Dart compiler bug here. Cannot set it to const 26 | final _naryOperatorMapping = { 27 | ...singleCharBigOps, 28 | ...singleCharIntegrals, 29 | }; 30 | -------------------------------------------------------------------------------- /example/lib/display.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_math_fork/flutter_math.dart'; 3 | 4 | class DisplayMath extends StatelessWidget { 5 | final String expression; 6 | 7 | const DisplayMath({ 8 | Key? key, 9 | required this.expression, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) => Card( 14 | child: Column( 15 | crossAxisAlignment: CrossAxisAlignment.center, 16 | mainAxisSize: MainAxisSize.min, 17 | mainAxisAlignment: MainAxisAlignment.start, 18 | children: [ 19 | Padding( 20 | padding: const EdgeInsets.all(8.0), 21 | child: Text(expression, softWrap: true), 22 | ), 23 | Divider( 24 | thickness: 1.0, 25 | height: 1.0, 26 | ), 27 | Expanded( 28 | child: Center(child: Math.tex(expression)), 29 | ) 30 | ], 31 | ), 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/katex_fonts/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Khan Academy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /lib/src/render/layout/reset_baseline.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/rendering.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class ResetBaseline extends SingleChildRenderObjectWidget { 5 | final double height; 6 | const ResetBaseline({ 7 | Key? key, 8 | required this.height, 9 | required Widget child, 10 | }) : super(key: key, child: child); 11 | 12 | @override 13 | RenderResetBaseline createRenderObject(BuildContext context) => 14 | RenderResetBaseline(height: height); 15 | 16 | @override 17 | void updateRenderObject( 18 | BuildContext context, RenderResetBaseline renderObject) => 19 | renderObject..height = height; 20 | } 21 | 22 | class RenderResetBaseline extends RenderProxyBox { 23 | RenderResetBaseline({required double height, RenderBox? child}) 24 | : _height = height, 25 | super(child); 26 | 27 | double get height => _height; 28 | double _height; 29 | set height(double value) { 30 | if (_height != value) { 31 | _height = value; 32 | markNeedsLayout(); 33 | } 34 | } 35 | 36 | @override 37 | double computeDistanceToActualBaseline(TextBaseline baseline) => height; 38 | } 39 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'demo.dart'; 4 | import 'equations.dart'; 5 | import 'feature.dart'; 6 | 7 | void main() { 8 | runApp(MyApp()); 9 | } 10 | 11 | class MyApp extends StatelessWidget { 12 | @override 13 | Widget build(BuildContext context) => MaterialApp( 14 | title: 'Flutter Math Demo v0.2.0', 15 | theme: ThemeData( 16 | primarySwatch: Colors.blue, 17 | visualDensity: VisualDensity.adaptivePlatformDensity, 18 | ), 19 | home: DefaultTabController( 20 | length: 3, 21 | child: Scaffold( 22 | appBar: AppBar( 23 | title: Text( 24 | 'Flutter Math Demo v0.2.0', 25 | ), 26 | bottom: TabBar(tabs: [ 27 | Text('Interactive Demo'), 28 | Text('Equation Samples'), 29 | Text('Supported Features'), 30 | ]), 31 | ), 32 | body: TabBarView(children: [ 33 | DemoPage(), 34 | EquationsPage(), 35 | FeaturePage(), 36 | ]), 37 | ), 38 | ), 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/widgets/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import '../ast/syntax_tree.dart'; 3 | import '../utils/text_extension.dart'; 4 | 5 | class MathController extends ChangeNotifier { 6 | MathController({ 7 | required SyntaxTree ast, 8 | TextSelection selection = const TextSelection.collapsed(offset: -1), 9 | }) : _ast = ast, 10 | _selection = selection; 11 | 12 | SyntaxTree _ast; 13 | SyntaxTree get ast => _ast; 14 | set ast(SyntaxTree value) { 15 | if (_ast != value) { 16 | _ast = value; 17 | _selection = const TextSelection.collapsed(offset: -1); 18 | notifyListeners(); 19 | } 20 | } 21 | 22 | TextSelection get selection => _selection; 23 | TextSelection _selection; 24 | set selection(TextSelection value) { 25 | if (_selection != value) { 26 | _selection = sanitizeSelection(ast, value); 27 | notifyListeners(); 28 | } 29 | } 30 | 31 | TextSelection sanitizeSelection(SyntaxTree ast, TextSelection selection) { 32 | if (selection.end <= 0) return selection; 33 | return selection.constrainedBy(ast.root.range); 34 | } 35 | 36 | List get selectedNodes => 37 | ast.findSelectedNodes(selection.start, selection.end); 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/ast/nodes/style.dart: -------------------------------------------------------------------------------- 1 | import '../options.dart'; 2 | import '../syntax_tree.dart'; 3 | 4 | /// Node to denote all kinds of style changes. 5 | class StyleNode extends TransparentNode { 6 | final List children; 7 | 8 | /// The difference of [MathOptions]. 9 | final OptionsDiff optionsDiff; 10 | 11 | StyleNode({ 12 | required this.children, 13 | required this.optionsDiff, 14 | }); 15 | 16 | @override 17 | List computeChildOptions(MathOptions options) => 18 | List.filled(children.length, options.merge(optionsDiff), growable: false); 19 | 20 | @override 21 | bool shouldRebuildWidget(MathOptions oldOptions, MathOptions newOptions) => 22 | false; 23 | 24 | @override 25 | ParentableNode updateChildren(List newChildren) => 26 | copyWith(children: newChildren); 27 | 28 | @override 29 | Map toJson() => super.toJson() 30 | ..addAll({ 31 | 'children': children.map((e) => e.toJson()).toList(growable: false), 32 | 'optionsDiff': optionsDiff.toString(), 33 | }); 34 | 35 | StyleNode copyWith({ 36 | List? children, 37 | OptionsDiff? optionsDiff, 38 | }) => 39 | StyleNode( 40 | children: children ?? this.children, 41 | optionsDiff: optionsDiff ?? this.optionsDiff, 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/ast/types.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | enum Mode { math, text } 25 | -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Flutter Math Demo 18 | 19 | 20 | 21 | 22 | 25 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /lib/src/render/svg/static.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../../ast/options.dart'; 4 | import '../../ast/size.dart'; 5 | import '../layout/reset_baseline.dart'; 6 | import 'svg_geomertry.dart'; 7 | import 'svg_string.dart'; 8 | 9 | const svgData = { 10 | // path, width, height 11 | 'vec': [0.471, 0.714], // values from the font glyph 12 | 'oiintSize1': [0.957, 0.499], // oval to overlay the integrand 13 | 'oiintSize2': [1.472, 0.659], 14 | 'oiiintSize1': [1.304, 0.499], 15 | 'oiiintSize2': [1.98, 0.659], 16 | }; 17 | 18 | Widget staticSvg(String name, MathOptions options, 19 | {bool needBaseline = false}) { 20 | final dimen = svgData[name]; 21 | if (dimen == null) { 22 | throw ArgumentError.value(name, 'name', 'Invalid static svg name'); 23 | } 24 | final width = dimen[0]; 25 | final height = dimen[1]; 26 | final viewPortWidth = width.cssEm.toLpUnder(options); 27 | final viewPortHeight = height.cssEm.toLpUnder(options); 28 | 29 | final svgWidget = svgWidgetFromPath( 30 | svgPaths[name]!, 31 | Size(viewPortWidth, viewPortHeight), 32 | Rect.fromLTWH(0, 0, 1000 * width, 1000 * height), 33 | options.color, 34 | ); 35 | if (needBaseline) { 36 | return ResetBaseline( 37 | height: viewPortHeight, 38 | child: svgWidget, 39 | ); 40 | } 41 | return svgWidget; 42 | } 43 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_math_fork/flutter_math.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | import 'load_fonts.dart'; 6 | 7 | void main() { 8 | setUpAll(loadKaTeXFonts); 9 | group('Flutter Math', () { 10 | testWidgets('Should show default error message', (tester) async { 11 | await tester.pumpWidget( 12 | MaterialApp( 13 | home: Scaffold(body: Math.tex(r'\Gaarbled$')), 14 | ), 15 | ); 16 | final finder = find.byType(SelectableText); 17 | expect(finder, findsOneWidget); 18 | expect( 19 | (finder.evaluate().single.widget as SelectableText) 20 | .data! 21 | .startsWith('Parser Error:'), 22 | isTrue); 23 | }); 24 | testWidgets('Should show onErrorFallback widget', (tester) async { 25 | await tester.pumpWidget( 26 | MaterialApp( 27 | home: Scaffold( 28 | body: Math.tex( 29 | r'\Gaarbled$', 30 | onErrorFallback: (_) => Container( 31 | width: 100, 32 | height: 100, 33 | ), 34 | ), 35 | ), 36 | ), 37 | ); 38 | final finder = find.byType(Container); 39 | expect(finder, findsOneWidget); 40 | }); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/accent_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_math_fork/ast.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:flutter_math_fork/src/encoder/tex/encoder.dart'; 5 | 6 | void main() { 7 | group('accent encoding test', () { 8 | test('general encoding math', () { 9 | final bar = AccentNode( 10 | base: EquationRowNode.empty(), 11 | label: '\u00AF', 12 | isStretchy: false, 13 | isShifty: true, 14 | ); 15 | expect(bar.encodeTeX(), '\\bar{}'); 16 | 17 | final widehat = AccentNode( 18 | base: EquationRowNode.empty(), 19 | label: '\u005e', 20 | isStretchy: true, 21 | isShifty: true, 22 | ); 23 | expect(widehat.encodeTeX(), '\\widehat{}'); 24 | 25 | final overline = AccentNode( 26 | base: EquationRowNode.empty(), 27 | label: '\u00AF', 28 | isStretchy: true, 29 | isShifty: false, 30 | ); 31 | expect(overline.encodeTeX(), '\\overline{}'); 32 | }); 33 | 34 | test('general encoding text', () { 35 | final bar = AccentNode( 36 | base: EquationRowNode.empty(), 37 | label: '\u00AF', 38 | isStretchy: false, 39 | isShifty: true, 40 | ); 41 | expect(bar.encodeTeX(conf: TexEncodeConf.textConf), '\\={}'); 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /lib/ast.dart: -------------------------------------------------------------------------------- 1 | /// Utilities to manipulate Flutter Math's ASTs 2 | /// 3 | /// See also: 4 | /// 5 | /// * [GreenNode] 6 | /// * [SyntaxNode] 7 | /// * [SyntaxTree] 8 | library ast; 9 | 10 | import 'src/ast/syntax_tree.dart'; 11 | 12 | export 'src/ast/nodes/accent.dart'; 13 | export 'src/ast/nodes/accent_under.dart'; 14 | export 'src/ast/nodes/enclosure.dart' show EnclosureNode; 15 | export 'src/ast/nodes/equation_array.dart'; 16 | export 'src/ast/nodes/frac.dart' show FracNode; 17 | export 'src/ast/nodes/function.dart'; 18 | export 'src/ast/nodes/left_right.dart' show LeftRightNode; 19 | export 'src/ast/nodes/matrix.dart'; 20 | export 'src/ast/nodes/multiscripts.dart'; 21 | export 'src/ast/nodes/nary_op.dart'; 22 | export 'src/ast/nodes/over.dart'; 23 | export 'src/ast/nodes/phantom.dart'; 24 | export 'src/ast/nodes/raise_box.dart'; 25 | export 'src/ast/nodes/space.dart'; 26 | export 'src/ast/nodes/sqrt.dart' show SqrtNode; 27 | export 'src/ast/nodes/stretchy_op.dart' show StretchyOpNode; 28 | export 'src/ast/nodes/style.dart'; 29 | export 'src/ast/nodes/symbol.dart' show SymbolNode; 30 | export 'src/ast/nodes/under.dart'; 31 | export 'src/ast/options.dart'; 32 | export 'src/ast/size.dart'; 33 | export 'src/ast/style.dart'; 34 | export 'src/ast/syntax_tree.dart' 35 | hide TemporaryNode, BuildResult, PositionDependentMixin; 36 | export 'src/ast/tex_break.dart' hide BreakResult; 37 | export 'src/ast/types.dart'; 38 | -------------------------------------------------------------------------------- /lib/src/widgets/selection/focus_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | mixin FocusManagerMixin on State { 4 | FocusNode get focusNode; 5 | 6 | late FocusNode _oldFocusNode; 7 | 8 | late FocusAttachment _focusAttachment; 9 | 10 | @override 11 | void initState() { 12 | super.initState(); 13 | 14 | _focusAttachment = focusNode.attach(context); 15 | _oldFocusNode = focusNode; 16 | } 17 | 18 | @override 19 | void didUpdateWidget(T oldWidget) { 20 | if (focusNode != _oldFocusNode) { 21 | _focusAttachment.detach(); 22 | _focusAttachment = focusNode.attach(context); 23 | _oldFocusNode = focusNode; 24 | } 25 | super.didUpdateWidget(oldWidget); 26 | } 27 | 28 | @override 29 | void dispose() { 30 | _focusAttachment.detach(); 31 | super.dispose(); 32 | } 33 | 34 | @mustCallSuper 35 | @override 36 | Widget build(BuildContext context) { 37 | super.build(context); 38 | _focusAttachment.reparent(); 39 | return _NullWidget(); 40 | } 41 | } 42 | 43 | class _NullWidget extends StatelessWidget { 44 | const _NullWidget(); 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | throw FlutterError( 49 | 'Widgets that mix FocusManagerMixin into their State must call' 50 | 'super.build() but must ignore the return value of the superclass.'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example/lib/feature.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'display.dart'; 4 | import 'supported_data.dart'; 5 | 6 | const largeSections = { 7 | 'Delimiter Sizing', 8 | 'Environment', 9 | 'Unicode Mathematical Alphanumeric Symbols', 10 | }; 11 | 12 | class FeaturePage extends StatelessWidget { 13 | @override 14 | Widget build(BuildContext context) { 15 | final entries = supportedData.entries.toList(); 16 | return ListView.builder( 17 | itemCount: supportedData.length, 18 | itemBuilder: (context, i) => Column( 19 | children: [ 20 | Text( 21 | entries[i].key, 22 | style: Theme.of(context).textTheme.displaySmall, 23 | ), 24 | GridView.builder( 25 | shrinkWrap: true, 26 | physics: NeverScrollableScrollPhysics(), 27 | itemCount: entries[i].value.length, 28 | gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( 29 | maxCrossAxisExtent: 30 | largeSections.contains(entries[i].key) ? 250 : 125, 31 | mainAxisSpacing: 10, 32 | crossAxisSpacing: 10, 33 | childAspectRatio: 1, 34 | ), 35 | itemBuilder: (BuildContext context, int j) => 36 | DisplayMath(expression: entries[i].value[j]), 37 | ), 38 | ], 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to pub.dev 2 | 3 | on: 4 | push: 5 | tags: 6 | # Must align with the tag-pattern configured on pub.dev. 7 | - 'v[0-9]+.[0-9]+.[0-9]+*' 8 | 9 | jobs: 10 | pana: 11 | permissions: 12 | contents: read 13 | timeout-minutes: 9 14 | runs-on: ubuntu-latest 15 | name: Configuration of ${{ matrix.package }} 16 | strategy: 17 | matrix: 18 | package: 19 | - 'flutter_math_fork' 20 | fail-fast: false 21 | 22 | steps: 23 | - uses: actions/checkout@v2.3.3 24 | - uses: axel-op/dart-package-analyzer@v3 25 | with: 26 | relativePath: ${{ matrix.package }} 27 | githubToken: ${{ secrets.GITHUB_TOKEN }} 28 | 29 | publish: 30 | needs: pana 31 | timeout-minutes: 4 32 | runs-on: ubuntu-latest 33 | name: Publishing of ${{ matrix.package }} 34 | permissions: 35 | id-token: write # Allows to publish to pub.dev without personal credentials 36 | strategy: 37 | matrix: 38 | package: 39 | - 'flutter_math_fork' 40 | fail-fast: false 41 | 42 | steps: 43 | - uses: actions/checkout@v2.3.3 44 | with: 45 | fetch-depth: 2 46 | 47 | - uses: dart-lang/setup-dart@v1 48 | - uses: subosito/flutter-action@v2 49 | with: 50 | channel: 'stable' 51 | - run: flutter pub get 52 | - run: flutter pub publish --dry-run 53 | - run: flutter pub publish --force 54 | -------------------------------------------------------------------------------- /lib/src/encoder/tex/functions/left_right.dart: -------------------------------------------------------------------------------- 1 | part of '../functions.dart'; 2 | 3 | EncodeResult _leftRightEncoder(GreenNode node) { 4 | final leftRightNode = node as LeftRightNode; 5 | final left = _delimEncoder(leftRightNode.leftDelim); 6 | final right = _delimEncoder(leftRightNode.rightDelim); 7 | final middles = 8 | leftRightNode.middle.map(_delimEncoder).toList(growable: false); 9 | return TransparentTexEncodeResult([ 10 | '\\left', 11 | left, 12 | ...leftRightNode.body.first.children, 13 | for (var i = 1; i < leftRightNode.body.length; i++) ...[ 14 | '\\middle', 15 | middles[i - 1], 16 | ...leftRightNode.body[i].children, 17 | ], 18 | '\\right', 19 | right, 20 | ]); 21 | } 22 | 23 | EncodeResult _delimEncoder(String? delim) { 24 | if (delim == null) return StaticEncodeResult('.'); 25 | final result = _baseSymbolEncoder(delim, Mode.math); 26 | return result != null 27 | ? delimiterCommands.contains(result) 28 | ? StaticEncodeResult(result) 29 | : NonStrictEncodeResult.string( 30 | 'illegal delimiter', 31 | 'Non-delimiter symbol ${unicodeLiteral(delim)} ' 32 | 'occured as delimiter', 33 | result, 34 | ) 35 | : NonStrictEncodeResult.string( 36 | 'unknown symbol', 37 | 'Unrecognized symbol encountered during TeX encoding: ' 38 | '${unicodeLiteral(delim)} with mode Math', 39 | '.', 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/src/parser/tex/token.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import 'source_location.dart'; 25 | 26 | class Token { 27 | String text; 28 | SourceLocation? loc; 29 | bool noexpand = false; 30 | bool treatAsRelax = false; 31 | Token(this.text, [this.loc]); 32 | 33 | static Token range(Token startToken, Token endToken, String text) => 34 | Token(text, SourceLocation.range(startToken, endToken)); 35 | } 36 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/symbol_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:flutter_math_fork/src/ast/nodes/symbol.dart'; 4 | import 'package:flutter_math_fork/src/ast/syntax_tree.dart'; 5 | import 'package:flutter_math_fork/src/ast/types.dart'; 6 | import 'package:flutter_math_fork/src/encoder/tex/encoder.dart'; 7 | import 'package:flutter_math_fork/src/parser/tex/parser.dart'; 8 | import 'package:flutter_math_fork/src/parser/tex/settings.dart'; 9 | 10 | String recodeTexSymbol(String tex, [Mode mode = Mode.math]) { 11 | if (mode == Mode.text) { 12 | tex = '\\text{$tex}'; 13 | } 14 | var node = TexParser(tex, const TexParserSettings()).parse().children.first; 15 | while (node is ParentableNode) { 16 | node = node.children.first!; 17 | } 18 | assert(node is SymbolNode); 19 | return node.encodeTeX( 20 | conf: mode == Mode.math ? TexEncodeConf.mathConf : TexEncodeConf.textConf, 21 | ); 22 | } 23 | 24 | void main() { 25 | group('symbol encoding test', () { 26 | test('base math symbols', () { 27 | expect(recodeTexSymbol('a'), 'a'); 28 | expect(recodeTexSymbol('0'), '0'); 29 | expect(recodeTexSymbol('\\pm'), '\\pm'); 30 | }); 31 | 32 | test('base text symbols', () { 33 | expect(recodeTexSymbol('a', Mode.text), 'a'); 34 | expect(recodeTexSymbol('0', Mode.text), '0'); 35 | expect(recodeTexSymbol('\\dag', Mode.text), '\\dag'); 36 | }); 37 | 38 | test('escaped math symbols', () { 39 | expect(recodeTexSymbol('\\{'), '\\{'); 40 | expect(recodeTexSymbol('\\}'), '\\}'); 41 | expect(recodeTexSymbol('\\&'), '\\&'); 42 | expect(recodeTexSymbol('\\#'), '\\#'); 43 | expect(recodeTexSymbol('\\_'), '\\_'); 44 | }); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/ast/symbols/unicode_accents.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | const unicodeAccents = { 25 | '\u0300': '\u0060', // \grave 26 | '\u0308': '\u00a8', // \ddot 27 | '\u0303': '\u007e', // \tilde 28 | '\u0304': '\u00AF', // \bar 29 | '\u0301': '\u00b4', // \acute 30 | '\u0306': '\u02d8', // \breve 31 | '\u030c': '\u02c7', // \check 32 | '\u0302': '\u005e', // \hat 33 | '\u0307': '\u02d9', // \dot 34 | '\u030a': '\u02da', // \mathring 35 | '\u030b': '\u02dd', // double acute 36 | }; 37 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/array.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _arrayEntries = { 27 | ['\\hline', '\\hdashline']: FunctionSpec( 28 | numArgs: 0, 29 | allowedInText: true, 30 | allowedInMath: true, 31 | handler: _throwExceptionHandler, 32 | ) 33 | }; 34 | 35 | GreenNode _throwExceptionHandler(TexParser parser, FunctionContext context) { 36 | throw ParseException( 37 | '${context.funcName} valid only within array environment'); 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/break.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _breakEntries = { 27 | ['\\nobreak', '\\allowbreak']: 28 | FunctionSpec(numArgs: 0, handler: _breakHandler) 29 | }; 30 | 31 | GreenNode _breakHandler(TexParser parser, FunctionContext context) => SpaceNode( 32 | height: Measurement.zero, 33 | width: Measurement.zero, 34 | breakPenalty: context.funcName == '\\nobreak' ? 10000 : 0, 35 | // noBreak: context.funcName == '\\nobreak', 36 | mode: parser.mode, 37 | ); 38 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/raise_box.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _raiseBoxEntries = { 27 | ['\\raisebox']: 28 | FunctionSpec(numArgs: 2, allowedInText: true, handler: _raiseBoxHandler), 29 | }; 30 | GreenNode _raiseBoxHandler(TexParser parser, FunctionContext context) { 31 | final dy = parser.parseArgSize(optional: false) ?? Measurement.zero; 32 | final body = parser.parseArgHbox(optional: false); 33 | return RaiseBoxNode( 34 | body: body.wrapWithEquationRow(), 35 | dy: dy, 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | NSAppTransportSecurity 43 | 44 | NSAllowsArbitraryLoads 45 | 46 | io.flutter.embedded_views_preview 47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/src/ast/nodes/raise_box.dart: -------------------------------------------------------------------------------- 1 | import '../../render/layout/shift_baseline.dart'; 2 | import '../options.dart'; 3 | import '../size.dart'; 4 | import '../syntax_tree.dart'; 5 | 6 | /// Raise box node which vertically displace its child. 7 | /// 8 | /// Example: `\raisebox` 9 | class RaiseBoxNode extends SlotableNode { 10 | /// Child to raise. 11 | final EquationRowNode body; 12 | 13 | /// Vertical displacement. 14 | final Measurement dy; 15 | 16 | RaiseBoxNode({ 17 | required this.body, 18 | required this.dy, 19 | }); 20 | 21 | @override 22 | BuildResult buildWidget( 23 | MathOptions options, List childBuildResults) => 24 | BuildResult( 25 | options: options, 26 | widget: ShiftBaseline( 27 | offset: dy.toLpUnder(options), 28 | child: childBuildResults[0]!.widget, 29 | ), 30 | ); 31 | 32 | @override 33 | List computeChildOptions(MathOptions options) => [options]; 34 | 35 | @override 36 | List computeChildren() => [body]; 37 | 38 | @override 39 | AtomType get leftType => AtomType.ord; 40 | 41 | @override 42 | AtomType get rightType => AtomType.ord; 43 | 44 | @override 45 | bool shouldRebuildWidget(MathOptions oldOptions, MathOptions newOptions) => 46 | false; 47 | 48 | @override 49 | RaiseBoxNode updateChildren(List newChildren) => 50 | copyWith(body: newChildren[0]); 51 | 52 | @override 53 | Map toJson() => super.toJson() 54 | ..addAll({ 55 | 'body': body.toJson(), 56 | 'dy': dy.toString(), 57 | }); 58 | 59 | RaiseBoxNode copyWith({ 60 | EquationRowNode? body, 61 | Measurement? dy, 62 | }) => 63 | RaiseBoxNode( 64 | body: body ?? this.body, 65 | dy: dy ?? this.dy, 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/sqrt.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the 'Software'), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _sqrtEntries = { 27 | ['\\sqrt']: FunctionSpec( 28 | numArgs: 1, 29 | numOptionalArgs: 1, 30 | handler: _sqrtHandler, 31 | ), 32 | }; 33 | GreenNode _sqrtHandler(TexParser parser, FunctionContext context) { 34 | final index = parser.parseArgNode(mode: null, optional: true); 35 | final body = parser.parseArgNode(mode: null, optional: false)!; 36 | return SqrtNode( 37 | index: index?.wrapWithEquationRow(), 38 | base: body.wrapWithEquationRow(), 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_ext/not.dart: -------------------------------------------------------------------------------- 1 | part of katex_ext; 2 | 3 | const _notEntries = { 4 | ['\\not']: FunctionSpec(numArgs: 1, handler: _notHandler) 5 | }; 6 | 7 | const _notRemap = { 8 | '\u2190': '\u219A', 9 | '\u2192': '\u219B', 10 | '\u2194': '\u21AE', 11 | '\u21D0': '\u21CD', 12 | '\u21D2': '\u21CF', 13 | '\u21D4': '\u21CE', 14 | '\u2208': '\u2209', 15 | '\u220B': '\u220C', 16 | '\u2223': '\u2224', 17 | '\u2225': '\u2226', 18 | '\u223C': '\u2241', 19 | '\u007E': '\u2241', 20 | '\u2243': '\u2244', 21 | '\u2245': '\u2247', 22 | '\u2248': '\u2249', 23 | '\u224D': '\u226D', 24 | '\u003D': '\u2260', 25 | '\u2261': '\u2262', 26 | '\u003C': '\u226E', 27 | '\u003E': '\u226F', 28 | '\u2264': '\u2270', 29 | '\u2265': '\u2271', 30 | '\u2272': '\u2274', 31 | '\u2273': '\u2275', 32 | '\u2276': '\u2278', 33 | '\u2277': '\u2279', 34 | '\u227A': '\u2280', 35 | '\u227B': '\u2281', 36 | '\u2282': '\u2284', 37 | '\u2283': '\u2285', 38 | '\u2286': '\u2288', 39 | '\u2287': '\u2289', 40 | '\u22A2': '\u22AC', 41 | '\u22A8': '\u22AD', 42 | '\u22A9': '\u22AE', 43 | '\u22AB': '\u22AF', 44 | '\u227C': '\u22E0', 45 | '\u227D': '\u22E1', 46 | '\u2291': '\u22E2', 47 | '\u2292': '\u22E3', 48 | '\u22B2': '\u22EA', 49 | '\u22B3': '\u22EB', 50 | '\u22B4': '\u22EC', 51 | '\u22B5': '\u22ED', 52 | '\u2203': '\u2204' 53 | }; 54 | GreenNode _notHandler(TexParser parser, FunctionContext context) { 55 | final base = parser.parseArgNode(mode: null, optional: false)!; 56 | final node = assertNodeType(base); 57 | final remappedSymbol = _notRemap[node.symbol]; 58 | if (node.mode != Mode.math || 59 | node.variantForm == true || 60 | remappedSymbol == null) { 61 | throw ParseException('\\not has to be followed by a combinable character'); 62 | } 63 | return node.withSymbol( 64 | remappedSymbol, 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/phantom.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _phantomEntries = { 27 | ['\\phantom', '\\hphantom', '\\vphantom']: 28 | FunctionSpec(numArgs: 1, allowedInText: true, handler: _phantomHandler), 29 | }; 30 | 31 | GreenNode _phantomHandler(TexParser parser, FunctionContext context) { 32 | final body = parser.parseArgNode(mode: null, optional: false)!; 33 | return PhantomNode( 34 | phantomChild: body.wrapWithEquationRow(), 35 | zeroHeight: context.funcName == '\\hphantom', 36 | zeroDepth: context.funcName == '\\hphantom', 37 | zeroWidth: context.funcName == '\\vphantom', 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/parser/tex/source_location.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import 'lexer.dart'; 25 | import 'token.dart'; 26 | 27 | class SourceLocation { 28 | final LexerInterface lexer; 29 | final int start; 30 | final int end; 31 | SourceLocation(this.lexer, this.start, this.end); 32 | 33 | static SourceLocation? range(Token first, [Token? second]) { 34 | if (second == null) { 35 | return first.loc; 36 | } else if (first.loc == null || 37 | second.loc == null || 38 | first.loc!.lexer != second.loc!.lexer) { 39 | return null; 40 | } else { 41 | return SourceLocation( 42 | first.loc!.lexer, first.loc!.start, second.loc!.end); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/rule.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _ruleEntries = { 27 | ['\\rule']: 28 | FunctionSpec(numArgs: 2, numOptionalArgs: 1, handler: _ruleHandler), 29 | }; 30 | GreenNode _ruleHandler(TexParser parser, FunctionContext context) { 31 | final shift = parser.parseArgSize(optional: true) ?? Measurement.zero; 32 | final width = parser.parseArgSize(optional: false) ?? Measurement.zero; 33 | final height = parser.parseArgSize(optional: false) ?? Measurement.zero; 34 | 35 | return SpaceNode( 36 | height: height, 37 | width: width, 38 | shift: shift, 39 | fill: true, 40 | // background: Colors.black, 41 | mode: Mode.math, 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | .katex_symbol_mapping/ 12 | ._breakable/ 13 | lib/src/parser/unicode_math/ 14 | lib/src/render/layout/breakable/ 15 | coverage 16 | .fvm 17 | 18 | # IntelliJ related 19 | *.iml 20 | *.ipr 21 | *.iws 22 | .idea/ 23 | 24 | # The .vscode folder contains launch configuration and tasks you configure in 25 | # VS Code which you may wish to be included in version control, so this line 26 | # is commented out by default. 27 | .vscode/ 28 | .VSCodeCounter/ 29 | 30 | # Flutter/Dart/Pub related 31 | **/doc/api/ 32 | .dart_tool/ 33 | .flutter-plugins 34 | .flutter-plugins-dependencies 35 | .packages 36 | .pub-cache/ 37 | .pub/ 38 | build/ 39 | pubspec.lock 40 | 41 | # Android related 42 | **/android/**/gradle-wrapper.jar 43 | **/android/.gradle 44 | **/android/captures/ 45 | **/android/gradlew 46 | **/android/gradlew.bat 47 | **/android/local.properties 48 | **/android/**/GeneratedPluginRegistrant.java 49 | 50 | # iOS/XCode related 51 | **/ios/**/*.mode1v3 52 | **/ios/**/*.mode2v3 53 | **/ios/**/*.moved-aside 54 | **/ios/**/*.pbxuser 55 | **/ios/**/*.perspectivev3 56 | **/ios/**/*sync/ 57 | **/ios/**/.sconsign.dblite 58 | **/ios/**/.tags* 59 | **/ios/**/.vagrant/ 60 | **/ios/**/DerivedData/ 61 | **/ios/**/Icon? 62 | **/ios/**/Pods/ 63 | **/ios/**/.symlinks/ 64 | **/ios/**/profile 65 | **/ios/**/xcuserdata 66 | **/ios/.generated/ 67 | **/ios/Flutter/App.framework 68 | **/ios/Flutter/Flutter.framework 69 | **/ios/Flutter/Flutter.podspec 70 | **/ios/Flutter/Generated.xcconfig 71 | **/ios/Flutter/app.flx 72 | **/ios/Flutter/app.zip 73 | **/ios/Flutter/flutter_assets/ 74 | **/ios/Flutter/flutter_export_environment.sh 75 | **/ios/ServiceDefinitions.json 76 | **/ios/Runner/GeneratedPluginRegistrant.* 77 | 78 | # Exceptions to above rules. 79 | !**/ios/**/default.mode1v3 80 | !**/ios/**/default.mode2v3 81 | !**/ios/**/default.pbxuser 82 | !**/ios/**/default.perspectivev3 83 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 84 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.example" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /lib/src/font/metrics/unicode_scripts.dart: -------------------------------------------------------------------------------- 1 | class Script { 2 | final String name; 3 | final List> blocks; 4 | const Script({ 5 | required this.name, 6 | required this.blocks, 7 | }); 8 | } 9 | 10 | const Map>> scriptData = { 11 | // Latin characters beyond the Latin-1 characters we have metrics for. 12 | // Needed for Czech, Hungarian and Turkish text, for example. 13 | 'latin': [ 14 | [0x0100, 0x024f], // Latin Extended-A and Latin Extended-B 15 | [0x0300, 0x036f], // Combining Diacritical marks 16 | ], 17 | 18 | // The Cyrillic script used by Russian and related languages. 19 | // A Cyrillic subset used to be supported as explicitly defined 20 | // symbols in symbols.js 21 | 'cyrillic': [ 22 | [0x0400, 0x04ff] 23 | ], 24 | 25 | // The Brahmic scripts of South and Southeast Asia 26 | // Devanagari (0900–097F) 27 | // Bengali (0980–09FF) 28 | // Gurmukhi (0A00–0A7F) 29 | // Gujarati (0A80–0AFF) 30 | // Oriya (0B00–0B7F) 31 | // Tamil (0B80–0BFF) 32 | // Telugu (0C00–0C7F) 33 | // Kannada (0C80–0CFF) 34 | // Malayalam (0D00–0D7F) 35 | // Sinhala (0D80–0DFF) 36 | // Thai (0E00–0E7F) 37 | // Lao (0E80–0EFF) 38 | // Tibetan (0F00–0FFF) 39 | // Myanmar (1000–109F) 40 | 'brahmic': [ 41 | [0x0900, 0x109F] 42 | ], 43 | 44 | 'georgian': [ 45 | [0x10A0, 0x10ff] 46 | ], 47 | 48 | // Chinese and Japanese. 49 | // The "k" in cjk is for Korean, but we've separated Korean out 50 | 'cjk': [ 51 | [0x3000, 0x30FF], // CJK symbols and punctuation, Hiragana, Katakana 52 | [0x4E00, 0x9FAF], // CJK ideograms 53 | [0xFF00, 0xFF60], // Fullwidth punctuation 54 | // TODO: add halfwidth Katakana and Romanji glyphs 55 | ], 56 | 57 | // Korean 58 | 'hangul': [ 59 | [0xAC00, 0xD7AF] 60 | ], 61 | }; 62 | 63 | final allBlocks = 64 | scriptData.entries.expand((entry) => entry.value).toList(growable: false); 65 | 66 | bool supportedCodepoint(int codepoint) => 67 | allBlocks.any((block) => codepoint >= block[0] && codepoint <= block[1]); 68 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/styling.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the 'Software'), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _stylingEntries = { 27 | [ 28 | '\\displaystyle', 29 | '\\textstyle', 30 | '\\scriptstyle', 31 | '\\scriptscriptstyle', 32 | ]: FunctionSpec( 33 | numArgs: 0, 34 | allowedInText: true, 35 | handler: _stylingHandler, 36 | ), 37 | }; 38 | 39 | GreenNode _stylingHandler(TexParser parser, FunctionContext context) { 40 | final body = parser.parseExpression( 41 | breakOnInfix: true, breakOnTokenText: context.breakOnTokenText); 42 | final style = parseMathStyle( 43 | context.funcName.substring(1, context.funcName.length - 5)); 44 | return StyleNode( 45 | children: body, 46 | optionsDiff: OptionsDiff(style: style), 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/parser/tex/unicode_accents.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import '../../ast/types.dart'; 25 | 26 | const Map> unicodeAccents = { 27 | '\u0300': {Mode.text: '\\`', Mode.math: '\\grave'}, 28 | '\u0308': {Mode.text: '\\"', Mode.math: '\\ddot'}, 29 | '\u0303': {Mode.text: '\\~', Mode.math: '\\tilde'}, 30 | '\u0304': {Mode.text: '\\=', Mode.math: '\\bar'}, 31 | '\u0301': {Mode.text: "\\'", Mode.math: '\\acute'}, 32 | '\u0306': {Mode.text: '\\u', Mode.math: '\\breve'}, 33 | '\u030c': {Mode.text: '\\v', Mode.math: '\\check'}, 34 | '\u0302': {Mode.text: '\\^', Mode.math: '\\hat'}, 35 | '\u0307': {Mode.text: '\\.', Mode.math: '\\dot'}, 36 | '\u030a': {Mode.text: '\\r', Mode.math: '\\mathring'}, 37 | '\u030b': {Mode.text: '\\H', Mode.math: null}, 38 | }; 39 | -------------------------------------------------------------------------------- /lib/src/render/svg/svg_string.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | 4 | String svgStringFromPath( 5 | String path, 6 | Size viewPort, 7 | Rect viewBox, 8 | Color color, { 9 | String preserveAspectRatio = 'xMidYMid meet', 10 | }) => 11 | '' 17 | '' 18 | ''; 19 | 20 | final _alignmentToString = { 21 | Alignment.topLeft: 'xMinYMin', 22 | Alignment.topCenter: 'xMidYMin', 23 | Alignment.topRight: 'xMaxYMin', 24 | Alignment.centerLeft: 'xMinYMid', 25 | Alignment.center: 'xMidYMid', 26 | Alignment.centerRight: 'xMaxYMid', 27 | Alignment.bottomLeft: 'xMinYMax', 28 | Alignment.bottomCenter: 'xMidYMax', 29 | Alignment.bottomRight: 'xMaxYMax', 30 | }; 31 | 32 | Widget svgWidgetFromPath( 33 | String path, 34 | Size viewPort, 35 | Rect viewBox, 36 | Color color, { 37 | Alignment align = Alignment.topLeft, 38 | BoxFit fit = BoxFit.fill, 39 | }) { 40 | final alignment = _alignmentToString[align]; 41 | 42 | assert(fit != BoxFit.none && 43 | fit != BoxFit.fitHeight && 44 | fit != BoxFit.fitWidth && 45 | fit != BoxFit.scaleDown); 46 | final meetOrSlice = fit == BoxFit.contain ? 'meet' : 'slice'; 47 | 48 | final preserveAspectRatio = 49 | fit == BoxFit.fill ? 'none' : '$alignment $meetOrSlice'; 50 | 51 | final svgString = svgStringFromPath(path, viewPort, viewBox, color, 52 | preserveAspectRatio: preserveAspectRatio); 53 | return Container( 54 | height: viewPort.height, 55 | width: viewPort.width, 56 | child: SvgPicture.string( 57 | svgString, 58 | width: viewPort.width, 59 | height: viewPort.height, 60 | fit: fit, 61 | alignment: align, 62 | ), 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/text.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the 'Software'), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _textEntries = { 27 | [ 28 | // Font families 29 | '\\text', '\\textrm', '\\textsf', '\\texttt', '\\textnormal', 30 | // Font weights 31 | '\\textbf', '\\textmd', 32 | // Font Shapes 33 | '\\textit', '\\textup', 34 | ]: FunctionSpec( 35 | numArgs: 1, 36 | greediness: 2, 37 | allowedInText: true, 38 | handler: _textHandler, 39 | ) 40 | }; 41 | GreenNode _textHandler(TexParser parser, FunctionContext context) { 42 | final body = parser.parseArgNode(mode: Mode.text, optional: false)!; 43 | final fontOptions = texTextFontOptions[context.funcName]; 44 | if (fontOptions == null) return body; 45 | return StyleNode( 46 | optionsDiff: OptionsDiff(textFontOptions: fontOptions), 47 | children: body.expandEquationRow(), 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/sizing.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the 'Software'), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _sizeFuncs = [ 27 | '\\tiny', 28 | '\\sixptsize', 29 | '\\scriptsize', 30 | '\\footnotesize', 31 | '\\small', 32 | '\\normalsize', 33 | '\\large', 34 | '\\Large', 35 | '\\LARGE', 36 | '\\huge', 37 | '\\Huge', 38 | ]; 39 | 40 | const _sizingEntries = { 41 | _sizeFuncs: FunctionSpec( 42 | numArgs: 0, 43 | allowedInText: true, 44 | handler: _sizingHandler, 45 | ), 46 | }; 47 | 48 | GreenNode _sizingHandler(TexParser parser, FunctionContext context) { 49 | final body = parser.parseExpression( 50 | breakOnInfix: false, breakOnTokenText: context.breakOnTokenText); 51 | return StyleNode( 52 | children: body, 53 | optionsDiff: OptionsDiff( 54 | size: MathSize.values[_sizeFuncs.indexOf(context.funcName)], 55 | ), 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/underover.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the 'Software'), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _underOverEntries = { 27 | ['\\stackrel', '\\overset', '\\underset']: FunctionSpec( 28 | numArgs: 2, 29 | handler: _underOverHandler, 30 | ) 31 | }; 32 | GreenNode _underOverHandler(TexParser parser, FunctionContext context) { 33 | final shiftedArg = parser.parseArgNode(mode: null, optional: false)!; 34 | final baseArg = parser.parseArgNode(mode: null, optional: false)!; 35 | if (context.funcName == '\\underset') { 36 | return UnderNode( 37 | base: baseArg.wrapWithEquationRow(), 38 | below: shiftedArg.wrapWithEquationRow(), 39 | ); 40 | } else { 41 | return OverNode( 42 | base: baseArg.wrapWithEquationRow(), 43 | above: shiftedArg.wrapWithEquationRow(), 44 | stackRel: context.funcName == '\\stackrel', 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/encoder/tex/encoder_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_math_fork/ast.dart'; 2 | import 'package:flutter_math_fork/src/encoder/encoder.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | import 'package:flutter_math_fork/src/encoder/tex/encoder.dart'; 6 | 7 | import 'recode.dart'; 8 | 9 | void main() { 10 | group('EquationRowEncoderResult', () { 11 | test('empty row', () { 12 | final result = EquationRowTexEncodeResult([]); 13 | expect(result.stringify(TexEncodeConf.mathConf), '{}'); 14 | expect(result.stringify(TexEncodeConf.mathParamConf), ''); 15 | }); 16 | 17 | test('normal row', () { 18 | final result = EquationRowTexEncodeResult([ 19 | 'a', 20 | StaticEncodeResult('b'), 21 | SymbolNode(symbol: 'c'), 22 | EquationRowNode.empty(), 23 | ]); 24 | expect(result.stringify(TexEncodeConf.mathConf), '{abc{}}'); 25 | expect(result.stringify(TexEncodeConf.mathParamConf), 'abc{}'); 26 | }); 27 | 28 | test('symbol contanetation', () { 29 | const testStrings = [ 30 | 'i\\pi x', 31 | 'i\\pi\\xi', 32 | ]; 33 | for (final testString in testStrings) { 34 | expect(recodeTex(testString), testString); 35 | } 36 | }); 37 | }); 38 | group('TexCommandEncoderResult', () { 39 | test('basic spec lookup', () { 40 | final result = 41 | TexCommandEncodeResult(command: '\\frac', args: []); 42 | expect(result.numArgs, 2); 43 | expect(result.numOptionalArgs, 0); 44 | expect(result.argModes, [null, null]); 45 | }); 46 | 47 | test('empty math param', () { 48 | final result = TexCommandEncodeResult( 49 | command: '\\frac', 50 | args: [EquationRowNode.empty(), EquationRowNode.empty()]); 51 | expect(result.stringify(TexEncodeConf.mathConf), '\\frac{}{}'); 52 | }); 53 | 54 | test('single char math param', () { 55 | final result = 56 | TexCommandEncodeResult(command: '\\frac', args: ['1', '2']); 57 | expect(result.stringify(TexEncodeConf.mathConf), '\\frac{1}{2}'); 58 | }); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/ast/nodes/cursor_node.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | import '../../../ast.dart'; 5 | import '../../render/layout/line.dart'; 6 | import '../syntax_tree.dart'; 7 | 8 | /// Node displays vertical bar the size of [MathOptions.fontSize] 9 | /// to replicate a text edit field cursor 10 | class CursorNode extends LeafNode { 11 | @override 12 | BuildResult buildWidget( 13 | MathOptions options, List childBuildResults) { 14 | final baselinePart = 1 - options.fontMetrics.axisHeight / 2; 15 | final height = options.fontSize * baselinePart * options.sizeMultiplier; 16 | final baselineDistance = height * baselinePart; 17 | final cursor = Container(height: height, width: 1.5, color: options.color); 18 | return BuildResult( 19 | options: options, 20 | widget: _BaselineDistance( 21 | baselineDistance: baselineDistance, 22 | child: cursor, 23 | )); 24 | } 25 | 26 | @override 27 | AtomType get leftType => AtomType.ord; 28 | 29 | @override 30 | Mode get mode => Mode.text; 31 | 32 | @override 33 | AtomType get rightType => AtomType.ord; 34 | 35 | @override 36 | bool shouldRebuildWidget(MathOptions oldOptions, MathOptions newOptions) => 37 | false; 38 | } 39 | 40 | /// This render object overrides the return value of 41 | /// [RenderProxyBox.computeDistanceToActualBaseline] 42 | /// 43 | /// Used to align [CursorNode] properly in a [RenderLine] in respect to symbols 44 | class _BaselineDistance extends SingleChildRenderObjectWidget { 45 | const _BaselineDistance({ 46 | Key? key, 47 | required this.baselineDistance, 48 | Widget? child, 49 | }) : super(key: key, child: child); 50 | 51 | final double baselineDistance; 52 | 53 | @override 54 | _BaselineDistanceBox createRenderObject(BuildContext context) => 55 | _BaselineDistanceBox(baselineDistance); 56 | } 57 | 58 | class _BaselineDistanceBox extends RenderProxyBox { 59 | _BaselineDistanceBox(this.baselineDistance); 60 | 61 | final double baselineDistance; 62 | 63 | @override 64 | double? computeDistanceToActualBaseline(TextBaseline baseline) => 65 | baselineDistance; 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/accent_under.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _accentUnderEntries = { 27 | [ 28 | '\\underleftarrow', 29 | '\\underrightarrow', 30 | '\\underleftrightarrow', 31 | '\\undergroup', 32 | // '\\underlinesegment': , 33 | '\\utilde', 34 | 35 | '\\underline' 36 | ]: FunctionSpec(numArgs: 1, handler: _accentUnderHandler), 37 | }; 38 | 39 | const accentUnderMapping = { 40 | '\\underleftarrow': '\u2190', 41 | '\\underrightarrow': '\u2192', 42 | '\\underleftrightarrow': '\u2194', 43 | '\\undergroup': '\u23e0', 44 | // '\\underlinesegment', 45 | '\\utilde': '\u007e', 46 | 47 | '\\underline': '\u00af' 48 | }; 49 | 50 | GreenNode _accentUnderHandler(TexParser parser, FunctionContext context) { 51 | final base = parser.parseArgNode(mode: null, optional: false)!; 52 | return AccentUnderNode( 53 | base: base.wrapWithEquationRow(), 54 | label: accentUnderMapping[context.funcName]!, 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/mclass.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _mclassEntries = { 27 | [ 28 | '\\mathop', 29 | '\\mathord', 30 | '\\mathbin', 31 | '\\mathrel', 32 | '\\mathopen', 33 | '\\mathclose', 34 | '\\mathpunct', 35 | '\\mathinner', 36 | ]: FunctionSpec(numArgs: 1, handler: _mclassHandler), 37 | }; 38 | 39 | GreenNode _mclassHandler(TexParser parser, FunctionContext context) { 40 | final body = parser.parseArgNode(mode: null, optional: false)!; 41 | return EquationRowNode( 42 | children: body.expandEquationRow(), 43 | overrideType: const { 44 | '\\mathop': AtomType.op, 45 | '\\mathord': AtomType.ord, 46 | '\\mathbin': AtomType.bin, 47 | '\\mathrel': AtomType.rel, 48 | '\\mathopen': AtomType.open, 49 | '\\mathclose': AtomType.close, 50 | '\\mathpunct': AtomType.punct, 51 | '\\mathinner': AtomType.inner, 52 | }[context.funcName]); 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/ast/nodes/phantom.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | import '../../render/layout/reset_dimension.dart'; 4 | import '../options.dart'; 5 | import '../syntax_tree.dart'; 6 | import '../types.dart'; 7 | 8 | /// Phantom node. 9 | /// 10 | /// Example: `\phantom` `\hphantom`. 11 | class PhantomNode extends LeafNode { 12 | Mode get mode => Mode.math; 13 | 14 | /// The phantomed child. 15 | // TODO: suppress editbox in edit mode 16 | // If we use arbitrary GreenNode here, then we will face the danger of 17 | // transparent node 18 | final EquationRowNode phantomChild; 19 | 20 | /// Whether to eliminate width. 21 | final bool zeroWidth; 22 | 23 | /// Whether to eliminate height. 24 | final bool zeroHeight; 25 | 26 | /// Whether to eliminate depth. 27 | final bool zeroDepth; 28 | 29 | PhantomNode({ 30 | required this.phantomChild, 31 | this.zeroHeight = false, 32 | this.zeroWidth = false, 33 | this.zeroDepth = false, 34 | }); 35 | 36 | @override 37 | BuildResult buildWidget( 38 | MathOptions options, List childBuildResults) { 39 | final phantomRedNode = 40 | SyntaxNode(parent: null, value: phantomChild, pos: 0); 41 | final phantomResult = phantomRedNode.buildWidget(options); 42 | Widget widget = Opacity( 43 | opacity: 0.0, 44 | child: phantomResult.widget, 45 | ); 46 | widget = ResetDimension( 47 | width: zeroWidth ? 0 : null, 48 | height: zeroHeight ? 0 : null, 49 | depth: zeroDepth ? 0 : null, 50 | child: widget, 51 | ); 52 | return BuildResult( 53 | options: options, 54 | italic: phantomResult.italic, 55 | widget: widget, 56 | ); 57 | } 58 | 59 | @override 60 | AtomType get leftType => phantomChild.leftType; 61 | 62 | @override 63 | AtomType get rightType => phantomChild.rightType; 64 | 65 | @override 66 | bool shouldRebuildWidget(MathOptions oldOptions, MathOptions newOptions) => 67 | phantomChild.shouldRebuildWidget(oldOptions, newOptions); 68 | 69 | @override 70 | Map toJson() => super.toJson() 71 | ..addAll({ 72 | 'phantomChild': phantomChild.toJson(), 73 | if (zeroWidth != false) 'zeroWidth': zeroWidth, 74 | if (zeroHeight != false) 'zeroHeight': zeroHeight, 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /example/lib/equations.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_math_fork/flutter_math.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | 5 | const equations = [ 6 | ['Solution of quadratic equation', r'x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}'], 7 | [ 8 | 'Schrodinger\'s equation', 9 | r'i\hbar\frac{\partial}{\partial t}\Psi(\vec x,t) = -\frac{\hbar}{2m}\nabla^2\Psi(\vec x,t)+ V(\vec x)\Psi(\vec x,t)' 10 | ], 11 | [ 12 | 'Fourier transform', 13 | r'\hat f(\xi) = \int_{-\infty}^\infty {f(x)e^{- 2\pi i \xi x}\mathrm{d}x}' 14 | ], 15 | [ 16 | 'Maxwell\'s equations', 17 | r'''\left\{\begin{array}{l} 18 | \nabla\cdot\vec{D} = \rho \\ 19 | \nabla\cdot\vec{B} = 0 \\ 20 | \nabla\times\vec{E} = -\frac{\partial\vec{B}}{\partial t} \\ 21 | \nabla\times\vec{H} = \vec{J}_f + \frac{\partial\vec{D}}{\partial t} 22 | \end{array}\right.''' 23 | ], 24 | ]; 25 | 26 | class EquationsPage extends StatelessWidget { 27 | @override 28 | Widget build(BuildContext context) => Center( 29 | child: Container( 30 | constraints: BoxConstraints(maxWidth: 800), 31 | child: ListView( 32 | children: equations 33 | .map((entry) => Padding( 34 | padding: const EdgeInsets.all(10), 35 | child: Card( 36 | child: Column( 37 | children: [ 38 | ListTile( 39 | title: Text(entry[0]), 40 | subtitle: SelectableText( 41 | entry[1], 42 | style: GoogleFonts.robotoMono(), 43 | ), 44 | ), 45 | Container( 46 | padding: const EdgeInsets.fromLTRB(1, 5, 1, 5), 47 | child: SelectableMath.tex( 48 | entry[1], 49 | textStyle: TextStyle(fontSize: 22), 50 | ), 51 | ) 52 | ], 53 | ), 54 | ), 55 | )) 56 | .toList(), 57 | ), 58 | ), 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/ast/nodes/function.dart: -------------------------------------------------------------------------------- 1 | import '../../render/layout/line.dart'; 2 | import '../options.dart'; 3 | import '../spacing.dart'; 4 | import '../syntax_tree.dart'; 5 | 6 | /// Function node 7 | /// 8 | /// Examples: `\sin`, `\lim`, `\operatorname` 9 | class FunctionNode extends SlotableNode { 10 | /// Name of the function. 11 | final EquationRowNode functionName; 12 | 13 | /// Argument of the function. 14 | final EquationRowNode argument; 15 | 16 | FunctionNode({ 17 | required this.functionName, 18 | required this.argument, 19 | }); 20 | 21 | @override 22 | BuildResult buildWidget( 23 | MathOptions options, List childBuildResults) => 24 | BuildResult( 25 | options: options, 26 | widget: Line(children: [ 27 | LineElement( 28 | trailingMargin: 29 | getSpacingSize(AtomType.op, argument.leftType, options.style) 30 | .toLpUnder(options), 31 | child: childBuildResults[0]!.widget, 32 | ), 33 | LineElement( 34 | trailingMargin: 0.0, 35 | child: childBuildResults[1]!.widget, 36 | ), 37 | ]), 38 | ); 39 | 40 | @override 41 | List computeChildOptions(MathOptions options) => 42 | List.filled(2, options, growable: false); 43 | 44 | @override 45 | List computeChildren() => [functionName, argument]; 46 | 47 | @override 48 | AtomType get leftType => AtomType.op; 49 | 50 | @override 51 | AtomType get rightType => argument.rightType; 52 | 53 | @override 54 | bool shouldRebuildWidget(MathOptions oldOptions, MathOptions newOptions) => 55 | false; 56 | 57 | @override 58 | FunctionNode updateChildren(List newChildren) => 59 | copyWith(functionName: newChildren[0], argument: newChildren[1]); 60 | 61 | @override 62 | Map toJson() => super.toJson() 63 | ..addAll({ 64 | 'functionName': functionName.toJson(), 65 | 'argument': argument.toJson(), 66 | }); 67 | 68 | FunctionNode copyWith({ 69 | EquationRowNode? functionName, 70 | EquationRowNode? argument, 71 | }) => 72 | FunctionNode( 73 | functionName: functionName ?? this.functionName, 74 | argument: argument ?? this.argument, 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/math.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _mathEntries = { 27 | ['\\(', '\$']: FunctionSpec( 28 | numArgs: 0, 29 | allowedInMath: false, 30 | allowedInText: true, 31 | handler: _mathLeftHandler, 32 | ), 33 | ['\\)', '\\]']: FunctionSpec( 34 | numArgs: 0, 35 | allowedInMath: false, 36 | allowedInText: true, 37 | handler: _mathRightHandler), 38 | }; 39 | GreenNode _mathLeftHandler(TexParser parser, FunctionContext context) { 40 | final outerMode = parser.mode; 41 | parser.switchMode(Mode.math); 42 | final close = context.funcName == '\\(' ? '\\)' : '\$'; 43 | final body = 44 | parser.parseExpression(breakOnInfix: false, breakOnTokenText: close); 45 | 46 | parser.expect(close); 47 | parser.switchMode(outerMode); 48 | 49 | return StyleNode( 50 | optionsDiff: OptionsDiff(style: MathStyle.text), 51 | children: body, 52 | ); 53 | } 54 | 55 | GreenNode _mathRightHandler(TexParser parser, FunctionContext context) { 56 | throw ParseException('Mismatched ${context.funcName}'); 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/color.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _colorEntries = { 27 | ['\\textcolor']: FunctionSpec( 28 | numArgs: 2, 29 | allowedInText: true, 30 | greediness: 3, 31 | handler: _textcolorHandler, 32 | ), 33 | ['\\color']: FunctionSpec( 34 | numArgs: 1, 35 | allowedInText: true, 36 | greediness: 3, 37 | handler: _colorHandler, 38 | ), 39 | }; 40 | GreenNode _textcolorHandler(TexParser parser, FunctionContext context) { 41 | final color = parser.parseArgColor(optional: false)!; 42 | final body = parser.parseArgNode(mode: null, optional: false)!; 43 | return StyleNode( 44 | optionsDiff: OptionsDiff(color: color), 45 | children: body.expandEquationRow(), 46 | ); 47 | } 48 | 49 | GreenNode _colorHandler(TexParser parser, FunctionContext context) { 50 | final color = parser.parseArgColor(optional: false); 51 | 52 | final body = parser.parseExpression( 53 | breakOnInfix: true, breakOnTokenText: context.breakOnTokenText); 54 | return StyleNode( 55 | optionsDiff: OptionsDiff(color: color), 56 | children: body, 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/cr.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _crEntries = { 27 | ['\\cr', '\\newline']: FunctionSpec( 28 | numArgs: 0, 29 | numOptionalArgs: 1, 30 | allowedInText: true, 31 | handler: _crHandler, 32 | ), 33 | }; 34 | 35 | class CrNode extends TemporaryNode { 36 | final bool newLine; 37 | final bool newRow; 38 | final Measurement? size; 39 | CrNode({ 40 | required this.newLine, 41 | required this.newRow, 42 | this.size, 43 | }); 44 | } 45 | 46 | GreenNode _crHandler(TexParser parser, FunctionContext context) { 47 | final size = parser.parseArgSize(optional: true); 48 | final newRow = context.funcName == '\\cr'; 49 | var newLine = false; 50 | if (!newRow) { 51 | if (parser.settings.displayMode && 52 | parser.settings.useStrictBehavior( 53 | 'newLineInDisplayMode', 54 | 'In LaTeX, \\\\ or \\newline ' 55 | 'does nothing in display mode')) { 56 | newLine = false; 57 | } else { 58 | newLine = true; 59 | } 60 | } 61 | return CrNode(newLine: newLine, newRow: newRow, size: size); 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/encoder/encoder.dart: -------------------------------------------------------------------------------- 1 | import '../ast/syntax_tree.dart'; 2 | import '../parser/tex/settings.dart'; 3 | import '../utils/log.dart'; 4 | import 'exception.dart'; 5 | 6 | abstract class EncodeResult { 7 | const EncodeResult(); 8 | String stringify(covariant EncodeConf conf); 9 | } 10 | 11 | class StaticEncodeResult extends EncodeResult { 12 | const StaticEncodeResult(this.string); 13 | 14 | final String string; 15 | 16 | @override 17 | String stringify(EncodeConf conf) => string; 18 | } 19 | 20 | class NonStrictEncodeResult extends EncodeResult { 21 | final String errorCode; 22 | final String errorMsg; 23 | final EncodeResult placeHolder; 24 | 25 | const NonStrictEncodeResult( 26 | this.errorCode, 27 | this.errorMsg, [ 28 | this.placeHolder = const StaticEncodeResult(''), 29 | ]); 30 | 31 | NonStrictEncodeResult.string( 32 | this.errorCode, 33 | this.errorMsg, [ 34 | String placeHolder = '', 35 | ]) : this.placeHolder = StaticEncodeResult(placeHolder); 36 | 37 | @override 38 | String stringify(EncodeConf conf) { 39 | conf.reportNonstrict(errorCode, errorMsg); 40 | return placeHolder.stringify(conf); 41 | } 42 | } 43 | 44 | typedef EncoderFun = EncodeResult Function(T node); 45 | 46 | typedef StrictFun = Strict Function(String errorCode, String errorMsg, 47 | [dynamic token]); 48 | 49 | abstract class EncodeConf { 50 | final Strict strict; 51 | 52 | final StrictFun? strictFun; 53 | 54 | const EncodeConf({ 55 | this.strict = Strict.warn, 56 | this.strictFun, 57 | }); 58 | 59 | void reportNonstrict(String errorCode, String errorMsg, [dynamic token]) { 60 | final strict = this.strict != Strict.function 61 | ? this.strict 62 | : (strictFun?.call(errorCode, errorMsg, token) ?? Strict.warn); 63 | switch (strict) { 64 | case Strict.ignore: 65 | return; 66 | case Strict.error: 67 | throw EncoderException( 68 | "Nonstrict Tex encoding and strict mode is set to 'error': " 69 | '$errorMsg [$errorCode]', 70 | token); 71 | case Strict.warn: 72 | warn("Nonstrict Tex encoding and strict mode is set to 'warn': " 73 | '$errorMsg [$errorCode]'); 74 | break; 75 | default: 76 | warn('Nonstrict Tex encoding and strict mode is set to ' 77 | "unrecognized '$strict': $errorMsg [$errorCode]"); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/render/svg/delimiter.dart: -------------------------------------------------------------------------------- 1 | import '../../ast/options.dart'; 2 | import '../../ast/size.dart'; 3 | import '../../ast/style.dart'; 4 | import '../../ast/symbols/symbols.dart'; 5 | import '../../ast/types.dart'; 6 | import '../../font/metrics/font_metrics.dart'; 7 | import '../../utils/unicode_literal.dart'; 8 | 9 | class DelimiterConf { 10 | final FontOptions font; 11 | final MathStyle style; 12 | 13 | const DelimiterConf(this.font, this.style); 14 | } 15 | 16 | const mainRegular = FontOptions(fontFamily: 'Main'); 17 | const size1Regular = FontOptions(fontFamily: 'Size1'); 18 | const size2Regular = FontOptions(fontFamily: 'Size2'); 19 | const size3Regular = FontOptions(fontFamily: 'Size3'); 20 | const size4Regular = FontOptions(fontFamily: 'Size4'); 21 | 22 | const stackNeverDelimiterSequence = [ 23 | DelimiterConf(mainRegular, MathStyle.scriptscript), 24 | DelimiterConf(mainRegular, MathStyle.script), 25 | DelimiterConf(mainRegular, MathStyle.text), 26 | DelimiterConf(size1Regular, MathStyle.text), 27 | DelimiterConf(size2Regular, MathStyle.text), 28 | DelimiterConf(size3Regular, MathStyle.text), 29 | DelimiterConf(size4Regular, MathStyle.text), 30 | ]; 31 | 32 | const stackAlwaysDelimiterSequence = [ 33 | DelimiterConf(mainRegular, MathStyle.scriptscript), 34 | DelimiterConf(mainRegular, MathStyle.script), 35 | DelimiterConf(mainRegular, MathStyle.text), 36 | ]; 37 | 38 | const stackLargeDelimiterSequence = [ 39 | DelimiterConf(mainRegular, MathStyle.scriptscript), 40 | DelimiterConf(mainRegular, MathStyle.script), 41 | DelimiterConf(mainRegular, MathStyle.text), 42 | DelimiterConf(size1Regular, MathStyle.text), 43 | DelimiterConf(size2Regular, MathStyle.text), 44 | DelimiterConf(size3Regular, MathStyle.text), 45 | DelimiterConf(size4Regular, MathStyle.text), 46 | ]; 47 | 48 | double getHeightForDelim({ 49 | required String delim, 50 | required String fontName, 51 | required MathStyle style, 52 | required MathOptions options, 53 | }) { 54 | final char = symbolRenderConfigs[delim]?.math?.replaceChar ?? delim; 55 | final metrics = 56 | getCharacterMetrics(character: char, fontName: fontName, mode: Mode.math); 57 | if (metrics == null) { 58 | throw StateError('Illegal delimiter char $delim' 59 | '(${unicodeLiteral(delim)}) appeared in AST'); 60 | } 61 | final fullHeight = metrics.height + metrics.depth; 62 | final newOptions = options.havingStyle(style); 63 | return fullHeight.cssEm.toLpUnder(newOptions); 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/render/layout/shift_baseline.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/rendering.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class ShiftBaseline extends SingleChildRenderObjectWidget { 5 | const ShiftBaseline({ 6 | Key? key, 7 | this.relativePos, 8 | this.offset = 0, 9 | required Widget child, 10 | }) : super(key: key, child: child); 11 | 12 | final double? relativePos; 13 | 14 | final double offset; 15 | 16 | @override 17 | RenderShiftBaseline createRenderObject(BuildContext context) => 18 | RenderShiftBaseline(relativePos: relativePos, offset: offset); 19 | 20 | @override 21 | void updateRenderObject( 22 | BuildContext context, RenderShiftBaseline renderObject) { 23 | renderObject 24 | ..relativePos = relativePos 25 | ..offset = offset; 26 | } 27 | } 28 | 29 | class RenderShiftBaseline extends RenderProxyBox { 30 | RenderShiftBaseline({ 31 | RenderBox? child, 32 | double? relativePos, 33 | double offset = 0, 34 | }) : _relativePos = relativePos, 35 | _offset = offset, 36 | super(child); 37 | 38 | double? get relativePos => _relativePos; 39 | double? _relativePos; 40 | set relativePos(double? value) { 41 | if (_relativePos != value) { 42 | _relativePos = value; 43 | markNeedsLayout(); 44 | } 45 | } 46 | 47 | double get offset => _offset; 48 | double _offset; 49 | set offset(double value) { 50 | if (_offset != value) { 51 | _offset = value; 52 | markNeedsLayout(); 53 | } 54 | } 55 | 56 | var _height = 0.0; 57 | 58 | @override 59 | Size computeDryLayout(BoxConstraints constraints) => 60 | child?.getDryLayout(constraints) ?? Size.zero; 61 | 62 | @override 63 | double? computeDistanceToActualBaseline(TextBaseline baseline) { 64 | if (relativePos != null) { 65 | return relativePos! * _height + offset; 66 | } 67 | if (child != null) { 68 | // assert(!debugNeedsLayout); 69 | final childBaselineDistance = 70 | child!.getDistanceToActualBaseline(baseline) ?? _height; 71 | //ignore: avoid_returning_null 72 | // if (childBaselineDistance == null) return null; 73 | return childBaselineDistance + offset; 74 | } else { 75 | return super.computeDistanceToActualBaseline(baseline); 76 | } 77 | } 78 | 79 | @override 80 | void performLayout() { 81 | super.performLayout(); 82 | // We have to hack like this to know the height of this object!!! 83 | _height = size.height; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/encoder/matcher_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_math_fork/src/ast/nodes/frac.dart'; 2 | import 'package:flutter_math_fork/src/ast/nodes/symbol.dart'; 3 | import 'package:flutter_math_fork/tex.dart'; 4 | import 'package:flutter_test/flutter_test.dart' hide isA, isNull; 5 | 6 | import 'package:flutter_math_fork/src/encoder/matcher.dart'; 7 | 8 | void main() { 9 | group('Matcher test', () { 10 | test('null matcher', () { 11 | expect(isNull.match(null), true); 12 | expect(isNull.match(EquationRowNode.empty()), false); 13 | }); 14 | 15 | test('node matcher', () { 16 | final target = TexParser('\\frac{123}{abc}', TexParserSettings()) 17 | .parse() 18 | .children 19 | .first; 20 | expect(isA().match(target), true); 21 | expect(isA().match(target), false); 22 | 23 | expect( 24 | isA(children: [ 25 | isA(), 26 | isA(), 27 | isA(), 28 | ]).match(target), 29 | false, 30 | ); 31 | expect( 32 | isA(children: [ 33 | isA(), 34 | isNull, 35 | ]).match(target), 36 | false, 37 | ); 38 | 39 | expect(isA(child: isA()).match(target), false); 40 | 41 | expect(isA(firstChild: isA()).match(target), false); 42 | 43 | expect(isA(lastChild: isA()).match(target), false); 44 | 45 | expect(isA(anyChild: isA()).match(target), false); 46 | 47 | expect( 48 | isA( 49 | everyChild: isA( 50 | anyChild: isA(matchSelf: (node) => node.symbol == '1'), 51 | ), 52 | ).match(target), 53 | false, 54 | ); 55 | 56 | final completeMacher = isA( 57 | matchSelf: (node) => node.barSize == null, 58 | selfSpecificity: 1, 59 | children: [ 60 | isA(), 61 | isA(), 62 | ], 63 | firstChild: isA(), 64 | lastChild: isA(), 65 | anyChild: isA(), 66 | everyChild: isA(), 67 | ); 68 | expect( 69 | completeMacher.specificity, 70 | 3 * isA().specificity + 71 | isA().specificity + 72 | 1, 73 | ); 74 | 75 | expect(completeMacher.match(target), true); 76 | }); 77 | }); 78 | } 79 | -------------------------------------------------------------------------------- /lib/src/ast/spacing.dart: -------------------------------------------------------------------------------- 1 | import 'size.dart'; 2 | import 'style.dart'; 3 | 4 | import 'syntax_tree.dart'; 5 | 6 | const thinspace = Measurement(value: 3, unit: Unit.mu); 7 | const mediumspace = Measurement(value: 4, unit: Unit.mu); 8 | const thickspace = Measurement(value: 5, unit: Unit.mu); 9 | 10 | const Map> _spacings = { 11 | AtomType.ord: { 12 | AtomType.op: thinspace, 13 | AtomType.bin: mediumspace, 14 | AtomType.rel: thickspace, 15 | AtomType.inner: thinspace, 16 | }, 17 | AtomType.op: { 18 | AtomType.ord: thinspace, 19 | AtomType.op: thinspace, 20 | AtomType.rel: thickspace, 21 | AtomType.inner: thinspace, 22 | }, 23 | AtomType.bin: { 24 | AtomType.ord: mediumspace, 25 | AtomType.op: mediumspace, 26 | AtomType.open: mediumspace, 27 | AtomType.inner: mediumspace, 28 | }, 29 | AtomType.rel: { 30 | AtomType.ord: thickspace, 31 | AtomType.op: thickspace, 32 | AtomType.open: thickspace, 33 | AtomType.inner: thickspace, 34 | }, 35 | AtomType.open: {}, 36 | AtomType.close: { 37 | AtomType.op: thinspace, 38 | AtomType.bin: mediumspace, 39 | AtomType.rel: thickspace, 40 | AtomType.inner: thinspace, 41 | }, 42 | AtomType.punct: { 43 | AtomType.ord: thinspace, 44 | AtomType.op: thinspace, 45 | AtomType.rel: thickspace, 46 | AtomType.open: thinspace, 47 | AtomType.close: thinspace, 48 | AtomType.punct: thinspace, 49 | AtomType.inner: thinspace, 50 | }, 51 | AtomType.inner: { 52 | AtomType.ord: thinspace, 53 | AtomType.op: thinspace, 54 | AtomType.bin: mediumspace, 55 | AtomType.rel: thickspace, 56 | AtomType.open: thinspace, 57 | AtomType.punct: thinspace, 58 | AtomType.inner: thinspace, 59 | }, 60 | AtomType.spacing: {}, 61 | }; 62 | 63 | const Map> _tightSpacings = { 64 | AtomType.ord: { 65 | AtomType.op: thinspace, 66 | }, 67 | AtomType.op: { 68 | AtomType.ord: thinspace, 69 | AtomType.op: thinspace, 70 | }, 71 | AtomType.bin: {}, 72 | AtomType.rel: {}, 73 | AtomType.open: {}, 74 | AtomType.close: { 75 | AtomType.op: thinspace, 76 | }, 77 | AtomType.punct: {}, 78 | AtomType.inner: { 79 | AtomType.op: thinspace, 80 | }, 81 | AtomType.spacing: {}, 82 | }; 83 | 84 | Measurement getSpacingSize(AtomType left, AtomType right, MathStyle style) => 85 | (style <= MathStyle.script 86 | ? (_tightSpacings[left]?[right]) 87 | : _spacings[left]?[right]) ?? 88 | Measurement.zero; 89 | -------------------------------------------------------------------------------- /lib/src/parser/tex/functions/katex_base/kern.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | part of katex_base; 25 | 26 | const _kernEntries = { 27 | ['\\kern', '\\mkern', '\\hskip', '\\mskip']: FunctionSpec( 28 | numArgs: 1, 29 | allowedInText: true, 30 | handler: _kernHandler, 31 | ), 32 | }; 33 | GreenNode _kernHandler(TexParser parser, FunctionContext context) { 34 | final size = parser.parseArgSize(optional: false) ?? Measurement.zero; 35 | 36 | final mathFunction = (context.funcName[1] == 'm'); 37 | final muUnit = (size.unit == Unit.mu); 38 | if (mathFunction) { 39 | if (!muUnit) { 40 | parser.settings.reportNonstrict( 41 | 'mathVsTextUnits', 42 | "LaTeX's ${context.funcName} supports only mu units, " 43 | 'not ${size.unit} units'); 44 | } 45 | if (parser.mode != Mode.math) { 46 | parser.settings.reportNonstrict('mathVsTextUnits', 47 | "LaTeX's ${context.funcName} works only in math mode"); 48 | } 49 | } else { 50 | if (muUnit) { 51 | parser.settings.reportNonstrict('mathVsTextUnits', 52 | "LaTeX's ${context.funcName} doesn't support mu units"); 53 | } 54 | } 55 | 56 | return SpaceNode( 57 | height: Measurement.zero, 58 | width: size, 59 | mode: parser.mode, 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /doc/migration.0.2.0.md: -------------------------------------------------------------------------------- 1 | # v0.2.0 Migration Guide 2 | 3 | ## Name Changes 4 | | Old name | New name | 5 | |------------|-------------------| 6 | | Options | MathOptions | 7 | | Settings | TexParserSettings | 8 | | SizeMode | MathSize | 9 | | ParseError | ParseException | 10 | 11 | ## New Widget Variant 12 | The `FlutterMath` class has been deprecated and becomes a wrapper of `Math` widget. You should use more specific widget variants: `Math` and `SelectableMath`. 13 | 14 | `Math` is static, non-selectable. `SelectableMath` is static but selectable. If you wish to avoid extra performance cost brings by selectibility, use `Math`. 15 | 16 | The constructor name has also been changed to `Math.tex` and `SelectableMath.tex`. 17 | 18 | ## TextStyle over Options 19 | `TextStyle` will be adopted as the parameter in exposed APIs to replace `Options`. Directly supplying `Options` is OK, but is apparently not the best way. 20 | 21 | ### Use TextStyle to controll the size of your equation 22 | 1. `TextStyle.fontSize` will be the size of any math symbols. 23 | 2. `logicalPpi` will be used to decide the size of absolute units (pt, cm, inch, etc). 24 | 3. If `logicalPpi` is null, then absolute units will resize on different `TextStyle.fontSize` to keep a consistent ratio (Just like current `baseSizeMultiplier`'s behavior). 25 | 4. `baseSizeMultiplier` is deprecated. If you still wish similar behavior, calculate relevant parameters from `MathOptions.defaultFontSize` and `double defaultLogicalPpi(double fontSize)`. 26 | 5. If neither `TextStyle.fontSize` nor `logicalPpi` is supplied, then the widget will use the default `TextStyle` supplied by Flutter's build context. 27 | 28 | ## Sanitized Error System 29 | `ParseError` will be renamed to `ParseException`. Also, other throws within the library will be sanitized to throw either `ParseException`, `BuildException`, `EncodeExecption`. All of them extends `FlutterMathException`. As a result, `onErrorFallback` will have a different signature and allows users to handle exceptions with type safety. 30 | 31 | Detailed exception variant can be found in their respective API documentations. 32 | 33 | The final API will look like 34 | ``` 35 | Math.tex( 36 | r'\frac a b', 37 | textStyle: TextStyle(fontSize: 42), 38 | // settings: TexParserSettings(), 39 | // logicalPpi: defaultLogicalPpi(42), 40 | onErrorFallback: (err) => { 41 | if (error is ParseException) 42 | return SelectableText('ParseError: ${err.message}'); 43 | return Container( 44 | color: Colors.red, 45 | SelectableText(err.toString()); 46 | ) 47 | }, 48 | ) 49 | ``` -------------------------------------------------------------------------------- /lib/src/encoder/tex/functions.dart: -------------------------------------------------------------------------------- 1 | library tex_encoder_functions; 2 | 3 | import 'package:collection/collection.dart'; 4 | 5 | import '../../ast/nodes/accent.dart'; 6 | import '../../ast/nodes/accent_under.dart'; 7 | import '../../ast/nodes/frac.dart'; 8 | import '../../ast/nodes/function.dart'; 9 | import '../../ast/nodes/left_right.dart'; 10 | import '../../ast/nodes/multiscripts.dart'; 11 | import '../../ast/nodes/nary_op.dart'; 12 | import '../../ast/nodes/over.dart'; 13 | import '../../ast/nodes/sqrt.dart'; 14 | import '../../ast/nodes/stretchy_op.dart'; 15 | import '../../ast/nodes/style.dart'; 16 | import '../../ast/nodes/symbol.dart'; 17 | import '../../ast/nodes/under.dart'; 18 | import '../../ast/options.dart'; 19 | import '../../ast/size.dart'; 20 | import '../../ast/style.dart'; 21 | import '../../ast/symbols/symbols_composite.dart'; 22 | import '../../ast/syntax_tree.dart'; 23 | import '../../ast/types.dart'; 24 | import '../../parser/tex/font.dart'; 25 | import '../../parser/tex/functions.dart'; 26 | import '../../parser/tex/functions/katex_base.dart'; 27 | import '../../parser/tex/symbols.dart'; 28 | import '../../utils/alpha_numeric.dart'; 29 | import '../../utils/unicode_literal.dart'; 30 | import '../encoder.dart'; 31 | import '../matcher.dart'; 32 | import '../optimization.dart'; 33 | import 'encoder.dart'; 34 | 35 | part 'functions/accent.dart'; 36 | part 'functions/accent_under.dart'; 37 | part 'functions/frac.dart'; 38 | part 'functions/function.dart'; 39 | part 'functions/left_right.dart'; 40 | part 'functions/multiscripts.dart'; 41 | part 'functions/nary.dart'; 42 | part 'functions/sqrt.dart'; 43 | part 'functions/stretchy_op.dart'; 44 | part 'functions/style.dart'; 45 | part 'functions/symbol.dart'; 46 | 47 | const Map encoderFunctions = { 48 | EquationRowNode: _equationRowNodeEncoderFun, 49 | AccentNode: _accentEncoder, 50 | AccentUnderNode: _accentUnderEncoder, 51 | FracNode: _fracEncoder, 52 | FunctionNode: _functionEncoder, 53 | LeftRightNode: _leftRightEncoder, 54 | MultiscriptsNode: _multisciprtsEncoder, 55 | NaryOperatorNode: _naryEncoder, 56 | SqrtNode: _sqrtEncoder, 57 | StretchyOpNode: _stretchyOpEncoder, 58 | SymbolNode: _symbolEncoder, 59 | StyleNode: _styleEncoder, 60 | }; 61 | 62 | EncodeResult _equationRowNodeEncoderFun(GreenNode node) => 63 | EquationRowTexEncodeResult((node as EquationRowNode) 64 | .children 65 | .map(encodeTex) 66 | .toList(growable: false)); 67 | 68 | final optimizationEntries = [ 69 | ..._fracOptimizationEntries, 70 | ..._functionOptimizationEntries, 71 | ]..sortBy((entry) => -entry.priority); 72 | -------------------------------------------------------------------------------- /lib/src/parser/tex/define_environment.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import '../../ast/syntax_tree.dart'; 25 | import '../../ast/types.dart'; 26 | import 'environments/array.dart'; 27 | import 'environments/eqn_array.dart'; 28 | import 'parser.dart'; 29 | 30 | class EnvContext { 31 | final Mode mode; 32 | final String envName; 33 | // final TexParser parser; 34 | const EnvContext({ 35 | required this.mode, 36 | required this.envName, 37 | // required this.parser, 38 | }); 39 | } 40 | 41 | class EnvSpec { 42 | final int numArgs; 43 | final int greediness; 44 | final bool allowedInText; 45 | final int numOptionalArgs; 46 | final GreenNode Function(TexParser parser, EnvContext context) handler; 47 | const EnvSpec({ 48 | required this.numArgs, 49 | this.greediness = 1, 50 | this.allowedInText = false, 51 | this.numOptionalArgs = 0, 52 | required this.handler, 53 | }); 54 | } 55 | 56 | final Map _environments = {}; 57 | Map get environments { 58 | if (_environments.isEmpty) { 59 | _environmentsEntries.forEach((key, value) { 60 | for (final name in key) { 61 | _environments[name] = value; 62 | } 63 | }); 64 | } 65 | return _environments; 66 | } 67 | 68 | final _environmentsEntries = { 69 | ...arrayEntries, 70 | ...eqnArrayEntries, 71 | }; 72 | -------------------------------------------------------------------------------- /lib/src/parser/tex/symbols_extra.dart: -------------------------------------------------------------------------------- 1 | // These symbols are migrated from elsewhere in KaTeX, e.g., macro 2 | 3 | import '../../ast/syntax_tree.dart'; 4 | import 'symbols.dart'; 5 | 6 | const extraTexMathSymbolCommandConfigs = { 7 | // Stacked operator 8 | // '\u2258': TexSymbolConfig('\u2258'), 9 | '\u2259': TexSymbolConfig('\u2259'), 10 | '\u225A': TexSymbolConfig('\u225A'), 11 | '\u225B': TexSymbolConfig('\u225B'), 12 | '\u225D': TexSymbolConfig('\u225D'), 13 | '\u225E': TexSymbolConfig('\u225E'), 14 | '\u225F': TexSymbolConfig('\u225F'), 15 | 16 | // Circled characters 17 | // '\\copyright': TexSymbolConfig('\u00A9'), // © 18 | 19 | // Negated relations 20 | // '\\not': TexSymbolConfig('\u0338'), 21 | '\\neq': TexSymbolConfig('\u2260'), 22 | '\\notin': TexSymbolConfig('\u2209'), 23 | '\\notni': TexSymbolConfig('\u220C'), 24 | '\u2260': TexSymbolConfig('\u2260'), 25 | '\u2209': TexSymbolConfig('\u2209'), 26 | '\u220C': TexSymbolConfig('\u220C'), 27 | 28 | // colon 29 | '\\colon': TexSymbolConfig(':', type: AtomType.punct), // From MathJax 30 | 31 | // Composite characters 32 | '\\dblcolon': TexSymbolConfig('\u2237'), 33 | '\\coloneqq': TexSymbolConfig('\u2254'), 34 | '\\eqqcolon': TexSymbolConfig('\u2255'), 35 | '\\eqcolon': TexSymbolConfig('\u2239'), 36 | '\\llbracket': TexSymbolConfig('\u27e6'), 37 | '\\rrbracket': TexSymbolConfig('\u27e7'), 38 | '\\lBrace': TexSymbolConfig('\u2983'), 39 | '\\rBrace': TexSymbolConfig('\u2984'), 40 | 41 | // // Private KaTeX code point 42 | // '\\gvertneqq': TexSymbolConfig('\u2269', type: AtomType.rel), 43 | // '\\lvertneqq': TexSymbolConfig('\u2268', type: AtomType.rel), 44 | // '\\ngeqq': TexSymbolConfig('\u2271', type: AtomType.rel), 45 | // '\\ngeqslant': TexSymbolConfig('\u2271', type: AtomType.rel), 46 | // '\\nleqq': TexSymbolConfig('\u2270', type: AtomType.rel), 47 | // '\\nleqslant': TexSymbolConfig('\u2270', type: AtomType.rel), 48 | // '\\nshortmid': TexSymbolConfig('∤', type: AtomType.rel), 49 | // '\\nshortparallel': TexSymbolConfig('∦', type: AtomType.rel), 50 | // '\\nsubseteqq': TexSymbolConfig('\u2288', type: AtomType.rel), 51 | // '\\nsupseteqq': TexSymbolConfig('\u2289', type: AtomType.rel), 52 | // '\\varsubsetneq': TexSymbolConfig('⊊', type: AtomType.rel), 53 | // '\\varsubsetneqq': TexSymbolConfig('⫋', type: AtomType.rel), 54 | // '\\varsupsetneq': TexSymbolConfig('⊋', type: AtomType.rel), 55 | // '\\varsupsetneqq': TexSymbolConfig('⫌', type: AtomType.rel), 56 | }; 57 | 58 | const extraTexTextSymbolCommandConfigs = { 59 | // '\\textcopyright': TexSymbolConfig('\u00A9'), // © 60 | // '\\textregistered': TexSymbolConfig('\u00AE'), // ® 61 | }; 62 | -------------------------------------------------------------------------------- /lib/src/widgets/selection/gesture_detector_builder_selectable.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'gesture_detector_builder.dart'; 5 | import 'overlay_manager.dart'; 6 | 7 | class SelectableMathSelectionGestureDetectorBuilder 8 | extends MathSelectionGestureDetectorBuilder { 9 | SelectableMathSelectionGestureDetectorBuilder({ 10 | required SelectionOverlayManagerMixin delegate, 11 | }) : super(delegate: delegate); 12 | 13 | @override 14 | void onForcePressStart(ForcePressDetails details) { 15 | super.onForcePressStart(details); 16 | if (delegate.selectionEnabled && shouldShowSelectionToolbar) { 17 | delegate.showToolbar(); 18 | } 19 | } 20 | 21 | @override 22 | void onForcePressEnd(ForcePressDetails details) { 23 | // Not required. 24 | } 25 | 26 | @override 27 | void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) { 28 | if (delegate.selectionEnabled) { 29 | delegate.handleSelectionChanged( 30 | delegate.getWordsRangeInRange( 31 | from: details.globalPosition - details.offsetFromOrigin, 32 | to: details.globalPosition), 33 | SelectionChangedCause.longPress, 34 | ); 35 | } 36 | } 37 | 38 | @override 39 | void onSingleTapUp(TapDragUpDetails details) { 40 | delegate.hide(); 41 | if (delegate.selectionEnabled) { 42 | switch (Theme.of(delegate.context).platform) { 43 | case TargetPlatform.iOS: 44 | case TargetPlatform.macOS: 45 | delegate.selectPositionAt( 46 | from: lastTapDownPosition!, 47 | cause: SelectionChangedCause.tap, 48 | ); 49 | // Should select word edge here, but not supporting now 50 | break; 51 | case TargetPlatform.android: 52 | case TargetPlatform.fuchsia: 53 | case TargetPlatform.linux: 54 | case TargetPlatform.windows: 55 | delegate.selectPositionAt( 56 | from: lastTapDownPosition!, 57 | cause: SelectionChangedCause.tap, 58 | ); 59 | break; 60 | } 61 | } 62 | // if (_state.widget.onTap != null) 63 | // _state.widget.onTap(); 64 | } 65 | 66 | @override 67 | void onSingleLongTapStart(LongPressStartDetails details) { 68 | if (delegate.selectionEnabled) { 69 | delegate.selectWordAt( 70 | offset: details.globalPosition, 71 | cause: SelectionChangedCause.longPress, 72 | ); 73 | 74 | Feedback.forLongPress(delegate.context); 75 | 76 | // renderEditable.selectWord(cause: SelectionChangedCause.longPress); 77 | // Feedback.forLongPress(_state.context); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/parser/tex/parse_error.dart: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013-2019 Khan Academy and other contributors 4 | // Copyright (c) 2020 znjameswu 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import '../../widgets/exception.dart'; 25 | import 'token.dart'; 26 | 27 | class ParseException implements FlutterMathException { 28 | /// Nullable 29 | int? position; 30 | String message; 31 | 32 | String get messageWithType => 'Parser Error: $message'; 33 | 34 | /// Nullable 35 | Token? token; 36 | 37 | ParseException(String message, [this.token]) : message = '$message' { 38 | final loc = token?.loc; 39 | if (loc != null && loc.start <= loc.end) { 40 | final input = loc.lexer.input; 41 | 42 | final start = loc.start; 43 | this.position = start; 44 | final end = loc.end; 45 | if (start == input.length) { 46 | message = '$message at end of input: '; 47 | } else { 48 | message = '$message at position ${start + 1}: '; 49 | } 50 | 51 | final underlined = input 52 | .substring(start, end) 53 | .replaceAllMapped(RegExp(r'[^]'), (match) => '${match[0]}\u0332'); 54 | if (start > 15) { 55 | message = '$message…${input.substring(start - 15, start)}$underlined'; 56 | } else { 57 | message = '$message${input.substring(0, start)}$underlined'; 58 | } 59 | if (end + 15 < input.length) { 60 | message = '$message${input.substring(end, end + 15)}…'; 61 | } else { 62 | message = '$message${input.substring(end)}'; 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/encoder/tex/functions/style_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter_math_fork/ast.dart'; 4 | import 'package:flutter_math_fork/src/parser/tex/font.dart'; 5 | import 'package:flutter_test/flutter_test.dart'; 6 | 7 | import 'package:flutter_math_fork/src/encoder/tex/encoder.dart'; 8 | 9 | void main() { 10 | group('style encoding test', () { 11 | test('math style handling', () { 12 | expect( 13 | StyleNode( 14 | optionsDiff: OptionsDiff(style: MathStyle.display), 15 | children: [SymbolNode(symbol: 'a')], 16 | ).encodeTeX(), 17 | '{\\displaystyle a}', 18 | ); 19 | }); 20 | 21 | test('size handling', () { 22 | expect( 23 | StyleNode( 24 | optionsDiff: OptionsDiff(size: MathSize.scriptsize), 25 | children: [SymbolNode(symbol: 'a')], 26 | ).encodeTeX(), 27 | '{\\scriptsize a}', 28 | ); 29 | }); 30 | 31 | test('font handling', () { 32 | expect( 33 | StyleNode( 34 | optionsDiff: 35 | OptionsDiff(mathFontOptions: texMathFontOptions['\\mathbf']), 36 | children: [SymbolNode(symbol: 'a')], 37 | ).encodeTeX(), 38 | '\\mathbf{a}', 39 | ); 40 | expect( 41 | StyleNode( 42 | optionsDiff: 43 | OptionsDiff(textFontOptions: texTextFontOptions['\\textbf']), 44 | children: [SymbolNode(symbol: 'a', mode: Mode.text)], 45 | ).encodeTeX(), 46 | '\\textbf{a}', 47 | ); 48 | }); 49 | 50 | test('color handling', () { 51 | expect( 52 | StyleNode( 53 | optionsDiff: OptionsDiff(color: Color.fromARGB(0, 1, 2, 3)), 54 | children: [SymbolNode(symbol: 'a')], 55 | ).encodeTeX(), 56 | '\\textcolor{#010203}{a}', 57 | ); 58 | }); 59 | 60 | test('avoid extra brackets', () { 61 | expect( 62 | StyleNode( 63 | optionsDiff: OptionsDiff( 64 | style: MathStyle.display, 65 | size: MathSize.scriptsize, 66 | color: Color.fromARGB(0, 1, 2, 3), 67 | ), 68 | children: [SymbolNode(symbol: 'a')], 69 | ).encodeTeX(), 70 | '\\textcolor{#010203}{\\displaystyle \\scriptsize a}', 71 | ); 72 | 73 | expect( 74 | EquationRowNode(children: [ 75 | SymbolNode(symbol: 'z'), 76 | StyleNode( 77 | optionsDiff: OptionsDiff( 78 | style: MathStyle.display, 79 | size: MathSize.scriptsize, 80 | ), 81 | children: [SymbolNode(symbol: 'a')], 82 | ), 83 | ]).encodeTeX(), 84 | '{z\\displaystyle \\scriptsize a}', 85 | ); 86 | }); 87 | }); 88 | } 89 | -------------------------------------------------------------------------------- /lib/src/ast/nodes/under.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../../render/layout/min_dimension.dart'; 4 | import '../../render/layout/vlist.dart'; 5 | import '../options.dart'; 6 | import '../size.dart'; 7 | import '../style.dart'; 8 | import '../syntax_tree.dart'; 9 | 10 | /// Under node. 11 | /// 12 | /// Examples: `\underset` 13 | class UnderNode extends SlotableNode { 14 | /// Base where the under node is applied upon. 15 | final EquationRowNode base; 16 | 17 | /// Argumentn below the base. 18 | final EquationRowNode below; 19 | UnderNode({ 20 | required this.base, 21 | required this.below, 22 | }); 23 | 24 | // KaTeX's corresponding code is in /src/functions/utils/assembleSubSup.js 25 | @override 26 | BuildResult buildWidget( 27 | MathOptions options, List childBuildResults) { 28 | final spacing = options.fontMetrics.bigOpSpacing5.cssEm.toLpUnder(options); 29 | return BuildResult( 30 | italic: 0.0, 31 | options: options, 32 | widget: Padding( 33 | padding: EdgeInsets.only(bottom: spacing), 34 | child: VList( 35 | baselineReferenceWidgetIndex: 0, 36 | children: [ 37 | childBuildResults[0]!.widget, 38 | // TexBook Rule 13a 39 | MinDimension( 40 | minHeight: 41 | options.fontMetrics.bigOpSpacing4.cssEm.toLpUnder(options), 42 | topPadding: 43 | options.fontMetrics.bigOpSpacing2.cssEm.toLpUnder(options), 44 | child: childBuildResults[1]!.widget, 45 | ), 46 | ], 47 | ), 48 | ), 49 | ); 50 | } 51 | 52 | @override 53 | List computeChildOptions(MathOptions options) => [ 54 | options, 55 | options.havingStyle(options.style.sub()), 56 | ]; 57 | 58 | @override 59 | List computeChildren() => [base, below]; 60 | 61 | @override 62 | AtomType get leftType => AtomType.ord; 63 | 64 | @override 65 | AtomType get rightType => AtomType.ord; 66 | 67 | @override 68 | bool shouldRebuildWidget(MathOptions oldOptions, MathOptions newOptions) => 69 | false; 70 | 71 | @override 72 | UnderNode updateChildren(List newChildren) => 73 | copyWith(base: newChildren[0], below: newChildren[1]); 74 | 75 | @override 76 | Map toJson() => super.toJson() 77 | ..addAll({ 78 | 'base': base.toJson(), 79 | 'below': below.toJson(), 80 | }); 81 | 82 | UnderNode copyWith({ 83 | EquationRowNode? base, 84 | EquationRowNode? below, 85 | }) => 86 | UnderNode( 87 | base: base ?? this.base, 88 | below: below ?? this.below, 89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_math_fork 2 | description: Fast and high-quality TeX math equation rendering with pure Dart & Flutter. 3 | version: 0.7.4 4 | homepage: https://github.com/simpleclub/flutter_math 5 | 6 | environment: 7 | sdk: '>=3.0.0 <4.0.0' 8 | flutter: '>=3.0.0' 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | flutter_svg: ">=2.0.0+1 <3.0.0" 15 | provider: ^6.0.5 16 | meta: ^1.8.0 17 | collection: ^1.17.0 18 | tuple: ^2.0.1 19 | 20 | dev_dependencies: 21 | flutter_test: 22 | sdk: flutter 23 | 24 | flutter: 25 | fonts: 26 | - family: KaTeX_Main 27 | fonts: 28 | - asset: lib/katex_fonts/fonts/KaTeX_Main-Regular.ttf 29 | - asset: lib/katex_fonts/fonts/KaTeX_Main-Italic.ttf 30 | style: italic 31 | - asset: lib/katex_fonts/fonts/KaTeX_Main-Bold.ttf 32 | weight: 700 33 | - asset: lib/katex_fonts/fonts/KaTeX_Main-BoldItalic.ttf 34 | weight: 700 35 | style: italic 36 | - family: KaTeX_Math 37 | fonts: 38 | - asset: lib/katex_fonts/fonts/KaTeX_Math-Italic.ttf 39 | style: italic 40 | - asset: lib/katex_fonts/fonts/KaTeX_Math-BoldItalic.ttf 41 | weight: 700 42 | style: italic 43 | - family: KaTeX_AMS 44 | fonts: 45 | - asset: lib/katex_fonts/fonts/KaTeX_AMS-Regular.ttf 46 | - family: KaTeX_Caligraphic 47 | fonts: 48 | - asset: lib/katex_fonts/fonts/KaTeX_Caligraphic-Regular.ttf 49 | - asset: lib/katex_fonts/fonts/KaTeX_Caligraphic-Bold.ttf 50 | weight: 700 51 | - family: KaTeX_Fraktur 52 | fonts: 53 | - asset: lib/katex_fonts/fonts/KaTeX_Fraktur-Regular.ttf 54 | - asset: lib/katex_fonts/fonts/KaTeX_Fraktur-Bold.ttf 55 | weight: 700 56 | - family: KaTeX_SansSerif 57 | fonts: 58 | - asset: lib/katex_fonts/fonts/KaTeX_SansSerif-Regular.ttf 59 | - asset: lib/katex_fonts/fonts/KaTeX_SansSerif-Bold.ttf 60 | weight: 700 61 | - asset: lib/katex_fonts/fonts/KaTeX_SansSerif-Italic.ttf 62 | style: italic 63 | - family: KaTeX_Script 64 | fonts: 65 | - asset: lib/katex_fonts/fonts/KaTeX_Script-Regular.ttf 66 | - family: KaTeX_Typewriter 67 | fonts: 68 | - asset: lib/katex_fonts/fonts/KaTeX_Typewriter-Regular.ttf 69 | - family: KaTeX_Size1 70 | fonts: 71 | - asset: lib/katex_fonts/fonts/KaTeX_Size1-Regular.ttf 72 | - family: KaTeX_Size2 73 | fonts: 74 | - asset: lib/katex_fonts/fonts/KaTeX_Size2-Regular.ttf 75 | - family: KaTeX_Size3 76 | fonts: 77 | - asset: lib/katex_fonts/fonts/KaTeX_Size3-Regular.ttf 78 | - family: KaTeX_Size4 79 | fonts: 80 | - asset: lib/katex_fonts/fonts/KaTeX_Size4-Regular.ttf 81 | --------------------------------------------------------------------------------