2121import ui # copy message
2222from scriptHandler import script # copy MathML via ctrl-c
2323from synthDriverHandler import getSynth # speech engine param setting
24- import winUser # clipboard manipultation
24+ import winUser # clipboard manipulation
25+ import gettext
26+ _ = gettext .gettext
2527from ctypes import windll # register clipboard formats
28+ from typing import Any , Optional
2629
2730from . import libmathcat
2831
4851 # Prosody.
4952 r"|<prosody(?: pitch='(?P<pitch>\d+)%')?(?: volume='(?P<volume>\d+)%')?(?: rate='(?P<rate>\d+)%')?> ?"
5053 r"|(?P<prosodyReset></prosody>) ?"
54+ r"|<audio src='(?P<beep>beep.mp4)'>.*?</audio> ?" # hack for beeps
5155 # Other tags, which we don't care about.
5256 r"|<[^>]+> ?"
5357 # Commas indicating pauses in navigation messages.
6165 "rate" : RateCommand ,
6266}
6367
64- def ConvertSSMLTextForNVDA (text , language = "" ):
68+ def ConvertSSMLTextForNVDA (text : str , language : str = "" ):
6569 # MathCAT's default rate is 180 wpm.
6670 # Assume that 0% is 80 wpm and 100% is 450 wpm and scale accordingly.
6771 synth = getSynth ()
@@ -82,24 +86,20 @@ def ConvertSSMLTextForNVDA(text, language=""):
8286 if use_break :
8387 out .append (BreakCommand (time = int (int (m .group ("break" )) * breakMulti )))
8488 elif m .lastgroup == "char" :
85- # get the NVDA settings for what to do for a capital char and apply them
8689 ch = m .group ("char" )
87- if ch .isupper ():
88- if synthConfig ["sayCapForCapitals" ]:
89- out .append (_ (u"cap" )) # capital letter prefix
90- if use_pitch :
91- out .append (PitchCommand (multiplier = int (synthConfig ["capPitchChange" ])))
92- if synthConfig ["beepForCapitals" ]:
93- out .append (BeepCommand (2000 , 50 ))
9490 if use_character :
9591 out .extend ((CharacterModeCommand (True ), ch , CharacterModeCommand (False )))
9692 else :
9793 out .extend ((" " , ch , " " ))
98- if use_pitch and ch .isupper ():
99- out .append (PitchCommand (multiplier = 1 ))
10094 elif m .lastgroup == "comma" :
10195 if use_break :
10296 out .append (BreakCommand (time = 100 ))
97+ elif m .lastgroup == "beep" :
98+ out .append (BeepCommand (2000 , 50 ))
99+ elif m .lastgroup == "pitch" :
100+ if use_pitch :
101+ out .append (PitchCommand (multiplier = int (m .group (m .lastgroup ))))
102+ resetProsody .add (PitchCommand )
103103 elif m .lastgroup in PROSODY_COMMANDS :
104104 command = PROSODY_COMMANDS [m .lastgroup ]
105105 if command in supported_commands :
@@ -130,7 +130,7 @@ class MathCATInteraction(mathPres.MathInteractionNVDAObject):
130130 CF_MathML_Presentation = windll .user32 .RegisterClipboardFormatW ("MathML Presentation" )
131131 # log.info("2**** MathCAT registering data formats: CF_MathML %x, CF_MathML_Presentation %x" % (CF_MathML, CF_MathML_Presentation))
132132
133- def __init__ (self , provider = None , mathMl = None ):
133+ def __init__ (self , provider = None , mathMl : Optional [ str ] = None ):
134134 super (MathCATInteraction , self ).__init__ (provider = provider , mathMl = mathMl )
135135 provider ._setSpeechLanguage (mathMl )
136136 try :
@@ -149,7 +149,7 @@ def reportFocus(self):
149149 speech .speakMessage (_ ("Error in speaking math: see NVDA error log for details" ))
150150
151151
152- def getBrailleRegions (self , review = False ):
152+ def getBrailleRegions (self , review : bool = False ):
153153 # log.info("***MathCAT start getBrailleRegions")
154154 yield braille .NVDAObjectRegion (self , appendText = " " )
155155 region = braille .Region ()
@@ -165,7 +165,7 @@ def getBrailleRegions(self, review=False):
165165 # log.info("***MathCAT end getBrailleRegions ***")
166166 yield region
167167
168- def getScript (self , gesture ):
168+ def getScript (self , gesture : KeyboardInputGesture ):
169169 # Pass most keys to MathCAT. Pretty ugly.
170170 if isinstance (gesture , KeyboardInputGesture ) and "NVDA" not in gesture .modifierNames and (
171171 gesture .mainKeyName in {
@@ -178,7 +178,7 @@ def getScript(self, gesture):
178178 return self .script_navigate
179179 return super ().getScript (gesture )
180180
181- def script_navigate (self , gesture ):
181+ def script_navigate (self , gesture : KeyboardInputGesture ):
182182 # log.info("***MathCAT script_navigate")
183183 try :
184184 if gesture != None :
@@ -206,11 +206,11 @@ def script_navigate(self, gesture):
206206 @script (
207207 gesture = "kb:control+c" ,
208208 )
209- def script_rawdataToClip (self , gesture ):
209+ def script_rawdataToClip (self , gesture : KeyboardInputGesture ):
210210 try :
211211 mathml = libmathcat .GetNavigationMathML ()[0 ]
212212 if not re .match (self ._startsWithMath , mathml ):
213- mathml = "<math>" + mathml + "</math>" # copy will fix up namespacing
213+ mathml = "<math>" + mathml + "</math>" # copy will fix up name spacing
214214 self ._copyToClipAsMathML (mathml )
215215 ui .message (_ ("copy" ))
216216 except Exception as e :
@@ -227,7 +227,6 @@ def _wrapMathMLForClipBoard(self, text: str) -> str:
227227 mathml_with_ns = mathml_with_ns .replace ('math' , 'math xmlns="http://www.w3.org/1998/Math/MathML"' , 1 )
228228 return '<?xml version="1.0"?>' + mathml_with_ns
229229
230- from typing import Any , Optional
231230 def _copyToClipAsMathML (self , text : str , notify : Optional [bool ] = False ) -> bool :
232231 """Copies the given text to the windows clipboard.
233232 @returns: True if it succeeds, False otherwise.
@@ -238,8 +237,9 @@ def _copyToClipAsMathML(self, text: str, notify: Optional[bool] = False) -> bool
238237 if not isinstance (text , str ) or len (text ) == 0 :
239238 return False
240239 from api import getClipData
240+ from mainFrame import Handle
241241 try :
242- with winUser .openClipboard (mainFrame . Handle ):
242+ with winUser .openClipboard (Handle ):
243243 winUser .emptyClipboard ()
244244 self ._setClipboardData (self .CF_MathML , self ._wrapMathMLForClipBoard (text ))
245245 self ._setClipboardData (self .CF_MathML_Presentation , self ._wrapMathMLForClipBoard (text ))
@@ -257,13 +257,13 @@ def _copyToClipAsMathML(self, text: str, notify: Optional[bool] = False) -> bool
257257 ui .reportTextCopiedToClipboard () # No argument reports a failure.
258258 return False
259259
260- def _setClipboardData (self , format ,data ):
260+ def _setClipboardData (self , format , data : str ):
261261 # Need to support MathML Presentation, so this copied from winUser.py and the first two lines are commented out
262262 # For now only unicode is a supported format
263263 # if format!=CF_UNICODETEXT:
264264 # raise ValueError("Unsupported format")
265265 from textUtils import WCHAR_ENCODING
266- from ctypes import c_wchar
266+ from ctypes import c_wchar , WinError
267267 import winKernel
268268 text = data
269269 bufLen = len (text .encode (WCHAR_ENCODING , errors = "surrogatepass" )) + 2
@@ -276,7 +276,7 @@ def _setClipboardData(self, format,data):
276276 buf .value = text
277277 # Set the clipboard data with the global memory
278278 if not windll .user32 .SetClipboardData (format ,h ):
279- raise ctypes . WinError ()
279+ raise WinError ()
280280 # NULL the global memory handle so that it is not freed at the end of scope as the clipboard now has it.
281281 h .forget ()
282282
@@ -296,7 +296,7 @@ def __init__(self):
296296 speech .speakMessage (_ ("MathCAT initialization failed: see NVDA error log for details" ))
297297
298298
299- def getSpeechForMathMl (self , mathml ):
299+ def getSpeechForMathMl (self , mathml : str ):
300300 self ._setSpeechLanguage (mathml )
301301 try :
302302 libmathcat .SetMathML (mathml )
@@ -305,6 +305,14 @@ def getSpeechForMathMl(self, mathml):
305305 speech .speakMessage (_ ("Illegal MathML found: see NVDA error log for details" ))
306306 libmathcat .SetMathML ("<math></math>" ) # set it to something
307307 try :
308+ synth = getSynth ()
309+ synthConfig = config .conf ["speech" ][synth .name ]
310+ supported_commands = synth .supportedCommands
311+ # Set preferences for capital letters
312+ libmathcat .SetPreference ("CapitalLetters_Beep" , "true" if synthConfig ["beepForCapitals" ] else "false" )
313+ libmathcat .SetPreference ("CapitalLetters_UseWord" , "true" if synthConfig ["sayCapForCapitals" ] else "false" )
314+ if PitchCommand in supported_commands :
315+ libmathcat .SetPreference ("CapitalLetters_Pitch" , str (synthConfig ["capPitchChange" ]))
308316 if self ._add_sounds ():
309317 return [BeepCommand (800 ,25 )] + ConvertSSMLTextForNVDA (libmathcat .GetSpokenText ()) + [BeepCommand (600 ,15 )]
310318 else :
@@ -321,7 +329,7 @@ def _add_sounds(self):
321329 except :
322330 return False
323331
324- def getBrailleForMathMl (self , mathml ):
332+ def getBrailleForMathMl (self , mathml : str ):
325333 # log.info("***MathCAT getBrailleForMathMl")
326334 try :
327335 libmathcat .SetMathML (mathml )
@@ -337,18 +345,14 @@ def getBrailleForMathMl(self, mathml):
337345 return ""
338346
339347
340- def interactWithMathMl (self , mathMl ):
341- MathCATInteraction (provider = self , mathMl = mathMl ).setFocus ()
342- MathCATInteraction (provider = self , mathMl = mathMl ).script_navigate (None )
348+ def interactWithMathMl (self , mathml : str ):
349+ MathCATInteraction (provider = self , mathMl = mathml ).setFocus ()
350+ MathCATInteraction (provider = self , mathMl = mathml ).script_navigate (None )
343351
344- def _setSpeechLanguage (self , mathMl ):
345- lang = mathPres .getLanguageFromMath (mathMl )
346- if not lang :
347- lang = speech .getCurrentLanguage ()
348- try :
349- libmathcat .SetPreference ("Language" , lang .replace ("_" , "-" ))
350- self ._language = lang
351- except Exception as e :
352- log .error (e )
353- speech .speakMessage (_ ("MathCAT does not support language %s: see NVDA error log for details" % lang ))
352+ def _setSpeechLanguage (self , mathml : str ):
353+ # NVDA inserts its notion of the current language into the math tag, so we can't use it
354+ # see nvda\source\mathPres\mathPlayer.py for original version of this code
355+ # lang = mathPres.getLanguageFromMath(mathml)
354356
357+ # it might have changed, so can't just set it in init()
358+ self ._language = libmathcat .GetPreference ("Language" )
0 commit comments