1+ //! When calling from python, the general ordering is:
2+ //! 1. whatever preferences the AT needs to set, it is done with calls to [`SetPreference`].
3+ //! 2. the MathML is sent over via [`SetMathML`].
4+ //! 3. AT calls to get the speech [`GetSpokenText`] and calls [`GetBraille`] to get the (Unicode) braille.
5+ //!
6+ //! Navigation can be done via calls to either:
7+ //! * [`DoNavigateKeyPress`] (takes key events as input)
8+ //! * [`DoNavigateCommand`] (takes the commands the key events internally map to)
9+ //! Both return a string to speak.
10+ //! To highlight the node on is on, 'id's are used. If they weren't already present,
11+ //! [`SetMathML`] returns a string representing MathML that contains 'id's for any node that doesn't already
12+ //! have an 'id' set. You can get the current node with
13+ //! * [`GetNavigationMathMLId`]
14+ //! * [`GetNavigationMathML`] -- returns a string representing the MathML for the selected node
15+ //! Note: a second integer is returned. This is the offset in characters for a leaf node.
16+ //! This is needed when navigating by character for multi-symbol leaf nodes such as "sin" and "1234"
17+ //!
18+ //! It is also possible to find out what preferences are currently set by calling [`GetPreference`]
19+ //!
20+ //! AT can pass key strokes to allow a user to navigate the MathML by calling [`DoNavigateKeyPress`]; the speech is returned.
21+ //! To get the MathML associated with the current navigation node, call [`GetNavigationMathML`].
22+ //!
23+
24+ // for Python interfaces --#[...] doesn't help on name mangled python function names
25+ #![ allow( non_snake_case) ]
26+ #![ allow( clippy:: needless_return) ]
27+
28+ use libmathcat:: interface:: StringOrFloat ;
29+ use pyo3:: prelude:: * ;
30+ use pyo3:: wrap_pyfunction;
31+ use pyo3:: exceptions:: PyOSError ;
32+
33+ /// The error type returned from MathCAT is from an error package, so we can't implement From on it.
34+ /// Instead, we write a wrapper function that deals with the error conversion
35+ fn convert_error < T > ( result : Result < T , libmathcat:: errors:: Error > ) -> PyResult < T > {
36+ return match result {
37+ Ok ( answer) => Ok ( answer) ,
38+ Err ( e) => {
39+ Err ( PyOSError :: new_err ( e. to_string ( ) ) )
40+ } ,
41+ } ;
42+ }
43+
44+ #[ pyfunction]
45+ /// The absolute path location of the MathCAT Rules dir.
46+ /// IMPORTANT: This should be the first call to MathCAT
47+ pub fn SetRulesDir ( _py : Python , rules_dir_location : String ) -> PyResult < ( ) > {
48+ return convert_error ( libmathcat:: interface:: SetRulesDir ( rules_dir_location) ) ;
49+ }
50+
51+ #[ pyfunction]
52+ /// The MathML to be spoken, brailled, or navigated.
53+ ///
54+ /// This will override any previous MathML that was set.
55+ /// Returns: the MathML that was set, annotated with 'id' values on each node (if none were present)
56+ /// The 'id' values can be used during navigation for highlighting the current node
57+ pub fn SetMathML ( _py : Python , mathml_str : String ) -> PyResult < String > {
58+ return convert_error ( libmathcat:: interface:: SetMathML ( mathml_str) ) ;
59+ }
60+ #[ pyfunction]
61+ /// Get the spoken text of the MathML that was set.
62+ /// The speech takes into account any AT or user preferences.
63+ pub fn GetSpokenText ( _py : Python ) -> PyResult < String > {
64+ return convert_error ( libmathcat:: interface:: GetSpokenText ( ) ) ;
65+ }
66+
67+ #[ pyfunction]
68+ /// Set an API preference. The preference name should be a known preference name.
69+ /// The value should either be a string or a number (depending upon the preference being set)
70+ ///
71+ /// This function can be called multiple times to set different values.
72+ /// The values are persistent but can be overwritten by setting a preference with the same name and a different value.
73+ pub fn SetPreference ( _py : Python , name : String , value : String ) -> PyResult < ( ) > {
74+ let as_float = value. parse :: < f64 > ( ) ;
75+ let str_or_float = match as_float {
76+ Ok ( f) => StringOrFloat :: AsFloat ( f) ,
77+ Err ( _) => StringOrFloat :: AsString ( value) ,
78+ } ;
79+ return convert_error ( libmathcat:: interface:: SetPreference ( name, str_or_float) ) ;
80+ }
81+
82+ #[ pyfunction]
83+ /// Set an API preference. The preference name should be a known preference name.
84+ /// The value should either be a string or a number (depending upon the preference being set)
85+ ///
86+ /// This function can be called multiple times to set different values.
87+ /// The values are persistent but can be overwritten by setting a preference with the same name and a different value.
88+ pub fn GetPreference ( _py : Python , name : String ) -> PyResult < String > {
89+ return match libmathcat:: interface:: GetPreference ( name) {
90+ Some ( value) => Ok ( value) ,
91+ None => Err ( PyOSError :: new_err ( "Unknown preference name" ) ) ,
92+ }
93+ }
94+
95+ #[ pyfunction]
96+ #[ allow( unused_variables) ]
97+ /// Get the braille associated with the MathML node with a given id (MathML set by `SetMathML`]).
98+ /// An empty string can be used to return the braille associated with the entire expression.
99+ ///
100+ /// The braille returned depends upon the preference for braille output.
101+ pub fn GetBraille ( _py : Python , nav_node_id : String ) -> PyResult < String > {
102+ return convert_error ( libmathcat:: interface:: GetBraille ( nav_node_id) ) ;
103+ }
104+
105+ #[ pyfunction]
106+ /// Given a key code along with the modifier keys, the current node is moved accordingly (or value reported in some cases).
107+ ///
108+ /// The spoken text for the new current node is returned.
109+ pub fn DoNavigateKeyPress ( _py : Python , key : usize , shift_key : bool , control_key : bool , alt_key : bool , meta_key : bool ) -> PyResult < String > {
110+ return convert_error ( libmathcat:: interface:: DoNavigateKeyPress ( key, shift_key, control_key, alt_key, meta_key) ) ;
111+ }
112+
113+ #[ pyfunction]
114+ /// Given a command, the current node is moved accordingly (or value reported in some cases).
115+ ///
116+ /// The spoken text for the new current node is returned.
117+ ///
118+ /// The list of legal commands are:
119+ /// "MovePrevious", "MoveNext", "MoveStart", "MoveEnd", "MoveLineStart", "MoveLineEnd",
120+ /// "MoveCellPrevious", "MoveCellNext", "MoveCellUp", "MoveCellDown", "MoveColumnStart", "MoveColumnEnd",
121+ /// "ZoomIn", "ZoomOut", "ZoomOutAll", "ZoomInAll",
122+ /// "MoveLastLocation",
123+ /// "ReadPrevious", "ReadNext", "ReadCurrent", "ReadCellCurrent", "ReadStart", "ReadEnd", "ReadLineStart", "ReadLineEnd",
124+ /// "DescribePrevious", "DescribeNext", "DescribeCurrent",
125+ /// "WhereAmI", "WhereAmIAll",
126+ /// "ToggleZoomLockUp", "ToggleZoomLockDown", "ToggleSpeakMode",
127+ /// "Exit",
128+ /// "MoveTo0","MoveTo1","MoveTo2","MoveTo3","MoveTo4","MoveTo5","MoveTo6","MoveTo7","MoveTo8","MoveTo9",
129+ /// "Read0","Read1","Read2","Read3","Read4","Read5","Read6","Read7","Read8","Read9",
130+ /// "Describe0","Describe1","Describe2","Describe3","Describe4","Describe5","Describe6","Describe7","Describe8","Describe9",
131+ /// "SetPlacemarker0","SetPlacemarker1","SetPlacemarker2","SetPlacemarker3","SetPlacemarker4","SetPlacemarker5","SetPlacemarker6","SetPlacemarker7","SetPlacemarker8","SetPlacemarker9",
132+ pub fn DoNavigateCommand ( _py : Python , command : String ) -> PyResult < String > {
133+ return convert_error ( libmathcat:: interface:: DoNavigateCommand ( command) ) ;
134+ }
135+
136+ #[ pyfunction]
137+ /// Return the MathML associated with the current (navigation) node.
138+ pub fn GetNavigationMathMLId ( _py : Python ) -> PyResult < ( String , usize ) > {
139+ return convert_error ( libmathcat:: interface:: GetNavigationMathMLId ( ) ) ;
140+ }
141+
142+ #[ pyfunction]
143+ /// Return the MathML associated with the current (navigation) node.
144+ pub fn GetNavigationMathML ( _py : Python ) -> PyResult < ( String , usize ) > {
145+ return convert_error ( libmathcat:: interface:: GetNavigationMathML ( ) ) ;
146+ }
147+
148+ #[ pymodule]
149+ fn libmathcat ( _py : Python , m : & PyModule ) -> PyResult < ( ) > {
150+ m. add_function ( wrap_pyfunction ! ( SetRulesDir , m) ?) ?;
151+ m. add_function ( wrap_pyfunction ! ( SetMathML , m) ?) ?;
152+ m. add_function ( wrap_pyfunction ! ( GetSpokenText , m) ?) ?;
153+ m. add_function ( wrap_pyfunction ! ( SetPreference , m) ?) ?;
154+ m. add_function ( wrap_pyfunction ! ( GetPreference , m) ?) ?;
155+ m. add_function ( wrap_pyfunction ! ( GetBraille , m) ?) ?;
156+ m. add_function ( wrap_pyfunction ! ( DoNavigateKeyPress , m) ?) ?;
157+ m. add_function ( wrap_pyfunction ! ( DoNavigateCommand , m) ?) ?;
158+ m. add_function ( wrap_pyfunction ! ( GetNavigationMathMLId , m) ?) ?;
159+ m. add_function ( wrap_pyfunction ! ( GetNavigationMathML , m) ?) ?;
160+
161+ return Ok ( ( ) ) ;
162+ }
163+
164+ #[ cfg( test) ]
165+ mod py_tests {
166+ use super :: * ;
167+
168+ #[ test]
169+ fn test_setting ( ) {
170+ // this isn't a real test
171+ pyo3:: prepare_freethreaded_python ( ) ;
172+ let mathml_str = "<math><mo>(</mo><mrow><mn>451</mn><mo>,</mo><mn>231</mn></mrow><mo>)</mo></math>" ;
173+ match convert_error ( libmathcat:: interface:: SetMathML ( mathml_str. to_string ( ) ) ) {
174+ Ok ( _mathml_with_ids) => println ! ( "MathML is set w/o error" ) ,
175+ Err ( e) => println ! ( "Error is {}" , e. to_string( ) ) ,
176+ }
177+ // still alive?
178+ match convert_error ( libmathcat:: interface:: SetMathML ( mathml_str. to_string ( ) ) ) {
179+ Ok ( _mathml_with_ids) => panic ! ( "MathML is set 2nd time w/o error" ) ,
180+ Err ( e) => panic ! ( "Error remains {}" , e. to_string( ) ) ,
181+ }
182+ }
183+ }
0 commit comments